Claude Agent SDK 深度解析:给 Agent 一台电脑,再给它一个可验证的循环

Claude Agent SDK 深度解析:给 Agent 一台电脑,再给它一个可验证的循环

大多数 Agent 框架在教模型"调用工具",这个 SDK 的思路是先给它一台电脑。

Anthropic 在 2025 年 9 月 29 日发了一篇官方博客《Building agents with the Claude Agent SDK》,核心信息一句话:Claude Code SDK 正式改名为 Claude Agent SDK。

改名不是营销动作,而是一次"承认现实":Claude Code 本来是为 Anthropic 内部开发者提效做的编码工具,结果他们自己开始拿它做深度研究、视频生成、笔记整理——官方原话说它"已经开始驱动我们几乎所有主要的 Agent 循环"。既然底层这套 harness 能跑的不只是编码任务,名字就不该再限制它。

这篇把博客里的设计原则、最佳实践,以及 Python SDK 的实际用法串起来讲清楚。

本文提纲

  1. 为什么改名:harness 比应用更重要
  2. 核心设计原则:给 Agent 一台电脑
  3. Agent loop:收集上下文 → 采取行动 → 验证工作
  4. 收集上下文:四件武器与它们的取舍
  5. 采取行动:工具、脚本、代码生成与 MCP
  6. 验证工作:规则、视觉反馈与 LLM as judge
  7. 测试与改进:四个诊断问题
  8. 落到代码:Python SDK 上手
  9. 选型与注意事项

为什么改名:harness 比应用更重要

博客里给的解释很直接:Claude Code 的设计前提是Claude 需要程序员日常用的那些工具——在代码库里找到合适的文件、读写编辑、跑 lint、执行、调试,然后迭代到成功。

关键发现是:一旦有了这些工具,它就不只能写代码。 给它跑 bash、编辑文件、创建文件、搜索文件的能力之后,它可以读 CSV、搜网页、做可视化、解读指标——也就是说,"会写代码 + 有一台电脑"本身就构成了通用 Agent 的底座。

所以改名反映的是分层:Claude Code 是应用,Claude Agent SDK 是驱动它的那套 harness。官方把它总结成一句设计原则——给你的 Agent 一台电脑,让它像人一样工作。

他们列出的典型场景也印证了这一点:

  • 金融 Agent:理解持仓与目标,接外部 API、存数据、写代码做计算;
  • 个人助理 Agent:订行程、管日历、约会议、写简报,跨应用跟踪上下文;
  • 客服 Agent:处理高歧义请求,查用户数据、连外部 API、回消息、该升级给人时升级;
  • 深度研究 Agent:在大规模文档里搜索、跨文件交叉验证、产出详细报告。

Agent loop:收集上下文 → 采取行动 → 验证工作

这是全文最有价值的一个框架。官方观察到 Claude Code 内部反复出现同一个反馈循环:

gather context  →  take action  →  verify work  →  repeat

他们把这三段拆成了"该给 Agent 配什么能力"的清单,并且全程用一个邮件 Agent 举例(比如让它读历史邮件、起草回复)。这个框架的好处是:当你发现 Agent 不好用时,可以按阶段定位缺什么——是它拿不到信息(gather),还是没有合适的手段(act),还是无从判断自己做得好不好(verify)。

收集上下文:四件武器与它们的取舍

官方有个挺妙的说法:文件系统代表"可以被拉进模型上下文的信息",而 Agent 的文件夹和文件结构,本身就是一种上下文工程。

具体怎么工作?当 Claude 遇到大文件(日志、用户上传的文件),它会用 grep、tail 这类 bash 脚本自己决定怎么把内容装进上下文,而不是无脑全读。所以对邮件 Agent,把历史邮件放进一个 Conversations 文件夹,就等于给了它一个可检索的语料库。

官方的态度很值得注意,几乎是罕见的"劝你别急着上向量":语义检索通常更快,但更不准确、更难维护、更不透明——它要切块、embedding、按向量检索概念。

结论是:先从 agentic search 开始,只有当你确实需要更快的结果或更大的召回变化时,再加语义检索。

三、Subagents(子 Agent)

SDK 默认支持子 Agent,官方给了两个理由,都说到点子上:

  1. 并行化:同时开多个子 Agent 处理不同任务;
  2. 上下文管理:子 Agent 使用独立的上下文窗口,只把相关信息回传给编排者,而不是把整个上下文倒回去。

第二个理由才是关键:它天生适合"要翻大量信息、但其中绝大多数没用"的任务。 邮件 Agent 可以派出多个搜索子 Agent 并行查不同关键词,每个只返回相关片段,而不是整封邮件线程。

四、Compaction(压缩)

长任务必然撞上上下文上限。SDK 的 compact 功能会在接近上限时自动摘要前面的消息,它构建在 Claude Code 的 /compact 命令之上。这不是可选项——Agent 跑得越久,上下文维护越关键。

采取行动:工具、脚本、代码生成与 MCP

工具:设计成"主要动作"

官方提醒了一句容易被忽略的话:工具在上下文窗口里非常显眼,是 Claude 决定如何完成任务时首先考虑的动作。 所以你要有意识地设计工具,最大化上下文效率。

原则是:工具应该是你希望 Agent 采取的主要动作。 对邮件 Agent 来说,就是 fetchInbox、searchEmails 这类最高频的动作。工具设计的具体方法论,官方指向另一篇博客《Writing effective tools for agents》——那是配套阅读。

Bash 与脚本:通用兜底

Bash 是"用电脑干活"的通用工具。博客举的例子很实用:用户的重要信息可能在附件里,Claude 可以写代码把 PDF 下载下来、转成文本、再在里面搜索——一条临时拼出来的链路,不需要事先准备工具。

代码生成:被低估的行动方式

这一段我觉得是全文最值得抄进自己系统设计的部分:

代码是精确的、可组合的、可无限复用的,因此是需要可靠执行复杂操作的 Agent 的理想输出。

官方的例子:Claude.ai 的文件创建功能完全依赖代码生成——Claude 写 Python 脚本来生成 Excel、PowerPoint、Word 文档,从而保证格式一致和复杂功能。放在邮件 Agent 上,就是"让用户能对收件邮件设定规则",而这需要写代码在事件上运行。

一个值得自问的问题:我的任务里,哪些部分表达成代码会更可靠? 很多时候这个问题的答案会打开新的能力。

MCP:标准化的外部集成

MCP(Model Context Protocol)提供标准化的外部服务集成,自动处理认证和 API 调用,所以接 Slack、GitHub、Google Drive、Asana 都不需要自己写集成、不用管 OAuth。邮件 Agent 因此可以直接调 search_slack_messages 或 get_asana_tasks。生态越大,你越能"专注于 Agent 行为本身"。

验证工作:规则、视觉反馈与 LLM as judge

官方对这一段的价值判断很明确:能检查并改进自己输出的 Agent,本质上更可靠——它们在错误累积之前抓住错误,跑偏时自我纠正,并在迭代中变好。

关键是给 Claude 具体的评估方式。三种:

一、定义规则(最好的反馈)

最好的反馈形式,是为输出定义清晰的规则,然后说明哪条规则没过、为什么。

代码 lint 是规则反馈的绝佳例子,而且反馈越深入越好:官方特别指出,生成 TypeScript 再 lint,通常比生成纯 JavaScript 更好,因为它给你多出好几层反馈。

落到邮件场景:检查收件人地址是否合法(不合法就报 error)、用户是否曾给这个人发过邮件(符合就报 warning)。

二、视觉反馈

做视觉类任务(UI 生成、测试)时,把截图或渲染结果喂回模型很有效。比如发送 HTML 格式邮件时,截图后让模型自己核对四个维度:

  • 布局:元素位置对不对、间距合不合适;
  • 样式:颜色、字体、格式是否符合预期;
  • 内容层级:信息顺序和强调是否正确;
  • 响应式:看起来有没有挤坏(不过单张截图的视口信息有限)。

配一个 Playwright 之类的 MCP server,就能把这个视觉反馈循环自动化:截图、多视口、甚至测试交互元素。

三、LLM as judge

用另一个模型按模糊规则给输出打分。官方对它的评价很坦率:方法不够稳健,延迟代价还很大,但如果你就是愿意为一点提升付出代价,它有用。邮件 Agent 的用法是:派一个子 Agent 专门评审草稿的语气,看是否贴合用户过往的沟通风格。

三种方式其实构成一个可靠性梯度:规则 > 视觉 > 判官,越靠前越确定、越便宜。

测试与改进:四个诊断问题

跑了几轮 agent loop 之后,官方建议认真看输出——尤其是失败的案例,然后"设身处地"问一句:它有没有拿到干这件事该有的工具?

他们给了四个诊断问题,每一个都指向一类不同的修法:

症状 可能的根因与修法
Agent 误解任务 可能缺关键信息——能不能改搜索 API 的结构,让它更容易找到该知道的东西
Agent 反复失败 能不能在工具调用里加一条正式规则,用来识别并修复这个失败
Agent 修不好自己的错 能不能给它更有用或更有创意的工具,换一条路解决问题
Agent 性能随功能增加而波动 基于真实用户用法建一个有代表性的测试集,做程序化评估(eval)

最后一条尤其重要:没有 eval 集,你只能靠手感判断"这次改动是不是让 Agent 变笨了"。

落到代码:Python SDK 上手

博客讲了"为什么"和"怎么想",具体怎么用要回到 SDK。Python 包名是 claude-agent-sdk:

pip install claude-agent-sdk

一个细节挺贴心:Claude Code CLI 会随包自动捆绑,不需要单独安装;想用系统安装或指定版本,可以在选项里给 cli_path。前置要求是 Python 3.10+。

单次查询:query()

import anyio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock

async def main():
    options = ClaudeAgentOptions(
        system_prompt="You are a helpful assistant",
        max_turns=1,
    )
    async for message in query(prompt="Tell me a joke", options=options):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(block.text)

anyio.run(main)

query() 返回一个 AsyncIterator,消息类型分 AssistantMessage / UserMessage / SystemMessage / ResultMessage,内容块分 TextBlock / ToolUseBlock / ToolResultBlock。

权限:先搞清楚 allowed_tools 的语义

这是最容易搞错的一点,README 专门强调了:allowed_tools 是"自动批准清单",不是"可用工具白名单"。 列进去的工具会被自动批准,没列进去的会落到 permission_mode 和 can_use_tool decision;它不会把工具从 Claude 的工具集里移除。要禁用某个工具,得用 disallowed_tools。

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Write", "Bash"],   # 这些自动批准
    permission_mode="acceptEdits",             # 自动接受文件编辑
    cwd="/path/to/project",
)

需要更细的控制时,用权限回调——它拿到工具名、输入和上下文,返回 PermissionResultAllow 或 PermissionResultDeny,还能改写输入:

async def my_permission_callback(tool_name, input_data, context):
    if tool_name in ["Read", "Glob", "Grep"]:
        return PermissionResultAllow()

    if tool_name in ["Write", "Edit", "MultiEdit"]:
        file_path = input_data.get("file_path", "")
        if file_path.startswith("/etc/") or file_path.startswith("/usr/"):
            return PermissionResultDeny(message=f"Cannot write to system directory: {file_path}")
    return PermissionResultAllow()

这就是"给 Agent 一台电脑"必须配的另一半:电脑的边界由你定义。

双向会话:ClaudeSDKClient

query() 适合一次性的问答;需要多轮交互时用 ClaudeSDKClient。它的额外价值在于:只有它支持自定义工具与 hooks,而且这两者都可以直接写成 Python 函数。

async with ClaudeSDKClient(options=options) as client:
    await client.query("Greet Alice")
    async for msg in client.receive_response():
        print(msg)

自定义工具:进程内的 MCP server

这是这套 SDK 里最有工程味的设计。自定义工具通过进程内 MCP server 实现——不需要像常规 MCP 那样起独立进程:

from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions, ClaudeSDKClient

@tool("greet", "Greet a user", {"name": str})
async def greet_user(args):
    return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}

server = create_sdk_mcp_server(name="my-tools", version="1.0.0", tools=[greet_user])

options = ClaudeAgentOptions(
    mcp_servers={"tools": server},
    allowed_tools=["mcp__tools__greet"],   # 预批准,避免权限提示
)

注意工具名的命名空间规则:mcp__<server名>__<工具名>。官方列的好处也很实在:不用管子进程、没有 IPC 开销、单进程部署、同进程调试、类型安全。而且进程内 server 和外部 MCP server 可以混用,mcp_servers 里同时放两种都行。

Hooks:由"应用"而不是 Claude 调用的拦截点

README 里对这个概念的界定很精确:hook 是 Claude Code 这个"应用"(不是 Claude)在 Agent 循环的特定位置调用的 Python 函数,用于提供确定性的处理和自动反馈。

async def check_bash_command(input_data, tool_use_id, context):
    if input_data["tool_name"] != "Bash":
        return {}
    command = input_data["tool_input"].get("command", "")
    for pattern in ["foo.sh"]:
        if pattern in command:
            return {"hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "permissionDecision": "deny",
                "permissionDecisionReason": f"Command contains invalid pattern: {pattern}",
            }}
    return {}

options = ClaudeAgentOptions(
    allowed_tools=["Bash"],
    hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[check_bash_command])]},
)

这提供了权限清单做不到的能力:基于命令内容做细粒度判断,而不是基于工具名。

子 Agent:程序化定义

博客里说子 Agent 用于并行和上下文隔离,Python SDK 里可以直接声明:

from claude_agent_sdk import AgentDefinition, ClaudeAgentOptions, query

options = ClaudeAgentOptions(
    agents={
        "code-reviewer": AgentDefinition(
            description="Reviews code for best practices and potential issues",
            prompt="You are a code reviewer. Analyze code for bugs, performance issues, ...",
            tools=["Read", "Grep"],
            model="sonnet",
        ),
    },
)

async for message in query(
    prompt="Use the code-reviewer agent to review the code in src/...",
    options=options,
):
    ...

子 Agent 有自己的 prompt、tools 和 model——用便宜模型 + 只读工具做审查,是很典型的省钱配置。

成本与错误

ResultMessage.total_cost_usd 能拿到本次运行的费用,仓库里还有 max_budget_usd.py 示例做预算上限。错误类型分了层:ClaudeSDKError 是基类,往下有 CLINotFoundError(没装 CLI)、CLIConnectionError、ProcessError、ResultError(运行以错误结果结束,异常上带 subtype / terminal_reason / api_error_status)、CLIJSONDecodeError,按类型分别处理比抓一个大异常再猜要靠谱得多。

选型与注意事项

它适合什么:你需要一个能用电脑干活的通用 Agent——读写文件、跑命令、写代码做计算、连外部服务,而且要能被人审查和约束。博客里那四类场景(金融、个人助理、客服、深度研究)都落在这个范围里。

用之前要接受的三件事:

  1. 它给的是"一台电脑",权限边界就是你的责任。 allowed_tools 的语义(自动批准 vs 禁用)、permission_mode、can_use_tool 回调、hooks,这几层要真的配起来,而不是全开。
  2. 验证环节最容易被跳过,也最决定上限。 规则 > 视觉 > 判官这个梯度不是可选项——没有 lint、没有截图回灌、没有 eval 集,Agent 就只能靠模型自觉。
  3. 迁移时注意改名成本。 从 Claude Code SDK(< 0.1.0)升级要做 ClaudeCodeOptions → ClaudeAgentOptions 的改名、系统提示词配置合并、settings 隔离,新增了程序化子 Agent 与会话 fork 等能力,CHANGELOG.md 里有完整清单。

另外补两个生态数字:截至目前,Python SDK 仓库 8,216 stars,TypeScript SDK 1,781 stars——Python 是主战场,但两边的核心概念(query / options / 自定义工具 / hooks / 子 Agent)是对齐的。

参考链接

你的 Agent 现在卡在"收集上下文""采取行动"还是"验证工作"哪一段?评论区聊聊,觉得这套框架有用就点个赞。


作者: itech001
来源: 公众号:AI人工智能时代(the-ai-era)
网站: https://www.theaiera.top/
关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top

本文首发于 AI人工智能时代,转载请注明出处。

posted @ 2026-10-05 17:02  iTech  阅读(7)  评论(0)    收藏  举报