31 KiB
调研报告:事件驱动架构 + 上下文管理策略
版本: 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 是一个可变、事件驱动的队列,支持拓扑依赖解析。
// 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
关键设计:
- 事件类型:
task:ready(依赖满足)、task:complete(任务完成)、task:error(任务失败) - 自动解锁:当 task complete 时,自动检查所有依赖于它的 pending 任务,如果依赖全部满足,触发
task:ready - 并行决策:纯依赖声明驱动——没有依赖关系的任务自然并行执行
- 零基础设施:纯 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 文件
关键设计:
- 信号机制:通过文件系统信号(
.proposed、.committed)通知其他 Agent - 原子性保证:flock 文件锁 + 三阶段提交 = 无竞态
- 零外部依赖:不需要 Redis/数据库做分布式锁
- 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)
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 跨进程通知
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 事件处理优先级
# 高优先级事件:立即 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 辅助仲裁
- 显式依赖:任务创建时声明
depends_on(庞统/创建者负责声明) - 自动并行:
depends_on为空且assignee不同的任务,自动并行 spawn - 自动串行:有
depends_on的任务,上游完成事件触发下游 spawn - 冲突检测:如果两个任务的
files_modified有交集,Daemon 警告并建议串行 - 庞统仲裁:争议场景 spawn 庞统决定
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,仍然可控。
真正的问题不是单个任务的黑板信息,而是:
- 产出物文件内容(code diff、分析报告)可能很大
- 长讨论线程(30+ 评论来回讨论)
- 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
核心机制:
-
自动压缩(Auto-compact):
- 当 context 使用接近 limit 时,先清除旧 tool output
- 然后自动 summarize 对话历史
- 用户可通过 CLAUDE.md 自定义压缩策略(如 "压缩时保留修改文件列表和测试命令")
/compact手动触发,/compact "保留所有类型定义"可带自定义指令
-
文件引用(@-reference):
- 用
@file_path引用文件内容,而非内联到对话中 - LLM 按需读取,避免全量加载
- 用
-
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 时的上下文模板
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 无缝接力的关键
- 产出物标准化:每个产出必须包含
summary(一句话摘要)+content_path(文件路径),不存大段文本 - 决策必须记录:Agent 的关键决策写入 decisions 表,后继者可通过 L2 了解"为什么这么做"
- 未完成事项显式化:Agent 结束前在评论中写 "未完成:XXX",后继者一目了然
- 触发原因明确: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 限制,不需要做复杂的压缩/摘要。核心策略是:
- L1 必传:~300-500 tokens,任何场景都负担得起
- L2 按需:Agent 自己决定是否需要深入了解讨论/决策
- 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 模式 |