Files
sanguo_moziplus_v2/docs/research/v2.6-research-02-eventdriven-context.md
T
2026-05-15 09:31:37 +08:00

680 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 调研报告:事件驱动架构 + 上下文管理策略
**版本**: 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 CASclaim_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 profilesspawn 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 请求帮助 | Daemonspawn 被提及的人) | 高 |
| `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 内部事件触发 |
| **文件系统 watchinotify/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:进程内 EventBusasyncio**
```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 2Signal 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 3Fallback Tick30s 兜底)**
保留简化版 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 模式 |