Opik Python SDK:用 opik_context.get_current_span_data 在 track 追踪函数中读取当前 Span 数据
2026/9/13 8:35:37 网站建设 项目流程

Opik Python SDK:用 opik_context.get_current_span_data 在 track 追踪函数中读取当前 Span 数据

【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm

本文围绕 Opik Python SDK 的 API 文档页get_current_span_data展开,讲解如何在@track装饰器包裹的函数内部获取当前正在执行的 Span 上下文数据(SpanData),包括其返回结构、返回None的边界条件、底层基于contextvars的 span 栈实现原理,以及在 Guardrails、分布式追踪等真实场景中的典型用法。读完本文,你可以掌握在任意被追踪代码中"自下而上"读取 trace_id、span id、父 span 关系、model/usage 等字段的方法,并理解它与get_current_trace_dataupdate_current_spanget_distributed_trace_headers等配套函数的调用关系。

文档定位:一个 API 参考页背后是完整的上下文模块

仓库中的关联文档 get_current_span_data.rst 本身是一页 Sphinxautofunction生成的 API 参考页,仅一行核心指令:

.. autofunction:: opik.opik_context.get_current_span_data

它指向 SDK 中真实实现的 opik_context.py 模块里的get_current_span_data函数。该模块所属的opik_context子包整体介绍"如何从被追踪函数内部访问当前 span 和 trace 数据",完整用法示例见 opik_context/index.rst:

from opik import opik_context, track @track def my_function(): # Get the current span data span_data = opik_context.get_current_span_data() print(span_data) # Get the current trace data trace_data = opik_context.get_current_trace_data() print(trace_data) # Update the current span metadata opik_context.update_current_span(metadata={"my_key": "my_value"}) # Update the current trace tags opik_context.update_current_trace(tags=["my_tag"])

下面逐层拆解get_current_span_data的签名、行为与底层机制。

函数签名与核心行为

get_current_span_data的实现位于 opik_context.py:

def get_current_span_data() -> Optional[span.SpanData]: """ Returns the current span created by track() decorator or None if no span was found. """ span_data = context_storage.top_span_data() if span_data is None: return None return span.SpanData(**span_data.__dict__)

要点逐条说明:

  • 无参数调用:它不接收任何参数,完全依赖"当前执行上下文"来定位 span。也就是说,调用方必须位于由@track装饰器(或等价的 span context manager)创建的追踪函数内部。
  • 返回值类型Optional[span.SpanData]。在追踪上下文内返回SpanData对象;若上下文中不存在任何 span,返回None而不是抛异常。这与同模块的update_current_spanget_distributed_trace_headers(上下文为空时抛出OpikException("There is no span in the context."),见 opik_context.py)形成明确对比:"读取"语义宽容,"写入/派生"语义严格
  • 返回的是副本:实现通过span.SpanData(**span_data.__dict__)用当前栈顶 span 的全部属性重新构造了一个新的SpanData实例。从源码结构看,这是把上下文栈中维护的内部对象拷贝出来交给调用方,避免业务代码在函数执行过程中意外改动栈内状态(__dict__展开属于浅拷贝,嵌套的 metadata 等字典仍与内部对象共享引用,深度隔离并非设计目标)。

返回的 SpanData 结构有哪些字段

SpanData定义于 span_data.py,继承自 observation_data.py 的ObservationData。字段分为两组:

SpanData 自有字段

字段类型说明
trace_idstr所属 trace 的 id(SpanData 的必填字段)
idstr当前 span 的 id,默认由helpers.generate_id自动生成
parent_span_idOptional[str]父 span 的 id,根 span 为None
typeSpanTypespan 类型,默认"general",LLM 调用为"llm"
usageOptional[Dict[str, Any] \| OpikUsage]token 用量数据
modelOptional[str]所用 LLM 名称
providerOptional[str \| LLMProvider]LLM 提供方
total_costOptional[float]本次调用成本(USD)

ObservationData 基类通用字段TraceDataSpanData共享):namestart_timeend_timemetadatainputoutputtagsfeedback_scoresproject_nameerror_infoattachmentssource(默认"sdk")、environment

拿到span_data后,一个典型用法是提取关键标识用于日志关联或跨系统透传:

from opik import opik_context, track @track def process_user_query(query: str) -> str: span_data = opik_context.get_current_span_data() if span_data is not None: # 将 trace/span 标识写入业务日志,便于与 Opik UI 中的 trace 对齐 logger.info( "processing query, trace_id=%s, span_id=%s, project=%s", span_data.trace_id, span_data.id, span_data.project_name, ) return query.upper()

边界条件:何时返回 None

返回None的场景包括:

  1. 在追踪上下文外调用:顶层脚本、未被@track覆盖的普通函数中直接调用,此时 span 栈为空。
  2. 追踪被运行时配置禁用:SDK 有 tracing 运行时开关(tracing_runtime_config.is_tracing_active(),见 opik_context.py 中update_current_span的短路逻辑)。从源码结构看,禁用追踪时 span 不会被压入上下文栈,get_current_span_data自然返回None。SDK 的单元测试也专门覆盖了这一路径,例如 test_track_disabled_mode.py 验证了 disabled 模式下上下文 API 的表现。

因此生产代码中的稳健写法是先判空再使用:

span_data = opik_context.get_current_span_data() if span_data is None: return # 或降级到本地日志

底层原理:基于 contextvars 的不可变 span 栈

get_current_span_data的数据来源是 context_storage.py 中的OpikContextStorage@track进入被追踪函数时会add_span_data压栈,函数退出时pop_span_data出栈;get_current_span_data内部调用的top_span_data()直接取栈顶元素:

def top_span_data(self) -> Optional[span.SpanData]: if self.span_data_stack_empty(): return None stack = self._spans_data_stack_context.get() return stack[-1]

该实现有两个值得注意的设计点:

  • 上下文隔离:span 栈存放在contextvars.ContextVar中,且刻意使用不可变 tuple并以"取旧值、构造新值再 set"的模式更新(见 context_storage.py 类注释与 add_span_data)。这保证了多线程、asyncio 并发任务之间各自的 span 栈互不污染——"当前 span"是相对于当前执行上下文而言的,而不是进程全局单例。
  • 嵌套 @track 的语义@track嵌套调用时内层 span 会压到栈顶,因此内层函数里get_current_span_data拿到的是最内层那个 span,其parent_span_id指向外层 span 的idtrace_id保持一致。这一父链关系正是 Opik 在 UI 中渲染嵌套 span 树的基础。

同一文件还提供了防御性工具trim_span_data_stack_to_certain_span(context_storage.py),供回调式集成(如 LangChain callback)在可能漏掉 pop 操作时按已知 span id 截断栈,避免"悬挂 span"污染后续上下文——这也从侧面说明 span 栈模型是 SDK 追踪体系的核心数据结构。

SDK 内部真实调用场景

get_current_span_data不只是给用户用的公共 API,SDK 自身也大量依赖它。在 guardrail.py 中,Guardrail 执行时通过current_span = get_current_span_data()找到当前 span,把守卫的评估结果(通过/拦截、分数)挂载到正在执行的追踪数据上,从而让拦截行为出现在 Opik 对应 trace 的 span 详情里。类似地,get_current_trace_data的调用点散布于 llm_judge.py、ragas_metric.py 等评估与集成模块中,用于把自动评估分数回写到当前 trace。

与同模块其他函数的协作关系

opik_context模块的完整 API 面(见 opik_context.py 的__all__)可以按"读取 / 更新 / 派生"三类理解get_current_span_data的位置:

函数类别无 span 时的行为典型用途
get_current_span_data()读取返回None获取当前 span 的 id、trace_id、父子关系
get_current_trace_data()读取返回None获取当前 trace 的 id 与字段
update_current_span(...)更新OpikException在追踪函数内补写 metadata、output、feedback_scores 等
update_current_trace(...)更新OpikException补写 trace 级字段
get_distributed_trace_headers()派生OpikException拿到{opik_trace_id, opik_parent_span_id}字典,透传给远程节点实现跨进程追踪

一个组合场景是分布式追踪:index.rst 给出了直接取分布式头的示例;若你需要更细粒度地拼装 header,也可以先span_data = opik_context.get_current_span_data(),再用span_data.trace_idspan_data.id构造等价内容——get_distributed_trace_headers 内部正是这样做的。

验证手段

该 API 的行为有对应测试可查证:

  • test_span_context_manager.py:验证@track/ span context manager 内get_current_span_data返回的 span 数据(trace_id、parent 关系等)符合预期;
  • test_track_disabled_mode.py:覆盖追踪禁用时的上下文行为;
  • test_distributed_headers_context_manager.py:覆盖基于当前 span 的分布式头生成;
  • 端到端层面,test_tracing.py 等 E2E 用例确认了这些字段最终落库后可在检索结果中被读取。

小结

opik_context.get_current_span_data是 Opik Python SDK 中"追踪函数自省"的入口:它以零参数返回当前@track上下文栈顶的SpanData副本,无上下文时安静地返回None;其背后是contextvars+ 不可变 tuple 实现的线程/任务安全 span 栈。掌握它之后,你可以在业务代码里把 trace_id / span_id 写入日志系统做跨系统关联,在自定义 Guardrail 或评估逻辑中把结果挂回当前 span,并在此基础上用get_distributed_trace_headers把追踪跨进程传递。如需创建新的 trace/span 而非读取现有上下文,请参阅 SDK 的 context manager 文档(context_manager/index.rst 所指向的目录)。

【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm

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

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

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

立即咨询