读书笔记 · 约 16,497 字 · 预计推送 2 部分

LLM 应用可观测性:token、延迟、成本、质量与 trace 如何统一记录 —— Langfuse / Phoenix / OpenTelemetry 主题精读 未开始

目录

题记与说明

条目 ID:knowledge-p5j2vm4l-4 | 阅读日期:2026-10-31 | 形式:主题研究(3-5 个来源群,在线检索并逐页精读)
说明:本笔记全部结论基于本次会话中实际抓取并阅读的 8 个官方文档页 / 仓库原文 / 第三方对比文,来源链接与原文引述均随文标注。未实际部署运行 Langfuse 或 Phoenix 实例,凡属文档声称而非实测之处均已注明。

一、核心问题(它在解决什么问题)

LLM 应用的五类遥测数据天然分散:token 用量在模型 API 响应里、延迟在客户端计时里、成本需要"token × 定价表"另行计算、质量(正确性/幻觉/相关性)根本不是运行时自动产生的信号、而请求链路(trace)又分散在各个 SDK 的私有格式里。主题要回答的问题是:能否用一套统一的数据模型/语义约定,把五类信号记录在同一个管道中,既可聚合分析,又不被单一厂商锁定?

三家对象在答案中处于三个不同层次:

层次对象角色
规范层OpenTelemetry GenAI Semantic Conventions(+ Arize 发起的 OpenInference)定义"字段叫什么、放哪",不是产品
管道层OTLP / OTel Collector统一传输协议,一次插桩多后端投递
应用层Langfuse、Phoenix存储、UI、定价表驱动的成本计算、质量评估对象

二、来源清单(本次实际精读)

  1. OpenTelemetry GenAI 语义约定仓库(规范层主来源)
    • 仓库:https://github.com/open-telemetry/semantic-conventions-genai
    • 本次全文下载精读:docs/gen-ai/gen-ai-spans.md(68,689 字节)、docs/gen-ai/gen-ai-metrics.md(65,149 字节)、docs/gen-ai/gen-ai-token-metrics.md、docs/gen-ai/client-inference.md、README
    • 佐证:opentelemetry.io 上的旧页面(/docs/specs/semconv/gen-ai/、gen-ai-spans/、gen-ai-metrics/)已全部返回"Moved"占位页,原文引用:"GenAI semantic conventions have moved to the OpenTelemetry GenAI semantic conventions repository. This page has moved and is no longer maintained in this repository." —— 说明约定正在迁移期。
  2. Langfuse — Observability Data Model:https://langfuse.com/docs/observability/data-model
  3. Langfuse — OpenTelemetry (OTEL) for LLM Observability(含完整属性映射表):https://langfuse.com/integrations/native/opentelemetry
  4. Langfuse — Scores / Evals:https://langfuse.com/docs/evaluation/scores/overview.md 与 https://langfuse.com/docs/scores/overview
  5. Arize Phoenix — Setup OTEL:https://arize.com/docs/phoenix/tracing/how-to-tracing/setup-tracing/setup-using-phoenix-otel
  6. Arize Phoenix — Cost Tracking:https://arize.com/docs/phoenix/tracing/how-to-tracing/cost-tracking
  7. Arize Phoenix — Evaluation:https://arize.com/docs/phoenix/evaluation
  8. Kosmoy — "Langfuse vs Arize (2026): LLM Observability Compared"(第三方对比,注意其竞争厂商立场):https://www.kosmoy.com/resources/blog/langfuse-vs-arize

三、逐家精读

3.1 OpenTelemetry GenAI:规范层管住 token 与延迟,不管成本与质量

Span 属性(trace 信号)。gen-ai-spans.md 的属性表中,唯一 Required 级别的是:

gen_ai.operation.name — Required — string — The name of the operation being performed.(示例值:chat、generate_content、text_completion)

gen_ai.provider.name、gen_ai.request.model(如 gpt-4)均为 Conditionally Required。工具调用有独立 span 形态:gen_ai.operation.name SHOULD be execute_tool,gen_ai.tool.name 为 Required。所有 gen_ai.* 属性都带 Development 稳定性徽章——整族约定仍是开发态。

Token 用量属性(client-inference.md):

gen_ai.usage.input_tokens — Recommended — int — The number of input tokens reported by the provider for this inference operation.
gen_ai.usage.output_tokens — Recommended — int — The number of output tokens reported by the provider...

并向下细分到缓存与模态:gen_ai.usage.cache_read.input_tokens、gen_ai.usage.cache_write.input_tokens、gen_ai.usage.reasoning.output_tokens、gen_ai.usage.audio.*、gen_ai.usage.image.*、gen_ai.usage.text.*,且规定嵌套值 SHOULD 包含进总量(如 audio/input 计入 gen_ai.usage.input_tokens)。

延迟与用量指标(metrics 信号)。gen-ai-metrics.md 定义:

指标类型单位含义
gen_ai.client.operation.durationHistogramsGenAI operation duration
gen_ai.server.request.durationHistogramstime-to-last byte or last output token
gen_ai.server.time_to_first_tokenHistogramsTime to generate first token(TTFT)
gen_ai.server.time_per_output_tokenHistograms首 token 之后每输出 token 耗时(TPOT/解码速度)
gen_ai.invoke_agent.duration / gen_ai.execute_tool.durationHistogramsagent 端到端 / 工具单次执行耗时
gen_ai.client.inference.usage.input_tokens / output_tokens(见 gen-ai-token-metrics.md)Counter{token}按 gen_ai.token.modality 细分的用量计数器

关键空白:没有 cost,没有 quality。 我对四份约定全文做了 grep -i cost 检索:client-inference.md 零命中;gen-ai-spans.md 唯一一处命中是无关句子("storage costs, regulatory requirements...",指记忆存储的成本考量)。成本属性不在标准内;质量/评分信号同样完全缺位。这就是"统一记录"在规范层止步的地方——token、延迟、trace 三类有标准,成本、质量两类由应用层补。

3.2 Langfuse:数据模型四对象 + 可作 OTel Backend 的应用层

四对象数据模型(data-model 页原文):

"Langfuse organizes an application's data into three core concepts: observations, traces, and sessions."

加上 Score 即四类对象。Observation 是"LLM calls, tool calls, retrieval steps",可嵌套;trace 是"一个请求/操作"的全部 observation 的逻辑分组;session 再聚合多轮 trace。存储上的关键设计(原文):

"Conceptually, Langfuse stores one observations table, and each row holds the observation-level data plus a copy of the trace-level attributes. This keeps queries and aggregations fast."

Built on OpenTelemetry(原文):

"Langfuse is built on OpenTelemetry... This means you're not locked into using only Langfuse-specific SDKs. You can also send your traces to multiple destinations at once, like Langfuse for LLM observability and Datadog for infrastructure monitoring."

具体接入形态:Langfuse 可作为 OTel backend 接收 OTLP:

"Langfuse can receive traces on the /api/public/otel (OTLP) endpoint."

约束与坑(原文摘录):

属性映射:gen_ai.* 如何变成 token/成本记录(OTEL 集成页映射表摘译):

Langfuse 字段映射自 OTel 属性
modelgen_ai.request.model / gen_ai.response.model / llm.model_name
usage(token)langfuse.observation.usage_details / gen_ai.usage.* / llm.token_count.*
cost(USD)langfuse.observation.cost_details / gen_ai.usage.cost(写入 total 键)
input/outputlangfuse.observation.input / gen_ai.prompt / gen_ai.completion / OpenInference 的 input.value/output.value

同时明确承认规范在漂移(原文):

"As the Semantic Conventions for GenAI attributes on traces are still evolving, Langfuse maps the received OTel traces to the Langfuse data model and supports additional attributes that are popular in the OTel GenAI ecosystem."

以及命名空间优先级:"Langfuse uses attributes within the langfuse.* namespace to map OpenTelemetry span attributes directly to the Langfuse data model. These specific attributes always take precedence over the generic OpenTelemetry conventions."

聚合的前提:trace 级属性必须传播到每个 span。这是本次精读中最有工程价值的发现之一(原文):

"If you want to filter and aggregate by userId, sessionId, metadata, version, release, or tags, you need to propagate these trace-level attributes to every span in the trace."
"Langfuse filters and aggregations increasingly operate across individual observations rather than only at the trace level."

推荐实现是 OTel Baggage + BaggageSpanProcessor,并附安全警告:"OpenTelemetry baggage is propagated across service boundaries and to third-party APIs. Do not include sensitive information... in baggage."

未映射的 OTel 属性会落入 metadata.attributes catch-all 且"not directly filterable",只有 langfuse.trace.metadata.* / langfuse.observation.metadata.* 前缀的键才进入一级可过滤 metadata。

质量:Score 对象。Langfuse 对 quality 的统一记录方式是独立于 trace/observation 的第四类对象(scores overview 原文):

"Every score has a name (like "correctness" or "helpfulness"), a value, and a data type. Scores also support an optional comment..."

四种数据类型:NUMERIC(连续判断)、CATEGORICAL(预定义类别,如 "correct"、"partially correct")、BOOLEAN(0/1,幻觉检测/格式校验)、TEXT。评分来源覆盖四条路:LLM-as-a-Judge 自动评估(可跑在线生产 trace 或实验)、人工标注(Annotation Queues)、终端用户反馈、代码评估器。选型口诀(原文):"if you already know the category at tracing time... use a tag. If you need to classify or evaluate traces later, use a score."

许可证与部署(第三方来源):Kosmoy 对比文:"Langfuse's core is MIT-licensed (OSI-approved) and unlimited when self-hosted, running the exact same codebase as its cloud with no scalability limits." 另提到 2026 年 1 月被 ClickHouse 收购但承诺许可不变。

3.3 Phoenix:OTel 原生优先 + 定价表驱动的成本引擎

接入即 OTel(Setup OTEL 页原文):

"The Phoenix OTEL SDK provides a lightweight wrapper around OpenTelemetry with sensible defaults for Phoenix."

register() 一个函数完成配置,auto_instrument=True 自动发现并激活环境中已安装的全部 OpenInference instrumentor("Phoenix automatically discovers and activates all OpenInference instrumentor packages installed in your Python environment—no additional code required")。协议支持 "grpc" 或 "http/protobuf" 两种(Python 端比 Langfuse 多一种 gRPC 选择)。Python 端默认 batch: False,TS 端默认 batch: true,官方同样警告进程退出前要 flush:"With batch: true, call shutdown() to explicitly flush before exit."

SDK 与服务器版本解耦(原文):"Any recent SDK version works with any Phoenix server version — you don't need to upgrade the server when you upgrade the SDK... The SDK sends traces over OTLP using OpenInference semantic conventions, both of which are backward compatible."

成本:Phoenix 侧自动计算(Cost Tracking 页原文):

"Phoenix allows you to track token-based costs for LLM runs automatically. The costs are calculated from token counts and model pricing data, then rolled up to the trace and project level for comprehensive cost analysis."

要点:

质量:评估结果回写为 span annotation(Evaluation 页原文):

"log evaluation results back to Phoenix as span annotations. Phoenix uses span identifiers to associate eval outputs with the correct execution."

即评估对象不是独立实体,而是挂在 span 上的 annotation。内置 judge 模板覆盖"relevance, correctness, faithfulness, summarization quality, and toxicity";SDK 侧批量跑(async_evaluate_dataframe(..., concurrency=10))后回写,评估结果在 UI 中与 trace 并排出现。

许可证(第三方来源,Kosmoy 原文):"Arize's Phoenix is under the Elastic License 2.0, which is not OSI-approved open source and restricts offering it as a managed service."

3.4 三家对照总表(本次精读综合)

维度OpenTelemetry GenAILangfusePhoenix
本质语义约定规范(Development 态)应用层平台,MIT 开源,可自托管应用层平台,Elastic License 2.0
tracespan/事件/实体模型(gen_ai.*)Session→Trace→Observation;根 span 定 traceProject→Trace→Span;OpenInference span kinds
tokengen_ai.usage.*(含 cache/reasoning/模态)+ Counter 指标映射 gen_ai.usage.* 至 observation.usageOpenInference token 属性;成本据此自动算
延迟Histogram:operation.duration、TTFT、time_per_output_tokenspan 起止时间戳 + completion_start_time(TTFT 映射)同 OTel 信号,UI 聚合
成本无标准属性(grep 证实)observation.cost;接受 gen_ai.usage.cost 扩展内置定价表自动计算,支持 regex 自定义与 token 类型细分,五级聚合
质量无信号Score 对象(NUMERIC/CATEGORICAL/BOOLEAN/TEXT),四来源span annotation 形式回写评估结果,内置 judge 模板
传输OTLP(gRPC/HTTP 均可,视实现)仅 OTLP/HTTP(不支持 gRPC)gRPC 或 OTLP/http/protobuf
锁定规避规范本身即反锁定"built on OpenTelemetry",可多后端同时投递"lightweight wrapper around OpenTelemetry"

四、主要主张(综合论点)

  1. "统一记录"在 2026 年的现实形态是分层而非单一标准:OTel GenAI 约定统一了 trace/token/延迟三类信号的命名与传输;成本与质量在规范层是空白,由 Langfuse(Score 对象 + cost 字段)与 Phoenix(定价表引擎 + span annotation)在应用层各自补齐。选型时真正的差异点在应用层的成本引擎与质量评估设计,而非插桩层。
  2. 插桩写在 OTel 层、后端可换:两家都原生吃 OTLP。Langfuse 明说可同时发 Langfuse 与 Datadog;Phoenix 的 SDK 只是"wrapper with sensible defaults"。把 instrumentor 选在 OTel GenAI / OpenInference 一侧,后端迁移成本最低。
  3. 聚合靠传播,不靠存储:无论哪家,user/session/tags 这类切片维度都必须复制到每个 span(Langfuse 用 BaggageSpanProcessor,Phoenix 用 using_session/using_user 等 context manager)才能在观测表中聚合。"trace 是聚合键"是五元组统一的技术前提。
  4. 规范仍在漂移,要有兼容层心态:OTel GenAI 全族属性为 Development 态且刚迁至独立仓库;Langfuse 文档明确承认"still evolving"并保留 langfuse.* 命名空间作为优先出口。生产系统应预期属性名变化,把映射集中在一处。

五、方法(本次如何研究)

六、数据与证据(关键引述索引)

七、结论与效果

八、局限

  1. 纯文档精读,无实测:本次未部署 Langfuse/Phoenix 验证映射表行为、成本计算精度与 UI 聚合;所有"支持/不支持"均为文档声称。
  2. OTel 约定为 Development 快照:属性表可能随后续版本变动;本文引用的是抓取当日(2026-10-31)仓库 main 分支状态。
  3. 对比文的厂商立场:Kosmoy 是竞争厂商,其 81/100、74/100 打分与 air-gap "undocumented" 等说法是编辑性判断,仅取其许可证与部署事实性内容并已标注。
  4. 版本时效:Langfuse v4 的 ingestion header 与 trace input/output 弃用等细节随版本变化快;Phoenix 文档站为 Mintlify 动态渲染,个别代码块(token 属性的具体键名)在抓取中未展开,笔记中该处以 OpenInference 语义约定的文字描述为准。
  5. 未覆盖 LangSmith、OpenLLMetry/OpenLIT 插桩细节、W&B Weave 等同类,仅在 Langfuse 文档的插桩库对比表中提及。

九、对我的启发

  1. 自建 agent/LLM 工作流时直接用 gen_ai.* 属性打 span(gen_ai.operation.name、gen_ai.request.model、gen_ai.usage.input_tokens/output_tokens),后端今天接 Langfuse、明天换 Phoenix 或自建 ClickHouse 都不用改插桩——这是本次最可复用的决策。
  2. 成本记录自己维护一张"定价表 + token 计数"的最小实现(参考 Phoenix 的 regex 匹配 + 每 1M 单价 + 生效日期模型),因为规范层短期不会有 cost 标准。
  3. 质量信号挂 ID 不入 trace 流:eval 结果以 score/annotation 关联 trace_id/span_id 回写,保持运行时遥测与评估数据可分可合——与我此前精读的 RAGAS/TruLens 评估方法论(2026-10-29 条目)可以接上:评估器输出即 Score。
  4. trace_id/session_id/user_id 是五元组的聚合键,任何插桩先解决"这三个 ID 传播到每个 span",再做别的(Langfuse 的 BaggageSpanProcessor 是现成模式)。
  5. Windows 本地场景(延续本库本地推理/语音条目的视角):Phoenix 支持 uvx arize-phoenix serve 一条命令本地起服务(默认 localhost:6006,无 API key),Langfuse v3.22+ 也有 /api/public/otel 本地端点——两者都可作为个人实验的可观测性后端,OTLP/HTTP 一条管道通吃。

十、本次学到什么


附:原文引述备查(未在正文展开)

← 资料库今日播报 →