写在前面 传统 Web 服务出问题,看日志和链路追踪就行。Agent 系统不一样——它的"故障"往往不是报错,而是成功返回了一个错误的答案。这种问题在日志里长得和正常请求一模一样。
一、问题的特殊性
一次 Agent 请求内部可能发生:路由判断、若干次检索、若干次模型调用、若干次工具调用、一次质量校验。
当用户反馈"答得不对"时,你需要知道:
- 它把这个问题判成了哪一类?判错了吗?
- 检索出了哪些片段?相关吗?
- 模型看到的完整上下文是什么?
- 有没有调用工具?调用参数对吗?返回了什么?
- 质量校验通过了吗?
普通的应用日志回答不了这些问题。你需要为 Agent 的执行结构专门设计一套记录。
传统日志为什么不够
一个典型的传统 Web 服务请求日志长这样:
[2026-07-08 10:23:15] GET /api/search?q=xxx → 200 OK in 45ms
排查这个请求:用 query 参数查数据库、看索引命中率、判断是否慢了。链路很短、因果关系清晰。
一个 Agent 请求的内部日志如果只记一行"请求完成",你完全不知道中间发生了什么。而如果把每一步都打印到标准日志里,会得到几百行混杂的文本,排查时靠 grep 大海捞针。问题不是日志不够多,是日志没有结构。
二、三张表
我们的结构是三层:
agent_runs — 一次完整运行
记录:请求标识、用户、进入了哪个 Profile、开始与结束时间、最终状态、总耗时、总消耗。这是排查的入口。
agent_steps — 运行中的每一步
记录:所属运行、步骤序号、步骤类型(检索/生成/校验等)、输入摘要、输出摘要、耗时、报错信息。这里能看出流程走到哪一步开始不对。
agent_tool_calls — 每一次工具调用
记录:所属步骤、工具名、调用参数、返回结果、耗时、成功与否。工具调用是最容易出问题的一环,单独一张表值得。
三张表通过外键串起来,从一次运行可以下钻到每一步、每一次调用。
一个真实的排查路径
用户反馈:"我上传论文问摘要,它给的内容好像不是这篇论文的。"
排查步骤:
- 在
agent_runs找到这次请求 → Profile 是paper_analysis,耗时 3.2 秒,状态正常 - 在
agent_steps看步骤 → 检索步骤耗时 0.8 秒,返回了 5 个片段 - 点进去看检索结果 → 发现检索返回的是另一篇论文的片段
- 问题定位:用户上传了同名文件覆盖了旧文件,但向量索引没有更新——是索引同步问题,不是 Agent 理解问题
整个过程从用户反馈到定位,三分钟。没有这套记录,我们可能要在对话记录里翻半天。
三、记录什么、不记录什么
这里有几个实际的取舍:
完整上下文要不要存? 存了非常有用,但体积大、含用户数据。我们的做法是存摘要和引用,配合原始资源的标识——需要还原时可以按标识重新组装,而不是把全文冗余一份。
模型的思考过程要不要存? 要。这是判断"它为什么这么答"最直接的依据。对于支持 thinking/reasoning 的模型,这一部分的 token 往往是最终回答的好几倍,也是排查中最有价值的。
存多久? 按数据敏感度和存储成本设保留期,超期归档或清理。这一点在企业私有化交付里通常有明确的合规要求,需要提前对齐。我们内部按"最近 30 天热存储 + 归档到 12 个月"来规划。
同步写还是异步写? 我们选异步。可观测性数据不应该拖慢主链路,更不应该因为它写失败而让用户的请求失败。用一个内存 buffer + 批量写入的机制,既能保证延迟不增加,又能控制写入压力。
不该存的内容
- 原始密码、API Key 等敏感凭证——即使加密也不该存,因为排查场景不需要这些信息
- 完整的多媒体文件——可以存文件路径和哈希,但文件本身留在对象存储里
- 每次模型调用的完整 token 流——这个数据量增长极快。改为保留输入/输出的完整内容(非流式记录),以及 token 消耗的统计值
四、这套数据的三个用途
排障
最直接的用途。用户反馈问题时,凭请求标识就能还原完整过程。这把"我们查一下"从几小时缩短到几分钟。
成本分析
哪类请求最贵、哪个环节的调用次数超预期、哪些工具调用经常超时后重试——这些只有落成结构化数据才看得出来。凭感觉估算通常偏差很大。
一个实例:我们跑了一个月的可观测数据后发现,literature_review 这个 Profile 的平均调用次数是 retrieval_qa 的 5 倍——因为它要跨多份材料检索。如果没有这个数据,我们可能会一直以为是"所有请求都差不多贵"。
质量迭代
质量门控拦截了哪些回答、被拦截的原因分布如何、哪个 Profile 的拦截率最高——这些直接指向下一轮该优化什么。
第三点是我们做这套东西的初衷。没有数据,Agent 的优化就只能靠"感觉这次好像好一点"。
五、观测模式:先看,再拦
我们在质量门控上用了一个值得推荐的做法:新的校验规则上线时,先只记录不拦截。
跑一段真实流量后,你会看到:
- 这条规则实际触发的频率是多少
- 触发的样本里,有多少是真问题、多少是误伤
- 不同场景下的触发率差异
有了这些数据,再决定哪些场景开启强制拦截、阈值定在哪里。
直接上强制拦截的风险很实在:一条设计时觉得很合理的规则,可能会误伤大量正常回答,而你要等到用户投诉才知道。
观测模式的数据怎么用
在观测模式下,每一条被"标记"的记录都会触发一条标记日志,包含:
- 触发了哪条规则
- 当前的输出内容和引用
- 规则判断的依据
这些数据会被定期抽查。一个可操作的建议:至少抽查 100 条标记记录,确认误伤率低于 5% 之后再考虑开启强制拦截。 100 条是凭经验定的人为判断阈值——太少不可信,太多成本高。
六、一个建议
如果你正在从零做 Agent 系统,建议把可观测性放在第一批要做的事里,而不是"等有空再补"。
原因很直接:这套记录需要 Agent 的执行结构本身是可拆解的。如果你先做了一个大黑盒,之后想加观测点,会发现根本没有"点"可加——你得先把黑盒拆开,那基本等于重写。
反过来,先有分层结构和记录,后续无论是调优、排障还是做成本控制,都有据可依。
想在你的业务里试试同样的做法?
告诉我们业务问题与数据现状,我们会给出方案建议与落地路径。
需要帮助?
联系我们的团队,获取贴合你业务场景的方案建议。