第 5 章 StrandsHooks生命周期
第 5 章:用 Hooks 控制 Agent
第 5 章:用 Hooks 控制 Agent
关于本课
本课程的视频是某个时间点的快照。Strands 一直在活跃开发,所以本页的代码反映的是最新写法,视频里讲的概念依然适用。拿不准的时候,以代码为准。
本课代码:samples/05-hooks
不依赖 Model 的规则
Hooks 在生命周期事件上注入代码(Tool 调用前后、agent loop 前后),不改变 Agent 的逻辑。Tool 由 Model 决定用不用,Hook 不一样,它每次都会自动触发,不管 Model 推理出什么。关键区别就在这里:prompt 是请求,Hook 是保证。
一个失控的循环可能把同一个 Tool 调用几十次。Model 也可能不问一声就去执行破坏性操作。Prompting 是请 Model 好好表现,Hooks 则保证它这么表现。
Hook 架构
- 继承
HookProvider来定义 Hook - 在
register_hooks()里为生命周期事件注册 callback - 多个 Hook 可以监听同一个事件(可叠加)
event.interrupt()暂停循环,等待人工输入event.cancel_tool阻止 Tool 执行,并把原因作为消息回传给 Model
示例:删除操作的人工审批
from strands import Agent, tool
from strands.hooks import BeforeToolCallEvent, HookProvider, HookRegistry
class DeleteApprovalHook(HookProvider):
"""Intercepts delete operations for human approval."""
def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(BeforeToolCallEvent, self.check_delete)
def check_delete(self, event: BeforeToolCallEvent) -> None:
if event.tool_use["name"] != "delete_file":
return
approval = event.interrupt(
"delete-approval",
reason={"path": event.tool_use["input"]["path"]}
)
if approval.lower() != "y":
event.cancel_tool = "User denied file deletion"
agent = Agent(
tools=[list_files, read_file, write_file, delete_file],
hooks=[DeleteApprovalHook()],
)
event.interrupt() 会暂停 Agent 执行,把控制权交回调用方。审批类的工作流就靠它实现:人不回答,Agent 就不往下走。
示例:限制 Tool 调用频率
from strands.hooks import BeforeInvocationEvent, BeforeToolCallEvent, HookProvider, HookRegistry
class LimitToolCounts(HookProvider):
def __init__(self, max_calls: int = 3):
self.max_calls = max_calls
self.counts: dict[str, int] = {}
def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(BeforeInvocationEvent, self.reset)
registry.add_callback(BeforeToolCallEvent, self.check)
def reset(self, event: BeforeInvocationEvent) -> None:
self.counts = {}
def check(self, event: BeforeToolCallEvent) -> None:
name = event.tool_use["name"]
self.counts[name] = self.counts.get(name, 0) + 1
if self.counts[name] > self.max_calls:
event.cancel_tool = (
f"'{name}' hit the {self.max_calls}-call limit. "
"Do NOT call this tool again."
)
agent = Agent(tools=[get_weather], hooks=[LimitToolCounts(max_calls=3)])
注意计数器是在 BeforeInvocationEvent 时重置的。频率限制是每个请求一份,不是整个 Agent 生命周期共用。另外,因为 cancel_tool 会把消息回传给 Model,Model 就知道了这次调用为什么被拦,不会再反复重试。
何时使用 Hooks
- 限制 Tool 的使用频率,防止失控循环
- 破坏性操作前要求人工审批
- 记录每一次 Tool 调用,留作审计轨迹
- 按业务规则校验 Tool 的输入和输出
- 对敏感 Tool 做访问控制
Hooks 不碰 Tool,也不碰 prompt。它是一层独立的、可复用的东西,可以随手加到任何 Agent 上。