用 LangSmith 打通 Agent 链路追踪:从 Trace 到回归测试的实战指南

发布时间:2026/10/7 13:28:44
用 LangSmith 打通 Agent 链路追踪:从 Trace 到回归测试的实战指南
上个月帮朋友排查他们内部客服机器人的 Bug问题特别典型用户提交退款申请机器人明明查询到订单状态是“已退款”最后却回复“退款正在处理中请耐心等待”。从结果看这就是一次标准幻觉但最让人头疼的从来不是幻觉本身而是你完全不知道它从哪一步开始跑偏——是查询工具没被调用是工具返回内容被截断还是模型在最后归纳时犯了傻在没有追踪系统的情况下这类问题只能靠猜往 prompt 里多加几句“注意不要瞎说”然后祈祷。后来我把这套系统接入了 LangSmith。LangSmith 是 LangChain 团队推出的 LLM 应用可观测平台核心就是解决 Agent 链路“过程黑盒”的问题。你只需要做最小化的配置之后每一个 Agent 任务从开始到结束的完整路径包括每次 LLM 调用、每个工具请求与响应、每一步决策都会被记录成一条可浏览、可检索、可回放的 Trace。这篇文章我就以一个正在把 Agent 项目往生产推的工程师视角聊聊怎么用 LangSmith 打通 Agent 链路追踪以及我在真实项目里踩过的一些坑。1. 先搞明白一件事Agent 为什么必须要“可追踪”1.1 Agent 应用和传统接口调用差在哪传统后端接口是确定性流水线请求进来经过预设逻辑返回结果。出错时你有异常堆栈、有断点调试、有日志上下文哪怕链路再长也能靠调用链工具一层层翻到底。Agent 应用不一样。它是由模型驱动的决策循环LLM 先理解用户目标决定下一步动作调用工具后拿到结果再重新判断循环往复直到模型认为任务完成。这导致同一个问题的执行路径每次都可能不同而且绝大多数“过程”发生在模型的隐层思考里。我习惯用一个类比传统系统像工厂里固定好的传送带哪里卡住一眼就能看到Agent 则像一个有自主裁量权的临时员工你必须全程看着他办事才知道他把活干成了什么样。大家现在都在聊 agent 开发、agent 架构、agent 框架与编排但很多团队只关心“怎么把 Agent 接进来”却忽略了真正上线后会遇到的问题你不知道它每次到底干了什么。1.2 Agent 链路里最容易翻车的几个环节做 agent 应用时间长了你会发现Agent 的幺蛾子往往就出在这几个地方环节常见问题在链路里怎么看意图理解用户真实诉求和任务目标偏移看 LLM 节点输入里是否包含完整上下文工具选择多个工具时选错、或者干脆不调用工具看是否出现了 ToolCall 记录工具入参JSON 参数拼错、日期格式传错、字段缺位看工具节点的 arguments 字段工具结果处理返回过长被截断、格式异常、模型忽略结果看工具节点的 output 和后续 LLM 输入循环与终止Agent 反复调用工具停不下来或提前误判完成看 Trace 总步数和每个节点的耗时副作用安全工具带写入操作时危险调用难以被发现看全部工具调用记录做事后审计很多时候最终回答是错的但根因在中间某一步。没有哑链路记录你只能对着最终输出猜猜来猜去几乎不可能命中。1.3 LangSmith 在设计上把钱花在哪里LangSmith 不只是一个日志系统它把 LLM 应用的可观测性分成了几层链路追踪tracing、调试debugging、离线评估evaluation、线上监控monitoring。链路追踪是所有上层操作的地基。它的核心价值在于“全自动”。只要用了 LangChain 或 LangGraph代码里设置好环境变量框架会自动把整条 Agent 执行链上报不需要你为每一次 LLM 调用、每一个工具函数手工写埋点。这一点太重要了因为手工埋点的最大问题不是麻烦而是你永远会遗漏几个关键位置而真正出问题的地方恰恰是你没埋的那一步。当然LangSmith 现在也提供了手动接入能力不依赖 LangChain 也能用。对于已经用 LangChain 跑通的团队接入成本几乎为零这也是我推荐大家先上手的理由。2. 开始接入前先认清 LangSmith 的核心概念2.1 Project、Run、Trace、Span它们到底什么关系我见过不少开发者第一次打开 LangSmith 后台直接懵掉满屏英文词分不清谁是谁。先把概念理顺后面效率会高很多。一个 Project 相当于一个应用的日志“文件夹”通常我们按项目名或环境来建比如 agent-demo、prod-support-bot。一个 Project 里包含大量 Run。Run 是某个执行单元的记录可以嵌套最顶层的那个 Run 就是 Trace代表一次完整任务。Tracean 下面每一个具体步骤比如一次 LLM 调用、一次工具调用都叫 Span。这样理解会更顺Project 是巡检记录本Trace 是某一次巡检的完整记录Span 是巡检过程中一个个具体动作。你在 LangSmith 界面看到的树形结构本质就是 Trace 和 Span 的嵌套关系。2.2 最小化接入环境变量与初始化代码接入 LangSmith 的第一步不是写代码而是先拿到 API Key。登录 LangSmith 后台在 Settings 里创建 API Key格式一般是lsv2_开头。然后配置环境变量我推荐在应用启动脚本或环境配置文件里统一设置export LANGCHAIN_TRACING_V2true export LANGCHAIN_API_KEYlsv2_你的key export LANGCHAIN_PROJECTagent-tutorial如果你用的是较新的 SDK有的版本也支持LANGSMITH_API_KEY为了兼容起见两个变量都设上没坏处。依赖方面最少需要这些pip install langchain langchain-openai langsmith如果要用社区工具再补一个langchain-community。配置完环境变量LangChain 的链式调用、AgentExecutor、LangGraph 状态图会自动产生 Trace不需要额外传参。第一次跑通时你会看到后台出现第一条不完整或完整的链路那一刻的成就感非常直观。2.3 先想清楚采样策略与成本很多人接入 LangSmith 之后立刻开启全量上报跑了几天发现账单不太舒服。LangSmith 的定价和数据量挂钩虽然单条 Trace 费用不高但生产环境请求量大积少成多就明显了。我建议从一开始就把采样率定好。开发环境全量追踪没问题因为量小、调试需要生产环境使用LANGCHAIN_TRACING_SAMPLING_RATE控制采样比例比如只追踪 10% 的请求export LANGCHAIN_TRACING_SAMPLING_RATE0.1另外线上问题和开发问题要分 Project。同一个 Project 里混合开发流量和生产流量排查时很容易互相干扰。我见过朋友在生产集群上忘记切 Project结果一个 Bad Case 翻半天因为其他环境的 Trace 太多。按环境建 Project按需求调整采样比例是我刚开始接入时最后悔没做的事。3. 第一个可追踪的 Agent从代码到 Trace3.1 用 LangChain 跑通一个带工具的 Agent我们直接写一个最小可运行的 Agent它带一个天气查询工具用户问天气时LLM 判断需要工具调用它拿数据后组织回答。import os os.environ[LANGCHAIN_TRACING_V2] true os.environ[LANGCHAIN_API_KEY] lsv2_你的key os.environ[LANGCHAIN_PROJECT] agent-tutorial from langchain_openai import ChatOpenAI from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain_core.tools import tool tool def get_weather(city: str) - str: 返回指定城市的实时天气只接受精确城市名。 例如北京市、上海市。 # 生产环境这里换成真实天气 API return f{city}今天晴气温18-24℃体感舒适。 tools [get_weather] llm ChatOpenAI(modelgpt-4o-mini, temperature0) agent create_tool_calling_agent(llm, tools) executor AgentExecutor(agentagent, toolstools) resp executor.invoke({input: 北京今天需要穿羽绒服吗}) print(resp[output])跑完这段代码打开 LangSmith 后台的 agent-tutorial 项目你会看到一条新 Trace。这套流程里模型先规划是否使用工具然后生成一个 ToolCall框架调用 get_weather 并把结果送回给模型最后模型根据真实天气数据生成回答。整个过程被完整记录。3.2 打开 LangSmith逐层读懂节点信息进入某条 Trace 后你看到的结构大概长这样AgentExecutor 根节点整次任务 └── ChatOpenAI 调用模型决策生成 ToolCall └── get_weather 工具调用入参 / 返回结果 └── ChatOpenAI 调用读取工具结果生成最终回答每个节点都可以点开看到输入输出、开始结束时间、耗时、Token 用量、模型名等原始信息。排查问题的时候我最常看的就是工具的原始返回它是脏数据、是超时、是被截断一眼就能判断。比如刚才那个客服机器人案例我用 LangSmith 一查发现它压根没有调用查询工具模型直接凭印象生成了一句话。根因不在 prompt 最后那句“你要诚实”而在模型决策阶段没有把“先查单再回答”作为强制动作。这种定位没有链路追踪之前靠猜很难得出。3.3 换成 LangGraph 后 Trace 和代码一一对应LangChain 的 AgentExecutor 封装度高Debug 起来有时候还是隔了一层。最近 agent 框架的主流方向是更显式的编排LangGraph 逐渐成为标配。节点和边都由你定义链路追踪的逻辑和代码结构完全对应。用 LangGraph 重写上面的 Agentfrom typing import TypedDict, Annotated from langgraph.graph import StateGraph, START, END from langgraph.prebuilt import ToolNode from langgraph.graph.message import add_messages from langchain_core.messages import AnyMessage, HumanMessage class AgentState(TypedDict): messages: Annotated[list[AnyMessage], add_messages] llm_with_tools llm.bind_tools(tools) def call_agent(state: AgentState) - dict: response llm_with_tools.invoke(state[messages]) return {messages: [response]} builder StateGraph(AgentState) builder.add_node(agent, call_agent) builder.add_node(tools, ToolNode(tools)) builder.add_edge(START, agent) builder.add_conditional_edges( agent, lambda state: tools if state[messages][-1].tool_calls else END, ) builder.add_edge(tools, agent) graph builder.compile() graph.invoke({messages: [HumanMessage(content上海适合穿什么)]})这次 Trace 会清晰地出现两条路径agent 节点第一轮决策 └── tools 节点执行天气查询 └── agent 节点第二轮生成回答你写的是什么架构在 LangSmith 里看到的就是什么架构。节点粒度、边逻辑、条件判断全部可见排查 Agent 行为的体验会好很多。这也是我建议新项目直接上 LangGraph 的原因之一。3.4 非 LangChain 项目手动埋点与 wrap_openai如果你没有用 LangChain而是直接调用 OpenAI SDK或者自己封装了一层 HTTP 调用也不是不能用 LangSmith。官方 SDK 提供了traceable装饰器和wrap_openai包装器。以 OpenAI 为例接入方式非常轻量from langsmith.wrappers import wrap_openai from openai import OpenAI client wrap_openai(OpenAI()) resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: 今天北京天气怎么样}], )调用中的模型名、回调、Token 用量都会被 LangSmith 自动记录。traceable则适合包裹你自己的业务函数让每个手动步骤也变成一行 Span。区别就是你需要自己决定哪里埋点不像 LangChain 那样全自动但接入成本依然可控。4. 不只是记录把 Trace 变成真正有用的证据链4.1 从“哪里报错”到“为什么出错”链路追踪最容易踩的误区是把 Trace 当成“新式日志”只看“有没有报错”。真正要做的是把 Trace 当作证据链沿着它找到因果。我的排查套路固定是三步第一看有没有调用预期中的工具。这一步能区分“模型没规划对”和“工具执行出问题”。第二看工具参数和原始返回。参数拼错属于模型生成问题返回脏数据属于工具侧问题。第三看最终回答是否忠实于工具结果。如果工具明确返回“已退款”模型却说“正在处理中”那就是生成阶段忠实度不足。这个套路在 LangSmith 上操作起来很顺手因为每一步都有独立 Span你能快速跳转、对比。出问题后不要直接在结论上争论把证据链贴出来比一万句解释都有效。4.2 用 Playground 快速现场复现LangSmith 的 Playground 是我很依赖的功能。它可以在网页端直接加载一条 Trace 的配置重新跑一遍相同或修改后的输入不需要把代码仓库拉下来。我经常用它做两类事一类是复现 Bad Case把用户原始输入原封不动重发看看是不是稳定复现另一类是现场调参比如把 temperature 从 0 调到 0.3换一个 prompt 前缀立刻能看到对比结果。这里有个很实用的经验把“怀疑点”写成多条不同的 prompt 变体在 Playground 里来回切几分钟就能确定哪个改动真正解决问题。比在本地一遍遍改代码跑测试高效得多。4.3 人工反馈与迭代闭环好的链路追踪系统一定支持人工反馈。LangSmith 里可以在 UI 上给 Trace 点赞、点踩也可以通过 API 记录结构化反馈便于后续做数据分析。from langsmith import Client client Client() client.create_feedback( run_id带 id 的那条 trace, keyuser_score, score1, comment回答正确但有点啰嗦, )线上用户反馈、内部人工标注都会和对应 Trace 绑定。久而久之它就变成你评估集的一部分为后面的回归测试提供素材。5. 把追踪变成护栏回归测试与评估5.1 从历史 Trace 一键生成测试数据集Agent 项目迭代得越多越需要一个“考卷”。最缺的反而不是测试用例而是那些从线上捞回来的真实 Bad Case。LangSmith 支持直接从 Trace 生成数据集你看到一条代表性的历史记录点一下 Add to dataset它就会被固化进指定的 Dataset。我通常把数据集分成两类一类是正确行为防止回归一类是历史 Bad Case验证修复有没有生效。这样每次改 prompt、换模型、调整工具逻辑都有了一个可复现的基准。5.2 离线评估跑起来有了 Dataset下一步就是在 LangSmith 里跑离线评估。用代码触发或者直接在后台创建测试任务框架会自动把数据集里的每条输入跑一遍然后用你定义的评估器打分输出一个可比较的实验结果。简单示意如下from langsmith.evaluation import evaluate def run_agent(inputs: dict) - dict: answer executor.invoke({input: inputs[input]}) return {output: answer[output]} evaluate( run_agent, dataagent-tutorial-dataset, )你可以加自带评估器也可以写自定义规则。LangSmith 会把两次实验的得分放在一起对比让你快速看到这次改动的平均表现和分项表现。这一步做下来prompt 迭代就不再是拍脑袋而是有“考试成绩”支撑的工程行为。5.3 发布前先问自己新 prompt 会让老问题反弹吗我吃过最典型的亏是修好了 A 类 Bad Case结果 B 类历史问题复发。原因很简单改 prompt 通常是在“压”模型某一类行为而模型行为是整体联动的很容易顾此失彼。现在我的习惯非常固定每次要动 prompt 或换模型先跑一遍整个历史数据集看分数有没有下降。如果之前的重点场景出现回归宁可不改或者继续调。没有回归测试这套护栏Agent 的迭代几乎必然走向“按下葫芦浮起瓢”。6. 实战排雷LangSmith 使用中的高频坑与心得6.1 Trace 不出来先按这个顺序排查接入 LangSmith 最常遇到的尴尬是代码跑完了后台一条 Trace 都没有。我见过的情况基本集中在这几类现象原因处理方式完全无数据环境变量没设置或 Key 填错检查LANGCHAIN_TRACING_V2和 API Key数据跑到旧项目LANGCHAIN_PROJECT没更新确认当前进程的环境变量偶发缺失采样率设置过低把LANGCHAIN_TRACING_SAMPLING_RATE调高或临时去掉程序结束无输出异步上报没刷新进程退出前调用flush()另外我要提醒一下如果你所在网络环境访问 LangSmith API 不稳定链路会静默丢数据。遇到“偶尔有 Trace、偶尔没有”的情况先检查网络连通性和超时日志不要一上来怀疑 SDK。6.2 成本与性能怎么控采集本身是异步的对主流程延迟影响很小但极端情况下大量 Trace 同时上报还是可能出现堆积。我这里有几个实用的控制手段生产环境默认采样遇到重大发布或线上事故再临时开全量。按环境拆分 Project方便按项目配额管理。定期清理旧项目或用完的 Dataset避免后台数据越来越乱。核心链路全量追踪非核心链路低频采样留存最有价值的证据。成本控制的核心思想是把追踪作为一种“可调节的观测手段”而不是无脑全开。6.3 数据安全、权限与团队协作Agent 的 Trace 里往往包含用户输入、工具调用参数、检索结果甚至可能是拼凑出来的隐私信息。如果你的业务对数据合规有要求上云端的默认区域前必须先确认数据策略。LangSmith 支持私有化部署适合要求数据不出内网的团队。团队协作方面LangSmith 支持成员角色管理和项目权限隔离。建议按团队或模块分 Project只给需要的人开权限。另外尽量别在生产环境把 API Key 硬编码在代码里用密钥管理服务权限最小化。6.4 一点个人体会用 LangSmith 时间越久我越觉得它的真正价值不是“看链路”而是逼着我把 Agent 的调用结构想清楚。Agent 开发看上去很自由但如果没有可观测性兜底它就是一团看不清的黑箱。链路追踪做起来之后prompt 迭代、模型选型、工具设计都会从“玄学”变成“工程”。最后分享一个小技巧给自己业务日志里加上 request_id 或 session_id然后通过元数据写进 LangSmith。线上用户报问题时你拿着业务侧 ID 一搜就能定位到那条 Trace时间从半小时缩短到几秒钟。生产排查体验的提升是立竿见影的。