合集

Claude Agent SDK 实战

已更新 27 章
阅读进度 11%

第 2 章

有了 Claude Code,为什么还要用 Agent SDK?

这问题我被问过很多次。

有一次我在群里说“Agent SDK 不是 Agent 框架”,有人追着问:“那我直接用 Claude Code 不就行了?你们怎么总爱造轮子?”

我当时的回答不太好,大意是“SDK 更灵活”。这话等于没说。

后来我干脆把问题拆成五个,谁再问,我就按这五个答。今天一次性写出来。


Q1:Claude Code 和 Claude Agent SDK,到底啥关系?

说人话:Claude Code 是成品,Agent SDK 是同一套 Harness 的库接口。

先把“谁在干什么”画出来:

text
你的代码 / 你的产品
        │
        ▼
Claude Agent SDK          ← 你写的 Python / TS 代码在这一层
        │
        ▼
Claude Code CLI           ← 循环、工具、权限、上下文、压缩、会话都在这
        │
        ▼
模型 API

注意中间那层。SDK 不实现 Agent Loop。

循环、工具执行、权限判定、上下文组装、压缩、会话落盘,全在 Claude Code CLI 里。SDK 干两件事:把你的配置翻译成 CLI 的启动参数和控制请求;把 CLI 的消息流、回调翻译回你的代码。

所以你不能“import 一个循环”,只能驱动一个已经跑起来的循环。

我一般这么跟人解释:Claude Code 是台装好的机器,SDK 是这台机器的控制面板。你能按按钮、能接外设、能读仪表盘,但你不能把发动机拆了换一个。

Claude CodeClaude Agent SDK
谁在用人程序
形态终端里的 CLI 产品Python / TypeScript 库
循环内置同一套,在 CLI 里
工具内置 + MCP内置 + MCP + 进程内工具
权限交互 UI + settings / hookscan_use_tool + 权限模式 + hooks
输出终端文本消息流、结构化输出
部署本地 / CI嵌进产品、多租户服务
定制设置、技能、子 Agent上述全部 + 程序化控制

所以别误会:用了 SDK,不等于你写了一个 Claude Code。 你只是换了个入口,发动机还是那台。

SDK 本身是开源的,CLI 是闭源的。你看得到控制面板的代码,看不到发动机内部。这也是为什么这本书不带你读 CLI 源码——读不到,也不该猜。


Q2:既然有 Claude Code,为什么还要 SDK?

先问一句:你是想自己用,还是想让程序用?

如果你是自己用——坐在终端里,让 AI 帮你读代码、改文件、跑测试——Claude Code 已经够好了。真的,别造。造出来大概率还不如它。

但如果你想让你的程序用,事情就变了。

我见过三个很典型的场景。

第一个,客服后台。 用户点“申请退款”,后台要查订单、看政策、判断能不能退、超过 500 块转人工。这一串动作不能让客服自己去终端里敲命令,得由你的服务在背后跑。

第二个,内部数据平台。 运营想看“上周华东区的退款率”,Agent 需要调你们自己的 Python 函数、查数据库、把结果整理成表格。这些工具就跑在你的进程里,不是一个 CLI 能接进去的。

第三个,CI 批处理。 每天晚上跑 50 个仓库的依赖升级,每个仓库一个 Agent,跑完收集结构化结果,失败的进工单系统。你要的是能编排、能拿 JSON、能重试,而不是 50 个终端窗口。

这三个场景的共同点是:Agent 不是主角,你的产品才是。 Agent 只是里面的一颗零件。

Claude Code 能不能无头跑?能,claude -p 就是干这个的。但你拿到的是文本和退出码,后面的事都得自己来:解析输出、管理进程、做审批、处理并发、记录成本。SDK 把这些变成了 API。

所以 SDK 的定位不是“更高级的 Claude Code”,是“把 Claude Code 嵌进程序的接口”。


Q3:有哪些事是 Claude Code 做不了、SDK 能做的?

先纠正一个说法:与其说“做不了”,不如说“它不是为这件事设计的”。

Claude Code 可以通过 MCP 接外部工具,可以通过 settings 配权限,可以通过 hooks 做拦截。它不是一个封闭的黑盒。但它的产品形态决定了,很多事它只能做到“够用”,做不到“可编程”。

我把常见的差异列成五条:

你要做的事Claude CodeSDK
把 Agent 嵌进自己的服务只能当子进程调,解析文本原生消息流、控制请求、回调
用自己的工具和数据MCP 为主MCP + 进程内 @tool,直接调你的函数
用自己的策略和审批settings / hooks + 交互 UIcan_use_tool 接到你的风控引擎
程序化编排、结构化输出脚本接口有限typed messages、output_format
多租户、计量、配额单机产品,不适合给原语,隔离/计量仍要你写

每一条都可以展开,但先看一条最容易误解的:工具。

Claude Code 通过 MCP 接工具,这没问题。但 MCP 意味着你的工具要跑在一个 server 里,通过协议暴露出去。SDK 的进程内工具不用:一个 @tool 装饰器,函数就在你的 Python 进程里,参数直接是 dict,返回值直接回给 CLI。对“我就是要调一下公司内部的那个 Python 函数”这种需求,少一层协议,少一堆事。

比如你要接一个“查退款政策”的函数,大概就长这样:

python
from claude_agent_sdk import tool, create_sdk_mcp_server

@tool("refund_policy", "查询订单的退款政策", {"order_id": str})
async def refund_policy(args: dict) -> dict:
    return {"content": [{"type": "text", "text": policy_for(args["order_id"])}]}

server = create_sdk_mcp_server("shop", tools=[refund_policy])

函数在你进程里,参数是 dict,返回值是一个 MCP 风格的 content 列表。没有额外的 server 进程,也没有端口。

再说一条更隐蔽的:审批。

Claude Code 的审批是给人看的:终端弹出来,你按 y/n。SDK 的 can_use_tool 是给程序看的:退款金额、订单归属、租户 ID 传进来,你的代码返回 allow 或 deny,还能带上拒绝理由。这不是 UI 的差别,是“审批逻辑能不能接进你的业务系统”的差别。

审批大概长这样:

python
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny

async def approve(name, inp, ctx):
    if name == "mcp__shop__create_refund" and inp.get("amount", 0) > 500:
        return PermissionResultDeny(message="超过 500, 转人工")
    return PermissionResultAllow()

注意 mcp__shop__create_refund 这个前缀:SDK 会把 MCP 工具名拼成 mcp__<server>__<tool>。第一次写的时候很容易漏。

但 SDK 也不是万能的。下面这些事它做不了:

  • 改循环。 你想自己实现 plan-execute-verify、自己决定什么时候压缩、自己调度工具——SDK 不给你这个口子。
  • 改 Harness 内部。 工具怎么并发、压缩用什么算法、权限按什么顺序判,都在 CLI 里。你只能通过公开旋钮影响,不能替换。
  • 替你写领域层。 你的工具、提示、项目知识、评分标准,SDK 不知道。
  • 替你写产品层。 UI、登录、计费、数据存储、告警,SDK 不是应用框架。
  • 保证跨版本行为不变。 CLI 升级,行为可能变。你得固定版本、做回归、留降级。

所以如果你要的是“改循环”,SDK 不是答案。用裸 Messages API,或者自己写一个 Harness——这也是第 3 章开始要做的事。


Q4:为什么我用了 SDK,还是不如 Claude Code?

这问题最扎心,也最值得展开。

还有个变量得先排除:你用的是同一个模型吗? 同一个端点?同样的 effort / thinking?如果你换了小模型、换了兼容端点、把 effort 调低了,那没什么好比的,先把这个变量固定住。

固定之后,差距通常在三层:

层你可能的配置Claude Code 的配置怎么查
模型层小模型、兼容端点、低 effort同款模型 + 调过的参数先固定模型和端点
Harness 层循环 + 几个工具,没权限/压缩/会话工具、权限、上下文、压缩、会话、子 Agent、错误恢复用第 16 章的控制矩阵对照
领域层一句系统提示 + 通用工具为编码任务打磨的提示、工具、项目知识、完成标准用任务集评测,做 ablation

我见过最典型的一种配错,长这样:

python
options = ClaudeAgentOptions(
    tools=[],
    setting_sources=[],
    system_prompt="你是一个 coding agent",
    max_turns=10,
)

这段代码能跑,但它把 Claude Code 配成了 Hello World。

  • tools=[]:模型没有工具,读不了文件、改不了代码,只能聊天。
  • setting_sources=[]:CLAUDE.md、项目设置不加载;你的项目约定和自定义技能看不到了(内置技能可能还在)。
  • system_prompt 只有一句:没有工作流,没有“什么叫做完”,没有“不许改测试”。
  • 没有权限、没有 Stop Hook、没有评测:它能改测试,也能提前宣布完成。

把这几条对成表,排障的时候好用:

你写的配置你会看到的现象先改什么
tools=[]只会聊天,不读文件加 Read/Edit/Bash,或自己的工具
setting_sources=[]看不到 CLAUDE.md,不按项目约定加载 project,或把约定写进提示
系统提示只有一句提前停、乱改写工作流 + 完成标准
没有权限改测试、读 .envtests/ 只读 + can_use_tool
没有 Stop Hook没跑测试就说“完成”Stop 检查测试是否跑过
没有评测凭感觉调参10 个任务 × 3 次,先量基线

我第一次这么配的时候,还觉得自己挺克制:工具越少越安全嘛。结果它连 README 都读不了,当然也不知道“不许改测试”这条规矩。后来我才明白,安全和能力不是靠“关掉一切”实现的。

然后你跑一个任务,发现它不如 Claude Code,得出结论:“SDK 不行。”

不是 SDK 不行,是你把 Harness 的配置全关了。

我一般建议先问自己十个问题。不用全答,能答出前三个,方向就清楚很多:

  1. 你和 Claude Code 用的是同一个模型、同一个端点吗?
  2. cwd 对吗?Agent 能读到项目文件吗?
  3. 它有完成任务所需的工具吗——读、改、搜、跑、测?
  4. 工具描述说清了“什么时候用、返回什么”吗?
  5. 系统提示里有工作流和“完成标准”吗?
  6. 它看得到项目约定吗——CLAUDE.md、README、目录结构?
  7. 它会验证自己的工作吗?验证是强制的吗?
  8. 失败后它能恢复吗——工具错误、测试失败、上下文溢出、进程崩溃?
  9. 它停得对吗——太早宣布完成、死循环、预算耗尽?
  10. 你是在一组任务上量过,还是只看了一次运行?

这十个问题,基本就是这本书第二、三、四部分的内容。第 16 章会把它们收成一张控制矩阵。

如果只能记一句:先固定模型,再用同一组任务量,然后一次只改一个变量,再量。 别凭感觉调提示词,真的。


Q5:那我到底该用哪个?

我不想给你一个“看情况”的答案,那等于没答。先把选择画成树:

text
你是自己用,还是让程序用?
├─ 自己用 → Claude Code
└─ 让程序用
   ├─ 要改循环/自己编排? → 裸 API 或别的框架
   └─ 不改循环
      ├─ 要接自己的工具/审批/多租户? → SDK
      └─ 只是偶尔调一次? → Messages API

下面这张表,按你的处境选:

你的处境建议
自己在终端里写代码Claude Code
要把 Agent 嵌进产品/服务Agent SDK
要接自己的工具、数据、审批Agent SDK
要多租户、计量、配额Agent SDK + 自己写隔离
要改循环、自己编排裸 Messages API,或别的框架
只需要一次 LLM 调用Messages API,别引入进程
只是想做个小 demo,验证想法先用 Claude Code 跑一遍

选型不是信仰,是成本问题。Claude Code 的成本是“你只能用它给的形态”;SDK 的成本是“你得自己配、自己测、自己运维”;裸 API 的成本是“循环、工具、权限、上下文全得自己写”。

如果你不确定,我有个很土但有效的办法:先用 Claude Code 跑一周。 哪一步让你最难受,那个“难受”通常就是你要自己造的理由。

是“我想把它接进客服系统”?那是 SDK。

是“我想让它按我们的审批流走”?那是 SDK + 权限回调。

是“我想让它先规划再执行,Claude Code 的计划模式不够用”?那可能得自己写循环。

是“我只是觉得每次都要开终端很烦”?那可能写个脚本包一下就行,不用造 Agent。


最后

这一章没写多少代码,因为它在回答“要不要造、造什么”。下一章开始,我们不再讨论选型,直接动手。

先写一个 20 行的 while True,让它把测试改掉;然后一行一行,把 Harness 补起来。


本章代码 这一章没有新增代码;第 1 章的示例仍然适用: https://github.com/kevinkelin/claude-agent-sdk-book/tree/master/code/lab/taskflow 下一章会新建 code/tiny/agent.py。