FDE W4D5 学习手册 · Trace 与成本
W4 Day5 · A 级(必须掌握,面试核心)· 4h · 学会给 Agent 装上"黑匣子":一次调用全程可观测、成本可归因、结果可评测
本日定位:Agent 是个"黑盒",没有 Trace 你永远不知道它为什么慢、为什么贵、为什么错。本日定义一套贯穿全链路的 Trace 字段与 Span 规范,并讲清它如何服务排错、成本、评测三件事。
学完能回答:① 一次 Agent 调用的 Trace 结构(固定字段 + 自定义 Span);② Trace 怎么用于排错、成本归因、评测。
使用方法:通读原理 → 重点背「面试话术」「易错点」→ 做自测清单 → 配合《FDE-W4D5-评测题.md》。选中不熟的词可标注(左下★重要 / 右下📌待查)。
候选人视角:36 岁,Java + 大数据 + 医疗保险。你的大数据背景在这里是加分项——Trace 本质是"结构化日志 + 指标 + 血缘",正是你熟悉的领域。
一、Trace 的本质:Agent 的黑匣子
Trace(追踪)是对一次完整请求从入口到出口的全过程记录,由一棵有序的 Span(跨度)树组成。对 Agent 而言,一次用户提问可能触发多次 LLM 调用、RAG 检索、工具调用、人工审批——Trace 把它们串成一条可回放的因果链。
1.1 为什么 Agent 特别需要 Trace
- 链路长、分支多:一个回答背后是 N 次 LLM + 检索 + 工具,出错难定位。
- 成本高且波动大:token 随检索结果、对话历史变化,必须按维度拆解。
- 评测依赖数据:离线评测、回归测试都要拿真实 Trace 当语料。
- 合规审计:医疗理赔要能复盘"当时模型看到了什么、人批了什么"。
Trace 不是"多打几行日志",而是结构化的、带父子关系的、可聚合的观测数据。它的价值在于既要能"看一条"(排错),也要能"看一片"(成本/质量趋势)。这正是可观测性三支柱(Logs/Metrics/Traces)里 Trace 的位面。
Trace 就是 Agent 的黑匣子:一次请求全程的 Span 树。没有它你不知道 Agent 为什么慢、为什么贵、为什么错。它既能单条回放排错,也能批量聚合做成本和评测分析。
① Trace 不等于"打印日志"——日志是扁平文本,Trace 是带 parent/child 关系的树,能还原因果。② 不要等出问题才想接 Trace,应在架构设计期就定好字段和 Span 规范。③ Trace 数据量大,要权衡"全量采样 vs 按比例采样",医疗高风险场景建议关键链路全量。
二、固定 Trace 字段:全链路统一埋点
无论底层用什么框架(LangChain/LangGraph/Spring AI),都应有一组统一、必填的顶层字段,保证跨系统可聚合、可比对。
| 字段 | 含义 | 用途 |
trace_id | 一次请求全局唯一 ID | 串联所有 Span,定位单次会话 |
user_id_hash | 用户的脱敏哈希(非明文) | 用户级成本/质量分析,保护隐私 |
tenant_id | 租户/机构 ID(多租户) | 按机构拆分成本与权限 |
model_provider | 模型厂商(如 qwen/deepseek) | 厂商维度成本/质量对比 |
model_name | 具体模型(如 qwen-max) | 模型维度归因 |
prompt_version | 提示词版本 | 提示迭代的 A/B 与回归 |
knowledge_version | 知识库版本 | RAG 效果归因 |
agent_version | Agent/工作流版本 | 整体迭代归因 |
tool_name | 调用的工具名 | 工具成功率/耗时分析 |
input_tokens | 输入 token 数 | 成本计算 |
output_tokens | 输出 token 数 | 成本计算 |
latency_ms | 耗时(毫秒) | 性能/SLA 监控 |
cost | 本次花费(金额) | 成本归因 |
status | 成功/失败 | 成功率监控 |
error_type | 错误类型 | 故障归类 |
版本字段(prompt/knowledge/agent_version)是隐藏重点:它们让"效果变化"可归因到"哪次改动"。比如某天拒赔率突增,一查是 knowledge_version 从 v3 升 v4 引入脏数据——没有版本字段你只能瞎猜。
固定字段是"全链路统一身份证":trace_id 串联、user_id_hash/tenant_id 做隐私与多租户拆分、model/prompt/knowledge/agent 四个 version 做归因、token/cost/latency/status/error 做成本与质量。版本字段最能体现代码素养。
① user_id_hash 一定要脱敏,不能直接存明文身份证/手机号,否则合规事故。② token 字段要在"单次 LLM 调用"粒度记录,而非只在最外层记总数,否则无法定位哪次调用最贵。③ cost 建议服务端按厂商单价计算后落库,别让前端传,避免被篡改且单价易变。
三、自定义 Span:把 Agent 的关键动作都埋进去
除了框架自动生成的 LLM Span,必须按业务手动埋点以下 Span 类型,才能覆盖 Agent 特有环节。
| Span 类型 | 代表动作 | 关键属性 |
llm.call | 一次大模型推理 | model, input/output tokens, latency, prompt_version |
rag.retrieve | 向量检索召回 | query, top_k, 命中数, knowledge_version, 命中得分 |
rerank.call | 重排序 | 模型, 重排前后 top 变化, latency |
agent.step | Agent 一个推理-决策步 | step_index, 选定工具, 思考摘要 |
tool.call | 工具/外部接口调用 | tool_name, 入参(脱敏), 出参摘要, 状态码 |
human.approval | 人工审批节点 | 审批人, 决定, 等待时长, 是否修改 |
eval.case | 评测用例执行 | case_id, 指标, 期望/实际, 评分 |
自定义 Span 把"Agent 专有动作"显性化。比如 human.approval Span 记录了"等了多久、人改了什么",这是纯 LLM 观测工具看不到的——而它恰恰是医疗理赔成本与风险的核心。埋点规范要进代码规范,而非临时打印。
除了框架自带的 llm.call,我们还要埋 rag.retrieve、rerank.call、agent.step、tool.call、human.approval、eval.case。尤其 human.approval 记录等待时长和人的决定,是医疗理赔成本和风险的关键观测点。
① 别只在最外层记一个总耗时,要细分到每个 Span,否则"慢"无从定位。② tool.call 的入参可能含敏感信息(身份证、病历),必须脱敏后再进 Trace。③ human.approval 的"等待时长"很重要——审批排队会直接拉高端到端延迟,是成本隐性来源。
四、一次 Agent 调用的 Trace 层级结构
Trace 是一棵树:根 Span 是本次请求,子 Span 按调用嵌套。
Trace (trace_id=T1, user_id_hash=U*, tenant_id=HospitalA)
└─ agent.step#1 # 规划:决定先检索
└─ rag.retrieve # 召回 8 条,knowledge_version=v4
└─ rerank.call # 重排后取 top3
└─ llm.call # 生成工具调用参数(qwen-max, p_v2)
└─ tool.call(查询医保目录) # 成功,12ms
└─ agent.step#2 # 规划:需人工审批
└─ human.approval # 等待 8min,人改金额 8w→5w,通过
└─ tool.call(放款) # 幂等键保护,成功
└─ llm.call # 生成最终答复(deepseek, p_v1)
Trace = 根Span(请求) → 嵌套 Span(agent.step → rag/rerank/llm/tool/human.approval) 每Span带固定字段 + 自定义属性
一次 Agent 调用的 Trace 是棵树:根是本次请求,下面是 agent.step(每步决策),再嵌套 rag.retrieve、rerank、llm.call、tool.call、human.approval。每个 Span 都带固定字段(token/cost/latency/status)。
① Span 必须有正确的 parent 关系,否则树会被拉平、因果丢失。② 同一 trace_id 下的 Span 要在不同服务间传播(用 W3C traceparent 头),分布式部署时最容易断链。③ 别把"并行分支"强行串成串行,会歪曲 latency 归因。
五、埋点落地:在哪插、怎么传上下文
5.1 上下文传播
Agent 常跨进程/跨服务(编排层、模型网关、工具服务),trace_id 与父 Span 上下文需通过请求头/上下文对象传递,保证 Span 能正确挂到同一棵树。
5.2 自动 vs 手动
| 方式 | 覆盖 | 说明 |
| 自动埋点 | llm.call(框架集成) | LangChain/LangGraph 与 Langfuse/OTel 集成自动出 LLM Span |
| 手动埋点 | rag/rerank/tool/human.approval/eval | 在业务代码里显式 start/end Span 并附属性 |
5.3 Java/Spring AI 视角
- 若用 Spring AI,可借助 Micrometer Tracing + OTel 自动出 Span。
- 工具调用处用
@Observed 或手动 tracer.nextSpan() 包裹。
- 大数据背景可复用你熟悉的"埋点 SDK + 采集 agent + 数仓"管线,Trace 进 OLAP 做聚合分析。
埋点的工程难点不在"写一行 start_span",而在上下文跨服务传播和属性规范化。建议封装一个内部 SDK,统一把固定字段自动注入每个 Span,业务只需填业务属性,杜绝"有人漏填 trace_id"。
埋点靠"上下文传播"把跨服务的 Span 挂到同一棵树:自动埋点覆盖 llm.call(框架集成),rag/rerank/tool/human.approval 要手动埋。建议封装内部 SDK 自动注入固定字段,避免有人漏填。
六、Trace 用于排错:从"慢/错"到"根因"
当一次调用异常,Trace 让你"不靠猜"地定位。
6.1 排错 checklist
- 用 trace_id 拉出完整 Span 树,看哪个 Span
status=failed 或 latency_ms 异常。
- 若是
rag.retrieve 命中数为 0 → 知识库/检索问题,查 knowledge_version。
- 若是
llm.call 慢 → 看 model_provider、是否排队、是否上下文过长(input_tokens 大)。
- 若是
tool.call 失败 → 看 error_type、入参脱敏副本、下游状态码。
- 若是
human.approval 等待极长 → 审批队列瓶颈,非模型问题。
真实案例:某次理赔答复"答非所问",查 Trace 发现 rag.retrieve 命中 0 条(knowledge_version 刚升 v4 把目录表误删),模型无知识只能硬编 → 回滚知识版本即恢复。版本字段在此立功。
排错的本质是"用结构化 Span 把猜测变验证"。没有 Trace,你只能问"模型是不是抽风了";有 Trace,你能精确到"第 3 步检索命中 0 条导致幻觉"。这把 MTTR(平均修复时间)从小时级降到分钟级。
排错就看 trace_id 拉出的 Span 树:哪个 Span 失败或慢。rag 命中 0→知识库问题;llm 慢→上下文过长或厂商排队;tool 失败→看 error_type;human.approval 久等→审批队列瓶颈。版本字段能秒定位"哪次改动引入的"。
① 只看最外层 status 不够,必须下钻到失败的具体 Span。② 排错若发现是 human.approval 慢,别去调模型——那是流程/人力问题。③ 入参脱敏副本要保留足够信息用于复盘,又不能含明文隐私,需平衡。
七、Trace 用于成本归因:钱花在哪一步
成本是 Agent 上线后的头号运营指标。Trace 的 token/cost 字段让成本可按维度下钻。
7.1 成本下钻维度
| 维度 | 分析价值 |
| tenant_id | 哪个机构最烧钱,是否要调价/限流 |
| model_provider / model_name | 贵模型是否必要,能否换便宜模型 |
| prompt_version | 提示改动是否让 token 变多 |
| tool_name / agent.step | 哪类工具/步骤最贵 |
| user_id_hash | 是否有异常高频用户(刷接口) |
7.2 成本公式与优化
单次成本 = Σ(各 llm.call 的 input_tokens × 输入单价 + output_tokens × 输出单价)
端到端成本 = 模型成本 + 检索/重排成本 + 工具调用成本 + 人工审批人力成本
- 降输入:压缩历史、精简检索片段、prompt 缓存。
- 降输出:结构化短输出、关思维链。
- 换模型:简单步用便宜小模型,仅关键步用大模型。
- 降人工:human.approval 等待与触发率直接关系人力成本。
成本归因常暴露"反直觉"事实:比如你以为贵在模型,Trace 一拉发现 human.approval 等待导致重试、或 rag 召回太多片段把 input_tokens 撑爆。把"端到端成本"拆成"模型+检索+工具+人工",才能精准优化。这也是面试官爱问的"怎么给老板算 Agent 账单"。
成本靠 Trace 的 token/cost 按 tenant/模型/prompt/tool 维度下钻。端到端成本=模型+检索+工具+人工审批。常见反直觉:真凶是 rag 召回太多撑爆 input_tokens,或 human.approval 等待引发重试。降输入、降输出、换便宜模型、降人工触发率。
① 别只算模型 API 账单,人工审批人力也是成本,human.approval 的等待时长×人力单价要计入。② 重试会放大成本——Trace 里重试次数要记,否则成本对不上。③ 缓存命中(prompt cache)要单独标记,否则把"免费"算成"付费"。
八、Trace 用于评测:从"跑过"到"跑对"
离线和在线评测都依赖 Trace 作为数据源。
8.1 评测类型与 Trace 的关系
| 评测 | Trace 作用 |
| 离线回归 | 用历史真实 Trace 当 case,新版本跑后对比指标 |
| 在线监控 | 实时聚合 status/error_type/latency 出质量看板 |
| 失败样本挖掘 | 筛 status=failed 或低分 eval.case 做归因 |
| A/B 对比 | 按 prompt_version / model_name 分组比对指标 |
8.2 eval.case Span 的价值
把每个评测用例也写成一个 eval.case Span,记录 case_id、指标、期望/实际、评分。这样"评测"本身也进入可观测体系,能和线上 Trace 用同一套工具分析。
评测不是"评完扔掉",而是把 Trace 变成可复用的质量资产。线上真实失败 Trace 沉淀为回归 case,版本升级自动跑;eval.case Span 让"评测"和"生产"用同一观测栈,避免两套体系对不齐。
Trace 是评测的燃料:历史真实 Trace 当回归 case,线上实时聚合出质量看板,失败 Trace 沉淀为评测集。eval.case Span 把"评测"也纳入可观测体系,和生产用同一套工具。
① 评测不能只用"合成数据",要混入线上真实 Trace,否则测不出真实分布的问题。② eval.case 评分要可解释,别只存总分,期望/实际都要留,方便定位错在哪步。③ 评测指标(如任务完成率)要能从 Trace 自动算,别靠人工标注每条。
九、技术选型:Langfuse / OpenTelemetry / LangChain 回调
| 工具 | 定位 | 适用 |
| Langfuse | LLM/Agent 专用观测平台,自带 Trace 可视化、评测、Prompt 管理 | 快速给 Agent 接上专业观测,开箱即用 |
| OpenTelemetry (OTel) | 通用可观测性标准,AI Agent 可观测性有官方指南 | 要和标准 APM(Prometheus/Jaeger)打通、不锁定 |
| LangChain Callbacks | LangChain 的回调机制,流式与 Span 钩子 | 在 LangChain 代码里细粒度埋点/流式 |
推荐组合:用 Langfuse 做 Agent 专属观测与评测(最快出效果);底层走 OpenTelemetry 标准把 Span 导出到统一 APM,避免厂商锁定;LangChain 侧用 Callbacks 做流式与细粒度钩子。你的大数据背景可把 Trace 旁路进数仓做自定义聚合。
选型要平衡"开箱即用"和"不锁定"。Langfuse 专而快,OTel 通用不锁,两者可通过 OTel 导出器打通。关键决策:是否要把 LLM 观测并入公司统一可观测平台(通常要,因为 SRE 团队只认 OTel/Jaeger)。
推荐 Langfuse(Agent 专用、开箱即用)+ OpenTelemetry(通用标准、不锁定、打通公司 APM)+ LangChain Callbacks(流式与细粒度钩子)。大数据背景还能把 Trace 旁路进数仓做自定义聚合。
① 不要"Langfuse 和 OTel 各接一套"导致数据双写不一致,应 OTel 为底座、Langfuse 做上层。② Langfuse 默认采样/存储策略要看清,医疗数据可能涉及合规存储地域。③ 别把 Trace 当永久存储,要设 TTL 和归档,否则成本爆炸。
十、流式输出与回调:Trace 与体验的平衡
Agent 常用流式(streaming)提升体验,但流式下 Trace 的 token/latency 统计要特殊处理。
10.1 流式下的观测要点
- 首 token 延迟(TTFT):用户感知的"快不快",单独记一个 Span 指标。
- 完整 latency:从请求到最后一个 token,流式不改变总耗时,只是分段返回。
- token 统计:流式结束才能拿到完整 output_tokens,Span 在 end 时再落地。
- 回调钩子:LangChain 的
streaming + callbacks 可在 on_llm_new_token 里做增量处理,但别在钩子里做重逻辑(阻塞流)。
流式是"体验优化"不是"性能优化"——总 latency 不变,只是用户更早看到内容。Trace 要把 TTFT 单列,因为它才是用户体感指标;同时流式下 token 统计必须等结束,避免"边流边算"导致成本少算。
流式提升体感但不降总耗时。Trace 要单列 TTFT(首 token 延迟)作为用户体感指标;output_tokens 必须等流结束才落库,否则成本少算。LangChain 用 callbacks 钩子做增量处理,但钩子里别写重逻辑。
① 把"流式"误当"更快"是常见误解,它只改善体感、不改善总延迟。② 流式下若在每个 on_llm_new_token 做重计算会拖慢流、甚至 OOM,钩子要轻。③ output_tokens 在流未结束时报 0 是 bug,必须在 end 事件结算。
十一、特药理赔 Agent 的 Trace 实践
场景:用户提交特药理赔,Agent 完成抽取→合规校验→规则判断→人工审批→放款→答复。一次请求背后是多次 LLM + 检索 + 工具 + 1 次人工审批。
11.1 落地要点
- 顶层字段:trace_id 贯穿,tenant_id=医院,user_id_hash 脱敏,四个 version 全记录。
- Span:每个 agent.step 一个父 Span,下挂 rag.retrieve / rerank / llm.call / tool.call,human.approval 单独 Span 记等待时长与决定。
- 成本看板:按 tenant 出账单,按 model 看贵不贵,按 human.approval 看人力。
- 排错:答复错→查 rag 命中;慢→查 human.approval 等待或 llm 排队;贵→查 input_tokens(检索片段过多)。
对医疗理赔,Trace 还是合规证据链:监管要能复盘"这次拒赔,模型当时检索到了什么知识、人批了什么、用的哪个知识库版本"。固定字段 + human.approval Span 正好构成这份证据。
特药理赔 Agent 的 Trace:顶层四个 version 全记,每步一个 agent.step 父 Span 挂 rag/rerank/llm/tool,human.approval 记等待和决定。它不仅是排错工具,还是给监管的合规证据链。
十二、面试达标线①:一次 Agent 调用的 Trace 结构(字段 + Span)
| 层 | 内容 |
| 顶层固定字段 | trace_id / user_id_hash / tenant_id / model_provider / model_name / prompt_version / knowledge_version / agent_version / tool_name / input_tokens / output_tokens / latency_ms / cost / status / error_type |
| Span 树 | 根=请求;agent.step 为决策步;下挂 llm.call / rag.retrieve / rerank.call / tool.call / human.approval;eval.case 用于评测 |
| 关键属性 | 每 Span 带固定字段子集 + 业务属性(如 rag 的命中数、human 的等待时长) |
一次 Agent 调用的 Trace = 一棵 Span 树(根是请求,agent.step 为决策步,下挂 llm/rag/rerank/tool/human.approval)+ 一组统一固定字段(trace_id 串联、四个 version 归因、token/cost/latency/status 算成本质量)。
十三、面试达标线②:Trace 怎么用于排错 / 成本归因 / 评测
- 排错:用 trace_id 拉 Span 树,下钻到失败/异常慢的具体 Span;rag 命中 0→知识库问题,llm 慢→上下文/厂商,tool 失败→error_type,human.approval 久→流程瓶颈;版本字段秒定位"哪次改动引入"。
- 成本归因:token/cost 按 tenant/模型/prompt/tool 维度下钻;端到端成本=模型+检索+工具+人工;常见反直觉根因(检索片段过多撑爆 input、人工等待引发重试)。
- 评测:历史真实 Trace 当回归 case,线上聚合出质量看板,失败 Trace 沉淀为评测集,eval.case Span 让评测与生产同栈。
Trace 三用:排错(下钻 Span 树定位根因)、成本(按维度下钻+端到端=模型+检索+工具+人工)、评测(真实 Trace 当 case、eval.case 同栈)。一句话:看一条排错,看一片算钱与评测。
十四、W4D5 自测清单
- 能说清 Trace 与"普通日志"的区别(树状、带父子关系、可聚合)。
- 能背出 15 个固定字段,并说清每类字段的用途(串联/归因/成本/质量)。
- 能解释为什么 prompt_version / knowledge_version / agent_version 是隐藏重点。
- 能列出 7 个自定义 Span 及其关键属性。
- 能画出一次 Agent 调用的 Trace 层级结构(根→agent.step→子 Span)。
- 能讲清埋点的上下文传播与"自动+手动"分工。
- 能说清用 Trace 排错的标准流程(下钻到具体失败 Span)。
- 能解释成本归因的维度与端到端成本构成,点出"人工审批也是成本"。
- 能说清 Trace 如何服务评测(回归 case / 质量看板 / eval.case)。
- 能对比 Langfuse / OpenTelemetry / LangChain Callbacks 的选型。
- 能讲清流式下 Trace 的处理(TTFT 单列、token 流结束结算)。
- 能把特药理赔 Agent 的 Trace 实践讲成一段(达标线①②)。
十五、高频面试题速记卡
Q:Trace 和普通日志有什么不同?
Trace 是带父子关系的 Span 树,能还原因果、可聚合;日志是扁平文本,难定位链路。
Q:15 个固定字段里哪几个最体现素养?
四个 version(prompt/knowledge/agent/model):让"效果变化"可归因到"哪次改动"。
Q:自定义 Span 为什么要有 human.approval?
记录审批等待时长与决定,是医疗理赔成本与风险的关键观测点,纯 LLM 工具看不到。
Q:用 Trace 怎么排错"答非所问"?
拉 Span 树看 rag.retrieve 命中数;命中 0 即知识库/检索问题,再查 knowledge_version。
Q:Agent 成本只算模型 API 吗?
不是,端到端=模型+检索+工具+人工审批人力;human.approval 等待也计入成本。
Q:Trace 怎么服务评测?
历史真实 Trace 当回归 case,线上聚合质量看板,失败 Trace 沉淀评测集,eval.case 同栈。
Q:Langfuse 和 OpenTelemetry 怎么选?
Langfuse 专而快(Agent 观测/评测),OTel 通用不锁(打通公司 APM);OTel 为底座、Langfuse 上层。
Q:流式输出会让总 latency 变短吗?
不会,只改善体感;Trace 单列 TTFT,output_tokens 必须等流结束才结算。
Q:user_id_hash 为什么必须脱敏?
避免存明文身份证/病历,符合医疗数据合规;同时仍能做用户级成本/质量分析。
Q:一次理赔 Agent 调用的 Trace 一句话?
根是请求,agent.step 为决策步,下挂 llm/rag/rerank/tool,human.approval 记等待与决定,四 version 全记录。
FDE W4D5 学习手册 · Trace 与成本(面试级)· 配合《FDE-W4D5-评测题.md》自测
延伸学习资源(中文 / 非 OpenAI):
① Langfuse 官网:langfuse.com——LLM/Agent 专用观测与评测平台,Trace 可视化、Prompt 管理、评测集一体。
② OpenTelemetry 官方博客《AI Agent 可观测性》:opentelemetry.io/blog/2025/ai-agent-observability——用 OTel 标准做 Agent 全链路追踪的官方指南。
③ LangChain 流式与回调文档:python.langchain.com/docs/how_to/streaming——streaming + callbacks 的细粒度钩子用法,配合 Trace 统计 Token/延迟。
📌 待查★ 重要