返回 AIPA 笔记
AIPA Day 59

Claude Agent SDK 对比实现 — 谁拥有 loop,谁拥有 context

Claude Agent SDK 对比实现 — 谁拥有 loop,谁拥有 context

2026-08-12
claude-agent-sdkharnesscompactionframework-bench

日期: 2026-08-12 阶段: Phase 2 - AI-native 参考架构 标签: #claude-agent-sdk #harness #compaction #framework-bench

核心问题

数据点#2:把对账异常调查 agent 用 Claude Agent SDK(@anthropic-ai/claude-agent-sdk,TS)重写一遍,同任务、同 golden、同 eval(Day 57 三固定)。表面又是「照文档抄」,但 Claude Agent SDK 与 AI SDK 6 是两种世界观的框架,对比时有一个会让成本数字直接不可比的陷阱:

  • AI SDK 6:你拥有 loop——generateText/ToolLoopAgent 把控制权交给你,你数 step、你算 token、你管 context,框架不替你压缩历史。
  • Claude Agent SDK:harness 拥有 loop——query() 跑的是驱动 Claude Code 的同一套 harness,它自动做 compaction、默认开 prompt caching,并直接吐出 total_cost_usdcache_read_input_tokens

陷阱在最后一句:Claude Agent SDK 的成本已经含了 prompt-caching 折扣,而 AI SDK 6 我用本仓 estimateCost(不建模缓存)算出来的成本不含折扣。若直接把两个数字并排,等于拿「打折价」对比「原价」——Claude Agent SDK 会因为缓存看着更便宜,但这是口径差异不是框架优势。今天必须把这个口径对齐,否则数据点#2 会污染 Day 60 的整张 trade-off 矩阵。

关键内容

A. query() 的世界观:async-iterable 消息流 + harness 自治

Claude Agent SDK(2025-09 从 Claude Code SDK 改名)的核心是 query()(官方 TS reference):

function query({ prompt, options }: {
  prompt: string | AsyncIterable<SDKUserMessage>;
  options?: Options;
}): Query;   // Query extends AsyncGenerator<SDKMessage, void>

它返回一个异步生成器,你 for await 它,harness 内部跑「模型→工具→结果→继续」的 agentic loop,把过程作为 SDKMessage 联合类型流式吐回(SDKAssistantMessage / SDKResultMessage / SDKCompactBoundaryMessage / …)。对账 agent 实现:

import { query, createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk'
import { z } from 'zod'

// 五个对账工具注册为「进程内 SDK MCP server」
const reconServer = createSdkMcpServer({
  name: 'recon', version: '1.0.0',
  tools: [
    tool('fetchLedger', '取内部流水', { period: z.string() },
      async ({ period }) => ({ content: [{ type: 'text', text: JSON.stringify(loadLedger(period)) }] }),
      { annotations: { readOnlyHint: true } }),
    tool('finalReport', '提交异常清单', { breaks: z.array(breakSchema) },
      async ({ breaks }) => ({ content: [{ type: 'text', text: validateAndEcho(breaks) }] })),
    // fetchStatement / matchByAmountAndDate / classifyBreak 同理
  ],
})

const q = query({
  prompt: caseToPrompt(amlCase),
  options: {
    model: 'claude-sonnet-4-6',                 // 基准锁定,同 AI SDK 6
    systemPrompt: RECON_SYSTEM,                   // 共享字符串(注意 B 节 preset 坑)
    mcpServers: { recon: reconServer },           // 工具走 MCP 注册
    allowedTools: ['mcp__recon__fetchLedger', /* … */ 'mcp__recon__finalReport'],
    maxTurns: 8,                                  // 对齐 stopWhen=stepCountIs(8)
    permissionMode: 'dontAsk',                    // bench 不需人审,禁止挂起
  },
})

let result: SDKResultMessage | undefined
for await (const m of q) { if (m.type === 'result') result = m }
// result.total_cost_usd / result.usage / result.num_turns / result.modelUsage

与 AI SDK 6 的结构对比:AI SDK 6 工具是裸 Record<string, Tool> 直接喂 agent;Claude Agent SDK 工具必须包成进程内 MCP servercreateSdkMcpServer),工具名带 mcp__<server>__<tool> 前缀,且要进 allowedTools 白名单才会自动执行。这个差异本身是个数据点:Claude Agent SDK 把「工具=MCP」当一等公民(为 W8 的 MCP server 复用埋伏笔),代价是更重的注册仪式。

B. systemPrompt 的 preset 陷阱:三固定可能被悄悄破坏

这是个隐蔽到能让整个对比失效的坑。Claude Agent SDK 的 systemPrompt 有两种形态:

systemPrompt?: string | {
  type: 'preset'; preset: 'claude_code';
  append?: string; excludeDynamicSections?: boolean;
}

若用 { type: 'preset', preset: 'claude_code', append: RECON_SYSTEM },harness 会把驱动 Claude Code 的完整系统提示(数千 token 的工具使用规范、文件操作纪律等)塞在你的 RECON_SYSTEM 前面。

反直觉洞察①(Claude Agent SDK 的「默认更聪明」恰恰会破坏三固定):Claude Agent SDK 卖点是「defaults that match production」——内置工具、自动 compaction、Claude Code 级系统提示开箱即用。但在框架横评里,这些「贴心默认」全是污染源:preset 系统提示让 Claude Agent SDK 比 AI SDK 6 多吃几千 token 的 input(成本虚高)又可能因为更强的工具纪律而质量虚高——你测的不再是「框架的 tool-loop 调度」,而是「Anthropic 的系统提示工程」。基准必须用纯字符串 systemPrompt: RECON_SYSTEM(不走 preset),并显式 settingSources: [] 切断 user/project 配置注入,才能让两框架吃到逐字符相同的系统提示。三固定在这个框架上要主动「关掉聪明」。

同理 permissionMode 必须设 dontAsk(否则工具调用可能挂起等人审,污染 latency)、maxTurns: 8(对齐 stopWhen)。框架的默认越多,控制变量要关的开关越多

C. 成本口径对齐:prompt caching 让两框架的「便宜」不可直接比

SDKResultMessage 直接给出成本与缓存明细:

total_cost_usd: number          // harness 估算,含 prompt caching 折扣
usage: { input_tokens, output_tokens,
         cache_creation_input_tokens?, cache_read_input_tokens? }
modelUsage: { [model]: ModelUsage }   // 按模型拆分
num_turns: number               // ≈ AI SDK 6 的 steps

关键差异——缓存命中的 token 走 cache_read_input_tokens,计费远低于全价 input(Anthropic 缓存读约为原价 1/10 量级)。两框架成本口径对照:

维度AI SDK 6Claude Agent SDK可比性问题
成本来源我用 estimateCost(totalUsage)框架直接给 total_cost_usd口径不同源
prompt caching本仓 estimateCost 不建模缓存默认开,total_cost_usd 已扣折扣Claude SDK 看着更便宜=缓存非框架优势
input tokentotalUsage.inputTokens(全价)input_tokens + cache_read_input_tokens(拆分)直接比 input 数会错配
步数steps.lengthnum_turns语义近似但非严格相等
成本含义「无缓存原价」「有缓存折后价」并排=原价 vs 折扣价

反直觉洞察②(框架对比里,自动 prompt caching 是「成本优势」还是「口径噪声」取决于你问什么):若问「生产部署谁更省钱」,Claude Agent SDK 的自动缓存是真实优势(同样的对账历史重复命中,省 input);若问「框架的 tool-loop 调度效率谁高」,缓存折扣是必须剥离的噪声(它来自 Anthropic 计费策略,不是 loop 调度)。本基准两个口径都要报:(1) 裸 token 口径——用两框架的原始 input_tokens/output_tokens(Claude SDK 把 cache_read 也按全价折算回去)喂同一个 estimateCost,比「loop 调度产生多少 token」;(2) 实付口径——AI SDK 6 用 estimateCost、Claude SDK 用 total_cost_usd,比「真实部署账单」。两个口径分列,不混报——混报就是 Day 60 trade-off 矩阵的第一个雷。

D. 谁拥有 context:compaction 自治 vs 手动管理

最深的架构差异在 context 管理。Claude Agent SDK 在历史逼近上下文窗口时自动 compaction,并发 SDKCompactBoundaryMessage{ boundary_index, reason, tokens_saved } 信号;AI SDK 6 不替你压——历史全量回灌(Day 58 洞察①的二次增长在这里无人兜底,超窗就报 prompt_too_long)。

对 8 步的对账 agent,历史短,大概率不触发 compaction——但这恰恰暴露一个对比纪律:短任务上 compaction 不发力,会让 Claude Agent SDK 的 context 优势在本基准上「看不见」。这不是缺陷,是基准的边界:本基准(8 步、短历史)测不出 compaction 价值,要测它得换长任务(如 P3 的多步 AML 调查)。诚实地说:数据点#2 在 context 维度上对 Claude Agent SDK 不公平地保守——它最强的能力没被这个短任务触发。这条限定语必须进 Day 60 矩阵的脚注。

设计要点/决策表

要点决策理由
系统提示纯字符串 RECON_SYSTEM不走 presetpreset 注入 Claude Code 系统提示,破坏三固定
配置隔离settingSources: [] + permissionMode: 'dontAsk'切断 user/project 注入;防工具挂起污染 latency
工具注册createSdkMcpServer + mcp__recon__* 白名单Claude SDK 把工具当 MCP;为 W8 MCP server 复用埋点
成本口径裸 token + 实付双列,不混报自动缓存是实付优势但 loop 口径噪声
步数对齐maxTurns: 8stepCountIs(8)num_turns 与 steps 语义对齐
context 限定语标注「短任务测不出 compaction」数据点#2 对 Claude SDK context 维度保守

对本项目的落地

  • 落地 bench/frameworks/claudeSdk/reconAgent.ts:按 A 节,query() + createSdkMcpServer 包五个共享工具(从 bench/reconciliation/tools.ts 的 zod schema 适配为 Claude SDK 的 tool() 形态——schema 同源,仅 handler 包装层不同),for await 收集 SDKResultMessage唯一框架特有代码是这层 MCP 包装 + query 装配;任务/golden/eval/system 仍 import 固定层。
  • bench/score.ts 加成本口径归一化:新增 normalizeCost(frameworkResult) —— AI SDK 6 分支用 estimateCost(totalUsage);Claude SDK 分支产出两个值:裸口径((input_tokens + cache_read_input_tokens) → estimateCost 折回全价)+ 实付口径(total_cost_usd)。矩阵两列分列。这是 D58 钉死 token 口径之后的第二层口径纪律。
  • MCP 复用伏笔:Claude Agent SDK 强制「工具=MCP」的形态,与 W8 计划的「把检索/AML 工具封装为 07-28 规范 MCP server」天然契合——本实现产出的 createSdkMcpServer 工具定义可在 W8 复用为真实 MCP server 的工具层(src/agent/rag/hybridSearch.ts 的检索工具同理可包)。记入 ADR 附注。
  • 诚实标注reconAgent.ts 头注明确「(1) 成本双口径,禁止与 AI SDK 6 单口径直接并排;(2) 本短任务测不出 Claude SDK 的 compaction/长上下文优势,context 维度对其保守;(3) preset 已关闭以保三固定」。数据点#2 数字执行当日回填,bench 不进阻断式 CI gate。

参考资料

  1. Claude Docs — Agent SDK reference: TypeScript(code.claude.com/docs/en/agent-sdk/typescript):query({prompt, options})→Query (AsyncGenerator<SDKMessage>)OptionssystemPrompt preset/string、mcpServersagentshookspermissionModemaxTurnsmaxBudgetUsdsettingSources);tool()/createSdkMcpServer()SDKResultMessagetotal_cost_usdusage{input_tokens,output_tokens,cache_creation/read_input_tokens}num_turnsmodelUsage);SDKCompactBoundaryMessage{tokens_saved} 自动 compaction 信号(2026,执行当周确认版本)
  2. Claude Docs — Agent SDK overview(code.claude.com/docs/en/agent-sdk/overview):与 Claude Code 同一 agent loop/context management/permission/subagent;2025-09 自 Claude Code SDK 改名;@anthropic-ai/claude-agent-sdk 内置工具+sessions+MCP;2026-06-15 起订阅计划 Agent SDK 额度独立计费(2026)
  3. DEV — Claude Agent SDK vs Vercel AI SDK 6 — which to pick in 2026:Anthropic SDK「opinionated/Claude-shaped/autonomy」,Vercel SDK「flexible/provider-neutral/interface」;Claude SDK 默认开 prompt caching + 自动 compaction(2026)
  4. Anthropic — Claude Sonnet 4.5 powers coding agents(vercel.com,与 Vercel 合作):Claude Agent SDK 用于自治长任务(2026)
  5. 本仓物证:src/agent/orchestrator/orchestratorAgent.ts(AI SDK 函数式 loop 对照)、src/agent/shared/cost.tsestimateCost 不建模缓存——本笔记 C 节口径差异根因)、bench/frameworks/aiSdk6/reconAgent.ts(Day 58 数据点#1)、src/agent/rag/hybridSearch.ts(W8 MCP 工具复用对象)(2026-06)

SOTA 检查 (2026-08-12)

  • Claude Agent SDK 是 Claude 优先自治 agent 的标准框架(2026-08 当前):query() async-iterable 形态、自动 compaction、createSdkMcpServer 进程内工具均为稳定 API;执行当周须 npm view @anthropic-ai/claude-agent-sdk version 确认版本(brief 口径 Py 0.2.95 / TS 0.3.170,2026-08 须重核),核对 SDKResultMessage.total_cost_usd/cache_read_input_tokens 字段名未变。
  • 「harness 拥有 loop vs 你拥有 loop」是两框架最本质的架构分野(2026 业界共识,DEV/Medium 对比一致):Claude SDK = 自治/Claude-shaped/默认贴近生产;AI SDK 6 = 控制权在你/provider-neutral——这是 Day 60 trade-off 矩阵的主轴,非本项目独创观点。
  • 成本口径必须区分裸 token 与实付:Claude SDK 默认 prompt caching 让 total_cost_usd 含折扣,与 AI SDK 6 的 estimateCost(无缓存建模)口径不同源——洞察②把它显式化为基准纪律,避免「折扣价 vs 原价」误读。
  • 过时认知警示:(1) 不可用 preset: 'claude_code' 做框架横评——注入数千 token 系统提示破坏三固定(洞察①);(2) 不可宣称「本基准证明 Claude SDK context 管理更优」——8 步短任务根本没触发 compaction(D 节限定语),其最强能力未被测试。
  • 待跟踪:Day 60 汇总时,确认 num_turns(Claude SDK)与 steps.length(AI SDK 6)在「调用 finalReport 即停」下是否数值一致(若 Claude SDK 把 result 消息也计入 turn,需 -1 归一);订阅计划 2026-06-15 起 Agent SDK 独立额度计费是否影响本项目的 API key 计量口径(本项目用 API key 非订阅,应不受影响,执行当日确认)。