
做过 LangGraph Agent 或 RAG 之后很快就会遇到两个问题程序能跑但内部到底发生了什么以及这个 Agent 到底好不好LangSmith 主要解决的就是这两类问题。本文默认你已熟悉 LangChain、LangGraph 和 RAG直接以一个现成的 RAG Agent 为对象串起 Trace、Monitoring、Dataset、Evaluator、Experiment 五个核心能力。一、接入 LangSmith进入 smith.langchain.com 创建 API Key然后在.env中加入LANGCHAIN_API_KEY你的_LangSmith_API_Key LANGCHAIN_PROJECTlangsmith-test LANGCHAIN_TRACING_V2true三者分别表示身份认证、Trace 归属项目、开启链路追踪。注意区分LANGCHAIN_API_KEY用于 LangSmithOPENAI_API_KEY用于模型服务。LangSmith 本身不负责调用业务模型只负责记录和分析调用过程。配置完成后LangChain / LangGraph 运行时即可自动上报 Trace。二、Trace看清一次 Agent 执行先看一个最小 LangGraphimport dotenv/config; import { Annotation, START, END, StateGraph } from langchain/langgraph; const StateAnnotation Annotation.Root({ text: Annotation({ reducer: (_prev, next) next, default: () }) }); const stepOk (state) ({ text: ${state.text}[ok] }); const stepThrow () { throw new Error(DemoError: 节点内故意抛出异常); }; const graph new StateGraph(StateAnnotation) .addNode(step_ok, stepOk) .addNode(step_throw, stepThrow) .addEdge(START, step_ok) .addEdge(step_ok, step_throw) .addEdge(step_throw, END) .compile(); try { await graph.invoke({ text: start }); } catch (error) { console.error(error.message); }运行后进入Tracing → langsmith-test可以看到LangGraph → step_ok → step_throw的调用树。这里要理解两个概念Trace表示一次完整调用Run表示 Trace 中的某一个具体执行单元。点开任意 Run可以看到 Input、Output、Error、Attributes、Latency节点报错时还能看到完整异常堆栈。与传统日志最大的区别是日志是按时间输出的字符串而 Trace 是有父子关系的执行树。三、在真实 RAG 中看 Trace假设已有START → retrieve → generate → END的 LangGraph RAG其中 retrieve 调用retriever.invoke(state.question)generate 把context拼接后调用 chain。运行node src/cli.mjs 无理由退货要在几天内终端只看到最终答案但 LangSmith 中能看到完整调用树LangGraph → retrieve → VectorStoreRetriever以及generate → qwen-plus。点击VectorStoreRetriever可以看到用户问题与实际召回的文档点击generate可以看到它收到的 question 和 context继续点击模型 Run可以看到真正发送给模型的 System Prompt、User Message、模型输出与耗时。因此当 RAG 回答异常时可以快速判断是 Retriever 召回错了还是 Context 正确但模型回答错了还是 Prompt 拼接出了问题还是某一步耗时异常四、Monitoring从单次请求看向整体Trace 解决“这一条请求发生了什么”Monitoring 解决“这段时间整个 Agent 运行得怎么样”。LangSmith Monitoring 中可以观察 Trace Count、成功与失败情况、Latency以及 LLM Calls、Cost Tokens、Tools、Run Types 等统计信息。简单区分调试具体问题看 Trace观察线上整体健康状态看 Monitoring。五、Dataset建立固定测试集从“可观测”进入“可评估”需要 Dataset、Evaluator、Experiment 三者配合Dataset 是测试数据Evaluator 是评分规则Experiment 让 Agent 批量执行 Dataset 并用 Evaluator 打分。安装pnpm install langsmith创建src/evals/build_dataset.mjsimport dotenv/config; import { Client } from langsmith; const DATASET_NAME rag-eval-v1; const EXAMPLES [ { inputs: { question: 无理由退货要在几天内申请 }, outputs: { answer: 自签收之日起 7 天内支持无理由退货。 } }, { inputs: { question: 满多少元包邮 }, outputs: { answer: 满 99 元包邮部分大件商品和冷链商品除外。 } }, { inputs: { question: 手机保修多久 }, outputs: { answer: 手机、平板和耳机全国联保 1 年。 } } ]; async function main() { const client new Client({ apiKey: process.env.LANGCHAIN_API_KEY }); let dataset; try { dataset await client.readDataset({ datasetName: DATASET_NAME }); } catch { dataset await client.createDataset(DATASET_NAME, { description: RAG Agent 回归评估集 }); } await client.createExamples(EXAMPLES.map(e ({ dataset_id: dataset.id, inputs: e.inputs, outputs: e.outputs }))); } main();运行后进入Datasets Experiments可以看到 Inputs 与 Reference Outputs。Dataset 的价值在于把零散的人工测试问题变成固定回归测试集以后修改 Prompt、Retriever、模型或 RAG 参数都可以重跑同一批数据。六、Evaluator给 RAG 定义评分维度本文使用 OpenEvals 内置的三个 RAG 指标Groundedness答案是否被检索上下文支撑、Helpfulness回答是否切题、是否解决用户问题、Retrieval Relevance检索内容是否与问题相关。安装pnpm install openevals创建src/evals/evaluators.mjsimport { createLLMAsJudge, RAG_GROUNDEDNESS_PROMPT, RAG_HELPFULNESS_PROMPT, RAG_RETRIEVAL_RELEVANCE_PROMPT } from openevals; import { ChatOpenAI } from langchain/openai; const judge new ChatOpenAI({ apiKey: process.env.OPENAI_API_KEY, configuration: { baseURL: process.env.OPENAI_BASE_URL }, model: process.env.MODEL_NAME ?? qwen-plus, temperature: 0 }); const ragGroundednessJudge createLLMAsJudge({ prompt: RAG_GROUNDEDNESS_PROMPT, feedbackKey: rag_groundedness, judge, continuous: true }); const ragHelpfulnessJudge createLLMAsJudge({ prompt: RAG_HELPFULNESS_PROMPT, feedbackKey: rag_helpfulness, judge, continuous: true }); const ragRetrievalRelevanceJudge createLLMAsJudge({ prompt: RAG_RETRIEVAL_RELEVANCE_PROMPT, feedbackKey: rag_retrieval_relevance, judge, continuous: true }); export async function ragGroundednessEvaluator({ outputs }) { return ragGroundednessJudge({ context: { documents: outputs.context }, outputs: { answer: outputs.answer } }); } export async function ragHelpfulnessEvaluator({ inputs, outputs }) { return ragHelpfulnessJudge({ inputs, outputs: { answer: outputs.answer } }); } export async function ragRetrievalRelevanceEvaluator({ inputs, outputs }) { return ragRetrievalRelevanceJudge({ inputs, context: { documents: outputs.context } }); } export const ragEvaluators [ ragGroundednessEvaluator, ragHelpfulnessEvaluator, ragRetrievalRelevanceEvaluator ];三个指标到底在比较什么Groundedness 比较 Answer 与 Context关注模型说的内容是否有检索材料支撑Helpfulness 比较 Answer 与 Question关注是否答非所问Retrieval Relevance 比较 Context 与 Question关注 Retriever 返回的文档是否相关。于是一个 RAG 问题可以被拆成检索对不对 → 检索正确后回答有没有依据 → 有依据后回答是否真正有用。这比单独给一个“总分”更容易定位问题。七、Reference Output 与三个指标的关系这里有一个容易误解的地方Dataset 中虽然保存了 Reference Outputs但上述三个 Evaluator 并没有直接使用它因为它们分别比较的是 Answer vs Context、Answer vs Question、Context vs Question。比如 Reference Output 是“金卡会员享 95 折同时拥有专属客服和每月优惠券”而 Agent 只回答“金卡会员享 95 折”某些指标依然可能给高分因为这个回答没有脱离 Context也确实回答了 Question。如果业务还希望评价 Actual Answer 与 Reference Answer 的差异应再增加答案 Correctness 一类的 Evaluator。Dataset 可以保存标准答案但最终哪些字段参与评分由 Evaluator 决定。八、Experiment跑一次完整评估创建src/evals/run_eval.mjs先把 RAG 包装成评测目标再调用evaluate()import dotenv/config; import { Client } from langsmith; import { evaluate } from langsmith/evaluation; import { ask } from ../rag_agent.mjs; import { ragEvaluators } from ./evaluators.mjs; const DATASET_NAME rag-eval-v1; const client new Client({ apiKey: process.env.LANGCHAIN_API_KEY }); async function runRagAgent(inputs) { const { answer, context } await ask(inputs.question); return { answer, context: context.map(doc doc.pageContent) }; } async function main() { const result await evaluate(runRagAgent, { data: DATASET_NAME, evaluators: ragEvaluators, client, experimentPrefix: rag-openevals-${process.env.MODEL_NAME ?? qwen}, maxConcurrency: 2 }); for await (const _row of result) { /* 等待所有样例完成 */ } console.log(✅ 评测完成); console.log(实验名:, result.experimentName); } main();运行node src/evals/run_eval.mjsLangSmith 会创建一次新的 Experiment。进入Datasets Experiments → rag-eval-v1 → Experiments可以看到 Inputs、Reference Outputs、Outputs以及rag_groundedness、rag_helpfulness、rag_retrieval_relevance等评分字段。评估不再是“感觉回答还不错”而变成可量化的分数。Experiment 的真正价值在比较Experiment A 用原 PromptExperiment B 用新 Prompt或者 Retriever k4 对比 k2或者模型 A 对比模型 B。因为测试集没有变化可以观察修改究竟让哪些指标变好、哪些变差。这才是 Dataset Experiment 最核心的工程意义修改前跑一次修改后再跑一次用数据判断修改是否真的有效。九、把完整逻辑串起来LangSmith 可以理解成两部分。第一部分是 ObservabilityAgent → Trace → RunInput / Output / Error / Latency / Token / Tool Call / LLM Call再向上汇总为 Monitoring整体调用量、错误情况、耗时趋势、Token / Cost。第二部分是 EvaluationDataset → Agent → Outputs → Evaluator → Scores → Experiment。它不是单纯的 Trace Viewer而是把调试、监控、评估串成一套完整工作流。五个核心概念可以记成一张表Trace 是一次 Agent 调用的完整链路Monitoring 是多次调用形成的整体运行统计Dataset 是固定的测试样本集合Evaluator 是自动评分规则Experiment 是在 Dataset 上批量运行并评分的一次实验。对于已经能开发 LangGraph Agent 或 RAG 的工程师来说LangSmith 真正解决的不是“怎么让 Agent 跑起来”而是“跑起来以后怎么知道内部发生了什么”以及“修改一版后怎么证明它真的比上一版更好”。前者由 Trace 和 Monitoring 解决后者由 Dataset、Evaluator 和 Experiment 解决。当 Agent 从 Demo 走向真实项目可观测与可评估往往比继续增加更多节点、更多工具调用更重要。