680 lines
31 KiB
Markdown
680 lines
31 KiB
Markdown
# 调研报告:事件驱动架构 + 上下文管理策略
|
||
|
||
**版本**: v1.0
|
||
**作者**: 庞统(副军师)
|
||
**日期**: 2026-05-15
|
||
**范围**: moziplus v2.6 AI Native DevOps 平台
|
||
|
||
---
|
||
|
||
## 课题 2:事件驱动架构 + 并行/串行决策
|
||
|
||
### 1. 已有经验(知识库/wiki)
|
||
|
||
在 `~/.openclaw/sanguo_projects/sanguo_moziplus/docs/design/` 中有完整的架构设计文档:
|
||
|
||
| 文档 | 关键内容 |
|
||
|------|----------|
|
||
| `architecture-v2.6.md` | 当前 v2.6 使用 60s polling tick 轮询黑板,Daemon tick 读取黑板状态→发现需要介入的→spawn agent |
|
||
| `technical-design-v2.6.md` | Daemon ticker.py 实现了 tick 循环主逻辑,含 @mention 解析、session 管理、健康检查 |
|
||
|
||
**当前痛点**:
|
||
- Agent 被 @mention 后最多等 60 秒才知道(polling 间隔)
|
||
- Daemon tick 是重量级操作(读全量黑板、检查所有任务、处理所有 mention)
|
||
- 没有即时响应能力,用户紧急操作只能靠手动触发 tick
|
||
|
||
### 2. 业界优秀实践对比表
|
||
|
||
| 项目 | 通知机制 | 事件类型 | 依赖解析 | 并行/串行决策 | 亮点 |
|
||
|------|----------|----------|----------|--------------|------|
|
||
| **open-multi-agent** | 事件驱动 TaskQueue(`task:ready` 事件自动触发下游) | `task:ready`, `task:complete`, `task:error` | 拓扑排序(topological sort),complete→自动解锁下游 pending 任务 | **依赖声明驱动**:depends_on 声明后自动判断并行/串行 | 3 个运行时依赖,零基础设施,纯 TypeScript EventEmitter |
|
||
| **Network-AI** | 原子黑板信号(propose→validate→commit + file-backed mutex) | `propose`, `validate`, `commit`, `rollback` | 无显式依赖图,通过原子提交保证一致性 | Agent 自行判断并行(信号锁机制防竞态) | 零外部依赖,251 个测试,12 框架适配器 |
|
||
| **Hermes Kanban** | Dispatcher 60s polling tick(与 v2.6 相同) | 状态变化 + comment + mention | 7 状态完整状态机 | Dispatcher 调度决定并行/串行 | 成熟稳定,但同样受限于 polling 延迟 |
|
||
| **Claude Code Agent Teams** | 共享任务列表 + 直接消息(SendMessage tool) | `task:claimed`, `task:completed`, message | 无显式依赖图 | Agent 自主认领(先到先得) | 每个 agent 独立 1M token context window,通过消息通信 |
|
||
| **GSD (Get-Shit-Done)** | Wave-based 执行,阶段式推进 | Plan 级事件(phase complete) | Plan 中声明 `depends_on` 和 `wave` 编号 | **Wave 执行**:同 wave 并行,跨 wave 串行 | 每个 executor 独立 200K token 新鲜上下文,消除 context rot |
|
||
| **Confluent 事件驱动模式** | Kafka 事件流 | `AgentTaskSubmitted`, `AgentResultPublished` | DAG 依赖声明 | Event Router 根据依赖图路由 | 4 种模式:链式、广播-聚合、动态路由、竞技 |
|
||
| **Azure Agent Patterns** | 多种:顺序/并发/群聊/交接 | 按模式定义 | Workflow pattern 支持依赖图 | **编排模式选择**:sequential vs concurrent vs group-chat | 最完整的模式分类 |
|
||
|
||
### 3. 重点项目深度分析
|
||
|
||
#### 3.1 open-multi-agent — 事件驱动 TaskQueue
|
||
|
||
**核心机制**:`TaskQueue` 是一个可变、事件驱动的队列,支持拓扑依赖解析。
|
||
|
||
```typescript
|
||
// queue.ts 核心逻辑
|
||
// Tasks enter in 'pending' state.
|
||
// Queue promotes them to 'blocked' when unresolved dependencies exist.
|
||
// When dependencies resolve → back to 'pending', firing 'task:ready'
|
||
// When task completes → auto-unlock downstream dependent tasks
|
||
```
|
||
|
||
**关键设计**:
|
||
1. **事件类型**:`task:ready`(依赖满足)、`task:complete`(任务完成)、`task:error`(任务失败)
|
||
2. **自动解锁**:当 task complete 时,自动检查所有依赖于它的 pending 任务,如果依赖全部满足,触发 `task:ready`
|
||
3. **并行决策**:纯依赖声明驱动——没有依赖关系的任务自然并行执行
|
||
4. **零基础设施**:纯 EventEmitter,不依赖 Redis/Kafka/数据库
|
||
|
||
**对我们的启示**:
|
||
- **complete→auto-unlock** 是核心模式,替代 polling tick 的"检查依赖是否满足"
|
||
- 依赖声明优于 Agent 自行判断并行性
|
||
- 事件驱动不一定要引入消息队列基础设施
|
||
|
||
#### 3.2 Network-AI — 原子黑板信号
|
||
|
||
**核心机制**:propose→validate→commit 三阶段原子提交,file-backed mutex 防竞态。
|
||
|
||
```
|
||
1. Agent 调用 propose(key, value) → 写入 .proposed 文件
|
||
2. validate() → 检查约束条件(其他 agent 没有冲突 propose)
|
||
3. commit() → 原子性写入最终状态
|
||
4. 失败 → rollback() → 清理 .proposed 文件
|
||
```
|
||
|
||
**关键设计**:
|
||
1. **信号机制**:通过文件系统信号(`.proposed`、`.committed`)通知其他 Agent
|
||
2. **原子性保证**:flock 文件锁 + 三阶段提交 = 无竞态
|
||
3. **零外部依赖**:不需要 Redis/数据库做分布式锁
|
||
4. **251 个测试**:竞态条件覆盖充分
|
||
|
||
**对我们的启示**:
|
||
- 我们的 SQLite CAS(claim_task 的原子 UPDATE WHERE)已经实现了类似功能
|
||
- 三阶段提交模式适合"产出审核"场景(propose 产出 → 庞统 validate → commit 到黑板)
|
||
- 文件系统信号可以作为轻量级通知机制的灵感
|
||
|
||
#### 3.3 Hermes Kanban — 当前 v2.6 的参考
|
||
|
||
**核心机制**:Dispatcher 60s tick + SQLite 黑板 + 7 状态机。
|
||
|
||
```
|
||
Dispatcher loop (every 60s):
|
||
1. Reclaim stale claims(超时回收)
|
||
2. Promote ready tasks(依赖满足 → 推进状态)
|
||
3. Atomically claim(原子认领)
|
||
4. Spawn assigned profiles(spawn agent)
|
||
```
|
||
|
||
**与 v2.6 的对比**:我们的设计直接参考了 Hermes,但 Hermes 本身也有 polling 延迟问题。Hermes 提供了 `Nudge dispatcher` 手动触发来缓解。
|
||
|
||
### 4. 事件类型定义
|
||
|
||
基于所有调研项目,我们需要的核心事件类型:
|
||
|
||
| 事件类型 | 触发条件 | 消费者 | 优先级 |
|
||
|---------|---------|--------|--------|
|
||
| `task_created` | 任何 Agent 创建任务 | Daemon(检查是否需要 spawn) | 中 |
|
||
| `task_claimed` | Agent 认领任务 | Daemon(更新 agent 状态) | 低 |
|
||
| `task_completed` | Agent 完成任务 | Daemon(解锁下游依赖 + spawn 下一环) | **高** |
|
||
| `task_failed` | Agent 任务失败 | Daemon(重试逻辑 + 通知庞统) | 高 |
|
||
| `task_blocked` | Agent 请求帮助 | Daemon(spawn 被提及的人) | 高 |
|
||
| `comment_added` | 评论写入 | Daemon(检查 @mention) | **高** |
|
||
| `output_written` | 产出写入 | Daemon(通知相关方) | 中 |
|
||
| `@mention` | 评论中提及 Agent | Daemon(立即 spawn 被提及者) | **最高** |
|
||
| `user_action` | 用户在黑板上的操作 | Daemon(立即响应) | **最高** |
|
||
|
||
### 5. 通知机制对比
|
||
|
||
| 方案 | 延迟 | 复杂度 | 依赖 | 适用场景 |
|
||
|------|------|--------|------|----------|
|
||
| **Polling(当前)** | ≤60s | 最低 | 无 | 简单场景,容忍延迟 |
|
||
| **SQLite update_hook** | ~0ms(进程内) | 低 | sqlite3 C API | Daemon 内部事件触发 |
|
||
| **文件系统 watch(inotify/FSEvents)** | ~ms | 中 | OS 原生 | 跨进程通知 |
|
||
| **SQLite WAL file watch** | ~ms | 中 | fswatch/watchdog | 跨进程 SQLite 变更通知 |
|
||
| **Unix socket / Named pipe** | ~ms | 中 | 无 | 本地进程间通信 |
|
||
| **Redis pub/sub** | ~ms | 中 | Redis | 分布式场景 |
|
||
| **Signal file + polling** | ≤1s | 最低 | 无 | 混合方案 |
|
||
| **asyncio.Event(进程内)** | ~0ms | 最低 | 无 | Daemon 进程内部 |
|
||
|
||
### 6. 推荐方案:分层事件驱动(结合 AI Native 目标)
|
||
|
||
#### 6.1 核心思路:不引入重依赖,进程内事件 + 轻量跨进程信号
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Daemon (Python asyncio) │
|
||
│ │
|
||
│ ┌─────────────┐ ┌──────────────────────┐ │
|
||
│ │ EventBus │ │ Tick Loop (fallback) │ │
|
||
│ │ (asyncio) │ │ 30s 轮询兜底 │ │
|
||
│ └──────┬──────┘ └──────────────────────┘ │
|
||
│ │ │
|
||
│ Agent 操作黑板时: │
|
||
│ 1. 写入 SQLite(当前逻辑不变) │
|
||
│ 2. 写入 signal file(通知 daemon) │
|
||
│ 3. Daemon 检测到信号 → 立即触发事件处理 │
|
||
└─────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 6.2 具体实现
|
||
|
||
**Layer 1:进程内 EventBus(asyncio)**
|
||
|
||
```python
|
||
import asyncio
|
||
from enum import Enum
|
||
from dataclasses import dataclass
|
||
from typing import Callable, Awaitable
|
||
|
||
class EventType(str, Enum):
|
||
TASK_CREATED = "task_created"
|
||
TASK_CLAIMED = "task_claimed"
|
||
TASK_COMPLETED = "task_completed"
|
||
TASK_FAILED = "task_failed"
|
||
TASK_BLOCKED = "task_blocked"
|
||
COMMENT_ADDED = "comment_added"
|
||
OUTPUT_WRITTEN = "output_written"
|
||
USER_ACTION = "user_action"
|
||
|
||
@dataclass
|
||
class Event:
|
||
type: EventType
|
||
task_id: str | None
|
||
agent: str | None
|
||
detail: dict | None
|
||
|
||
class EventBus:
|
||
def __init__(self):
|
||
self._handlers: dict[EventType, list[Callable]] = {}
|
||
self._queue: asyncio.Queue[Event] = asyncio.Queue()
|
||
|
||
def on(self, event_type: EventType, handler: Callable):
|
||
self._handlers.setdefault(event_type, []).append(handler)
|
||
|
||
async def emit(self, event: Event):
|
||
await self._queue.put(event)
|
||
|
||
async def start(self):
|
||
"""主事件循环"""
|
||
while True:
|
||
event = await self._queue.get()
|
||
handlers = self._handlers.get(event.type, [])
|
||
for handler in handlers:
|
||
try:
|
||
await handler(event)
|
||
except Exception as e:
|
||
logger.error(f"Event handler error: {e}")
|
||
|
||
async def emit_from_blackboard_op(self, event_type: EventType, task_id: str,
|
||
agent: str, detail: dict = None):
|
||
"""黑板操作后触发事件"""
|
||
await self.emit(Event(type=event_type, task_id=task_id, agent=agent, detail=detail))
|
||
```
|
||
|
||
**Layer 2:Signal File 跨进程通知**
|
||
|
||
```python
|
||
import os
|
||
from pathlib import Path
|
||
|
||
SIGNAL_DIR = Path("~/.sanguo_projects/sanguo_moziplus_v2/signals")
|
||
|
||
async def write_signal(event_type: str, task_id: str = None, agent: str = None):
|
||
"""Agent(外部进程)写入信号文件"""
|
||
signal_file = SIGNAL_DIR / f"{event_type}.signal"
|
||
payload = f"{task_id or ''}:{agent or ''}:{time.time()}"
|
||
signal_file.write_text(payload)
|
||
|
||
async def watch_signals():
|
||
"""Daemon 监听信号文件"""
|
||
while True:
|
||
for signal_file in SIGNAL_DIR.glob("*.signal"):
|
||
if signal_file.exists():
|
||
payload = signal_file.read_text()
|
||
parts = payload.split(":")
|
||
event_type = signal_file.stem
|
||
await event_bus.emit(Event(
|
||
type=EventType(event_type),
|
||
task_id=parts[0] or None,
|
||
agent=parts[1] or None,
|
||
detail=None
|
||
))
|
||
signal_file.unlink() # 处理完删除
|
||
await asyncio.sleep(0.5) # 500ms 检查间隔(比 60s 好 120 倍)
|
||
```
|
||
|
||
**Layer 3:Fallback Tick(30s 兜底)**
|
||
|
||
保留简化版 tick 循环,30s 一次(从 60s 降为 30s),只处理:
|
||
- 健康检查(zombie 检测、stale 任务回收)
|
||
- 信号遗漏兜底(万一 signal file 处理失败)
|
||
|
||
#### 6.3 事件处理优先级
|
||
|
||
```python
|
||
# 高优先级事件:立即 spawn
|
||
HIGH_PRIORITY = {
|
||
EventType.TASK_COMPLETED, # 解锁下游
|
||
EventType.TASK_FAILED, # 触发重试
|
||
EventType.TASK_BLOCKED, # 请求帮助
|
||
EventType.COMMENT_ADDED, # 可能含 @mention
|
||
EventType.USER_ACTION, # 用户操作
|
||
}
|
||
|
||
# 低优先级事件:排队处理
|
||
LOW_PRIORITY = {
|
||
EventType.TASK_CREATED,
|
||
EventType.TASK_CLAIMED,
|
||
EventType.OUTPUT_WRITTEN,
|
||
}
|
||
```
|
||
|
||
### 7. 并行/串行决策推荐
|
||
|
||
#### 7.1 决策矩阵
|
||
|
||
| 场景 | 并行/串行 | 决策依据 | 示例 |
|
||
|------|----------|---------|------|
|
||
| 无依赖任务 | **并行** | `depends_on` 为空 | 赵云下载数据 + 庞统写方案 |
|
||
| 有依赖任务 | **串行** | `depends_on` 声明 | 先下载数据 → 再回测 |
|
||
| 同一任务冲突 | **串行**(先到先得) | SQLite CAS | 张飞和关羽抢同一个 review |
|
||
| 产出审核 | **串行**(完成→审核→通过) | 状态机 | 张飞完成 → 司马懿审核 |
|
||
| 独立研究 | **并行** | 无交集的子任务 | 多个因子独立调研 |
|
||
| 同文件修改 | **串行** | 文件路径冲突检测 | 不能两人同时改同一个文件 |
|
||
|
||
#### 7.2 推荐策略:**依赖声明优先,Daemon 辅助仲裁**
|
||
|
||
1. **显式依赖**:任务创建时声明 `depends_on`(庞统/创建者负责声明)
|
||
2. **自动并行**:`depends_on` 为空且 `assignee` 不同的任务,自动并行 spawn
|
||
3. **自动串行**:有 `depends_on` 的任务,上游完成事件触发下游 spawn
|
||
4. **冲突检测**:如果两个任务的 `files_modified` 有交集,Daemon 警告并建议串行
|
||
5. **庞统仲裁**:争议场景 spawn 庞统决定
|
||
|
||
```python
|
||
async def on_task_completed(event: Event):
|
||
"""任务完成 → 解锁下游依赖"""
|
||
task_id = event.task_id
|
||
|
||
# 查找所有依赖于该任务的 blocked 任务
|
||
downstream = db.query(
|
||
"SELECT * FROM tasks WHERE status='pending' AND depends_on LIKE ?",
|
||
(f'%{task_id}%',)
|
||
)
|
||
|
||
for task in downstream:
|
||
deps = json.loads(task['depends_on'])
|
||
# 检查所有依赖是否都完成了
|
||
all_done = all(
|
||
db.query("SELECT status FROM tasks WHERE id=?", (d,))[0]['status'] == 'done'
|
||
for d in deps
|
||
)
|
||
if all_done:
|
||
# 自动解锁:触发 task:ready
|
||
await event_bus.emit(Event(
|
||
type=EventType.TASK_READY,
|
||
task_id=task['id'],
|
||
agent=task['assignee'],
|
||
))
|
||
# 立即 spawn 对应 agent
|
||
await spawn_agent(task['assignee'], f"任务 {task['id']} 依赖已满足,请查看并认领。")
|
||
```
|
||
|
||
---
|
||
|
||
## 课题 10:上下文管理策略
|
||
|
||
### 1. 黑板信息量具体测算
|
||
|
||
基于 `architecture-v2.6.md` 中的 SQLite Schema,逐项测算:
|
||
|
||
#### 1.1 一个任务基础信息(tasks 表)
|
||
|
||
```
|
||
字段 典型内容 估算 tokens
|
||
─────────────────────────────────────────────────────────────────────
|
||
id "task-001" 1
|
||
title "动量因子策略回测" 5
|
||
description "对动量因子进行历史回测,验证策略有效性" 20
|
||
status "working" 1
|
||
assignee "zhangfei-dev" 2
|
||
assigned_by "pangtong-fujunshi" 2
|
||
depends_on ["task-001", "task-002"] 5
|
||
parent_task "task-000" 1
|
||
priority 5 1
|
||
task_type "coding" 1
|
||
created_at "2026-05-15 09:00:00" 3
|
||
updated_at "2026-05-15 10:30:00" 3
|
||
claimed_at "2026-05-15 09:05:00" 3
|
||
started_at "2026-05-15 09:10:00" 3
|
||
deadline "2026-05-16 18:00:00" 3
|
||
retry_count 0 1
|
||
─────────────────────────────────────────────────────────────────────
|
||
小计(结构化格式) ~50-60 tokens
|
||
```
|
||
|
||
**一个任务基础信息 ≈ 50-60 tokens**
|
||
|
||
#### 1.2 评论(10 条,comments 表)
|
||
|
||
```
|
||
每条评论典型内容:
|
||
"张飞,你的实现方案我看了,回测数据量大时内存会爆。关羽,从风控角度也看看?
|
||
@关羽 @张飞"
|
||
+ author + created_at + mentions JSON
|
||
≈ 30-50 tokens/条
|
||
|
||
10 条评论 ≈ 300-500 tokens
|
||
```
|
||
|
||
#### 1.3 产出(5 个,outputs 表)
|
||
|
||
```
|
||
每个产出典型内容:
|
||
output_type: "code"
|
||
title: "分批加载实现"
|
||
content_path: "task-001/output-zhangfei-v2.md"
|
||
summary: "实现分批加载,单批50万条"
|
||
metadata: {"files_changed": 3, "lines_added": 150}
|
||
+ agent + created_at
|
||
≈ 30-50 tokens/个
|
||
|
||
5 个产出 ≈ 150-250 tokens
|
||
```
|
||
|
||
#### 1.4 事件日志(10 条,events 表)
|
||
|
||
```
|
||
每条事件:
|
||
event_type: "task_claimed"
|
||
agent: "zhangfei-dev"
|
||
detail: {"from": "pending", "reason": null}
|
||
+ created_at
|
||
≈ 15-25 tokens/条
|
||
|
||
10 条事件 ≈ 150-250 tokens
|
||
```
|
||
|
||
#### 1.5 决策记录(3 条,decisions 表)
|
||
|
||
```
|
||
每条决策:
|
||
decision: "使用分批加载而非流式"
|
||
rationale: "流式需要改底层框架,分批只需改回测模块"
|
||
alternatives: ["流式加载", "内存映射"]
|
||
≈ 40-80 tokens/条
|
||
|
||
3 条决策 ≈ 120-240 tokens
|
||
```
|
||
|
||
#### 1.6 观察记录(3 条,observations 表)
|
||
|
||
```
|
||
每条观察:
|
||
severity: "warning"
|
||
body: "止损逻辑在分批模式下可能漏触发"
|
||
≈ 20-30 tokens/条
|
||
|
||
3 条观察 ≈ 60-90 tokens
|
||
```
|
||
|
||
#### 1.7 总计
|
||
|
||
| 组件 | 数量 | 估算 tokens |
|
||
|------|------|-------------|
|
||
| 任务基础信息 | 1 个 | 50-60 |
|
||
| 评论 | 10 条 | 300-500 |
|
||
| 产出 | 5 个 | 150-250 |
|
||
| 事件日志 | 10 条 | 150-250 |
|
||
| 决策记录 | 3 条 | 120-240 |
|
||
| 观察记录 | 3 条 | 60-90 |
|
||
| **总计** | | **830-1390 tokens** |
|
||
| + JSON 格式开销(花括号、引号) | | ~150-200 tokens |
|
||
| + 提示词格式(标题、分隔符) | | ~100-150 tokens |
|
||
| **实际传给 LLM 的总量** | | **~1100-1750 tokens** |
|
||
|
||
**关键结论:一个典型任务的全量黑板信息约 1100-1750 tokens。**
|
||
|
||
极端情况(30 条评论、10 个产出、50 条事件):
|
||
- 评论:30 × 40 = 1200 tokens
|
||
- 产出:10 × 40 = 400 tokens
|
||
- 事件:50 × 20 = 1000 tokens
|
||
- 总计约 3000-4000 tokens
|
||
|
||
**即使极端情况也在 4000 tokens 以内,远小于 200K context window。**
|
||
|
||
但是——如果有多个任务(比如庞统规划了 5 个子任务),5 个任务 × 1500 tokens = 7500 tokens,仍然可控。
|
||
|
||
**真正的问题不是单个任务的黑板信息,而是**:
|
||
1. 产出物文件内容(code diff、分析报告)可能很大
|
||
2. 长讨论线程(30+ 评论来回讨论)
|
||
3. Agent 的 system prompt + SOUL.md + skill 文件已经占用大量 context
|
||
|
||
### 2. 业界优秀实践对比表
|
||
|
||
| 项目 | 上下文策略 | 核心机制 | 优点 | 缺点 |
|
||
|------|-----------|----------|------|------|
|
||
| **Claude Code** | Auto-compact + file reference | 1) 自动压缩:接近 context limit 时先清 tool output,再 summarize 对话 2) `/compact` 手动压缩 3) `@file` 引用而非内联 4) CLAUDE.md 持久记忆 | 成熟稳定,官方支持 | 压缩可能丢失细节;单一 session 内累积 |
|
||
| **Claude Code Agent Teams** | 每个 Agent 独立 1M token context | 隔离 context window + 共享任务列表 + SendMessage 直接通信 | 天然隔离,无 context rot 问题 | 跨 agent 通信开销 |
|
||
| **GSD (Get-Shit-Done)** | Wave Execution:每个 plan 独立新鲜 200K context | 1) Plan 分 wave 执行 2) 每个 executor 新鲜 context 3) 原子 git commit 4) 主 context 保持 30-40% | 彻底消除 context rot | 需要精确的 plan 分解 |
|
||
| **Network-AI** | 原子黑板(数据在黑板,不在 context) | Agent 只读黑板摘要,详细数据在文件中 | context 极小 | 需要额外读取操作 |
|
||
| **Hermes Kanban** | kanban_show() 按需读取 | Agent 只看当前任务看板,不全量读取 | 简单有效 | 可能遗漏全局信息 |
|
||
| **Agent Chorus** | Checkpoint + 跨 Agent 消息 | Standup drain inbox + conclude broadcast + checkpoint 状态广播 | 跨框架兼容 | 复杂度较高 |
|
||
| **Opal-Bridge** | Cross-agent session translator | Claude Code ↔ Codex CLI 格式转换 | 多工具协作 | 范围有限 |
|
||
| **MindStudio** | 三层内存:工作/短期/长期 | Working(当前会话)+ Short-term(任务级)+ Long-term(项目级) | 分层清晰 | 需要额外基础设施 |
|
||
|
||
### 3. 重点项目深度分析
|
||
|
||
#### 3.1 Claude Code — Auto-compact + File Reference
|
||
|
||
**核心机制**:
|
||
|
||
1. **自动压缩(Auto-compact)**:
|
||
- 当 context 使用接近 limit 时,先清除旧 tool output
|
||
- 然后自动 summarize 对话历史
|
||
- 用户可通过 CLAUDE.md 自定义压缩策略(如 "压缩时保留修改文件列表和测试命令")
|
||
- `/compact` 手动触发,`/compact "保留所有类型定义"` 可带自定义指令
|
||
|
||
2. **文件引用(@-reference)**:
|
||
- 用 `@file_path` 引用文件内容,而非内联到对话中
|
||
- LLM 按需读取,避免全量加载
|
||
|
||
3. **CLAUDE.md 持久记忆**:
|
||
- 项目级(`CLAUDE.md`)+ 用户级(`~/.claude/CLAUDE.md`)
|
||
- 每次 session 启动自动加载
|
||
- 相当于 L1 核心信息
|
||
|
||
**对我们的启示**:
|
||
- 产出的详细内容不应放在黑板 context 中,只放摘要 + 文件路径引用
|
||
- Agent 读取产出时应按需 read 文件,不全量注入 context
|
||
|
||
#### 3.2 GSD (Get-Shit-Done) — Wave Execution
|
||
|
||
**核心机制**:
|
||
|
||
```
|
||
/gsd:execute-phase N
|
||
├── 分析 plan 依赖关系
|
||
├── Wave 1(无依赖的 plan):
|
||
│ ├── Executor A(新鲜 200K context)→ commit
|
||
│ └── Executor B(新鲜 200K context)→ commit
|
||
├── Wave 2(依赖 Wave 1):
|
||
│ └── Executor C(新鲜 200K context)→ commit
|
||
└── Verifier(检查所有产出)
|
||
```
|
||
|
||
**关键数据**:
|
||
- 主 context 始终保持在 30-40%(因为重活在 subagent 中做)
|
||
- 每个 executor 独立 200K token,不做压缩(新鲜 context 不需要)
|
||
- 每个 plan 一个原子 git commit(可回滚)
|
||
|
||
**对我们的启示**:
|
||
- **我们的 Agent spawn 模式天然就是 Wave Execution**:Daemon spawn 隔离 session,每个 agent 独立 context
|
||
- 关键是 spawn 时传多少上下文——传太多浪费,传太少丢信息
|
||
- 产出写入黑板摘要 + 文件路径,下一个 agent 按需读取
|
||
|
||
#### 3.3 Opal-Bridge — Cross-Agent Session Translator
|
||
|
||
**核心机制**:将 Claude Code 的 session 格式转换为 Codex CLI 的格式,实现跨工具接力。
|
||
|
||
**Fidelity 概念**(从 cross_agent_session_resumer 项目借鉴):
|
||
- **Native fidelity**:保留原始格式的完整信息
|
||
- **Lossy export**:转换为通用格式时丢失部分信息
|
||
- **Checkpoint**:关键节点快照
|
||
|
||
**对我们的启示**:
|
||
- Agent 之间的"接力"需要定义标准化的交接格式
|
||
- 不是所有信息都需要完整传递——关键是决策、产出摘要、未完成事项
|
||
|
||
### 4. 推荐方案:分层传递策略
|
||
|
||
#### 4.1 三层信息架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ L1: 核心信息(必须传入,~200-400 tokens) │
|
||
│ ─────────────────────────────────────────────────── │
|
||
│ • 任务 ID + 标题 + 状态 │
|
||
│ • 任务描述(description) │
|
||
│ • 我的角色 + 触发原因(为什么 spawn 我) │
|
||
│ • 依赖任务的状态摘要(1 行/任务) │
|
||
│ • 最近 3 条评论摘要 │
|
||
│ │
|
||
│ 传递方式:spawn message 中直接写入 │
|
||
│ 保证:任何 Agent 都能在 L1 信息下开始工作 │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ L2: 扩展信息(按需读取,~500-1500 tokens) │
|
||
│ ─────────────────────────────────────────────────── │
|
||
│ • 完整评论线程 │
|
||
│ • 产出摘要列表(title + summary,不含详细内容) │
|
||
│ • 决策记录 │
|
||
│ • 观察记录 │
|
||
│ • 其他相关任务摘要 │
|
||
│ │
|
||
│ 传递方式:Agent 通过 CLI 按需读取 │
|
||
│ CLI: blackboard.py read --task task-001 --level L2 │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ L3: 全量信息(按需读取,不限制大小) │
|
||
│ ─────────────────────────────────────────────────── │
|
||
│ • 产出物文件完整内容 │
|
||
│ • 完整事件日志 │
|
||
│ • 所有子任务详情 │
|
||
│ │
|
||
│ 传递方式:文件路径引用,Agent 用 read 工具读取 │
|
||
│ 黑板只存 content_path,不存文件内容 │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 4.2 Agent Spawn 时的上下文模板
|
||
|
||
```python
|
||
def build_spawn_message_L1(task_id: str, agent_id: str, trigger: str) -> str:
|
||
"""L1 核心:~200-400 tokens,直接写入 spawn message"""
|
||
task = get_task(task_id)
|
||
deps_status = []
|
||
for dep_id in json.loads(task['depends_on'] or '[]'):
|
||
dep = get_task(dep_id)
|
||
deps_status.append(f" {dep_id}: {dep['status']} - {dep['title']}")
|
||
|
||
recent_comments = get_comments(task_id, limit=3)
|
||
comments_str = ""
|
||
for c in recent_comments:
|
||
comments_str += f" [{c['created_at'][:16]} {c['author']}] {c['body'][:100]}\n"
|
||
|
||
return f"""黑板任务通知(L1):
|
||
任务:{task['title']}({task['id']})
|
||
状态:{task['status']}
|
||
类型:{task['task_type']}
|
||
触发原因:{trigger}
|
||
描述:{task['description'] or '(无)'}
|
||
|
||
依赖状态:
|
||
{chr(10).join(deps_status) if deps_status else ' (无依赖)'}
|
||
|
||
最近评论:
|
||
{comments_str if comments_str else ' (无评论)'}
|
||
|
||
请使用以下命令获取更多信息:
|
||
L2(扩展):blackboard.py read --task {task_id} --level L2
|
||
L3(全量产出):blackboard.py read --task {task_id} --type outputs
|
||
"""
|
||
```
|
||
|
||
#### 4.3 黑板上的信息分层规则
|
||
|
||
| 信息类别 | 存储位置 | 黑板上展示 | 说明 |
|
||
|---------|---------|-----------|------|
|
||
| 任务元数据 | 黑板 tasks 表 | 完整 | ID、标题、状态、描述、分配、依赖 |
|
||
| 评论 | 黑板 comments 表 | 完整 | 所有评论保留,L2 读取 |
|
||
| 产出摘要 | 黑板 outputs 表 | title + summary + path | 详细内容在文件中 |
|
||
| 产出详细内容 | 文件系统 | 仅文件路径 | Agent 按需 read |
|
||
| 决策记录 | 黑板 decisions 表 | 完整 | L2 读取 |
|
||
| 观察记录 | 黑板 observations 表 | 完整 | L2 读取 |
|
||
| 事件日志 | 黑板 events 表 | 仅最近 10 条 | L3 按需查询 |
|
||
|
||
#### 4.4 后接 Agent 无缝接力的关键
|
||
|
||
1. **产出物标准化**:每个产出必须包含 `summary`(一句话摘要)+ `content_path`(文件路径),不存大段文本
|
||
2. **决策必须记录**:Agent 的关键决策写入 decisions 表,后继者可通过 L2 了解"为什么这么做"
|
||
3. **未完成事项显式化**:Agent 结束前在评论中写 "未完成:XXX",后继者一目了然
|
||
4. **触发原因明确**:Daemon spawn 时必须说明为什么 spawn(@mention / 依赖满足 / 用户指令)
|
||
|
||
### 5. Context 预算分配建议
|
||
|
||
假设使用 128K context window 的模型(如 gpt-4o / claude-3.5):
|
||
|
||
| 组件 | 预算 | 说明 |
|
||
|------|------|------|
|
||
| System Prompt + SOUL.md + IDENTITY.md | ~3000-5000 tokens | 固定开销 |
|
||
| Skills + AGENTS.md | ~2000-4000 tokens | 固定开销 |
|
||
| L1 spawn message | ~300-500 tokens | 任务核心信息 |
|
||
| L2 黑板扩展信息(按需) | ~500-1500 tokens | 完整讨论、决策 |
|
||
| L3 产出物文件(按需) | ~2000-10000 tokens | 代码/报告内容 |
|
||
| 工作空间(Agent 思考+输出) | ~30000-50000 tokens | 预留给 Agent 实际工作 |
|
||
| **总计** | ~40K-70K tokens | 远小于 128K,安全 |
|
||
|
||
**关键结论**:我们的黑板信息量远小于 context window 限制,**不需要做复杂的压缩/摘要**。核心策略是:
|
||
|
||
1. **L1 必传**:~300-500 tokens,任何场景都负担得起
|
||
2. **L2 按需**:Agent 自己决定是否需要深入了解讨论/决策
|
||
3. **L3 文件引用**:大内容只在文件中,黑板只存路径
|
||
|
||
---
|
||
|
||
## 总结与行动建议
|
||
|
||
### 课题 2 行动建议(事件驱动)
|
||
|
||
| 优先级 | 行动 | 工作量 |
|
||
|--------|------|--------|
|
||
| P0 | 实现 asyncio EventBus 进程内事件总线 | 1 天 |
|
||
| P0 | 实现 Signal File 跨进程通知(Agent→Daemon) | 0.5 天 |
|
||
| P1 | 事件处理:task_completed → 解锁下游依赖 | 0.5 天 |
|
||
| P1 | 事件处理:comment_added → @mention 检测 | 0.5 天 |
|
||
| P2 | 保留 30s fallback tick(健康检查) | 0 天(简化现有 tick) |
|
||
| P2 | files_modified 冲突检测 | 1 天 |
|
||
|
||
**预期效果**:
|
||
- @mention 响应从 ≤60s 降到 ≤1s
|
||
- 任务依赖解锁从 ≤60s 降到 ~0ms(进程内事件)
|
||
- 不引入任何新依赖
|
||
|
||
### 课题 10 行动建议(上下文管理)
|
||
|
||
| 优先级 | 行动 | 工作量 |
|
||
|--------|------|--------|
|
||
| P0 | 定义 L1/L2/L3 分层读取 API | 0.5 天 |
|
||
| P0 | 实现 L1 spawn message 模板 | 0.5 天 |
|
||
| P1 | 产出物只存摘要+路径的规范落地 | 0 天(已在 schema 中) |
|
||
| P2 | blackboard.py read --level L2 实现 | 0.5 天 |
|
||
| P2 | Agent 规范:结束前写未完成事项 | 0 天(文档规范) |
|
||
|
||
**核心原则**:
|
||
- 黑板是索引(做什么 + 在哪找),不是仓库(详细内容)
|
||
- 产出物在文件中,黑板只存路径
|
||
- Agent spawn 时传最小充分上下文(L1),按需获取更多(L2/L3)
|
||
- 不做复杂压缩——信息量本身就很小
|
||
|
||
---
|
||
|
||
## 参考项目索引
|
||
|
||
| 项目 | URL | 关键参考价值 |
|
||
|------|-----|-------------|
|
||
| open-multi-agent | github.com/JackChen-me/open-multi-agent | 事件驱动 TaskQueue + 依赖自动解锁 |
|
||
| Network-AI | github.com/jovanSAPFIONEER/Network-AI | 原子黑板 propose→validate→commit |
|
||
| Hermes Kanban | github.com/NousResearch/hermes-agent | 60s tick + SQLite 黑板 + 状态机 |
|
||
| GSD (Get-Shit-Done) | github.com/gsd-build/get-shit-done | Wave Execution + 新鲜 context |
|
||
| Claude Code | code.claude.com | Auto-compact + file reference + agent teams |
|
||
| Agent Chorus | github.com/cote-star/agent-chorus | 跨 Agent checkpoint + 消息传递 |
|
||
| Azure Agent Patterns | learn.microsoft.com | 编排模式分类(sequential/concurrent/handoff) |
|
||
| Confluent 事件驱动 | confluent.io/blog | 4 种事件驱动多 Agent 模式 |
|