AI Agent Observability 2026: Làm Sao Biết Agent Chạy Đúng?
Posted on: 6/4/2026 1:13:22 AM
Table of contents
- 1. Vì sao đánh giá Agent khó hơn đánh giá Model?
- 2. Cú dịch chuyển 2026: từ "câu trả lời đúng" sang "trajectory đúng"
- 3. Giải phẫu một Trace: cây span của agent
- 4. OpenTelemetry GenAI: chuẩn hóa để khỏi khóa nhà cung cấp
- 5. Stack Observability 5 tầng
- 6. Những metric thực sự quan trọng
- 7. LLM-as-Judge: dùng model để chấm model
- 8. Offline Eval và Online Monitoring: hai mặt của một đồng xu
- 9. So sánh công cụ: chọn nền tảng nào?
- 10. Bắt tay vào instrument: ví dụ thực tế
- 11. Những cái bẫy thường gặp
- 12. Kết luận
Bạn dành hai tuần xây một AI Agent. Buổi demo diễn ra hoàn hảo: agent đọc email, tra cứu đơn hàng, gọi đúng API, trả lời mạch lạc. Sếp gật gù, dự án được duyệt lên production. Ba tuần sau, một khách hàng phàn nàn agent khẳng định chắc nịch về một chính sách hoàn tiền không hề tồn tại. Bạn mở log lên và thấy một biển JSON dài vô tận. Câu hỏi đơn giản — "chuyện gì đã xảy ra?" — bỗng trở nên gần như không thể trả lời.
Đây chính là bài toán mà observability cho AI Agent sinh ra để giải. Với phần mềm truyền thống, một dòng code chạy đúng hôm nay sẽ chạy đúng ngày mai. Với agent, cùng một câu hỏi có thể đi qua năm con đường khác nhau, gọi những tool khác nhau, và cho ra kết quả khác nhau ở mỗi lần chạy. Bài viết này mổ xẻ vì sao đánh giá agent khó đến vậy, và bộ công cụ năm 2026 — trace theo chuẩn OpenTelemetry GenAI, đánh giá theo trajectory, và LLM-as-Judge — giúp bạn lấy lại quyền kiểm soát như thế nào.
1. Vì sao đánh giá Agent khó hơn đánh giá Model?
Hầu hết kỹ sư đến với agent từ thế giới machine learning, nơi việc đánh giá đã được giải khá gọn: bạn có tập test, một metric (accuracy, F1, BLEU...), chạy một lần ra một con số. Nhưng agent phá vỡ toàn bộ giả định đó vì bốn lý do.
- Phi tất định (non-determinism): Cùng một input, hai lần chạy có thể cho hai chuỗi hành động khác nhau. Không có "đáp án vàng" duy nhất.
- Nhiều bước (multi-step): Một tác vụ có thể trải qua 3, 10 hay 40 bước. Lỗi không nằm ở "câu trả lời cuối" mà thường nằm ở bước thứ 7 — agent gọi nhầm tool, rồi cố sửa, rồi đi lạc.
- Đường đi quan trọng ngang kết quả: Một agent có thể đưa ra câu trả lời đúng nhưng đi qua 25 bước thừa, đốt gấp 4 lần token, gọi một API tốn tiền ba lần. "Đúng" mà tốn kém và chậm thì vẫn là một thất bại vận hành.
- Tầng tool và bộ nhớ: Agent không chỉ sinh chữ — nó gọi function, truy vấn vector DB, đọc/ghi bộ nhớ. Mỗi mắt xích là một điểm có thể hỏng, và chúng tương tác với nhau theo cách khó đoán.
Sai lầm phổ biến nhất
Coi agent như một "hộp đen sinh text" và chỉ chấm điểm câu trả lời cuối cùng. Một agent có thể trả lời đúng vì may mắn, hoặc trả lời sai sau một chuỗi suy luận hoàn hảo bị hỏng ở bước cuối. Nếu bạn chỉ nhìn output, bạn không bao giờ phân biệt được hai trường hợp đó — và không bao giờ sửa được nguyên nhân gốc.
2. Cú dịch chuyển 2026: từ "câu trả lời đúng" sang "trajectory đúng"
Đây là thay đổi tư duy quan trọng nhất của năm. Năm 2024–2025, hầu hết nhóm còn chấm agent bằng metric đơn lượt: "câu trả lời có khớp ground truth không?". Sang 2026, đơn vị đánh giá đã chuyển thành trajectory — toàn bộ con đường mà agent đã đi.
Thay vì hỏi "model có trả lời đúng không?", câu hỏi vận hành thực sự đã trở thành: "bước nào hỏng, dưới lời gọi tool nào, với phiên bản prompt nào, ngữ cảnh retrieval nào, độ trễ và chi phí bao nhiêu?". Bạn chấm điểm cả việc agent chọn tool nào, có phục hồi được sau một lời gọi thất bại không, và đã đi bao nhiêu bước thừa.
flowchart LR
A["Eval đơn lượt
(2024-2025)"] -->|"chỉ chấm
output cuối"| B["Đúng / Sai"]
C["Eval trajectory
(2026)"] -->|"chấm cả
con đường"| D["Tool selection"]
C --> E["Khả năng phục hồi"]
C --> F["Số bước thừa"]
C --> G["Chi phí & độ trễ
mỗi bước"]
C --> H["Output cuối"]
style A fill:#f8f9fa,stroke:#888,color:#2c3e50
style C fill:#e94560,stroke:#fff,color:#fff
style B fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style D fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style E fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style F fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style G fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style H fill:#f8f9fa,stroke:#e94560,color:#2c3e50
3. Giải phẫu một Trace: cây span của agent
Nền tảng của mọi observability là trace. Một trace mô tả trọn vẹn một lần agent xử lý request, được tổ chức thành cây các span — mỗi span là một đơn vị công việc có thời điểm bắt đầu/kết thúc, thuộc tính, và quan hệ cha-con. Vay mượn trực tiếp từ distributed tracing (Jaeger, Zipkin), nhưng được mở rộng cho ngữ nghĩa của LLM.
flowchart TD
R["TRACE: 'Hủy đơn #4821 và hoàn tiền'"] --> S1["Span: agent.invoke
(root, 8.2s, $0.04)"]
S1 --> S2["Span: llm.chat — lượt 1
quyết định gọi tool"]
S1 --> S3["Span: tool.lookup_order
(120ms)"]
S1 --> S4["Span: llm.chat — lượt 2
đọc kết quả, lập kế hoạch"]
S1 --> S5["Span: tool.process_refund
(340ms) GỌI SAI THAM SỐ"]
S1 --> S6["Span: llm.chat — lượt 3
tự sửa lỗi & thử lại"]
S1 --> S7["Span: tool.process_refund
(310ms) OK"]
S1 --> S8["Span: llm.chat — lượt 4
tổng hợp câu trả lời"]
style R fill:#2c3e50,stroke:#fff,color:#fff
style S1 fill:#e94560,stroke:#fff,color:#fff
style S5 fill:#fff3e0,stroke:#ff9800,color:#2c3e50
style S2 fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style S3 fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style S4 fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style S6 fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style S7 fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style S8 fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
Mỗi span mang theo attributes — và đây là nơi vàng nằm ở chi tiết. Một span llm.chat ghi lại model, prompt, completion, số token vào/ra, chi phí, độ trễ, temperature. Một span tool.* ghi lại tên tool, tham số đầu vào, kết quả trả về, lỗi (nếu có). Khi mọi span được gắn nhãn nhất quán, bạn có thể truy vấn cả triệu trace để trả lời những câu như "tool nào hay thất bại nhất?" hay "phiên bản prompt nào làm tăng số bước thừa?".
4. OpenTelemetry GenAI: chuẩn hóa để khỏi khóa nhà cung cấp
Vấn đề của giai đoạn đầu là mỗi nền tảng định nghĩa span theo một kiểu riêng. Trace của bạn bị khóa chặt vào LangSmith, hoặc Langfuse, hoặc một SDK độc quyền nào đó. OpenTelemetry GenAI Semantic Conventions ra đời để giải quyết: một bộ schema span và tên thuộc tính chuẩn, trung lập với nhà cung cấp, để mọi công cụ cùng "nói chung một ngôn ngữ".
gen_ai.* cho LLM client span: gen_ai.system, gen_ai.request.model, gen_ai.usage.input_tokens...Ý nghĩa thực tiễn rất lớn: bạn instrument agent một lần theo chuẩn OTel, rồi tự do xuất trace sang bất kỳ backend nào — từ Jaeger self-host, đến Langfuse, đến Datadog — mà không phải đổi một dòng code instrumentation. Đây là cùng triết lý đã làm nên thành công của OpenTelemetry trong thế giới microservices, nay áp dụng cho agent.
| Loại span | Thuộc tính tiêu biểu (gen_ai.*) | Trả lời câu hỏi gì? |
|---|---|---|
| LLM client span | system, request.model, usage.input_tokens, usage.output_tokens, response.finish_reason | Lời gọi model tốn bao nhiêu token, dùng model nào, kết thúc vì sao? |
| Agent span | agent.name, agent.id, operation.name (invoke) | Agent nào, phiên bản nào đang chạy tác vụ này? |
| Tool / execute span | tool.name, tool.call.arguments, tool.call.result | Tool nào được gọi, tham số gì, trả về gì, lỗi không? |
| Events | gen_ai.system.message, gen_ai.user.message, gen_ai.choice | Nội dung prompt/completion đầy đủ để replay và đánh giá lại. |
5. Stack Observability 5 tầng
Một hệ thống observability agent trưởng thành năm 2026 được tổ chức thành năm tầng, mỗi tầng giải quyết một mối lo riêng. Đừng cố nhảy lên tầng 4 (đánh giá) khi chưa có tầng 1–2 (thu thập & chuẩn hóa) vững chắc.
flowchart TD
L1["TẦNG 1 — SDK & Instrumentation
OpenLLMetry, auto-instrument SDK"] --> L2["TẦNG 2 — Chuẩn & Schema span
OpenTelemetry GenAI Conventions"]
L2 --> L3["TẦNG 3 — Tracing & Replay
cây span, time-travel, debug từng bước"]
L3 --> L4["TẦNG 4 — Đánh giá & Chấm điểm
LLM-as-Judge, small-model judge, Ragas"]
L4 --> L5["TẦNG 5 — Chi phí & Vận hành
token, $, độ trễ, alert, dashboard"]
style L1 fill:#16213e,stroke:#e94560,color:#fff
style L2 fill:#1f4068,stroke:#e94560,color:#fff
style L3 fill:#2c3e50,stroke:#e94560,color:#fff
style L4 fill:#e94560,stroke:#fff,color:#fff
style L5 fill:#4CAF50,stroke:#fff,color:#fff
6. Những metric thực sự quan trọng
Sau khi có trace, câu hỏi là: chấm cái gì? Dưới đây là bộ metric cốt lõi mà các nhóm production 2026 theo dõi. Lưu ý chúng phủ cả ba lớp: kết quả, hành vi, và vận hành.
| Metric | Đo cái gì | Vì sao quan trọng |
|---|---|---|
| Task Success Rate | Agent có hoàn thành đúng mục tiêu người dùng giao không | Metric "bắc cầu" cuối cùng — nhưng một mình nó không cho biết vì sao hỏng. |
| Tool Selection Accuracy | Agent có gọi đúng tool, đúng tham số ở mỗi bước không | Một agent có thể gọi đúng mọi tool mà vẫn trượt tác vụ — và ngược lại. |
| Trajectory Quality | Số bước thừa, vòng lặp, khả năng phục hồi sau lỗi | Phát hiện agent "đi lạc" hoặc kẹt vòng lặp dù cuối cùng vẫn ra kết quả. |
| Faithfulness / Hallucination | Câu trả lời có bám vào ngữ cảnh/dữ liệu được cấp không | Chấm bằng LLM-as-Judge; bắt những trường hợp agent "bịa" tự tin. |
| Cost per Step | Token và tiền cho mỗi bước, mỗi tool, mỗi tác vụ | "Đúng" mà đốt gấp 5 lần ngân sách vẫn là thất bại vận hành. |
| Latency per Tool | Độ trễ và tỉ lệ lỗi của từng tool trên cây trace | Khoanh vùng nút cổ chai — thường là một API ngoài chậm, không phải model. |
Quy tắc vàng
Đừng tối ưu một metric đơn lẻ. Một agent đẩy Task Success lên 95% bằng cách gọi mọi tool có thể ở mỗi bước sẽ phá nát Cost per Step. Observability tốt là nhìn cả bảng cùng lúc và hiểu các đánh đổi.
7. LLM-as-Judge: dùng model để chấm model
Với những thứ không có "đáp án vàng" — chất lượng câu trả lời, độ bám ngữ cảnh, tông giọng — bạn không thể viết một hàm assert đơn giản. Giải pháp chủ đạo 2026 là LLM-as-Judge: dùng chính một LLM (thường là model mạnh hơn, hoặc một model nhỏ chuyên biệt) để chấm output theo rubric bạn định nghĩa.
# LLM-as-Judge: cham faithfulness theo thang 1-5
JUDGE_PROMPT = """Ban la giam khao danh gia cau tra loi cua AI Agent.
Chi cham dua tren NGU CANH duoc cung cap, KHONG dung kien thuc ngoai.
[NGU CANH]
{context}
[CAU HOI]
{question}
[CAU TRA LOI CUA AGENT]
{answer}
Cham diem faithfulness tu 1 (bia hoan toan) den 5 (bam sat ngu canh).
Tra ve JSON: {{"score": <1-5>, "reason": ""}}"""
def judge_faithfulness(context, question, answer):
resp = judge_client.chat(
model="claude-haiku-4-5", # model nho, re, du tot de cham
messages=[{"role": "user", "content": JUDGE_PROMPT.format(
context=context, question=question, answer=answer)}],
response_format={"type": "json_object"},
temperature=0, # deterministic de tai lap duoc
)
return json.loads(resp.content)
Hai lưu ý sống còn khi dùng LLM-as-Judge:
- Judge cũng cần được đánh giá. Trước khi tin một judge, hãy đối chiếu điểm của nó với một tập nhỏ do người chấm tay. Một judge lệch chuẩn còn nguy hiểm hơn không có judge, vì nó cho bạn cảm giác an toàn giả.
- Phân tầng để tiết kiệm. Mô hình 2026 phổ biến là kết hợp rule-based cho thứ kiểm tra được bằng code (JSON có hợp lệ không, tool có tồn tại không) với small-model judge (như các model nhỏ chuyên đánh giá) cho phần ngữ nghĩa — đạt phủ 100% production với chi phí chấp nhận được, thay vì gọi model lớn cho mọi trace.
Mẹo: chấm online và offline cùng một rubric
Dùng đúng một bộ judge cho cả eval offline (trên dataset, trong CI) và monitoring online (sample trace production). Khi chúng dùng chung rubric, điểm offline và online so sánh được với nhau — bạn biết ngay model có "rớt" khi ra thực địa hay không.
8. Offline Eval và Online Monitoring: hai mặt của một đồng xu
Đừng nhầm lẫn hai khái niệm này. Offline eval là chấm agent trên một dataset cố định trước khi deploy — như unit test, chạy trong CI, chặn merge nếu điểm tụt. Online monitoring là quan sát agent trên lưu lượng thật sau khi deploy — sample trace, chấm bằng judge, bắn alert khi metric trượt ngưỡng.
flowchart LR
subgraph OFF["OFFLINE — truoc deploy"]
D["Dataset vang
(case + ky vong)"] --> RUN["Chay agent"]
RUN --> SC["Cham diem
(judge + rule)"]
SC --> GATE{"Diem >= nguong?"}
GATE -->|"Khong"| BLOCK["Chan merge"]
GATE -->|"Co"| SHIP["Deploy"]
end
SHIP --> PROD["PRODUCTION"]
subgraph ON["ONLINE — sau deploy"]
PROD --> TR["Trace that"]
TR --> SMP["Sample & cham"]
SMP --> ALERT{"Metric truot?"}
ALERT -->|"Co"| PAGE["Alert + dieu tra"]
ALERT -->|"Khong"| OK["Tiep tuc"]
end
PAGE -.->|"case loi thanh
test moi"| D
style OFF fill:#f8f9fa,stroke:#e94560
style ON fill:#f8f9fa,stroke:#4CAF50
style PROD fill:#2c3e50,stroke:#fff,color:#fff
style GATE fill:#e94560,stroke:#fff,color:#fff
style ALERT fill:#4CAF50,stroke:#fff,color:#fff
Mũi tên gạch đứt là phần nhiều nhóm bỏ quên: mỗi sự cố production cần được biến thành một test case mới trong dataset offline. Không có vòng phản hồi này, bạn sẽ vá đi vá lại cùng một loại lỗi mãi mãi.
9. So sánh công cụ: chọn nền tảng nào?
Hệ sinh thái 2026 đã chín. Không có công cụ "tốt nhất" tuyệt đối — chỉ có công cụ hợp với stack và ràng buộc của bạn. Bốn cái tên đáng cân nhắc nhất:
| Công cụ | Thế mạnh | Hợp nhất khi |
|---|---|---|
| Langfuse | Open-source, self-host mạnh, chuẩn OTel, lưu trace ở hạ tầng của bạn (nền ClickHouse). Overhead ~15%. | Bạn cần data residency, muốn tự host, hoặc cần tách khỏi một framework cụ thể. |
| LangSmith | Tích hợp sâu LangChain/LangGraph, overhead gần như bằng không, gom trace thành "Insights". | Stack của bạn đã xây trên LangChain/LangGraph và muốn trải nghiệm liền mạch. |
| AgentOps | Mạnh ở replay và debug multi-framework, dựng lại từng bước của session. Overhead ~12%. | Bạn chạy nhiều framework agent khác nhau và cần "tua lại" để gỡ lỗi. |
| Arize Phoenix | Open-source, mạnh về eval và phát hiện drift, chuẩn OTel ngay từ lõi. | Bạn ưu tiên khâu đánh giá & giám sát chất lượng theo thời gian. |
Một góc nhìn ngắn gọn
Có người tóm tắt rất gọn vai trò của chúng: Langfuse cho bạn trace, LangSmith gom chúng thành insight, Braintrust giúp dựng dataset eval từ chúng, còn AgentOps cho bạn tua lại. Nhiều nhóm trưởng thành thực ra dùng kết hợp — nhưng nhờ chuẩn OpenTelemetry GenAI, chi phí chuyển đổi giữa chúng đã thấp hơn nhiều so với trước.
10. Bắt tay vào instrument: ví dụ thực tế
Điều đẹp nhất của chuẩn OTel là bạn thường không phải viết span thủ công — chỉ cần gắn auto-instrumentation. Dưới đây là một ví dụ tối giản theo phong cách OpenTelemetry cho một agent Python, xuất trace tới bất kỳ backend tương thích OTLP nào.
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
# 1. Cau hinh provider, xuat sang backend tuong thich OTLP (Langfuse, Jaeger...)
provider = TracerProvider()
provider.add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint="https://otel.your-backend.io/v1/traces"))
)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("my-agent")
# 2. Bao mot luot agent bang span goc, gan thuoc tinh theo chuan gen_ai.*
def run_agent(task: str):
with tracer.start_as_current_span("agent.invoke") as root:
root.set_attribute("gen_ai.agent.name", "support-agent")
root.set_attribute("gen_ai.operation.name", "invoke")
for step in agent_loop(task):
# 3. Moi lan goi tool la mot span con
with tracer.start_as_current_span(f"tool.{step.tool}") as ts:
ts.set_attribute("gen_ai.tool.name", step.tool)
ts.set_attribute("gen_ai.tool.call.arguments", json.dumps(step.args))
result = step.execute()
ts.set_attribute("gen_ai.tool.call.result", str(result)[:2000])
return root
Với .NET, hệ sinh thái cũng đã bắt kịp: System.Diagnostics.ActivitySource ánh xạ trực tiếp sang span của OpenTelemetry, và các thư viện như Microsoft.Extensions.AI phát ra telemetry theo chuẩn gen_ai.* — nghĩa là một agent viết bằng .NET 10 có thể xuất trace cùng định dạng với agent Python, vào chung một dashboard.
11. Những cái bẫy thường gặp
- Log thay vì trace. Đổ một biển
print()vào file không phải observability. Không có quan hệ cha-con giữa các bước, bạn không bao giờ tái dựng được trajectory. - Tin một judge chưa kiểm chứng. LLM-as-Judge phải được hiệu chỉnh với người chấm tay trước khi đưa vào CI gate, nếu không bạn đang chặn merge dựa trên ý kiến ngẫu nhiên của một model.
- Bỏ quên chi phí. Theo dõi accuracy mà không theo dõi cost per step là công thức cho một hóa đơn API gây sốc cuối tháng.
- Không sample ở production. Chấm 100% trace bằng LLM-as-Judge trên lưu lượng lớn sẽ tốn ngang chi phí vận hành chính agent. Hãy sample thông minh — ưu tiên trace có lỗi, độ trễ cao, hoặc chi phí bất thường.
- Vòng phản hồi đứt. Bắt được lỗi production nhưng không biến nó thành test case offline — bạn sẽ gặp lại đúng lỗi đó.
12. Kết luận
Năm 2024, câu hỏi là "model nào mạnh nhất?". Năm 2026, khi mọi đội đều có quyền truy cập những model gần ngang nhau, câu hỏi quyết định thắng thua đã đổi: "bạn có nhìn thấy agent của mình đang làm gì không?". Đội nào trace được mọi bước, chấm được trajectory chứ không chỉ output, và khép được vòng lặp từ sự cố production về dataset — đội đó sẽ vận hành agent đáng tin cậy, trong khi phần còn lại vẫn đang đoán mò trên một biển JSON.
Observability không phải thứ làm sau cùng "nếu còn thời gian". Trong kỷ nguyên agentic, nó chính là sự khác biệt giữa một demo ấn tượng và một sản phẩm production dám đặt trước mặt khách hàng. Hãy instrument theo chuẩn OpenTelemetry GenAI ngay từ dòng code đầu tiên — tương lai của bạn sẽ cảm ơn vì điều đó.
Nguồn tham khảo
- Anthropic — Effective context engineering for AI agents
- OpenTelemetry — GenAI Semantic Conventions
- Confident AI — LLM Agent Evaluation Metrics in 2026
- AWS — Evaluating AI agents: real-world lessons from building agentic systems at Amazon
- Coralogix — Agentic AI Observability: A Practical Guide for 2026
- Langfuse — Langfuse vs. LangSmith for LLM Observability
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.