1. 为什么链路追踪和自动化评估值得放在一起聊做过 RAG 应用的人大概都有过这种体验本地跑一个问答 Demo效果看着还行一旦上线用户问法千奇百怪回答质量就开始飘。更麻烦的是你根本不知道问题出在哪一步——是检索没召回相关文档还是召回了但排序不对又或者是模型拿着正确的上下文却答偏了。早期我排查这类问题基本靠print大法把检索结果、拼接后的 Prompt、模型输出一股脑打到日志里然后肉眼比对。项目小的时候还能扛一旦链路变长、并发上来日志就成了灾难现场。链路追踪要解决的就是这个“黑盒”问题。它把一次请求经过的每个环节——检索、重排、Prompt 组装、模型调用、后处理——都记录下来形成一棵可回溯的调用树。而自动化评估解决的是另一个问题你改了 Prompt、换了 Embedding 模型、调整了 chunk 大小怎么知道效果是变好了还是变差了靠人工抽检几十条样本既慢又不客观。把评估脚本化、自动化每次改动跑一遍用数据说话才是可持续的做法。这两个能力放在一起价值就出来了追踪告诉你“哪里出了问题”评估告诉你“改动有没有效果”。LangSmith 这个平台恰好把这两件事串成了一条线。这篇内容我会从实际使用角度把链路追踪的接入方式、RAG 评估的数据集构建、评估器选型、以及踩过的坑尽量讲透。适合正在做 RAG 应用、被效果调优折磨、想建立一套可量化迭代流程的开发者参考。哪怕你只是刚接触 LangChain 生态也能从里面的思路里拿到能直接抄的配置。2. 链路追踪的接入与核心机制拆解2.1 追踪到底记录了什么先说清楚追踪的粒度。一次 RAG 请求在 LangSmith 里会被拆成多个 Run每个 Run 对应一个操作单元。最外层是一个 chain 类型的 Run代表整条链路里面嵌套着 retriever 的 Run、prompt 模板的 Run、llm 的 Run如果用了工具调用还会有 tool 类型的 Run。每个 Run 都带着输入、输出、耗时、token 消耗、以及可选的元数据。这棵调用树的价值在于你能一眼看出时间花在哪。我遇到过一个典型案例用户反馈回答慢追踪一看LLM 调用只花了 1.2 秒但 retriever 花了 4 秒多。问题根本不在模型而在向量库的查询没建好索引。如果没有追踪大概率会去优化模型参数方向完全错了。另一个容易被忽略的是 token 统计。RAG 场景下 Prompt 往往很长检索回来的文档拼接进去token 消耗可能远超预期。追踪里能看到每次调用的 prompt token 和 completion token方便你算成本。我习惯在追踪面板里按 token 排序找出那些“吃 token 大户”往往能发现一些检索返回了超长无关文档的情况。2.2 接入方式与环境变量配置接入追踪本身不复杂核心就是配几个环境变量。以 Python 为例你需要设置 API Key、项目名、以及追踪服务的端点。这里我不写具体的服务地址你按自己部署的实际情况填。import os os.environ[LANGCHAIN_TRACING_V2] true os.environ[LANGCHAIN_API_KEY] your_api_key_here os.environ[LANGCHAIN_PROJECT] rag-eval-demo os.environ[LANGCHAIN_ENDPOINT] your_endpoint_here配好之后只要你用的是 LangChain 的组件追踪会自动挂上去不需要在每个函数里手动埋点。这是它比自建追踪系统省事的地方。但要注意自动追踪只覆盖 LangChain 封装过的调用。如果你在链路中间直接用了原生 SDK 调模型那部分不会被记录需要手动用traceable装饰器包一层。from langsmith import traceable traceable(namecustom_rerank) def rerank_documents(query, docs): # 自定义重排逻辑 return sorted(docs, keylambda d: d.get(score, 0), reverseTrue)手动埋点的好处是能给 Run 起个有意义的名字排查时一眼能认出来。我一般会把重排、过滤、格式化这些自定义步骤都加上traceable不然追踪树里会出现一堆匿名的函数调用看着头疼。2.3 项目隔离与命名规范这里有个实操经验不同环境一定要用不同的 project 名。我见过有人开发、测试、生产共用一个 project结果追踪数据混在一起排查时根本分不清哪条是线上真实请求。我的做法是用项目名-环境的格式比如rag-demo-dev、rag-demo-prod。这样在面板里切换项目就能看到对应环境的数据。命名规范还体现在 Run 的 name 上。默认情况下LangChain 会用组件的类名作为 Run 名比如RetrievalQA、ChatOpenAI。当链路复杂时这些名字区分度不够。我习惯在构建 chain 的时候显式指定 name比如retriever_bm25、retriever_vector这样在追踪树里能直接看出用的是哪路检索。提示追踪数据默认会上传到远端。如果涉及敏感数据建议在本地部署追踪服务或者对输入输出做脱敏处理。我一般会在追踪前把用户 ID、手机号这类字段替换掉。3. RAG 自动化评估的数据集与指标设计3.1 评估数据集怎么建才靠谱自动化评估的前提是有一批“标准答案”。RAG 的评估数据集通常包含三部分问题question、检索到的上下文contexts、以及参考答案answer。但这里有个坑很多人直接拿业务日志里的真实问题当数据集结果发现这些问题要么太简单要么答案依赖实时数据根本没法评估。我的做法是分两步走。第一步先人工构造一批“黄金问题”覆盖典型场景事实型问答、多跳推理、需要综合多篇文档的总结、以及一些边界情况比如问一个知识库里没有的问题看模型会不会胡编。这批问题数量不用多20 到 50 条就能看出趋势。第二步从线上追踪里捞真实问题筛选出那些回答质量差、或者用户反馈不好的补充进数据集。这样数据集既有代表性又能持续迭代。数据集在 LangSmith 里是以 Dataset 的形式管理的。你可以通过 SDK 创建也可以从追踪记录里直接“一键加入数据集”。后者特别方便看到一条效果不好的追踪点一下就能把这条请求的输入输出存成评估样本然后手动补上参考答案。from langsmith import Client client Client() dataset client.create_dataset(rag-eval-set, descriptionRAG 评估数据集) client.create_example( inputs{question: 某产品的保修期是多久}, outputs{answer: 根据文档保修期为两年。}, dataset_iddataset.id )3.2 评估器的选型与组合评估器决定了你用什么标准打分。RAG 场景下我一般会组合使用以下几类评估器类型评估目标适用场景注意事项正确性Correctness答案是否与参考答案一致事实型问答需要参考答案对措辞差异敏感相关性Relevance答案是否切题开放性问题不需要参考答案但主观性强忠实度Faithfulness答案是否基于检索到的上下文防幻觉需要上下文能发现胡编上下文召回Context Recall检索是否召回了必要信息调检索需要标注哪些文档是必要的上下文精度Context Precision检索结果排序是否合理调重排计算相对复杂这几类评估器里忠实度是我最看重的。因为 RAG 最大的风险就是模型拿着不相关的上下文硬编答案。忠实度评估的逻辑是把答案拆成若干断言逐条检查每个断言是否能从上下文中推断出来。如果答案里有上下文没提到的信息忠实度就会低。这个指标能有效抓住幻觉问题。上下文召回和上下文精度是调检索环节的利器。召回低说明该找的文档没找到可能是 Embedding 模型不行也可能是 chunk 切得不好精度低说明检索结果里混了太多无关文档会干扰模型这时候可以考虑加个重排环节。3.3 用 LLM 当裁判的利与弊很多评估器底层是用 LLM 来打分的也就是所谓的“LLM as a judge”。好处是灵活能处理开放式问题坏处是成本高、有随机性、而且裁判模型本身可能有偏见。我踩过的坑是用同一个模型既做生成又做评估结果模型倾向于给自己的输出打高分。后来换成用能力更强的模型做裁判或者至少换一个不同厂商的模型评分才客观一些。另外评估的 Prompt 要写得足够具体明确打分标准和输出格式不然裁判模型会给出模棱两可的结果。from langchain.evaluation import load_evaluator evaluator load_evaluator( labeled_score_string, criteriacorrectness, llmjudge_llm )注意LLM 裁判的评分不是绝对真理它只是一个相对参考。我一般会同时看多个指标再结合人工抽检避免被单一分数误导。4. 从零搭建一条可复现的评估流水线4.1 整体流程设计一条完整的评估流水线大概是这个顺序加载数据集 → 对每个样本跑一遍 RAG 链路 → 收集生成答案和检索上下文 → 用评估器打分 → 汇总结果。LangSmith 提供了evaluate函数把这几步串起来了。from langsmith import evaluate def predict(inputs): # 这里调用你的 RAG chain return rag_chain.invoke({question: inputs[question]}) results evaluate( predict, datarag-eval-set, evaluators[correctness_evaluator, faithfulness_evaluator], experiment_prefixbaseline )experiment_prefix这个参数很关键。每次评估都会生成一个 experiment用不同的前缀区分比如baseline、chunk-512、rerank-v2。这样在面板里能横向对比不同实验的分数一眼看出哪次改动有效。4.2 参数对比实验的实操记录我做过一组 chunk 大小的对比实验想看看 chunk 从 256 调到 512 再调到 1024对检索和最终答案的影响。数据集是 30 条黄金问题评估器用了上下文召回和正确性。实验名chunk 大小上下文召回正确性平均耗时chunk-2562560.720.681.8schunk-5125120.810.762.1schunk-102410240.790.742.6s结果很有意思512 的召回和正确性都是最高的1024 反而下降了。分析追踪发现chunk 太大时检索回来的文档里混入了更多无关内容虽然必要信息也在里面但模型被干扰了。这个结论如果只靠肉眼看几条样本根本发现不了。4.3 评估结果的分析与归因拿到分数只是第一步更重要的是归因。LangSmith 的评估面板支持按样本查看详情能看到每个样本的输入、输出、参考答案、以及各个评估器的打分和理由。我一般会先按分数排序看那些低分样本然后点进去看追踪定位问题环节。有一次发现一批样本的正确性很低但上下文召回很高。说明检索没问题是模型生成环节出了问题。进一步看追踪发现这些样本的 Prompt 里塞了太多文档超出了模型的上下文窗口后面的内容被截断了。解决办法是加一个文档数量限制或者用重排只保留 top-3。改完之后正确性从 0.61 提到了 0.78。这个归因过程如果没有追踪和评估的联动基本靠猜。有了这套工具问题定位从“玄学”变成了“看数据”。5. 常见问题与排查技巧实录5.1 追踪数据不完整或丢失最常见的原因是环境变量没配对或者配在了代码执行之后。LANGCHAIN_TRACING_V2必须在导入 LangChain 相关模块之前设置。我遇到过在 notebook 里先 import 再设环境变量结果追踪死活不生效。解决办法是把环境变量配置放到最前面或者用.env文件统一管理。另一个原因是网络问题导致上报失败。追踪上报是异步的失败时不会报错只会静默丢失。如果发现追踪面板里数据时有时无可以检查一下网络连通性或者看日志里有没有上报相关的警告。5.2 评估分数波动大LLM 裁判本身有随机性同样的输入跑两次可能分数不一样。如果波动超过 0.1就要考虑几个因素一是裁判模型的 temperature 是不是设得太高评估场景建议设成 0二是评估 Prompt 是不是不够明确导致裁判模型理解不一致三是样本量太小统计上不显著。我的经验是评估数据集至少 30 条起步重要决策建议 50 条以上。另外可以固定随机种子减少不必要的波动。5.3 评估成本失控用 LLM 做裁判成本是实打实的。一次评估如果 50 条样本、3 个评估器就是 150 次 LLM 调用。如果每次改动都全量跑账单会很难看。我的做法是分层评估日常迭代只跑一个快速子集10 条样本、1 个核心评估器确认方向没问题后再跑全量。另外能用规则评估的地方就别用 LLM比如答案里是否包含某个关键词、格式是否符合要求这些用代码判断又快又便宜。5.4 追踪与评估的联动技巧LangSmith 支持从评估结果直接跳转到对应的追踪记录。这个功能在归因时特别有用看到某个样本分数低点一下就能看到这次请求的完整链路省去了手动搜索的麻烦。我习惯在评估前给每个样本打上标签比如type:factual、type:multi-hop这样评估后可以按标签分组看分数快速定位是哪类问题拖了后腿。提示评估实验的命名建议带上日期和改动点比如20240115-rerank-top3。时间久了实验多了没有命名规范根本分不清哪个是哪个。6. 一些个人体会和后续可扩展的方向这套链路追踪加自动化评估的组合我用了大半年最大的感受是它把 RAG 调优从“手艺活”变成了“工程活”。以前改 Prompt 靠感觉现在改完跑一遍评估分数涨了还是跌了一目了然。虽然前期搭数据集、写评估器要花点时间但一旦跑起来后续每次迭代都能省下大量人工比对的时间。后续我打算尝试的方向有两个。一是把评估接入 CI每次代码合并前自动跑一遍核心评估集分数低于阈值就阻断合并防止 Prompt 改动引入回归。二是做在线评估从生产追踪里采样用轻量级评估器实时打分发现异常及时告警。这两个方向都能进一步减少人工介入让整个系统更自驱。如果你也在做 RAG 应用建议先从追踪接入开始跑起来看看链路里到底发生了什么。很多时候光是“看见”就已经能解决一半问题了。评估数据集不用一开始就追求完美先攒 20 条跑起来再慢慢迭代。这套东西的价值是随着使用时间增长的越早开始越划算。