19 KiB
title, created, version, status, changelog
| title | created | version | status | changelog |
|---|---|---|---|---|
| Unified Toolchain Design — 统一工具链工作流设计 | 2026-06-20 | v1.0 draft | draft | v1.0 初版 |
Unified Toolchain Design
范围: 仅 toolchain 流程。task 和 mail 不变。 前置: §17 ToolchainHandler、§20 Issue-Centric Orchestration 目标: 把割裂的单点优化整合为统一的 AI native 工作流
§1. 背景与问题
1.1 当前割裂点
| # | 割裂 | 现状 | 本文解决 |
|---|---|---|---|
| 1 | agent 同时引用两套 API | 黑板 API(action report/outputs)+ Gitea API(Issue/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 Review(APPROVE/REQUEST_CHANGES)+ Issue comment 记录 |
| agent 声明式交接 | next_capability 字段 | agent 声明"我搞不定,需要 XX 能力的人" | Issue comment @对方(已有 mention 机制)✅ |
| retry 历史 | task_attempts 表 | daemon 知道"试了几次" | daemon 内部 task_state 表 ✅ 不变 |
| 风险观察 | observations 表 | agent 标记"发现风险" | Issue comment(约定标记)⚠️ 需约定 |
结论:大部分机制可以自然迁移到 Issue。两个需要约定:
- 前序产出引用:Issue body 中用
Depends: #N或Parent: #N引用前序 Issue - 风险观察:Issue comment 中用约定标记(如
⚠️ [观察])
§2. 设计原则
- Gitea only:toolchain agent 只操作 Gitea,不引用黑板 API、不用 Mail
- 模板驱动:steps 从硬编码改为模板,不同 Issue type 对应不同流程
- action_type 和 business_type 分离:事件类型决定 action_hint,业务类型决定 steps
- 完成检测:终态事件 + 输出约束:用 Gitea 终态事件检测完成 + 结构化输出约束 agent 汇报
- 无缝接续不丢弃: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"
- "创建 PR(body 引用 Issue:Closes #{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"
- "创建 PR(body 引用 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"
- "创建 PR(body 说明根因和修复方式)"
- "等 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}"
- "重构 + 确保现有测试不 break(python -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 查找模板:
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 分流:
# 伪代码
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:
_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_assigned:action_type=issue_assigned + business_type 从 label 确定
- ci_failure:action_type=ci_failure(无 business_type,CI 失败就是 CI 失败)
- review_result:action_type=review_result(无 business_type,Review 就是 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 中用约定引用:
## 依赖
Depends: #42(前序任务)
Parent: #40(父 Issue)
## 前序产出摘要
- #42 完成了数据获取模块(分支 fix/42-data,PR #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 Review(Gitea 原生,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. 不做的事
| 不做 | 理由 |
|---|---|
| 不改 task handler | task 流程不变(§20 设计中的 task 逐步迁移到 Issue 是后续工作) |
| 不改 mail | mail 职责不变 |
| 不改 dispatcher/ticker 核心逻辑 | 调度逻辑不变(§20 Phase 1 的 dispatcher SQL 迁移是前置工作) |
| 不做前端改造 | 后续独立设计 |
| 不改 experiences/checkpoints/decisions 表 | 执行面表保留在 daemon |