如何把 LangGraph 应用接入 DeepEval 追踪并给节点和工具调用打分?
【免费下载链接】deepevalThe LLM Evaluation Framework项目地址: https://gitcode.com/GitHub_Trending/de/deepeval
假设你已经用 LangGraph 的StateGraph搭好了一个带工具的图(例如chatbot节点 +ToolNode),现在要完成两件事:把每次图运行的节点、模型调用、工具调用全部抓成可检查的 trace,并用 DeepEval 的指标对整条轨迹或其中单个组件打分。本文以 Python 为主路径,内容来自 LangGraph 集成文档;TypeScript 侧有等价 API,文中在相关位置单独标出。
安装依赖
pip install -U deepeval langgraph langchain-openaiLangGraph 走的是 LangChain 的 callback 系统,所以 DeepEval 的接入是按调用生效的:你在哪次invoke的 config 里传入CallbackHandler(),哪次运行就被追踪,不需要改动图本身的代码。
TypeScript 项目对应安装:
npm install -D deepeval @langchain/langgraph @langchain/core @langchain/openai zod方式相同,传入new DeepEvalCallbackHandler({})即可。
给图加上追踪:把 CallbackHandler 传入 graph config
下面是文档中的完整示例,一个chatbot节点加一个ToolNode的最小天气图:
from langchain.chat_models import init_chat_model from langgraph.graph import StateGraph, MessagesState, START, END from langgraph.prebuilt import ToolNode, tools_condition from deepeval.integrations.langchain import CallbackHandler from deepeval.dataset import EvaluationDataset, Golden from deepeval.metrics import TaskCompletionMetric def get_weather(city: str) -> str: """Return the weather in a city.""" return f"It's always sunny in {city}!" llm = init_chat_model("openai:gpt-4o-mini").bind_tools([get_weather]) def chatbot(state: MessagesState): return {"messages": [llm.invoke(state["messages"])]} graph = ( StateGraph(MessagesState) .add_node(chatbot) .add_node("tools", ToolNode([get_weather])) .add_edge(START, "chatbot") .add_conditional_edges("chatbot", tools_condition) .add_edge("tools", "chatbot") .compile() ) # Goldens are the inputs you want to evaluate. dataset = EvaluationDataset(goldens=[Golden(input="What is the weather in Paris?")]) # The `TaskCompletionMetric` is passed into the `evals_iterator`. for golden in dataset.evals_iterator(metrics=[TaskCompletionMetric()]): graph.invoke( {"messages": [{"role": "user", "content": golden.input}]}, config={"callbacks": [CallbackHandler()]}, )模型用的是文档示例中的openai:gpt-4o-mini,你可以把init_chat_model(...)的参数换成自己能调用的模型。接入追踪的关键只有一行:config={"callbacks": [CallbackHandler()]}。
追踪出来的 span 结构
每次接收到 callback 的 LangGraph 运行会产生一个trace(用户视角的端到端单元),里面是 LangChain 回调发出的各类组件 span:
- Agent span— 编译后
StateGraph的invoke(...)调用(span 树的根),嵌套的子图节点保持为自定义 span; - LLM spans— 节点内的 chat model / completion 调用;
- Tool spans— 由
ToolNode(或你自己的节点)执行的工具调用; - Retriever spans— 如果你的图用到检索。
文档给出的示例结构如下(文档示例):
Trace ← what the user observes └── Agent: weather_graph ← one graph invoke(...) call ├── Node: chatbot ← model picks a tool │ └── LLM: gpt-4o-mini ├── Node: tools ← ToolNode runs the tool │ └── Tool: get_weather └── Node: chatbot ← model writes the final answer └── LLM: gpt-4o-miniTrace 和它的每个组件 span 都可以独立参与评估。
验证追踪是否生效
不需要登录任何平台,两种本地检查方式:
deepeval inspectTUI:evals_iterator的每次运行都会把结果快照落盘,直接运行deepeval inspect就能打开 trace 树,查看每个 span 的分数、metric reason、输入/输出和工具调用。也可以显式指定文件或目录,目录方式会取其中最新的test_run_*.json:deepeval inspect deepeval inspect ./experiments/test_run_20260512_174200.json deepeval inspect ./experiments # 取目录内最新的 test_run_*.json需要 TUI 时,CLI 文档给出的安装方式是
pip install 'deepeval[inspect]'(见 command-line-interface)。代码里取原始 trace:trace 在内存中捕获,可以直接当 Python 字典读取(见 Tracing AI Agents):
from deepeval.tracing import trace_manager travel_graph_result = graph.invoke( {"messages": [{"role": "user", "content": "What's the weather in Paris?"}]}, config={"callbacks": [CallbackHandler()]}, ) traces = trace_manager.get_all_traces_dict() for trace in traces: for span_type in ["agentSpans", "llmSpans", "toolSpans"]: for span in trace.get(span_type, []): print(f" [{span_type}] {span.get('name')}: {span.get('input')} -> {span.get('output')}")遍历
llmSpans和toolSpans可以逐个确认每个工具收到的参数和返回结果。长驻脚本里可以在两次运行之间调用trace_manager.clear_traces(),避免 trace 在内存里累积。
给整条轨迹打分:trajectory metrics
把轨迹类指标传给evals_iterator,每个 golden 触发一次带追踪的图运行,指标在 trace 作用域上对整个有序轨迹打分——包括图的路由决策、模型调用、工具使用,自定义的图/节点 span 也属于这条有序轨迹的一部分:
from deepeval.metrics import TaskCompletionMetric, StepEfficiencyMetric, PlanAdherenceMetric from deepeval.integrations.langchain import CallbackHandler from deepeval.dataset import EvaluationDataset, Golden dataset = EvaluationDataset(goldens=[Golden(input="What is the weather in Paris?")]) metrics = [TaskCompletionMetric(), StepEfficiencyMetric(), PlanAdherenceMetric()] for golden in dataset.evals_iterator(metrics=metrics): graph.invoke( {"messages": [{"role": "user", "content": golden.input}]}, config={"callbacks": [CallbackHandler()]}, )迭代器为每个 golden 捕获一条 trace,并在图运行结束后评估完整轨迹,每个指标的分数和 reason 与该 trace 一起存储,失败可以定位到具体执行路径。更多指标选择见 trajectory-based evaluation。
运行后终端会打印每个 span 的打分,文档展示的示例输出(文档示例,数值不是固定预期):
$ deepeval test run test_langgraph_agent.py ● test_langgraph_agent │ └─ weather_graph Task Completion 0.94 190ms ✓ ├─ chatbot · gpt-4o-mini G-Eval 0.42 72ms ✗ ├─ get_weather(city="Paris") 32ms └─ chatbot · gpt-4o-mini Faithfulness 0.95 78ms ✓ Trace score 0.77 · 2/3 metrics passed给单个节点或 LLM 调用打分
传给evals_iterator的指标作用在整条 trace 上。要评估组件,就先把指标“暂存”到 callback 下一个要打开的 span 上,再执行图:
Agent span:用next_agent_span包住graph.invoke(...),指标落到invoke(...)打开的根 agent span 上——适合只给图这次运行本身一个 span 级分数,而不评整条 trace。
from deepeval.metrics import TaskCompletionMetric from deepeval.tracing import next_agent_span for golden in dataset.evals_iterator(): with next_agent_span(metrics=[TaskCompletionMetric()]): graph.invoke( {"messages": [{"role": "user", "content": golden.input}]}, config={"callbacks": [CallbackHandler()]}, )评估子图/子 agent 节点时也是这个套路:staging 的指标会落到图运行期间 callback 打开的下一个匹配的 agent span 上。
LLM 调用:用next_llm_span包住 invoke,指标落到图发出的第一个LLM span 上:
from deepeval.metrics import AnswerRelevancyMetric from deepeval.tracing import next_llm_span for golden in dataset.evals_iterator(): with next_llm_span(metrics=[AnswerRelevancyMetric()]): graph.invoke( {"messages": [{"role": "user", "content": golden.input}]}, config={"callbacks": [CallbackHandler()]}, )注意这是one-shot语义:next_llm_span只覆盖块内第一个 LLM span,循环里后续经过chatbot节点的 LLM 调用不会拿到该指标。要给每次 LLM 调用都打分,文档给出的替代路径是使用 scope 级上下文trace(...),或者干脆用evals_iterator的 trace 级指标端到端评分。
工具调用:集成文档的明确建议是——对确定性的工具调用,用 tool span 记录可追溯性、输入、输出和元数据,避免把指标直接挂到 tool span 上。也就是说工具 span 负责把“调了什么工具、传了什么参数、返回了什么”完整留痕,工具使用质量的分数由轨迹级指标(如TaskCompletionMetric、StepEfficiencyMetric)在整条轨迹上体现。
TypeScript 侧对应的 staging 辅助是nextAgentSpan/nextLlmSpan(来自deepeval/tracing),例如:
import { AnswerRelevancyMetric } from "deepeval/metrics"; import { nextLlmSpan } from "deepeval/tracing"; for await (const golden of dataset.evalsIterator()) { await nextLlmSpan({ metrics: [new AnswerRelevancyMetric()] }, () => ask((golden as Golden).input), ); }在 CI/CD 中打分
把同样的数据集、带追踪的图和指标放进 pytest 测试:每次参数化调用就是一次 LangGraph 运行,指标不达标时测试失败,测试失败即构建失败:
import pytest from langchain.chat_models import init_chat_model from langgraph.graph import StateGraph, MessagesState, START, END from langgraph.prebuilt import ToolNode, tools_condition from deepeval import assert_test from deepeval.integrations.langchain import CallbackHandler from deepeval.dataset import EvaluationDataset, Golden from deepeval.metrics import TaskCompletionMetric def get_weather(city: str) -> str: """Return the weather in a city.""" return f"It's always sunny in {city}!" llm = init_chat_model("openai:gpt-4o-mini").bind_tools([get_weather]) def chatbot(state: MessagesState): return {"messages": [llm.invoke(state["messages"])]} graph = ( StateGraph(MessagesState) .add_node(chatbot) .add_node("tools", ToolNode([get_weather])) .add_edge(START, "chatbot") .add_conditional_edges("chatbot", tools_condition) .add_edge("tools", "chatbot") .compile() ) dataset = EvaluationDataset(goldens=[ Golden(input="What is the weather in Paris?"), Golden(input="What is the weather in London?"), ]) @pytest.mark.parametrize("golden", dataset.goldens) def test_langgraph_agent(golden: Golden): graph.invoke( {"messages": [{"role": "user", "content": golden.input}]}, config={"callbacks": [CallbackHandler()]}, ) assert_test(golden=golden, metrics=[TaskCompletionMetric()])运行:
deepeval test run test_langgraph_agent.py这就是验证方式:退出前你会得到每个 golden 的 pass/fail,任何轨迹指标低于阈值,deepeval test run就会失败。TypeScript 侧用 vitest 的toPass()匹配器(expect(golden).toPass([...], { task: (g) => ask(g.input) })),命令为npx deepeval test run langgraph-agent.test.ts。
如果指标是通过next_agent_span这类 staging 挂在组件 span 上的,测试里就不需要再传 trace 级指标:调用run_graph(golden.input)后直接assert_test(golden=golden)即可。
可选分支与边界
- LangGraph server 部署:部署到 LangGraph server 时是 server 在调用你的图,没有自己的
invoke可以塞 callback。改成在编译图时把 handler 烘焙进去——.compile().with_config(callbacks=[CallbackHandler()])——再把langgraph.json指到导出的graph,然后langgraph dev启动,server 执行的每个请求都被追踪。注意烘焙进去的 handler 属性(thread_id、user_id等)是per-handler的,所有请求共用同一份;要按请求变化的值,仍然用 per-call 传 callback。 - trace 元数据:
CallbackHandler接受name、tags、metadata、thread_id、user_id、metrics、metric_collection、test_case_id、turn_id等 kwarg,作为使用该 callback 的运行的默认值。例如CallbackHandler(name="weather-graph", tags=["langgraph", "weather"], metadata={"team": "support"}, user_id="user-123")。 - 组件级评估目前是 single-turn only(见 component-level evaluation),多轮组件级评估在路线图里。
metrics参数是同步执行 LLM-as-a-judge 评估的,会给运行增加延迟,文档建议仅用于开发和测试;生产环境改用metric_collection做异步评估(需要 Confident AI)。- 登录 Confident AI(
deepeval login)可以可视化完整图 trace——每个节点、模型调用、工具调用都以嵌套 span 展示并带分数——这一步是可选的,本地脚本、CI 和deepeval inspect都不依赖它。
完成判定
接入完成的标准按文档可核对:deepeval test run(或脚本 +evals_iterator)跑完后,终端输出中每个 span 带有指标分数和 pass/fail;deepeval inspect打开的 trace 树里能看到Agent / Node / LLM / Tool的完整层级,工具 span 带参数和返回内容;CI 中指标不达标时deepeval test run返回失败,从而阻断构建。
【免费下载链接】deepevalThe LLM Evaluation Framework项目地址: https://gitcode.com/GitHub_Trending/de/deepeval
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考