Files
sanguo_moziplus_v2/docs/design/21-unified-toolchain-design.md
T
cfdaily 65eb7d6a99
CI / lint (pull_request) Successful in 19s
CI / test (pull_request) Successful in 1m4s
CI / frontend (pull_request) Successful in 12s
CI / notify-on-failure (pull_request) Successful in 0s
[moz] docs(§21): fix 姜维 S1 分支名前缀按 type + S2 sub Issue body 加 Depends
2026-06-20 21:24:28 +08:00

896 lines
36 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.
---
title: "Unified Toolchain Design — 统一工具链工作流设计"
created: 2026-06-20
version: v1.0 draft
status: draft
changelog: v1.0 初版
---
# Unified Toolchain Design
> **范围**: 仅 toolchain 流程。task 和 mail 不变。
> **前置**: §17 ToolchainHandler、§20 Issue-Centric Orchestration
> **目标**: 把割裂的单点优化整合为统一的 AI native 工作流
---
## §1. 背景与问题
### 1.1 当前割裂点
| # | 割裂 | 现状 | 本文解决 |
|---|------|------|---------|
| 1 | agent 同时引用两套 API | 黑板 APIaction report/outputs+ Gitea APIIssue/PR comment | §3 统一到 Gitea |
| 2 | steps 硬编码 | 9 处 `steps=[...]` 写死在 toolchain_routes.py | §4 模板化 |
| 3 | issue_assigned 只分 2 路 | 只按 infrastructure label 分流,不分 type/feat vs type/docs | §5 按 type/* 6 路分流 |
| 4 | action_type 和 business_type 混在一起 | issue_assigned 内部混入业务场景 | §6 分离两个维度 |
| 5 | verify 依赖黑板 comment_type | 迁移到 Gitea 后失效 | §7 重新定义完成检测 + 输出约束 |
### 1.2 黑板中的"无缝接续"设计(不能丢弃)
黑板 DB 中有多个机制确保后续 agent 可以无缝接续:
| 机制 | 当前实现 | 用途 | 迁移到 Issue 后 |
|------|---------|------|----------------|
| **任务描述** | tasks.title + tasks.description | agent 知道"做什么" | Issue title + body ✅ 直接对应 |
| **验收标准** | tasks.must_haves | agent 知道"怎样算完成" | Issue body 中结构化字段(模板定义)✅ |
| **前序产出** | outputs 表 + depends_on | agent 知道"之前做了什么" | Issue body 引用前序 Issue/PR(如 `Depends: #42`)⚠️ 需约定 |
| **handoff comment** | comment_type=handoff | agent 之间交接上下文(≥50 字符) | Issue/PR comment ✅ 天然支持 |
| **讨论历史** | comments 表 | agent 知道"讨论了什么" | Issue/PR comment 全部可读 ✅ |
| **审查结果** | reviews 表(verdict/round/consensus | agent 知道"审查结论" | PR ReviewAPPROVE/REQUEST_CHANGES+ Issue comment 记录 |
| **agent 声明式交接** | next_capability 字段 | agent 声明"我搞不定,需要 XX 能力的人" | Issue comment @对方(已有 mention 机制)✅ |
| **retry 历史** | task_attempts 表 | daemon 知道"试了几次" | daemon 内部 task_state 表 ✅ 不变 |
| **风险观察** | observations 表 | agent 标记"发现风险" | Issue comment(约定标记)⚠️ 需约定 |
**结论**:大部分机制可以自然迁移到 Issue。两个需要约定:
1. **前序产出引用**Issue body 中用 `Depends: #N``Parent: #N` 引用前序 Issue
2. **风险观察**Issue comment 中用约定标记(如 `⚠️ [观察]`
---
## §2. 设计原则
1. **Gitea only**toolchain agent 只操作 Gitea,不引用黑板 API、不用 Mail
2. **模板驱动**:steps 从硬编码改为模板,不同 Issue type 对应不同流程
3. **action_type 和 business_type 分离**:事件类型决定 action_hint,业务类型决定 steps
4. **完成检测:终态事件 + 输出约束**:用 Gitea 终态事件检测完成 + 结构化输出约束 agent 汇报
5. **无缝接续不丢弃**:handoff/前序产出/审查结果等机制迁移到 Issue 语义
---
## §3. 统一到 Gitea(割裂 1 解决)
### 3.1 agent API 引用变更
| 操作 | 现在(黑板 API | 改造后(Gitea API |
|------|----------------|-------------------|
| 提交 action report | `POST localhost:8083/.../comments` (comment_type=action_report) | `POST Gitea .../issues/{N}/comments`(结构化 body,§7 定义) |
| 提交产出 | `POST localhost:8083/.../outputs` | git push 到分支(代码/文档/测试) |
| 讨论 / @mention | `POST localhost:8083/.../comments` | `POST Gitea .../issues/{N}/comments` |
| 创建 PR | 已是 Gitea API(不变) | 不变 |
| 创建 Issue | 已是 Gitea API(不变) | 不变 |
### 3.2 agent prompt 中的 API 指引
ToolchainApiSection 改造——去掉所有 `localhost:8083` 引用,只保留 Gitea API
```
## 操作指令
### 汇报执行结果
执行完步骤后,在关联的 Issue 上 comment 汇报:
```bash
curl -X POST "{GITEA}/repos/{repo}/issues/{N}/comments" \
-H "Authorization: token <token>" \
-d '{"body": "[Action Report]\n\n**操作**...\n**结果**...\n**CI**..."}'
```
### 需要其他角色支持
在关联的 Issue/PR 上 comment @对方(已有机制)
### 代码产出
git push 到功能分支 → 创建 PR
```
---
## §4. steps 模板化(割裂 2 解决)
### 4.1 模板存储
steps 模板存放在 daemon 配置文件 `config/toolchain-templates.yaml`
```yaml
# 每种 business_type 对应一套 steps + output_template
issue_assigned:
feature:
steps:
- "理解需求(Issue body)→ 如有不明确在 Issue comment 追问"
- "git checkout main && git pull origin main"
- "git checkout -b fix/{issue_number}-{brief}"
- "编码实现 + 写 UT"
- "git add -A && git commit -m '[moz] feat: {title}' && git push"
- "创建 PRbody 引用 IssueCloses #{issue_number}"
- "等 CI + Review"
output_template: |
[Action Report]
**分支**fix/{issue_number}-{brief}
**PR**#{pr_number}
**改动文件**{files}
**CI**{ci_status}
impl:
steps:
- "读设计文档(Issue body 中的路径)→ 理解实现范围"
- "git checkout main && git pull origin main"
- "git checkout -b impl/{issue_number}-{brief}"
- "按设计编码实现 + 写 UT"
- "git add -A && git commit -m '[moz] impl: {title}' && git push"
- "创建 PRbody 引用 Issue + 设计文档路径)"
- "等 CI + Review"
output_template: |
[Action Report]
**设计文档**{design_doc}
**分支**impl/{issue_number}-{brief}
**PR**#{pr_number}
**改动文件**{files}
**CI**{ci_status}
bug:
steps:
- "读 Bug 描述 + 复现步骤(Issue body"
- "定位根因(读代码/日志,不要猜测)"
- "git checkout main && git pull origin main"
- "git checkout -b fix/{issue_number}-{brief}"
- "修复 + 写回归测试"
- "git add -A && git commit -m '[moz] fix: {title}' && git push"
- "创建 PRbody 说明根因和修复方式)"
- "等 CI + Review"
output_template: |
[Action Report]
**根因**{root_cause}
**修复方式**{fix_approach}
**分支**fix/{issue_number}-{brief}
**PR**#{pr_number}
**CI**{ci_status}
docs:
steps:
- "读文档目标(Issue body"
- "git checkout main && git pull origin main"
- "git checkout -b docs/{issue_number}-{brief}"
- "编写文档到 docs/ 对应目录"
- "git add -A && git commit -m '[moz] docs: {title}' && git push"
- "创建 PR"
- "等 Review"
output_template: |
[Action Report]
**文档路径**{doc_path}
**分支**docs/{issue_number}-{brief}
**PR**#{pr_number}
refactor:
steps:
- "读重构目标 + 影响范围(Issue body"
- "git checkout main && git pull origin main"
- "git checkout -b refactor/{issue_number}-{brief}"
- "重构 + 确保现有测试不 breakpython -m pytest tests/unit/ -q"
- "git add -A && git commit -m '[moz] refactor: {title}' && git push"
- "创建 PR(body 说明重构内容和影响范围)"
- "等 CI + Review"
output_template: |
[Action Report]
**重构范围**{scope}
**测试结果**{test_result} passed
**分支**refactor/{issue_number}-{brief}
**PR**#{pr_number}
**CI**{ci_status}
test:
steps:
- "读测试目标(Issue body"
- "git checkout main && git pull origin main"
- "git checkout -b test/{issue_number}-{brief}"
- "编写测试脚本到 tests/ 对应目录"
- "运行测试验证(python -m pytest {test_file} -v"
- "git add -A && git commit -m '[moz] test: {title}' && git push"
- "创建 PR"
- "等 CI + Review"
output_template: |
[Action Report]
**测试文件**{test_file}
**测试结果**{test_result}
**分支**test/{issue_number}-{brief}
**PR**#{pr_number}
infrastructure:
steps:
- "根据 Issue body 中的错误来源和日志片段排查问题"
- "修复基础设施问题(CI runner/网络/Gitea/磁盘等)"
- "修复后在 Issue 上 comment 说明修复方式和结果"
- "汇报执行结果"
output_template: |
[Action Report]
**问题**{problem}
**根因**{root_cause}
**修复方式**{fix}
**验证**{verification}
# toolchain 事件(非 issue_assigned)的模板
ci_failure:
steps:
- "查看 CI 日志(PR 页面或 Gitea Actions"
- "判断失败原因:a.代码问题→修复→push b.基础设施→创建 Issue 指派 jiangwei-infra"
- "汇报结果"
output_template: |
[Action Report]
**原因类型**{cause_type}
**操作**{action}
**CI 重跑**{ci_status}
# ... 其他 toolchain 事件(review_result/review_request/...)各自定义
```
### 4.2 模板加载
daemon 启动时加载 YAML 配置,运行时按 `action_type + business_type` 查找模板:
```python
def get_steps(action_type: str, business_type: str = "") -> list[str]:
"""从模板配置获取 steps"""
section = TEMPLATES.get(action_type, {})
if isinstance(section, dict) and business_type:
return section.get(business_type, {}).get("steps", section.get("default", {}).get("steps", []))
return section.get("steps", [])
```
---
## §5. 按 type/* 6 路分流(割裂 3 解决)
### 5.1 issue_assigned handler 改造
当前只按 `infrastructure` label 分 2 路。改为按 `type/*` label 分流:
```python
# 伪代码
labels_list = [lbl.get("name", "") for lbl in (issue.get("labels") or [])]
# 1. 基础设施(不变)
if any("infrastructure" in lbl.lower() for lbl in labels_list):
business_type = "infrastructure"
# 2. 按 type/* 确定 business_type
elif "type/feat" in labels_list:
business_type = "feature"
elif "type/impl" in labels_list:
business_type = "impl"
elif "type/bug" in labels_list:
business_type = "bug"
elif "type/docs" in labels_list:
business_type = "docs"
elif "type/refactor" in labels_list:
business_type = "refactor"
elif "type/test" in labels_list:
business_type = "test"
else:
business_type = "feature" # 默认走编码流程
# 从模板获取 steps + output_template
template = get_template("issue_assigned", business_type)
steps = template["steps"]
output_template = template["output_template"]
```
### 5.2 action_hint 差异化
当前 action_hint 按 action_type 固定("你收到一个 Issue 指派...")。改为同时体现 business_type
```python
_ACTION_HINTS = {
"issue_assigned": {
"feature": "你收到一个功能需求,理解需求后编码实现。",
"impl": "你收到一个实现任务,按设计文档编码实现。",
"bug": "你收到一个 Bug 报告,定位根因后修复。",
"docs": "你收到一个文档任务,编写文档。",
"refactor": "你收到一个重构任务,重构并确保测试通过。",
"test": "你收到一个测试任务,编写测试脚本。",
"infrastructure": "你收到一个基础设施问题报告,请排查并修复。",
},
"ci_failure": "你收到一个 CI 失败通知,这是一个需要你修复失败测试的事件。",
"review_result": "你收到一个 Review 结果通知,这是一个需要你执行动作的事件。",
# ... 其他 action_type 不分 business_type
}
```
---
## §6. action_type 和 business_type 分离(割裂 4 解决)
### 6.1 两个维度
| 维度 | 来源 | 决定什么 | 示例 |
|------|------|---------|------|
| **action_type** | webhook 事件类型 | action_hint"你收到一个 XX 通知" | ci_failure / review_result / issue_assigned |
| **business_type** | Issue label type/* | steps + output_template | feature / impl / bug / docs / refactor / test |
### 6.2 组合规则
- issue_assignedaction_type=issue_assigned + business_type 从 label 确定
- ci_failureaction_type=ci_failure(无 business_typeCI 失败就是 CI 失败)
- review_resultaction_type=review_result(无 business_typeReview 就是 Review
- 只有 issue_assigned 需要 business_type 维度(因为同一个 action_type 下不同业务的流程不同)
---
## §7. 完成检测 + 输出约束(割裂 5 解决)
### 7.1 设计原则
| 关注点 | 方案 |
|--------|------|
| 完成检测 | Gitea 终态事件优先 + Issue comment 兜底 |
| 输出约束 | 按 business_type 定义的 output_template(不是空泛的"简要描述" |
### 7.2 完成检测:按 action_type 分类
| action_type | 终态信号 | 检测方式 | 兜底 |
|-------------|---------|---------|------|
| issue_assigned | PR merged 或 Issue closed | webhook: pull_request/closed(merged=true) 或 issues/closed | — |
| ci_failure | agent Issue comment 汇报 | Issue comment 检测([Action Report] 标记) | ⚠️ Gitea 1.26.2 不触发 CI status webhook,只能靠 comment 兜底。ticker 可选轮询 Gitea commit status API 作为补充 |
| review_result(APPROVED) | PR merged | webhook: pull_request/closed(merged=true) | — |
| review_result(CHANGES) | agent push 到分支 | webhook: pull_request/synchronize | — |
| review_request | Review 提交 | webhook: pull_request_review | — |
| review_updated | Review 提交 | webhook: pull_request_review | — |
| review_comment | agent comment | webhook: issue_comment/created | — |
| mention | agent comment | webhook: issue_comment/created | — |
| deploy_failure | agent Issue comment 汇报 | Issue comment 检测 | ✅ |
| infrastructure_failure | agent Issue comment 汇报 | Issue comment 检测 | ✅ |
| review_merged | — | auto-pass | — |
### 7.3 状态流转:单一终态保证
一个 task 只有一个终态触发。daemon 内部状态机保证:
```
pending → working → done(终态事件触发,只触发一次)
→ failed(超时/异常)
```
终态事件到来时检查 task 当前状态:
- 如果已经 done/failed → 忽略(幂等)
- 如果 working → 标 done
- 如果 pending → 异常,记日志
**中间事件**push/comment/Review submitted**不改变 task 状态**——它们是过程中的信号,不是终态。
### 7.4 输出约束:output_template
每种 business_type 有自己的 output_template(§4.1 定义)。agent 完成后在 Issue comment 中按模板汇报。
daemon 的 verify 通过 webhook 事件检测终态(不需要检查 comment 内容)。但 output_template 的价值是**约束 agent 的汇报质量**——不是检测完成用的,而是给后续 agent/审查者提供结构化信息。
output_template 作为 steps 的最后一步注入 prompt
### 7.5 action report 识别规范
daemon 通过 webhook `issue_comment/created` 感知到新 comment 后,需要判断是否为 action report。
**匹配规则**
- 精确匹配:comment body 以 `[Action Report]` 开头(允许前导空白)
- 容错策略:如果 body 包含 `[Action Report]`(不要求开头),也接受
- 大小写不敏感
**匹配失败处理**
- 不匹配的 comment 不触发完成检测
- 作为普通讨论 comment 处理(agent 之间的 handoff/讨论)
```
最后一步:汇报执行结果,在 Issue 上 comment,格式:
[Action Report]
**根因**<根因描述>
**修复方式**<做了什么>
**分支**fix/42-xxx
**PR**#43
**CI**:✅ 通过
```
---
## §8. 无缝接续机制迁移
### 8.1 前序产出引用
当前黑板用 `depends_on` 字段 + `PriorOutputsSection` 注入前序产出摘要。
迁移到 Issue 后,在 Issue body 中用约定引用:
```markdown
## 依赖
Depends: #42(前序任务)
Parent: #40(父 Issue
## 前序产出摘要
- #42 完成了数据获取模块(分支 fix/42-dataPR #43
- 数据路径:/Volumes/stock/xxx
```
agent 读 Issue body 自然获得前序上下文。daemon 的 spawner 在构建 prompt 时,可以解析 Issue body 中的 `Depends: #N`,调 Gitea API 读取前序 Issue 的 comment(包含 action report)作为上下文注入。
### 8.2 handoff comment
当前 handoff 通过黑板 `comment_type=handoff` + ≥50 字符约束。
迁移到 Issue 后,agent 的 handoff 就是**Issue/PR comment**。不需要 comment_type 字段——所有有实质内容的 comment 都是为后续 agent 提供上下文的"handoff"。
### 8.3 审查结果
当前黑板 reviews 表存 verdict/round/consensus。
迁移后:
- **代码审查**PR ReviewGitea 原生,APPROVE/REQUEST_CHANGES
- **方案审查**(设计 PR):同上
- **庞统 round review**:保留在 daemon 内部(不迁移,这是编排逻辑)
---
## §9. prompt 层级(L0-L4 不变,L2 重组)
| 层 | 内容 | 不变? |
|---|------|-------|
| L0 铁律 | 安全底线 | ✅ 不变 |
| L1 角色 | SOUL.md / IDENTITY.md | ✅ 不变 |
| L2 引擎注入 | **本文重组** | 改造 |
| L3 被参考 | Skill 列表 | ✅ 不变 |
L2 重组后的 section 列表:
| priority | Section | 内容 | 来源 |
|----------|---------|------|------|
| 10 | ToolchainContextSection | action_hint + Issue body(需求)+ steps | 改造:从模板加载 steps |
| 20 | PriorContextSection | 前序产出(解析 Issue body 中的 Depends | 改造现有 PriorOutputsSection |
| 30 | RoleSkillSection | 角色 Skill | 不变 |
| 35 | GitOperationSection | Git 操作说明(PR #95 已有) | 不变 |
| 40 | GiteaApiSection | Gitea API 指引(Issue comment + PR 创建) | 改造:去掉黑板 API |
| 50 | ToolchainConstraintsSection | 约束 + Red Flags | 不变 |
| 55 | GiteaConventionSection | Gitea 标题规范 | 不变 |
| 60 | WikiGuideSection | 知识查询引导 | 不变 |
| 65 | DeliveryChecklistSection | 交付检查 | 改造:output_template 替代空泛的"简要描述" |
---
## §10. 涉及改动
| 文件 | 改动 | 工作量 |
|------|------|-------|
| `config/toolchain-templates.yaml` | 新建:6 种 business_type steps + output_template | 新文件 |
| `src/daemon/toolchain_handler.py` | ToolchainApiSection 改为 GiteaApiSection(去黑板 API);action_hint 支持 business_type | 改造 |
| `src/daemon/toolchain_handler.py` | verify_completion 改为终态事件检测 + Issue comment 兜底 | 改造 |
| `src/api/toolchain_routes.py` | issue_assigned handler 按 type/* 6 路分流 | 改造 |
| `src/api/toolchain_routes.py` | steps 从模板加载(替代硬编码) | 改造 |
| `src/daemon/toolchain_handler.py` | webhook handler 增加终态事件检测 | 新增 |
| `.gitea/ISSUE_TEMPLATE/` | 新增 impl.yml / docs.yml / refactor.yml | 新文件 |
| `tests/` | 更新测试 | 改造 |
---
## §11. Issue closed 事件处理
### 11.1 问题
当前 `_handle_issues` 只处理 `action == "assigned"`,不处理 `action == "closed"`。Issue 被关闭时:
- daemon 不感知(webhook `issues/closed` 被忽略)
- 创建者 / 关注者收不到通知
- 如果该 Issue 对应一个活跃的 taskdaemon 不知道 Issue 已关闭
### 11.2 设计
`_handle_issues` 增加 `action == "closed"` 分支:
**谁被通知**Issue 创建者(`issue.user.login`)。
**通知内容**(通过 toolchain task 发给创建者):
- Issue 标题 + 编号
- 关闭者(`payload.sender.login`
- 关闭时间
- Issue 上最后一个 comment 的摘要(修复说明)
**通知类型**:纯通知(event_type=issue_closedverify auto-pass,和 review_merged 一样)。
**特殊情况**
- 如果关闭者是创建者自己(自己关自己创建的),不通知(避免自环)
- 如果 Issue 没有创建者信息或创建者不是已知 agent,跳过
### 11.3 实现伪代码
```python
# _handle_issues 中新增
if action == "closed":
issue_creator = issue.get("user", {}).get("login", "")
closed_by = payload.get("sender", {}).get("login", "")
# 自己关自己创建的,不通知
if issue_creator == closed_by:
return
# 只通知已注册的 agent
if issue_creator not in AGENT_IDS:
return
# 读取最后一个 comment 作为修复摘要
comments = issue.get("comments", 0)
last_comment_summary = "(无 comment)"
# 可选:调 Gitea API 读最后一个 comment
title = f"Issue 已关闭: {issue_title} ({repo}#{issue_number})"
description = f"Issue {repo}#{issue_number} 已被 {closed_by} 关闭。\n\n{last_comment_summary}"
_send_toolchain_task(
to_agent=issue_creator,
title=title,
description=description,
event_type="issue_closed",
action_type="issue_closed",
steps=[], # 纯通知,无步骤
context_data={
"issue_number": issue_number,
"repo": repo,
"issue_title": issue_title,
"closed_by": closed_by,
},
)
```
### 11.4 _ACTION_HINTS 新增
```python
"issue_closed": "你创建的 Issue 已被关闭。这是一条纯通知,阅读即可。",
```
### 11.5 EVENT_LABELS_ZH 新增
```python
"issue_closed": "Issue 已关闭",
```
### 11.6 verify_completion
issue_closed 走 auto-pass(和 review_merged 一样),纯通知不需要 agent 动作。
### 11.7 涉及改动
| 文件 | 改动 |
|------|------|
| `src/api/toolchain_routes.py` `_handle_issues` | 新增 `action == "closed"` 分支 |
| `src/daemon/toolchain_handler.py` `_ACTION_HINTS` | 新增 issue_closed |
| `src/daemon/toolchain_handler.py` `EVENT_LABELS_ZH` | 新增 issue_closed |
| `src/daemon/toolchain_handler.py` `verify_completion` | issue_closed auto-pass |
| `templates/toolchain/issue_closed.md` | 新建通知模板 |
| `tests/` | 新增 closed 事件测试 |
---
## §12. AI Native 能力完整性(v2 补充)
> 本节确保 Gitea 替代黑板后,PRD-v3.0 的 AI native 能力不降级。
> 对照 §01 四相循环实现 + spawner.py / ticker.py / operations.py 的实际代码逐项检查。
### 12.1 完整能力对照表
| # | AI native 能力 | 黑板实现 | Gitea Issue 对应 | 保留? | §21 补充 |
|---|-------------|---------|-----------------|-------|---------|
| 1 | **Discussion 讨论** | spawner DISCUSSION_PROMPT_TEMPLATE + spawn_type=discussion + ticker 广播 | parent Issue 创建后 ticker 广播 spawn discussion promptIssue comment 是讨论空间 | ✅ | §13 |
| 2 | **Agent 自建 sub** | agent POST /tasks {parent_task, must_haves} | agent POST Gitea /issues {title: "[repo][sub][parent #N] ..."} + assign 自己 | ✅ | §14 |
| 3 | **@mention 通知** | comment mentions 字段 → mention_queue → ticker 扫描 → spawn 被@者 | Gitea Issue/PR comment @ → webhook issue_comment → daemon 解析 @ → spawn | ✅ | §15 |
| 4 | **Round review** | _check_round_complete → parent sub 全终态 → spawn 庞统三问 | daemon 扫 task_state parent 下所有 sub 终态 → spawn 庞统 review | ✅ | §16 |
| 5 | **Retry 上下文** | retry_count 字段 + _build_retry_context | task_state.retry_count + retry prompt(不变) | ✅ | 不需额外 |
| 6 | **Handoff comment** | comment_type=handoff + ≥50 字符 | Issue/PR comment(无 type 区分,但 Boids 行为准则约束 agent 写实质内容) | ✅ | §17 |
| 7 | **Outputs 产出物** | outputs 表 {agent, type, content_path, summary} | 分支 commit(代码/文档)+ Issue comment(摘要) | ✅ | 不需额外 |
| 8 | **Depends_on 前序** | tasks.depends_on 字段 + PriorOutputsSection | Issue body `Depends: #N` + daemon 解析注入 | ✅ | §17 |
| 9 | **Boids 行为准则** | DISCUSSION_PROMPT 中的 4 条准则 | discussion prompt 不变(Boids 准则在 prompt 中,与存储介质无关) | ✅ | §13 |
| 10 | **Agent 自主涌现** | 无 assignee 的 parent task → ticker 广播 → agent 自主讨论/创建 sub | 无 assignee 的 parent Issue → ticker 广播 discussion → agent 自建 sub Issue | ✅ | §13/§14 |
| 11 | **Guardrail 安全红线** | dispatcher check_task → violations → block | 不变(guardrail 查 task_state,不依赖黑板) | ✅ | 不需额外 |
| 12 | **Classify outcome** | spawner _classify_outcome → done/failed/pending | 不变(classify 逻辑在 daemon 内部) | ✅ | 不需额外 |
| 13 | **Rebuttal** | review.py submit_rebuttal → @mention assignee + 回到 working | PR Review REQUEST_CHANGES → webhook → daemon 通知 agent | ✅ | 不需额外 |
| 14 | **Checkpoints** | checkpoint_routes.py → approve/reject | PR Review(代码/方案审查统一走 PR Review) | ✅ | 不需额外 |
### 12.2 结论
**14 项 AI native 能力全部可保留。** 其中需要补充设计的是 5 项(§13-§17),其余沿用现有 daemon 逻辑不变。
---
## §13. Discussion 能力保留
### 13.1 设计
庞统创建 parent Issue(无 assignee)后,触发 discussion
```
庞统创建 parent Issue → webhook: issues/assigned(或 ticker 发现 pending 无 assignee
→ daemon 检测:无 assignee = 广播讨论
→ ticker 广播 spawn 所有 agentspawn_type=discussion
→ 每个 agent 收到 DISCUSSION_PROMPT_TEMPLATE
```
### 13.2 Discussion Prompt 设计(v3 重构)
**设计参考**:Edict(角色驱动主动发言)+ APM(自包含 Task Prompt+ PAV 循环(输入/输出/验证)。
当前问题:DISCUSSION_PROMPT 只列了"你可以做什么",没引导 agent 思考"我和这个需求什么关系"。agent 读完和自己没关系就忽略了。
重构后 discussion prompt
```
你被 spawn 来参与 Gitea Issue 讨论。这是一个四相循环的讨论环节。
## 讨论主题
{parent Issue body 全文}
## 你是谁
你是 {agent_id}{display_name}),你的角色是 {role},你的专业能力是 {capabilities}。
(如:你是 zhangfei-dev(张飞 翼德),角色是编码先锋,能力是编码/脚本/实现)
## 你必须做什么
读完需求后,在 Issue 上 comment 回应(必须,不是可选):
1.【定位】这个需求和你有什么关系?你的专业能力能贡献什么?
2.【建议】你对实现方案有什么建议?(技术选型、数据来源、实现路径)
3.【认领】如果你需要参与,创建 sub Issue 并在 parent Issue comment 注册:
- 创建 sub IssuePOST /repos/{repo}/issues
title: "[repo][sub][parent #{N}] 任务名"
body: "Parent: #{N}\nDepends: #M (如果有前序依赖)\n## 任务\n..."
assignees: ["{你的 agent_id}"]
- 创建后在 parent Issue comment"[{你的角色名}] 我创建了 sub #{M}: {任务名},我负责 {简述}"
4.【风险】如果你发现风险、不合理的假设、或遗漏的环节,直接提出
⚠️ 每个 agent 必须 comment。即使你认为和自己无关,也要说明原因——这证明你读过并思考过了。不 comment 的 agent 会被视为未参与讨论。
## Comment 格式
你的 comment 必须以角色名开头,让其他人知道你是谁:
[{角色名}] {你的观点}
例:[张飞] 我来负责策略编码,用 vnpy CtaTemplate 实现。
例:[关羽] 这个策略需要风控,连亏 3 天应暂停。
例:[赵云] 数据已就绪,2024-08 缺失已补齐。
例:[姜维] 我与此需求无直接关系,但建议关注回测滑点设置。
## APIGitea
- 读 Issue 详情+commentsGET /repos/{repo}/issues/{N}
- 写 commentPOST /repos/{repo}/issues/{N}/comments
- 创建 sub IssuePOST /repos/{repo}/issues
## 行为准则
1. 你是自主的。读 Issue、思考、行动,不要等指令。
2. 不重复别人的工作。动手前先读 Issue comments 看谁在做什么(Separation)。
3. 保持方向对齐。你的产出方向和 parent goal 对齐,不确定时 @pangtong-fujunshiAlignment)。
4. 产出可共享。产出写入 Issue comment,让其他人能看到你的成果(Cohesion)。
5. 不越界。安全红线不要碰,超出能力的 @ 庞统升级(Boundary)。
6. 随时讨论。执行过程中需要协作时 @ 对应 Agent,讨论是灵活的不是固定阶段的。
```
**与现有实现差异**
1. 新增"你是谁"段——agent 知道自己的角色和能力,有定位感
2. 新增"你必须做什么"——4 条必须回应的维度,不是可选
3. 新增 comment 格式——以角色名开头,其他人知道是谁在说
4. 新增"创建 sub 后在 parent comment 注册"——parent Issue comment 流自然包含所有 agent 的表态和分工
5. 底线约束:不 comment = 未参与——强制每个 agent 思考和表态
**输入/输出/验证标准**PAV 循环):
| 阶段 | 输入 | 输出 | 验证 |
|------|------|------|------|
| Discussion | parent Issue bodygoal| 每个 agent 的 comment(定位+建议+认领+风险)| 所有被 spawn 的 agent 都 comment 了 |
### 13.3 庞统初始引导
庞统创建 parent Issue 时,可以在 Issue body 中 @ 特定 agent 引导认领:
```
Issue body:
## 任务
做一个双均线量化策略...
## 建议分工
@zhangfei-dev 你来认领策略编码
@zhaoyun-data 你来认领数据准备
```
但庞统不需要知道全部——关羽可能发现需要风控,自己创建 sub 去做。司马懿可能发现需要测试,自己创建 sub。**这是涌现,不是分配。**
### 13.4 Parent Issue 中的 sub 注册
agent 创建 sub Issue 后,**自己在 parent Issue 上 comment 注册**(不需要 daemon 做):
```
[张飞] 我创建了 sub #101: 策略编码,我来负责实现。
技术方案:用 vnpy CtaTemplate,金叉买入死叉卖出。
```
Gitea 的 Issue reference 功能会自动在 parent Issue timeline 显示 "Referenced by #N"。
agent 的 comment + Gitea 自动引用 = parent Issue 中有完整的 sub 注册信息。
### 13.5 分支创建时机
| 阶段 | 谁创建分支? | 分支名 |
|------|-----------|-------|
| DiscussionPhase 3 | ❌ 不创建分支 | — |
| ExecutorPhase 4 | agent 收到 executor prompt 后自己创建 | `{type}/{sub_issue_number}-{brief}`type 按 Issue 类型:fix/feat/impl/docs/refactor/test |
**executor prompt 的 steps 中明确指定分支名**
```
git checkout -b fix/{issue_number}-{brief}
```
多分支并行场景(每个 sub Issue number 不同,分支名不同,不冲突):
```
张飞 sub #101 → 分支 fix/101-dual-ma-strategy
关羽 sub #102 → 分支 fix/102-risk-control
司马懿 sub #103 → 分支 fix/103-strategy-test
```
### 13.6 L2 输入输出约束(PAV 循环)
每种 prompt 都有明确的输入/输出/验证标准:
| prompt 类型 | 输入 | 输出 | 验证 |
|------------|------|------|------|
| Discussion | parent Issue bodygoal| 每个 agent comment(定位+建议+认领+风险)| 所有 spawn 的 agent 都 comment 了 |
| Executor | sub Issue body + parent Issue body + 所有 comments | [Action Report](分支+PR+改动+CI| PR 创建 + CI 通过 |
| Review(司马懿)| PR diff + sub Issue body + parent goal | Review verdictAPPROVE/REQUEST_CHANGES| Review API 提交 |
| Round Review(庞统)| parent Issue body + 所有 sub outputs + 所有 comments | 三问评估(goal 一致性/成果覆盖/下一步)| GOAL_ACHIEVED 或新轮 sub |
---
## §14. Agent 自建 sub Issue 模式
### 14.1 设计
替代当前的 claim 竞争模式。每个 agent 自己创建 sub Issue + assign 自己。
**标题格式**
```
[quant][sub][parent #100] 策略编码
```
- `[quant]` — 项目代号
- `[sub]` — 标记为 sub Issue
- `[parent #100]` — parent Issue 编号(Gitea 自动渲染 #100 为链接)
- 后面是人类可读的任务描述
**Sub Issue body**
```markdown
Parent: #100
## 任务
从 parent Issue 继承的具体任务描述
## 验收标准
...
```
Gitea 的 Issue reference 功能会自动在 parent Issue #100 上显示 "Referenced by #101"。
### 14.2 不需要 claim 竞争
当前黑板的 claim 模式(CAS 原子操作)是为了防止两个 agent 认领同一个 task。
在 Gitea Issue 模式下,每个 agent 创建自己的 sub Issue——**不存在竞争**。各创建各的,各 assign 各的。
**重复不怕**:如果关羽和张飞创建了内容重叠的 sub Issue,庞统在 round review 时引导两人统一看法。这不是错误,是讨论的契机。**不做严谨工作流,做 AI 生态。**
### 14.3 daemon 内部 parent/sub 映射
daemon 维护 parent/sub 层级(用于 round review 检测):
```sql
-- task_state 表(§20 设计)
CREATE TABLE task_state (
issue_number INTEGER,
repo TEXT,
parent_issue INTEGER, -- 新增:parent Issue 编号
status TEXT DEFAULT 'pending',
...
);
```
daemon 监听 Issue 创建 webhook → 解析标题中的 `[parent #N]` → 记录 parent_issue。
⚠️ **需同步更新 §20 task_state DDL** 新增 `parent_issue INTEGER` 列。
---
## §15. @mention 通知迁移
### 15.1 当前实现
```
agent 写 comment → mentions 字段 → mention_queue 表 → ticker 扫描 → spawn 被@者
```
### 15.2 Gitea 迁移
```
agent 写 Issue/PR comment → webhook: issue_comment/created → daemon 解析 @ → spawn 被@者
```
**mention_queue 表保留**,但数据来源改为 webhook payload。daemon 收到 issue_comment webhook 后:
1. 正则提取 comment body 中的 `@(
[-
]*)`
2. 写入 mention_queue
3. ticker 消费 mention_queue → spawn 被@者
复用现有 mention_utils.py 的 extract_mentions 逻辑(§25 已实现)。
---
## §16. Round Review 迁移
### 16.1 当前实现
ticker._check_round_complete
1. 扫描所有 parent task
2. 检查 sub task 是否全部终态
3. spawn 庞统 review(三问框架)
### 16.2 Gitea 迁移
ticker._check_round_complete 改为:
1. 扫描 task_state 中 parent_issue IS NOT NULL(替代原 `SELECT DISTINCT parent_task FROM tasks`,语义不变——都是找有子任务的 parent)
2. 找到所有 parent_issue 相同的 sub Issue
3. 检查 sub Issue 是否全部终态(通过 task_state.status
4. 全部终态 → spawn 庞统 review
**庞统三问 prompt 不变**
```
1. Goal 还清晰吗?(是否有 goal drift)
2. 成果物覆盖 goal 了吗?(逐条检查验收标准)
3. 下一轮需要做什么?(创建新 sub / 标记完成 / 调整方向)
```
庞统通过 Gitea API 读 parent Issue bodygoal+ 所有 sub Issue 的 comments/outputs → 做评估。
---
## §17. 无缝接续机制完整迁移
### 17.1 handoff comment
当前:comment_type=handoff + ≥50 字符
Gitea:所有 Issue/PR comment 都是天然的 handoff。Boids 行为准则约束 agent 写实质内容。不强制 ≥50 字符——用 Boids 准则引导比硬阈值更 AI native。
### 17.2 depends_on 前序引用
当前:tasks.depends_on 字段 + PriorOutputsSection
GiteaIssue body 中 `Depends: #N`。daemon 解析 → 读前序 Issue 的 comments(含 Action Report)→ 注入 prompt。
### 17.3 retry 上下文
currentretry_count 字段 + _build_retry_context
Giteatask_state.retry_count(不变,daemon 内部)。retry prompt 中加入前序 Issue/PR 链接供 agent 参考。
### 17.4 状态转换流转
agent 完成后的状态转换(working → review → done)全部由 daemon 内部管理。agent 不需要手动 POST status。daemon 通过以下信号自动感知:
| 信号 | 来源 | 触发 |
|------|------|------|
| agent 创建 sub Issue | webhook: issues/opened | sub status=pending → dispatch |
| agent 创建 PR | webhook: pull_request/opened | sub status → review |
| PR Review 提交 | webhook: pull_request_review | review 结果 → done 或 back to working |
| PR merge | webhook: pull_request/closed(merged) | sub status → done + Issue auto-close |
| agent Issue comment | webhook: issue_comment/created | 检查是否为 Action Report → 终态检测 |
---
## §18. 不做的事
| 不做 | 理由 |
|------|------|
| 不改 task handler | task 流程不变(§20 设计中的 task 逐步迁移到 Issue 是后续工作) |
| 不改 mail | mail 职责不变 |
| 不改 dispatcher/ticker 核心逻辑 | 调度逻辑不变(§20 Phase 1 的 dispatcher SQL 迁移是前置工作) |
| 不做前端改造 | 后续独立设计 |
| 不改 experiences/checkpoints/decisions 表 | 执行面表保留在 daemon |