bug: session/trace cost mis-aggregates for multi-agent runs traced via the OpenInference Claude Agent SDK instrumentor

用户在 Langfuse 4.x 版本中,通过 OpenInference Claude Agent SDK 探针( openinference-instrumentation-claude-agent-sdk )将多 Agent 工作流的追踪数据通过 OTel 协议导入 Langfuse。典型场景

bug: session/trace cost mis-aggregates for multi-agent runs traced via the OpenInference Claude Agent SDK instrumentor

bug: session/trace cost mis-aggregates for multi-agent runs traced via the OpenInference Claude Agent SDK instrumentor

快速结论:使用 OpenInference ClaudeAgentSDKInstrumentor 将多 Agent Claude Agent SDK 调用链路导入 Langfuse 时,Session 和 Trace 级别的费用/Token 聚合结果不可靠。优先排查 Session 总费用是否为 $0.00 或与 Trace 总费用不一致,检查父 Agent Span 上的 llm.cost.total 属性是否被 Langfuse 正确解析。

问题场景

用户在 Langfuse 4.x 版本中,通过 OpenInference Claude Agent SDK 探针(openinference-instrumentation-claude-agent-sdk)将多 Agent 工作流的追踪数据通过 OTel 协议导入 Langfuse。典型场景为:query() 方法调度了至少一个子 Agent(通过 Task 工具),导致顶层 ClaudeAgentSDK.query Span 成为整棵调用树的父 Span。

报错原文

Session totalCost = $0.00, while trace.totalCost reports the correct ~$2.42.
For a small one-subagent run, the partial parent-span cost was counted in the session total.

原因分析

存在三个叠加问题:

  • 属性未被识别:Langfuse 的 extractCostDetails() 函数仅检查 langfuse.observation.cost_detailsgen_ai.usage.cost 属性,但 OpenInference 约定的 llm.cost.total 属性未被纳入解析逻辑。尽管 Claude Agent SDK 探针将真实费用写入了顶层 AGENT Span 的 llm.cost.total 中,Langfuse 仍会丢弃该值,回退至根据不完整的 Token 计数 × 定价表推算费用。
  • 费用仅绑定了 GENERATION/EMBEDDING 观察类型:Langfuse 目前只对 GENERATIONEMBEDDING 类型的观察对象填充费用字段。顶层 ClaudeAgentSDK.query Span 映射的是 AGENT 类型,即使 llm.cost.total 被识别,其费用仍会被忽略,除非 Span 类型被重新分类。
  • Session 层面独立求和:Session 的 totalCost 是通过对每个观察对象的 cost_details['total'] 字段做 sumMap 计算得来的,而不是从预先计算好的 Trace 总费用汇总。因此,如果 AGENT 观察对象的 cost_details 为空(由于问题1和2),Session 汇总结果会变为 $0.00,导致 Session 视图与 Trace 视图之间出现差异。

环境排查

  • Langfuse 版本(验证是否 >= 4.x,尤其是 4.5.1)
  • openinference-instrumentation-claude-agent-sdk 版本(如 0.1.6)
  • claude-agent-sdk 版本(如 0.2.93)
  • opentelemetry-sdk 版本(如 1.41.1)
  • Python 版本(如 3.13)
  • 使用的 Langfuse 部署环境(Cloud 或自托管)

解决步骤

  1. 临时规避方案(可优先尝试):在自定义的 SpanProcessor 中,读取 AGENT Span 上的 llm.cost.total 属性,并将其映射为 gen_ai.usage.cost(OTel GenAI 语义约定)写入一个类型为 GENERATION 的 Span,或者写入 langfuse.observation.cost_details 属性(如 {"total": 0.52})。Langfuse 会优先采纳后者。
  2. 等待官方修复:该问题已被确认,最直接的修复方式是修改 Langfuse 核心代码中的 extractCostDetails() 函数(位于 packages/shared/src/server/otel/OtelIngestionProcessor.ts),增加对 llm.cost.total 属性的识别作为第三个回退选项。
  3. 关注 Langfuse 官方路线图:支持在非 GENERATION 类型的观察对象上记录费用是一个更大的架构决策,需要 Langfuse 团队后续处理。

验证方法

重新执行多 Agent 工作流,在 Langfuse 会话列表中检查 Session 级别的 totalCost 是否与 Trace 级别的 totalCost 一致,且均能正确反映调用链路的真实总费用(应与 SDK 报告的 ResultMessage.total_cost_usd 匹配)。

参考来源

langfuse/langfuse #14338

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 9605

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注