Agent SDK
Hooks 与生命周期回调
按事件注册回调,记录工具调用,并处理超时、输出和应用回执。
在执行节点接入宿主逻辑
Hooks 适合记录工具动作、补充运行上下文、检查执行条件。它们在运行时声明的事件上触发,和最终结果回调不同。先选事件,再选匹配范围,最后实现回调。
常见事件包括 PreToolUse、PostToolUse、PostToolUseFailure、UserPromptSubmit、Stop、SessionStart 和 SessionEnd。公开常量位于 hook 包;运行时是否触发特定事件仍取决于实际支持,不要仅根据常量存在来启用产品承诺。
添加工具审计回调
options = options.AddHookMatcher(hook.EventPostToolUse, hook.Matcher{
Matcher: "Write|Edit",
Timeout: 5 * time.Second,
Hooks: []hook.Callback{
func(ctx context.Context, input hook.Input, toolUseID string) (hook.Output, error) {
if err := ctx.Err(); err != nil { return hook.Output{}, err }
log.Printf("tool=%s tool_use_id=%s session=%s", input.ToolName, toolUseID, input.SessionID)
return hook.Output{}, nil
},
},
})
片段使用 hook、context、time、log 包。它只记录关联身份,不打印整个工具输入。接入持久审计时,再记录经过筛选的路径、动作和状态。避免把文件内容、令牌或业务数据写入普通日志。
理解输入和输出
hook.Input 提供事件相关的类型化字段,例如 ToolName、ToolInput、ToolResponse、SessionID、CWD。不是每个事件都有这些值:工具事件才应读取工具字段,生命周期事件则按其事件含义处理。
空 hook.Output{} 表示不附加修改。需要影响运行时行为时,可使用 Continue、StopReason、SpecificOutput 等字段。部分布尔字段是指针,用于区分“不设置”和“明确 false”。不要用零值猜测协议是否会收到字段。
SpecificOutput 和 RawSpecificOutput 承载事件专属结果。不同事件允许的字段不同;应以 hook 类型及对应运行时契约为准,不要把工具前置事件的响应复用到 Stop 事件。
超时与取消
回调在执行链路中。把长网络调用放进 Hook 会增加用户等待时间;使用传入的 context,外部请求也应继承它。设置 Matcher.Timeout 后仍需让你的回调代码响应取消,不要创建脱离上下文的后台写操作。
需要保证的业务审计应由宿主持久化,不应只依赖进程退出时的 SessionEnd。进程崩溃、系统强杀时,正常生命周期回调不一定有机会执行。
知道响应何时被应用
Output.OnApplied 表示 Hook 响应被运行时应用后的回执入口,与“Go 回调已返回”不同。使用前检查 CapabilityHookResponseAck。未协商时不能把本地返回时间当作运行时已采纳结果的时间。
回执适合关联状态变更,不宜作为新的长任务执行入口。保留 request ID 和 tool use ID,便于排查重复响应或取消竞态。
Hooks 与权限如何配合
权限回调负责未决审批,Hook 负责生命周期节点逻辑。若宿主要求每次写入都经过业务检查,应明确事件、运行时行为和失败处理,而不是仅添加一条“请不要写入”的系统提示。最终隔离仍依赖进程与工具边界。