
做 Agent 开发的人大概率都经历过这种状况本地跑得好好的多步推理链路一放到线上就开始出现各种奇怪行为。翻日志只能看到一串零散的 LLM 调用记录根本说不清是哪一步决策出了问题、哪个工具返回了错误数据、又是哪一个中间结果把 Agent 带偏了。这也是我为什么现在所有 Agent 项目都要先接上 LangSmith——它解决的核心问题就是链路可追踪让你把一次 Agent 运行从收到消息、中间决策、工具调用、再到最终回答的全部过程像看调用拓扑图一样看得清清楚楚。LangSmith 是 LangChain 团队推出的 LLM 可观测性平台作用相当于给 Agent 装上行车记录仪但它并不只对 LangChain 友好。OpenAI、Anthropic、LlamaIndex甚至你自己裸写 Prompt 循环都能通过 langsmith SDK 接入。无论你是刚开始接触 Agent 开发的新手还是负责线上 Agent 服务的后端同学这篇文章都值得花十分钟看一下我会从概念讲起带你把一个 Agent 项目完整接进 LangSmith再整理我在生产环境中踩过的坑和排查思路。1. 为什么做 Agent 的人都在补「可观测性」这堂课1.1 Agent 链路和普通接口日志的本质区别先说一个事实传统后端观测是「请求级」的。一个 REST 接口收到请求、处理、返回你靠日志加指标加链路追踪比如 OpenTelemetry就能把一次请求的过程完整拼出来。因为逻辑是确定的分支是有限的错误大致是可枚举的所以「排查问题 定位分支」就够了。但 Agent 完全不是这个逻辑。一个 Agent 项目里LLM 是自由的决策者它可能第一步就决定调用某个工具也可能先反问用户再决定下一步同样的输入两次运行的路径可能完全不一样同样的工具在不同上下文里被调用的参数也可能千奇百怪。你没法用「先 A 后 B 再 C」的固定流程图去描述一次运行。我举个自己的例子。之前做一个信息收集类 Agent用户问「帮我查一下上海这周适合户外活动的天气」Agent 的规划是好的但工具调用时把城市参数解析成了「上海中心城区」结果数据源一直匹配不上Agent 就反复重试同一把工具白白消耗了三次 LLM 调用最后还给了个含糊答案。如果没有链路追踪这种问题基本只能靠猜。有了追踪一眼就能看到那一环的工具入参出了偏差修复时间从两小时缩短到五分钟。这就是 Agent 可观测性的本质需求你要关注的不只是「这接口通不通」而是「这个自主决策的流程每一步到底做了什么、为什么这么做」。1.2 LangSmith 到底解决了什么问题LangSmith 的核心价值拆开看其实有四块调试Debug把一次 Agent 运行的完整决策树可视化包括每一步的 Prompt、工具入参和出参、中间结果、Token 消耗。评测Eval沉淀数据集对同样的输入跑不同模型、不同 Prompt批量打分。监控Monitor把线上 Agent 的 Token 消耗、延迟、错误率做成实时面板。运营Feedback接收集成用户反馈点赞、点踩用真实数据和主观评价一起优化 Agent。对刚入门的朋友你最该先吃透的是第一项「调试」也就是链路可追踪。后面几项基本都建立在良好 Trace 的基础上链路都没有谈别的都是空中楼阁。另外很重要的一点LangSmith 不是 LangChain 的附属品。官方提供了 langsmith SDK你可以直接拿它去包装任意的 Python 或 JS 函数。换句话说哪怕你完全不用 LangChain、LangGraph而是自己写了一套 Agent 循环——自己调模型、自己管工具列表、自己写反思逻辑——一样可以把完整链路送进 LangSmith 看板。这也是我认为它在当下的 Agent 可观测性工具里最值得先学的原因。2. 接入前必须搞懂的四个概念Trace、Span、Run、Observation2.1 名词拆解一张表讲清楚打开 LangSmith 控制台你看到的界面其实围绕几个固定名词组织起来的。先记四个概念一句话解释类比Project一个项目存放一批相关的 Trace一个业务系统的日志目录Trace一次完整请求运行的记录一次会话的回放视频Span / RunTrace 里的一个步骤片段可嵌套视频里的一个镜头Observation某个步骤的具体观测数据如 Token、延迟、错误镜头的参数信息有一点要特别注意随着 LangSmith API 升级旧文档里经常出现 Run 和 Span 混用的情况。v2 API 里官方更强调 TreeTrace和 Span 这对概念在 SDK 代码和旧资料里又能看到 run_type、parent_run_id 这类字段。理解它们本质是同一件事就够了——一次 Trace 是一棵树每个 Span 是树上的一层节点可以在自己的父 Span 下面继续开子 Span。顺带说一句最近很多人问 agent harness 和 agent 框架有什么区别其实放到可观测性这个语境里也说得通框架负责组织 Agent 的执行流程harness 更像「怎么把这套流程安全地跑起来、管起来」LangSmith 则服务于这两者的共同需求——不管你是用 LangGraph、CrewAI 还是自研框架观测层都是独立的一层不要绑死在具体框架上。2.2 数据是怎么一层层「串」起来的一个典型的 Agent 调用在 LangSmith 里会长成这样的树形结构Tracerun_agent(上海周六适合户外活动吗) ├── SpanplanLLM 调用生成工具调用计划 │ ├── Observationprompt、completion、token usage │ └── Observationmodel 名称、温度 ├── Spancall_weather_tool调用天气工具 │ ├── Observation工具入参 { city: 上海, date: 2025-12-06 } │ └── Observation工具出参 { temperature: 8, condition: 小雨 } ├── Spanread_resultLLM 调用根据工具结果生成回复 │ └── Observation输出文本关键在读树的方式每一个 Span 都是独立的计时单元你可以看到它耗时多久、消耗了多少 Token、返回了什么内容如果某个 Span 抛异常LangSmith 会标红并把堆栈和错误信息带出来。排查问题时不用去猜直接在树上点开可疑节点看细节就行。这棵树是怎么来的原理其实很朴素——LangSmith SDK 会拿到当前运行上下文里的 Trace ID 和父 Span ID用它们把每个节点串成树。这也是为什么后面你会看到接入 LangSmith 本质上就是「给函数套装饰器、加上下文」让 SDK 知道每一步都属于哪条链路。3. 十分钟接入环境配置与最小可运行示例3.1 创建账号、拿 Key、配环境变量接入的第一步是去 LangSmith 官网注册账号创建一个 Workspace然后在 Settings 里生成 API Key。API Key 的格式一般是ls__开头这个 Key 用于识别你是哪个团队的请求所以要保管好别 commit 到公开仓库里。安装依赖很简单一条命令pip install langsmith openai python-dotenv关键环境变量有三组我直接列成表格环境变量作用LANGCHAIN_TRACING_V2true开启 v2 协议上报LANGCHAIN_API_KEYls__xxx认证身份LANGCHAIN_PROJECTmy-agent指定项目名还有一个经常被忽略的LANGCHAIN_ENDPOINT。默认指向 LangSmith 官方云服务如果你用的是自托管或内网部署版需要改成对应地址。生产环境如果走内网建议把它显式写进配置避免默认域名连不通。关于 Key 管理我建议不要在代码里写死用一个 .env 文件配合 python-dotenv 读取或者直接注入到 CI/CD 环境变量里。别小看这一步我见过好几个项目因为 Key 写在测试代码里泄露出去被迫重置。3.2 用 traceable 装饰器完成最小接入假设你现在有一个最朴素的 Agent一个函数调模型一个函数调工具一个函数把两者串起来。最小可运行示例是这样import os from dotenv import load_dotenv from openai import OpenAI load_dotenv() # 接入 LangSmith 的最小配置 os.environ.setdefault(LANGCHAIN_TRACING_V2, true) os.environ.setdefault(LANGCHAIN_API_KEY, os.getenv(LANGCHAIN_API_KEY)) os.environ.setdefault(LANGCHAIN_PROJECT, quickstart-agent) client OpenAI()然后用 traceable 装饰器包装函数from langsmith import traceable traceable(run_typellm, namecall_llm) def call_llm(prompt: str) - str: resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: prompt}], ) return resp.choices[0].message.content traceable(run_typetool, nameweather_tool) def get_weather(city: str) - dict: return {city: city, temperature: 8, condition: 小雨} traceable(run_typechain, namerun_agent) def run_agent(query: str) - str: plan call_llm(f用户提问{query}。请判断是否需要查天气并返回要查询的城市名。) city plan.strip() weather get_weather(city) answer call_llm(f根据天气信息 {weather} 回答用户问题{query}) return answer跑一次run_agent(上海周六适合户外活动吗)然后打开 LangSmith 控制台你会立刻看到一条 Trace根节点是 run_agent下面两个 LLM Span 和一个工具 Span 清清楚楚。这里有几个细节值得说run_type参数llm、chain、tool、retriever、embedding 等。它决定在 UI 里显示的图标和着色方便你一眼分辨哪一步是模型调用、哪一步是工具执行。name参数不写的话默认取函数名。建议显式命名因为生产环境下函数名可能被混淆或缩写。装饰器默认记录函数的入参和出参。如果 Prompt 里可能带敏感信息建议用 metadata 或参数排除来控制哪些数据进 Trace。这段演示用的是最朴素的循环没有引入 LangChain。实际你如果用 LangChain 的 AgentExecutor、LangGraph 的状态图LangSmith 的集成会更自动化——框架会在关键位置自动埋点不用你每个函数都手动加装饰器。但对自建 Agenttraceable 就是最好的接入方式。对于异步函数它也支持直接装饰 async def 即可调用时照常 await注意异常和取消事件要处理干净否则容易上报不完整。4. Agent 场景的关键埋点多步推理、工具调用、费用统计4.1 把 Agent 的每一步都变成 Span接完最小示例后你可能会问我的 Agent 不止三步有反思、记忆、多个工具循环怎么让链路更清晰我的做法是坚持一个原则一个「动作」就是一个 Span。具体来说每次 LLM 调用单独形成一个 llm 类型的 Span每次工具执行单独形成一个 tool 类型的 Span把「规划」「反思」「写摘要」这类逻辑块包一层 chain 类型的 Span用父 Span 把一组相关动作串成「阶段」比如一个典型 ReAct 循环traceable(run_typechain, namereact_loop) def react_loop(query: str, max_iterations: int 3): messages [{role: user, content: query}] for i in range(max_iterations): thought call_llm(messages) action parse_action(thought) if action[type] finish: return thought tool_result execute_tool(action) messages.append({role: assistant, content: thought}) messages.append({role: tool, content: str(tool_result)}) return 迭代超限在这个结构里LangSmith 的树会显示每一轮循环的完整过程。你不仅能看出第几轮出了问题还能对比「Agent 是在哪一轮开始重复同一个工具」「哪一轮的 Token 消耗突然变大」。再给一个非常实际的操作建议在每轮循环开始时把轮次号写进 Span 名。做法是动态传给 name或通过 run 对象更新 display name。这样在 UI 展开时你一眼就能看到「第 1 轮」「第 2 轮」排查多轮 Agent 效率提升非常明显。4.2 用 Metadata 和 Feedback 做精细化追踪默认情况下Trace 只有入参、出参和耗时。但线上定位问题往往需要更多上下文。LangSmith 允许你给每次运行附加 metadata 和 feedback。Metadata 适合放和这次运行相关的业务字段from langsmith import traceable traceable( metadata{ user_id: U_12345, channel: app, agent_version: v2.1.0, region: cn-east, } ) def run_agent(query: str): ...这样在 Trace 列表页你可以直接按 user_id 或 agent_version 过滤比手动一个个点开看方便太多。对于多租户系统这个字段几乎是必需品。Feedback 则适合做线上质量信号采集。把用户的点赞、点踩、复制回答等行为回传给 LangSmithfrom langsmith import Client ls_client Client() ls_client.create_feedback( run_idtrace_id, keyuser_rating, score1, # 1 表示点赞0 表示点踩 comment回答信息过时, )这里的 run_id 怎么拿在你调用 run_agent 后可以通过 langsmith 的上下文工具拿到当前 run 的 ID或者在装饰器内部直接读取。很多团队觉得接反馈是「事后再说」的功能但我建议产品一上线就接。有了 feedback 加 metadata你就可以回答「v2.1.0 版本在 app 渠道的用户里被点踩的比例是不是比 v2.0.9 高了」这种数据对 Agent 迭代特别有价值。费用统计也要提一句。LangSmith 的 Trace 详情页会展示每个 LLM Span 的 Token 用量和预估费用但前提是模型提供商返回的 usage 字段被正确记录。用 OpenAI SDK 默认会带如果你用的是自部署模型或其他兼容网关记得把 usage 信息透传出来否则 UI 上看到的费用会是 0。这也是一个高频踩坑点。4.3 沉淀数据集把 Trace 用于批量评测链路可追踪的上层玩法是把你跑出来的 Trace 沉淀成评测数据集。LangSmith 里可以直接把某条 Trace 的输入输出保存成 example也可以手动导出一批问题作为测试集。日常操作我一般这样做from langsmith import Client client Client() dataset_name weather_agent_test client.create_examples( inputs[{query: 上海周六适合户外活动吗}], outputs[{answer_contains: 小雨}], dataset_namedataset_name, )然后用这个数据集批量跑 Agent再通过 LangSmith 的 Evaluator 给回答打分。这样每次改 Prompt、换模型都能快速跑一遍回归而不是靠感觉判断「好像变好了」。注意不同版本 SDK 的评测 API 细节略有差异依赖版本以官方文档为准但核心思路是一样的没有数据集就没有回归没有回归Agent 迭代就是裸奔。5. 常见问题与排查技巧实录5.1 Trace 没上报的几类典型原因接入 LangSmith 后最常见的挫败感来自「代码跑完了控制台什么都没有」。我按出现频率排序整理一下典型原因一是环境变量没生效。最常见的是 LANGCHAIN_TRACING_V2 没设成字符串 true或者 Key 设置位置和代码执行顺序不对。python-dotenv 的 load_dotenv() 必须在读取环境变量的代码之前执行否则你用 os.environ 读到的还是旧值。二是项目名拼写不一致。控制台里的 Project 名区分大小写。如果 LANGCHAIN_PROJECT 设置了 MyAgent而你在 UI 里打开的是 myagent看起来就像丢数据了实际只是换了个项目存储。三是装饰器没作用于异步函数或流式场景。traceable 对 async 函数支持没问题但如果你在流式输出里直接套装饰器有时只能拿到最终内容拿不到中间增量。对流式场景建议每次都查一下官方对 stream 的专门说明别凭感觉处理。四是上报被采样或网络不通。某些部署环境默认对 Trace 做了采样或者公司内网屏蔽了 LangSmith 域名。排查方法是在本地跑一次最小示例本地能上报那基本就是网络或代理问题本地也不行回到前三条检查。5.2 排查技巧与日常调优心得最后分享几个实践经验。第一先建一个「最小链路」沙箱项目。我习惯在 LangSmith 里单独建一个 playground 项目专门用来跑最小示例。排查问题时先在 playground 里复现避免和线上流量混在一起也避免污染正式项目的指标。第二善用 Trace 详情页的对比和标注功能。同一输入、不同版本跑出来的 Trace 可以并排对比你才能理解一次改动到底产生了什么影响。这个功能在调 Prompt、换模型时特别好用——肉眼对比两个 Trace 的每一步差异往往能直接找到性能瓶颈或逻辑漂移点。第三生产环境建议开启采样而不是全量上报。Agent 的 Trace 数据量不小每个 Span 都带完整 Prompt全量上报既费流量又费配额。LangSmith 支持按比例采样通过 LANGCHAIN_TRACING_SAMPLING_RATE 设置服务稳定后我一般把采样率压到 10% 左右但保留错误 Trace 全量上报。这样既保证排障能力又不会让成本失控。第四也是最重要的一条把 LangSmith 当「事实来源」而不是「事后查阅工具」。养成每次 Agent 改动后都去翻 Trace 的习惯就像后端同学改完接口要看监控一样。链路可追踪这件事价值不只在出问题时才体现更在日常迭代里每一次「哦原来它这一步是这么走的」的顿悟。我个人用下来的体会是LangSmith 的学习成本其实很低真正有门槛的是「链路思维」——你能不能把一个自由决策的过程拆成可观测的节点。这个思维一旦建立不只是工具好用还会反向促使你把 Agent 代码写得更模块化。毕竟一个函数一个 Span代码结构不好Trace 也不会好看。下一步有空的话我打算再把 LangSmith 的在线评测和多版本对比单独写一篇那部分对换模型、调 Prompt 的帮助更大。