排查LangChain应用时,最麻烦的往往不是异常堆栈,而是某个中间步骤悄悄返回了错误结构或无关内容,最终回答却看起来没有明显报错。LangSmith提供的链路追踪(Trace)机制可以把一次完整调用拆成多个Run,每个Run对应一次模型调用、工具执行、检索操作或Prompt渲染。开发者不需要在代码里到处写日志,就能看到每一步的输入、输出、耗时和反馈。下面结合RAG问答与Agent执行场景,说明如何配置追踪、读懂Trace树,并用中间结果面板定位问题。

一、开启LangSmith追踪:环境变量与项目配置
LangSmith的追踪功能对LangChain应用几乎是零侵入的。只要在进程启动前设置好环境变量,LangChain内部就会自动把每次invoke、stream、batch调用包装成可记录的Run。核心配置包括LANGCHAIN_TRACING_V2、LANGCHAIN_API_KEY和LANGCHAIN_PROJECT。其中第一个变量用来开启V2版本的追踪接口,第二个变量是LangSmith平台生成的API Key,第三个变量用来给追踪数据分项目,便于后续筛选。
下面是一段在Python脚本中配置追踪的示例。如果使用dotenv加载.env文件,也可以把这些变量写在环境文件里,效果相同。注意API Key不要提交到版本库,生产环境建议通过密钥管理服务注入。
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls__your_api_key"
os.environ["LANGCHAIN_PROJECT"] = "rag-debug-demo"
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个严谨的技术助手,回答必须基于给定资料。"),
("user", "{question}")
])
chain = prompt | llm
result = chain.invoke({"question": "LangSmith 如何记录中间结果?"})
print(result.content)
运行这段代码后,LangSmith会自动收到一条Trace。追踪数据并不只记录最终答案,而是把Prompt渲染、模型调用等步骤分别保存为父子Run。开发者可以在LangSmith的项目页面看到运行列表,点击某一条运行后展开完整调用链。此时即使代码里没有显式日志,也能回溯到模型实际收到的完整Prompt文本和返回的原始消息对象。
需要说明的是,开启追踪后可能会有少量网络开销,但不会改变LangChain的执行逻辑。对于调试环境或灰度环境,建议始终开启;对于高并发生产环境,可以按采样比例控制记录量,避免产生过多运行数据。LangSmith本身也支持通过SDK过滤和删除不需要的Run。
二、读懂Trace树:父Run与子Run的层级关系
Trace本质上是一棵有向树,顶层通常是一个Chain或Agent的运行记录,下面挂载若干子Run。比如RAG链路由检索器、Prompt模板、Chat模型、输出解析器等多个组件组成,每个组件都可能产生一个Run。父Run负责描述整体执行范围和开始结束时间,子Run则记录具体输入输出。理解这层层级关系是分析中间结果的前提。
在一个典型的RAG链路中,顶层Run的名字可能是RunnableSequence,展开后能看到Retriever、ChatPromptTemplate、ChatOpenAI等子Run。每个Run卡片会显示延迟、Token消耗、错误状态和标签。点击Retriever子Run,右侧面板会展示它返回的文档列表,包括文档内容片段和元数据。这一步非常关键,因为很多回答跑偏的问题并不在模型生成阶段,而是检索阶段返回了不相关或过时的文档。
如果链路里包含自定义函数,可以使用@traceable装饰器手动把函数标记为可追踪单元。LangSmith会自动把该函数的入参和返回值记录下来,并在Trace树中生成对应节点。这样即使某些步骤没有走LangChain的标准Runnable接口,也能纳入统一调试视图。对于Agent执行,工具调用会被记录为独立的子Run,每个工具Run下面还可以继续挂嵌套的模型推理Run。
三、中间结果可视化分析:从检索到生成的每一步
LangSmith的中间结果面板不只是把输入输出打印出来,它还提供了结构化的查看方式。对于Chat模型,输入会展示成消息列表,输出也会区分消息角色和内容;对于检索器,返回的Document对象会被拆成可折叠的条目;对于Prompt模板,则可以看到变量替换完成后的完整Prompt。相比在命令行里打印长文本,这种可视化能更快发现字段缺失、内容截断或角色错乱。
以一个常见问题为例:你发现模型回答时完全没有使用检索到的资料。通过Trace面板打开模型子Run,查看输入消息,可能会发现Prompt模板中的{context}变量没有被正确注入,导致模型只收到了用户问题。此时可以直接定位到Prompt模板子Run,检查它的输入变量和渲染结果,确认是上游没有返回context还是模板变量名写错。整个排查过程不需要反复修改代码并重新运行,一次Trace就能提供足够信息。
除了单次查看,面板还能对比两次运行的中间结果。比如修改检索参数前跑一次,修改后再跑一次,把两次Trace并排打开,可以清楚看到文档集合、相似度分数和生成结果的变化。这种对比方式对微调Prompt或调整检索top_k值时非常有效。对于耗时问题,每个Run卡片的延迟数据也能帮助快速锁定瓶颈,是检索慢了还是模型首Token延迟高,都能直接看到。
四、用反馈与筛选缩小问题范围
当项目积累了大量Trace后,逐个点击排查会变得低效。LangSmith允许通过代码给运行添加反馈,例如标记一条运行是否成功、答案是否相关、检索质量是否达标。下面这段代码演示了如何用langsmith包查询某个项目中最近出错的运行,便于集中分析失败案例。
from langsmith import Client
client = Client()
runs = list(client.list_runs(
project_name="rag-debug-demo",
execution_order=1,
error=True,
limit=10,
))
for run in runs:
print(run.id, run.name, run.error)
在Web界面中,也可以通过错误状态、运行时长、标签等条件过滤。比如只查看执行时间超过5秒的Trace,或者只查看包含某个工具调用的Agent运行。对于已经定位的问题,可以在代码里给成功和失败运行打上反馈,后续用反馈分数作为过滤维度,形成持续优化的闭环。
另一个实用做法是在CI流程中接入LangSmith评估。每次代码合并后自动运行一组测试查询,并记录Trace。如果某次修改导致检索质量下降或输出格式错误,可以直接通过对比CI前后的Trace快速定位引入问题的提交。相比只关注最终对错,这种中间结果层面的回归分析能显著减少调试时间。
总之,LangSmith的链路追踪让LangChain应用的调试从黑盒变成了白盒。配置成本很低,但收益在于每一步中间状态都变得可查看、可对比、可筛选。对于RAG、Agent以及复杂的多步链式应用,这套追踪和可视化分析方式能够帮助开发者快速定位根因,而不是在模型输出和代码逻辑之间反复猜测。