特性分析
集成支持
deepeval 通过一个统一的 Tracing 核心层来兼容所有框架。无论外部框架的形式如何不同,最终都汇聚到同一套数据模型和 trace 管理器。

- Monkey-Patch 直接替换 SDK 类方法
- Callback Handler 实现框架原生回调接口
- Trace Processor 实现框架的追踪处理器协议
- OTel 桥接则通过 OpenTelemetry 标准协议转换
所有路径最终都通过 TraceManager 统一管理 span 的生命周期。
在其中不同的数据最终都会转变成span系统

- BaseSpan 是所有 span 的基类,包含通用字段(uuid、父子关系、时间戳、状态)。
- 四种子类分别对应 LLM 调用、Agent执行、工具调用、检索器调用,各自携带领域特定的属性。
- Trace 是顶层容器,包含一棵 span 树。
其核心在于Observer 与 TraceManager
Observer 是一个上下文管理器,负责 span 的创建、注册、上下文传播和结束:
# 简化的 Observer 工作流程
class Observer:
def __enter__(self):
# 1. 确保有活跃的 trace(没有则创建)
# 2. 根据 span_type 创建对应的 span 实例
# 3. 将 span 注册到 trace_manager.active_spans
# 4. 设置 current_span_context(ContextVar)
# 5. 建立父子关系
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 1. 设置 span 状态(SUCCESS / ERRORED)
# 2. 记录 end_time
# 3. 恢复 current_span_context 到父 span
# 4. 如果是根 span,结束 trace
trace_manager = TraceManager() # 全局单例
class TraceManager:
def configure(self, openai_client=None, anthropic_client=None, ...):
# 配置客户端 patch、采样率、环境等
def start_new_trace(self) -> Trace:
# 创建新 trace,注册到 active_traces
def end_trace(self, trace_uuid):
# 结束 trace,根据 eval_session.mode 决定:
# - 非评估模式:post_trace() 发送到 Confident AI
# - 评估模式:存入 eval_session 供 evaluate() 使用
def post_trace(self, trace):
# 通过后台 worker 线程异步发送 trace 到 API
# deepeval/tracing/context.py
current_trace_context: ContextVar[Optional[Trace]] = ContextVar(...)
current_span_context: ContextVar[Optional[BaseSpan]] = ContextVar(...)
所有集成都通过这两个 ContextVar 实现 span 的嵌套和父子关系。
异步场景下,LangChain 的 CallbackHandler 通过_ctx()上下文管理器显式恢复context,解决跨 Task 边界的 ContextVar 丢失问题。
模式一:Monkey-Patch(OpenAI / Anthropic)

用户只需将from openai import OpenAI改为 from deepeval.openai import OpenAI,导入时自动触发类级别的 monkey-patch。
每次 API 调用被包裹在@observe(type="llm") 中,自动提取 model、messages、token 用量等信息填充到 LlmSpan。
这些主要是在 deepeval/openai 和deepeval/tracing/patchers.py 。
OpenAI 的两种 patch 方式区别:
- 类级别 patch(
from deepeval.openai import OpenAI):替换 SDK 类的方法,所有实例自动生效 - 实例级别 patch(
trace_manager.configure(openai_client=client)):只 patch 传入的特定客户端实例
Anthropic 仅支持实例级别 patch:
from anthropic import Anthropic
from deepeval.tracing import trace_manager
client = Anthropic()
trace_manager.configure(anthropic_client=client)
Anthropic patcher 本身不创建 span,它依赖调用方已通过 @observe(type="llm") 在上下文中放置了 LlmSpan,patcher 只负责填充model、input、output、token 用量。
模式二:Callback Handler(LangChain / LangGraph / LlamaIndex / CrewAI)

框架在执行过程中主动调用回调方法,deepeval 的 CallbackHandler 在每个回调中创建/结束对应类型的 span。
通过 run_id → span_uuid的映射维护正确的父子层级关系。
LlamaIndex span 分类启发式:基于方法名(run → agent, call_tool → tool, retrieve → retriever)
LangGraph 兼容:LangGraph 构建在 LangChain 之上,使用相同的 callback 系统,
on_chain_start/end自动捕获 graph node 执行CrewAI 双层 hook:
- Monkey-patch 层(
wrap_all()):替换Crew.kickoff、Agent.execute_task等方法,用 Observer 创建顶层 span - 事件监听层(
CrewAIEventsListener(BaseEventListener)):监听 LLMCallStartedEvent、ToolUsageStartedEvent 等细粒度事件创建子 span
- Monkey-patch 层(
模式三:Trace Processor(OpenAI Agents)

OpenAI Agents SDK 提供了TracingProcessor接口,deepeval 实现该接口后注册为 trace processor。
SDK 在执行过程中主动推送 span的开始/结束事件,deepeval 根据 SpanData 的具体类型(AgentSpanData、GenerationSpanData、FunctionSpanData 等)分类为对应的 span 类型。
在deepeval/openai_agents/callback_handler.py中有:
class DeepEvalTracingProcessor(TracingProcessor):
def on_span_start(self, span: "Span") -> None:
span_type = self.get_span_kind(span.span_data)
if span_type == "noop":
return
observer = Observer(span_type=span_type, func_name="NA")
self.span_observers[span.span_id] = observer
observer.__enter__()
def on_span_end(self, span: "Span") -> None:
observer = self.span_observers.pop(span.span_id, None)
if observer:
observer.__exit__(None, None, None)
def get_span_kind(self, span_data) -> str:
if isinstance(span_data, AgentSpanData): return "agent"
if isinstance(span_data, FunctionSpanData): return "tool"
if isinstance(span_data, GenerationSpanData): return "llm"
if isinstance(span_data, ResponseSpanData): return "llm"
# TaskSpanData, TurnSpanData 等 → "noop"(忽略)
模式四:OTel SpanProcessor(Pydantic AI / AWS AgentCore)

这些框架原生支持 OpenTelemetry,deepeval 注册自定义的 SpanProcessor 来拦截 OTel span。
SpanInterceptor 负责将框架特定的 OTel 属性翻译为
confident.*命名空间的属性。ContextAwareSpanProcessor 根据是否存在 deepeval trace context 来智能路由:
- 有 context 时走 REST 路径(通过ConfidentSpanExporter 重建为 deepeval 的 BaseSpan 树)
- 无 context 时走 OTLP 直推
eval 指标

所有指标位于deepeval/metrics/目录下,每个指标一个子文件夹,包含三个核心文件:
<metric>.py— 核心实现(measure / a_measure)schema.py— Pydantic 输出模型template.py— LLM 提示词模板
真实性
这是 RAG 系统最核心的诉求。用户给了agent一堆参考资料,agent的回答必须忠于这些资料,不能"创造性发挥"。
它的颗粒度在sequence,所以一般都会拆开来句子,然后逐个判断。
这个范式是 deepeval 最常见的设计模式。你会在后面的偏见、毒性、指令遵循等指标里反复看到它。
主要治标有:
- FaithfulnessMetric:每句话都能在参考资料里找到依据吗
- HallucinationMetric:编了多少没有出处的东西
- ContextualRecallMetric:参考资料里的关键信息有没有用
只有拆开来每一句分析,才能知道溯源回去看看哪里不对。
除了真实性本身错,还有比如说检索不对导致的真实性错了

这三个参数是互补的,巧妙之处在于 ContextualPrecision 不是简单的 "相关数/总数"。
它用的是加权精确率@k——相关文档排在越前面,得分越高。
这背后的直觉是:如果有用的段落被埋在第 10 条,LLM可能根本看不到它(受限于注意力窗口),等于白检索。