[moz] docs(§20): Issue-Centric Orchestration — Gitea Issue 替代黑板 DB 协作面
设计目标: 1. 黑板 DB 协作面迁移到 Gitea Issue 2. 成果物以 Gitea 为基础(分支+PR) 3. webhook 部分替代 ticker 4. task 状态+spawner 逻辑不变 5. prompt 改造(黑板 API → Gitea API) §3 目标架构:Gitea 做协作介质,daemon task_index 做轻量索引 §5 daemon 改造点:数据访问层(P1-P4)需讨论 §9 实施路径:5 个 Phase 分阶段实施
This commit is contained in:
@@ -0,0 +1,342 @@
|
||||
---
|
||||
title: "Issue-Centric Orchestration — Gitea Issue 替代黑板 DB 协作面"
|
||||
created: 2026-06-19
|
||||
version: v1.0 draft
|
||||
status: draft
|
||||
---
|
||||
|
||||
# Issue-Centric Orchestration
|
||||
|
||||
> **作者**: 庞统(副军师)🐦
|
||||
> **日期**: 2026-06-19
|
||||
> **定位**: 将黑板 DB 的协作面迁移到 Gitea Issue,daemon 逻辑保持不变
|
||||
> **前置文档**: PRD-v3.0(共享意识空间)、§14 TaskTypeRegistry、§17 ToolchainHandler
|
||||
|
||||
---
|
||||
|
||||
## §1. 设计目标
|
||||
|
||||
| # | 目标 | 说明 |
|
||||
|---|------|------|
|
||||
| 1 | 黑板 DB 协作面迁移到 Gitea Issue | 需求、讨论、产出从黑板 DB 迁到 Issue |
|
||||
| 2 | 成果物以 Gitea 为基础存放 | 分支 commit + PR |
|
||||
| 3 | webhook 部分替代 ticker | 主动触发替代轮询,ticker 保留兜底 |
|
||||
| 4 | task 状态 + spawner 逻辑不变 | daemon 内部状态机不变 |
|
||||
| 5 | prompt 改造 | 黑板 API 引用改为 Gitea API,告知 agent 使用 Gitea 协作 |
|
||||
|
||||
**核心原则**: 只有数据存储位置变了(黑板 DB → Gitea Issue),daemon 的调度逻辑(dispatcher/ticker/spawner)不变。
|
||||
|
||||
---
|
||||
|
||||
## §2. 现状分析
|
||||
|
||||
### 2.1 黑板 DB 当前承担的角色
|
||||
|
||||
根据 PRD-v3.0,黑板是**共享意识空间**——所有 agent 通过它读写状态、感知变化、协调工作。
|
||||
|
||||
黑板 DB 包含 12 张表:
|
||||
|
||||
| 表 | 用途 | 分类 |
|
||||
|---|------|------|
|
||||
| tasks | 任务(标题、描述、状态、指派、retry) | 协作面 + 执行面 |
|
||||
| comments | 讨论、@mention、action_report | 协作面 |
|
||||
| outputs | 产出物(文本摘要、文件路径) | 协作面 |
|
||||
| events | 事件流(SSE 推送) | 协作面 |
|
||||
| reviews | 审查记录(verdict、round、consensus) | 执行面 |
|
||||
| checkpoints | 阶段审查(approve/reject) | 执行面 |
|
||||
| decisions | 决策记录 | 执行面 |
|
||||
| observations | 风险观察 | 执行面 |
|
||||
| experiences | 经验沉淀 | 执行面 |
|
||||
| routing_decisions | 路由记录 | 执行面 |
|
||||
| task_attempts | 重试历史 | 执行面 |
|
||||
| mention_queue | @mention 队列 | 执行面 |
|
||||
|
||||
**协作面**(tasks/comments/outputs/events)= 迁移到 Gitea Issue
|
||||
**执行面**(reviews/checkpoints/decisions 等)= 保留在 daemon 内部
|
||||
|
||||
### 2.2 daemon 数据访问方式
|
||||
|
||||
当前 daemon 三个核心模块如何读写黑板 DB:
|
||||
|
||||
| 模块 | 读什么 | 怎么读 |
|
||||
|------|-------|-------|
|
||||
| ticker | pending task 列表 | `SELECT * FROM tasks WHERE status='pending'` (SQLite) |
|
||||
| dispatcher | task 详情(title/description/must_haves) | `Task.from_row(row)` 从 SQLite 行构建 |
|
||||
| spawner | task 上下文构建 prompt | 从 task 对象的 title/description/must_haves 字段 |
|
||||
|
||||
| 模块 | 写什么 | 怎么写 |
|
||||
|------|-------|-------|
|
||||
| dispatcher | task status(pending→claimed→working) | `UPDATE tasks SET status=?` |
|
||||
| spawner | task status(working→done/failed) | `UPDATE tasks SET status=?` |
|
||||
| handler | comment / output | `INSERT INTO comments/outputs` |
|
||||
|
||||
**关键发现**: daemon 大量依赖 `SELECT * FROM tasks WHERE status=?` 这种 SQL 查询来发现和调度 task。如果数据源迁到 Gitea Issue,这些查询的方式会变(从 SQLite 变为 Gitea API 或本地索引),但**查询的语义和返回的数据结构不变**。
|
||||
|
||||
---
|
||||
|
||||
## §3. 目标架构
|
||||
|
||||
### 3.1 分层
|
||||
|
||||
```
|
||||
Gitea(协作介质,替代黑板 DB 协作面)
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Issue #42: "实现功能 A" │
|
||||
│ body: 需求描述 + 验收标准 │
|
||||
│ assignee: zhangfei-dev │
|
||||
│ labels: type/feat, priority/P2 │
|
||||
│ comments: 讨论、@mention、进展汇报 │
|
||||
│ │
|
||||
│ 分支: fix/42-feature-a │
|
||||
│ PR #43: fix/42 → main │
|
||||
│ CI: lint + test │
|
||||
│ Review: APPROVE / REQUEST_CHANGES │
|
||||
│ → merge → Issue auto-close │
|
||||
└──────────────────────────────────────────┘
|
||||
↕ webhook(被动) ↕ API(主动)
|
||||
┌──────────────────────────────────────────┐
|
||||
│ daemon(执行引擎,内部状态管理不变) │
|
||||
│ │
|
||||
│ task_index(轻量索引,替代 tasks 表): │
|
||||
│ issue_number → status, branch, retry │
|
||||
│ │
|
||||
│ 执行面表(不变): │
|
||||
│ reviews, checkpoints, decisions, │
|
||||
│ observations, experiences, │
|
||||
│ routing_decisions, task_attempts │
|
||||
│ │
|
||||
│ 调度逻辑(不变): │
|
||||
│ ticker → 扫 task_index → dispatch │
|
||||
│ dispatcher → 读 Gitea Issue → spawn │
|
||||
│ spawner → 读 Gitea Issue → prompt │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 数据映射
|
||||
|
||||
| 黑板 DB | Gitea 对应 | 迁移方式 |
|
||||
|---------|-----------|---------|
|
||||
| tasks.title | Issue.title | 直接对应 |
|
||||
| tasks.description | Issue.body | 直接对应 |
|
||||
| tasks.assignee | Issue.assignee | 直接对应 |
|
||||
| tasks.status (pending/working/review/done) | daemon task_index 内部维护 | Issue open/closed 只表示生命周期 |
|
||||
| tasks.priority | Issue label (priority/P0-P3) | label 模拟 |
|
||||
| tasks.must_haves (JSON) | daemon task_index 内部存储 | daemon 专用元数据 |
|
||||
| tasks.depends_on | Issue blocked_by | Gitea 原生 dependency |
|
||||
| tasks.parent_task | Issue body 引用(如 `Parent: #42`) | 约定 |
|
||||
| tasks.retry_count / dispatch_count | daemon task_index 内部维护 | 执行面数据 |
|
||||
| comments | Issue comment | 直接对应 |
|
||||
| outputs | 分支 commit(代码/文档)+ Issue comment(摘要) | 成果物载体迁移 |
|
||||
| events | webhook | 主动推送替代 SSE |
|
||||
|
||||
### 3.3 daemon task_index 结构
|
||||
|
||||
替代黑板 tasks 表的**轻量索引**,只存 Gitea Issue 没有的信息:
|
||||
|
||||
```sql
|
||||
CREATE TABLE task_index (
|
||||
issue_number INTEGER, -- Gitea Issue 编号
|
||||
repo TEXT, -- 仓库名(如 sanguo/sanguo_moziplus_v2)
|
||||
branch TEXT, -- 功能分支名(如 fix/42-feature-a)
|
||||
status TEXT DEFAULT 'pending', -- daemon 内部状态
|
||||
assignee TEXT, -- agent id(冗余,加速 dispatch 查询)
|
||||
must_haves TEXT, -- JSON(daemon 元数据:event_type, steps 等)
|
||||
retry_count INTEGER DEFAULT 0,
|
||||
dispatch_count INTEGER DEFAULT 0,
|
||||
max_retries INTEGER DEFAULT 2,
|
||||
created_at TEXT,
|
||||
updated_at TEXT,
|
||||
PRIMARY KEY (issue_number, repo)
|
||||
);
|
||||
```
|
||||
|
||||
**注意**: task_index 不存 title/description/labels——这些从 Gitea Issue 实时读。
|
||||
|
||||
---
|
||||
|
||||
## §4. 流程设计
|
||||
|
||||
### 4.1 创建 Task
|
||||
|
||||
```
|
||||
① 庞统/主公在 Gitea 创建 Issue + 指派 agent
|
||||
② Gitea webhook: issues/assigned
|
||||
③ daemon toolchain handler 收到 webhook
|
||||
→ 在 task_index 插入一行(issue_number, repo, assignee, status=pending)
|
||||
④ ticker 扫 task_index 发现 pending → dispatch → spawn agent
|
||||
```
|
||||
|
||||
**和现在的区别**: 当前是庞统在黑板 API 创建 task。改造后是庞统在 Gitea 创建 Issue,webhook 自动触发 daemon 建索引。
|
||||
|
||||
### 4.2 执行 Task
|
||||
|
||||
```
|
||||
① dispatcher 扫 task_index 发现 pending task
|
||||
② spawner 从 Gitea API 读 Issue body(需求描述)
|
||||
③ spawner 用 Issue body 构建 prompt(替代从黑板 DB 读 description)
|
||||
→ prompt 结构: Issue body(需求)+ PromptSection 注入(工作流程、约束、API 指引)
|
||||
④ agent 收到 prompt → 执行
|
||||
⑤ agent 在 Gitea Issue comment 汇报进展(替代黑板 comment)
|
||||
⑥ daemon 更新 task_index status=working
|
||||
```
|
||||
|
||||
### 4.3 审查
|
||||
|
||||
```
|
||||
① agent 编码完成 → push 到分支 → 创建 PR
|
||||
② Gitea webhook: pull_request/opened
|
||||
→ daemon 更新 task_index status=review
|
||||
③ Reviewer 在 Gitea 做 PR Review
|
||||
④ Gitea webhook: pull_request_review
|
||||
→ daemon 根据 Review 结果更新 task_index
|
||||
⑤ Review 通过 → PR merge
|
||||
→ Gitea 自动关闭 Issue
|
||||
→ Gitea webhook: issues/closed
|
||||
→ daemon 更新 task_index status=done
|
||||
```
|
||||
|
||||
**审查统一走 PR Review**——不区分设计审查和代码审查,所有成果物都在分支上,Reviewer 一次性审。
|
||||
|
||||
### 4.4 CI 失败处理
|
||||
|
||||
```
|
||||
① PR 创建 → CI 自动跑
|
||||
② CI 失败 → Gitea webhook: pull_request(CI status)
|
||||
→ daemon toolchain handler 创建 ci_failure toolchain task
|
||||
→ 指派给 PR 作者
|
||||
→ agent 按 ci_failure steps 处理(已有逻辑,不变)
|
||||
③ agent 修复 → push 到同分支 → PR 自动更新 → CI 重跑
|
||||
```
|
||||
|
||||
### 4.5 ticker 兜底
|
||||
|
||||
webhook 可能丢失或延迟。ticker 保留原有逻辑,改为:
|
||||
- 扫 `task_index` 中 status=pending 的记录(替代扫黑板 tasks 表)
|
||||
- 扫 `task_index` 中 status=working 但超时的记录
|
||||
- 如果发现 Gitea Issue 已 closed 但 task_index 还是 working → 更新为 done
|
||||
|
||||
---
|
||||
|
||||
## §5. daemon 需要改的地方
|
||||
|
||||
**原则: daemon 调度逻辑不变,只改数据访问层。**
|
||||
|
||||
### 5.1 数据访问层改造
|
||||
|
||||
| 模块 | 现在 | 改造后 | 影响范围 |
|
||||
|------|------|-------|---------|
|
||||
| `queries.pending_dispatchable()` | `SELECT * FROM tasks WHERE status='pending'` | `SELECT * FROM task_index WHERE status='pending'` | SQL 改表名 |
|
||||
| `Task.from_row(row)` 构建 task 对象 | 从 tasks 表行直接取 title/description | 从 task_index 取 issue_number → 调 Gitea API 读 Issue title/body | 需要新增 Gitea API 调用 |
|
||||
| `UPDATE tasks SET status=?` | 直接更新 SQLite | 更新 task_index(SQLite) | SQL 改表名 |
|
||||
| `INSERT INTO comments` | 写黑板 DB | 改为 Gitea Issue comment API | 需要新增 Gitea API 调用 |
|
||||
|
||||
### 5.2 ⚠️ 需要讨论的改造点
|
||||
|
||||
以下是因为数据源变了,daemon 实现需要调整的地方:
|
||||
|
||||
**P1: spawner 每次 spawn 都要调 Gitea API 读 Issue body?**
|
||||
|
||||
当前 spawner 从 SQLite 读 task description(微秒级)。改为从 Gitea API 读(毫秒级,HTTP 请求)。
|
||||
|
||||
- **方案 A**: 每次 spawn 时实时调 Gitea API。简单但慢
|
||||
- **方案 B**: webhook 触发时缓存 Issue body 到 task_index.must_haves 或单独字段。spawn 时从缓存读
|
||||
- **推荐**: 方案 B。webhook 触发时 daemon 已经有 Issue body(在 payload 中),直接缓存
|
||||
|
||||
**P2: dispatcher 的 `Task.from_row(row)` 返回的对象缺少字段**
|
||||
|
||||
当前 Task 对象有 30+ 个字段(title/description/priority/risk_level 等)。task_index 只存少量字段。dispatcher/dispatch 逻辑如果访问了 Task 对象的 title/description 字段,会取不到值。
|
||||
|
||||
- **方案**: Task 对象新增 `issue_data` 字段(lazy load),首次访问 title/description 时从 Gitea API 或缓存加载
|
||||
- **影响**: 需要检查 dispatcher 中所有访问 task.title/task.description 的地方
|
||||
|
||||
**P3: agent 的 prompt 中引用黑板 API 的地方需要改**
|
||||
|
||||
PromptSection 中有多处 `POST localhost:8083/api/projects/.../tasks/.../comments`(黑板 API)。这些要改为 Gitea API:
|
||||
- `task_handler.py` TaskApiSection: `POST .../status` 和 `POST .../outputs` → 不需要(daemon 通过 webhook 自动感知状态)
|
||||
- `toolchain_handler.py` ToolchainApiSection: `POST .../comments`(action report)→ 改为 `POST Gitea API .../issues/.../comments`
|
||||
- `toolchain_handler.py` ToolchainApiSection: `POST .../outputs` → 改为"push 到分支"指引
|
||||
|
||||
**P4: comments 表的 @mention 机制**
|
||||
|
||||
当前 @mention 通过黑板 `mention_queue` 表排队。改造后 @mention 通过 Gitea Issue/PR comment(webhook 自然触发)。但 mention_queue 的消费逻辑(ticker 扫描 → 通知 → agent 处理)需要适配。
|
||||
|
||||
- **方案**: mention_queue 保留,但数据来源从黑板 comment 改为 Gitea webhook payload
|
||||
- **影响**: ticker 中的 mention 处理逻辑需要适配
|
||||
|
||||
---
|
||||
|
||||
## §6. prompt 改造
|
||||
|
||||
### 6.1 受影响的 PromptSection
|
||||
|
||||
| Section | 文件 | 当前内容 | 改造后 |
|
||||
|---------|------|---------|-------|
|
||||
| TaskApiSection | task_handler.py | 黑板 API(status 回写、outputs 提交) | 删除 status 回写(daemon 自动管);outputs 改为 git push |
|
||||
| ToolchainApiSection | toolchain_handler.py | 黑板 API(action_report comment、outputs) | action_report 改为 Issue comment;outputs 改为 git push |
|
||||
| TaskConstraintsSection | task_handler.py | "blackboard comment" 引用 | 改为 "Issue/PR comment" |
|
||||
| ToolchainConstraintsSection | toolchain_handler.py | 已禁止 Mail | 同时告知 agent 使用 Gitea 协作 |
|
||||
|
||||
### 6.2 agent prompt 新增指引
|
||||
|
||||
agent 需要知道工作方式变了。新增一个通用 section(或加入现有 constraints section):
|
||||
|
||||
```
|
||||
## 协作方式
|
||||
- 你的任务通过 Gitea Issue 管理
|
||||
- 需求描述在 Issue body 中
|
||||
- 进展汇报通过 Issue comment
|
||||
- 代码产出通过分支 commit + PR
|
||||
- 审查通过 PR Review
|
||||
- 不要使用黑板 API,不要使用 Mail API
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## §7. Issue ↔ 分支 ↔ PR 关系
|
||||
|
||||
| 场景 | Issue : 分支 : PR |
|
||||
|------|------------------|
|
||||
| 简单任务(bugfix、小功能) | 1 : 1 : 1 |
|
||||
| 复杂任务(多阶段) | 1 : 1 : N(分阶段提交 PR,同一个分支) |
|
||||
| 极复杂任务(需拆解) | 1 : N : N(Issue body 列出子任务,每个一个分支+PR) |
|
||||
|
||||
**默认 1:1:1**。分支命名规范不变:`fix/{issue_number}-{brief}`。
|
||||
|
||||
---
|
||||
|
||||
## §8. 不做的事
|
||||
|
||||
| 不做 | 理由 |
|
||||
|------|------|
|
||||
| 不做数据迁移 | 主公确认当前无正式使用数据 |
|
||||
| 不做 Issue 状态 label(status/xxx) | 中间状态 daemon 内部管,Issue 只有 open/closed |
|
||||
| 不改 Mail | Mail 职责不变。tc 和 task 都不使用 Mail |
|
||||
| 不改前端(本阶段) | 前端改造独立于后端,后续设计 |
|
||||
| 不做存量 task 退役 | 原 task 流程和 Issue 流程可共存,原 task 自然退役 |
|
||||
| 不改 experiences/checkpoints/decisions 表 | 执行面表保留在 daemon,不受影响 |
|
||||
|
||||
---
|
||||
|
||||
## §9. 实施路径
|
||||
|
||||
| 阶段 | 内容 | 依赖 |
|
||||
|------|------|------|
|
||||
| Phase 1 | task_index 表创建 + webhook handler 适配(Issue assigned → 建索引) | 设计 Review 通过 |
|
||||
| Phase 2 | dispatcher/ticker 数据源从 tasks 表切换到 task_index | Phase 1 |
|
||||
| Phase 3 | spawner 读 Issue body 构建 prompt(替代读黑板 description) | Phase 2 |
|
||||
| Phase 4 | prompt 改造(黑板 API → Gitea API) | Phase 3 |
|
||||
| Phase 5 | 验证 + 清理废弃的黑板协作面表 | Phase 4 |
|
||||
|
||||
每个 Phase 独立可验证,出问题可以回退。
|
||||
|
||||
---
|
||||
|
||||
## §10. 风险评估
|
||||
|
||||
| 风险 | 等级 | 缓解 |
|
||||
|------|------|------|
|
||||
| Gitea API 不可用时 daemon 完全瘫痪 | 中 | webhook 触发时缓存 Issue body(P1 方案 B),减少运行时 Gitea API 依赖 |
|
||||
| Gitea webhook 丢失 | 低 | ticker 兜底扫描 |
|
||||
| task_index 和 Gitea Issue 状态不一致 | 中 | ticker 定期校验(发现 Issue closed 但 index 未更新则修复) |
|
||||
| spawner 性能下降(Gitea API 调用) | 低 | 方案 B 缓存 Issue body,spawn 时不调 Gitea API |
|
||||
| 原 task 流程和新 Issue 流程共存期混乱 | 中 | 可以限定只在特定项目中启用 Issue 模式,逐步切换 |
|
||||
Reference in New Issue
Block a user