做 AI Agent 的人,多半都有同一种感受:模型效果不好不可怕,最怕的是出了 Bug 你根本没法查。传统后端出问题,打开日志、看报错、翻调用链,基本能定位。Agent 应用完全是另一回事——一次回答背后可能调用了十几次甚至几十次大模型,中间穿插着工具返回、上下文拼接、路由判断,任何一环悄悄跑偏,用户感知到的只是“它答非所问”,而你在后端连个像样的堆栈都拿不出来。
我在带一个客服型 Agent 项目时,对这种痛点体会特别深。线上偶尔反馈“这个机器人又在乱说了”,本地死活复现不了,最后靠 LangSmith 做全链路观测,把整条推理链路上每一个输入输出抓出来,问题才真正按住。这篇文章算是一次使用复盘,聊清楚 LangSmith 怎么接、怎么用、能解决哪些问题,以及哪些坑是我踩过之后不希望你再踩的。
1. AI Agent 为什么需要全链路观测
1.1 传统日志体系在这里为什么失灵
传统 Web 服务的调用是确定性的。请求进来,经过固定代码路径,返回结果。你只需要在关键节点打日志,出了问题顺着链路就能找回来。但 Agent 不一样,它的执行路径不是写死在代码里的,而是模型根据当前上下文、工具返回结果、历史消息现场“决策”出来的。
举个最简单的例子:同一个问题,用户多问了一句“顺便帮我查下天气”,Agent 就可能多走一次工具调用。也就是说不存在一份固定的调用堆栈供你事后对照。你想复现一个线上问题,往往需要复现当时完整的对话上下文,这在用户量大起来以后基本做不到。
所以传统日志体系在这里至少有三个盲区:
- 日志只记录了业务侧的入参出参,记录不到每一轮的 prompt 拼了什么内容。
- 对工具返回的数据没有完整的快照,脏数据导致模型跑偏的时候根本不知道源头在哪。
- 模型调用有随机性,同样的输入两次输出可能不同,靠经验猜不如靠回放看清。
我当时最大的挫败感来源就是“猜”。猜是上下文问题,猜是模型问题,猜是工具超时。猜来猜去,效率极低。
1.2 一个“幽灵故障”是怎么被揪出来的
分享一个真实发生过的现场。客服 Agent 接到一个订单查询请求,用户说“我的订单还没发货”。正常流程应该是 Agent 先查出订单列表,再调用物流工具查进度。但线上有段时间出现偶发情况:Agent 不直接回答,反而反问用户“请问您的订单号是什么”。
从业务日志看,推理工具确实被调用了,返回结果也正常,但 Agent 给出的回复就是不对劲。我把日志翻来覆去看了好几遍,Local 复现又一直正常,一度怀疑是模型概率问题。直到接上 LangSmith,才看到整条 Trace 里某一个环节的真相。
问题出在上游另一个 Agent。它在会话上下文里生成订单号时,有一次因为外部接口超时,落库了一个默认占位符“N/A”。等客服 Agent 后续拿到这个字段去查物流,查询工具正常返回了“未查询到有效订单”,这个结果再被塞回上下文,模型判断“无订单信息”,于是进入了话术兜底分支,反问用户订单号。
这一整条因果链牵涉两个 Agent、两次大模型推理、一次外部接口超时,中间任何一个环节,用传统日志都看不到全貌。但 Trace 把它从头到尾串起来了。这正是全链路观测的意义:你不仅要看到“某一个调用返回了什么”,还要看到“这个结果是在什么上下文中被接收的”。
2. LangSmith 的核心设计:从调用链到评测体系
2.1 几个必须先搞懂的概念
LangSmith 的基础概念不复杂,但对第一次接触的人来说,最容易被术语绕晕。我按自己的理解给你捋一遍。
| 概念 | 一句话解释 | 类比 |
|---|---|---|
| Run | 一次最小执行单元,比如一次 LLM 调用、一次工具调用、一次检索器查询 | 快递链路里的一个站点 |
| Trace | 多个 Run 按调用关系组成的树,展示一次 Agent 任务的完整流水 | 从商家到用户手的全流程 |
| Project | 逻辑隔离的容器,一般按环境或业务线分 | 独立的快递仓库,各管各的 |
| Dataset | 用于评测的任务样本集 | 试卷 |
| Evaluator | 对 Trace 或输出打分的规则/模型 | 阅卷老师 |
你用 LangChain 或 LangGraph 写 Agent 时,每一次model.invoke()、tool.execute()、retriever.search()都会被自动包装成一个 Run。这些 Run 自带上层调用关系,LangSmith 拿到以后按照时间线和父子关系拼成 Trace。
在 UI 里你会看到一个一左一右的树形瀑布图,每一条树干代表一次完整 Agent 任务,点开以后每一层都能看输入、输出、耗时、Token 数。这不是事后拼接的日志,而是执行时就被埋好的结构化因果链,所以它有传统日志给不了的回放能力。
2.2 光“看得到”还不够,得把评测做进闭环
全链路观测如果只是给一个可视化界面,那它顶多算是一个升级版日志平台。LangSmith 真正的增值点在于:它把观测数据和评测能力绑在了一起。
什么是评测?你可以把线上跑过的优质对话收集起来,作为正样本建一个 Dataset。之后每当 Agent 逻辑改动,你可以拿这个数据集重新跑一遍,让系统自动给结果打分。打分方式既可以是简单的 Python 规则,比如回答里是否包含关键字段,也可以是用另一个大模型当裁判,按“正确性”“语气”“是否完整引用工具结果”等维度打分。
我用得最多的组合是:先靠 Trace 发现“某类问题在上下文缺失工具结果时会出现”,然后把这类样本打入 Dataset,写一个 LLM-as-a-judge 的评估规则,再修改 prompt 或路由逻辑,用网格跑测试验证修复是否生效。
换句话说,LangSmith 帮你把“发现线上问题”这件事从偶发的人工排查,变成了可持续的回归测试。这是 Agent 工程化里特别重要的一环。模型输出天然不稳定,如果评测不沉淀成测试集,每次迭代都是带盲区上线。
3. 实操接入:把 LangSmith 跑起来
3.1 最小配置:两步就能出 Trace
接入过程比想象中简单,最基础的方式连代码都不用改,只用环境变量。
先去 LangSmith 官网创建一个账号,拿到一个项目 API Key。Key 的格式一般是lsv2_开头。然后在你启动 Agent 进程的环境里加上以下几项:
export LANGCHAIN_TRACING_V2=true export LANGCHAIN_API_KEY=lsv2_你的key export LANGCHAIN_PROJECT=my_agent_demo设置完以后,只要你的 Agent 项目是用 LangChain、LangGraph 或 LangChain 生态的组件搭的,所有通过组件触发的大模型调用、工具调用、链式逻辑,都会自动上报到 LangSmith 对应的 Project 里。什么都不用改,运行一次任务,去 Web 控制台刷新,就能看到第一条 Trace。
如果你的环境不是纯 LangChain,或者你只想在特定进程里开启追踪,也可以在代码里显式初始化:
from langsmith import Client client = Client( api_url="https://api.smith.langchain.com", api_key="lsv2_你的key", ) # 相当于在进程内开启追踪 from langchain_core.tracing import set_tracing_callback_manager不过说实话,日常项目里环境变量那一套是最省事的,改代码反而容易漏。跑不同的业务模块时换个LANGCHAIN_PROJECT再启动进程即可,不用动业务逻辑。
3.2 在 LangGraph Agent 里接入观测
如果你的 Agent 是用 LangGraph 写的,接入观测几乎零成本,而且 LangGraph 的节点执行信息也会被完整记录。这里给你一个最简的 ReAct Agent 示例:
from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langgraph.prebuilt import create_react_agent @tool def get_weather(city: str) -> str: """返回指定城市的天气信息。""" return f"{city}今天多云,气温 24 摄氏度。" model = ChatOpenAI(model="gpt-4o-mini", temperature=0) agent = create_react_agent(model, tools=[get_weather]) resp = agent.invoke({ "messages": [{"role": "user", "content": "北京今天天气怎么样?"}] }) print(resp["messages"][-1].content)只要环境变量在前面已经配好,这段代码跑完,LangSmith 控制台会自动出现一条 Trace。你能看到它分为 “Agent 节点” “工具调用 get_weather” “最终回复” 几个层级,每一层分别耗时多少、消耗了多少 Token、输入输出是什么。
有一点实际经验值得注意:在 LangGraph 里做复杂 Agent 时,节点命名很重要。默认的节点名如果是一串难以理解的英文,Trace 可读性会很差。建议在定义 Graph 时,给每个节点一个语义化的 name,比如retrieve_order、check_refund_policy,这样排查问题时一眼就知道执行到哪一步,也方便后续用名字做过滤器。
3.3 不用 LangChain 也能接:SDK 直连方式
不是所有团队都用 LangChain。有些项目会直接用 OpenAI SDK 写循环,有些用 Spring AI,极端的还有用 Rust 从零搭 Agent 运行时。这些场景下,LangSmith 依然可以接入,因为它本质上是一个可上报的追踪后端,只不过提供了 LangChain 生态的自动埋点。
你可以通过官方 Python 或 TypeScript SDK,手动创建 Run 和嵌套子 Run。代码结构大概是这样的:
from langsmith import Client client = Client() # 开启一条自定义 Agent 执行记录 run_id = client.create_run( name="custom_agent", inputs={"question": "用户问题原文"}, run_type="chain", ) # 你的Agent实际逻辑,内部可以再嵌套LLM Run或Tool Run # ... client.update_run( run_id, outputs={"answer": "最终回答"}, )这种手动埋点的方式也适用于 Rust 或 Java 生态。你可以在网关层统一封装一个上报函数,把每次模型请求和工具返回序列化后发到 LangSmith。我们组有一个用 Rust 写工具的同事,他最后就是在网关层做了统一上报,而不是在每个工具函数里手写埋点,效果一样,还省得侵入业务代码。
4. 全链路观测的实战要点
4.1 拿到一条 Trace 后,先看哪几个指标
Trace 页面打开以后,信息量很大,新手容易一头扎进输入输出文本里。我的建议是,按以下顺序看:
第一是总耗时和各级耗时。整个 Agent 任务用了 40 秒,到底卡在哪一步?如果大模型调用占了 35 秒,可能是模型响应慢,也可能是上下文太大导致首 Token 延迟高。如果工具调用占了 20 秒,那问题大概率在上游 API。
第二是 Token 消耗分布。Agent 反复调用模型,经常出现“上下文越滚越大、单次调用输入 Token 数千”的情况。Trace 里会标注每次调用的输入输出 Token 数,看到某层输入异常大,就该怀疑是否有历史消息被重复注入。
第三是错误信息和重试标记。LangSmith 会记录异常堆栈,Rate Limit、超时、格式解析错误都会显示。特别要注意“看起来成功但实际上内容不对”的情况。模型返回 200 不代表没出错,有可能返回的是兜底话术,这需要看输出内容本身去判断。
有一条 UI 使用习惯值得分享:排查具体用户问题时,不要只在“最近 Trace”列表里刷。优先用 Trace 里的搜索框按 metadata 过滤,或者按时间范围、Token 数排序,要么精准定位要么排掉干扰项。
4.2 用 Metadata 和标签做业务维度的切片
线上 Agent 服务用户量大,所有 Trace 混在一个 Project 里,等于没有观测。我习惯在每次调用时写入业务元数据,LangSmith 支持通过 metadata 字段携带业务维度信息。
以 LangChain/LangGraph 为例:
agent.invoke( {"messages": [{"role": "user", "content": "我要退款"}]}, config={ "metadata": { "user_id": "u_10086", "session_id": "s_20240101", "channel": "app", "env": "production", "business_line": "after_sale" } } )这些键值对会跟着 Trace 一起存储。之后在 LangSmith 的查询界面里,你就能按user_id、session_id过滤出某一个用户当天的所有 Agent 操作。业务侧客服反馈“某个用户昨天在 App 端遇到了问题”,你可以用 channel 和 user_id 两个条件精准筛出当时的原始链路。
我还会在 metadata 里加一个version字段,记录当前 Agent 的 prompt 版本或代码发布版本。这个字段看起来简单,但对复盘“这个版本上线之后召回率下降”这类问题非常有用。没有版本标记,你以为在对比数据,实际上比的是不同代码时期的产物,结论全是错的。
4.3 把成本“观测”起来
Agent 项目里最大的隐藏成本不是推理模型,而是被中间反复调用堆积出来的 Token。传统应用没有这个概念,后端逻辑多跑几次最多是 CPU 多点负载,Agent 多跑一轮模型就是在烧钱。
LangSmith 在每次 LLM Run 上都会记录 Token 数,并且按模型单价折算成本估算。我建议每个项目从第一天就关注这几个指标:单次会话平均 Token 成本、工具调用失败后的重试成本、上下文滚大之后的边际成本。
我实际遇到过一个场景:Agent 在做多轮问答时,每轮都把完整聊天记录发给模型,导致第 10 轮时单次输入 Token 已经接近上下文窗口上限。单看每一轮不觉得贵,用 LangSmith 按 session 聚合看趋势后,才知道成本曲线是陡增的。后来改成按需截断历史消息策略,总成本降了差不多 60%。
5. 生产环境常见问题与排查技巧实录
5.1 高并发场景下观测采样怎么取舍
很多人一上来就把 Agent 服务的所有调用 100% 接入 LangSmith,结果跑到某天发现上报量大、费用上涨、存储与检索变慢。这里“怎么扛并发”的关键不一定在客户端,而在于采样策略。
我建议分环境区别对待:
- 开发环境:100% 全量上报,哪怕成本高一点,但要保证每次实验都有完整回放能力。
- 生产环境:按需采样。LangSmith 支持设置采样率,你可以配置 trace_sample_rate,比如线上只采样 10%,但把错误链路和关键业务链路设为 100% 上报。
怎么实现关键业务全量?你可以在调用代码里做判断。比如支付、退费等高风险意图的会话,把 metadata 里标记must_trace=true,同时开启一个单独的强制上报通道。这样既保留了大部分业务场景的排查能力,又不会让所有高并发流量把观测平台打爆。
采样还有一个隐藏作用:它强迫你想清楚“什么业务真的需要精细观测”。不是所有 Agent 调用都值得全链路记录,把有限的存储留给高价值请求,是生产级工程的正确思路。
5.2 大模型调用失败的排查链路
生产环境里最恶心的问题之一是大模型接口偶发失败。原因五花八门:触发限流、上下文超长、网关超时、返回格式不合法。以前排查这类问题只能看服务端日志里的一行报错,而 Agent 往往在调用前已经拼了很长的上下文,只靠一个错误码完全搞不清触发条件。
接上 LangSmith 以后,排查路径就清晰了。一次超时异常,你会在 Trace 里看到:
- 完整的 prompt 详情,包括系统提示词、工具返回内容、历史轮次。
- 请求耗时和重试次数。有些模型 SDK 会自动重试,Trace 里能看到 Retry 标记,定位到是“第一次超时后重试成功”还是“重试仍然失败”。
- 异常类型和报错原文。上下文超限、Rate Limit 这类错误在 Trace 树上一目了然,不用再去查云服务商的纯裸日志。
有一次线上问题很邪门:Agent 偶尔返回“抱歉我无法处理你的请求”。看返回内容是正常的,也没有任何异常。Trace 一拉,发现工具层返回了一个超长字符串,把它塞进 LLM 之后,上下文压缩策略把关键指令给截掉了,导致模型判定无法回答。这种事情如果没有完整 Trace,光靠观察最终结果,永远定位不到工具返回长度这个根因。
5.3 敏感字段的脱敏问题,别等出事再处理
观测平台把所有输入输出都记录下来,这对排查问题很方便,但也直接带来了数据安全和隐私问题。Agent 在客服场景里经常会拿到手机号、订单号、身份证等信息,如果原样上报到观测平台,不合规且风险极大。
我的处理习惯是:在埋点上报之前,对敏感字段做系统级脱敏。这不是靠提示词要求模型“不要输出敏感信息”,模型做不到稳定承诺。正确做法是在业务代码的边界层,把手机号、身份证号等字段做正则替换,如138****1234,订单号保留前后几位即可。
LangSmith 本身也提供一些数据处理能力,比如在 SDK 里配置阻断规则或者在上报前对输入输出做二次处理。但我还是那句话:不要把敏感信息发出去再回来清洗,源头就别让它流出。观测要的是结构化信息,不是隐私原文。
6. 选型对比与个人体会
6.1 LangSmith 与同类工具怎么选
说到全链路观测,可选的方案不止 LangSmith。我用过的还有 Langfuse 这类开源或半开源方案,也见过团队直接基于 OpenTelemetry 自建链路监控。给你一张主观对比表,供参考。
| 维度 | LangSmith | 开源/自建方案 | 自建监控体系 |
|---|---|---|---|
| LangChain 集成度 | 极深,基本零代码 | 中等,需要配置 | 低,完全自己写 |
| 自动评测能力 | 内置 Dataset/Evaluator | 部分有,需自建 | 大概率没有 |
| 开箱即用体验 | 高 | 中 | 低 |
| 数据管控灵活度 | 受平台限制 | 高 | 最高 |
| 上手门槛 | 低 | 中 | 高 |
选型建议很简单:如果你整个技术栈深度绑定 LangChain / LangGraph,LangSmith 的自然优势非常明显,开箱即用,评测闭环也最完整。如果你的 Agent 是纯自研架构,用 Rust、Go、Spring AI 这类生态,或者公司对数据私隐管理要求极其严格,那自部署一个开源观测平台或者自建收集链路可能更务实,别被框架绑定拖住。
6.2 什么时候其实不需要全链路观测
不是所有项目都要上全套观测。刚起步的 Demo、个人玩具项目、原型验证阶段,直接打印日志看结果反而更快,没必要为了观测而观测。
真正需要全链路观测的信号,我觉得有三个:第一,Agent 已经开始服务真实用户;第二,问题不再能靠本地复现;第三,你正在高频迭代 prompt 和工具逻辑。满足任意两个,就值得接观测。
回到我自己的体会:Agent 工程化中,最贵的东西不是 Token,是排查问题的时间。一个线上问题如果靠猜,可能消耗整个团队一周;靠 Trace 回放,可能半天就定位。LangSmith 这种全链路观测工具解决的不只是“看得到”,它让 Agent 的推理路径变得可回放、可评估、可改进,这是一个把玄学变成工程的过程。
最后再分享一个我最近养成的习惯:每次改动 Agent 的 prompt 或工具逻辑,都强制先跑一遍历史问题集,在 LangSmith 里把评测结果和上一版做 Diff。刚开始觉得很折腾,坚持下来以后,线上回归问题明显少了。如果你也在维护一个投入生产的 Agent,建议从今天就开始准备一个属于你自己的问题集,把每一次线上翻车都沉淀进去。