Opik Python SDK 的 Spans Client:通过 REST API 全面管理 Span 生命周期
Opik Python SDK 的 Spans Client通过 REST API 全面管理 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 的 REST API 客户端中负责 Span 管理的SpansClient位于 spans/client.py系统讲解如何通过client.rest_client.spans完成 Span 的创建、查询、更新、删除、反馈评分与评论管理。读完本文你将掌握该客户端全部 18 个方法的使用方式、底层 HTTP 端点与核心数据模型并能直接编写可运行的调用代码。一、SpansClient 是什么在 Opik 中Span 是 LLM 应用中一次可观测操作的基本执行单元如一次模型调用、一次检索、一个工具执行多个 Span 通过parent_span_id挂载到同一 Trace 之下形成调用链。面向高阶用户Opik Python SDK 提供了直接访问平台 REST API 的能力SpansClient就是这层 REST 客户端中负责 Span 全部操作的入口。官方文档 spans.rst 通过 Sphinx autodoc 将opik.rest_api.spans.client.SpansClient的全部成员含未文档化成员与继承成员渲染为 API 参考其正文给出的核心用法如下import opik client opik.Opik() # Get a span by ID span client.rest_client.spans.get_span_by_id(span-id) # Search for spans spans client.rest_client.spans.search_spans( project_namemy-project, trace_idtrace-id, max_results100 ) # Add feedback score to a span client.rest_client.spans.add_span_feedback_score( idspan-id, namerelevance, value0.85 )需要注意的是REST 客户端并不保证向后兼容SDK 版本升级时其 API 契约可能发生变化因此官方建议仅在以下场景使用高层 SDK 未覆盖的操作、自定义集成、高级过滤/查询、批量性能优化以及需要处理原始响应时。关于这一约束与客户端整体定位可参考 REST API Overview。二、客户端结构同步与异步、raw 与 data从源码看spans/client.py 定义了四个类类职责SpansClient同步客户端返回解包后的数据_response.dataAsyncSpansClient异步客户端方法均为async def配合asyncio使用RawSpansClient同步 raw 客户端返回HttpResponse[T]AsyncRawSpansClient异步 raw 客户端返回AsyncHttpResponse[T]每个方法都实现了“raw 实现 数据解包”的分层SpansClient内部持有RawSpansClient普通方法直接转发调用并返回_response.data通过client.rest_client.spans.with_raw_response属性可以拿到 raw 客户端从而访问状态码、原始响应头等完整 HTTP 信息。raw 实现位于 spans/raw_client.py由 Fern 从 API 定义自动生成其底层基于httpx客户端self._client_wrapper.httpx_client.request(...)发起请求。异步版本的使用模式import asyncio import opik async def main() - None: client opik.Opik() span await client.rest_client.spans.get_span_by_id(span-id) await client.rest_client.spans.add_span_feedback_score( idspan-id, namerelevance, value0.85 ) asyncio.run(main())三、Span 数据模型与通用请求参数3.1 核心类型Spans 模块依赖的 REST 类型定义在 sdks/python/src/opik/rest_api/types/ 下常用的有SpanWrite创建 Span 时的写入模型字段见 span_write.py其中start_time: dt.datetime是唯一必填字段project_name、trace_id、parent_span_id、name、type、end_time、input、output、metadata、model、provider、tags、usage、error_info、source、environment、total_estimated_cost、ttft等均为可选模型配置了extraallow可容忍后端新增字段。SpanPublic查询时返回的公开 Span 视图。SpanUpdate更新 Span 时的部分更新模型。SpanFilterPublic流式搜索的过滤条件结构为{field: ..., operator: ..., value: ...}见 span_filter_public.py。FeedbackScoreSource反馈分数来源取值为ui、sdk、online_scoring三者之一见 feedback_score_source.py。SpanWriteType / SpanWriteSource / SpanUpdateType / SpanUpdateSourceSpan 类型general/llm/tool 等与来源的枚举。ErrorInfoWrite / ErrorInfo错误信息写入与查询模型。3.2 请求选项与 OMIT 语义所有方法都接受request_options: typing.Optional[RequestOptions] None用于传递请求级配置如超时、chunk_size、自定义 header 等。可选字段以OMIT typing.cast(typing.Any, ...)为默认值spans/client.py表示“该字段不参与序列化”与显式传None有语义区别传OMIT时 JSON 请求体中不含该键传None时则序列化为null。四、完整方法速查表SpansClient共提供 18 个方法按功能分组如下方法签名来自 spans/client.py方法作用返回get_span_by_id(id, *, strip_attachments...)按 ID 获取单个 SpanSpanPublicsearch_spans(...)流式搜索 SpanIterator[bytes]get_spans_by_project(...)按项目分页查询 SpanSpanPagePublicget_span_stats(...)获取 Span 统计ProjectStatsPublicexist(...)判断项目是否已有 Span廉价探测ExistenceResponsecreate_span(*, start_time, ...)创建单个 SpanNonecreate_spans(*, spans)批量创建 SpanNoneupdate_span(id, *, trace_id, ...)更新单个 SpanNonebatch_update_spans(*, ids, update, merge_tags...)批量更新 Span最多 1000 个Nonedelete_span_by_id(id)删除单个 SpanNoneadd_span_feedback_score(id, *, name, value, source, ...)为 Span 添加反馈分数Nonescore_batch_of_spans(*, scores)批量反馈评分Nonedelete_span_feedback_score(id, *, name, author..., ...)删除反馈分数Nonefind_feedback_score_names1(*, project_id..., type...)查询反馈分数名称列表FeedbackScoreNamesPublicadd_span_comment(id_, *, text, ...)添加 Span 评论Noneget_span_comment(span_id, comment_id)获取单条评论Commentupdate_span_comment(comment_id, *, text, ...)更新评论Nonedelete_span_comments(*, ids)批量删除评论None五、创建与写入 Span5.1 创建单个 Spanimport datetime import opik client opik.Opik() client.rest_client.spans.create_span( start_timedatetime.datetime.fromisoformat(2024-01-15 09:30:0000:00), project_namemy-project, namellm_call, typellm, input{prompt: What is the capital of France?}, output{response: Paris}, modelgpt-4o, provideropenai, tags[prod, rag], usage{prompt_tokens: 12, completion_tokens: 5}, ttft320.0, # Time to first token, in milliseconds environmentproduction, )关键参数说明来自 spans/client.py 的 docstringstart_time必填Span 开始时间ISO-8601 格式的datetime。project_name目标项目为None时使用默认项目Default Project。trace_id所属 Traceparent_span_id父 Span ID二者配合即可把 Span 挂入调用链。typeSpanWriteType枚举表示 Span 类型。ttft首 Token 延迟毫秒用于 LLM 性能观测。sourceSpanWriteSource标识数据来源。total_estimated_cost/total_estimated_cost_version预估成本及成本版本。从 raw_client.py 可见其底层调用为POST v1/private/spansraw_client.py。5.2 批量创建import datetime import opik from opik.rest_api import SpanWrite client opik.Opik() client.rest_client.spans.create_spans( spans[ SpanWrite( start_timedatetime.datetime.fromisoformat(2024-01-15 09:30:0000:00), namestep-1, project_namemy-project, ), SpanWrite( start_timedatetime.datetime.fromisoformat(2024-01-15 09:30:0500:00), namestep-2, project_namemy-project, ), ] )底层端点POST v1/private/spans/batchraw_client.py。批量写入适用于数据回填或从其他系统导入历史 Trace 的场景。六、查询与检索 Span6.1 按 ID 获取import opik client opik.Opik() span client.rest_client.spans.get_span_by_id(span-id) print(span.name, span.trace_id, span.input)strip_attachments参数可在响应中去除附件内容以减小载荷。底层端点GET v1/private/spans/{id}raw_client.py。6.2 按项目分页查询import opik client opik.Opik() page client.rest_client.spans.get_spans_by_project( page0, size20, project_namemy-project, trace_idtrace-id, # 可选限定到某条 Trace typellm, # 可选按 Span 类型过滤 from_timeNone, to_timeNone, ) spans page.content # 当前页 Span 列表 total page.total # 总数该方法返回SpanPagePublic分页字段为page/size底层端点GET v1/private/spansraw_client.py。6.3 流式搜索search_spanssearch_spans是文档示例中重点演示的检索方式采用 POST 流式接口v1/private/spans/searchraw_client.py返回Iterator[bytes]适合大结果集逐块消费import opik client opik.Opik() for chunk in client.rest_client.spans.search_spans( project_namemy-project, trace_idtrace-id, filters[ {field: name, operator: contains, value: llm}, {field: start_time, operator: , value: 2024-01-01T00:00:00Z}, ], limit100, # Max number of spans to be streamed from_timeNone, to_timeNone, ): print(chunk)参数要点来自 docstringfiltersSequence[SpanFilterPublic]每个元素为field/operator/value三元组。limit最大流式返回的 Span 数量。truncate是否截断 input/output/metadata 中的图片数据。exclude需要从响应中排除的字段SpanSearchStreamRequestPublicExcludeItem。from_time/to_timeISO-8601 时间窗口to_time缺省为当前时间且必须晚于from_time。last_retrieved_id流式游标用于断点续传。6.4 统计与存在性探测import opik client opik.Opik() # 项目级统计 stats client.rest_client.spans.get_span_stats( project_namemy-project, ) print(stats) # 是否存在至少一条 SpanLIMIT 1 的廉价探测 exists client.rest_client.spans.exist(project_namemy-project)get_span_stats返回ProjectStatsPublic底层端点GET v1/private/spans/statsraw_client.py。exist的 docstring 明确说明其用途是驱动空状态 UI 决策的廉价存在性探测LIMIT 1避免全项目扫描聚合底层端点GET v1/private/spans/existsraw_client.py。七、更新与删除 Span7.1 更新单个 Spanimport opik client opik.Opik() client.rest_client.spans.update_span( idspan-id, trace_idtrace-id, project_namemy-project, end_time2024-01-15T09:30:1000:00, output{response: Paris (updated)}, tags_to_add[reviewed], tags_to_remove[draft], usage{prompt_tokens: 12, completion_tokens: 5, total_tokens: 17}, )docstring 要点trace_id为必填project_name与project_id均为空时默认归属 Default Projecttags整体替换标签而tags_to_add/tags_to_remove做增量修改。底层端点PATCH v1/private/spans/{id}raw_client.py。7.2 批量更新import opik from opik.rest_api import SpanUpdate client opik.Opik() client.rest_client.spans.batch_update_spans( ids[span-1, span-2], updateSpanUpdate(trace_idtrace-id), merge_tagsTrue, # True 时与既有标签合并默认 False 为整体替换 )ids最多 1000 个merge_tagsFalse默认会用新标签整体替换旧标签。底层端点PATCH v1/private/spans/batchraw_client.py。7.3 删除import opik client opik.Opik() client.rest_client.spans.delete_span_by_id(span-id)底层端点DELETE v1/private/spans/{id}raw_client.py。八、反馈评分单条与批量8.1 单条评分import opik client opik.Opik() client.rest_client.spans.add_span_feedback_score( idspan-id, namerelevance, value0.85, sourcesdk, # ui | sdk | online_scoring category_nameNone, # 可选分数类别 reasonMatches user intent, # 可选评分理由 )source为必填的FeedbackScoreSource仅接受ui、sdk、online_scoring三个字面量。底层端点PUT v1/private/spans/{id}/feedback-scoresraw_client.py。8.2 批量评分import opik from opik.rest_api import FeedbackScoreBatchItem client opik.Opik() client.rest_client.spans.score_batch_of_spans( scores[ FeedbackScoreBatchItem( idspan-1, namerelevance, value0.9, sourcesdk, ), FeedbackScoreBatchItem( idspan-2, namerelevance, value0.7, sourcesdk, ), ] )底层端点PUT v1/private/spans/feedback-scoresraw_client.py。批量评分适合评测系统跑完一批样本后统一回写结果。8.3 删除评分与查询评分名import opik client opik.Opik() client.rest_client.spans.delete_span_feedback_score( idspan-id, namerelevance, authortester, # 可选 ) names client.rest_client.spans.find_feedback_score_names1( project_idproject-id, typellm, ) print(names)delete_span_feedback_score的底层端点POST v1/private/spans/{id}/feedback-scores/delete[raw_client.py](https://link.gitcode.com/i/09c90c3261d775b431a1e996904abb6a#L862find_feedback_score_names1返回FeedbackScoreNamesPublic底层端点GET v1/private/spans/feedback-scores/namesraw_client.py。九、Span 评论管理Opik 支持在 Span 上挂载人工评论便于标注与团队协作import opik client opik.Opik() # 添加评论 client.rest_client.spans.add_span_comment( id_span-id, textThis span shows high latency, worth investigating, ) # 查询单条评论 comment client.rest_client.spans.get_span_comment( span_idspan-id, comment_idcomment-id, ) print(comment.text) # 更新评论内容 client.rest_client.spans.update_span_comment( comment_idcomment-id, textLatency resolved after model switch, ) # 批量删除评论 client.rest_client.spans.delete_span_comments(ids[comment-id])对应底层端点均在 raw_client.py 中添加POST v1/private/spans/{id}/comments查询GET v1/private/spans/{span_id}/comments/{comment_id}更新PATCH v1/private/spans/comments/{comment_id}raw_client.py删除POST v1/private/spans/comments/deleteraw_client.py十、错误处理与使用建议REST 客户端在非 2xx 响应时抛出ApiError来自 opik.rest_api.core.api_error在 raw 客户端中通过ApiError(status_code..., headers..., body...)构造可以按状态码区分错误from opik.rest_api.core.api_error import ApiError try: span client.rest_client.spans.get_span_by_id(invalid-id) except ApiError as e: if e.status_code 404: print(Span not found) else: print(fAPI error: {e.status_code} - {e.body})实践建议需要原始 HTTP 响应状态码、headers时使用client.rest_client.spans.with_raw_response获取RawSpansClient普通场景直接用SpansClient的方法即可拿到解包数据。批量导入历史数据优先使用create_spans批量回写评测结果优先使用score_batch_of_spans可显著减少请求往返。大结果集检索优先使用search_spans流式接口配合limit与last_retrieved_id控制流量需要精确分页浏览时再使用get_spans_by_project。由于 REST 客户端不保证跨版本向后兼容关键业务代码应固定 SDK 版本并做好回归测试。十一、相关参考官方文档Spans Client、REST API Overview客户端实现spans/client.py、spans/raw_client.py请求类型spans/types/如GetSpansByProjectRequestType、SpanSearchStreamRequestPublicType数据模型types/span_write.py、types/span_filter_public.py、types/feedback_score_source.py高层 SDK 入口opik.Opik()的rest_client属性对应 overview.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),仅供参考