智能体编排框架横评(3):厂商 Agent SDK
本文总结OpenAI Agents SDK、Claude Agent SDK、Google ADK、AWS Strands 入门:每家从安装和第一个 Agent 讲起,再给报销例子加上建单前的确认,最后比较它们和自家平台绑得多紧。
这一站讲什么
上一站的 LangGraph 要你先画图,流程怎么走写在代码里。这一站的四个 SDK 不用画图:你给模型一组工具和一段指令,模型自己决定下一步做什么,SDK 负责执行工具、把结果交回模型、在该停的地方停下。
这四个 SDK 都出自模型厂商或云厂商:
| SDK | 出品方 | 一句话 |
|---|---|---|
| OpenAI Agents SDK | OpenAI | 原语很少的 Agent 库,带审批、交接和追踪 |
| Claude Agent SDK | Anthropic | 把 Claude Code 整个搬进你的程序,可编程的 harness |
| Google ADK | 多语言 Agent 框架,2.0 起底层是图引擎 | |
| Strands Agents | AWS | 模型驱动的轻量 SDK,默认接 Amazon Bedrock |
每家按同样的顺序讲:安装,第一个 Agent,几个基本概念,报销例子怎么加确认,常见问题。报销场景沿用 主线:一个报销请求走完整个 Agent 系统:员工问"上周打车超标还能报吗,能就帮我提单",get_invoice 返回 INV-102、金额 118.5、标准 80(示意数字),create_ticket 会真正建单,建单前必须员工确认。
代码都是 Python 版,按各家 2026-11 的官方文档写,是示意代码,没有运行过。
OpenAI Agents SDK
安装
pip install openai-agents
export OPENAI_API_KEY="sk-..."
第一个 Agent
hello_openai.py 文件代码:
from agents import Agent, Runner
agent = Agent(name="assistant", instructions="你是公司的报销助手,回答要简短。")
result = Runner.run_sync(agent, "报销一般需要哪些材料?")
print(result.final_output)
输出结果类似:
一般需要发票、行程或用途说明,以及审批人签字。
Agent 描述一个 Agent 是谁、能做什么;Runner 负责把它跑起来。run_sync 是同步版本,在异步代码里用 await Runner.run(...)。
基本概念
工具。 用 @function_tool 装饰普通函数,docstring 就是工具说明:
from agents import function_tool
@function_tool
def get_invoice(invoice_id: str) -> dict:
"""按发票号查金额和报销标准。"""
return {"id": invoice_id, "amount": 118.5, "standard": 80}
agent = Agent(name="expense", instructions="...", tools=[get_invoice])
循环。 Runner 反复调用模型:模型给出最终回答就结束;要调工具就执行,把结果加进对话再调;要交给别的 Agent 就切换过去再调。默认最多 10 轮(max_turns),超过抛 MaxTurnsExceeded。
交接与 Agent 当工具。 一个 Agent 可以把对话交给另一个 Agent(handoffs=[...]),也可以把另一个 Agent 当工具调用(other.as_tool(...)),前者是对方接管,后者是自己保持控制。
护栏(Guardrail)。 在输入、输出或工具调用上挂检查函数,检查不通过就抛异常中止。
会话(Session)。 传 session=SQLiteSession("emp-001"),SDK 每轮自动读取和追加历史,多轮对话不用自己拼消息。
追踪。 默认开启,每次运行的模型调用、工具调用、交接都会发到 OpenAI 后台的 Traces 页面。
报销例子:建单前要员工批准
给 create_ticket 设 needs_approval=True,模型每次要调用它时,运行会暂停。
from agents import Agent, Runner, function_tool
@function_tool
def search_policy(query: str) -> str:
"""查报销制度。"""
return "《差旅费管理办法》4.2:市内打车单次标准 80 元"
@function_tool
def get_invoice(invoice_id: str) -> dict:
"""查发票。"""
return {"id": invoice_id, "amount": 118.5, "standard": 80}
@function_tool(needs_approval=True) # 每次调用都先停下等批准
def create_ticket(invoice_id: str, reason: str) -> str:
"""提交报销单。"""
return "R-55821"
agent = Agent(name="expense", instructions="先查制度和发票,判断能报再提单。",
tools=[search_policy, get_invoice, create_ticket])
result = await Runner.run(agent, "上周打车超标还能报吗,能就帮我提单")
while result.interruptions: # 有待批准的调用
state = result.to_state()
for item in result.interruptions:
if ask_employee(item.name, item.arguments): # 你的确认界面
state.approve(item)
else:
state.reject(item, rejection_message="员工未确认,不提单")
result = await Runner.run(agent, state) # 带着决定接着跑
print(result.final_output)
流程是这样的:
- 模型要调用
create_ticket,运行暂停,待批准的调用放在result.interruptions里; - 用
result.to_state()拿到这次运行的状态,逐个approve或reject; - 把状态交回
Runner.run,运行接着往下走。拒绝时模型会看到rejection_message。
员工可能过几个小时才确认,这时把状态存起来:state.to_json() 存进数据库,回来后用 RunState.from_json(agent, data) 读出再跑。needs_approval 也可以传一个函数,按参数决定要不要批准,比如只有超标的单子才要。
常见问题
- 批准后服务器重启,单子建了两次。 官方文档说明批准只表示"可以执行",不保证只执行一次。
create_ticket要自己带幂等键。 - 输入护栏拦住了,但工具已经执行了。 输入护栏默认和 Agent 并行跑。有副作用的流程设
run_in_parallel=False,或者把检查放进工具护栏。 - 护栏没生效。 输入护栏只对第一个 Agent 生效,输出护栏只对给出最终回答的 Agent 生效。
- 不要把序列化的状态交给浏览器保存。
RunState.from_json不校验内容真伪,状态要留在服务器上。 - 换成非 OpenAI 模型后,网页搜索之类的工具没了。
WebSearchTool、FileSearchTool、CodeInterpreterTool这些托管工具在 OpenAI 服务端执行,只能配 OpenAI 的 Responses 模型。
Claude Agent SDK
安装
pip install claude-agent-sdk
export ANTHROPIC_API_KEY="sk-ant-..."
Python 包里已经带了 Claude Code 命令行程序,不用另外安装。
第一个 Agent
hello_claude.py 文件代码:
import anyio
from claude_agent_sdk import query
async def main():
async for message in query(prompt="用一句话说明报销需要哪些材料"):
print(message)
anyio.run(main)
运行后会依次打印几条消息:一条系统消息(会话信息),一条或几条 AssistantMessage(Claude 的回答),最后一条 ResultMessage(最终文本、用量、费用、会话 ID)。
和另外三家最大的不同在这里:query() 并不是直接调用模型,而是启动一个 Claude Code 子进程,和它通过标准输入输出通信。所以你拿到的是一个完整的 Claude Code:自带读写文件、执行命令等工具,自带权限系统、hooks、子代理、上下文压缩和会话记录。官方的说法是"驱动 Claude Code 的那套工具、循环和上下文管理,可以在 Python 和 TypeScript 里编程使用"。
基本概念
两种调用方式。 query() 每次开一个新会话,适合一次性任务;ClaudeSDKClient 可以在同一个会话里连续对话,还能中途打断。
选项。 几乎所有配置都放在 ClaudeAgentOptions 里:
from claude_agent_sdk import ClaudeAgentOptions
options = ClaudeAgentOptions(
system_prompt="你是公司的报销助手",
allowed_tools=["Read"], # 不用询问就能用的工具
max_turns=5,
cwd="/srv/expense", # 工作目录
)
自定义工具。 用 @tool 定义,再用 create_sdk_mcp_server() 包成一个进程内的 MCP 服务器。工具在 Claude 那边的名字是 mcp__服务器名__工具名。
权限。 每次工具调用按顺序检查:hooks、拒绝规则、询问规则、权限模式、允许规则,最后才问你的 can_use_tool 回调。拒绝规则在任何模式下都优先。
项目配置。 默认会读取工作目录 .claude/ 下的 CLAUDE.md、skills 和设置,和在命令行里用 Claude Code 一样。在服务端不想带入本机配置,写 setting_sources=[]。
报销例子:建单前要员工批准
做法是把两个只读工具写进 allowed_tools,create_ticket 不写。这样它的调用会一路走到权限检查的最后一步,进入 can_use_tool 回调,由你决定放行还是拒绝。
from claude_agent_sdk import (ClaudeSDKClient, ClaudeAgentOptions, tool,
create_sdk_mcp_server, PermissionResultAllow, PermissionResultDeny)
def text(s): return {"content": [{"type": "text", "text": s}]}
@tool("search_policy", "查报销制度", {"query": str})
async def search_policy(args): return text("4.2:市内打车单次标准 80 元")
@tool("get_invoice", "查发票", {"invoice_id": str})
async def get_invoice(args): return text('{"id":"INV-102","amount":118.5,"standard":80}')
@tool("create_ticket", "提交报销单,会真正建单", {"invoice_id": str, "reason": str})
async def create_ticket(args): return text("R-55821")
async def can_use_tool(name, input_data, ctx): # 只有没被规则放行的调用才会进来
if await ask_employee(name, input_data):
return PermissionResultAllow(updated_input=input_data)
return PermissionResultDeny(message="员工未确认,不提单")
server = create_sdk_mcp_server(name="expense", version="1.0.0",
tools=[search_policy, get_invoice, create_ticket])
options = ClaudeAgentOptions(
tools=[], # 去掉读写文件、执行命令等内置工具
mcp_servers={"expense": server},
allowed_tools=["mcp__expense__search_policy", "mcp__expense__get_invoice"],
can_use_tool=can_use_tool)
async with ClaudeSDKClient(options=options) as client:
await client.query("上周打车超标还能报吗,能就帮我提单")
async for msg in client.receive_response():
print(msg)
几点说明:
tools=[]去掉了全部内置工具。报销助手不需要读写文件和执行命令,留着反而多了风险。- 回调返回
PermissionResultDeny时,message会交给模型,模型据此回复员工。 - 回调可以一直等员工。如果要等很久、进程不该一直开着,改用 PreToolUse hook 返回
permissionDecision: "defer",这一轮先结束,之后从保存的会话恢复。
常见问题
- 确认逻辑没执行。 检查是不是把
create_ticket也写进了allowed_tools。允许规则先于回调生效,命中就直接放行,回调根本收不到。 - 行为和命令行里的 Claude Code 不一样。 SDK 默认不使用 Claude Code 的系统提示,要原来的行为写
system_prompt={"type": "preset", "preset": "claude_code"}。 - 长对话里,开头交代的规则被忘了。 自动压缩会把早期对话换成摘要。长期有效的规则放进 CLAUDE.md。
- 部署比想象中重。 一个会话就是一个子进程,带着工作目录和会话文件,不能当成无状态接口随便扩容。多租户要按官方 hosting 文档做隔离。
- 只能用 Claude 模型。 可以经 Anthropic API、Amazon Bedrock、Google Cloud 等渠道调用,但不能换别家模型。
Google ADK
安装
pip install google-adk
export GOOGLE_API_KEY="..."
ADK 有 Python、TypeScript、Go、Java 等多种语言版本,这里用 Python。
第一个 Agent
ADK 约定一个 Agent 是一个目录,里面的 agent.py 定义一个叫 root_agent 的变量:
expense/
├── __init__.py # 内容:from . import agent
└── agent.py
expense/agent.py 文件代码:
from google.adk import Agent
def search_policy(query: str) -> dict:
"""查报销制度。"""
return {"clause": "4.2:市内打车单次标准 80 元"}
root_agent = Agent(
name="expense_agent",
model="gemini-flash-latest",
instruction="你是公司的报销助手。",
tools=[search_policy],
)
在 expense 的上一级目录运行:
adk web
浏览器打开它给出的地址,就能在开发界面里和 Agent 对话,看到每一步的事件和状态。也可以用 adk run expense 在命令行里对话。
基本概念
Agent。 模型、指令、工具的组合。普通函数直接放进 tools 就是工具,docstring 是工具说明。
子 Agent。 sub_agents=[...] 列出下属 Agent,模型可以把任务转交给它们;也可以用 AgentTool 把一个 Agent 当工具调用。
Workflow(2.0)。 2.0 起 ADK 的底层是图引擎。需要固定顺序的步骤时,用 Workflow(edges=[...]) 把函数、工具和 Agent 连成图,RequestInput 节点可以在两步之间要人工输入。
Runner 和 Session。 在自己的程序里运行 Agent 时用 Runner。对话记录存在 SessionService 里:InMemorySessionService 只在内存,DatabaseSessionService 存数据库。会话里的 State 是键值对,user:、app:、temp: 前缀区分作用范围。
回调。 Agent、模型、工具各有调用前、调用后两个回调,返回值可以替换或跳过原来的逻辑。
报销例子:建单前要员工确认
ADK 叫 Tool Confirmation。把建单函数包成 FunctionTool 并设 require_confirmation=True:
from google.adk import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import FunctionTool
def get_invoice(invoice_id: str) -> dict:
"""查发票。"""
return {"id": invoice_id, "amount": 118.5, "standard": 80}
def create_ticket(invoice_id: str, reason: str) -> dict:
"""提交报销单,会真正建单。"""
return {"ticket_id": "R-55821"}
root_agent = Agent(
name="expense_agent", model="gemini-flash-latest",
instruction="先查制度和发票,判断能报再提单。",
tools=[search_policy, get_invoice,
FunctionTool(create_ticket, require_confirmation=True)])
runner = Runner(agent=root_agent, app_name="expense",
session_service=InMemorySessionService())
运行时,模型调用 create_ticket 的那一刻,事件流里会出现一个名为 adk_request_confirmation 的函数调用,建单暂停。员工确认后,在同一个会话里发回一条对它的回复:
# name="adk_request_confirmation",id 是那次确认请求的调用 id
# response={"confirmed": True}
确认请求本身就是一次函数调用,所以可以通过 ADK 的 REST 接口从邮件、IM 等地方远程确认。require_confirmation 也可以传一个返回布尔值的函数,按参数决定要不要确认。需要员工填写内容时,在工具里调用 tool_context.request_confirmation(hint=..., payload=...)。
常见问题
- 确认和数据库会话不能一起用。 Tool Confirmation 目前是实验特性,不支持
DatabaseSessionService和VertexAiSessionService,上线前要确认这个组合能不能用。 - 旧教程里的
SequentialAgent、ParallelAgent、LoopAgent。 2.0 起它们被 Workflow 取代,新代码直接用 Workflow。 - 工具里写了宽泛的
except Exception,自动重试失效。 2.0 会重试抛异常的节点,异常被吞掉就重试不了。 - 离开 Gemini 后,搜索 grounding、代码执行这些内置工具用不了。 ADK 可以通过 LiteLLM 接别家模型,但 Gemini 专属工具只能配 Gemini。
- 文档里的云服务名字对不上。 Vertex AI 改名 Gemini Enterprise Agent Platform,Agent Engine 改名 Agent Runtime,但 ADK 的类名还是旧的,比如
VertexAiSessionService。
AWS Strands Agents
安装
pip install strands-agents strands-agents-tools
默认模型走 Amazon Bedrock,需要先配好 AWS 凭证(aws configure)并在 Bedrock 控制台开通模型。也可以换成 Anthropic、OpenAI、Gemini、Ollama 等。
第一个 Agent
from strands import Agent
agent = Agent(system_prompt="你是公司的报销助手。")
agent("报销一般需要哪些材料?")
调用 agent(...) 时回答会边生成边打印出来。Strands 的官方说法是"模型驱动":你只给模型、提示词和工具,规划和选工具都交给模型。循环就是调模型,要工具就执行,再调模型,直到不再要工具为止。
基本概念
工具。 用 @tool 装饰函数:
from strands import Agent, tool
@tool
def get_invoice(invoice_id: str) -> dict:
"""查发票。"""
return {"id": invoice_id, "amount": 118.5, "standard": 80}
agent = Agent(tools=[get_invoice])
strands-agents-tools 包里有一批现成工具(读写文件、HTTP 请求、计算器等)可以直接用。
Hooks。 在 Agent 生命周期的事件上挂函数,比如 BeforeToolCallEvent(单个工具调用前)、BeforeToolsEvent(一批工具调用前)。审批就是用它实现的。
会话管理。 用 session manager 保存对话,Python 新代码推荐 SnapshotSessionManager,也有存文件和存 S3 的版本。
多 Agent。 支持把 Agent 当工具、Swarm、Graph、Workflow 几种组合方式,跨进程用 A2A 协议。
报销例子:建单前要员工确认
在 BeforeToolCallEvent 的 hook 里调用 event.interrupt(),Agent 会停下来:
from strands import Agent, tool
from strands.hooks import BeforeToolCallEvent
@tool
def create_ticket(invoice_id: str, reason: str) -> str:
"""提交报销单,会真正建单。"""
return "R-55821"
def confirm(event: BeforeToolCallEvent):
if event.tool_use["name"] == "create_ticket":
answer = event.interrupt("confirm-ticket", reason=event.tool_use["input"])
if answer != "y":
event.cancel_tool = "员工未确认,不提单"
agent = Agent(tools=[search_policy, get_invoice, create_ticket]) # 前两个同样用 @tool 定义
agent.add_hook(confirm, BeforeToolCallEvent)
result = agent("上周打车超标还能报吗,能就帮我提单")
while result.stop_reason == "interrupt":
result = agent([{"interruptResponse": {"interruptId": i.id, "response": ask(i)}}
for i in result.interrupts])
过程是:hook 里第一次调用 event.interrupt(),Agent 停下,返回的 stop_reason 是 "interrupt";你带着员工的回复再调用一次 Agent,hook 会重新执行,这次 interrupt() 直接返回员工的回复。员工拒绝时设置 event.cancel_tool,工具不执行,模型收到这段说明。
常见问题
- hook 里
interrupt()前面的代码执行了两次。 恢复时 hook 会重新执行,interrupt()之前不要放有副作用的代码。 - 从不可信来源加载会话文件。 官方文档提醒会话状态是可信输入,不要加载来源不明的会话文件,比如克隆下来的仓库里带的。
四家放在一起看
四家的核心是同一个循环:模型决定下一步,SDK 执行工具、把结果交回模型、模型不再要工具时停下。这和 跟读 learn-claude-code(1):循环内核 的 s01 循环形状相同,差别在循环外面挂了什么。ADK 2.0 是例外,它的底层是图引擎,模型驱动的 Agent 只是图里的一种节点,离 LangGraph 最近。
下面这张图对比了同一次建单,三家 SDK 怎么停下来:
- 三家内核一样:模型决定调用什么,SDK 执行工具并回填结果
- OpenAI:工具设 needs_approval,暂停后用 RunState 恢复
- Claude:靠权限规则和 can_use_tool 回调
- ADK:把确认请求本身做成一次函数调用
- 写法不同,结果一样:员工确认之后才建单
OpenAI Agents SDK的细节:
- create_ticket 设了 needs_approval,执行前先查有没有决定
- 没有决定,runner 暂停,待审项放进 result.interruptions
- result.to_state() 转成 RunState,要等很久就 to_json 存起来
- 员工确认或拒绝,对应 state.approve 和 state.reject
- 把 state 交回 Runner.run,这次查到决定,照着执行或拒绝
Claude Agent SDK的细节:
- 模型请求工具,SDK 按权限规则一条条评估
- 两个只读工具写在 allowed_tools 里,直接执行
- create_ticket 不在任何 allow 规则里,最后落到 can_use_tool
- 员工确认,返回 Allow
- 员工拒绝,返回 Deny,message 作为结果回给模型
- 要等很久时改用 PreToolUse 返回 defer,进程不用一直开着
Google ADK的细节:
- 建单工具设了 require_confirmation,调用先暂停
- 事件流里出现一次名为 adk_request_confirmation 的函数调用
- 它是普通的函数调用,可以经 /run 接口从别的渠道回复
- 同一 session 里发回 function_response,确认为真
- 收到确认后才执行 create_ticket
建单前确认的写法对照:
| SDK | 怎么标记要确认的工具 | 暂停后拿到什么 | 怎么恢复 |
|---|---|---|---|
| OpenAI Agents SDK | @function_tool(needs_approval=True) |
result.interruptions |
state.approve/reject 后再 Runner.run(agent, state) |
| Claude Agent SDK | 不写进 allowed_tools,由 can_use_tool 决定 |
回调参数里的工具名和参数 | 回调直接返回 Allow 或 Deny |
| Google ADK | FunctionTool(..., require_confirmation=True) |
名为 adk_request_confirmation 的函数调用 |
同一会话里发回 function_response |
| Strands Agents | hook 里调 event.interrupt() |
result.interrupts |
带 interruptResponse 再调用 Agent |
和自家平台绑得多紧:
| SDK | 默认模型 | 能不能换模型 | 换了会失去什么 |
|---|---|---|---|
| OpenAI Agents SDK | OpenAI | 能,接其他模型的适配器还是 beta | 托管工具、托管 MCP |
| Claude Agent SDK | Claude | 只能换 Claude 的接入渠道 | 不适用,整套就是为 Claude 写的 |
| Google ADK | Gemini | 能,通过 BaseLlm 接口和 LiteLLM |
Gemini 内置工具 |
| Strands Agents | Bedrock 上的模型 | 能,多家模型都是一等支持 | Bedrock 侧的护栏、缓存等配置 |
换模型通常只改一行,真正难换的在模型以外:追踪数据默认发到哪里(OpenAI Traces、Cloud Trace),会话和记忆托管在哪里,部署到哪个平台(Agent Runtime、Bedrock AgentCore)。这些换起来要改架构。
版本说明
本文按 2026-11 初各家的文档写。查阅时的版本:OpenAI Agents SDK 0.24,Claude Agent SDK 0.2(两者都还是 0.x,接口可能变),Google ADK 2.13,Strands Agents 1.64。对照旧资料时注意:
- Claude Agent SDK 原名 Claude Code SDK,2025-09 改名,包名从
claude-code-sdk换成claude-agent-sdk,ClaudeCodeOptions换成ClaudeAgentOptions。 - Google ADK 2.0(Python 版 2026-05 正式发布)把底层从层级执行改成图引擎,1.x 的流程 Agent 写法被 Workflow 取代。
- OpenAI 的可视化搭建工具 Agent Builder 已宣布弃用,官方迁移方向是 Agents SDK。
和手写循环的对照
跟读 learn-claude-code:17 个 harness 机制怎么挂到一个循环上 系列从零写了一个 harness。拿它对照,四家 SDK 替你做的和没做的大致如下。
替你做了的:
- 循环和它的规矩。 跟读 learn-claude-code(1):循环内核 里的三条:没有工具调用就停、每个调用配一个结果、结果交回模型作为下一轮输入。四家都内置了,还都有轮数上限。
- 权限检查。 s03 有硬拒绝、规则、询问三道检查。Claude Agent SDK 扩成了更多步,拒绝规则永远最先生效,道理和 s03 把硬拒绝放最前面一样。另外三家的审批开关只对应"询问"这一道,硬拒绝要自己用护栏或回调写。
- 钩子。 s04 的 PreToolUse、PostToolUse、Stop 在 Claude Agent SDK 里是同名同义的事件;ADK 的回调、Strands 的 hooks 是同类机制。
- 技能和 MCP。 技能、MCP 与集成 里 s07 的技能目录、s14 的
mcp__服务器__工具命名,在 Claude Agent SDK 里原样存在。另外三家把 MCP 当成一种工具来源,没有技能这一层。 - 压缩和子代理。 计划、子代理与上下文预算 里 s06 的子代理和 s08 的压缩,对应 Claude Agent SDK 的子代理和自动压缩、ADK 的事件压缩配置。
没替你做的:
- 副作用只执行一次。 OpenAI 文档直说审批不保证只执行一次,ADK 2.0 会重试节点,Strands 恢复时会重跑 hook。
create_ticket的幂等键仍然是你的事,见 可靠执行:状态、幂等与恢复。 - 确认界面展示什么。 四家只提供暂停、拿到参数、带着决定恢复这套机制。员工看到的是不是真实的金额和发票号,由你的界面决定,见 人在回路。
- 目标检查。 固定编排与目标闸门:Workflow 运行时和 Goal Loop 的 s17 规定模型自己说做完了不算数,四家都没有内置这一层。
- 跨进程的任务和定时调度。 任务图、后台与定时:让工作撑过一轮对话 讲的任务图、后台任务不在 SDK 里,要接 Temporal、DBOS 这类持久执行引擎,或用各家的托管运行时。
自测
这四个 SDK 和 LangGraph 最大的区别是什么?
参考:控制流归谁。LangGraph 的流程由你写的边决定;这四家默认由模型在循环里决定下一步,SDK 只负责执行工具和回填结果。ADK 2.0 是例外,它也提供图式的 Workflow。
OpenAI Agents SDK 里,create_ticket 设了 needs_approval=True,员工批准后服务器重启,恢复时又执行了一次。为什么,怎么防?
参考:审批结果存在 RunState 里,恢复就是从这份状态接着跑。如果重启发生在工具执行之后、新状态保存之前,工具就会再执行。官方也说明批准不保证只执行一次。给 create_ticket 传幂等键,比如员工 ID 加发票号,由工单系统拒绝重复建单。
Claude Agent SDK 里,把 mcp__expense__create_ticket 也写进了 allowed_tools,can_use_tool 里的确认逻辑还会执行吗?
参考:不会。允许规则排在回调之前,命中就直接放行。要确认的工具不能写进 allowed_tools。
Claude Agent SDK 和直接调用 Messages API 写工具循环,最根本的区别是什么?
参考:前者启动一个 Claude Code 子进程,循环、内置工具、权限、hooks、压缩、会话记录都在子进程里,你通过选项和回调配置它;后者只是一次模型调用,循环和工具执行都要自己写,也就是 跟读 learn-claude-code(1):循环内核 那套。前者部署时要按每个会话一个长期进程规划资源。
ADK 2.0 之后,报销流程应该写成一个 Agent 还是一个 Workflow?
参考:看哪些步骤必须固定。查制度、查发票、判断能否报销可以交给模型;"先确认再建单"这种必须遵守的顺序,适合写进 Workflow 的边里,用 RequestInput 节点或 Tool Confirmation 要人工确认。
四家里哪一家换模型代价最小,哪一家最大?换模型之外还有什么会把你绑住?
参考:Strands 把多家模型当一等能力,代价最小;ADK 次之;Claude Agent SDK 只能换 Claude 的接入渠道。真正难换的是追踪后台、托管的会话与记忆、部署平台,以及只在自家模型上可用的托管工具。
参考资料
以下链接均查阅于 2026-11。
- OpenAI Agents SDK 文档首页
- OpenAI Agents SDK:Quickstart
- OpenAI Agents SDK:Human-in-the-loop
- OpenAI Agents SDK:Guardrails
- OpenAI Agents SDK:Sessions
- OpenAI Agents SDK:Tracing
- OpenAI Agents SDK:MCP
- Claude Agent SDK overview
- Claude Agent SDK:How the agent loop works
- Claude Agent SDK:Permissions
- Claude Agent SDK:Handle approvals and user input
- Claude Agent SDK:Hosting
- Claude Agent SDK:Migration guide
- anthropics/claude-agent-sdk-python GitHub
- ADK 文档首页
- Welcome to ADK 2.0
- ADK:Graph-based agent workflows
- ADK:Get action confirmation for ADK Tools
- ADK:Session, State, and Memory
- ADK:Introduction to A2A
- Strands Agents:Agent loop
- Strands Agents:Interrupts
- Strands Agents:Multi-agent patterns
连接
- 智能体编排框架横评:同一个报销 Agent 换六种写法:系列总览,本站代表模型驱动循环这一派。
- 智能体编排框架横评(2):LangGraph:上一站,显式图路线;ADK 2.0 的图引擎可以和它对照着看。
- 智能体编排框架横评(4):多智能体框架:下一站,交接、子代理、A2A 之外的角色团队式多 Agent。
- 主线:一个报销请求走完整个 Agent 系统:报销例子的场景和示意数字来源。
- 跟读 learn-claude-code(1):循环内核:四家共享的循环内核,以及权限、钩子的教学版。
- 技能、MCP 与集成:技能与 MCP 命名空间在 Claude Agent SDK 里的真实形态。
- 计划、子代理与上下文预算:子代理隔离和压缩,对照各家的子 Agent 与压缩配置。
- 任务图、后台与定时:让工作撑过一轮对话:会话落盘和跨进程任务,SDK 只覆盖前一半。
- 人在回路:四种工具审批机制都只负责暂停和恢复,确认内容怎么设计看这里。
- 可靠执行:状态、幂等与恢复:审批后的重放、自动重试都要求建单幂等。
- 记忆:ADK 的 State 与 MemoryService 对应短期和长期记忆。
- 可观测性:追踪一条任务的代价:各家追踪默认发往自家后台,接 OpenTelemetry 时要注意的地方。
- 工具 / 协议:MCP 与 A2A 的面试版问答。
- Coding / Harness:框架和 Harness 的区别,Claude Agent SDK 是偏 Harness 的那一端。