OceanPresent

OpenTelemetry GenAI 语义约定和接入


第一部分:规范调研

一、GenAI规范概述

OpenTelemetry GenAI 语义约定(Semantic Conventions for Generative AI)是一套专为生成式 AI 系统设计的可观测性标准,定义了统一的 Span、Metric 和 Event 规范,使不同 LLM 提供商和框架能够产出一致的遥测数据。

该规范目前处于 Development(开发中) 状态,尚未正式稳定(Stable),也没有明确的稳定化时间表。最初作为 OpenTelemetry Semantic Conventions 主仓库的一部分,现已迁移至独立仓库 open-telemetry/semantic-conventions-genai,使用 Weaver 工具扩展核心 OTel 规范。截至 2026 年 5 月,规范版本约为 v1.41,涵盖六大层次:Client Spans、Agent/Workflow Spans、MCP Conventions、Events、Metrics、Provider-specific Attributes。

二、Span 结构与操作类型

GenAI 规范围绕 gen_ai.operation.name 属性定义了一组标准操作名,每个操作名对应一种 Span 类型:

核心操作类型包括 chat(聊天/对话式推理,对应 Chat Completions)、text_completion(纯文本生成)、embeddings(向量嵌入计算)、chat_completions(聊天补全)。这些是 LLM 推理的最基础操作。

Agent 与工具操作类型涵盖 invoke_agent(调用 Agent,记录 Agent 的完整执行过程,可拆分为 CLIENT 和 INTERNAL 两种 Span Kind)、execute_tool(执行工具调用,Span 名称中必须包含工具名)、embeddings(RAG 场景下的向量检索)以及 chat 配合 gen_ai.tool.call.* 属性用于 Function Calling 场景。

MCP(Model Context Protocol)约定确保跨 Agent 和工具调用的 Trace 上下文连续性,使分布式 Agent 系统能够保持端到端追踪。

三、属性体系(gen_ai.* Attributes)

GenAI 属性按命名空间组织在 gen_ai.* 前缀下,以下是按功能分组的关键属性:

3.1 操作与身份信息

属性类型说明
gen_ai.operation.namestring操作名称,如 chat、text_completion、embeddings
gen_ai.provider.namestringGenAI 提供商标识,如 openai、anthropic
gen_ai.request.modelstring请求使用的模型名,如 gpt-4o、claude-3.5-sonnet
gen_ai.response.modelstring实际生成响应的模型名(可能与请求的不同)

3.2 请求参数

属性类型说明
gen_ai.request.temperaturedouble采样温度
gen_ai.request.top_pdoubleTop-p 采样参数
gen_ai.request.top_kdoubleTop-k 采样参数
gen_ai.request.max_tokensint最大生成 token 数
gen_ai.request.frequency_penaltydouble频率惩罚
gen_ai.request.presence_penaltydouble存在惩罚
gen_ai.request.stop_sequencesstring[]停止序列
gen_ai.request.seedint随机种子
gen_ai.request.streamboolean是否流式请求
gen_ai.request.choice.countint候选结果数量
gen_ai.request.encoding_formatsstring[]嵌入编码格式

3.3 响应信息

属性类型说明
gen_ai.response.idstring响应唯一标识
gen_ai.response.finish_reasonsstring[]停止生成原因数组,如 stop、length、tool_calls
gen_ai.response.time_to_first_chunkdouble流式响应中首 chunk 到达时间

3.4 输入/输出内容

属性类型说明
gen_ai.input.messagesany发送给模型的聊天历史/输入消息
gen_ai.output.messagesany模型返回的响应消息
gen_ai.system_instructionsany系统提示/指令
gen_ai.output.typestring请求的输出类型,如 text、json、image

内容捕获支持三种模式:关闭(不记录 prompt/completion 内容)、Span 属性(直接记录在 Span 上)和外部存储引用(通过 Event API 将大体积内容记录为独立 Event,Span 上仅保留引用)。OpenTelemetry GenAI Working Group 推荐使用 Event API 而非 Span 属性来记录完整的 prompt 和 completion 内容,以避免 Span 过大。

3.5 Token 用量

属性类型说明
gen_ai.usage.input_tokensint输入(prompt)token 数
gen_ai.usage.output_tokensint输出(completion)token 数
gen_ai.usage.reasoning.output_tokensint推理(chain-of-thought)消耗的 token 数
gen_ai.usage.cache_creation.input_tokensint写入提供商缓存的 token 数
gen_ai.usage.cache_read.input_tokensint从提供商缓存读取的 token 数
gen_ai.token.typestringtoken 类型标识(input/output)

3.6 Agent 与工具

属性类型说明
gen_ai.agent.idstringAgent 唯一标识
gen_ai.agent.namestringAgent 可读名称
gen_ai.agent.descriptionstringAgent 描述
gen_ai.agent.versionstringAgent 版本
gen_ai.conversation.idstring会话/线程唯一标识
gen_ai.workflow.namestring工作流名称
gen_ai.tool.namestring工具名称
gen_ai.tool.descriptionstring工具描述
gen_ai.tool.typestring工具类型(function、extension、datastore)
gen_ai.tool.definitionsany可用工具定义列表
gen_ai.tool.call.idstring工具调用 ID
gen_ai.tool.call.argumentsany工具调用参数
gen_ai.tool.call.resultany工具调用结果

3.7 RAG 与检索

属性类型说明
gen_ai.retrieval.query.textstring检索查询文本
gen_ai.retrieval.documentsany检索到的文档列表
gen_ai.data_source.idstring数据源标识
gen_ai.embeddings.dimension.countint嵌入向量维度数

3.8 评估(Evaluation)

属性类型说明
gen_ai.evaluation.namestring评估指标名称
gen_ai.evaluation.score.valuedouble评估分数
gen_ai.evaluation.score.labelstring评估标签,如 relevant、correct
gen_ai.evaluation.explanationstring评估者的解释说明

3.9 已废弃属性

废弃属性替代方案
gen_ai.system使用 gen_ai.provider.name
gen_ai.prompt使用 Event API + gen_ai.input.messages
gen_ai.completion使用 Event API + gen_ai.output.messages
gen_ai.usage.prompt_tokens使用 gen_ai.usage.input_tokens
gen_ai.usage.completion_tokens使用 gen_ai.usage.output_tokens

四、Metrics 定义

GenAI 规范定义了两个核心 Metric:

gen_ai.client.token.usage:Histogram 类型,记录每次操作的 token 消耗量。通过 gen_ai.token.type(input/output)维度区分输入和输出 token,可按 model、provider 等维度聚合。这是监控 LLM 使用成本的关键指标。

gen_ai.client.operation.duration:Histogram 类型,单位为秒(s),记录 GenAI 操作的端到端耗时。可按 gen_ai.operation.name、gen_ai.request.model、gen_ai.provider.name 等维度切片分析。用于监控延迟和 SLA。

这两个 Metric 配合 Span 属性,可以实现对 LLM 应用的全面性能和成本监控。

五、Agentic AI 扩展(提案中)

针对多 Agent 协作系统,规范社区正在讨论引入更丰富的语义约定(Issue #35、#37),拟新增以下概念:

  • Task(任务):最小可追踪单元,定义目标并可分解为子任务。属性包括 gen_ai.task.id、gen_ai.task.state、gen_ai.task.status、gen_ai.task.feedback 等。
  • Action(动作):执行机制,如工具调用或 LLM 查询,用于完成 Task。
  • Agent(智能体):自主实体,编排 Action 和推理。属性包括 gen_ai.agent.* 系列。
  • Team(团队):Agent 协作组,共享目标。
  • Artifact(制品):有形的输入/输出,如 prompt、embedding。
  • Memory(记忆):持久化存储,支持跨会话上下文复用。

这些概念构成 gen_ai.task.*、gen_ai.action.* 等新的属性命名空间,目前仍在提案阶段。

六、MCP(Model Context Protocol)集成

GenAI 规范已纳入 MCP 语义约定(mcp.* 命名空间),用于标准化 Model Context Protocol 场景下的 Trace 传播。MCP 约定确保当 Agent 通过 MCP 协议调用外部工具或服务时,Trace Context 能够正确地在调用链中传递,实现跨进程、跨服务的端到端可观测性。

七、自动 Instrumentation 库

目前已有多个成熟的自动 Instrumentation 库支持 GenAI 语义约定:

  • OpenAI:opentelemetry-instrumentation-openai(Python),自动捕获 Chat Completions、Embeddings 等操作的 Span 和 Event
  • Anthropic:通过 OpenLLMetry 等社区库支持
  • LangChain / LlamaIndex:有对应的自动 Instrumentation 包
  • Azure OpenAI:通过 Azure SDK 的 OTel 集成
  • MLflow:通过设置 MLFLOW_ENABLE_OTEL_GENAI_SEMCONV=true 环境变量,可以将 MLflow 追踪数据以 GenAI 语义约定格式导出到 OTLP 端点

主流可观测性平台(MLflow、AgentLoop 等)已开始原生支持 OTel GenAI 语义约定的 OTLP 数据接入。


第二部分:实践接入方案

1. 整体架构

1.1 定位

LoongSuite GenAI OpenTelemetry Util for Go(包名 otelgenai)是 Python 版 loongsuite-otel-util-genai 的 Go 实现,为 Go 应用提供标准化的 GenAI 遥测采集能力。它将 LLM 调用、Agent 编排、工具执行、向量检索、文档重排、记忆操作等 AI 操作统一映射为 OpenTelemetry 遥测信号(Span、指标、事件)。

1.2 核心能力

  • 统一 Span 模型:支持 12 种 Span 类型(llm / embedding / execute_tool / create_agent / invoke_agent / retrieval / rerank / entry / react_step / memory / chain / run_task),涵盖 GenAI 全链路
  • Baggage 链路染色:通过 OTel Baggage 机制在 Context 中传播 gen_ai.agent.name / gen_ai.session.id / gen_ai.user.id / gen_ai.framework,实现跨 Span 上下文关联
  • 多模态外置存储:Handler 集成异步上传生命周期,Start* 时启动 goroutine,End 时等待完成,将图像/音频/视频等 BlobPart/Base64BlobPart 以字节流直传到存储后端,不占用额外磁盘空间
  • 指标体系:操作时长直方图 + Token 用量直方图,遵循 OTel GenAI 语义约定
  • 事件发射:可选的 gen_ai.client.inference.operation.details 日志事件,通过 OTel Logger API 发射
  • 内容捕获策略:四种模式(NoContent / SpanOnly / EventOnly / SpanAndEvent)精细控制消息内容写入位置
  • CompletionHook:完整对话内容的上传钩子机制

2. 系统架构

2.1 分层设计

┌─────────────────────────────────────────────────────────────────────┐
│                         用户应用代码                                  │
│   构造 XxxInvocation → Handler.Start*(ctx, inv) → span.End()/Fail() │
└───────────────────────────────┬─────────────────────────────────────┘
                                │
┌───────────────────────────────▼─────────────────────────────────────┐
│                    核心遥测层(根目录模块)                              │
│                                                                     │
│  ┌──────────┐  ┌───────────┐  ┌────────────┐  ┌─────────────────┐  │
│  │ Handler  │──│ XxxSpan   │──│ span_attrs │──│ MetricsRecorder │  │
│  │ (入口)   │  │ (生命周期) │  │ (属性设置)  │  │ (指标记录)      │  │
│  └──────────┘  └───────────┘  └────────────┘  └─────────────────┘  │
│  ┌──────────┐  ┌───────────┐  ┌────────────┐                       │
│  │ Config   │  │ types.go  │  │ option.go  │                       │
│  │ (配置)   │  │ (类型)    │  │ (选项)     │                        │
│  └──────────┘  └───────────┘  └────────────┘                       │
└────────────────┬─────────────────┬──────────────────────────────────┘
                 │                 │
┌────────────────▼───┐  ┌─────────▼──────────────────────────────────┐
│  semconv 模块       │  │  multimodal 模块         upload 模块       │
│                    │  │                                            │
│  attributes.go     │  │  PreUploader → Uploader → Processor        │
│  extended_attrs.go │  │  (异步多模态外置存储)     CompletionHook    │
│  (标准+扩展属性键)  │  │                          (完整内容上传)     │
└────────────────────┘  └────────────────────────────────────────────┘
                 │
┌────────────────▼───┐
│  internal 模块      │
│  envutil.go        │
│  jsonutil.go       │
│  (环境变量/JSON工具) │
└────────────────────┘

2.2 模块划分

模块路径核心文件职责
根目录模块/genai.go, handler.go, spans.go, multimodal_upload.go, span_attrs.go, span_container.go, types.go, config.go, option.go, metrics.go核心遥测处理:Handler 入口、Span 生命周期管理、异步上传协调、属性序列化、指标记录、配置加载、函数选项
internal/internalenvutil.go, jsonutil.go内部工具:环境变量读取(GetEnvOrDefault / GetEnvBool / GetEnvInt)、JSON 序列化
semconv/semconvattributes.go, extended_attributes.goOTel 语义约定:标准 GenAI 属性键 + LoongSuite 扩展属性键、操作名常量、Span 类型值
multimodal/multimodaltypes.go, preuploader.go, uploader.go, processor.go多模态数据异步外置存储:PreUploadItem → PreUploader.Prepare → UploadItem → Uploader.Upload
upload/uploadcompletion_hook.go完整内容上传钩子:CompletionHook 接口 + FsCompletionHook / NoOpCompletionHook 实现

2.3 模块间依赖关系

                    ┌──────────────┐
                    │   用户应用    │
                    └──────┬───────┘
                           │ import otelgenai
                           ▼
              ┌────────────────────────┐
              │   根目录模块 (otelgenai) │
              │  Handler / Span / Config│
              └──┬──────┬──────┬───────┘
                 │      │      │
         import  │      │      │  import
                 ▼      │      ▼
        ┌──────────┐    │   ┌──────────┐
        │  semconv  │    │   │ internal │
        └──────────┘    │   └──────────┘
                        │
          可选集成 ──────┤
                        │
              ┌─────────▼─────────┐
              │                   │
       ┌──────▼──────┐   ┌───────▼──────┐
       │  multimodal  │   │    upload    │
       └─────────────┘   └──────────────┘

说明:根目录模块单向依赖 semconv(属性键常量)和 internal(工具函数)。multimodal 模块由 Handler 在创建时自动集成(多模态上传功能)。upload 模块为独立子包,由用户应用按需集成。


3. 核心组件详解

3.1 Handler — 遥测入口

Handler 是整个库的核心入口,管理 Tracer、Meter、Logger 三大 OTel 组件的实例,并提供所有 Span 类型的启动方法。

type Handler struct {
    tracer          trace.Tracer        // OTel Tracer,创建 Span
    meter           metric.Meter        // OTel Meter,创建指标仪器
    logger          log.Logger          // OTel Logger,发射事件
    config          *Config             // 环境配置
    metricsRecorder *MetricsRecorder    // 指标记录器
    captureMode     ContentCapturingMode // 内容捕获模式
    sessionID       string              // 全局 Session ID(Baggage 回退值)
    userID          string              // 全局 User ID(Baggage 回退值)
    framework       string              // 全局 Framework(Baggage 回退值)
    uploadProcessor *multimodal.Processor // 多模态上传处理器(自动管理生命周期)
}

创建方式:通过 NewHandler(opts ...Option) 构造,支持函数选项模式注入自定义 Provider:

handler, err := otelgenai.NewHandler(
    otelgenai.WithTracerProvider(tp),
    otelgenai.WithMeterProvider(mp),
    otelgenai.WithSessionID("session-123"),
    otelgenai.WithMultimodalUploader(ossUploader),          // 可选:注入自定义多模态 Uploader
    otelgenai.WithMultimodalUploadTimeout(15 * time.Second), // 可选:上传超时
)
defer handler.Shutdown(ctx) // 确保关闭 Processor

全局单例:通过 SetGlobalHandler() / GetGlobalHandler() / MustGetGlobalHandler() 管理全局 Handler 实例,MustGetGlobalHandler() 使用 sync.Once 确保惰性初始化。

Handler 启动方法一览:

方法Span 类型Span KindSpan 命名规则
StartLLM(ctx, *LLMInvocation)LLMClient{operation_name} {request_model}
StartEmbedding(ctx, *EmbeddingInvocation)EmbeddingClientembeddings {request_model}
StartExecuteTool(ctx, *ExecuteToolInvocation)ExecuteToolInternalexecute_tool {tool_name}
StartCreateAgent(ctx, *CreateAgentInvocation)CreateAgentClientcreate_agent {agent_name}
StartInvokeAgent(ctx, *InvokeAgentInvocation)InvokeAgentInternalinvoke_agent {agent_name}
StartRetrieval(ctx, *RetrievalInvocation)RetrievalInternalretrieval
StartRerank(ctx, *RerankInvocation)RerankInternalrerank_documents
StartEntry(ctx, *EntryInvocation)EntryInternalenter_ai_application_system
StartReactStep(ctx, *ReactStepInvocation)ReactStepInternalreact step
StartMemory(ctx, *MemoryInvocation)MemoryClientmemory {operation}
StartChain(ctx, *ChainInvocation)ChainInternalchain {chain_name}
StartTask(ctx, *TaskInvocation)TaskInternalrun_task {task_name}

3.2 XxxSpan — Span 生命周期管理器

每种 Span 类型都有对应的结构体(如 LLMSpan、EmbeddingSpan、InvokeAgentSpan 等),统一遵循以下模式:

type XxxSpan struct {
    span       trace.Span          // 底层 OTel Span
    handler    *Handler            // 关联的 Handler(用于指标/事件)
    Invocation *XxxInvocation      // 关联的调用数据对象
    startTime  time.Time           // 启动时间戳
    ctx        context.Context     // 关联的 Context
    ended      bool                // 防止重复结束
}

通用 API:

  • Context() context.Context — 获取关联的 Context
  • Span() trace.Span — 获取底层 OTel Span
  • End() — 正常结束:applyFinishAttributes → emitEvent(可选) → recordMetrics → span.End()
  • Fail(err error) — 异常结束:applyFinishAttributes → applyErrorAttributes → emitEvent(可选) → recordMetrics → span.End()

类型特定的设置方法(示例):

Span 类型方法
LLMSpanSetOutput(), SetTokenUsage(), SetResponseModel(), SetResponseID(), SetFinishReasons()
EmbeddingSpanSetDimensionCount()
ExecuteToolSpanSetResult()
InvokeAgentSpanSetOutput(), SetTokenUsage()
RetrievalSpanSetDocuments()
RerankSpanSetResults()
ReactStepSpanSetFinishReason()

3.3 MetricsRecorder — 指标记录器

MetricsRecorder 封装了两个直方图仪器:

type MetricsRecorder struct {
    durationHistogram metric.Float64Histogram  // gen_ai.client.operation.duration
    tokenHistogram    metric.Int64Histogram     // gen_ai.client.token.usage
}

指标定义:

指标名类型单位桶边界
gen_ai.client.operation.durationFloat64Histogram秒 (s)[0.01, 0.02, 0.04, ..., 81.92](14 个边界,指数递增)
gen_ai.client.token.usageInt64Histogram{token}[1, 4, 16, 64, 256, ..., 67108864](14 个边界,4 倍递增)

记录方法:

  • RecordDuration(ctx, duration, attrs...) — 记录操作时长
  • RecordTokenUsage(ctx, tokenType, count, attrs...) — 记录 Token 用量(附加 gen_ai.token.type 属性)
  • recordLLMMetrics(ctx, inv, duration, errorType) — LLM 专用,构建完整属性集
  • recordExtendedMetrics(ctx, inv, duration, errorType) — 扩展操作通用,按 Invocation 类型分发

3.4 Config — 配置管理

Config 从环境变量加载,通过 LoadConfigFromEnv() 工厂函数创建。详见 第 9 节 配置体系。


4. 数据类型体系

4.1 MessagePart 接口层次

MessagePart 是所有消息部件的接口,通过非导出方法 partType() string 密封,防止外部包随意实现:

MessagePart (interface, sealed by partType())
├── TextPart                  — 文本内容 {Content, Type:"text"}
├── ReasoningPart             — 推理/思考内容 {Content, Type:"reasoning"}
├── BlobPart                  — 二进制数据 {MimeType, Modality, Content:[]byte, Type:"blob"}
├── Base64BlobPart            — Base64 编码二进制 {MimeType, Modality, Content:string, Type:"base64_blob"}
├── FilePart                  — 文件 ID 引用 {MimeType, Modality, FileID, Type:"file"}
├── UriPart                   — URI 引用 {MimeType, Modality, URI, Type:"uri"}
├── ToolCallPart              — 工具调用请求 {Arguments, Name, ID, Type:"tool_call"}
├── ToolCallResponsePart      — 工具调用结果 {Response, ID, Type:"tool_call_response"}
├── CompactionPart            — 对话压缩 {Content, ID, Type:"compaction"}
├── GenericPart               — 通用扩展 {Type, Properties map[string]interface{}, Type:动态}
├── ServerToolCallPart        — 服务端工具调用 {Name, Arguments, ID, Type:"server_tool_call"}
└── ServerToolCallResponsePart — 服务端工具响应 {Response, ID, Type:"server_tool_call_response"}

Modality 枚举:ModalityImage("image") / ModalityVideo("video") / ModalityAudio("audio") / ModalityDocument("document")

4.2 消息模型

type InputMessage struct {
    Role  string        // "user", "system", "assistant" 等
    Parts []MessagePart // 消息部件列表
    Name  string        // 可选,消息发送者名称
}

type OutputMessage struct {
    Role         string        // 通常为 "assistant"
    Parts        []MessagePart
    Name         string        // 可选,消息发送者名称
    FinishReason string        // "stop", "length", "tool_calls", "error", "content_filter"
}

ToolDefinition:联合类型,可以是 FunctionToolDefinition(函数工具)或 GenericToolDefinition(通用工具)。

4.3 Invocation 类型族

Invocation 是调用数据的载体,每种 Span 类型对应一个 Invocation 结构体:

Invocation关键字段特征
LLMInvocationRequestModel, InputMessages, OutputMessages, SystemInstruction, ToolDefinitions, Token 计数, 延迟参数最完整的 Invocation,包含全部 LLM 请求/响应参数和 LoongSuite 扩展字段(缓存 Token、首 Token 延迟、推理时间等)
EmbeddingInvocationRequestModel, DimensionCount, EncodingFormats, InputTokens向量嵌入操作
ExecuteToolInvocationToolName, ToolCallArguments, ToolCallResult, Skill*工具执行,包含技能关联信息
CreateAgentInvocationAgentName, AgentID, ProviderAgent 创建
InvokeAgentInvocationAgentName, InputMessages, OutputMessages, Token 计数Agent 调用,类似 LLM 但有 Agent 特有字段
RetrievalInvocationQuery, Documents, DataSourceID, TopK文档检索,包含 RetrievalDocument 列表
RerankInvocationQuery, TopK, InputDocuments, OutputDocuments, 各种重排参数文档重排,支持 LLM/Cohere/HuggingFace 多种重排器参数
EntryInvocationSessionID, UserID, InputMessages, OutputMessagesAI 系统入口,注入 Baggage
ReactStepInvocationFinishReason, RoundReAct 单步迭代
MemoryInvocationOperation, UserID, AgentID, 分页参数, 记忆参数记忆操作(add/search/update/delete 等)
ChainInvocationChainName, InputValue, OutputValue, UserTimeToFirstToken链式编排
TaskInvocationTaskName, InputValue, InputMimeType, OutputMimeType自定义任务

所有 Invocation 都包含 Attributes map[string]interface{} 字段用于自定义 Span 属性,部分包含 MetricAttributes 用于自定义指标属性。


5. 核心数据流

5.1 LLM 调用完整流程

                           ① 构造 LLMInvocation
                                  │
                                  ▼
                     ② handler.StartLLM(ctx, inv)
                        ┌─────────┴──────────┐
                        │ injectGlobalBaggage │ ← 注入 handler 级 session/user/framework
                        │ tracer.Start(CLIENT)│ ← 创建 OTel Span
                        │ injectBaggageAttrs  │ ← 从 Baggage 读取 → Span 属性
                        │ applyStartAttributes│ ← 设置启动属性 (operation.name, model, provider...)
                        │ startAsyncUpload   │ ← 扫描 BlobPart/Base64BlobPart,启动异步上传 goroutine
                        │ return LLMSpan      │   (包含 uploadFuture)
                        └─────────┬──────────┘
                                  │
                                  ▼
                     ③ 业务处理 (调用 LLM API)
                        ↑ 与后台上传 goroutine 并行执行
                        span.SetOutput(messages)
                        span.SetTokenUsage(in, out)
                        span.SetResponseModel(model)
                                  │
                                  ▼
                     ④ span.End() 或 span.Fail(err)
                        ┌─────────┴──────────────────────────────┐
                        │                                        │
                        │  a. 捕获 duration                       │
                        │     └─ duration = now - startTime       │
                        │        (不含后续上传等待时间)              │
                        │                                        │
                        │  b. waitAndReplace (若有 uploadFuture)  │
                        │     ├─ 等待上传完成(带超时,默认 30s) │
                        │     └─ BlobPart 替换为 UriPart          │
                        │                                        │
                        │  c. applyLLMFinishAttributes            │
                        │     ├─ 更新 Span 名称                    │
                        │     ├─ 设置 Common + Request + Response  │
                        │     │   属性 (model, tokens, latency...) │
                        │     ├─ 按 ContentCapturingMode 序列化    │
                        │     │   消息内容 → Span 属性              │
                        │     │   (消息中已是 URI,而非原始字节) │
                        │     └─ 设置自定义 Attributes             │
                        │                                        │
                        │  d. emitLLMEvent (若 EmitEvent=true)    │
                        │     └─ logger.Emit → LogRecord          │
                        │        (包含 input/output messages)      │
                        │                                        │
                        │  e. recordLLMMetrics                    │
                        │     ├─ durationHistogram.Record(秒)     │
                        │     ├─ tokenHistogram.Record(input)     │
                        │     └─ tokenHistogram.Record(output)    │
                        │     (使用步骤 a 捕获的纯 LLM duration)│
                        │                                        │
                        │  f. span.End()                          │
                        │     └─ TracerProvider 导出 Span          │
                        │                                        │
                        └────────────────────────────────────────┘

5.2 异常结束流程(Fail)

Fail 与 End 流程基本相同,但有重要区别:

  1. 不等待上传完成:Fail(err) 不会等待异步上传 goroutine 完成,上传在后台继续执行但不影响错误路径
  2. applyErrorAttributes:设置 span.SetStatus(Error)、span.RecordError(err)、error.type 属性
  3. 指标记录时附加 error.type 维度

5.3 扩展操作通用流程

除 LLM 外的 11 种扩展操作(Embedding、ExecuteTool、InvokeAgent 等)遵循统一模式:

handler.StartXxx(ctx, inv) → XxxSpan
    ↓
业务处理 + span.SetXxx(结果)
    ↓
span.End() / span.Fail(err):
    1. applyXxxFinishAttributes → span.SetAttributes
    2. [可选] applyErrorAttributes
    3. recordExtendedMetrics → durationHistogram + tokenHistogram(仅部分类型)
    4. span.End()

注意:扩展操作不发射事件(无 emitEvent),仅 LLM 操作支持事件发射。


6. Baggage 传播机制

6.1 概述

Baggage 是 OTel 的跨进程上下文传播机制。本库利用 Baggage 在 Span 链路中传播 GenAI 业务标识,使子 Span 能自动继承父级上下文信息。

6.2 传播的 Baggage 键

Baggage Key来源用途
gen_ai.agent.nameStartInvokeAgent 写入标识当前调用链所属 Agent
gen_ai.session.idStartEntry 写入 / Handler 级回退标识会话
gen_ai.user.idStartEntry 写入 / Handler 级回退标识终端用户
gen_ai.frameworkHandler 级回退标识框架名称

6.3 写入时机

1. Handler 级全局注入(injectGlobalBaggage)

在每次 Start*() 调用时执行。将 Handler 构造时设置的 sessionID / userID / framework 作为回退值写入 Baggage,仅当 Baggage 中对应键为空时才写入(不覆盖已有值)。

// injectGlobalBaggage 的优先级逻辑:
// 已有 Baggage 值 > Handler 全局配置值
if bag.Member("gen_ai.session.id").Value() == "" && h.sessionID != "" {
    bag.SetMember("gen_ai.session.id", h.sessionID)
}

2. Entry 入口注入

StartEntry 从 EntryInvocation 的 SessionID / UserID 字段直接写入 Baggage:

if inv.SessionID != "" {
    bag.SetMember("gen_ai.session.id", inv.SessionID)
}
if inv.UserID != "" {
    bag.SetMember("gen_ai.user.id", inv.UserID)
}

3. InvokeAgent Agent 名称注入

StartInvokeAgent 将 inv.AgentName 写入 gen_ai.agent.name,使该 Agent 下游的所有子 Span(如 LLM 调用、工具执行)都能自动关联 Agent 名称。

6.4 读取时机(injectBaggageAttributes)

在大多数 Start*() 方法中,创建 Span 后调用 injectBaggageAttributes(ctx, nil),从 Context 的 Baggage 中提取四个已知键并转为 Span 属性:

bag := baggage.FromContext(ctx)
if m := bag.Member("gen_ai.agent.name"); m.Value() != "" {
    attrs = append(attrs, semconv.GenAIAgentNameKey.String(m.Value()))
}
// ... 类似处理 session.id, user.id, framework

6.5 传播示例

Entry(SessionID="s1", UserID="u1")
  └─ Baggage: {gen_ai.session.id=s1, gen_ai.user.id=u1}
       │
       └─ InvokeAgent(AgentName="assistant")
            └─ Baggage: {..., gen_ai.agent.name=assistant}
                 │
                 ├─ LLM(model="qwen-max")
                 │    Span attrs: {gen_ai.agent.name=assistant,
                 │                 gen_ai.session.id=s1, gen_ai.user.id=u1}
                 │
                 └─ ExecuteTool(name="search")
                      Span attrs: {gen_ai.agent.name=assistant,
                                   gen_ai.session.id=s1, gen_ai.user.id=u1}

7. 多模态异步处理

7.1 架构概览

多模态模块负责将 LLM 请求/响应中的二进制数据(图像、音频、视频)异步外置到存储系统,避免大体积数据占用 Span 属性空间。

在新版本中,Handler 已集成 Processor 的完整生命周期管理。用户只需通过 WithMultimodalUploader 注入自定义 Uploader,Handler 会在内部自动完成 Processor 的创建、启动和关闭。

                    ┌───────────────────────────────────────────────────┐
                    │           Handler (自动管理)                          │
                    │                                                   │
                    │  Start*() ──► 扫描 BlobPart/Base64BlobPart       │
                    │                Base64 解码为字节流                    │
                    │                创建子 Span: gen_ai.multimodal.upload│
                    │                启动后台 goroutine 异步上传         │
                    │                                                   │
                    │  goroutine:                                       │
                    │    PreUploader.Prepare() ──► 生成目标路径          │
                    │    Uploader.Upload() ──► 字节流直传返回 URI   │
                    │                                                   │
                    │  End() ──► 等待上传完成(带超时)                   │
                    │            BlobPart 替换为 UriPart                │
                    │            记录 LLM duration(排除上传等待)       │
                    └───────────────────────────────────────────────────┘

7.2 异步上传流程(三路分支路由模型)

Handler 在每次 Start*() 调用中自动编排异步上传:

① handler.StartLLM(ctx, inv)
     │  扫描 InputMessages/OutputMessages 中的 BlobPart/Base64BlobPart/UriPart
     │  构建 PreUploadItem[] 提交给 Processor
     ▼
② Processor.Execute(ctx, items) → 返回 UploadFuture
     │
     ├─ Phase 1: PreUploader.Prepare(ctx, items)
     │    ├─ 处理内联字节数据 / 文件路径
     │    ├─ 计算 MD5 哈希、生成 TargetPath
     │    └─ 生成 []UploadItem(每个标记 ShouldUpload 和 ResolvedURI)
     │
     └─ Phase 2: 有界并发(semaphore, 默认 maxConcurrency=8)
          ├─ Branch 1: ShouldUpload=true
          │    └─ 获取 semaphore → Uploader.Upload(ctx, item) → 返回 URI
          ├─ Branch 2: ShouldUpload=false + ResolvedURI 非空
          │    └─ 直接使用 ResolvedURI 作为结果
          └─ Branch 3: ShouldUpload=false + 无 ResolvedURI
               └─ 跳过,不产出 result
     │
     ▼
③ 用户调用 span.End()
     │  先捕获 duration = now - startTime(排除上传等待)
     │  等待 UploadFuture.Wait() 完成(带超时,默认 30s)
     │  将 BlobPart 替换为 UriPart(MimeType/Modality 保留)
     │  然后执行 applyFinishAttributes / emitEvent / recordMetrics
     ▼
④ span.End(trace.WithTimestamp(endTime)) → TracerProvider 导出

7.3 容错机制

  • 上传失败降级:单个 part 上传失败不中断其余 part,失败时保留原始 BlobPart 不变
  • 超时降级:End() 等待超时后保留原始内容,记录警告日志,不阻塞 Span 结束
  • Fail 路径跳过等待:Fail(err) 结束时不等待上传完成,上传 goroutine 在后台继续执行

8. 语义约定

8.1 组织方式

semconv 包将属性键分为两个文件:

attributes.go** — 标准 OTel GenAI 属性**(遵循 OpenTelemetry Semantic Conventions):

类别属性键示例
系统gen_ai.system
模型gen_ai.request.model, gen_ai.response.model
操作gen_ai.operation.name
请求参数gen_ai.request.temperature, gen_ai.request.top_p, gen_ai.request.max_tokens, gen_ai.request.seed, gen_ai.request.stop_sequences, gen_ai.request.frequency_penalty, gen_ai.request.presence_penalty, gen_ai.request.top_k, gen_ai.request.choice.count
响应gen_ai.response.id, gen_ai.response.finish_reasons
Token 用量gen_ai.usage.input_tokens, gen_ai.usage.output_tokens
网络server.address, server.port
会话gen_ai.conversation.id, gen_ai.output.type
消息内容gen_ai.input.messages, gen_ai.output.messages, gen_ai.system.instructions

extended_attributes.go** — LoongSuite 扩展属性**:

类别属性键示例
Agentgen_ai.agent.name, gen_ai.agent.id, gen_ai.agent.description
Toolgen_ai.tool.name, gen_ai.tool.call.id, gen_ai.tool.call.arguments, gen_ai.tool.call.result, gen_ai.tool.type, gen_ai.tool.description, gen_ai.tool.definitions
Skillgen_ai.skill.name, gen_ai.skill.id, gen_ai.skill.description, gen_ai.skill.version
检索gen_ai.retrieval.query.text, gen_ai.retrieval.documents
重排gen_ai.rerank.documents.count, gen_ai.rerank.scoring_prompt, gen_ai.rerank.input_documents, gen_ai.rerank.output_documents 等
Embeddinggen_ai.embeddings.dimension.count, gen_ai.request.encoding_formats
缓存 Tokengen_ai.usage.cache_creation.input_tokens, gen_ai.usage.cache_read.input_tokens
延迟gen_ai.response.time_to_first_token, gen_ai.response.reasoning_time, gen_ai.latency.time_in_model_prefill, gen_ai.latency.time_in_model_decode, gen_ai.latency.time_in_model_inference
入口gen_ai.session.id, gen_ai.user.id
ReActgen_ai.react.round, gen_ai.react.finish_reason
Memorygen_ai.memory.operation, gen_ai.memory.user_id, gen_ai.memory.agent_id 等 15+ 属性
Chain/Taskinput.value, output.value, input.mime_type, output.mime_type, gen_ai.task.name
框架gen_ai.framework
Span Kindgen_ai.span.kind(LLM / AGENT / TOOL / EMBEDDING / RETRIEVER / RERANKER / ENTRY / STEP / MEMORY / CHAIN / TASK)

8.2 操作名常量

常量值对应 Span
OperationChat"chat"LLM
OperationEmbedding"embeddings"Embedding
OperationExecuteTool"execute_tool"ExecuteTool
OperationCreateAgent"create_agent"CreateAgent
OperationInvokeAgent"invoke_agent"InvokeAgent
OperationRetrieval"retrieval"Retrieval
OperationRerank"rerank_documents"Rerank
OperationEntry"enter"Entry
OperationReactStep"react"ReactStep
OperationMemory"memory"Memory
OperationChain"chain"Chain
OperationRunTask"run_task"Task

This website has been running for 4 years 4 months 7 days 22 hours 43 minutes 22 seconds

皖ICP备2021007094号 2021-PRESENT © OceanPresent