☰
LangSmith实战:用可观测性拆解Agent黑箱
2026/10/22 8:11:44 网站建设 项目流程

先说个我真实踩过的坑。某开发者把一个带工具调用的Agent丢到测试环境,第一次问“某个客户的账为什么对不上”,Agent调用财务接口查到一笔异常,紧接着又自作主张调了订单接口、还调了库存接口,最后基于三个接口的数据“推断”出一份错误结论。整个过程代码没有任何报错,普通日志里十几行输出,你根本分不清它到底在哪一步开始跑偏。这种问题一套通用日志根本抓不住,非得上追踪系统不可。

这就是Agent开发的典型处境:模型给你生成出一条执行轨迹,但轨迹本身不可见。你越想把Agent做复杂,越要先解决“看得见”的问题。所谓AI Agent Harness Engineering,直白讲就是给Agent装上完整的外围工程设备——工具注册、权限边界、循环控制、超时回退、记忆管理、可观测性,这一整套统称harness。而LangSmith正是这套体系里负责可观测性和评估的那一块:全链路追踪、耗时与token分析、可视化、以及基于真实trace做回归评测。

这篇文章不聊怎么写prompt,而是站在Harness Engineering的角度,把LangSmith怎么拆掉Agent黑箱这件事讲透。我会从概念、配置、排障、评估四个层面展开,附上可直接抄的实操步骤和踩坑心得,适合正在用LangChain搭Agent、或者已经受够“print大法”的工程同学。

1. Agent Harness 到底在解决什么问题

1.1 Agent不是“一个模型调用”,而是一条动态执行链

先统一一下语境。我见过的Agent应用,无论包装得多花哨,本质上都是在一个循环里反复执行“思考→调用工具→观察结果→再思考”,直到模型认为任务完成。这个循环里任何一次“再思考”,都可能改变后续方向和工具选择,所以Agent应用根本不是单个LLM调用,而是一条运行期动态生成的执行链。

这条链有两个工程特点:

  • 不可预枚举:链条的长度、分支、工具组合,是模型现场决定的,开发阶段没法把所有路径测一遍。
  • 不可强约束:你没法写死“只能调这两个接口”,模型会根据上下文自己判断。

这就引出Harness Engineering的必要性。你没法预判模型会做什么,但你可以给它的行为装上轨道和仪表盘。轨道就是工具注册表、权限校验、参数校验、超时控制;仪表盘就是结构化日志、全链路追踪、指标监控、trace回放。轨道解决“它不能乱来”,仪表盘解决“它做了什么你看得见”。LangSmith属于后者,它带给你的不是让Agent变聪明,而是让你能审问它。

1.2 裸模型天然“黑箱”的三个根源

第一个根源是采样生成。同样一个问题,你连问两遍,模型可能选择不同的工具、给出不同的推理路径。这是它的工作机制决定的,不是bug,但对你来说就是不确定性。如果你只记录最终答案,整个决策过程就是一笔糊涂账。

第二个根源是中间推理不显式暴露。模型内部那些“我觉得应该先查一下A,再对比B”的过程,只有一部分会出现在文本里,而且有时候它还“想”了没说。你以为它跳过了某一步,其实它只是没把思考过程完整输出。

第三个根源是工具调用链路的复杂叠加。一次Agent运行可能要调外部API、查数据库、处理文件、做向量检索,每一环都可能出错,而且出错方式千奇百怪:超时、空结果、JSON格式不对、数据本身脏。模型拿到异常结果后还会基于错误信息继续推理,错误就像滚雪球一样被放大。

这三个根源叠加,就构成一个真正的黑箱:你不知道输入之后发生了什么,也不知道那句错误的结论到底从哪一步开始蔓延。

1.3 为什么print日志顶不上

我知道很多团队现在的做法是到处打日志,print(step)、print(tool_result)、print(prompt)。坦白讲,我早期也这么干过。试过几次后你会发现它有三个硬伤:

  • 没有层次结构。一个Agent主循环里嵌套工具调用,工具内又发LLM请求,print打出来全是平铺的线性文本。你看到10行日志,但哪个是父调用、哪个是子调用、哪个是重试、哪个是并行分支,完全靠脑补。
  • 内容被截断。工具返回一大段JSON,你一print就是几千行,或者直接在日志里被截断。真正出错的那一半内容往往被切掉了。
  • 没有成本和时间维度。每个步骤消耗多少token、花了多久,print打不出来,而这两个指标对定位性能问题和成本爆炸至关重要。

所以需要的是一个专门按“树形执行拓扑”来记录的系统:父节点是Agent循环,子节点是工具调用,孙节点是底层LLM请求,每个节点都记录输入、输出、耗时、token、状态。LangSmith的追踪模型就是按这个思路设计的。

2. LangSmith追踪模型拆解:从一次调用到一棵Run Tree

2.1 Trace、Span、Run到底指什么

LangSmith里最基本的对象叫Run,每个Run代表一次独立执行单元。一个LLM请求是一个Run,一次工具调用是一个Run,一个Agent循环也是一个Run,Run之间可以嵌套。

嵌套关系形成一棵Run Tree。整棵树的根节点是Agent的入口调用,叶子节点是最底层的LLM请求和工具执行。每次你在LangSmith打开一个Trace,看到的就是这样一棵树。

用快递物流来类比:整个大包裹从下单到签收是一条完整流水线(Trace),流水线里每个节点的处理动作是一条操作记录(Run),而它们之间的包裹关系就是嵌套结构。物流公司能告诉你包裹卡在哪个中转站,LangSmith能告诉你Agent卡在哪个环节。

2.2 自动追踪原理:LangChain回调机制

LangSmith之所以好用,很大程度是因为它和LangChain深度绑定,你基本不需要改业务代码。

LangChain内部有一套回调机制(CallbackHandler)。你在链上创建Agent时,LangChain会在每个关键节点触发事件:LLM开始调用、工具开始执行、链开始运行。LangSmith通过注册一个回调处理器,把这些事件自动采集下来,组装成Run Tree,再上报到服务端。

这意味着你只要设置好环境变量,现有的LangChain代码跑一遍,追踪就自动出现了。我自己的项目从零接入到看到第一条完整Trace,只花了不到十五分钟,其中一半时间还是在找API Key。

如果你不用LangChain,直接用其他框架或者裸调模型接口,LangSmith也有独立SDK可以用,后面我会讲手动埋点的方式。

2.3 手动埋点覆盖追踪盲区

自动追踪覆盖的是LangChain标准组件。但真实系统里总有一些代码不在LangChain体系内:你写了一个自定义函数,从公司内部系统拉数据;你调了一个外部HTTP服务;你做了复杂的后处理逻辑。这些盲区会导致一棵树缺枝少叶,一旦问题出在这里,Trace上就是一片空白。

这时候就需要手动埋点。LangSmith提供@traceable装饰器,你把它加到自定义函数上,LangSmith就能自动记录函数名称、入参、出参、耗时:

from langsmith import traceable @traceable(run_type="tool", name="fetch_invoice_data") def fetch_invoice_data(bill_id: str): # 拉取财务账单数据 return client.query(bill_id)

加上装饰器之后,这个函数在Trace里就会变成一个独立节点,有输入输出、有耗时。对于不在LangChain体系内的自定义逻辑,这是最优雅的补盲方案。

还有一个建议:善用metadata字段。每次Agent执行时,把业务相关的信息塞进metadata,比如session_id、user_id、order_id、environment:

from langchain_core.runnables import RunnableConfig config = RunnableConfig( metadata={ "session_id": "abc-123", "env": "staging", "user_id": "user_42", } ) agent.invoke(user_input, config=config)

这些元数据不会出现在模型可见的内容里,但会在Trace中保留,方便你按业务维度筛选。比如排查“哪个商家的Agent调用尤其慢”,你不需要翻原始日志,直接在过滤条件里输入user_id就行。

3. 从零接入LangSmith:完整配置与第一次全链路可视化

3.1 三步完成接入

第一步,注册并拿到API Key。进入LangSmith平台,在设置里创建API Key,记下以ls__开头的那串字符。

第二步,配置环境变量。在启动Agent服务的机器上,设置以下三个关键变量:

export LANGCHAIN_TRACING_V2=true export LANGCHAIN_API_KEY=ls__your_key_here export LANGCHAIN_PROJECT=agent-harness-demo

如果你在本地测试,也可以写在代码里,但建议用环境变量,避免把Key提交到Git仓库。

第三步,重新运行你的Agent。只要代码基于LangChain构建,Trace就会自动上报。打开LangSmith控制台,选择对应的Project,你就能看见刚才那次执行的完整轨迹。

3.2 不要省掉Project规划这一步

LANGCHAIN_PROJECT这个变量是很多人刚开始会忽略的。如果不设置,所有Trace会堆在默认的Project里,开发、测试、生产不分家,后面想按环境筛选会很痛苦。

我建议至少按环境分三个Project:agent-dev、agent-staging、agent-prod。如果项目足够大,还可以按业务线分,比如agent-payment、agent-invoice、agent-risk。成本核算、性能分析、错误定位,全都依赖这个基础分区。

同时给每次运行的根节点起个有意义的Run名称,别用默认的AgentExecutor。名称里带上业务语义,比如invoice_reconcile_agent,在列表页扫一眼就能知道哪条Trace对应哪个业务场景。

3.3 在UI和API中读一次Trace

打开一条Trace,你会看到从根节点到叶子节点的完整树形结构。每个节点都能展开,展示该节点的输入输出、耗时、token使用量、错误信息。我习惯按三个层次看:

  • 耗时视角:找出耗时最长的节点,通常是性能瓶颈所在。如果一个工具调用占了总耗时80%,问题大概率不在模型,而在下游接口延迟。
  • token视角:看每个节点的输入输出token数,重点观察消息列表有没有异常膨胀,这是上下文超限和成本飙升的前兆。
  • 错误视角:红色节点代表异常,点开能看完整报错堆栈,不用再翻服务器日志。

除了网页,LangSmith还提供Python SDK,可以编程式读取Run数据。这对批量分析特别有用:

from langsmith import Client client = Client() runs = client.list_runs(project_name="agent-prod", execution_order=1) for run in runs: print(run.name, run.start_time, run.error)

你甚至可以把过滤条件下的Trace数据拉下来做聚合统计,比如算平均工具调用次数、错误率、token成本分布。这些数据喂给监控系统,就形成了Agent自己的“业务指标”。

4. 可视化之后:三个“黑箱”被照亮的真实排查案例

4.1 案例一:Agent陷入工具调用死循环

现象是Agent在测试环境中不退出,一直调用同一个查询工具,直到被外层超时强杀。代码里明明有max_iterations限制,但线上似乎没生效。

Trace里看得很清楚:根节点下面的工具调用节点长得一模一样,连续出现七八个相同的子节点,每个节点的输入只是微微变了几个字。这是经典的“模型认为还需要更多信息,但工具返回的内容不满足它”的循环。

根因是模型在几次工具调用后,没有判断出“已经拿到足够信息可以收尾”,于是一次次发起新调用。可视化让这个模式变得极其显眼,平时靠日志根本察觉不到。

解法分两层:在harness层把最大循环次数从10改为5并加上熔断逻辑;在工具层增加“查询结果是否已足够”的提示,让它尽早收敛。改完后再次回放Trace,节点数大幅下降。

4.2 案例二:工具返回解析失败后模型开始“脑补”

有个Agent经常给出一本正经的错误结论。代码没有报错,但答案明显是编的。这种问题最恼人,因为它不像调用失败那样有明确的异常节点。

Trace里点开出错前的那次工具调用,发现工具返回了一段结构不完整的JSON,解析器抛了警告。但警告只被当作日志记录,没有被当作错误中断,模型拿到的仍然是那段半截文本。随后模型带着残缺数据继续推理,在下一步的LLM调用里,输出竟然自动“补全”了缺失字段。

这就是黑箱的典型表现:工具层的错误被模型吞掉,然后模型生成了一段看似合理的解释。如果没有Trace,你根本找不到那半截JSON才是万恶之源。

修复方式是给工具调用加上严格校验:返回结构不对就抛异常,让Agent进入错误处理分支,而不是把坏数据传给模型。同时在渲染工具结果时,对解析失败标一个PARSE_ERROR前缀,让模型明确知道这次数据不可信。

4.3 案例三:上下文被日志和工具输出撑爆

某Agent运行到某个节点后直接报上下文超限。模型侧的提示词根本改不出效果,因为问题不在模型,而在喂进去的内容太多。

Trace里查看消息列表的token变化,发现工具返回的历史记录被完整塞进了下一轮输入,每一次循环都翻倍膨胀。日志内容、工具输出、历史对话全被无差别拼接进上下文,导致第6轮时输入直接逼近模型上限。

根因是消息历史裁剪策略失效。定位之后,我在harness层增加了压缩逻辑:超过阈值后,把早期消息摘要成一小段,同时限制单次工具结果最大长度,多余部分截断。改完后token曲线趋于平稳,成本也随之下降。

这三个案例有个共同点:问题都不在模型本身,而在工程链路,而它们全都在Trace里被一眼定位。这就是可观测性对Harness Engineering的价值。

5. 从追踪到评估:把Agent行为固化成可以回归的质量红线

5.1 从Trace一键沉淀数据集

追踪不仅能看现场,还能把现场变成测试资产。LangSmith提供“将Trace添加到数据集”的功能,你在UI里勾选一条Trace,点击加入数据集,它就变成一个可复用的测试样本。

这对Agent测试的意义很大。过去造测试集只能靠人工编,编出来的Case往往脱离真实分布。现在你直接从生产环境积累Trace,每一条都带着真实的用户输入、工具返回和模型输出,测试的置信度高出不少。建议定期把生产环境的典型成功案例和典型失败案例都收录进两个不同数据集,分别用于回归验证和纠错训练。

5.2 用评估器自动打分

数据集建好之后,LangSmith可以帮你跑评估。评估分两类:一类是传统指标,如精确匹配、编辑距离;另一类更适合Agent场景,用LLM当裁判。

比如你想评估“模型是否选择了正确工具”,可以定义针对性的评估器,让它看模型的工具调用记录做判断。LangSmith支持自定义评估器,也内置了常用的LLM-as-judge模板:

from langsmith import evaluate from langsmith.evaluation import evaluate results = evaluate( my_agent, data="agent_success_dataset", evaluators=[ tool_selection_correctness_evaluator, answer_relevancy_evaluator, ], )

跑完自动生成评估报告,每个样本都有打分、理由和对比情况。你可以在Agent的prompt或工具配置调整之后,重新跑一遍,看分数有没有退化。这就把Agent的“好坏”从主观感觉变成可量化的数字。

5.3 把关键指标接进CI与在线监控

数据集和评估器就绪后,接下来是建立质量红线。

在发布流程里加一道Agent回归闸门:每次改动Agent代码,自动拉取固定评估集跑一遍,分数低于阈值就阻止合并。这条线在初期不用设太高,先把明显退化拦住,慢慢收紧。

在线监控则关注三类指标:错误率、平均工具调用次数、单次会话token成本。错误率反映工具链路稳定性;平均工具调用次数反映Agent收敛能力,次数突然升高通常是循环或无效调用增加;token成本反映prompt和上下文管理是否合理。这三种指标你可以定期用SDK拉取LangSmith数据计算,也可以对接到现有监控体系,跑出异常时告警。

评估阶段的价值在于:没有这一步,Trace只是事后诸葛,有了这一步,Trace能变成事前防线。

6. 半年实战记录:接入LangSmith常见问题与避坑技巧

6.1 数据安全与敏感信息处理

追踪系统会把input和output全部上传到平台,如果你的Agent处理的是敏感的客户数据,这是必须正视的问题。

LangSmith支持在客户端配置对上传内容脱敏或截断。我的临时方案很简单:手动埋点时,在装饰器里只返回处理后的摘要;如果必须传原文,就用环境变量只对开发环境开启完整Trace,生产环境只采样5%且开启脱敏字段。不要图省事把生产环境全量原始数据一股脑传上去,出过一次泄露事件就够你后悔半年。

6.2 成本与采样率

追踪本身会带来额外成本,包括请求量和平台费用。默认情况下它是全量上报,流量大的时候账单会比较可观。

LangSmith提供采样率配置:

export LANGCHAIN_TRACING_SAMPLING_RATIO=0.1

设成0.1代表只上报10%的Trace。我的经验是开发环境全量、生产环境按需采样,比如默认5%,出问题时临时调高到50%定位,定位完再调回来。不要长期维持100%,多数数据你根本不会回看。

6.3 常见问题速查表

现象可能原因处理方式
控制台看不到Trace环境变量没生效重启服务,检查LANGCHAIN_TRACING_V2和API Key
只有根节点,没有子节点自动回调没触发确认组件基于LangChain构建,自定义逻辑用手动埋点
错误节点显示乱码工具返回值非UTF-8在渲染工具结果前做编码转换
Trace延迟很大采样率过高或上报线程阻塞降低采样率,开启异步上报
消息列表token翻倍历史消息未裁剪在harness层加摘要压缩和截断逻辑
自定义函数不在Trace中未加追踪装饰器用@traceable包一层,并设置正确的run_type

6.4 我的一点收尾实战体会

最后再分享一个很细的操作心得。调试Agent时,我习惯把max_iterations调大一些,刻意让Trace里多长几层树,然后完整走一遍“成对验证”流程:同一组问题,在旧版本和新版本上各跑一次,把两条Trace导出并排放在一起。节点的增减、工具选择的差异、token曲线的走势,一眼就能看清改动到底带来了什么影响。

这个习惯救过我很多次。Agent系统的大部分线上问题,都不是单个环节炸掉,而是状态在多步之间悄悄漂移。有了Trace和可视化,你就在每一条漂移路径旁边放了一台行车记录仪——当时车里到底发生了什么,随时可以回放。

如果你正准备给Agent搭工程体系,我的建议是把可观测性当第一优先级,而不是等出了问题再补。毕竟Harness Engineering的前提,是你得先看见。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询