<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Agent Tracing on 扎塔-Zata</title><link>https://www.zata.cc/tags/agent-tracing/</link><description>Recent content in Agent Tracing on 扎塔-Zata</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>Example Person</copyright><lastBuildDate>Mon, 21 Sep 2026 11:09:44 +0800</lastBuildDate><atom:link href="https://www.zata.cc/tags/agent-tracing/index.xml" rel="self" type="application/rss+xml"/><item><title>Agent 埋点接 ARMS：上报返回 success，控制台却是空的</title><link>https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/</link><pubDate>Fri, 18 Sep 2026 18:05:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/</guid><description>&lt;img src="https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/images/index/index.svg" alt="Featured image of post Agent 埋点接 ARMS：上报返回 success，控制台却是空的" />&lt;p>上报接口返回 &lt;code>200&lt;/code>，响应体是两个引号包着的 &lt;code>&amp;quot;success&amp;quot;&lt;/code>。控制台刷新了五遍，调用链分析里 &lt;code>Span 数量: 0&lt;/code>。&lt;/p>
&lt;p>第一反应当然是上报没成功。于是回头去查 payload、查鉴权、查代理——查了小半个小时，最后发现&lt;strong>数据一直都在，是我看的地方不对&lt;/strong>。&lt;/p>
&lt;p>这篇文章把这个过程完整记下来。前面是排查（含三个可以复用的判别实验），后面是这次顺带做的一个具体问题：&lt;strong>对话里带附件时，span 应该怎么记&lt;/strong>。&lt;/p>
&lt;h2 id="一gen_ai-到底是什么">一、&lt;code>gen_ai.*&lt;/code> 到底是什么
&lt;/h2>&lt;p>动手之前先明确一件事：埋点里那一堆 &lt;code>gen_ai.xxx&lt;/code> 不是我起的名字，是 &lt;strong>OpenTelemetry 的 GenAI 语义约定&lt;/strong>（Generative AI semantic conventions）——社区为&amp;quot;生成式 AI / LLM 应用&amp;quot;单独定义的一套属性规范。&lt;/p>
&lt;p>它解决的问题很朴素。没有规范时，同一个&amp;quot;模型名&amp;quot;会被写成 &lt;code>model&lt;/code>、&lt;code>model_name&lt;/code>、&lt;code>llm.model&lt;/code>；&amp;ldquo;输入 token&amp;quot;叫 &lt;code>prompt_tokens&lt;/code>、&lt;code>input_tokens&lt;/code>、&lt;code>usage.prompt&lt;/code>。结果就是&lt;strong>换个观测后端，埋点得重写一遍&lt;/strong>。&lt;/p>
&lt;p>约定把名字、类型、取值都钉死了，所以一份埋点能同时被 Langfuse、Phoenix、ARMS 正确解析。它大致覆盖五类：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类别&lt;/th>
&lt;th>属性&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>操作身份&lt;/td>
&lt;td>&lt;code>gen_ai.operation.name&lt;/code>&lt;/td>
&lt;td>&lt;code>chat&lt;/code> / &lt;code>embeddings&lt;/code> / &lt;code>invoke_agent&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>厂商与模型&lt;/td>
&lt;td>&lt;code>gen_ai.provider.name&lt;/code>、&lt;code>gen_ai.request.model&lt;/code>、&lt;code>gen_ai.response.model&lt;/code>&lt;/td>
&lt;td>provider 是判别字段&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>对话内容&lt;/td>
&lt;td>&lt;code>gen_ai.input.messages&lt;/code>、&lt;code>gen_ai.output.messages&lt;/code>、&lt;code>gen_ai.system_instructions&lt;/code>&lt;/td>
&lt;td>结构化消息，见下文&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>用量与结果&lt;/td>
&lt;td>&lt;code>gen_ai.usage.input_tokens&lt;/code>、&lt;code>output_tokens&lt;/code>、&lt;code>gen_ai.response.finish_reasons&lt;/code>&lt;/td>
&lt;td>成本核算靠这组&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具与会话&lt;/td>
&lt;td>&lt;code>gen_ai.tool.name&lt;/code>、&lt;code>gen_ai.tool.call.id&lt;/code>、&lt;code>gen_ai.conversation.id&lt;/code>&lt;/td>
&lt;td>关联工具调用与多轮会话&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>其中 &lt;code>input.messages&lt;/code> / &lt;code>output.messages&lt;/code> 是&lt;strong>结构化&lt;/strong>的，不是一段纯文本。每条消息是 &lt;code>{role, parts[]}&lt;/code>，part 有这些类型：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>part 类型&lt;/th>
&lt;th>必填字段&lt;/th>
&lt;th>用在哪&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>text&lt;/code>&lt;/td>
&lt;td>&lt;code>content&lt;/code>&lt;/td>
&lt;td>普通文本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>uri&lt;/code>&lt;/td>
&lt;td>&lt;code>uri&lt;/code>、&lt;code>modality&lt;/code>&lt;/td>
&lt;td>文件已在对象存储里，span 只记引用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>file&lt;/code>&lt;/td>
&lt;td>&lt;code>file_id&lt;/code>、&lt;code>modality&lt;/code>&lt;/td>
&lt;td>用厂商预上传能力（如 OpenAI Files API）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>blob&lt;/code>&lt;/td>
&lt;td>&lt;code>content&lt;/code>、&lt;code>modality&lt;/code>&lt;/td>
&lt;td>只有必须内联时才用，base64 进 span 有成本也有合规风险&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tool_call&lt;/code> / &lt;code>tool_call_response&lt;/code>&lt;/td>
&lt;td>&lt;code>name&lt;/code> / &lt;code>response&lt;/code>&lt;/td>
&lt;td>工具调用与结果&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>三个必须知道的坑：&lt;strong>这些属性绝大部分还是 &lt;code>Development&lt;/code> 状态，会改名&lt;/strong>——&lt;code>gen_ai.system&lt;/code> 就被重命名成了 &lt;code>gen_ai.provider.name&lt;/code>，&lt;code>gen_ai.prompt.*&lt;/code> 也被 &lt;code>input.messages&lt;/code> 取代；&lt;strong>结构化属性落不到 span 上&lt;/strong>，OTel 的 Python SDK 只接受基本类型，所以得 &lt;code>json.dumps&lt;/code> 成字符串再塞；&lt;strong>&lt;code>input/output.messages&lt;/code> 是 Opt-In 属性&lt;/strong>，规范明确说不该默认记录，因为可能含用户隐私。&lt;/p>
&lt;/blockquote>
&lt;h2 id="二span-树怎么设计">二、span 树怎么设计
&lt;/h2>&lt;p>目标形态是一次 Agent Run 对应一棵 span 树，业务库的 Run 记录里存 &lt;code>trace_id&lt;/code> 作为关联键：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">agent.run [run.id=run-xxx, user.id=demo-user-1, session.id=demo-session-1]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ agent.attach_document file.uri / file.mime_type / file.size_bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ gen_ai chat gen_ai.input.messages / usage.* / latency_ms
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ tool get_freight_quote gen_ai.tool.name / gen_ai.tool.call.id / tool.result
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─ gen_ai chat 基于工具结果的第二轮作答
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>写属性的部分收敛成几个 helper，别散在业务代码里——改名时只改一处：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">set_llm_request&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">span&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">messages&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">conversation_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.operation.name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;chat&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.provider.name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.request.model&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">conversation_id&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.conversation.id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">conversation_id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># system 消息按约定不进 input messages，单独走 system_instructions&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;gen_ai.input.messages&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">to_input_messages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">ensure_ascii&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>to_input_messages&lt;/code> 里做两件事：把 system 消息摘出去、把文本统一截断。&lt;strong>截断不是可选项&lt;/strong>——&lt;code>input.messages&lt;/code> 里塞进一个 8000 字的 prompt，span 会大得没法用。&lt;/p>
&lt;h2 id="三一个-200-success-的假象">三、一个 200 success 的假象
&lt;/h2>&lt;p>配置填完，跑起来，日志里看到这一行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">POST http://tracing-analysis-dc-sg.aliyuncs.com/adapt_&amp;lt;xx&amp;gt;_&amp;lt;xx&amp;gt;/api/otlp/traces HTTP/1.1&amp;#34; 200 9
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>200&lt;/code>、响应体 9 字节（就是 &lt;code>&amp;quot;success&amp;quot;&lt;/code> 加引号）。这种时候人很容易直接跳到&amp;quot;好，通了&amp;rdquo;，然后去控制台发现什么都没有，再回头怀疑人生。&lt;/p>
&lt;p>我做了三个实验来定位问题到底在链路哪一段。这三个实验本身挺通用，值得记：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>实验&lt;/th>
&lt;th>操作&lt;/th>
&lt;th>结果&lt;/th>
&lt;th>排除了什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>绕过本地代理&lt;/td>
&lt;td>&lt;code>--noproxy '*'&lt;/code> 直连重发&lt;/td>
&lt;td>同样 &lt;code>200 &amp;quot;success&amp;quot;&lt;/code>&lt;/td>
&lt;td>不是代理伪造的响应&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>发垃圾 payload&lt;/td>
&lt;td>&lt;code>head -c 300 /dev/urandom&lt;/code> 当 body 发过去&lt;/td>
&lt;td>&lt;code>400 Bad Request&lt;/code>&lt;/td>
&lt;td>网关&lt;strong>真的在解析&lt;/strong> protobuf，不是无脑返回成功&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>伪造 license key&lt;/td>
&lt;td>把 &lt;code>/adapt_.../&lt;/code> 换成随机串&lt;/td>
&lt;td>&lt;code>403 Forbidden&lt;/code>&lt;/td>
&lt;td>key 是&lt;strong>有效&lt;/strong>的，且服务端会校验 workspace&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>三个实验做完，结论就很硬了：&lt;strong>数据确实被服务端收下了&lt;/strong>，而且收进了一个合法的 workspace。既然服务端没问题，那&amp;quot;看不到&amp;quot;就只剩一种可能——&lt;strong>去看的那个地方，不是数据所在的地方&lt;/strong>。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/images/index/diagnosis.svg"
loading="lazy"
alt="诊断切段图：把上报链路切成 Agent 进程、本地代理、ARMS 网关、控制台四段，实验①绕过代理排除代理伪造，实验②发垃圾字节得到 400 排除网关不解析，实验③伪造 license key 得到 403 证明 key 有效，四段全部通过后剩下唯一的失败段是控制台的地域、页签与页面作用域"
>&lt;/p>
&lt;p>事实也确实如此。&lt;/p>
&lt;h2 id="四三个坑">四、三个坑
&lt;/h2>&lt;h3 id="坑一地域">坑一：地域
&lt;/h3>&lt;p>接入点是 &lt;code>tracing-analysis-dc-sg.aliyuncs.com&lt;/code>，&lt;code>dc-sg&lt;/code> 就是&lt;strong>新加坡&lt;/strong>。而控制台默认停在别的地域，调用链分析自然是空的。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>症状&lt;/strong>&lt;/td>
&lt;td>上报 &lt;code>200 success&lt;/code>，控制台调用链分析 &lt;code>Span 数量: 0&lt;/code>，左侧所有维度显示&amp;quot;没有匹配的值&amp;quot;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>控制台地域与接入点地域不一致。数据在 &lt;code>ap-southeast-1&lt;/code>，眼睛在别处&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>控制台切到与接入点同地域；&lt;code>应用列表&lt;/code> 也要在同地域看&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这类问题最坑的地方在于：&lt;strong>它不报错&lt;/strong>。上报端一切正常，服务端一切正常，只有&amp;quot;人对不上&amp;quot;。&lt;/p>
&lt;h3 id="坑二入口">坑二：入口
&lt;/h3>&lt;p>地域切对之后，还有第二层错位。控制台里同一个应用有不止一个入口：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>应用列表&lt;/strong> 分 &lt;code>OpenTelemetry&lt;/code> 和 &lt;code>ARMS&lt;/code> 两个页签。通过 OTel SDK 上报的应用只出现在前者；后者是 ARMS 探针接入的应用列表。看错页签 = 看到空列表。&lt;/li>
&lt;li>&lt;strong>应用维度的调用链分析&lt;/strong> 是带过滤的。我一度搜索框里直接粘 trace_id，仍然是 0 —— 因为页面作用域被钉死在某个应用上，搜索条件没换掉&amp;quot;应用&amp;quot;这个更大的前提。页面里搜出来的搜索框内容长这样：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">serviceName:&amp;#34;agentrun-agent-quick-oUpbu&amp;#34; and resources.acs.arms.service_id : &amp;#34;azmulz8rp8...&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>第三段 &lt;code>resources.acs.arms.service_id&lt;/code> 是 ARMS 探针注入的资源属性。&lt;strong>OTel 自己上报的 span 根本没有这个属性&lt;/strong>，所以这个条件永远匹配不上。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>症状&lt;/strong>&lt;/td>
&lt;td>搜 trace_id 也搜不到；应用列表某个页签永远是空的&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>页面作用域（应用 / 页签）与数据的归属不匹配&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>换到不绑定单个应用的入口，或把搜索条件改成 &lt;code>serviceName:&amp;quot;&amp;lt;你的服务名&amp;gt;&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="坑三配置写法">坑三：配置写法
&lt;/h3>&lt;p>第三个坑不致命，但错误信息很难自查，因为&lt;strong>不报错&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 错的：ARMS 的 header 不是 Authorization: Bearer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_HEADERS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">Authorization&lt;/span>&lt;span class="o">=&lt;/span>Bearer &amp;lt;token&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 对的：HTTP 接入点鉴权编码在 /adapt_&amp;lt;xx&amp;gt;_&amp;lt;xx&amp;gt;/ 路径里，headers 留空&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span>&lt;span class="o">=&lt;/span>http://tracing-analysis-dc-sg.aliyuncs.com/adapt_&amp;lt;xx&amp;gt;_&amp;lt;xx&amp;gt;/api/otlp/traces
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_HEADERS&lt;/span>&lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 只有 gRPC 才需要 header，格式是 Authentication=&amp;lt;token&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span>&lt;span class="o">=&lt;/span>http://tracing-analysis-dc-sg.aliyuncs.com:8090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_HEADERS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">Authentication&lt;/span>&lt;span class="o">=&lt;/span>&amp;lt;token&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>还有一条同类的：HTTP 接入点&lt;strong>自带&lt;/strong> &lt;code>/api/otlp/traces&lt;/code>，不要再拼 &lt;code>/v1/traces&lt;/code>——那是本地 Jaeger 的写法，拼上去就是 404。&lt;/p>
&lt;h2 id="五对话里带附件怎么记">五、对话里带附件怎么记
&lt;/h2>&lt;p>这是这次真正想解决的问题：Agent 的对话里带了文件，trace 上该怎么表达。&lt;/p>
&lt;p>关键决定是&lt;strong>文件不进 span 的字节流&lt;/strong>。做法是三处落点：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>位置&lt;/th>
&lt;th>记什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>agent.attach_document&lt;/code> span&lt;/td>
&lt;td>&lt;code>file.uri&lt;/code> / &lt;code>file.mime_type&lt;/code> / &lt;code>file.size_bytes&lt;/code> / &lt;code>file.doc_id&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>gen_ai chat&lt;/code> 的 &lt;code>gen_ai.input.messages&lt;/code>&lt;/td>
&lt;td>该条 user 消息的 &lt;code>parts&lt;/code> 里一个 &lt;code>uri&lt;/code> part + 一个 &lt;code>text&lt;/code> part&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>业务库&lt;/td>
&lt;td>文件归属业务层（doc_id ↔ Run），trace 里只留引用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/images/index/attachment.svg"
loading="lazy"
alt="三层落点图：左边对象存储是唯一保存文件字节的地方；中间 trace 用 agent.attach_document 记 file.uri 与规模，用 gen_ai.input.messages 的 text、uri、text 三个 part 记模型看到的内容；右边业务库用 trace_id 关联两层"
>&lt;/p>
&lt;p>实测落进 ARMS 的属性长这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[{&lt;/span>&lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;parts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;按附件里的信息报价&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;uri&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;uri&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;oss://bucket/uploads/doc-adcc4d1f/booking-note.txt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;modality&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;document&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;mime_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text/plain&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;订舱委托书 / BOOKING NOTE\n委托编号：BN-2026-0918-075…&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]}]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三个设计取舍值得说一下：&lt;/p>
&lt;p>&lt;strong>第一，&lt;code>uri&lt;/code> part 和 &lt;code>text&lt;/code> part 成对出现。&lt;/strong> &lt;code>uri&lt;/code> 是溯源（文件从哪来），&lt;code>text&lt;/code> 是模型真正读到的内容（服务端预抽取的结果）。只记 &lt;code>uri&lt;/code>，排查时无法回答&amp;quot;模型到底看没看到附件&amp;quot;；只记 &lt;code>text&lt;/code>，又丢了文件来源。两个都记才查得动。&lt;/p>
&lt;p>&lt;strong>第二，&lt;code>modality&lt;/code> 是必填。&lt;/strong> &lt;code>uri&lt;/code> 和 &lt;code>file&lt;/code> part 都要求这个字段，取值 &lt;code>image&lt;/code> / &lt;code>video&lt;/code> / &lt;code>audio&lt;/code> / &lt;code>document&lt;/code>。容易漏，漏了后端可能直接忽略这个 part。&lt;/p>
&lt;p>&lt;strong>第三，模型调用侧和 trace 侧要分开构造。&lt;/strong> 应用侧消息保留结构化内容（&lt;code>{type: &amp;quot;text&amp;quot;}&lt;/code> / &lt;code>{type: &amp;quot;document&amp;quot;}&lt;/code>），发给 OpenAI 兼容端点时拍平成纯文本；转成 &lt;code>gen_ai.input.messages&lt;/code> 时才展开成 uri + text。这样两边各取所需，不会为了迁就某一方而变形：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 模型侧：附件以抽取文本注入&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">to_openai_content&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">isinstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">item&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;document&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;[[附件 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;uri&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">]]&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;extracted_text&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>验证方式是让模型复述附件里的独有信息——它在回答里用上了委托书的 &lt;code>ETD 2026-09-25&lt;/code>、截关时间和&amp;quot;14 天免箱期&amp;quot;。这说明附件确实进了模型上下文，不只是进了 span。&lt;/p>
&lt;p>顺手补的一个洞：&lt;code>BatchSpanProcessor&lt;/code> 默认靠 &lt;code>atexit&lt;/code> 冲刷，进程被强杀（Ctrl-C、沙箱超时）时最后一批 span 会丢。Run 结束处显式 &lt;code>force_flush()&lt;/code> 更稳。&lt;/p>
&lt;h2 id="六数据怎么拿出来给-ai-看">六、数据怎么拿出来给 AI 看
&lt;/h2>&lt;p>控制台是给人看的，AI 想自己查得走另外三条路：&lt;/p>
&lt;p>&lt;strong>1. OpenAPI&lt;/strong>（免费，最直接）。&lt;code>xtrace&lt;/code> 产品两个接口就能拼出一个导出脚本：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>接口&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>关键参数&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>SearchTraces&lt;/code>&lt;/td>
&lt;td>按条件列出调用链，返回 TraceID 列表&lt;/td>
&lt;td>&lt;code>RegionId&lt;/code>、&lt;code>StartTime&lt;/code>/&lt;code>EndTime&lt;/code>（毫秒）、&lt;code>ServiceName&lt;/code>、&lt;code>OperationName&lt;/code>、&lt;code>MinDuration&lt;/code>、&lt;code>Tag&lt;/code>、&lt;code>PageNumber&lt;/code>/&lt;code>PageSize&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>GetTrace&lt;/code>&lt;/td>
&lt;td>按 TraceID 取完整 span 明细&lt;/td>
&lt;td>&lt;code>TraceID&lt;/code>、&lt;code>RegionId&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>翻页拿 ID，再逐条取详情，落地成 JSON 就完了。&lt;/p>
&lt;p>&lt;strong>2. 可观测 MCP Server&lt;/strong>（&lt;code>aliyun/alibabacloud-observability-mcp-server&lt;/code>）。Go 单二进制，里面正好有对应的工具：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mcpServers&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;alibaba_cloud_observability&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;./bin/alibabacloud-observability-mcp-server&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;start&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--stdio&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ALIBABA_CLOUD_ACCESS_KEY_ID&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;AK&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ALIBABA_CLOUD_ACCESS_KEY_SECRET&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;SK&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ALIBABA_CLOUD_REGION&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cn-singapore&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>接上之后可以直接问&amp;quot;昨天耗时最长的几条 Run 是什么，&lt;code>gen_ai.input.messages&lt;/code> 里带没带附件&amp;quot;，由 &lt;code>umodel_search_traces&lt;/code> / &lt;code>umodel_get_traces&lt;/code> 去取。两个注意点：用只读 RAM 用户（&lt;code>AliyunARMSReadOnlyAccess&lt;/code>），别用主账号 AK；&lt;code>sls_text_to_sql&lt;/code>、&lt;code>sls_sop&lt;/code> 这类 AI 工具走 STAROps 单独计费，不需要就在 &lt;code>config.yaml&lt;/code> 的 &lt;code>enabled_tools&lt;/code> 里关掉。&lt;/p>
&lt;p>&lt;strong>3. Collector 双写&lt;/strong>。反正上报端在自己手里，OTel Collector 同一份数据同时投 ARMS 和本地存储（ClickHouse / 文件 / 对象存储）。本地那份没有 AK、没有分页、随便 SQL——&lt;strong>AI 分析用它比用控制台 API 舒服得多&lt;/strong>。ARMS 负责看瀑布图和告警，本地那份负责喂给 Agent。&lt;/p>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>几个收获，都是这次真金白银换来的：&lt;/p>
&lt;p>&lt;strong>1. 200 不等于数据可用。&lt;/strong> &lt;code>&amp;quot;success&amp;quot;&lt;/code> 只说明网关收了，说明不了数据落到了你正在看的那个地方。判断&amp;quot;到底通没通&amp;quot;要有独立证据——我这次用的是&amp;quot;发垃圾字节看它报不报错&amp;quot;和&amp;quot;伪造 key 看它拦不拦&amp;quot;，两个实验各一次请求，就能把链路切段定位。&lt;/p>
&lt;p>&lt;strong>2. 可观测性接入的坑，多半在&amp;quot;人对不上&amp;quot;而不是&amp;quot;数据不通&amp;quot;。&lt;/strong> 地域、页签、页面作用域、filter 条件，这四样任何一个不对，现象都是同一个：空列表。而且全都不报错。排查时优先怀疑这个，比怀疑 SDK 划算。&lt;/p>
&lt;p>&lt;strong>3. 约定比实现重要。&lt;/strong> 用 &lt;code>gen_ai.*&lt;/code> 的收益在这次很直观：同一份埋点，改个 endpoint 就从本地控制台切到了 ARMS，一行代码没动。代价是要接受它还在 &lt;code>Development&lt;/code>、会改名——所以属性写入必须收敛到 helper 里。&lt;/p>
&lt;p>&lt;strong>4. 文件类数据，&amp;ldquo;引用 + 摘要&amp;quot;比&amp;quot;内容&amp;quot;有用。&lt;/strong> 把 pdf 塞进 span 不会让排查更容易，只会让它更贵更难搜。&lt;code>uri&lt;/code> part 记来源、&lt;code>text&lt;/code> part 记模型看到的内容、业务库存文件本身，三层各司其职。&lt;/p>
&lt;p>&lt;strong>5. 顺手能修的洞就别留着。&lt;/strong> &lt;code>force_flush()&lt;/code> 这种一行的事，不补就是&amp;quot;偶发丢 trace&amp;rdquo;，而偶发丢 trace 是最难查的那类问题——它会让上面所有的排查经验都建立在错误的观察上。&lt;/p></description></item><item><title>LangSmith使用教程</title><link>https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</link><pubDate>Fri, 30 May 2025 16:15:40 +0800</pubDate><guid>https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</guid><description>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index.png" alt="Featured image of post LangSmith使用教程" />&lt;h1 id="langsmith使用教程">LangSmith使用教程
&lt;/h1>&lt;p>LangSmith 是 LangChain 官方的 LLM 应用观测平台：调试、追踪、评估、监控都在上面。LLM 应用最麻烦的一点是行为不透明——同一份提示，换个模型、换个参数、换个措辞，结果就可能完全不同，只靠 print 调试很快就会失控。我的判断是：追踪不是可选项，而是基础设施；在 LangChain 生态里，LangSmith 是接入成本最低的那个。&lt;/p>
&lt;p>本文是我 2025 年 5 月的上手记录（截图都是当时的实拍）；2026 年 9 月整合博客时修订了环境变量命名和评估 API，过时的旧写法在文中用注记标出。&lt;/p>
&lt;h2 id="核心概念">核心概念
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>概念&lt;/th>
&lt;th>一句话说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Tracing（追踪）&lt;/td>
&lt;td>记录应用每个组件的输入、输出、耗时、token，还原完整执行路径&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Debugging（调试）&lt;/td>
&lt;td>基于 trace 定位错误和非预期行为&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Evaluation（评估）&lt;/td>
&lt;td>用数据集 + 评估器量化应用表现，比较不同版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Datasets（数据集）&lt;/td>
&lt;td>评估用的输入/期望输出样本集合，可从生产 trace 提取&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Monitoring（监控）&lt;/td>
&lt;td>部署后持续追踪错误率、延迟、token 消耗&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Project（项目）&lt;/td>
&lt;td>trace 的归属单位，一个应用一个项目&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="接入设置">接入设置
&lt;/h2>&lt;h3 id="账户与-api-key">账户与 API Key
&lt;/h3>&lt;ol>
&lt;li>到 &lt;a class="link" href="https://smith.langchain.com/" target="_blank" rel="noopener"
>smith.langchain.com&lt;/a> 注册，登录后创建组织（Organization）。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image_hu16999795923421345222.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image_hu13664730940339820997.png 1024w"
loading="lazy"
alt="langsmit界面"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;ol start="2">
&lt;li>在组织的 Settings → API Keys 里创建密钥，妥善保管，不要公开分享。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1_hu8868344347395063117.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1_hu18255957453875288326.png 1024w"
loading="lazy"
alt="settings"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2.png"
width="269"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2_hu13006059943811164616.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2_hu588751695666657627.png 1024w"
loading="lazy"
alt="apikey"
class="gallery-image"
data-flex-grow="28"
data-flex-basis="67px"
>&lt;/p>
&lt;h3 id="安装-sdk">安装 SDK
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langsmith
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="环境变量">环境变量
&lt;/h3>&lt;blockquote>
&lt;p>2026-09 修订：官方现在的推荐前缀是 &lt;code>LANGSMITH_&lt;/code>。本文写作时用的还是 &lt;code>LANGCHAIN_&lt;/code> 旧前缀（下方注记），两者目前兼容，新项目直接用新写法。&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANGSMITH_TRACING_ENABLED&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANGSMITH_ENDPOINT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://api.smith.langchain.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANGSMITH_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;YOUR_LANGSMITH_API_KEY&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANGSMITH_PROJECT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;YOUR_PROJECT_NAME&amp;#34;&lt;/span> &lt;span class="c1"># 可选，默认 &amp;#34;default&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>LANGSMITH_TRACING_ENABLED&lt;/code>：总开关，设为 true 后 LangChain / LangGraph 应用自动上报&lt;/li>
&lt;li>&lt;code>LANGSMITH_PROJECT&lt;/code>：trace 归属的项目名，可在界面上创建和管理&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>⚠️ 旧写法：&lt;code>LANGCHAIN_TRACING_V2=&amp;quot;true&amp;quot;&lt;/code> / &lt;code>LANGCHAIN_ENDPOINT&lt;/code> / &lt;code>LANGCHAIN_API_KEY&lt;/code> / &lt;code>LANGCHAIN_PROJECT&lt;/code>。仍兼容，但已弃用。&lt;/p>
&lt;/blockquote>
&lt;h2 id="追踪-langchain-应用">追踪 LangChain 应用
&lt;/h2>&lt;p>环境变量设好之后，LangChain 应用&lt;strong>零代码改动&lt;/strong>自动上报——这是它比手动埋点省事的核心原因。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StrOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 模型：阿里云百炼的 OpenAI 兼容模式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># （修订：原文这里误用了未导入的 Tongyi 类，统一改为博客其他文章一致的 ChatOpenAI 写法）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;qwen-plus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;DASHSCOPE_API_KEY&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://dashscope.aliyuncs.com/compatible-mode/v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_messages&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;system&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;You are a helpful assistant that translates &lt;/span>&lt;span class="si">{input_language}&lt;/span>&lt;span class="s2"> to &lt;/span>&lt;span class="si">{output_language}&lt;/span>&lt;span class="s2">.&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;human&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{text}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">StrOutputParser&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;English&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Chinese&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello, how are you?&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>运行后登录 LangSmith，在对应项目下就能看到这次运行的追踪记录：链的每个步骤、输入、输出、耗时、token，以及可能发生的任何错误。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3_hu668023011181215320.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3_hu14328061733863575152.png 1024w"
loading="lazy"
alt="trace"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4_hu395046776489291782.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4_hu3300973140154413831.png 1024w"
loading="lazy"
alt="记录"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;h2 id="追踪任意函数traceable">追踪任意函数：@traceable
&lt;/h2>&lt;p>不在 LangChain 链里的函数，用 &lt;code>@traceable&lt;/code> 装饰器也能纳入追踪：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langsmith&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">traceable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@traceable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;My Custom Function&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># name 可选，用于在 UI 中显示&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">my_data_processing_function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">upper&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@traceable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">my_llm_logic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 里面可以调 LLM、调其他被追踪函数，调用链会串在一条 trace 里&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">my_data_processing_function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">my_llm_logic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;This is some input text.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>被装饰的函数每次调用都会生成一条 trace，和 LangChain 链的 trace 在同一个视图里查看。&lt;/p>
&lt;h2 id="从-callbacks-到-astream_events观测方式的变化">从 Callbacks 到 astream_events：观测方式的变化
&lt;/h2>&lt;blockquote>
&lt;p>本节为 2026-09 整合时补充。&lt;/p>
&lt;/blockquote>
&lt;p>LangChain 0.x 时代，想监听应用内部发生的事，要写 CallbackHandler——继承 &lt;code>BaseCallbackHandler&lt;/code>，实现 &lt;code>on_llm_start&lt;/code> / &lt;code>on_llm_new_token&lt;/code> 等一串回调。能用，但啰嗦，和异步、流式代码搅在一起还容易出错。&lt;/p>
&lt;p>1.x 时代的官方答案是 &lt;strong>astream_events&lt;/strong>：一条流式事件管道，把链内部每个组件的开始/结束/token 都作为事件吐出来，不用写 handler 类：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">event&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">astream_events&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;English&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Chinese&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello, how are you?&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">version&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;v2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;event&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;on_chat_model_stream&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s2">&amp;#34;chunk&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">flush&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>分工可以这样理解：给用户看中间步骤、做流式 UI，用 astream_events；沉淀全量记录、做评估和监控，交给 LangSmith。两者不冲突。&lt;/p>
&lt;h2 id="查看追踪数据">查看追踪数据
&lt;/h2>&lt;p>登录平台后：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5_hu587824023913815603.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5_hu7873635904407332508.png 1024w"
loading="lazy"
alt="首页"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6_hu9543388786062130606.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6_hu279340018157319073.png 1024w"
loading="lazy"
alt="详情"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Projects 视图&lt;/strong>：所有项目的概览——运行次数、错误率&lt;/li>
&lt;li>&lt;strong>Traces 视图&lt;/strong>：项目下每条 trace 对应一次完整执行；点进去能看到 Chain / LLM / Tool / Retriever 的调用层级、输入输出、耗时&lt;/li>
&lt;li>出错时，错误信息和堆栈直接展示在 trace 里&lt;/li>
&lt;li>可以给 trace 打 metadata 和 tags，方便组织和筛选&lt;/li>
&lt;/ul>
&lt;h2 id="playground">Playground
&lt;/h2>&lt;p>Playground 是交互式的提示试验台：改提示、换模型、调参数（temperature、max_tokens），立即看输出；所有运行自动留 trace。compare 模式可以把两个版本的输出并排对比——调提示词时特别有用。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7.png"
width="5104"
height="2662"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7_hu5515896410810803528.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7_hu16710636975432683832.png 1024w"
loading="lazy"
alt="playground"
class="gallery-image"
data-flex-grow="191"
data-flex-basis="460px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8.png"
width="5104"
height="2662"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8_hu17388606319755112034.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8_hu16570506362763736550.png 1024w"
loading="lazy"
alt="compare"
class="gallery-image"
data-flex-grow="191"
data-flex-basis="460px"
>&lt;/p>
&lt;h2 id="prompt-hub">Prompt Hub
&lt;/h2>&lt;p>Prompt Hub 用来存放、版本化、复用提示模板。界面上创建模板，&lt;code>{xxx}&lt;/code> 占位符会自动识别成 inputs；修改模板会留 commit 记录，可以回溯。（&lt;a class="link" href="https://docs.smith.langchain.com/prompt_engineering/how_to_guides#prompt-hub" target="_blank" rel="noopener"
>文档地址&lt;/a>）&lt;/p>
&lt;p>代码里直接拉取调用：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9.png"
width="3454"
height="2230"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9_hu14466145963846159392.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9_hu17314811126818976642.png 1024w"
loading="lazy"
alt="调用代码"
class="gallery-image"
data-flex-grow="154"
data-flex-basis="371px"
>&lt;/p>
&lt;p>创建模板，右边自动出现 inputs：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10.png"
width="5104"
height="2662"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10_hu15835326501385185382.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10_hu6906222228396075153.png 1024w"
loading="lazy"
alt="创建方式"
class="gallery-image"
data-flex-grow="191"
data-flex-basis="460px"
>&lt;/p>
&lt;p>修改有 commit 记录：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11.png"
width="3454"
height="2230"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11_hu5092816811983418757.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11_hu13980254746554264517.png 1024w"
loading="lazy"
alt="prompt commit"
class="gallery-image"
data-flex-grow="154"
data-flex-basis="371px"
>&lt;/p>
&lt;h2 id="数据集与评估">数据集与评估
&lt;/h2>&lt;p>评估是 LangSmith 对我来说价值最大的部分：&lt;strong>凭感觉调提示，和拿数据说话，是两回事。&lt;/strong>&lt;/p>
&lt;h3 id="创建数据集">创建数据集
&lt;/h3>&lt;p>三种方式：&lt;/p>
&lt;ul>
&lt;li>UI 里手动创建，添加输入和期望输出（Ground Truth）&lt;/li>
&lt;li>从生产 trace 里筛选样本，「Add to Dataset」存进数据集——基于真实用户交互做评估，这是最推荐的路子&lt;/li>
&lt;li>SDK 代码创建（见下）&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-12.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-12_hu3027362747169187572.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-12_hu9836541955965867834.png 1024w"
loading="lazy"
alt="手动上传数据集"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;h3 id="sdk-创建数据集并运行评估">SDK 创建数据集并运行评估
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langsmith&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Client&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">evaluate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Client&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;My Translation Evaluations&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dataset_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Dataset for evaluating translations.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create_example&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;English&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;French&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;expected_translation&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Bonjour&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 大概率是数据集已存在，取现成的&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">list_datasets&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dataset_name_contains&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset_name&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 被评估的目标：包装成「接收 inputs、返回 dict」的函数（chain 即上文的翻译链）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">predict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">inputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">translation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">translation&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 自定义评估器：精确匹配&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">exact_match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">example&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">expected&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">example&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">outputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;expected_translation&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">outputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">expected&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;key&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;exact_match&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">score&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">evaluate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">predict&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset_name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">evaluators&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">exact_match&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">experiment_prefix&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;translation-eval&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>修订说明：原文这里原本是两行省略号占位（没有可运行代码），上面是按 langsmith SDK 的 &lt;code>evaluate()&lt;/code> 补全的最小示例，具体参数以&lt;a class="link" href="https://docs.smith.langchain.com/" target="_blank" rel="noopener"
>官方文档&lt;/a>为准。&lt;/p>
&lt;/blockquote>
&lt;p>评估器除了自定义函数，常用的几类：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>字符串评估器&lt;/strong>：精确匹配、正则、包含性判断&lt;/li>
&lt;li>&lt;strong>LLM-as-Judge&lt;/strong>：用另一个模型按标准打分（相关性、质量、有害性）&lt;/li>
&lt;li>&lt;strong>轨迹评估器&lt;/strong>：评 Agent 的完整执行轨迹（工具用得对不对），而不只是最终答案&lt;/li>
&lt;li>&lt;strong>比较评估器&lt;/strong>：两个版本在同一输入上并排比&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>⚠️ 版本注记：旧文档里的 &lt;code>RunCollector&lt;/code> / &lt;code>QAEvalChain&lt;/code>（langchain.evaluation 体系）已被 LangSmith 平台和 SDK 的 &lt;code>evaluate()&lt;/code> 取代，这里留个名字备查。&lt;/p>
&lt;/blockquote>
&lt;h3 id="我的建议">我的建议
&lt;/h3>&lt;ol>
&lt;li>先在 LangSmith UI 里熟悉评估流程，再上 SDK&lt;/li>
&lt;li>应用跑起来、有 trace 上报是评估的前提——评估的对象就是这些运行记录&lt;/li>
&lt;li>数据集从真实 trace 里攒，比凭空编造更接近真实分布&lt;/li>
&lt;/ol>
&lt;h2 id="监控与协作">监控与协作
&lt;/h2>&lt;p>应用部署后，LangSmith 可以持续监控：仪表盘看延迟、错误率、token 消耗、用户反馈；指标超阈值可设警报；用户点赞/点踩能关联到对应 trace。团队协作方面，组织内共享 trace、数据集和评估结果。&lt;/p>
&lt;h2 id="总结">总结
&lt;/h2>&lt;ul>
&lt;li>接入成本几乎为零：环境变量一设，LangChain 应用自动上报&lt;/li>
&lt;li>调试靠 trace，迭代靠 Playground + Prompt Hub，质量靠数据集 + 评估&lt;/li>
&lt;li>观测分工：给用户看的中间过程用 astream_events，记录和分析交给 LangSmith&lt;/li>
&lt;li>官方文档：&lt;a class="link" href="https://docs.smith.langchain.com/" target="_blank" rel="noopener"
>docs.smith.langchain.com&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>