DeepEval Traced Evals 实战指南:将指标挂载到 Trace 与 Span 的单轮评估范式
2026/9/13 14:45:16 网站建设 项目流程

DeepEval Traced Evals 实战指南:将指标挂载到 Trace 与 Span 的单轮评估范式

【免费下载链接】deepevalThe LLM Evaluation Framework项目地址: https://gitcode.com/GitHub_Trending/de/deepeval

Traced Evals 是 DeepEval 在应用具备可观测性(通过框架集成或手动@observe埋点产出 Trace)时采用的默认单轮(single-turn)评估路径:整次执行构成一个 Trace,其内部组件构成一个个 Span,组件级指标直接挂载在对应 Span 上,而不是拆分成独立的测试形态。本篇基于仓库文档 traced-evals.md,结合源码与测试,系统讲解如何把指标"焊"到 Span 上、如何用 pytest 与脚本两种形态运行 traced eval,以及如何将结果上报到 Confident AI;读完你可以在自己的 Agent / RAG / 工具调用应用中落地完整的"追踪即评估"闭环。

一、Traced Evals 的核心概念:Trace 是执行,Span 是组件

在 DeepEval 的评估体系中,传统做法是手工构造LLMTestCase,把inputactual_outputretrieval_context等字段喂给指标。而 traced eval 完全反过来:先有观测,再谈评估

  • Trace(追踪):一次端到端的应用执行,例如一次完整的 Agent 调用链;
  • Span(跨度):Trace 内部的组件,例如一次 LLM 调用、一次检索、一次工具调用或一次子 Agent 执行;
  • 指标与 Span 绑定:组件级指标(如检索器的 Contextual Precision、生成 LLM 的 Answer Relevancy)被挂载到它们所评估的那个具体 Span上,全部留在同一次单轮 tracing eval 里,而不是拆成多个独立的 test case。

从源码可以确认 Span 的类型体系:deepeval/tracing/types.py中定义了SpanType枚举,包含agentllmretrievertool四种具名类型(types.py),外加通用的base类型;deepeval/tracing/__init__.py统一导出了observetracetrace_managerflush_tracesa_flush_traces以及next_agent_spannext_llm_spannext_tool_spannext_retriever_spannext_span等全部 API(init.py)。

需要说明的是,本文聚焦eval-coupled(评估耦合)一侧:把指标挂到 Span 上,以及两种 traced eval 的代码形态。至于如何为应用埋点——添加@observe、接入框架集成、设置 Span 类型 / tags / metadata——属于deepeval-tracingskill 的范畴(仓库中另有 deepeval-tracing 技能文档),本文不再展开。

二、组件 / Span 级指标:把指标挂到"对的 Span"上

指标只有挂在正确的 Span 上,才能利用该 Span 携带的输入、输出、上下文等数据完成计算。DeepEval 提供两种挂载方式,分别对应"集成自动建 Span"与"手动埋点建 Span"两种场景。

2.1 集成创建 Span:用 next_*_span 预置指标

当某个受支持的框架集成(如 LangChain、CrewAI、OpenAI Agents 等)负责创建 Span 时,你无法在 Span 内部执行代码来设置指标,此时需要用next_*_span系列上下文管理器,为下一个该类型的 Span 预置指标等默认值:

from deepeval.tracing import next_retriever_span from metrics import RETRIEVER_SPAN_METRICS with next_retriever_span(metrics=RETRIEVER_SPAN_METRICS): run_ai_app_with_integration_tracing(golden.input)

当集成在with块内部创建第一个retriever类型的 Span 时,这些预置的默认值会被一次性消费并应用到该 Span 上。next_*_span家族共四个具名入口,按集成实际产生的 Span 类型选择:

函数匹配的 Span 类型典型场景
next_agent_span(...)agent子 Agent / 元 Agent 执行
next_llm_span(...)llm生成模型的 LLM 调用
next_tool_span(...)tool工具 / 函数调用
next_retriever_span(...)retrieverRAG 检索器查询

next_span(...)则是不区分类型的通用版本,适用于"下一个 Span 类型未知或无关紧要"的场合。这些上下文管理器不仅接受metrics,还接受与update_current_span一致的通用字段(inputoutputretrieval_contextcontextexpected_outputtools_calledexpected_toolsmetadatanametest_casemetric_collection),并且是"一站式"的:每个具名版本还额外接受自身类型专属字段,例如next_llm_span支持modelinput_token_countoutput_token_countcost_per_input_tokencost_per_output_tokentoken_intervalspromptnext_retriever_span支持embeddertop_kchunk_sizenext_agent_span支持available_toolsagent_handoffsnext_tool_span支持description(context.py)。

2.2 手动埋点:直接挂到 @observe 上

如果应用使用手动埋点,或者集成支持被观测的组件 Span,则可以直接把指标传给@observe装饰器:

from deepeval.tracing import observe from metrics import GENERATOR_LLM_SPAN_METRICS @observe(type="llm", metrics=GENERATOR_LLM_SPAN_METRICS) def call_model(messages): ...

@observetype参数决定创建的 Span 类型,可取值"agent""llm""retriever""tool"或自定义字符串(tracing.py)。除了metrics之外,还可以传入metric_collection以及随**observe_kwargs透传的类型专属参数——例如@observe(type="llm", model="gpt-4.1", metrics=[AnswerRelevancyMetric()]),这在仓库测试 test_openai.py 中有完整示例。observe装饰器对普通同步函数、协程、同步生成器与异步生成器均有支持;在Observer.__exit__中,Span 的input会回填为函数入参、output回填为函数返回值,并依据异常与否把 Span 状态标记为SUCCESSERRORED(tracing.py)。

2.3 指标列表命名约定:按组件命名,不要建全局列表

文档明确给出命名规范:按组件命名指标列表,例如RETRIEVER_SPAN_METRICSGENERATOR_LLM_SPAN_METRICSORDER_LOOKUP_TOOL_SPAN_METRICS不要为整个应用创建一个全局的组件指标列表。原因是指标与 Span 一一对应,全局列表会导致同一个指标被错误地套用到不相关的 Span 上,破坏"精确评估"的语义。

2.4 底层原理:next_*_span 是如何工作的

从 context.py 的实现看,next_*_span的机制有三个关键设计:

  1. 一次性消费(one-shot):每个next_*_span把默认值写入一个ContextVar,由集成(或其 OTel 处理器)在创建第一个匹配类型的 Span 时通过pop_pending_for(span_type)消费掉,with块内后续再创建的同类 Span 看到的是已清空的槽位(context.py)。
  2. 按类型隔离(per-type isolation)baseagentllmtoolretriever各自持有独立的ContextVar,因此with next_agent_span(...), next_llm_span(...):这种叠加写法是安全无歧义的;消费时pop_pending_for会把 base 槽位与类型槽位合并,类型槽位的值在重叠时优先("更具体者胜出")。
  3. 跨 asyncio 子上下文可见:框架 API(如Agent.run_sync)内部可能调用asyncio.run创建新的 asyncio 上下文,而新上下文继承的是父上下文ContextVar快照,在快照里ContextVar.set不会回传到外层。为此实现用一个_PendingSlot可变包装器持有 payload,跨上下文共享的是同一个引用,消费时直接改payload属性即可让外层与子上下文同时看到"已消费"状态,避免同一份默认值被重复应用(context.py)。消费后的字典通过apply_pending_to_spansetattr方式写回 Span,且只写 Span 实际声明的字段,防止跨类型泄漏(例如embedder不会落到LlmSpan上)。

三、两种评估形态:pytest 断言式 与 脚本迭代式

Traced eval 支持两种代码形态,分别面向 CI/CD 与本地迭代。两者的共同点是:直接把Golden传入被追踪的应用,让应用在运行过程中产出 Trace / Span,评估完全基于追踪结果完成。

3.1 pytest 形态:面向 CI/CD,断言即门禁

对于 CI/CD,优先采用各集成文档中展示的 pytest 形态。先用pytest.mark.parametrize遍历数据集中的Golden,把Golden直接传入被追踪的应用,最后用assert_test断言:

@pytest.mark.parametrize("golden", dataset.goldens) def test_agent(golden: Golden): run_ai_app_with_integration_tracing(golden.input) assert_test(golden=golden, metrics=TRACE_METRICS)

assert_test是 evaluate.py 中的核心入口。当传入golden而不传test_case时,它走的是trace-scoped分支:调用_assert_test_from_current_trace,从current_trace_context读取当前活跃的 Trace(evaluate.py)。该函数会把 Trace 及其 Span 树转换为TraceApi,并用Golden的字段与 Trace 上携带的outputexpected_outputcontextretrieval_contexttools_calledexpected_tools组装出对应的LLMTestCase,然后深度优先遍历 Span 树,把每个 Span 上挂载的指标逐一执行(trace_scope.py)。assert_test底层逻辑还做了几件工程化的事:

  • 指标未过阈值或执行出错时,assert_test会抛出携带指标明细的AssertionError(指标名、得分、阈值、strict 模式、reason),直接阻断 CI;
  • 标记为 flaky 的 test case 失败时只发 warning 不抛异常,避免偶发失败阻塞流水线(evaluate.py);
  • 自动跳过 DeepEval 内部 pytest 包装 Span,并把用户根 Span 提升为真正的根节点,保证上报到后端的 Span 树干净(trace_scope.py)。

3.2 脚本 / 迭代形态:用 evals_iterator 流式评估

对于脚本或本地迭代循环,使用数据集的evals_iterator,并同样把Golden传入被追踪的应用:

for golden in dataset.evals_iterator(metrics=TRACE_METRICS): run_ai_app_with_integration_tracing(golden.input)

evals_iterator定义在 dataset.py,每次yield一个Golden,并在此过程中接管 Trace 的采集、指标执行与结果聚合。它支持丰富的配置参数:

参数作用
metrics应用于 Trace 的指标列表,例如TRACE_METRICS
hyperparameters记录本次运行的超参数(值可为str/int/float/Prompt
identifier本次评估运行的标识符
display_config展示配置(DisplayConfig,含 verbose、进度条等)
cache_config缓存配置(CacheConfig,是否写 / 读缓存)
error_config错误配置(ErrorConfig,如skip_on_missing_paramsignore_errors
async_config异步配置(AsyncConfigrun_async=True时走异步执行器)

内部实现上,同步与异步分别委托给execute_agentic_test_cases_from_loop/a_execute_agentic_test_cases_from_loop;异步模式会维护trace_uuid -> Golden的映射,保证即使 Trace 数量与 Golden 数量不一致或执行顺序交错,每个 Trace 也能关联到正确的 Golden(tracing.py)。

仓库测试对这两种形态均有覆盖:TestEvalsIterator验证了同步 / 异步evals_iteratorErrorConfig(skip_on_missing_params=True/False)MissingTestCaseParamsError的行为(test_configs.py);test_dataset_iterator.py覆盖了同步应用 + 异步迭代等四种组合(test_dataset_iterator.py);端到端示例 example_e2e_trace_evals.py 展示了"拉取数据集 +evals_iterator(metrics=[...])+ 传入golden.input"的完整闭环。

3.3 红线:不要手工回退到 LLMTestCase

文档明确强调:除非用户明确选择不启用追踪,否则不要把 traced single-turn eval 转换成手工构造的LLMTestCase。理由在于:traced eval 的全部价值——精确到组件的指标定位、Span 树结构、上下游数据自动回填——都依赖 Trace 本身;一旦手工构造 test case,这些信息便全部丢失,等于退回到无追踪时代的评估方式。

四、Confident AI 结果上报与查看

4.1 认证:deepeval login 或 CONFIDENT_API_KEY

如果用户选择将评估结果上报到 Confident AI,需要先确认认证就绪,两种方式任选其一:

  1. 已执行过deepeval login(交互式登录,令牌写入本地配置);
  2. 已导出环境变量CONFIDENT_API_KEY

文档建议:在 CI 及其他非交互式运行中优先使用CONFIDENT_API_KEY环境变量,避免交互式登录在无 TTY 环境失效。仓库中deepeval loginCONFIDENT_API_KEY的读写、查询与注销逻辑位于 auth/command.py,其中也包含了"未登录时提示导出CONFIDENT_API_KEY"的引导路径。

4.2 查看报告:deepeval view

评估结束后,可使用deepeval view在浏览器中打开最近一次的托管报告。其实现逻辑是:若已登录,则优先打开本地记录的最新 test run 链接;否则自动上传并打开对应链接(main.py)。

4.3 底层:Trace 是如何被送达 Confident AI 的

从源码看,Trace 的上报由TraceManager后台工作线程 + 队列完成(tracing.py):

  • 每个 Trace / Span 先被转换为 API 模型(TraceApi/BaseApiSpan),其中每个 Span 的指标被序列化为MetricData,携带namethresholdsuccessscorereasonstrict_modeflakyevaluation_modelerrorevaluation_costinput_token_countoutput_token_count等字段(api.py);
  • Trace 进入队列后由 daemon 工作线程按最小间隔限速异步 POST 到 Confident AI 的 traces 端点;
  • 进程短命退出时,可通过flush_traces(timeout=30.0)(或异步版a_flush_traces)阻塞等待所有排队 Trace 发送完毕,避免丢失(tracing.py);未启用 flush 而进程退出时,管理器会打印"遗留 Trace"警告并提示设置CONFIDENT_TRACE_FLUSH=1
  • 评估模式下环境标签会被标记为TESTING,且每个 Span 的指标会随create_metric_data一并写入 API Span(tracing.py)。

这意味着 traced eval 的"评估结果"与"追踪数据"是同一条链路:指标得分、耗时、Token 消耗、成本与 Span 树一起送达云端,天然具备可回查、可对比的审计能力。

五、一次完整 Traced Eval 的数据流小结

把上述内容串起来,一次 traced eval 的生命周期是:

  1. 注入next_*_span(metrics=...)@observe(metrics=...)把指标预置 / 绑定到即将创建的 Span 上;
  2. 执行:应用(集成追踪或手动埋点)运行,Observer.__enter__/__exit__构建 Span 树并把入参、出参、错误状态写回;
  3. 评估:pytest 形态由assert_test(golden=..., metrics=TRACE_METRICS)触发,脚本形态由evals_iterator(metrics=TRACE_METRICS)触发——两者都基于活跃 Trace 提取测试数据、按 Span 挂载的指标逐项计算;
  4. 上报:Trace / Span / MetricData 经create_trace_api序列化后进入后台队列,送达 Confident AI;
  5. 查看deepeval view打开最近一次托管报告,可逐 Span 检查指标得分、Token 与成本明细。

这套范式让"可观测性"与"评估"合二为一:代码里不需要为每个指标手工拼装 test case,指标天然落在它们所评估的组件上,评估结果与执行链路一一对应,是 DeepEval 在 Agent / RAG 类应用上推荐的默认单轮评估方式。

【免费下载链接】deepevalThe LLM Evaluation Framework项目地址: https://gitcode.com/GitHub_Trending/de/deepeval

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询