← 回到知识图谱
实践持续完善

智能体编排框架横评(3):厂商 Agent SDK

本文总结OpenAI Agents SDK、Claude Agent SDK、Google ADK、AWS Strands 入门:每家从安装和第一个 Agent 讲起,再给报销例子加上建单前的确认,最后比较它们和自家平台绑得多紧。

创建 2026-11-05更新 2026-11-06阅读约 39 分钟
Agent框架OpenAI Agents SDKClaude Agent SDKGoogle ADKStrands Agents

智能体编排框架横评 第 3 站 · 上一站:LangGraph · 下一站:多智能体框架 · 全程:总览

这一站讲什么

上一站的 LangGraph 要你先画图,流程怎么走写在代码里。这一站的四个 SDK 不用画图:你给模型一组工具和一段指令,模型自己决定下一步做什么,SDK 负责执行工具、把结果交回模型、在该停的地方停下。

这四个 SDK 都出自模型厂商或云厂商:

SDK 出品方 一句话
OpenAI Agents SDK OpenAI 原语很少的 Agent 库,带审批、交接和追踪
Claude Agent SDK Anthropic 把 Claude Code 整个搬进你的程序,可编程的 harness
Google ADK Google 多语言 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)

流程是这样的:

  1. 模型要调用 create_ticket,运行暂停,待批准的调用放在 result.interruptions 里;
  2. 用 result.to_state() 拿到这次运行的状态,逐个 approve 或 reject;
  3. 把状态交回 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 怎么停下来可缩放的动画图,启用 JavaScript 后可以播放和放大。
  1. 三家内核一样:模型决定调用什么,SDK 执行工具并回填结果
  2. OpenAI:工具设 needs_approval,暂停后用 RunState 恢复
  3. Claude:靠权限规则和 can_use_tool 回调
  4. ADK:把确认请求本身做成一次函数调用
  5. 写法不同,结果一样:员工确认之后才建单

OpenAI Agents SDK的细节:

  1. create_ticket 设了 needs_approval,执行前先查有没有决定
  2. 没有决定,runner 暂停,待审项放进 result.interruptions
  3. result.to_state() 转成 RunState,要等很久就 to_json 存起来
  4. 员工确认或拒绝,对应 state.approve 和 state.reject
  5. 把 state 交回 Runner.run,这次查到决定,照着执行或拒绝

Claude Agent SDK的细节:

  1. 模型请求工具,SDK 按权限规则一条条评估
  2. 两个只读工具写在 allowed_tools 里,直接执行
  3. create_ticket 不在任何 allow 规则里,最后落到 can_use_tool
  4. 员工确认,返回 Allow
  5. 员工拒绝,返回 Deny,message 作为结果回给模型
  6. 要等很久时改用 PreToolUse 返回 defer,进程不用一直开着

Google ADK的细节:

  1. 建单工具设了 require_confirmation,调用先暂停
  2. 事件流里出现一次名为 adk_request_confirmation 的函数调用
  3. 它是普通的函数调用,可以经 /run 接口从别的渠道回复
  4. 同一 session 里发回 function_response,确认为真
  5. 收到确认后才执行 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 的事件压缩配置。

没替你做的:

自测

这四个 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。

连接

这是一篇可以继续生长的笔记。沿着关系继续阅读 ↗