# Hooks 系统
## 概述
Hooks 允许在 Agent 生命周期的关键节点注入自定义行为。框架提供三套独立的 Hook 系统:
1. **Skills Hooks** — 主 Hook 系统,支持 20 个事件和 5 种动作类型
2. **Task Hooks** — DAG 任务执行的生命周期回调
3. **Subagent Hooks** — 子代理调度的生命周期回调
---
## Skills Hooks
主 Hook 系统。通过 YAML 配置(`echo-agent.yaml` 或 SKILL.md frontmatter),由 `HookExecutor` 执行。
### Hook 事件
| `PreToolUse` | 工具执行前 | 输入、权限(允许/阻止) |
| `PostToolUse` | 工具成功后 | 输出、继续行为 |
| `PostToolUseFailure` | 工具失败后 | 错误反馈 |
| `PermissionRequest` | 权限对话框出现时 | 自动批准/拒绝 |
| `PermissionDenied` | 权限被拒绝时 | 重试信号 |
| `SessionStart` | 会话开始或恢复时 | 上下文注入 |
| `SessionEnd` | 会话终止时 | 清理 |
| `Stop` | Agent 完成响应时 | 继续原因 |
| `StopFailure` | Agent 遇到不可恢复错误时 | 告警/恢复 |
| `Notification` | Agent 需要用户注意时 | 权限快捷方式 |
| `UserPromptSubmit` | 用户提交 prompt 时 | 上下文注入、阻止 |
| `PreCompact` | 上下文压缩前 | 上下文注入 |
| `PostCompact` | 上下文压缩后 | 上下文注入 |
| `ConfigChange` | 配置文件变更时 | 阻止/重载 |
| `InstructionsLoaded` | 技能/指令加载后 | 加载后验证 |
| `PostToolBatch` | 并行工具调用批次完成后 | 聚合 |
| `SubagentStart` | 子代理调度前 | 上下文注入 |
| `SubagentStop` | 子代理完成后 | 结果注入 |
| `TaskCreated` | 任务创建/调度时 | 上下文注入 |
| `TaskCompleted` | 任务完成时 | 结果注入 |
### Hook 类型
| `command` | 执行 shell 命令;stdin 接收 JSON 上下文 |
| `prompt` | 为 LLM 注入提示消息 |
| `permission` | 直接返回权限决策(allow/deny/ask) |
| `http` | POST 事件数据到 URL,解析响应 |
| `mcp_tool` | 调用 MCP 服务器工具 |
### YAML 配置
```yaml
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "${SKILL_DIR}/validate.sh"
timeout: 5
- matcher: "Write"
hooks:
- type: prompt
prompt: "写入前检查文件权限"
PostToolUse:
- matcher: "Edit|Write"
hooks:
- type: command
command: "jq -r '.tool_input.file_path' | xargs prettier --write"
Stop:
- hooks:
- type: command
command: "osascript -e 'display notification \"完成\"'"
SessionStart:
- matcher: "startup"
hooks:
- type: prompt
prompt: "记住使用 bun,不要用 npm。"
PermissionRequest:
- matcher: "shell"
hooks:
- type: permission
decision: "allow"
```
### 匹配模式
`matcher` 字段过滤哪些工具/事件触发 Hook:
- `"Bash"` — 精确匹配工具名
- `"Edit|Write"` — 正则交替(匹配 Edit 或 Write)
- `"*"` 或省略 matcher — 匹配所有事件
- `"startup"` — 匹配 SessionStart 中的上下文关键词
### Command Hook 上下文
Command Hook 通过 stdin 接收 JSON:
```json
{
"event": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "ls -la" },
"session_id": "abc123",
"timestamp": "2026-05-29T10:30:00Z"
}
```
命令的 stdout 被解析为 `HookResult`:
```json
{
"decision": "allow",
"modified_input": { "command": "ls -la --color=never" },
"message": "已修改命令以禁用颜色"
}
```
### 安全限制
| 默认超时 | 10 秒 | 防止 Hook 挂起 |
| 最大超时 | 300 秒 | 硬上限 |
| 最大命令长度 | 32 KB | 防止畸形 YAML 滥用 |
| 沙箱执行 | 可选 | Hook 可在沙箱内运行 |
---
## Task Hooks
DAG 任务执行的生命周期回调。实现 `TaskHooks` trait。
### Trait 定义
```rust
use async_trait::async_trait;
use echo_orchestration::tasks::{RetryDecision, TaskHookContext, TaskHooks};
struct LoggingHooks;
#[async_trait]
impl TaskHooks for LoggingHooks {
async fn before_execute(&self, ctx: &TaskHookContext) {
println!("开始任务: {}", ctx.task.subject);
}
async fn after_execute(&self, ctx: &TaskHookContext, result: &str) {
println!("完成: {} -> {}", ctx.task.subject, result);
}
async fn on_failure(&self, ctx: &TaskHookContext, error: &str) -> RetryDecision {
if ctx.task.retry_count < ctx.task.max_retries {
RetryDecision::Retry { delay_secs: 1 }
} else {
RetryDecision::Fail
}
}
}
```
### Hook 上下文
```rust
pub struct TaskHookContext {
pub task: Task, // 正在执行的任务
pub attempt: u32, // 当前尝试次数(从 1 开始)
pub executor: Option<String>, // 执行任务的 Agent
}
```
### 重试决策
| `Retry { delay_secs }` | 延迟后重新执行 |
| `Skip` | 跳过任务,继续 DAG |
| `Fail` | 标记任务为失败 |
---
## Subagent Hooks
子代理调度的生命周期回调。实现 `SubagentHooks` trait。
### Trait 定义
```rust
use async_trait::async_trait;
use echo_agent::subagent::{SubagentHooks, SubagentHookContext, SubagentRetryDecision, SubagentResult};
struct MySubagentHooks;
#[async_trait]
impl SubagentHooks for MySubagentHooks {
async fn before_dispatch(&self, ctx: &SubagentHookContext) {
println!("调度到: {}", ctx.subagent_name);
}
async fn after_dispatch(&self, ctx: &SubagentHookContext, result: &SubagentResult) {
println!("完成: {}", ctx.subagent_name);
}
async fn on_failure(&self, ctx: &SubagentHookContext, error: &str) -> SubagentRetryDecision {
SubagentRetryDecision::Retry { delay_secs: 2 }
}
}
```
### Hook 上下文
```rust
pub struct SubagentHookContext {
pub parent_agent: String, // 父 Agent 名称
pub subagent_name: String, // 被调度的子代理
pub execution_mode: ExecutionMode, // Sync/Fork/Teammate
pub task: String, // 被调度的任务
pub attempt: u32, // 当前尝试次数(从 1 开始)
}
```
### 重试决策
| `Retry { delay_secs }` | 延迟后重新调度 |
| `Fail` | 向父 Agent 传播错误 |
| `Delegate { alternative_agent }` | 调度到不同的子代理 |
---
## 组合使用
三套 Hook 系统可以同时使用:
```rust
let agent = ReactAgentBuilder::new()
.model("qwen3-max")
.with_task_hooks(Arc::new(LoggingHooks))
.with_subagent_hooks(Arc::new(MySubagentHooks))
.build()?;
```
Skills Hooks 通过 YAML 配置,从 `echo-agent.yaml` 或 SKILL.md 文件自动加载。