cloudwego / cloudwego/eino

[Proposal] 提供原生 OpenTelemetry handler,按 GenAI 语义约定为 ChatModel / Tool / Retriever / Graph / ADK 上报 span

Open
#1,028 3 comments 1 reaction 0 assignees View on GitHub
Dominant language
Go
Stars
13k
Forks
1.1k
Avg merge
4h 6m
Merged PRs (30d)
41

Description

### Is your feature request related to a problem? Please describe.

要给 eino 应用接入可观测性,目前必须自行实现 `callbacks.Handler`。社区生态里有 Langfuse / cozeloop 两个实现(位于 eino-ext),但它们都是面向各自后端的**专有映射**,无法直接对接通用的 OTel collector / APM。eino-ext#328 提出 OTel 上报,但范围只限 Indexer / Retriever。

OpenTelemetry 已经发布 GenAI Semantic Conventions(`gen_ai.system`、`gen_ai.request.model`、`gen_ai.usage.input_tokens`、`gen_ai.operation.name` 等),主流框架普遍原生支持(Google ADK、Pydantic AI + Logfire、Mastra、LlamaIndex)。eino 缺一个**框架自带、与后端解耦**的 OTel handler。这本质上是「切面扩展」能力的标准化输出。

### Describe the solution you'd like

新增 `callbacks/otel` 包,提供一个标准 handler:

```go
package otel

func NewHandler(tp trace.TracerProvider, mp metric.MeterProvider, opts ...Option) callbacks.Handler
```

行为:

1. 按 `RunInfo.Component`(`model` / `tool` / `retriever` / ...)映射到 `gen_ai.operation.name`(`chat` / `execute_tool` / `embeddings` / ...)。
2. `OnStart` 注入 `gen_ai.request.*`,`OnEnd` 注入 `gen_ai.response.*` 与 `gen_ai.usage.*`。
3. 流式场景在 `OnEndWithStreamOutput` 内累计 token,stream 关闭时一次性落 span。
4. Graph / ADK 节点产生父 span,Component 调用作为子 span,形成完整调用树。
5. Metrics:`gen_ai.client.token.usage`、`gen_ai.client.operation.duration` 直方图。

分期:

- 一期:ChatModel / Tool / Embedding / Retriever / Indexer 的 span + 标准 attribute
- 二期:Graph 节点与 ADK Agent 的父子 span 层级 + AgentEvent 拓扑
- 三期:AgenticMessage(Responses API)的 reasoning / tool 双 span 表示

### Describe alternatives you've considered

- **继续用 Langfuse / cozeloop handler**:绑定特定后端,无法对接已有的 OTel collector / Jaeger / Tempo / 各类 APM。
- **由用户各自实现 OTel handler**:每个团队重复造轮子,且很难统一遵循 GenAI semconv,trace 不可跨项目复用。
- **只做 metrics 不做 trace**:丢失调用树,无法定位多 Agent / 多节点链路。

### Additional context

- OTel GenAI Semantic Conventions: https://opentelemetry.io/docs/specs/semconv/gen-ai/
- 现有实现可参考:eino-ext `callbacks/langfuse`。
- 关联:eino-ext#328(仅 indexer/retriever,范围窄)。
- 放置位置(主仓库 `callbacks/otel` vs eino-ext)可讨论;倾向主仓库,因为 OTel 是行业标准、且不引入重后端依赖。

愿意在 maintainer 认可方向后承担实现。

Contributor guide

Open the contributing guide

Research direction

Start by reading the existing eino-ext callbacks/langfuse implementation and the OpenTelemetry GenAI Semantic Conventions. Confirm with maintainers whether the handler belongs in callbacks/otel or eino-ext, then scope the first phase to ChatModel, Tool, Embedding, Retriever, and Indexer spans with standard attributes. Done means the agreed first-phase handler reports those spans and metrics through provider interfaces without backend-specific mapping.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
observability-sre
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.