进阶
AI Agent 可观测性实战:日志、追踪与效果评估
很多团队把 Agent 上线后,遇到用户投诉才发现"它最近变笨了"或者"昨晚跑了笔异常大账单"——因为他们根本看不见 Agent 内部发生了什么。传统系统有日志和监控,LLM 应用更需要可观测性:一次回答可能调用了 7 次模型、绕了 3 个工具、花了 2 万 Token。本教程教你把 Agent 从"黑盒"变成"玻璃盒"。
📡 本教程适合:已经把 Agent 跑起来、准备投入生产的开发者和技术负责人;以及被"为什么又变贵了/又答错了"反复折磨过的运营方。需要基础的 Python/后端概念。
一、Agent 可观测性到底要"看见"什么?
和传统监控相比,LLM 应用多了一层"语义不确定性"。你需要同时盯住四个维度:
| 维度 | 要回答的问题 | 典型指标 |
|---|---|---|
| 调用层 | 模型被调了几次? | 调用次数 / 耗时 / 错误率 |
| 成本层 | 花了多少 Token? | Token 数 / 金额 / 人均成本 |
| 链路层 | 多步过程卡在哪? | Trace / 每步耗时 / 工具调用路径 |
| 质量层 | 答得对不对? | 自动评分 / 人工抽检 / 用户反馈 |
我的判断:可观测性不是"锦上添花",而是 Agent 上生产的前置条件。没有它,你既不能解释一次事故,也无法判断一次模型升级是变好还是变坏。先有度量,才有优化。
Step 1:埋点——记录每一次 LLM 调用
1 最基础的日志该记什么
在每次模型调用处加一层 wrapper,把关键信息落库。这是所有高级可观测性的地基。
import time, json, uuid
def trace_llm_call(prompt, model, call_fn):
trace_id = str(uuid.uuid4())[:8]
start = time.time()
try:
resp = call_fn(prompt) # 实际的模型调用
latency = round(time.time() - start, 3)
usage = resp.get("usage", {}) # prompt/completion token
log = {
"trace_id": trace_id,
"model": model,
"latency_s": latency,
"prompt_tokens": usage.get("prompt_tokens"),
"completion_tokens": usage.get("completion_tokens"),
"status": "ok",
}
write_to_logstore(log) # 写入日志系统
return resp
except Exception as e:
write_to_logstore({"trace_id": trace_id, "status": "error", "err": str(e)})
raise
💡 实操要点:至少记录 trace_id(串联整次请求)、model、耗时、token、状态。prompt 和 completion 原文要不要存,取决于隐私合规——敏感业务建议只存脱敏后的摘要。这一步做扎实,后面排查问题能省几天。
Step 2:链路追踪(Trace)
2 把多步 Agent 跑通串成一条时间线
Agent 一次回答往往=多次模型调用+多次工具调用。用"父-子"span 结构把它们串起来,才能看出"卡在哪一步、哪一步最贵"。
# 伪代码:用父子关系组织 span
with span("agent_run", trace_id=TRACE):
with span("plan", parent=TRACE):
model_call("请规划步骤...")
with span("tool:search", parent=TRACE):
run_tool("search", query)
with span("answer", parent=TRACE):
model_call("综合结果作答...")
# 可视化后你能看到每段的耗时占比和调用顺序
🚀 进阶玩法:给每个 span 打业务标签(用户 ID、功能模块、模型版本)。这样能回答"付费用户的平均耗时是不是比免费用户低""升级模型后 plan 阶段是不是变快了"这类业务问题,而不只是看全局平均值。
Step 3:成本与 Token 监控
3 成本要拆到"能追责"的粒度
# 按维度聚合每日成本
SELECT
date,
user_id,
feature, -- 哪个功能模块
model,
SUM(prompt_tokens + completion_tokens) AS total_tokens,
SUM(cost_usd) AS cost
FROM llm_logs
GROUP BY date, user_id, feature, model
ORDER BY cost DESC;
📊 经验参考:成本异常 90% 来自"某个功能忘了加长度上限"或"陷入了调用循环"。按 feature 和 user 双维度看,能快速定位是哪个功能/哪个用户在烧钱。给单用户、单会话设日额度上限,是防止"天价账单"的硬保险。
Step 4:输出质量评估
4 能度量质量,才能优化质量
光看"有没有报错"不够,还要看"答得好不好"。两条腿走路:
# 1) 自动评估:用更强的模型当评委(LLM-as-judge)
eval_prompt = f"""请给下面回答打分(1-5)并说明原因:
【问题】{question}
【标准答案要点】{ref_answer}
【待评回答】{candidate}
只输出 JSON: {{"score":, "reason":}}"""
# 2) 人工抽检:抽样 1%-5% 让运营/标注员打分
# 两者结合,自动评估看趋势,人工抽检看真相
重要提醒:LLM-as-judge 自己也会犯错,不能全信。它适合监控"整体趋势是否下滑",不适合作为单个回答的对错裁决。真正的质量底线,仍要保留人工抽检和用户的"踩/赞"反馈通道。
Step 5:异常检测与告警
5 让系统自己"喊疼"
# 设置几类关键告警,出问题立刻通知
ALERTS = {
"error_rate": "5分钟错误率 > 5% → 飞书/钉钉告警",
"p95_latency": "P95 延迟 > 15s → 告警(可能模型/网络抖动)",
"cost_spike": "单用户日成本 > $10 → 告警(可能调用循环)",
"jailbreak": "检测到 Prompt 注入特征 → 安全告警",
}
🔑 关键认知:告警要"可行动",不要"狼来了"。每条告警都应对应一个明确的处置动作。同时把"安全类事件"(注入攻击、敏感信息泄露)单独通道、高优先级——这类问题隔夜才发现,代价可能是数据事故。
Step 6:开源工具选型
不必从零造轮子,主流方案:
| 工具 | 定位 | 适合 |
|---|---|---|
| Langfuse | 开源 LLM 观测,Trace+评估 | 想自托管、注重隐私 |
| LangSmith | 商业托管,生态完善 | 快速上手、不想运维 |
| OpenTelemetry + 自建 | 通用可观测性标准 | 已用 Prometheus/Grafana |
⚠️ 别过度埋点:可观测性也有成本——存 prompt 原文很贵、高频采样会拖慢推理。建议:生产环境采样 10%-100%(按流量),关键链路全采,普通请求抽样,敏感字段一律脱敏后再存。