docs(events): align hook design with runtime observation
Mark the original hook design as an early record and update observer examples to pkg/events runtime events and hook.runtime_event. Validation: make lint
This commit is contained in:
parent
b2249df3ea
commit
fce800414d
1 changed files with 34 additions and 29 deletions
|
|
@ -1,11 +1,15 @@
|
||||||
# PicoClaw Hook 系统设计(基于 `refactor/agent`)
|
# PicoClaw Hook 系统设计(基于 `refactor/agent`)
|
||||||
|
|
||||||
|
> 当前状态:本文是 hook 系统的早期设计记录。事件系统升级后,观察型 hook 的主路径已经切到
|
||||||
|
> `pkg/events.Event`、`RuntimeEventObserver` 和进程 hook 的 `hook.runtime_event`。
|
||||||
|
> 文中提到的 `agent.Event`、`EventKind`、`hook.event` 只代表迁移期兼容层,不应作为新代码接口。
|
||||||
|
|
||||||
## 背景
|
## 背景
|
||||||
|
|
||||||
本设计围绕两个议题展开:
|
本设计围绕两个议题展开:
|
||||||
|
|
||||||
- `#1316`:把 agent loop 重构为事件驱动、可中断、可追加、可观测
|
- `#1316`:把 agent loop 重构为事件驱动、可中断、可追加、可观测
|
||||||
- `#1796`:在 EventBus 稳定后,把 hooks 设计为 EventBus 的 consumer,而不是重新发明一套事件模型
|
- `#1796`:在 runtime event bus 稳定后,把 hooks 设计为事件 consumer,而不是重新发明一套事件模型
|
||||||
|
|
||||||
当前分支已经完成了第一步里的“事件系统基础”,但还没有真正的 hook 挂载层。因此这里的目标不是重新设计 event,而是在已有实现上补出一层可扩展、可拦截、可外挂的 HookManager。
|
当前分支已经完成了第一步里的“事件系统基础”,但还没有真正的 hook 挂载层。因此这里的目标不是重新设计 event,而是在已有实现上补出一层可扩展、可拦截、可外挂的 HookManager。
|
||||||
|
|
||||||
|
|
@ -52,8 +56,9 @@ pi-mono 的核心思路更接近当前分支:
|
||||||
|
|
||||||
当前分支已经具备 hook 系统的地基:
|
当前分支已经具备 hook 系统的地基:
|
||||||
|
|
||||||
- `pkg/agent/events.go` 定义了稳定的 `EventKind`、`EventMeta` 和 payload
|
- `pkg/events` 定义 runtime event envelope、kind、scope、source、severity 和 fan-out bus
|
||||||
- `pkg/agent/eventbus.go` 提供了非阻塞 fan-out 的 `EventBus`
|
- `pkg/agent/event_payloads.go` 保留 agent domain payload
|
||||||
|
- `pkg/agent/eventbus.go` 只作为迁移期兼容层存在
|
||||||
- `pkg/agent/loop.go` 中的 `runTurn()` 已在 turn、llm、tool、interrupt、follow-up、summary 等节点发射事件
|
- `pkg/agent/loop.go` 中的 `runTurn()` 已在 turn、llm、tool、interrupt、follow-up、summary 等节点发射事件
|
||||||
- `pkg/agent/steering.go` 已支持 steering、graceful interrupt、hard abort
|
- `pkg/agent/steering.go` 已支持 steering、graceful interrupt、hard abort
|
||||||
- `pkg/agent/turn.go` 已维护 turn phase、恢复点、active turn、abort 状态
|
- `pkg/agent/turn.go` 已维护 turn phase、恢复点、active turn、abort 状态
|
||||||
|
|
@ -62,7 +67,7 @@ pi-mono 的核心思路更接近当前分支:
|
||||||
|
|
||||||
当前分支还缺四件事:
|
当前分支还缺四件事:
|
||||||
|
|
||||||
- 没有 HookManager,只有 EventBus
|
- 没有 HookManager,只有旧 agent EventBus
|
||||||
- 没有 Before/After LLM、Before/After Tool 这种同步拦截点
|
- 没有 Before/After LLM、Before/After Tool 这种同步拦截点
|
||||||
- 没有审批型 hook
|
- 没有审批型 hook
|
||||||
- 子 agent 仍走 `pkg/tools/SubagentManager + RunToolLoop`,没有接入 `pkg/agent` 的 turn tree 和事件流
|
- 子 agent 仍走 `pkg/tools/SubagentManager + RunToolLoop`,没有接入 `pkg/agent` 的 turn tree 和事件流
|
||||||
|
|
@ -73,19 +78,19 @@ pi-mono 的核心思路更接近当前分支:
|
||||||
|
|
||||||
## 设计原则
|
## 设计原则
|
||||||
|
|
||||||
- Hook 必须建立在 `pkg/agent` 的 EventBus 和 turn 上下文之上
|
- Hook 必须建立在 `pkg/events` runtime event bus 和 turn 上下文之上
|
||||||
- EventBus 负责广播,HookManager 负责拦截,两者职责分离
|
- runtime event bus 负责广播,HookManager 负责拦截,两者职责分离
|
||||||
- 项目内挂载要简单,项目外挂载必须走 IPC
|
- 项目内挂载要简单,项目外挂载必须走 IPC
|
||||||
- 观察型 hook 不能阻塞 loop;拦截型 hook 必须有超时
|
- 观察型 hook 不能阻塞 loop;拦截型 hook 必须有超时
|
||||||
- 先覆盖主 turn,不把 sub-turn 一次做满
|
- 先覆盖主 turn,不把 sub-turn 一次做满
|
||||||
- 不新增第二套用户事件命名系统,优先复用 `EventKind.String()`
|
- 不新增第二套用户事件命名系统,新观察点统一使用 `pkg/events.Kind`
|
||||||
|
|
||||||
## 总体架构
|
## 总体架构
|
||||||
|
|
||||||
分成三层:
|
分成三层:
|
||||||
|
|
||||||
1. `EventBus`
|
1. `pkg/events` runtime event bus
|
||||||
负责广播只读事件,现有实现直接复用
|
负责广播只读事件,覆盖 agent、channel、gateway、bus、MCP 等运行时组件
|
||||||
|
|
||||||
2. `HookManager`
|
2. `HookManager`
|
||||||
负责管理 hook、排序、超时、错误隔离,并在 `runTurn()` 的明确检查点执行同步拦截
|
负责管理 hook、排序、超时、错误隔离,并在 `runTurn()` 的明确检查点执行同步拦截
|
||||||
|
|
@ -97,7 +102,7 @@ pi-mono 的核心思路更接近当前分支:
|
||||||
|
|
||||||
换句话说:
|
换句话说:
|
||||||
|
|
||||||
- EventBus 是“发生了什么”
|
- runtime event bus 是“发生了什么”
|
||||||
- HookManager 是“谁能介入”
|
- HookManager 是“谁能介入”
|
||||||
- HookMount 是“这些 hook 从哪里来”
|
- HookMount 是“这些 hook 从哪里来”
|
||||||
|
|
||||||
|
|
@ -113,11 +118,11 @@ pi-mono 的核心思路更接近当前分支:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type EventObserver interface {
|
type EventObserver interface {
|
||||||
OnEvent(ctx context.Context, evt agent.Event) error
|
OnRuntimeEvent(ctx context.Context, evt events.Event) error
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这类 hook 直接订阅 EventBus 即可。
|
这类 hook 直接订阅 runtime event bus 即可。旧 `OnEvent(ctx, agent.Event)` 仅用于迁移期兼容。
|
||||||
|
|
||||||
适用场景:
|
适用场景:
|
||||||
|
|
||||||
|
|
@ -156,7 +161,7 @@ type ToolApprover interface {
|
||||||
|
|
||||||
## 对外暴露的最小 hook 面
|
## 对外暴露的最小 hook 面
|
||||||
|
|
||||||
V1 不需要把所有 EventKind 都变成可拦截点。
|
V1 不需要把所有 runtime event kind 都变成可拦截点。
|
||||||
|
|
||||||
建议只开放这些同步 hook:
|
建议只开放这些同步 hook:
|
||||||
|
|
||||||
|
|
@ -168,19 +173,19 @@ V1 不需要把所有 EventKind 都变成可拦截点。
|
||||||
|
|
||||||
其余节点继续作为只读事件暴露:
|
其余节点继续作为只读事件暴露:
|
||||||
|
|
||||||
- `turn_start`
|
- `agent.turn.start`
|
||||||
- `turn_end`
|
- `agent.turn.end`
|
||||||
- `llm_request`
|
- `agent.llm.request`
|
||||||
- `llm_response`
|
- `agent.llm.response`
|
||||||
- `tool_exec_start`
|
- `agent.tool.exec_start`
|
||||||
- `tool_exec_end`
|
- `agent.tool.exec_end`
|
||||||
- `tool_exec_skipped`
|
- `agent.tool.exec_skipped`
|
||||||
- `steering_injected`
|
- `agent.steering.injected`
|
||||||
- `follow_up_queued`
|
- `agent.follow_up.queued`
|
||||||
- `interrupt_received`
|
- `agent.interrupt.received`
|
||||||
- `context_compress`
|
- `agent.context.compress`
|
||||||
- `session_summarize`
|
- `agent.session.summarize`
|
||||||
- `error`
|
- `agent.error`
|
||||||
|
|
||||||
`subturn_*` 在 V1 中保留名字,但不承诺一定触发,直到子 turn 迁移完成。
|
`subturn_*` 在 V1 中保留名字,但不承诺一定触发,直到子 turn 迁移完成。
|
||||||
|
|
||||||
|
|
@ -369,7 +374,7 @@ PicoClaw 启动外部进程,并在其 stdin/stdout 上跑协议。
|
||||||
### 观察链路
|
### 观察链路
|
||||||
|
|
||||||
```text
|
```text
|
||||||
runTurn() -> emitEvent() -> EventBus -> observers
|
runTurn() -> emitEvent() -> runtime event bus -> observers
|
||||||
```
|
```
|
||||||
|
|
||||||
### 拦截链路
|
### 拦截链路
|
||||||
|
|
@ -464,13 +469,13 @@ V1 不做复杂自动发现。
|
||||||
|
|
||||||
最适合 PicoClaw 当前分支的方案,不是直接复制 OpenClaw 的 hooks,也不是完整照搬 pi-mono 的 extension system,而是:
|
最适合 PicoClaw 当前分支的方案,不是直接复制 OpenClaw 的 hooks,也不是完整照搬 pi-mono 的 extension system,而是:
|
||||||
|
|
||||||
- 以现有 `EventBus` 为只读观察面
|
- 以 `pkg/events` runtime event bus 为只读观察面
|
||||||
- 以新增 `HookManager` 为同步拦截面
|
- 以新增 `HookManager` 为同步拦截面
|
||||||
- 项目内通过 Go 对象直接挂载
|
- 项目内通过 Go 对象直接挂载
|
||||||
- 项目外通过 `stdio JSON-RPC` 进程通信挂载
|
- 项目外通过 `stdio JSON-RPC` 进程通信挂载
|
||||||
|
|
||||||
这样做有三个好处:
|
这样做有三个好处:
|
||||||
|
|
||||||
- 和 `#1796` 一致,hooks 只是 EventBus 之上的消费层
|
- 和 `#1796` 一致,hooks 只是 runtime event bus 之上的消费层
|
||||||
- 和当前 `refactor/agent` 实现一致,不需要推翻已有事件系统
|
- 和当前 `refactor/agent` 实现一致,不需要推翻已有事件系统
|
||||||
- 同时满足“仓内简单挂载”和“仓外进程通信挂载”两个硬需求
|
- 同时满足“仓内简单挂载”和“仓外进程通信挂载”两个硬需求
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue