<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tracing on Weiuou的博客</title><link>https://blog.weiuou.top/tags/tracing/</link><description>Recent content in Tracing on Weiuou的博客</description><image><title>Weiuou的博客</title><url>https://blog.weiuou.top/avatar.png</url><link>https://blog.weiuou.top/avatar.png</link></image><generator>Hugo</generator><language>zh-cn</language><copyright>Weiuou</copyright><lastBuildDate>Sun, 05 Jul 2026 18:00:00 +0800</lastBuildDate><atom:link href="https://blog.weiuou.top/tags/tracing/index.xml" rel="self" type="application/rss+xml"/><item><title>Agent Tracing：理解 Agent 执行过程的可观测性</title><link>https://blog.weiuou.top/posts/agent-tracing/</link><pubDate>Wed, 01 Jul 2026 19:33:24 +0800</pubDate><guid>https://blog.weiuou.top/posts/agent-tracing/</guid><description>Agent workflow 不再只是一次模型调用，而是一条由模型生成、工具调用、上下文更新、guardrail 和 handoff 组成的执行链路。Tracing 可以把这条链路记录成可观察、可调试的执行轨迹。</description><content:encoded><![CDATA[<h2 id="本文结论">本文结论</h2>
<ul>
<li>Agent tracing 是对一次 Agent workflow 的结构化执行记录，不是普通日志的简单加长版。</li>
<li>Trace 记录端到端任务，span 记录一段操作，event 记录某个时间点发生的事情。</li>
<li>对 Agent 来说，tracing 的价值在于解释“为什么走到这个结果”，尤其适合分析工具调用、handoff、guardrail 和上下文问题。</li>
<li>Eval 告诉你任务是否成功，trace 告诉你成功或失败是怎么发生的。</li>
</ul>
<h2 id="适合谁读">适合谁读</h2>
<ul>
<li>正在开发 Agent workflow、工具调用系统或多步骤 LLM 应用的人。</li>
<li>已经遇到“最终答案错了，但不知道哪一步错了”的开发者。</li>
<li>想理解 Agent eval、trace replay 和可观测性之间关系的人。</li>
</ul>
<p>随着 LLM 应用从简单对话逐渐发展到 Agent workflow，系统的复杂度也在明显增加。</p>
<p>一个普通 Chatbot 通常可以被理解成一次模型调用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">用户输入 -&gt; LLM -&gt; 模型输出
</span></span></code></pre></div><p>但 Agent workflow 往往不是这样。它可能包含多轮模型生成、工具调用、上下文更新、规则检查、任务转交、失败重试，甚至还可能需要人工确认。</p>
<p>一个典型 Agent run 可能更像这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">用户输入任务
</span></span><span class="line"><span class="cl">  -&gt; Agent 接收任务
</span></span><span class="line"><span class="cl">  -&gt; LLM 生成下一步动作
</span></span><span class="line"><span class="cl">  -&gt; 调用工具
</span></span><span class="line"><span class="cl">  -&gt; 工具返回结果
</span></span><span class="line"><span class="cl">  -&gt; LLM 基于结果继续生成
</span></span><span class="line"><span class="cl">  -&gt; 再次调用工具
</span></span><span class="line"><span class="cl">  -&gt; 触发 guardrail 检查
</span></span><span class="line"><span class="cl">  -&gt; 最终输出结果
</span></span></code></pre></div><p>这意味着 Agent 的执行过程不再是一个单点动作，而是一条由多个操作组成的链路。</p>
<p>如果这条链路失败，只看最终输出通常无法判断问题在哪里。失败可能来自模型理解错误、工具参数错误、工具执行失败、上下文丢失、guardrail 拦截、状态流转异常，也可能是多个问题叠加。</p>
<p>这就是 Agent 系统需要 tracing 的原因。</p>
<p>Tracing 的作用，是记录一次 Agent run 中发生的关键操作，并把这些操作组织成一条可观察、可回放、可调试的执行轨迹。</p>
<h2 id="为什么-agent-run-需要-tracing">为什么 Agent Run 需要 Tracing</h2>
<p>Agent run 需要 tracing，核心原因是：Agent 的失败往往不是单点失败，而是链路失败。</p>
<p>在传统 Web 服务中，一次请求失败，通常可以通过日志、错误码、调用栈、metrics 来定位问题。例如接口返回 500，可以查看异常堆栈；数据库查询慢，可以看 SQL 耗时；服务之间调用失败，可以看 RPC 日志。</p>
<p>但 Agent workflow 的问题会更绕一点。</p>
<p>例如，最终结果错误时，真正原因可能是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">LLM 一开始误解了用户任务
</span></span><span class="line"><span class="cl">LLM 选择了错误工具
</span></span><span class="line"><span class="cl">function call 参数不合法
</span></span><span class="line"><span class="cl">工具执行失败
</span></span><span class="line"><span class="cl">工具返回结果太长，关键信息被截断
</span></span><span class="line"><span class="cl">模型没有正确理解 observation
</span></span><span class="line"><span class="cl">上下文中混入了错误信息
</span></span><span class="line"><span class="cl">状态机提前停止
</span></span><span class="line"><span class="cl">Agent 在错误步骤中反复循环
</span></span><span class="line"><span class="cl">guardrail 阻止了某个操作
</span></span><span class="line"><span class="cl">handoff 转交给了不合适的 agent
</span></span></code></pre></div><p>这些问题只看最终回答很难判断。Tracing 的价值就在于，它可以把一次 Agent run 展开成多个可观察步骤，让开发者知道：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">这次任务经历了哪些步骤？
</span></span><span class="line"><span class="cl">每一步输入是什么？
</span></span><span class="line"><span class="cl">每一步输出是什么？
</span></span><span class="line"><span class="cl">哪一步失败了？
</span></span><span class="line"><span class="cl">失败原因是什么？
</span></span><span class="line"><span class="cl">模型为什么调用这个工具？
</span></span><span class="line"><span class="cl">工具返回后模型又做了什么？
</span></span><span class="line"><span class="cl">最终结果是如何产生的？
</span></span></code></pre></div><p>所以，Agent tracing 通常服务于几个目标：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">可回放：复现一次 Agent run 的执行过程
</span></span><span class="line"><span class="cl">可观测：看到每一步耗时、状态、输入输出和错误
</span></span><span class="line"><span class="cl">可调试：定位失败发生在哪个环节
</span></span><span class="line"><span class="cl">可评测：分析 Agent 成功率和失败类型
</span></span><span class="line"><span class="cl">可优化：发现性能瓶颈、成本瓶颈和行为问题
</span></span></code></pre></div><p>Tracing 不是普通日志的替代品，而是 Agent workflow 的执行记录结构。普通日志更像散点记录，trace 则更像一棵执行树。</p>
<h2 id="trace-是什么">Trace 是什么</h2>
<p>Trace 表示一次完整的端到端操作。</p>
<p>在 Agent 场景中，可以把一个 trace 理解为一次完整的 Agent run。例如用户发起任务：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">帮我分析这个项目的代码结构，并找出潜在问题
</span></span></code></pre></div><p>从 Agent 接收这条任务开始，到它完成多轮模型调用、工具调用、上下文处理，并最终输出结果为止，这整个过程就是一个 trace。</p>
<p>Trace 通常会包含一些全局属性：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">workflow_name
</span></span><span class="line"><span class="cl">trace_id
</span></span><span class="line"><span class="cl">group_id
</span></span><span class="line"><span class="cl">metadata
</span></span><span class="line"><span class="cl">started_at
</span></span><span class="line"><span class="cl">ended_at
</span></span><span class="line"><span class="cl">status
</span></span></code></pre></div><p><code>workflow_name</code> 表示逻辑上的 workflow 或应用名称，例如 <code>Code generation</code>、<code>Customer service</code>、<code>Data analysis</code>、<code>Research assistant</code>。</p>
<p><code>trace_id</code> 是这次 trace 的唯一标识。</p>
<p><code>group_id</code> 可以用来关联多个 trace。例如同一个聊天线程里，用户可能连续发起多次 Agent run。每次 run 都是一个独立 trace，但它们可以通过同一个 <code>group_id</code> 关联起来。</p>
<p>可以这样理解：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">一个 conversation / thread
</span></span><span class="line"><span class="cl">  -&gt; 可能包含多个 trace
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">一个 trace
</span></span><span class="line"><span class="cl">  -&gt; 表示一次完整 Agent run
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">一个 trace
</span></span><span class="line"><span class="cl">  -&gt; 由多个 span 组成
</span></span></code></pre></div><p>Trace 关注的是整体链路，而不是某一个具体步骤。</p>
<h2 id="span-是什么">Span 是什么</h2>
<p>Span 是 trace 中的一个具体工作单元。它表示一个有开始时间和结束时间的操作。</p>
<p>在 Agent workflow 中，以下操作都可以是 span：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">一次 agent 运行
</span></span><span class="line"><span class="cl">一次 LLM generation
</span></span><span class="line"><span class="cl">一次 function tool call
</span></span><span class="line"><span class="cl">一次 guardrail 检查
</span></span><span class="line"><span class="cl">一次 handoff
</span></span><span class="line"><span class="cl">一次 speech-to-text
</span></span><span class="line"><span class="cl">一次 text-to-speech
</span></span><span class="line"><span class="cl">一次自定义业务操作
</span></span></code></pre></div><p>Span 的关键特征是：它有持续时间。</p>
<p>也就是说，它不是一个瞬间发生的事件，而是一个从开始到结束的操作。例如，一次 LLM 调用可以是一个 span：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Span: generation
</span></span><span class="line"><span class="cl">  started_at: ...
</span></span><span class="line"><span class="cl">  ended_at: ...
</span></span><span class="line"><span class="cl">  input: messages, tools, model config
</span></span><span class="line"><span class="cl">  output: model response, tool calls
</span></span><span class="line"><span class="cl">  status: ok
</span></span></code></pre></div><p>一次工具调用也可以是一个 span：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Span: function
</span></span><span class="line"><span class="cl">  started_at: ...
</span></span><span class="line"><span class="cl">  ended_at: ...
</span></span><span class="line"><span class="cl">  input: tool name, tool arguments
</span></span><span class="line"><span class="cl">  output: tool result
</span></span><span class="line"><span class="cl">  status: ok / error
</span></span></code></pre></div><p>这里有一个容易混淆的点：function call result 通常不是单独的 span，而是 function call span 的 output。</p>
<p>也就是说：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">执行工具这个过程 = span
</span></span><span class="line"><span class="cl">工具执行结果 = span.output
</span></span></code></pre></div><p>同理：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">LLM generation 这个过程 = span
</span></span><span class="line"><span class="cl">LLM response = span.output
</span></span></code></pre></div><p>只有当响应处理本身足够复杂，例如 JSON 解析、参数校验、结果压缩、错误恢复，才有必要把这些处理步骤继续拆成新的 span。</p>
<h2 id="span-的父子关系">Span 的父子关系</h2>
<p>Span 不只是平铺记录，它们通常有父子关系。</p>
<p>例如，一次 Agent run 可以包含多次 LLM generation 和 function call：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Trace: Agent workflow
</span></span><span class="line"><span class="cl">  Span: agent
</span></span><span class="line"><span class="cl">    Span: generation
</span></span><span class="line"><span class="cl">    Span: function.lookup_order
</span></span><span class="line"><span class="cl">    Span: generation
</span></span><span class="line"><span class="cl">    Span: function.send_email
</span></span><span class="line"><span class="cl">    Span: generation
</span></span></code></pre></div><p>这表示：</p>
<ul>
<li>整个 workflow 是一个 trace</li>
<li>agent 的运行过程是一个大的 span</li>
<li>每次模型生成、工具调用都是它下面的子 span</li>
</ul>
<p>如果发生 handoff，结构可能会更复杂：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Trace: Customer service workflow
</span></span><span class="line"><span class="cl">  Span: agent.support_agent
</span></span><span class="line"><span class="cl">    Span: generation
</span></span><span class="line"><span class="cl">    Span: handoff.to_refund_agent
</span></span><span class="line"><span class="cl">      Span: agent.refund_agent
</span></span><span class="line"><span class="cl">        Span: generation
</span></span><span class="line"><span class="cl">        Span: function.create_refund
</span></span></code></pre></div><p>这种层级关系很重要。它可以帮助我们理解：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">某次工具调用属于哪次 agent run
</span></span><span class="line"><span class="cl">某次模型生成是否发生在 handoff 之前
</span></span><span class="line"><span class="cl">某个错误是父流程导致的，还是子流程内部导致的
</span></span><span class="line"><span class="cl">一次长 workflow 中各阶段的耗时分布
</span></span></code></pre></div><p>Span 通常通过 <code>trace_id</code> 归属于某个 trace，并通过 <code>parent_id</code> 指向父 span。</p>
<h2 id="event-是什么">Event 是什么</h2>
<p>Event 表示某个 span 内部发生的瞬时事件。</p>
<p>它和 span 的区别是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Span 有开始和结束，表示一段操作
</span></span><span class="line"><span class="cl">Event 是某个时间点发生的事情
</span></span></code></pre></div><p>在 Agent workflow 中，event 适合记录这些内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">模型生成了 tool call
</span></span><span class="line"><span class="cl">JSON 解析失败
</span></span><span class="line"><span class="cl">开始重试
</span></span><span class="line"><span class="cl">请求用户授权
</span></span><span class="line"><span class="cl">用户批准了工具调用
</span></span><span class="line"><span class="cl">上下文窗口超限
</span></span><span class="line"><span class="cl">状态发生跳转
</span></span><span class="line"><span class="cl">流式输出收到一个 chunk
</span></span><span class="line"><span class="cl">用户中断了任务
</span></span></code></pre></div><p>例如，在一次 LLM generation span 中，可以记录一个 event：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;tool_call_generated&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-07-01T10:00:00Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;tool_name&#34;</span><span class="p">:</span> <span class="s2">&#34;search_docs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;step&#34;</span><span class="p">:</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这里的含义是：在这次模型生成过程中，模型在某个时间点生成了一个 tool call。</p>
<p>但真正执行这个工具，则应该是另一个 span。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">模型决定调用某个工具 = event
</span></span><span class="line"><span class="cl">系统真正执行这个工具 = span
</span></span></code></pre></div><p>因为“决定调用工具”是一个瞬间动作，而“执行工具”有开始、结束、耗时、输入、输出和错误状态。</p>
<h2 id="attribute-是什么">Attribute 是什么</h2>
<p>Attribute 是附加在 trace、span 或 event 上的结构化元数据。</p>
<p>它通常用于描述一个操作的属性，方便后续过滤、检索、聚合和分析。</p>
<p>常见 attribute 包括：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">step
</span></span><span class="line"><span class="cl">model
</span></span><span class="line"><span class="cl">tool_name
</span></span><span class="line"><span class="cl">retry_count
</span></span><span class="line"><span class="cl">status
</span></span><span class="line"><span class="cl">error_type
</span></span><span class="line"><span class="cl">input_tokens
</span></span><span class="line"><span class="cl">output_tokens
</span></span><span class="line"><span class="cl">duration_ms
</span></span><span class="line"><span class="cl">prompt_version
</span></span><span class="line"><span class="cl">tool_schema_version
</span></span></code></pre></div><p>例如，一个 LLM generation span 可以有这些 attributes：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;generation&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;model&#34;</span><span class="p">:</span> <span class="s2">&#34;model-name&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;step&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;input_tokens&#34;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;output_tokens&#34;</span><span class="p">:</span> <span class="mi">300</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;finish_reason&#34;</span><span class="p">:</span> <span class="s2">&#34;tool_calls&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>一个 function call span 可以有这些 attributes：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;function.lookup_order&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;tool_name&#34;</span><span class="p">:</span> <span class="s2">&#34;lookup_order&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;step&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;retry_count&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;ok&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>可以这样区分：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Trace 适合记录“整次 workflow”
</span></span><span class="line"><span class="cl">Span 适合记录“这一步操作本身”
</span></span><span class="line"><span class="cl">Event 适合记录“这一步里发生了什么”
</span></span><span class="line"><span class="cl">Attribute 适合记录“这一步是什么样的”
</span></span></code></pre></div><p>这套结构让 Agent workflow 不再是一个不可见的黑盒，而是变成一棵可以检查的执行树。</p>
<h2 id="一个完整-trace-示例">一个完整 Trace 示例</h2>
<p>假设用户请求：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">帮我查一下订单状态，如果已经发货，告诉我物流信息。
</span></span></code></pre></div><p>对应 trace 可能是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Trace: Customer service workflow
</span></span><span class="line"><span class="cl">  attributes:
</span></span><span class="line"><span class="cl">    workflow_name: &#34;Customer service&#34;
</span></span><span class="line"><span class="cl">    group_id: &#34;thread_123&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  Span: agent.support_agent
</span></span><span class="line"><span class="cl">    attributes:
</span></span><span class="line"><span class="cl">      agent_name: &#34;Support Agent&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Span: generation
</span></span><span class="line"><span class="cl">      attributes:
</span></span><span class="line"><span class="cl">        step: 1
</span></span><span class="line"><span class="cl">        finish_reason: &#34;tool_calls&#34;
</span></span><span class="line"><span class="cl">      events:
</span></span><span class="line"><span class="cl">        - tool_call_generated
</span></span><span class="line"><span class="cl">      output:
</span></span><span class="line"><span class="cl">        tool_call: lookup_order
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Span: function.lookup_order
</span></span><span class="line"><span class="cl">      attributes:
</span></span><span class="line"><span class="cl">        tool_name: &#34;lookup_order&#34;
</span></span><span class="line"><span class="cl">        step: 1
</span></span><span class="line"><span class="cl">        status: &#34;ok&#34;
</span></span><span class="line"><span class="cl">      input:
</span></span><span class="line"><span class="cl">        order_id: &#34;...&#34;
</span></span><span class="line"><span class="cl">      output:
</span></span><span class="line"><span class="cl">        order_status: &#34;shipped&#34;
</span></span><span class="line"><span class="cl">        tracking_id: &#34;...&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Span: generation
</span></span><span class="line"><span class="cl">      attributes:
</span></span><span class="line"><span class="cl">        step: 2
</span></span><span class="line"><span class="cl">        finish_reason: &#34;stop&#34;
</span></span><span class="line"><span class="cl">      output:
</span></span><span class="line"><span class="cl">        final_answer: &#34;你的订单已经发货，物流单号是...&#34;
</span></span></code></pre></div><p>如果这次任务失败，例如工具参数缺失，trace 可能变成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Trace: Customer service workflow
</span></span><span class="line"><span class="cl">  Span: agent.support_agent
</span></span><span class="line"><span class="cl">    Span: generation
</span></span><span class="line"><span class="cl">      events:
</span></span><span class="line"><span class="cl">        - tool_call_generated
</span></span><span class="line"><span class="cl">      output:
</span></span><span class="line"><span class="cl">        tool_call: lookup_order
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Span: function.lookup_order
</span></span><span class="line"><span class="cl">      attributes:
</span></span><span class="line"><span class="cl">        status: &#34;error&#34;
</span></span><span class="line"><span class="cl">        error_type: &#34;missing_required_argument&#34;
</span></span><span class="line"><span class="cl">      input:
</span></span><span class="line"><span class="cl">        order_id: null
</span></span><span class="line"><span class="cl">      error:
</span></span><span class="line"><span class="cl">        message: &#34;order_id is required&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Span: generation
</span></span><span class="line"><span class="cl">      output:
</span></span><span class="line"><span class="cl">        final_answer: &#34;抱歉，我无法查询订单状态。&#34;
</span></span></code></pre></div><p>通过这条 trace 可以清楚看到：失败不是工具不可用，而是模型调用工具时缺少必要参数。</p>
<p>这就是 tracing 和普通最终日志最大的区别。最终日志只能告诉你“失败了”，trace 可以告诉你“失败是怎么发生的”。</p>
<h2 id="handoff-和-guardrail-为什么适合做-span">Handoff 和 Guardrail 为什么适合做 Span</h2>
<p>Handoff 表示控制权从一个 agent 转移到另一个 agent。</p>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">用户询问退款问题
</span></span><span class="line"><span class="cl">  -&gt; Support Agent 判断这是退款请求
</span></span><span class="line"><span class="cl">  -&gt; handoff 给 Refund Agent
</span></span><span class="line"><span class="cl">  -&gt; Refund Agent 查询订单并处理退款
</span></span></code></pre></div><p>这个过程中，handoff 不是一条简单日志，而是一次有输入、有输出、有开始和结束的操作。它可能成功，也可能失败；它可能携带上下文，也可能触发新的 agent run。</p>
<p>因此，handoff 适合记录为 span。一个 handoff span 可以包含：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">from_agent
</span></span><span class="line"><span class="cl">to_agent
</span></span><span class="line"><span class="cl">handoff_reason
</span></span><span class="line"><span class="cl">handoff_payload
</span></span><span class="line"><span class="cl">status
</span></span><span class="line"><span class="cl">started_at
</span></span><span class="line"><span class="cl">ended_at
</span></span></code></pre></div><p>Guardrail 也是类似的。它通常用于检查模型输入、模型输出或工具调用是否符合规则，例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">检查用户请求是否允许
</span></span><span class="line"><span class="cl">检查模型输出是否包含敏感内容
</span></span><span class="line"><span class="cl">检查工具调用是否越权
</span></span><span class="line"><span class="cl">检查某个操作是否需要用户确认
</span></span></code></pre></div><p>Guardrail 不是一个简单事件，而是一次判断过程。它有输入、规则、判断结果、可能的拦截原因。因此，guardrail 也适合作为 span。</p>
<p>这对调试非常重要。因为有些 Agent run 的失败并不是模型或工具的问题，而是被 guardrail 拦截了。如果没有 guardrail span，开发者可能只能看到任务没有继续执行，却不知道为什么停止。</p>
<h2 id="custom-span-和-custom-event">Custom Span 和 Custom Event</h2>
<p>默认 tracing 通常只能覆盖框架已知的操作，例如 LLM generation、function call、handoff、guardrail。</p>
<p>但实际业务系统中，可能还有很多自定义逻辑值得记录。</p>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">从数据库加载用户配置
</span></span><span class="line"><span class="cl">执行权限检查
</span></span><span class="line"><span class="cl">调用内部服务
</span></span><span class="line"><span class="cl">读取缓存
</span></span><span class="line"><span class="cl">命中某个业务规则
</span></span><span class="line"><span class="cl">进行结果格式化
</span></span><span class="line"><span class="cl">执行自定义评估
</span></span></code></pre></div><p>这些操作可以用 custom span 或 custom event 记录。选择标准仍然是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">如果它有开始和结束，记录为 custom span
</span></span><span class="line"><span class="cl">如果它是某个时间点发生的事情，记录为 custom event
</span></span><span class="line"><span class="cl">如果它是描述信息，记录为 attribute
</span></span></code></pre></div><p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">读取用户配置 = custom span
</span></span><span class="line"><span class="cl">缓存命中 = custom event
</span></span><span class="line"><span class="cl">cache_key = attribute
</span></span></code></pre></div><p>这样可以把业务侧逻辑和 Agent 框架内部逻辑放在同一条 trace 中观察。</p>
<h2 id="sensitive-datatracing-中的敏感数据问题">Sensitive Data：Tracing 中的敏感数据问题</h2>
<p>Tracing 会记录 Agent 的执行过程，因此很容易捕获敏感数据。</p>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">用户原始输入
</span></span><span class="line"><span class="cl">LLM messages
</span></span><span class="line"><span class="cl">模型输出
</span></span><span class="line"><span class="cl">function call 参数
</span></span><span class="line"><span class="cl">工具返回结果
</span></span><span class="line"><span class="cl">文件内容
</span></span><span class="line"><span class="cl">命令输出
</span></span><span class="line"><span class="cl">API 返回数据
</span></span><span class="line"><span class="cl">音频输入输出
</span></span></code></pre></div><p>这些数据对调试很有用，但在生产环境中也可能带来隐私和合规风险。</p>
<p>因此，tracing 系统一般需要支持是否记录敏感数据的配置。例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">是否记录完整 LLM 输入输出
</span></span><span class="line"><span class="cl">是否记录完整 function call 输入输出
</span></span><span class="line"><span class="cl">是否记录音频原始数据
</span></span><span class="line"><span class="cl">是否只记录摘要、hash 或 metadata
</span></span></code></pre></div><p>这里需要在两类目标之间做取舍：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">调试可用性
</span></span><span class="line"><span class="cl">隐私与合规安全
</span></span></code></pre></div><p>开发环境可以记录更完整的数据，方便定位问题。生产环境则应该更谨慎，默认只记录必要的结构化信息、错误类型、耗时、token 使用量和摘要。</p>
<h2 id="long-running-worker-中的-trace-导出">Long-running Worker 中的 Trace 导出</h2>
<p>在一些长时间运行的 worker 中，trace 不一定会在任务结束后立刻出现在 dashboard。</p>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Celery
</span></span><span class="line"><span class="cl">RQ
</span></span><span class="line"><span class="cl">Dramatiq
</span></span><span class="line"><span class="cl">FastAPI background tasks
</span></span></code></pre></div><p>这类系统中，trace processor 通常会批量导出数据。它可能每隔几秒导出一次，也可能等队列达到一定大小后再导出。</p>
<p>这种方式对性能更友好，但会带来一个现象：任务已经执行结束，但 trace dashboard 里还没有立刻显示。</p>
<p>如果需要在一个任务结束后立刻保证 trace 被导出，就需要显式 flush。</p>
<p>概念上可以理解为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">trace context 结束
</span></span><span class="line"><span class="cl">  -&gt; trace 构建完成
</span></span><span class="line"><span class="cl">  -&gt; flush buffered traces
</span></span><span class="line"><span class="cl">  -&gt; dashboard 可以看到完整数据
</span></span></code></pre></div><p>需要注意的是，flush 应该发生在 trace 结束之后。否则可能会导出一个还没构建完整的 trace。</p>
<h2 id="tracing-与-eval-的关系">Tracing 与 Eval 的关系</h2>
<p>Tracing 和 eval 是两个不同但互补的系统。</p>
<p>Eval 关心的是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Agent 最终有没有完成任务？
</span></span><span class="line"><span class="cl">答案是否正确？
</span></span><span class="line"><span class="cl">工具调用是否符合预期？
</span></span><span class="line"><span class="cl">是否违反规则？
</span></span></code></pre></div><p>Tracing 关心的是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Agent 是怎么一步步走到这个结果的？
</span></span><span class="line"><span class="cl">中间发生了哪些操作？
</span></span><span class="line"><span class="cl">哪一步导致了成功或失败？
</span></span></code></pre></div><p>只做 eval，可能只知道任务失败了，但不知道为什么失败。</p>
<p>只做 tracing，能看到执行过程，但不一定能自动判断结果好坏。</p>
<p>二者结合起来，才能做更深入的失败分析。例如，一个任务 eval 失败后，可以通过 trace 判断它属于哪类失败：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">模型理解错误
</span></span><span class="line"><span class="cl">工具选择错误
</span></span><span class="line"><span class="cl">工具参数错误
</span></span><span class="line"><span class="cl">工具执行错误
</span></span><span class="line"><span class="cl">上下文丢失
</span></span><span class="line"><span class="cl">guardrail 拦截
</span></span><span class="line"><span class="cl">handoff 错误
</span></span><span class="line"><span class="cl">最终答案格式错误
</span></span></code></pre></div><p>如果修改了 prompt、tool schema、guardrail、handoff 策略或上下文管理策略，就可以通过 eval 看成功率变化，通过 trace 看失败原因变化。</p>
<h2 id="小结">小结</h2>
<p>Agent tracing 的核心，是用结构化方式记录一次 Agent workflow 的完整执行过程。</p>
<p>几个关键概念可以这样理解：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Trace：一次完整的端到端 workflow
</span></span><span class="line"><span class="cl">Span：workflow 中一个有开始和结束的操作
</span></span><span class="line"><span class="cl">Event：span 内某个时间点发生的事件
</span></span><span class="line"><span class="cl">Attribute：trace、span 或 event 上的结构化元数据
</span></span></code></pre></div><p>在 Agent 系统中，常见 span 包括：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">agent
</span></span><span class="line"><span class="cl">generation
</span></span><span class="line"><span class="cl">function
</span></span><span class="line"><span class="cl">guardrail
</span></span><span class="line"><span class="cl">handoff
</span></span><span class="line"><span class="cl">transcription
</span></span><span class="line"><span class="cl">speech
</span></span><span class="line"><span class="cl">custom
</span></span></code></pre></div><p>Tracing 的价值不是“日志更详细”，而是让 Agent 的执行过程变得可检查。</p>
<p>它可以帮助开发者回答：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Agent 做了什么？
</span></span><span class="line"><span class="cl">为什么这么做？
</span></span><span class="line"><span class="cl">哪一步失败了？
</span></span><span class="line"><span class="cl">失败来自模型、工具、上下文、guardrail 还是 handoff？
</span></span><span class="line"><span class="cl">如何复现这次失败？
</span></span><span class="line"><span class="cl">如何评估和优化后续行为？
</span></span></code></pre></div><p>当 Agent workflow 变得越来越复杂时，tracing 会从一个辅助调试工具，变成理解 Agent 行为的基础设施。</p>
<h2 id="常见问题">常见问题</h2>
<h3 id="agent-tracing-和普通日志有什么区别">Agent tracing 和普通日志有什么区别？</h3>
<p>普通日志通常是散点记录，trace 更强调一次任务的端到端结构。它会把模型生成、工具调用、guardrail、handoff 等步骤组织成可以追踪父子关系和时间顺序的执行轨迹。</p>
<h3 id="tracespanevent-应该怎么区分">Trace、span、event 应该怎么区分？</h3>
<p>Trace 表示一次完整任务，span 表示任务中的一段操作，event 表示某个时间点发生的事件。比如一次 Agent run 是 trace，一次工具调用是 span，JSON 解析失败可以是 event。</p>
<h3 id="为什么-agent-eval-需要-trace">为什么 Agent eval 需要 trace？</h3>
<p>只看最终答案很难判断失败原因。Trace 可以告诉你 Agent 是否选错工具、参数是否错误、上下文是否丢失、是否触发 guardrail，以及失败发生在哪一步。</p>
<p>延伸阅读：</p>
<ul>
<li><a href="https://openai.github.io/openai-agents-python/tracing/">OpenAI Agents SDK: Tracing</a></li>
<li><a href="https://openai.github.io/openai-agents-python/ref/tracing/span_data/">OpenAI Agents SDK: Span data</a></li>
<li><a href="/topics/ai-agent-development/">AI Agent 开发</a></li>
<li><a href="/posts/agent-dev-notes-3-agent-eval-harness/">Agent开发笔记（3）从Agent Eval看为什么llm和harness是共同优化的整体</a></li>
</ul>
]]></content:encoded></item></channel></rss>