Typography

学习笔记

Claude Agent SDK学习19 OpenTelemetry

发布于 # Claude Agent SDK

当前 Agent 的问题

第十八章之后,你已经知道单次 query 花了多少钱,但对于真正的生产系统来说,这还远远不够。

你还会关心:

这时,单纯打印 total_cost_usd 已经不够了,你需要标准可观测能力。

本章功能的作用

这一章会引入通过 OpenTelemetry 导出:

和上一章只看单次成本不同,这一章关注的是整条执行链的可观测性。真正进入生产后,你要知道的不只是“花了多少钱”,还包括“卡在哪一步”“哪个工具最慢”“哪个子任务最常失败”。

官方实现里,真正发出 OTel 的不是 SDK 本身,而是它拉起的 Claude Code CLI 子进程。SDK 的职责更像“把环境变量和上下文透传下去”,所以你配置 telemetry 时,本质上是在配置这个子进程的导出行为。

先把信号流向看清楚,会比死记环境变量更容易:

flowchart LR
    A["你的应用"] --> B["Agent SDK"]
    B --> C["Claude Code CLI 子进程"]
    C --> D["生成 traces / metrics / logs"]
    D --> E["OTLP Collector"]
    E --> F["Honeycomb / Datadog / Grafana / Langfuse"]

具体使用方式

第一步:先准备一个可接收 OTLP 的 collector

OpenTelemetry 不是只改几行 SDK 代码就能“自动看见图表”。你需要先有一个本地或远程 collector,能够接收 otlp 协议上报的数据。

这一步的意义在于把教程预期讲清楚。没有 collector,脚本也许仍然能正常跑完,但你看不到任何观测结果;问题不在 SDK,而在于接收端根本不存在。

另外还有一个很容易踩的坑:官方明确不建议在 SDK 场景里使用 console exporter,因为 SDK 正是通过标准输出通道传递消息流。把 telemetry 也打到 stdout,最容易把消息通道和日志通道搅在一起。

第二步:把遥测相关环境变量整理成单独对象

教程里最推荐的写法,是把 CLAUDE_CODE_ENABLE_TELEMETRYOTEL_EXPORTER_OTLP_ENDPOINT 等变量集中放进 otelEnv 这类对象。这样配置项可读性更高,也更容易迁移到生产环境。

第三步:通过 env: { ...process.env, ...otelEnv } 合并传入

这里最重要的不是语法,而是“保留现有环境变量”。如果你直接用一个新对象覆盖环境,常见后果就是把 PATHANTHROPIC_API_KEY 一起弄丢。

第四步:把遥测观察和主流程结果分开验证

脚本本身仍然会像普通 query 一样返回结果;遥测是否成功,要到 collector 或可观测平台里去看。这是两个不同的验证面。

关键概念

1. 遥测不是 SDK 自己直接发的

Claude Agent SDK 底层会拉起 Claude Code CLI 子进程。真正发 OTel 数据的是这个 CLI 子进程,SDK 负责把配置透传给它。

2. TypeScript 里 env 会覆盖继承环境

这是一个非常重要的细节。你通常应该这样写:

env: { ...process.env, ...otelEnv }

否则你可能把 PATHANTHROPIC_API_KEY 等变量一起覆盖掉。

3. 本章示例的可运行前提

要真正看到遥测数据,你需要本机已有一个可接收 OTLP 的 collector。

如果你没有 collector,这个例子更适合拿来理解配置结构,而不是立即看到可视化结果。

可运行示例

假设你本地已经有一个 collector 监听 http://localhost:4318,把下面代码保存为 chapter-19-otel.ts

import { query } from "@anthropic-ai/claude-agent-sdk";

const otelEnv = {
  CLAUDE_CODE_ENABLE_TELEMETRY: "1",
  CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
  OTEL_TRACES_EXPORTER: "otlp",
  OTEL_METRICS_EXPORTER: "otlp",
  OTEL_LOGS_EXPORTER: "otlp",
  OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
  OTEL_EXPORTER_OTLP_ENDPOINT: "http://localhost:4318"
};

async function main() {
  for await (const message of query({
    prompt: "List the main capabilities of the Claude Agent SDK in one paragraph.",
    options: {
      env: { ...process.env, ...otelEnv }
    }
  })) {
    if (message.type === "result") {
      console.log(message.result);
    }
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

运行:

npx tsx chapter-19-otel.ts

示例拆解

第一步:先定义 otelEnv

示例把所有关键遥测环境变量集中到一个对象中,作用是让读者一眼看清“要开哪些开关”和“数据往哪发”。

第二步:在 query() 里通过 options.env 透传配置

这一行演示的是 SDK 场景下的实际接法。Claude Agent SDK 会把这些环境变量传给底层 Claude Code CLI 子进程,由它完成 OTel 上报。

第三步:让 query 本身保持最小化

示例 prompt 很简单,目的是避免把问题复杂度和可观测配置混在一起。只要 collector 正常,哪怕是一个最简单的 query,也足够产生 trace 和日志信号。

第四步:分别检查终端输出和后端遥测

终端里看到的 message.result 只能说明 query 成功;真正的 OTel 验证要去 collector 或可观测平台中确认 interaction、tool、llm_request 等信号是否出现。

运行时你应该观察什么

易错点

本章结束后你应该掌握

本章小结

到这里,你的 agent 已经进入真正可运营阶段:不仅知道结果是什么,还能知道过程发生了什么。