DeepEval OTel 指南:confident.trace.*追踪级属性全解与 Confident AI 观测数据契约
【免费下载链接】deepevalThe LLM Evaluation Framework项目地址: https://gitcode.com/GitHub_Trending/de/deepeval
本篇基于 DeepEval 仓库中 trace-attributes.md 参考文档,系统讲解 Confident AI 观测平台(Observatory)在接收原始 OpenTelemetry(OTLP)追踪时如何识别"整条 Trace"级别的元数据:全部 15 个confident.trace.*属性的键名、类型与语义、环境(environment)的解析优先级,以及 OTLP 数据类型编码规则。读完后你能够不依赖deepevalPython 包、仅用任意语言的 OpenTelemetry SDK,把 AI 应用(LLM 应用、Agent、RAG 管线、聊天机器人)的完整追踪以正确的数据契约导出到 Confident AI,并用仓库内的模板代码做冒烟验证。
追踪级属性与 Span 级属性的边界
在动手之前必须先分清两级数据的语义边界:
- Trace(追踪)是一次端到端的完整执行(例如一次用户提问触发的 Agent 运行),它的各个组成部分是 Span;
- Span 级属性(
confident.span.*及按类型划分的confident.llm.*、confident.agent.*、confident.retriever.*、confident.tool.*)描述的是单个 Span——也就是追踪中的某一个组件,这些字段单独记录在 span-attributes.md 中; - Trace 级属性(即本篇主题)用
confident.trace.*前缀,描述的是整条追踪。设置方式非常直接:把它们作为普通属性写到该追踪中的任意一个 Span上——最自然的位置是根 Span。Confident AI 的导出端会把这些属性聚合(aggregate)到 Trace 层面,你无需在 OTLP 协议之外再做任何 Trace 级封装。
这个设计带来两个实用推论:其一,属性写在哪个 Span 上都合法,但集中在根 Span 上可读性最好;其二,父子嵌套关系不靠属性表达,而完全来自原生 OpenTelemetry 的 Span 上下文(在tracer.start_as_current_span(...)的with块内开启的子 Span 会自动挂到父 Span 下)。
confident.trace.*属性全表
以下是参考文档定义的完整属性表。所有属性均为可选——只设置对你的应用有意义的字段即可。
| 属性键 | 类型 | 说明 |
|---|---|---|
confident.trace.name | string | 人类可读的追踪名称。 |
confident.trace.input | string | 追踪输入。透传字段;若值本身不是字符串,先 JSON 编码。 |
confident.trace.output | string | 追踪输出。透传字段;若值本身不是字符串,先 JSON 编码。 |
confident.trace.user_id | string | 终端用户 / 客户标识符。 |
confident.trace.thread_id | string | 对话或会话线程标识符。 |
confident.trace.tags | list of strings | 分组标签。原生 OTLP 字符串数组,或 JSON 数组字符串。 |
confident.trace.metadata | JSON string | 任意键值上下文。必须是 JSON 编码的对象字符串(OTLP 没有 map 类型)。 |
confident.trace.environment | string | 部署环境。默认"production",解析规则见下文专节。 |
confident.trace.retrieval_context | list of strings | 该追踪检索到的文本块 / 文档。原生 OTLP 字符串数组,或 JSON 数组字符串。 |
confident.trace.context | list of strings | 该追踪的"真值"(ground-truth)上下文。原生 OTLP 字符串数组,或 JSON 数组字符串。 |
confident.trace.tools_called | list of strings | 追踪过程中实际调用的工具。原生 OTLP 列表,其中每个元素是一个 JSON 序列化的ToolCall。 |
confident.trace.expected_tools | list of strings | 本应被调用的工具。原生 OTLP 列表,元素为 JSON 序列化的ToolCall字符串。 |
confident.trace.test_case_id | string | 对某个测试用例(test case)ID 的引用。 |
confident.trace.turn_id | string | 多轮对话中的轮次标识。 |
confident.trace.metric_collection | string | 一个 Confident AI 指标集合(metric collection)的名称,用于对该追踪执行在线(服务端)评估。 |
几点使用提示(直接来自参考文档的语义):
confident.trace.input/confident.trace.output是透传(passthrough):它们不做解析,所以如果你的输入输出是对象(如消息列表),请先json.dumps再写入,否则会被序列化器拒绝或丢失结构;confident.trace.tags用于在 Observatory 里对追踪做分组筛选,user_id/thread_id则是用户维度与多轮会话维度分析的基础字段;confident.trace.metric_collection是在线评估(server-side evals)的入口:写上指标集合名后,Confident AI 会在服务端对落地的追踪自动跑这套指标,无需在应用侧实现评估逻辑;test_case_id与turn_id用于把生产追踪与离线评估的测试用例、多轮会话的轮次对应起来,属于把"线上观测"与"离线评测"打通的关键字段。
Environment 解析:Resource 优先,默认production
confident.trace.environment接受部署环境字符串,常见取值为"production"、"staging"、"development"、"testing",缺省值为"production"。它可以在两个位置设置,且Resource 属性优先于 Span 属性:
- 作为Span 属性:
confident.trace.environment直接写在某个 Span 上; - 作为 OpenTelemetryResource 属性:挂在
TracerProvider的Resource上,键名同样是confident.trace.environment。这是推荐做法——对整个进程一次性打上环境戳,避免每个 Span / 每次埋点重复设置。
当两处同时存在且取值冲突时,以 Resource 上的值为准。这个优先级设计的意图很清晰:环境是部署层面的事实,应由进程级配置(Resource)表达,Span 级设置只是细粒度覆盖的退路。
用 Python 表达即:
from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.resources import Resource # 推荐:进程级一次性打环境戳(Resource 优先于 Span 属性) resource = Resource.create({ "service.name": "my-ai-app", "confident.trace.environment": "staging", }) provider = TracerProvider(resource=resource)OTLP 数据类型规则:JSON 字符串 vs 原生数组
参考文档明确声明:对象用 JSON 字符串、列表用原生 OTLP 数组、ToolCall列表的特殊编码,这三条规则对追踪级与 Span 级属性完全一致,统一收录在 span-attributes.md 的Data-Type Rules一节。在编码tags、metadata、context、retrieval_context、tools_called或expected_tools之前必须先读那一节。其核心内容如下(OpenTelemetry 属性值只允许原始类型或同质的原始类型列表,不存在 map / object 属性类型):
- 对象 / dict(
confident.span.metadata、confident.trace.metadata)必须是JSON 编码字符串——即json.dumps(...)的结果,而不是 dict 本身; - 字符串列表(
tags、context、retrieval_context,以及 Span 级的available_tools、agent_handoffs)可以用原生 OTLP 字符串数组(Python 里的list/tupleofstr),JSON 数组字符串也可以被接受; ToolCall列表(tools_called、expected_tools)必须是原生 OTLP 列表、每个元素是一个 JSON 序列化的ToolCall字符串——即"一个列表装若干 JSON 字符串",而不是"一个 JSON 字符串装一个列表"。这是最容易被写错的形态;input/output是透传:值若不是字符串,先 JSON 编码再设置;- 数字(
top_k、chunk_size、token 数、成本等)以原生 int / float 设置,不要写成字符串。
对应的最小编码示例:
import json span.set_attribute("confident.trace.tags", ["support", "example"]) # 原生数组 ✅ span.set_attribute( "confident.trace.metadata", json.dumps({"app_version": "1.0.0", "route": "order_status"}), # dict 必须 dumps ✅ ) span.set_attribute( "confident.trace.tools_called", [json.dumps({"name": "lookup_order", "args": {"order_id": "123"}})], # 列表,元素各自是 JSON 字符串 ✅ )源码级证据:ConfidentAttr键注册表与update_current_trace
参考文档定义的是"原始 OTLP 契约",而 DeepEval Python SDK 侧存在与之对应的规范键注册表,可以拿来交叉印证键名的拼写与集合。
ConfidentAttr(位于 attributes.py)集中定义了所有confident.*规范键,其中追踪级字段(第 54–87 行)与参考文档的 15 个属性一一对应:TRACE_NAME、TRACE_INPUT、TRACE_OUTPUT、TRACE_USER_ID、TRACE_THREAD_ID、TRACE_TAGS、TRACE_METADATA、TRACE_ENVIRONMENT、TRACE_RETRIEVAL_CONTEXT、TRACE_CONTEXT、TRACE_TOOLS_CALLED、TRACE_EXPECTED_TOOLS、TRACE_TEST_CASE_ID、TRACE_TURN_ID、TRACE_METRIC_COLLECTION。从源码结构看,该注册表还额外包含了TRACE_TEST_RUN_ID以及一组confident.trace.llm_test_case.*前缀键(input / context / expected_output / actual_output / retrieval_context / tools_called / expected_tools),这些是 SDK 把LLMTestCase字段落到追踪上时使用的内部键,不在原始 OTLP 参考文档的对外契约表里。
该模块的文档字符串也解释了为什么键名必须精确:
集成方把键写到 OTel Span 上,
ConfidentSpanExporter再读回来,因此拼写错误是隐性的——错误会落在一个没人查看的键上,对应字段就会无声地从追踪中消失。
这正是参考文档反复强调键名一字不差的原因。
SDK 的编程接口侧,update_current_trace(位于 context.py)暴露了与上述属性几乎同名的参数:name、tags、metadata、thread_id、user_id、input、output、retrieval_context、context、expected_output、tools_called、expected_tools、test_case、test_case_id、turn_id、metric_collection等,并支持直接传入一个test_case参数,一次性把LLMTestCase的 input、actual_output、expected_output、retrieval_context、context、tools_called、expected_tools 全部写入当前追踪上下文。可以看到,参考文档中的每个追踪级属性都在 SDK API 中有对应入口,两者是同一契约的"OTLP 原始形态"与"Python 便捷形态"。
实战:用官方模板发出一条带confident.trace.*的示例 Trace
confident_otel_setup.py 模板提供了可直接运行的最小示例:一个 agent 根 Span 包裹一个子 LLM Span,并在根 Span 上设置追踪级属性。模板中追踪级属性的写法(第 75–84、107 行)恰好覆盖了前文所有编码规则的典型形态:
with tracer.start_as_current_span("support-agent") as root: # 追踪级属性(confident.trace.*)可写在任意 Span 上,根 Span 最自然。 root.set_attribute("confident.trace.name", "support-chat") root.set_attribute("confident.trace.input", "Where is my order?") # 字符串列表 → 原生 OTLP 数组 root.set_attribute("confident.trace.tags", ["support", "example"]) # dict / metadata → 必须 JSON 编码为字符串(OTLP 没有 map 类型) root.set_attribute( "confident.trace.metadata", json.dumps({"app_version": "1.0.0", "route": "order_status"}), ) # ...(子 LLM Span 见模板第 87–104 行) root.set_attribute("confident.trace.output", answer)运行前需要:
- 安装依赖:
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http; - 导出
CONFIDENT_API_KEY环境变量(不要硬编码在源码里); - 直接
python confident_otel_setup.py——脚本会自动按 API key 的地区前缀选择端点(confident_eu_...走https://eu.otel.confident-ai.com,其余走https://otel.confident-ai.com),以OTLPSpanExporter指向<endpoint>/v1/traces并携带x-confident-api-key头完成导出,退出前调用trace.get_tracer_provider().shutdown()冲刷批处理器(BatchSpanProcessor)。
端点、鉴权头与"仅 OTLP/HTTP、拒绝 gRPC"等网络侧细节,完整说明见同目录的 endpoint-and-exporter.md;整体接入流程与"只导出 AI Span"的隔离原则见 SKILL.md。
关键要点回顾
- 两级契约:
confident.trace.*描述整条追踪、confident.span.*描述单个组件;追踪级属性写在任意 Span(推荐根 Span)上,由 Confident AI 端聚合到 Trace。 - 15 个属性全部可选:name / input / output / user_id / thread_id / tags / metadata / environment / retrieval_context / context / tools_called / expected_tools / test_case_id / turn_id / metric_collection,只设置有意义的字段。
- Environment 解析:默认
"production";Span 属性与 Resource 属性两处可设,Resource 优先,推荐进程级 Resource 一次性打戳。 - 数据类型规则与 Span 级完全一致:dict 一律
json.dumps,字符串列表用原生 OTLP 数组(JSON 数组字符串亦可),tools_called/expected_tools是"元素各自为 JSON 字符串"的列表,数字用原生 int / float——详见 span-attributes.md。 - 键名是硬契约:拼写错误不会报错,只会让字段静默消失;对照 ConfidentAttr 注册表可校验键名,用 confident_otel_setup.py 模板可端到端冒烟验证。
【免费下载链接】deepevalThe LLM Evaluation Framework项目地址: https://gitcode.com/GitHub_Trending/de/deepeval
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考