strands-agents Python SDK v1.51.0 发布解读:工具重命名、批量钩子、快照会话与模型能力升级

原创2026-09-27 05:31:421,400 阅读
文章标签:人工智能大模型AI AgentAgent 框架多智能体工具调用MCP 服务

strands-agents Python SDK v1.51.0 发布解读:工具重命名、批量钩子、快照会话与模型能力升级

strands-agents(strands-py)是 harness-sdk 仓库中面向生产级 AI Agent 的 Python SDK,支持任意模型与任意云环境。v1.51.0 是一次以「工具执行链路治理、会话持久化、模型能力对齐」为核心的迭代:其中包含一项破坏性变更(sandbox-routed 的 bash 工具正式更名为 shell),同时新增了工具批量钩子、选择性上下文卸载回调、快照会话管理器、estimateUtilization 模型估算方法、Gemini Tool Choice 支持等多项能力。读完本文,你将掌握 v1.51.0 的升级注意事项、新钩子/新参数的用法,以及这些能力在 strands-py 源码中的具体落点。

本文基于 site/src/content/changelog/sdk/python-v1.51.0.md 的 30 余条 changelog 条目展开,并对照 strands-py 源码进行验证。

破坏性变更:sandbox 路由的 bash 工具更名为 shell

v1.51.0 中唯一的 breaking change 来自 PR 3574:「rename sandbox-routed bash tool to shell」(scope: vended-tools)。此后所有经沙箱执行命令的工具统一命名为 shell,配套的工厂函数也从 make_bash 迁移为 make_shell(PR 3595 将 make_bash 标记为 typing_extensions.deprecated)。

更名原因

从 strands-py/src/strands/vended_tools/_bash.py 的模块注释可以确认:工具实际是通过沙箱路由命令的,而沙箱执行的是 sh(Docker 与本地环境)或 SSH 远程登录 shell,并不特指 bash,旧名称会造成误导。新模块 strands-py/src/strands/vended_tools/shell/shell.py 中的 make_shell 工厂函数签名如下:

def make_shell(
    *,
    sandbox: Sandbox | None = None,
    name: str = "shell",
    description: str = SANDBOX_SHELL_DESCRIPTION,
) -> DecoratedFunctionTool:
  • sandbox:创建时绑定的沙箱;为 None 时在调用期从 tool_context.agent.sandbox 读取;
  • name:工具名,默认 "shell";
  • description:展示给模型的工具描述。

生成的 shell_tool(command, tool_context, timeout=120) 每次调用都在全新 shell 中执行,变量与工作目录不会跨调用保留;命令超时(默认 120 秒)时以 exit_code=124 返回部分输出(见 shell.py)。

迁移路径与兼容策略

  • 新代码请使用 make_shell 与默认 shell 实例;
  • 旧入口 strands.vended_tools.bash 与 make_bash 保留至 v2.0.0,访问时会发出 DeprecationWarning(见 _bash.py);
  • 若你的代码以工具名 "bash" 做匹配,可通过 make_shell(name="bash") 显式保留旧名称;但注意命令运行的是沙箱提供的 sh/登录 shell,不要依赖 bash 专属语法。

新增批量工具钩子:BeforeToolsEvent 与 AfterToolsEvent

PR 3508 为 Python SDK 引入了两个按批次触发的工具生命周期钩子,事件类型定义在 strands-py/src/strands/hooks/events.py:

  • BeforeToolsEvent:当模型返回需要执行的 tool use 块、且尚未执行任何工具时触发一次。回调可设置 cancel 阻止整批工具执行(字符串将作为工具结果错误信息,True 使用默认消息)。它同时实现 _Interruptible,每个中断都有形如 v1:before_tools:{uuid5} 的稳定 ID。
  • AfterToolsEvent:当整批工具结果收集完毕、准备写入会话时触发。回调可设置 end_turn 让 Agent 循环在本批工具后直接终止(不再调用模型):字符串成为最终助手文本,list[ContentBlock] 成为最终助手消息内容,True 使用默认消息,此时 stop_reason 为 "end_turn"。

两个事件都携带 message(前者的助手消息含 tool use 请求,后者的用户角色消息含工具结果)与 invocation_state。需要注意两个语义细节:

  1. 每个循环触发一次:当逐工具中断(per-tool interrupt)拆分批次时,同一助手消息可能触发多次;
  2. 逆序回调:AfterToolsEvent.should_reverse_callbacks 为 True,后注册的回调先执行,适合做清理类操作。

与既有单工具钩子(BeforeToolCallEvent / AfterToolCallEvent,见 events.py)相比,这两个新钩子让你可以整批地做鉴权、拦截、审计或提前终止,无需逐工具处理。

上下文卸载增强:should_offload 选择性卸载与驱逐周期刷新

ContextOffloader 插件负责把超大的工具结果转存到存储后端、用截断预览 + 引用替换上下文内容。v1.51.0 带来两项改进:

should_offload 回调(PR 3267)

插件新增 should_offload 参数,用于按工具名选择性卸载。回调协议定义在 plugin.py:

class ShouldOffload(Protocol):
    def __call__(self, tool_name: str, token_count: int, **kwargs: Any) -> bool | Awaitable[bool]: ...
  • tool_name:产生结果的工具名;
  • token_count:结果的估算 token 数(由 Agent 的 model.count_tokens 计算);
  • 返回 True 卸载、False 保留在上下文中;同步/异步均可,且要求实现接受 **kwargs 以向前兼容;
  • 该回调只在结果超过 max_result_tokens(默认 2,500)时被调用;回调抛异常时回退为默认卸载并记录 warning(见 plugin.py)。

典型用法(摘自 plugin.py 文档示例):

from strands import Agent
from strands.vended_plugins.context_offloader import ContextOffloader, InMemoryStorage

# 只卸载超大文档类工具的结果
agent = Agent(plugins=[
    ContextOffloader(
        storage=InMemoryStorage(),
        should_offload=lambda tool_name, token_count, **kwargs: (
            tool_name == "get_document_text"
        ),
    )
])

retrieve 时刷新驱逐周期(PR 3487)

针对统一 Storage 后端,修复了「按存储时的 cycle 计算驱逐、导致活跃检索的条目提前被删」的问题。现在 retrieve_offloaded_content 每次成功检索都会刷新该引用的驱逐计时(见 plugin.py 的 _refresh_eviction_cycle),与 InMemoryStorage.retrieve 的 last-access 刷新行为对齐——被持续读取的内容不会在 evict_after_cycles(默认 20 个循环)后被误删。

其余核心参数一并列出(见 plugin.py):

参数 默认值 说明
storage None 统一 Storage(strands.storage)或旧式 offloader Storage;None 时从 Agent 级 storage 解析,最终回退内存存储
max_result_tokens 2,500 超过该 token 阈值即卸载
preview_tokens 1,000 上下文中保留的文本预览 token 数
include_retrieval_tool True 是否注册 retrieve_offloaded_content 工具
should_offload None 选择性卸载回调
evict_after_cycles 20 统一 Storage 的条目驱逐周期(None 禁用)

文本/JSON 按块转存,图片与文档保留原生格式并替换为占位符;preview_tokens >= max_result_tokens 等非法组合会直接抛 ValueError。

快照会话管理器:SnapshotSessionManager 落地 Python

PR 3283 将快照式会话管理引入 Python SDK。与逐条持久化消息的 RepositorySessionManager 不同,SnapshotSessionManager 把整个 Agent 序列化为单个版本化 Snapshot blob,是官方推荐的新建 Agent 会话方案。核心实现在 strands-py/src/strands/session/snapshot_session_manager.py。

双轨持久化模型

  • snapshot_latest.json(可变):每次符合策略的生命周期事件后覆盖写入,用于崩溃/重启恢复;
  • immutable_history/snapshot_<uuid7>.json(只读追加):当 snapshot_trigger 回调返回 True 时写入,实现时间旅行式检查点——可恢复到任意历史状态,而非仅最新状态。

存储键布局(相对 session 命名空间,见 snapshot_session_manager.py):

<session_id>/scopes/agent/<agent_id>/snapshots/
  snapshot_latest.json
  immutable_history/snapshot_<id>.json

核心参数与策略

from strands import Agent
from strands.session import SnapshotSessionManager
from strands.storage import LocalFileStorage

session = SnapshotSessionManager(
    "my-session",
    storage=LocalFileStorage(),
    save_latest_on="invocation",          # message | invocation | trigger
    multi_agent_save_latest_on="node",    # node | invocation
    snapshot_trigger=my_trigger,          # 每次 invocation 后回调,返回 True 追加不可变快照
)
agent = Agent(session_manager=session)
  • save_latest_on:"invocation"(默认,每次调用结束后保存,兼顾持久性与 I/O)、"message"(每条消息后保存,最持久但 I/O 最高)、"trigger"(仅 snapshot_trigger 触发时保存,见 snapshot_session_manager.py);
  • multi_agent_save_latest_on:Graph/Swarm 编排器仅保存最新(无不可变历史):"node"(默认,每节点完成后保存,崩溃从最后节点续跑)或 "invocation"(整个编排结束后保存,I/O 更低);
  • snapshot_trigger:每次调用完成后判定是否追加不可变检查点;也可随时用 save_snapshot(agent, is_latest=False) 强制生成检查点。

值得注意的实现细节

  • 自动恢复:Agent 初始化时自动从 snapshot_latest 恢复;若 Agent 已有消息会被覆盖,会记录 warning;
  • 有状态模型处理:恢复后若模型是 stateful(历史在服务端维护),会丢弃恢复的消息、只保留 model_state,避免与服务器视图漂移(见 snapshot_session_manager.py);
  • guardrail 红名单立即落盘:redact_latest_message 在任何策略(含 "trigger")下都会立即刷新,脱敏前内容绝不落盘——这与 TypeScript SDK 在 "trigger" 下不刷新的行为不同(见 snapshot_session_manager.py);
  • BidiAgent 不支持:对双向流式 Agent 附加该管理器会抛 NotImplementedError,需改用消息日志型会话管理器;
  • stash 集成:持久型 stash 存外部引用,临时型(如 InMemoryStorage)则内联序列化条目,保证恢复后上下文一致(见 snapshot_session_manager.py)。

时间旅行 API 包括 list_snapshot_ids(agent)、restore_snapshot(agent, snapshot_id=...) 与 delete_session(),快照 ID 为 UUIDv7 且按时间排序。

模型层更新:上下文窗口、估算方法与 Gemini Tool Choice

新增 Claude 5 与 GPT-5.6 家族上下文窗口限制(PR 3629)

模型默认值表 strands-py/src/strands/models/_defaults.py 新增了最新模型家族的上下文窗口上限,例如:

"gpt-5.6": 1_050_000,
"gpt-5.6-sol": 1_050_000,
"gpt-5.6-terra": 1_050_000,
"gpt-5.6-luna": 1_050_000,
"gpt-5.5": 1_050_000,
"gpt-5.4-mini": 272_000,
"gpt-5.4-nano": 272_000,
"gpt-5-pro": 128_000,

Claude 5 家族同样获得对应上限。这些值会被 Agent 循环用于上下文管理(如触发压缩/截断策略),确保在超大窗口模型上也能正确估算与规划。

Model 基类新增 estimateUtilization 方法(PR 3641)

Model 基类新增 estimateUtilization 方法(见 strands-py/src/strands/models/model.py),用于在调用前估算上下文利用率,与 context_manager.py 的上下文管理策略配合,为 proactive 的压缩/卸载决策提供统一入口。

Python 端支持 Gemini Tool Choice(PR 3551)

ToolChoice 类型现在可应用于 Gemini 模型。在 Anthropic 适配层(anthropic.py)可以看到 tool_choice 的映射语义:{"any": {}} → {"type": "any"}、{"auto": {}} → {"type": "auto"}、{"tool": {"name": ...}} → 强制指定工具;强制指定工具调用时还会省略 server tools 以避免冲突(见 anthropic.py)。不支持的 provider 会通过 strands-py/src/strands/models/_validation.py 的 warn_on_tool_choice_not_supported 发出警告而非静默忽略。

其它模型/干预修复

  • Bedrock guardContent 跳过不支持的图片格式(PR 3607):避免对模型不支持的图片格式错误地包一层 guardContent,见 strands-py/src/strands/models/bedrock.py;
  • Bedrock KB 默认区域解析(PR 3583):store 的客户端可在云环境解析默认 region,见 strands-py/src/strands 下的 bedrock-kb 相关实现。

HITL 与多智能体:风险分类、A2A 中断与 swarm 恢复

HITL 增加 LLM 驱动风险分类(PR 3575)

hitl-py 的干预(interventions)新增 classifier 选项,允许用 LLM 对工具调用/用户输入做风险分级,而非依赖纯规则。相关干预原语位于 strands-py/src/strands/interventions,配合 BeforeToolCallEvent/BeforeToolsEvent 钩子可实现「先分级、再放行/拦截」的生产级 HITL 流程。

A2A 支持中断往返(PR 3486)

scope: a2a 新增 interrupt round trip:通过 A2A 协议调用 Agent 时,中断(interrupt)可以在调用方与被调用方之间完整往返传递,见 strands-py/src/strands/multiagent/a2a/executor.py。

Swarm 崩溃-重启恢复修复(PR 3391)

修复了 handoff 与已完成(completed)swarm 的 crash-restart 恢复问题——此前这两类 swarm 在崩溃重启后无法正确续跑。swarm 实现位于 strands-py/src/strands/multiagent/。

MultiAgentResult 与 NodeResult 支持 str(PR 1998)

多智能体结果对象新增 __str__ 支持(见 strands-py/src/strands/multiagent/base.py),开发者在日志、调试与 REPL 中可直接打印结构化的多智能体结果,无需手动解析字段。

遥测、MCP 与其它修复

OpenTelemetry 语义化

  • execute_tool span 记录 gen_ai.tool.call.arguments/result(PR 3550):工具执行的 span 现在携带标准化的 gen_ai.tool.call.arguments 与 gen_ai.tool.call.result 属性,便于按 GenAI 语义做可观测性与成本归因;
  • 钩子重试的模型调用计入用量(PR 3627):通过 AfterModelCallEvent.retry 重试的调用,其 token 用量现在正确累计,不再漏计;
  • bidi 增加 telemetry 与 metrics(PR 3282):双向流式(bidi)通道补齐了 OTel 指标。

MCP 性能与稳定性

  • 搜索片段并发水合(PR 3435):hydrate search snippets concurrently,在 async/MCP 场景下并发水合文档搜索片段,降低延迟;
  • 水合时索引文档内容并保证线程安全(PR 3502):index document content on hydration with thread safety;
  • MCP 依赖锁定在 v2.0.0 以下(PR 3524):pin mcp dependency below v2.0.0,避免上游破坏性升级;
  • 测试改为等待 retained task 而非日志行(PR 3537):让 MCP 并发测试更稳定。

依赖与工程维护

  • litellm 要求更新为 >=1.75.9,<=1.95.0(PR 3587、3649);
  • cedarpy 4.8.6 → 4.8.7(PR 3214),用于 Cedar 授权策略解析;
  • AgentStreamStage / AgentStreamContext 类型新增(PR 3635),为流式中间件提供类型化阶段与上下文;
  • 社区集成目录(catalog)支持搜索、过滤与策划回填(PR 3416),相关代码见 site/src/util/catalog.ts;
  • 设计文档模板更新为「尽早暴露关键信息」(PR 3532)、注释规范扩展(PR 3676)等流程类改进。

升级建议与小结

对本仓库用户而言,v1.51.0 的升级路径可以概括为:

  1. 优先处理工具重命名:make_bash → make_shell、bash → shell;旧 API 保留至 v2.0.0,但应尽早迁移,避免工具描述与沙箱实际 shell(sh/远程登录 shell)语义不符;
  2. 善用新钩子:用 BeforeToolsEvent/AfterToolsEvent 做整批工具调用前的拦截与整批后的 end_turn 收尾;
  3. 控制上下文成本:为 ContextOffloader 配置 should_offload 选择性卸载,并注意统一 Storage 后端的驱逐行为已与内存后端对齐;
  4. 采用快照会话:新项目优先 SnapshotSessionManager 获得崩溃恢复 + 时间旅行检查点;
  5. 升级模型能力认知:Claude 5 / GPT-5.6 系列已有默认上下文窗口限制,Gemini 支持 Tool Choice,estimateUtilization 让上下文管理更可预测。

以上所有能力均可在当前仓库的 strands-py 源码、changelog 目录 与对应测试(strands-py/tests)中进一步验证与学习。

登录后查看全文
harness-sdk