跳转到内容

Anthropic claude-agent-sdk — egress proxy 配方

Anthropic 的 claude-agent-sdk 是 Anthropic 唯一官方发布的 agent SDK。它有点特别: 这个 SDK 以子进程方式拉起 claude CLI 二进制 来跑 agent 循环,所以每个 messages 请求都是从 CLI 进程内部发出的 HTTPS 调用, 离开的是 Python(或 Node)进程,而不是 SDK。SpendGuard 管控它的方式 和管控任何遵守 HTTPS 的 CLI 一样:一个跑在 localhost:9000 的正向 代理,加一张 CLI 接受的受信任根 CA。 D02 的 spendguard install 步骤这两件事都帮你做好。

SDK 的 PreToolUse 钩子是 tool 粒度 的:它在 agent 决定使用某个 tool(BashEditReadWebFetch)时运行。它 看不到 底层的 POST /v1/messages 请求。等到下一个 tool 的 PreToolUse 触发时,LLM 早就调过了,token 也已经计费了,模型也已经 产出了响应。

所以在 SDK 这一层根本没有什么可包的,包了也拦不住真正花出去的钱。真正 管用的闸门在下一层——CLI 遵守 HTTPS_PROXYNODE_EXTRA_CA_CERTS 的那个 HTTPS 边界。这个边界正是 D02 已经配好的,也正是 SpendGuard egress proxy 已经在终结的那一个。Anthropic 流量走代理里现成的 per-provider 路由(routing.rs)。 D30 就是把这些串起来的配方。

your code (Python or TypeScript)
uses claude_agent_sdk / @anthropic-ai/claude-agent-sdk
│ subprocess
`claude` CLI binary (Node)
│ HTTPS_PROXY=http://localhost:9000
│ NODE_EXTRA_CA_CERTS=<spendguard CA>
SpendGuard egress proxy ── route: api.anthropic.com/v1/messages
│ PRE: sidecar RequestDecision → CONTINUE / STOP
│ ↳ forward to api.anthropic.com
│ POST: sidecar ConfirmPublishOutcome (commit_estimated)
audit_outbox: one RESERVE_RESPONSE row + one matching COMMIT_OUTCOME row

CLI 是 HTTPS 客户端。egress proxy 是闸门。audit_outbox 里的那几行把审计链闭合。

  • 一个跑在 http://localhost:9000 的 SpendGuard egress proxy。 Quickstart 能在 5 分钟内把整套栈拉起来。
  • 这台主机上 spendguard install 已经干净跑通(D02)。CLI 安装器会把 SpendGuard 根 CA 写进操作系统信任库,并把 HTTPS_PROXY + NODE_EXTRA_CA_CERTS 写进你的 shell rc。
  • ANTHROPIC_API_KEY 里有一个 Anthropic API key。这个配方只支持 BYOK——Claude Code Pro / Max 订阅计量 是另一套独立集成。
  • Python 端:Python 3.11 或更新。TypeScript 端:Node.js 20 或更新。
Terminal window
pip install claude-agent-sdk
# spendguard install already wired your shell. Verify:
echo "$HTTPS_PROXY" # → http://localhost:9000
echo "$NODE_EXTRA_CA_CERTS" # → /etc/ssl/spendguard/ca.crt (or your platform path)
examples/claude-agent-sdk/example.py
import anyio
from claude_agent_sdk import query, ClaudeAgentOptions
PROMPT = "List two ways to reverse a string in Python in under 30 words."
async def main() -> None:
async for msg in query(
prompt=PROMPT,
options=ClaudeAgentOptions(model="claude-sonnet-4-5", max_turns=1),
):
print(msg)
if __name__ == "__main__":
anyio.run(main)

跑起来。CLI 子进程从你的 shell 环境继承 HTTPS_PROXY,所以 messages 调用会流经 SpendGuard egress proxy。代理会在 audit_outbox 里写出一行 RESERVE_RESPONSE 加一行匹配的 COMMIT_OUTCOME——用下面 确认 SpendGuard 抓到了这次调用 里的 SQL 来对账。

Terminal window
npm install @anthropic-ai/claude-agent-sdk
echo "$HTTPS_PROXY" # → http://localhost:9000
echo "$NODE_EXTRA_CA_CERTS" # → /etc/ssl/spendguard/ca.crt (or your platform path)
example.mjs
import { query } from "@anthropic-ai/claude-agent-sdk";
const PROMPT = "List two ways to reverse a string in Python in under 30 words.";
for await (const msg of query({
prompt: PROMPT,
options: { model: "claude-sonnet-4-5", maxTurns: 1 },
})) {
console.log(msg);
}

TypeScript SDK 跟 Python SDK 形状一样,底下用的也是同一个 claude CLI 二进制。两边都遵守 HTTPS_PROXYNODE_EXTRA_CA_CERTS,产出的审计链行也一样。

确认 SpendGuard 抓到了这次调用

一次 query(...) 跑完之后,SpendGuard 已经往 audit_outbox 写了两行——PRE 时刻一行 RESERVE_RESPONSE,POST 时刻一行 COMMIT_OUTCOME,共享同一个 request_id。直接查它们来确认:

SELECT
payload->>'event_type' AS event_type,
payload->>'provider' AS provider,
payload->>'model' AS model,
payload->>'committed_input_tokens' AS input_tokens,
payload->>'committed_output_tokens' AS output_tokens,
payload->>'request_id' AS request_id
FROM audit_outbox
WHERE payload->>'provider' = 'anthropic'
AND payload->>'model' LIKE 'claude-%'
ORDER BY created_at DESC
LIMIT 2;

这次调用你应该正好看到两行:一行 event_type = RESERVE_RESPONSE (调用前的预留)和一行 event_type = COMMIT_OUTCOME(调用后的 提交)——两行带同一个 request_id,而且提交那行 committed_input_tokens > 0committed_output_tokens > 0。 SpendGuard 仪表盘在 Operations → Dashboard 下也会呈现这同样的几行。

PreToolUse 是什么——以及不是什么

PreToolUse 是 agent 即将 调用某个 tool(BashEditReadWebFetch、自定义 MCP tool)时触发的 SDK 钩子。它 不会 在底层 LLM 调用上触发。这个区别对预算控制很关键:

  • 一个拒绝昂贵 tool 的 PreToolUse 钩子是 tool 策略 闸门。它能 拦住 Bash("rm -rf /")WebFetch("https://...")。这地方很适合 编码”这个 agent 不许跑代码”或”这个 agent 不许调这个域名”。
  • 一个想靠它来强制预算的 PreToolUse 钩子就是 自欺欺人。等到下一个 tool 触发时,LLM 早就响应过了。token 也已经计费了。这个钩子没法把那次 产生 tool-use 决定的 messages 交互倒带回去。

要做 LLM 粒度的管控——也就是 SpendGuard 的调用前预留真正省下花费的那个 地方——跑在 api.anthropic.com/v1/messages 上的 egress proxy 才是 唯一正确的切入点。本页这个配方默认这个切入点已经通过 D02 就位了。

  • CLI 子进程报 ssl.SSLCertVerificationError SpendGuard 根 CA 不在 CLI 读取的那个信任库里。重跑 spendguard install;Linux 上确认 update-ca-certificates 跑过; macOS 上确认证书通过 security add-trusted-cert 加进了系统钥匙串。 Node 端确认 NODE_EXTRA_CA_CERTS 指向一个当前用户能读的 PEM 文件。
  • 调用直接打到了 api.anthropic.com——没有审计行。 你当前的 shell 没有吃到 spendguard install 写的那段 rc snippet。开一个新 终端,或者 source ~/.zshrc / ~/.bashrc,再重新检查 echo "$HTTPS_PROXY"。如果你是从 IDE 里启动 SDK 的,重启 IDE 让它 继承更新后的环境。
  • HTTPS_PROXY 设了但 NODE_EXTRA_CA_CERTS 是空的。 CLI 基于 Node,会拿代理的叶子证书去校验 Node 自带的 CA 列表。没有 NODE_EXTRA_CA_CERTS,连接就会以 unable to verify the first certificate 失败。两个环境变量都得设;spendguard install 两个都设。
  • PreToolUse 拦住了某个 tool,但 LLM 调用还是发生了。 这是设计如此——见 PreToolUse 是什么——以及不是什么。 把预算上限挪到 egress proxy(就是这个配方)。