Bỏ qua

Bài 5: Agent Evaluation & Observability

Tổng quan

RAGAS và LLM-as-judge (Module I, Bài 7) đánh giá output cuối cùng - đủ cho RAG nhưng không đủ cho agent, vì agent có thể ra đúng đáp án bằng con đường sai (hoặc tốn kém, hoặc nguy hiểm). Bài này mở rộng evaluation sang trajectory - toàn bộ chuỗi hành động agent thực hiện - và cách giám sát agent trong production.


1. Eval Dimensions

graph TD
    EVAL[Agent Evaluation] --> TS[Task Success<br/>Có đạt mục tiêu không?]
    EVAL --> TQ[Trajectory Quality<br/>Con đường đi có hợp lý không?]
    EVAL --> TA[Tool Accuracy<br/>Chọn đúng tool, đúng tham số?]
    EVAL --> CL[Cost & Latency<br/>Có hiệu quả không?]
Chiều đánh giá Câu hỏi Ví dụ vấn đề nếu bỏ qua
Task Success Agent có hoàn thành mục tiêu người dùng đề ra không? Đây là metric "must-have" nhưng không đủ - có thể đúng do may mắn
Trajectory Quality Chuỗi bước có logic, không lặp vô ích, không đi đường vòng? Agent trả lời đúng nhưng gọi tool 15 lần cho task cần 3 lần
Tool Accuracy Có chọn đúng tool? Tham số truyền vào có chính xác? Agent gọi get_weather(city="Hà Lội") - lỗi chính tả khiến tool fail
Cost & Latency Tổng token, tổng thời gian, tổng số lời gọi LLM/tool? 2 agent cùng ra đúng đáp án nhưng 1 agent tốn gấp 5 lần chi phí

Task Success cao không có nghĩa là agent tốt

Một agent có thể đạt task success 95% nhưng trajectory kém - gọi tool thừa, lãng phí chi phí, hoặc thực hiện hành động rủi ro (xoá nhầm dữ liệu rồi tạo lại) mà vẫn ra kết quả đúng cuối cùng. Production cần đánh giá cả 4 chiều, không chỉ chiều cuối.


2. Methodologies

End-to-end Evaluation

So sánh output cuối với expected outcome - tương tự LLM-as-judge ở Module I nhưng áp dụng cho toàn bộ agent run.

from openai import OpenAI
import json

client = OpenAI()

def evaluate_task_success(task: str, final_output: str, success_criteria: str) -> dict:
    judge_prompt = f"""Đánh giá agent có hoàn thành nhiệm vụ không.

Nhiệm vụ: {task}
Tiêu chí thành công: {success_criteria}
Kết quả agent trả về: {final_output}

Trả về JSON: {{"success": true/false, "score": 0-1, "reasoning": "..."}}"""

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": judge_prompt}],
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)

Trajectory Evaluation

Đánh giá toàn bộ chuỗi Thought/Action/Observation, không chỉ câu trả lời cuối - phát hiện các vấn đề mà end-to-end eval bỏ sót.

def evaluate_trajectory(task: str, trajectory: list[dict]) -> dict:
    """
    trajectory: [{"action": "search_news", "args": {...}, "observation": "..."}, ...]
    """
    formatted = "\n".join([
        f"Bước {i+1}: gọi {step['action']}({step['args']}) → {step['observation'][:150]}"
        for i, step in enumerate(trajectory)
    ])

    judge_prompt = f"""Đánh giá chuỗi hành động của agent cho nhiệm vụ sau.

Nhiệm vụ: {task}

Chuỗi hành động:
{formatted}

Đánh giá theo tiêu chí (1-5):
1. Efficiency: Có bước thừa/lặp không cần thiết không?
2. Logical order: Thứ tự bước có hợp lý không?
3. Tool correctness: Tool và tham số có phù hợp với mục đích từng bước không?
4. Recovery: Nếu có bước lỗi, agent có xử lý hợp lý không?

Trả về JSON: {{"efficiency": X, "logical_order": X, "tool_correctness": X, "recovery": X, "issues": ["..."]}}"""

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": judge_prompt}],
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)

LLM-as-Judge cho Agent

Áp dụng lại các nguyên tắc giảm bias từ Module I (Bài 7) - đặc biệt quan trọng hơn với agent vì trajectory dài, dễ bị verbosity bias (judge thiên vị trajectory dài, chi tiết hơn dù không hiệu quả hơn).

Tái sử dụng nguyên tắc giảm bias từ Module I

6 loại bias của LLM-as-judge (position, verbosity, self-preference, style, calibration drift, preference leakage) đã học ở Module I, Bài 7 áp dụng nguyên vẹn cho agent evaluation. Với trajectory dài, verbosity bias càng dễ xảy ra - judge có thể đánh giá cao trajectory "trông kỹ lưỡng" dù thực chất kém hiệu quả hơn.

Unit Tests cho Tool Calls

Kiểm tra xác định (deterministic) - không cần LLM judge - cho các trường hợp có đáp án rõ ràng.

import pytest

def test_agent_calls_correct_tool_for_weather_query():
    result = run_agent("Thời tiết Hà Nội hôm nay thế nào?")
    tool_calls = extract_tool_calls(result)

    assert len(tool_calls) == 1
    assert tool_calls[0]["name"] == "get_weather"
    assert tool_calls[0]["args"]["city"] in ["Hà Nội", "Hanoi"]

def test_agent_does_not_call_tool_for_greeting():
    """Agent không nên gọi tool cho câu chào đơn giản - kiểm tra tránh over-calling."""
    result = run_agent("Xin chào!")
    tool_calls = extract_tool_calls(result)
    assert len(tool_calls) == 0

def test_agent_respects_max_retry_on_tool_error():
    result = run_agent_with_mocked_failing_tool(max_retries=3)
    assert result["retry_count"] <= 3

Phù hợp khi: Test case có đáp án đúng/sai rõ ràng (tool nào nên/không nên được gọi, giới hạn retry có được tôn trọng không) - nhanh, rẻ, chạy được trong CI.

So sánh methodologies

Phương pháp Chi phí Tốc độ Phát hiện được
Unit tests Thấp Nhanh (không cần LLM) Lỗi rõ ràng, có thể assert được
End-to-end (LLM judge) Trung bình Trung bình Task có hoàn thành không
Trajectory eval Cao (chuỗi dài) Chậm Hiệu quả, đường đi hợp lý, tool đúng

3. Observability

Evaluation chạy offline (trước khi deploy, trên test set). Observability giám sát agent online (production, real-time).

Tracing với LangSmith

import os

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls__..."
os.environ["LANGCHAIN_PROJECT"] = "vietnamese-research-agent"

# Mọi lời gọi LangGraph agent tự động được trace - mỗi node, mỗi tool call,
# mỗi token usage đều xuất hiện trên dashboard LangSmith
result = app.invoke({"messages": [...]}, config=config)

Trace của agent hiển thị dạng cây, phản ánh đúng graph: node nào gọi node nào, tool nào được gọi với tham số gì, mất bao lâu, tốn bao nhiêu token - khác với trace của 1 lời gọi LLM đơn (Module I) vốn chỉ là 1 span phẳng.

LangFuse (self-hosted alternative)

from langfuse.callback import CallbackHandler

langfuse_handler = CallbackHandler(
    public_key="pk-lf-...",
    secret_key="sk-lf-...",
    host="https://cloud.langfuse.com",
)

result = app.invoke(
    {"messages": [...]},
    config={"callbacks": [langfuse_handler], **config},
)

OpenTelemetry cho hệ thống đa framework

Khi agent kết hợp nhiều framework/service (vd: LangGraph + custom microservice), OpenTelemetry cung cấp chuẩn tracing không gắn với 1 vendor cụ thể.

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4317"))
)
tracer = trace.get_tracer("agent-service")

def agent_node(state: AgentState) -> dict:
    with tracer.start_as_current_span("agent_node") as span:
        response = llm.invoke(state["messages"])
        span.set_attribute("token_usage", response.usage_metadata["total_tokens"])
        return {"messages": [response]}
Công cụ Điểm mạnh Dùng khi
LangSmith Tích hợp sâu với LangGraph/LangChain, dashboard sẵn cho agent trace Stack chính là LangChain/LangGraph
LangFuse Open-source, tự host được, không vendor lock-in Cần kiểm soát dữ liệu, ngân sách hạn chế
OpenTelemetry Chuẩn mở, tích hợp được với hệ thống ngoài LLM (backend, DB) Kiến trúc đa framework, cần trace xuyên suốt toàn hệ thống

Metrics cần giám sát cho Agent (mở rộng từ Module I)

Metric Mô tả Alert threshold
Task success rate % task hoàn thành đúng < 90%
Avg steps per task Số bước trung bình để hoàn thành Tăng đột biến so với baseline
Tool error rate % tool call thất bại > 5%
Cost per task Tổng token × giá, trung bình mỗi task Vượt budget
Human intervention rate % task cần HITL can thiệp Tăng bất thường (dấu hiệu agent kém tự tin)
Loop detection Số lần agent lặp lại cùng 1 tool call > 3 lần liên tiếp

Tóm tắt

graph TD
    A[Đánh giá agent] --> B{Offline hay Online?}
    B -->|Offline, trước deploy| C[Evaluation]
    B -->|Online, production| D[Observability]
    C --> E{Loại kiểm tra?}
    E -->|Đáp án rõ ràng| F[Unit tests]
    E -->|Đánh giá output| G[End-to-end LLM judge]
    E -->|Đánh giá quá trình| H[Trajectory eval]
    D --> I[LangSmith / LangFuse / OpenTelemetry]
    I --> J[Giám sát: success rate, cost, tool errors, loop detection]
Khái niệm Ghi nhớ
Task Success Cần nhưng không đủ - đúng đáp án không đồng nghĩa quá trình tốt
Trajectory Quality Đánh giá cả con đường đi, không chỉ đích đến
Tool Accuracy Chọn đúng tool + đúng tham số
Cost & Latency So sánh hiệu quả giữa các agent cùng đạt task success
Unit tests Nhanh, rẻ, cho case có đáp án xác định - chạy trong CI
Trajectory eval Cần LLM judge, phát hiện lãng phí/đi đường vòng
LangSmith/LangFuse Trace cây phản ánh cấu trúc graph, khác trace phẳng của Module I
Loop detection Metric đặc thù agent - phát hiện agent kẹt lặp vô ích