如何为多步骤 LLM 工作流设计端到端 Trace?
简化版
LLM Trace 的目标是还原一次任务如何经过输入治理、检索、模型、工具、校验、重试和 Fallback,而不是把所有 Prompt 塞进日志。根 Trace 表示一个用户任务,每次实际尝试建独立 Span;Span 记录版本、阶段耗时、Token、结果码和输入输出的受控引用。
异步队列和人工审批要传播 trace_id、span_id/link、operation_id 与 baggage 中的低敏路由信息;重试是新 Span,不覆盖旧 Span。采样采用错误/高风险全保留、普通成功按比例或尾部采样,并把敏感原文放受限证据库。llm-observability-fields 讲字段字典,本题重点是 Span 拓扑、上下文传播与采样完整性。
详细版
先按责任边界划 Span:网关、上下文构造、Retriever、Reranker、每次模型调用、工具、验证器。并行工具是兄弟 Span,Fallback 是同一逻辑阶段下的新 attempt,缓存命中也要有短 Span 说明为什么绕过模型。
root task
├─ guard
├─ retrieve -> rerank
├─ model attempt=1 (timeout)
├─ model attempt=2 release=fallback
├─ tool operation_id=...
└─ validate -> response stream
Trace 不等于审计日志:Trace 为诊断可采样,审计证据通常不可按普通比例丢弃。验收时用故障注入验证跨队列、回调和流式连接仍能串联,父子时长合理,失败 attempt 没有被成功结果覆盖,且无 Prompt/Secret 意外进入属性。
完整版教学
字段决定“记录什么”,Trace 设计决定“这些事件如何组成因果链”;没有因果关系,一堆完整日志仍无法解释一次失败。
一、Trace 的根应该是什么
根 Trace 应对应用户可理解的一个逻辑任务,而非单个 HTTP 请求。一次任务可能经历客户端重试、多个模型调用和异步工具,若每段各自创建无关联 Trace,就无法计算端到端成功与成本。
根属性保存任务类型、租户受控 ID、release_id 和最终状态。高基数 request_id 用于查询,不直接作为指标标签。
二、Span 边界怎样划分
Span 应对应有独立所有者、耗时和失败语义的组件。一次模型调用、一次检索、一次工具执行和一次校验分别建 Span,而不是给每个内部函数都打点。
| Span | 关键事件 | 结束状态 |
|---|---|---|
| retrieval | query built、candidates | hit/empty/error |
| model_call | queued、first token、finish | stop/length/error |
| tool_call | validated、sent、committed | success/failed/unknown |
| validation | schema、grounding、安全 | pass/block/unavailable |
边界太粗无法定位,太细则成本和噪声过高。
三、重试与 Fallback 如何表示
每次网络尝试建立新的 Span,带 attempt、provider、release 与触发原因。不能复用同一个 Span 并把最终状态改成成功,否则会丢失第一次超时和真实成本。
Fallback Span 与主尝试共享逻辑 operation,但使用不同 release_id。根 Trace 汇总全部 attempt 的 Token 和费用,才能计算每次成功任务成本。
四、异步消息如何传播上下文
生产者把标准 Trace Context 注入消息 Header,消费者提取后创建子 Span。若消息在很久以后由独立工作流处理,可用 Span Link 表达因果关系,避免伪造一个持续数小时的同步父子调用。
enqueue span --context/link--> worker span --link--> callback span
消息重投递沿用同一 message_id,但每次处理建新 attempt Span。Header 只携带必要 ID,不能传播 Prompt、密钥或大块业务状态。
五、并行分支与聚合怎样画
多个独立工具并行调用时使用兄弟 Span,聚合节点记录等待哪些分支、哪些超时以及采取何种部分成功策略。简单串成父子会让时间轴误导为顺序执行。
总耗时也不能把并行 Span 时长直接相加。墙钟延迟看关键路径,资源成本才对各调用求和;Trace 分析需明确两种口径。
六、流式响应记录哪些事件
模型 Span 记录请求发送、首个有效内容 token、最后 token、Finish Reason 和上游取消;响应 Span 记录首字节成功写给客户端、背压和断连。服务端拿到 token 不等于客户端已收到。
工具等待期间不应算进模型 TPOT。可把工具阶段拆为独立 Span,并同时报告端到端相邻事件间隔,避免指标口径混淆。
七、内容怎样最小化
Span 属性优先存 Token 数、内容哈希、Prompt/文档版本、风险分类和受控 artifact_id。原始 Prompt、输出、工具参数或文档正文只在合法必要的受限证据库中保存。
脱敏要在数据离开业务进程前完成。Trace 后端访问也需权限与审计,调试开关必须自动过期。
八、采样策略如何兼顾成本与问题发现
头部采样在请求开始时决定,简单便宜但可能丢掉后来才出现的错误;尾部采样在知道状态、延迟和风险后决定,更适合保留失败与长尾,但需要缓存 Trace 数据。
可采用:安全/写操作与错误 100% 保留,P99 和未知状态高比例保留,普通成功按 1% 抽样。审计事件另走不可随诊断采样丢弃的通道。
九、Baggage 为什么要谨慎
Baggage 会随跨服务请求传播,适合低敏、低基数的租户等级、实验 ID 或 release_id。把用户文本、完整 tenant 权限或 Secret 放入 Baggage,会在每个下游复制并扩大泄露面。
传播字段要有白名单、大小上限和信任来源。下游不能把客户端自带 Baggage 直接当授权依据。
十、怎样从 Trace 生成指标
从 Span 聚合 QPS、错误、阶段时长、Fallback 率、工具未知状态和 Token 成本;标签只选模型、区域、稳定错误码等受控维度。Trace ID、Prompt Hash 等高基数字段留作查询。
若 8 秒总延迟中 Retriever 0.3 秒、Reranker 0.7 秒、排队 1.5 秒、Decode 5 秒、校验 0.5 秒,优化优先级明显不同于只有 total_ms=8000 的日志。
十一、如何验证 Trace 没有断链
注入 Provider 超时后 Fallback、队列重投递、并行工具部分失败、客户端取消和审批回调。检查根 Trace 是否包含所有 attempt、最终状态、关键路径与总成本。
监控 orphan span、缺失 release_id、负时长、时钟偏差、采样丢失和属性超限。部署新组件时用契约测试确认它正确传播上下文。
十二、常见误区与追问
- 误区:一次 HTTP 就是一条完整 Trace。 异步任务和重试可能跨多个请求,根应对应逻辑任务。
- 误区:重试成功后覆盖第一次失败即可。 会丢失故障、成本与重试放大证据。
- 误区:把所有原文存进 Span 最利于排障。 会扩大隐私与安全风险,应保存引用和受控采样。
- 误区:并行 Span 的时长应全部相加得到延迟。 墙钟延迟看关键路径,成本才对资源求和。
- 误区:Trace 可以替代审计日志。 Trace 可采样,审计对完整性和防篡改要求不同。
- 追问:队列消费者算子 Span 还是 Link? 短生命周期同一操作可继承父子;长延迟独立工作流更适合 Link。
- 追问:怎样保留罕见错误? 使用尾部采样规则按错误、风险和长尾时延优先保留。
十三、加强记忆
端到端 Trace 记“任务为根、责任为 Span、重试不覆盖、异步靠 Context/Link”。并行看关键路径,成本汇总全部 attempt;流式区分模型首 token 与客户端写出。内容只存最小字段和受控引用,错误与高风险优先尾采样,审计证据走独立通道。最终用故障注入证明链路不断、口径不乱、敏感数据不泄露。
项目实战落地
项目里怎么做的
《AI Agent智能会议纪要辅助系统》没有接入 Trace 系统,但一次任务里的每一步都能串起来,靠的是三张表和两个字段:
ai_call_log 每次模型调用一行
request_id AIC 前缀加随机串,唯一,标识这一次调用
call_type TRANSCRIPTION 转写、SPEAKER_MATCH 说话人匹配、MINUTES_CHUNK 纪要分段、
MINUTES_MERGE 纪要合并、AGENT_REVIEW 自检审查、AGENT_REFINE 自检重写
biz_id 按 call_type 指向不同的业务记录:
转写和说话人匹配 → 转写任务,纪要分段和合并 → 纪要,自检审查和重写 → 自检运行
agent_run 一次纪要自检一行:状态、轮次、修订稿、开始和结束时间
agent_step 自检的每一步一行:步骤类型(审查、重写、汇总)、状态、耗时
自检审查和自检重写两类日志的 biz_id 存的就是自检运行的主键,和 agent_step.run_id 是同一个值,所以一次自检的步骤和这次自检里的每一次模型调用,都能按运行 ID 对上。纪要生成、纪要自检、说话人匹配三处文本模型调用走同一个 invoke_json,转写走转写服务,两条路径成功和失败都写一条日志,失败的 usage_json 为空。
运行观测页按同一套关系判断员工能看哪些日志:先查出员工名下的转写任务、纪要和自检运行的 ID,再按 call_type 选对应的集合,比对日志的 biz_id,落在集合里才算他的调用。
《AI企业流程编排系统》的面试文档把它叫「三级可观测」:workflow_run 记一次运行的初始输入、最终上下文、状态和耗时;workflow_run_step 按执行顺序记每个节点执行前的完整上下文、节点输出、状态、耗时和错误原因,节点名称和类型存执行时的快照;ai_usage_log 记大模型节点的每次模型调用。前两层靠运行 ID 关联;模型调用日志带的是流程 ID 和节点 ID,没有运行 ID,要对到某一次运行只能靠流程、节点和调用时间。文档给的排查顺序是:先看运行记录的整体状态,再按步骤日志定位出错的节点,大模型节点再结合调用日志看模型调用详情。
为什么这样取舍
biz_id按call_type解释。 一张日志表承接转写、纪要、自检三种业务,用「类型 + 业务 ID」两个字段就能回到具体的那条业务记录,不用为每种业务加一列。- 运行和步骤单独成表。 自检是一个多轮的反思环,步骤的类型、状态和耗时是按运行看的;模型调用日志只记调用本身,两张表各管一层,靠运行 ID 对上。
- 归属判断复用同一关系。 员工能看哪些日志,不在日志表里另存用户,而是顺着
biz_id回到业务表看是谁发起的,权限和业务数据保持一致。
和正文说的端到端 Trace 相比,这套做法串得起「一条业务记录下的所有调用」,但没有父子 Span 和跨服务传播;链路本身怎么设计 Span,还是看正文。检索链路那一段记什么,见「RAG 链路 Trace 应该记录哪些信息?」。
面试官还会追问
- 运行观测页的「最近失败记录」把失败的模型调用和失败的自检运行合在一张表里,两类记录怎么排序、最多保留几条?
- 自检运行的「平均轮次」为什么只统计至少完成过一轮审查的运行?
学完《AI Agent智能会议纪要辅助系统》,上面这些追问你都会迎刃而解。