一次多轮 Agent 请求的 trace 里应该记哪些东西?
谁在问:工程向二面;平台/基础设施团队必问
口语化问法
- 你们 Agent 的链路是怎么观测的?一条 trace 里都记什么?
- 线上出问题了,你靠什么定位?日志里有什么?
- prompt 原文你们存吗?怎么处理合规问题?
考察意图
这是工程向二面的高频题,因为它同时暴露三件事:
- 你有没有真的靠 trace 定位过线上问题。 真查过的人给的是清单 + 理由(「没有这一栏,那类问题就查不了」);没查过的人给的是「记 prompt 和 response」。
- 你有没有取舍意识。 trace 的成本可以非常吓人,全存原文的团队第一个月账单就会教做人。
- 你知不知道有行业标准,以及这个标准现在处于什么状态。 2026 年这一点本身就是个很好的分水岭。
参考答案
60 分答案(及格线)
一条 trace 应该记完整的调用链:每次模型调用的输入输出、用的哪个模型、token 消耗、延迟;每次工具调用的名字、参数、返回值、有没有报错;还有整体的耗时和最终结果。多轮的话要有一个会话 ID 把几轮串起来。我们会用 OpenTelemetry 那套规范,span 之间父子嵌套,这样在看板上能看到瀑布图。
结构正确、要素基本齐全,能过。
它止步于 60 分的原因是:这是一份「记什么」的清单,不是一份「为什么必须记」的清单。也没有验收标准、没有取舍(原文、采样、保留期),也没提结束原因、模型小版本、effort 档位这几个真正决定能不能定位问题的字段。
90 分答案(有生产经验的回答)
我先给验收标准,再给清单,因为清单是从标准推出来的。
验收标准只有一条:能不能据这条 trace,把这次请求原样搬到离线环境复现一遍。 做不到,它就只是一堆好看的瀑布图。线上排障的第一刀就是「原样离线重放」——重放不出来,你连「是系统问题还是评测集问题」都分不了。
按这个标准,要记的东西分四类:
① 身份与串联:trace_id、conversation / session id(把多轮串起来的钥匙)、脱敏后的 user id、请求入口与渠道。渠道这一栏很多团队不记,但它是「新接了个渠道导致质量掉」这类问题唯一的定位手段。
② 可重放的输入:系统提示的版本号(不是原文,原文另说)、检索命中的文档 ID + 相似度分数、每次工具调用的真实入参与返回、上下文总长度、是否触发过压缩/裁剪。这一栏决定了能不能重放。
③ 模型与配置:模型 ID 含小版本、effort 档位、max_tokens、是否命中 prompt 缓存、是否走了降级路由。上游静默升级和高峰期降级到小模型,只有这一栏能证明。
④ 结果与判定:结束原因(正常 / 打到 max_tokens 被截断 / 工具报错 / 超时 / 被拒答)、结构化输出是否解析成功、以及评测分数与 judge 版本。截断和解析失败是最高发的两个假象,经常被误判成「模型变笨了」。
结构上我会对齐 OpenTelemetry 的 GenAI 语义约定:
invoke_agent作为根 span,下面挂chat和execute_tool,属性用gen_ai.*那一套,会话用gen_ai.conversation.id串。但这里有个 2026 的现实要讲:这套约定到现在还全是 Development,没有一个属性达到 Stable,而且 2026-06 刚整体搬出核心仓库;历史上还改过好几轮名字(prompt_tokens→input_tokens、gen_ai.system→gen_ai.provider.name)。所以我们的做法是在采集层做属性归一化,并把语义约定版本当依赖钉死,别让看板直接吃上游语义——不同框架很可能同时发好几代属性。还有一条容易漏的:评测分数本身就该是遥测。约定里有
gen_ai.evaluation.*这组属性,把分数挂在 trace 上,才能从「这周有据率掉了 3 个点」一键下钻到具体 span。分数活在另一个系统里,就只能靠人工对时间戳。最后是取舍:prompt / completion 原文存不存。 存了才能排障、才能做数据飞轮、才能把线上失败补进评测集;但它是 PII 风险和存储成本的绝对大头。我们的口径是默认脱敏 + 按采样存原文 + 敏感字段哈希 + 保留期分层——原文保留 7–30 天,结构化指标和评测分数长期保留。采样率上,普通路由几个百分点,涉钱、涉权限、涉外发的高风险路由全采。
追问链
多轮的 trace 边界怎么划?一整个会话一条还是一轮一条?
期望一轮一条 trace,用 conversation id 串:整会话一条会 span 数爆炸、写入延迟高,且会话可能持续几天。轮次 span 上记轮次序号、累计 token、累计上下文长度,跨轮趋势才看得见;后台 Agent、定时任务这类跨会话长任务另用业务 ID 串信号说「整个会话一条 trace」且没意识到规模问题 → 没跑过高流量;能说出「轮次上记累计上下文长度」 → 被上下文膨胀坑过prompt 原文不存的话,出了问题怎么查?
期望不存原文 ≠ 什么都没有:存可重建要素 —— 系统提示版本号、检索文档 ID、工具入参出参、输入哈希与长度;点踩、转人工、解析失败、判分低的样本单独全量留原文(量小价值高)。纯语义问题(语气不对、答得啰嗦)不看原文查不了,靠采样留存兜;脱敏必须在采集端做信号说「不存就查不了」 → 没想过分层;提到「脱敏必须在采集端」 → 过过合规评审的人trace 采样怎么做?全采太贵,随机采会漏掉问题。
期望分层,别一刀切随机:高风险路由全采(涉钱、涉权限、写操作、对外发送)→ 异常必采(报错、超时、截断、解析失败、判分低),这批必须走尾部采样(tail sampling),请求结束后才决定 → 其余低比例随机采保分布代表性。头部采样会系统性丢掉罕见失败,而那正是你唯一想看的信号只说「按比例随机采」 → 会漏掉几乎所有值得看的样本;能区分头部/尾部采样、并记下「为什么这条被留下」 → 强信号这套东西你怎么和评测体系接起来?
期望trace → 评测:一键导出「最小可复现输入包」补进评测集 —— 这是数据飞轮的物理接口;评测 → trace:判分结果写回属性(gen_ai.evaluation.*),线上曲线才能下钻到具体请求。judge 版本号必须一起记,否则曲线跨期不可比信号只说「把 trace 导出来当测试用例」 → 单向,缺了分数回写;能说出「代码版本 × 评测集版本 × judge 版本」三元组 → 维护过长期曲线财务发现 trace 存储成本占整个应用成本的 40%,要求砍到 10%,你砍哪里?
期望先问体积来自哪:80%以上是原文(prompt / completion / 工具返回值),不是结构化属性。按序下刀:① 原文保留期30 天 → 7 天,排障绝大多数在 72 小时内 → ② 大字段外置对象存储、trace 只留引用 → ③ 降正常路径采样率 → ④ 裁掉没人查的字段。不能砍的三样:异常样本原文、高风险路由全采、指标与评测分数的长期保留信号直接说「降采样率」 → 最直觉但性价比最低的一刀,还会伤到异常样本;先问「体积主要来自哪」 → 和排查质量问题同一个起手式
max_tokens 被截断」分开 —— 前四层考清单,第 5 层才考你砍成本时守得住哪三样。评分要点
- 给出验收标准(能否离线原样重放),而不只是字段清单
- 四类字段齐全:身份与串联 / 可重放输入 / 模型与配置 / 结果与判定
- 点名几个高价值易漏字段:渠道、系统提示版本号、模型小版本、effort 档位、结束原因(截断)、是否降级
- 对齐 OTel GenAI 语义约定,并知道它至今全是 Development、改过多轮,需在采集层归一化
- 知道评测分数应作为遥测挂在 trace 上(
gen_ai.evaluation.*) - 原文取舍口径完整:脱敏 + 采样 + 哈希 + 保留期分层,高风险路由全采
- 采样策略区分头部/尾部采样,异常必采
- 能把 trace 与评测双向打通,并记录版本三元组