strands-agents Python SDK v1.51.0 发布解读:工具重命名、批量钩子、快照会话与模型能力升级
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。需要注意两个语义细节:
- 每个循环触发一次:当逐工具中断(per-tool interrupt)拆分批次时,同一助手消息可能触发多次;
- 逆序回调:
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);cedarpy4.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 的升级路径可以概括为:
- 优先处理工具重命名:
make_bash→make_shell、bash→shell;旧 API 保留至 v2.0.0,但应尽早迁移,避免工具描述与沙箱实际 shell(sh/远程登录 shell)语义不符; - 善用新钩子:用
BeforeToolsEvent/AfterToolsEvent做整批工具调用前的拦截与整批后的end_turn收尾; - 控制上下文成本:为
ContextOffloader配置should_offload选择性卸载,并注意统一 Storage 后端的驱逐行为已与内存后端对齐; - 采用快照会话:新项目优先
SnapshotSessionManager获得崩溃恢复 + 时间旅行检查点; - 升级模型能力认知:Claude 5 / GPT-5.6 系列已有默认上下文窗口限制,Gemini 支持 Tool Choice,
estimateUtilization让上下文管理更可预测。
以上所有能力均可在当前仓库的 strands-py 源码、changelog 目录 与对应测试(strands-py/tests)中进一步验证与学习。