Skip to content

1. 架构设计


1. 三层架构

tRPC-Agent Langfuse 三层架构

tRPC-Agent 的 Langfuse 集成分为三层,职责清晰分离:

┌─────────────────────────────────────────────────────────────┐
│  Layer 1: 埋点层(Instrumentation)                          │
│  trpc_agent_sdk/telemetry/_trace.py                         │
│  trpc_agent_sdk/runners.py, agents/, ...                    │
│  → 在 Agent 执行时创建 OTel Span,写入 gen_ai.* 属性         │
└────────────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  Layer 2: 映射层(Transformation)                           │
│  trpc_agent_sdk/server/langfuse/tracing/opentelemetry.py    │
│  _LangfuseMixin / _LangfuseBatchSpanProcessor               │
│  → 按 gen_ai.operation.name 映射为 langfuse.* 属性           │
└────────────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  Layer 3: 导出层(Export)                                   │
│  _LangfuseOTLPExporter → Langfuse /api/public/otel/v1/traces│
│  → Basic Auth,BatchSpanProcessor 批量异步发送               │
└─────────────────────────────────────────────────────────────┘

生活类比

  • 埋点层 = 工人在各工位填原始生产记录
  • 映射层 = 行政把内部字段翻译成 Langfuse 能读懂的面单格式
  • 导出层 = 物流把包裹批量发往 Langfuse 仓库

2. 包与模块关系

trpc-agent(业务仓库)

    ├── trpc-agent-py(pip 依赖,模块名 trpc_agent_sdk)
    │       ├── telemetry/          ← 埋点实现
    │       └── server/langfuse/    ← Langfuse 映射 + 导出

    └── trpc_agent_ecosystem/
            ├── langfuse/           ← re-export SDK(方便 from trpc_agent_ecosystem.langfuse import ...)
            └── trpc_langfuse/      ← tRPC-Python 插件包装

trpc_agent/telemetry/trace.py 仅做 re-export,实际逻辑全在 trpc_agent_sdk 中:

python
from trpc_agent_sdk.telemetry import tracer
from trpc_agent_sdk.telemetry import trace_runner
from trpc_agent_sdk.telemetry import trace_call_llm
# ...

3. OpenTelemetry 与 Langfuse 的角色

组件角色
tracer = trace.get_tracer("trpc.python.agent")全局 Tracer,创建 Span
TracerProviderlangfuse_setup() 注册,挂载 SpanProcessor
SpanProcessorSpan 结束时触发映射与导出
OTLPSpanExporterHTTP 发送到 Langfuse
Langfuse Server解析 OTel Span,渲染为 Trace / Generation / Span UI

Langfuse 通过 特定 Span Attribute 前缀langfuse.trace.*langfuse.observation.*)识别语义,详见 4_attribute_mapping.md


4. 核心类一览

类 / 函数文件职责
LangfuseConfigopentelemetry.py配置 public_key / secret_key / host 等
setup()opentelemetry.py初始化 TracerProvider + Processor + Exporter
_LangfuseMixinopentelemetry.py属性映射、Span 过滤、重命名
_LangfuseBatchSpanProcessoropentelemetry.py批量导出(默认)
_LangfuseSpanProcessoropentelemetry.py同步导出(batch_export=False
_LangfuseOTLPExporteropentelemetry.py带 debug 日志的 OTLP HTTP Exporter
trace_runner()_trace.py向当前 Span 写入业务属性
TrpcLangfusePlugintrpc_langfuse/_langfuse.pytRPC 插件,在 MASTER 进程启动时调用 setup

5. setup() 做了什么

langfuse_setup() 的核心步骤(简化):

python
def setup(config: LangfuseConfig) -> TracerProvider:
    # 1. 解析配置(支持环境变量 LANGFUSE_*)
    # 2. 构造 Basic Auth: base64(public_key:secret_key)
    # 3. endpoint = f"{host}/api/public/otel/v1/traces"
    # 4. exporter = _LangfuseOTLPExporter(endpoint, headers)
    # 5. processor = _LangfuseBatchSpanProcessor(exporter)  # 或 Simple
    # 6. trace_provider = TracerProvider()
    # 7. trace_provider.add_span_processor(processor)
    # 8. trace.set_tracer_provider(trace_provider)
    return trace_provider

必须在 Runner 执行前调用,否则 Span 没有 Exporter,数据不会到达 Langfuse。


6. 设计选择:为何用 OTel 而非 Langfuse SDK

OTel 通路Langfuse SDK 直写
与 Galileo、ZhiyanLLM 等共用同一套埋点每种后端各写一套上报逻辑
框架只关心 Span 属性,后端可插拔框架与 Langfuse API 强耦合
Langfuse 官方支持 OTel 接入对小脚本更直观

tRPC-Agent 同时集成了多种可观测后端(Langfuse、Galileo、ZhiyanLLM),统一走 OTel 埋点 + 不同 SpanProcessor/Exporter 是更可持续的架构。


7. 异步 Generator 与 Span 上下文

框架在 Runner.run_async()BaseAgent.run_async() 中使用 tracer.start_span() 而非 start_as_current_span(),原因是:

在 async generator 中使用 start_as_current_span,取消时可能从另一上下文关闭 generator,触发 detach token 错误。

因此外层 Span(invocationagent_run创建但不激活为 current context;内层 LLM / Tool 使用 start_as_current_span()。这会影响 OTel 严格的 parent-child 链接行为,但 Langfuse 仍能通过 langfuse.session.idlangfuse.user.id 及 Span 时间序做会话级关联。

详见 3_instrumentation.md 第 2 节。