可观测性的重要性
Agent 系统比传统 API 复杂得多:一次任务可能经历数十次 LLM 调用、工具调用和状态转换。没有可观测性,你无法知道:Agent 在哪一步失败了?工具被错误调用了几次?哪个 Prompt 导致了幻觉?成本为什么这么高?
Agent 可观测性的三个层次:
层 1: 追踪(Tracing)— 看清每一步发生了什么
┌──────────────────────────────────────────────────────┐
│ 运行 ID → 节点执行序列 → LLM 输入/输出 → 工具调用记录 │
│ 延迟分布 → Token 用量 → 错误堆栈 │
└──────────────────────────────────────────────────────┘
层 2: 评估(Evaluation)— 量化质量好坏
┌──────────────────────────────────────────────────────┐
│ 任务完成率 → 答案正确率 → 工具调用精确率 │
│ 幻觉率 → 平均步骤数 → 平均成本/任务 │
└──────────────────────────────────────────────────────┘
层 3: 优化(Optimization)— 系统性改进
┌──────────────────────────────────────────────────────┐
│ Prompt 实验 → A/B 测试 → 数据集构建 → 持续评估 │
└──────────────────────────────────────────────────────┘
LangSmith 集成
LangSmith 是 LangChain 官方的可观测性平台,对 LangGraph Agent 有原生支持,配置极简:
import os
from langsmith import Client
# ── 环境变量配置(.env 文件)─────────────────────────────
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGCHAIN_API_KEY"] = "ls__xxxxxxxxxxxxxxxx"
os.environ["LANGCHAIN_PROJECT"] = "my-agent-project"
# 配置后,所有 LangChain/LangGraph 调用自动被追踪
# 无需修改任何业务代码
# ── 手动添加追踪元数据 ────────────────────────────────────
from langsmith import traceable
@traceable(name="research_agent_run", tags=["production", "v2"])
def run_research_agent(user_query: str, user_id: str) -> str:
# 自动追踪这个函数内的所有 LLM 调用
result = graph.invoke(
{"messages": [HumanMessage(content=user_query)]},
config={
"metadata": { # 追加到追踪记录的元数据
"user_id": user_id,
"query_type": "research"
}
}
)
return result["messages"][-1].content
# ── 使用 LangSmith Client 程序化访问追踪数据 ────────────
client = Client()
# 获取最近 100 次运行
runs = list(client.list_runs(
project_name="my-agent-project",
run_type="chain",
limit=100
))
# 分析失败的运行
failed_runs = [r for r in runs if r.error]
print(f"失败率:{len(failed_runs)/len(runs):.1%}")
# 计算平均 Token 使用量
avg_tokens = sum(r.total_tokens for r in runs if r.total_tokens) / len(runs)
print(f"平均 Token/次:{avg_tokens:.0f}")
OpenTelemetry 追踪(自托管)
对于不能将数据发送到第三方平台的场景,可以使用 OpenTelemetry 自托管追踪:
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
# 配置 Jaeger 导出器(可换成 Zipkin、Tempo 等)
jaeger_exporter = JaegerExporter(
agent_host_name="localhost",
agent_port=6831,
)
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(jaeger_exporter))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("agent.research")
# 手动 Span
def traced_agent_node(state):
with tracer.start_as_current_span("agent_reasoning") as span:
span.set_attribute("messages.count", len(state["messages"]))
span.set_attribute("iteration", state.get("iteration", 0))
response = llm_with_tools.invoke(state["messages"])
span.set_attribute("tool_calls.count", len(response.tool_calls))
span.set_attribute("response.tokens",
response.usage_metadata.get("output_tokens", 0))
return {"messages": [response]}
Agent 评估指标体系
任务完成率(Task Completion Rate)
Agent 成功完成用户请求的比例。需要定义"完成"的标准(如:返回了答案、执行了期望的操作、没有超过最大迭代次数)。是最核心的 KPI。目标值:>90%。
工具调用精确率(Tool Call Precision)
Agent 调用工具时选择了正确工具的比例。= 正确工具调用次数 / 总工具调用次数。过低说明工具描述不清晰或工具数量过多。目标值:>85%。
平均步骤数(Average Steps)
完成任务平均需要的工具调用次数。步骤数过多说明 Agent 在"绕弯路",可能需要更好的规划能力或更清晰的工具描述。
幻觉率(Hallucination Rate)
答案中包含错误或不在工具结果中的信息的比例。使用 LLM 作为评估者(LLM-as-Judge)来检测。这是 RAG Agent 的关键指标。
from langsmith import Client, evaluate
from langsmith.schemas import Run, Example
from typing import Dict
client = Client()
# ── 构建评估数据集 ────────────────────────────────────────
dataset = client.create_dataset("agent_eval_v1")
# 添加测试用例(问题 + 期望答案)
test_cases = [
{"question": "Python 3.11 比 3.10 快多少?",
"expected": "约快 10-60%,平均25%"},
{"question": "LangGraph 和 LangChain 的关系?",
"expected": "LangGraph 是 LangChain 的 Agent 编排库"},
]
for case in test_cases:
client.create_example(
inputs={"question": case["question"]},
outputs={"answer": case["expected"]},
dataset_id=dataset.id
)
# ── 定义评估器(LLM-as-Judge)────────────────────────────
def correctness_evaluator(run: Run, example: Example) -> Dict:
"""用 LLM 评估答案是否正确。"""
actual = run.outputs.get("answer", "")
expected = example.outputs.get("answer", "")
judge_llm = ChatOpenAI(model="gpt-4o", temperature=0)
score = judge_llm.invoke([
SystemMessage(content="""你是公正的答案评估者。
判断实际答案是否实质上符合期望答案(不需要完全一致)。
只回复 'yes' 或 'no',不要解释。"""),
HumanMessage(content=
f"期望:{expected}\n实际:{actual}"
)
])
is_correct = "yes" in score.content.lower()
return {"key": "correctness", "score": 1.0 if is_correct else 0.0}
def tool_efficiency_evaluator(run: Run, example: Example) -> Dict:
"""评估工具调用效率:步骤数越少越好。"""
steps = run.extra.get("tool_call_count", 0)
# 5步以内满分,每多一步扣0.1分
score = max(0, 1.0 - max(0, steps - 5) * 0.1)
return {"key": "tool_efficiency", "score": score}
# ── 运行评估 ──────────────────────────────────────────────
def agent_runner(inputs: dict) -> dict:
result = graph.invoke({"messages": [HumanMessage(content=inputs["question"])]})
return {"answer": result["messages"][-1].content}
results = evaluate(
agent_runner,
data="agent_eval_v1",
evaluators=[correctness_evaluator, tool_efficiency_evaluator],
experiment_prefix="v2-gpt4o-mini"
)
print(results.to_pandas()[["correctness", "tool_efficiency"]].describe())
Prompt 优化方法论
- 建立基线:先用最简单的 Prompt 运行,记录关键指标(正确率、步骤数、成本)。
- 错误分析:从 LangSmith 中筛选失败/低质量的运行,分析失败模式(工具选错?推理出错?格式问题?)。
- 逐步改进:每次只改变一个变量(Prompt、模型、工具描述),对比前后指标。
- A/B 测试:对两个版本同时运行相同数据集,统计显著性差异。
- 持续回归:新版本上线前,必须通过历史评估数据集的回归测试。
评估的黄金法则
评估数据集要覆盖三类情况:1)典型的正常用例;2)边界情况和极端输入;3)历史上出现过 bug 的回归用例。只有这三类都覆盖,评估才有实际意义。