docs: add #19 toolchain context layers design #11
@@ -0,0 +1,372 @@
|
||||
# #19 工具链事件中枢 — 上下文四层改造方案
|
||||
|
||||
> 版本: v1.0
|
||||
> 日期: 2026-06-09
|
||||
> 作者: 庞统(副军师)
|
||||
> 状态: 待主公确认
|
||||
> 前置: #13 工具链与开发流程 §16, #05 上下文四层架构
|
||||
> 来源: E2E 真实场景测试暴露的三个断层
|
||||
|
||||
---
|
||||
|
||||
## 一、问题诊断
|
||||
|
||||
### 1.1 E2E 真实场景测试暴露的三个断层
|
||||
|
||||
主公在 moziplus-v2 仓库创建了 Issue #32(添加 /api/stats 端点),指派张飞。链条在第一步就断了。
|
||||
|
||||
| 断层 | 现象 | 根因 |
|
||||
|------|------|------|
|
||||
| **Agent 不知道该做什么** | 张飞收到 Issue 指派 Mail,回复"已阅"就结束了 | Mail 模板(issue_assigned.md)5 行信息,无流程引导;spawn prompt 说"已阅即可" |
|
||||
| **Agent 去错了仓库** | 张飞去读了 sanguo_moziplus_v2 平台代码,而不是空的实验仓库 moziplus-v2 | Mail 模板没有仓库 clone URL,张飞凭习惯去了开发目录 |
|
||||
| **Agent 在 Control UI 提问** | 张飞遇到问题直接在 Control UI 问主公,没有去 Gitea Issue 评论 | 没有任何地方引导"有疑问去 Gitea Issue 评论" |
|
||||
| **Agent 不知道怎么协作** | 张飞判断任务需要澄清,但不知道该怎么请求澄清 | 没有"做不了→在 Issue 评论 / Mail 庞统"的回退路径 |
|
||||
| **跨 Agent @mention 无法通知** | 张飞在 Issue 评论 @赵云,赵云收不到通知 | issue_comment handler 只处理 [CI] 评论,@mention 被忽略 |
|
||||
|
||||
### 1.2 根因:工具链在四层架构中的断层
|
||||
|
||||
| 层 | 应该有 | 实际有 | Gap |
|
||||
|---|---|---|---|
|
||||
| **L0 铁律** | — | — | 无需改动 |
|
||||
| **L1 角色** | 工具链协作行为规范(所有 Agent 共享) | 无 | AGENTS.md 没有工具链相关内容 |
|
||||
| **L2 引擎注入** | 事件上下文(仓库 clone URL、Gitea API、Issue/PR 详情) | Mail 模板只有 5 行摘要 | 缺仓库信息和流程引导 |
|
||||
| **L3 被动参考** | 技术细节(分支命名、commit 规范、PR 创建方式) | git-workflow 等 Skill 已存在但没人触发 | Agent 不知道该加载哪个 Skill |
|
||||
|
||||
---
|
||||
|
||||
## 二、改造方案:四层归属
|
||||
|
||||
### 2.1 分层原则
|
||||
|
||||
| 层 | 放什么 | 不放什么 | 理由 |
|
||||
|---|---|---|---|
|
||||
| **L0** | 不放 | — | 工具链不是安全底线 |
|
||||
| **L1** | 协作行为规范:收到什么通知该做什么、遇到问题怎么办 | 技术细节(分支命名、commit 格式) | 行为规范是团队常识,每个 Agent 都要知道 |
|
||||
| **L2** | 事件上下文:仓库 clone URL、Gitea API URL、Issue/PR 链接、动态信息 | 固定的协作流程 | 动态信息每次不同,由 Mail 模板 + spawn 时注入 |
|
||||
| **L3** | 技术细节:git-workflow、code-review 等 Skill 全文 | — | 按需加载,Agent 知道"我要提 PR"后自己读 |
|
||||
|
||||
### 2.2 各层具体内容
|
||||
|
||||
#### L1:AGENTS.md 加工具链协作行为段(所有 Agent 统一)
|
||||
|
||||
```markdown
|
||||
## 工具链协作(Gitea)
|
||||
|
||||
收到 Gitea 事件通知(Issue 指派、Review 请求、CI 失败等)时,按以下流程操作:
|
||||
|
||||
### 基本流程
|
||||
- **Issue 指派** → clone 仓库 → 开分支 → 编码 → 提 PR(参考 git-workflow Skill)
|
||||
- **Review 请求** → 读 PR diff(Gitea API)→ 提交 Review(参考 code-review Skill)
|
||||
- **Review 通过** → 等 merge
|
||||
- **Review 驳回** → 看 review body → 修代码 → 重新 push
|
||||
- **CI 失败** → 看错误摘要 → 修代码 → push(自动重触发 CI)
|
||||
- **部署失败** → 查 deploy 日志 → 修复
|
||||
|
||||
### 协作规则
|
||||
- **有疑问?** 在 Gitea Issue 下评论,不要在 Control UI 或 Mail 里问
|
||||
- **需要别人帮忙?** 在 Issue 评论中 @mention 对应 Agent(如 @zhaoyun-data)
|
||||
- **做不了?** 回复 Mail 说明原因和建议的接手人
|
||||
- **获取完整上下文** → 用 Gitea API 拉取 Issue 详情和评论,不要只看 Mail 里的快照
|
||||
|
||||
### Gitea API 速查
|
||||
- Issue 详情: GET /api/v1/repos/{owner}/{repo}/issues/{number}
|
||||
- Issue 评论: GET /api/v1/repos/{owner}/{repo}/issues/{number}/comments
|
||||
- PR diff: GET /api/v1/repos/{owner}/{repo}/pulls/{number}.diff
|
||||
- 提交 Review: POST /api/v1/repos/{owner}/{repo}/pulls/{number}/reviews
|
||||
```
|
||||
|
||||
**改动范围**:6 个 Agent 的 AGENTS.md 各加一段(内容统一)。
|
||||
|
||||
#### L2:Mail 模板精简 + 事件上下文注入
|
||||
|
||||
**原则**:模板只放摘要 + 链接 + 仓库信息,不写固定步骤(步骤在 L1)。
|
||||
|
||||
**issue_assigned.md** 改为:
|
||||
|
||||
```markdown
|
||||
Issue 指派
|
||||
|
||||
Issue: {issue_url}
|
||||
标题: {issue_title}
|
||||
标签: {labels}
|
||||
|
||||
📋 获取完整上下文(先读再动手):
|
||||
- Issue 详情: GET {gitea_api}/repos/{repo}/issues/{issue_number}
|
||||
- Issue 评论: GET {gitea_api}/repos/{repo}/issues/{issue_number}/comments
|
||||
|
||||
仓库: {repo_clone_url}
|
||||
建议分支: feat/issue-{issue_number}-{brief}
|
||||
```
|
||||
|
||||
**review_request.md** 改为:
|
||||
|
||||
```markdown
|
||||
PR Review 请求
|
||||
|
||||
PR: {pr_url}
|
||||
标题: {pr_title}
|
||||
作者: {pr_author}
|
||||
分支: {branch}
|
||||
风险级别: {risk_level}
|
||||
|
||||
📋 获取完整上下文:
|
||||
- PR diff: GET {gitea_api}/repos/{repo}/pulls/{pr_number}.diff
|
||||
- PR 文件列表: GET {gitea_api}/repos/{repo}/pulls/{pr_number}/files
|
||||
```
|
||||
|
||||
**review_result.md** 改为:
|
||||
|
||||
```markdown
|
||||
Review {result}
|
||||
|
||||
PR: {pr_url}
|
||||
标题: {pr_title}
|
||||
审查者: {reviewer}
|
||||
|
||||
{review_body}
|
||||
```
|
||||
|
||||
**ci_failure.md** 改为:
|
||||
|
||||
```markdown
|
||||
CI 失败
|
||||
|
||||
Issue: {issue_url}
|
||||
分支: {branch}
|
||||
|
||||
错误摘要:
|
||||
{error_summary}
|
||||
|
||||
📋 CI 日志: {gitea_url}/{repo}/actions
|
||||
修复后 push 会自动重触发 CI。
|
||||
```
|
||||
|
||||
**deploy_failure.md** 改为:
|
||||
|
||||
```markdown
|
||||
部署失败
|
||||
|
||||
仓库: {repo}
|
||||
Commit: {commit_sha}
|
||||
|
||||
📋 排查步骤:
|
||||
- CI 日志: {gitea_url}/{repo}/actions
|
||||
- 服务器: pm2 logs {service_name}
|
||||
```
|
||||
|
||||
**L2 代码改动**(`toolchain_routes.py`):
|
||||
|
||||
1. 从 Webhook payload 的 `repository` 对象提取 `clone_url` 和 `html_url`
|
||||
2. `render_template()` 传入新变量:`gitea_api`、`gitea_url`、`repo_clone_url`
|
||||
3. 所有模板变量统一补齐
|
||||
|
||||
#### L3:Skill 按需加载(不改 Skill 本身)
|
||||
|
||||
git-workflow、code-review 等 Skill 保持不变。
|
||||
|
||||
L1 的协作行为段里会引用 Skill 名称("参考 git-workflow Skill"),Agent 收到 Mail 后根据 L1 的引导自主加载对应 Skill。
|
||||
|
||||
**不改 Skill 路由机制**——靠 L1 的文案触发 Agent 的 Skill 路由器匹配。
|
||||
|
||||
---
|
||||
|
||||
## 三、新增功能:issue_comment @mention 通知
|
||||
|
||||
### 3.1 设计
|
||||
|
||||
当前 `_handle_issue_comment` 只处理 `[CI]` 前缀评论。扩展为:
|
||||
|
||||
```
|
||||
issue_comment 事件
|
||||
├── 含 [CI] / CI 失败 → 原有 CI 失败通知逻辑
|
||||
└── 含 @username → 解析 @mention → Mail 通知被 @的 Agent
|
||||
```
|
||||
|
||||
### 3.2 实现
|
||||
|
||||
**`toolchain_routes.py` 新增 `_handle_issue_comment_mention()`**:
|
||||
|
||||
```python
|
||||
AGENT_IDS = {
|
||||
"zhangfei-dev", "guanyu-dev", "zhaoyun-data",
|
||||
"jiangwei-infra", "simayi-challenger", "pangtong-fujunshi",
|
||||
}
|
||||
|
||||
# 前缀映射:@张飞 → zhangfei-dev
|
||||
AGENT_ALIAS = {
|
||||
"张飞": "zhangfei-dev",
|
||||
"关羽": "guanyu-dev",
|
||||
"赵云": "zhaoyun-data",
|
||||
"姜维": "jiangwei-infra",
|
||||
"司马懿": "simayi-challenger",
|
||||
"庞统": "pangtong-fujunshi",
|
||||
"pangtong": "pangtong-fujunshi",
|
||||
"simayi": "simayi-challenger",
|
||||
"zhangfei": "zhangfei-dev",
|
||||
"guanyu": "guanyu-dev",
|
||||
"zhaoyun": "zhaoyun-data",
|
||||
"jiangwei": "jiangwei-infra",
|
||||
}
|
||||
|
||||
def extract_mentions(body: str, sender: str) -> list[str]:
|
||||
"""从评论 body 中提取 @mention 的 Agent ID"""
|
||||
candidates = re.findall(r"@([a-zA-Z\u4e00-\u9fa5][a-zA-Z0-9\u4e00-\u9fff-]*)", body)
|
||||
result = set()
|
||||
for c in candidates:
|
||||
# 精确匹配
|
||||
if c in AGENT_IDS:
|
||||
result.add(c)
|
||||
# 前缀/别名匹配
|
||||
elif c in AGENT_ALIAS:
|
||||
result.add(AGENT_ALIAS[c])
|
||||
else:
|
||||
# 前缀模糊匹配:@zhangfei → zhangfei-dev
|
||||
for aid in AGENT_IDS:
|
||||
if aid.startswith(c):
|
||||
result.add(aid)
|
||||
break
|
||||
# 过滤掉评论者自己
|
||||
result.discard(sender)
|
||||
return list(result)
|
||||
```
|
||||
|
||||
**新增 mention 通知模板** `templates/toolchain/mention.md`:
|
||||
|
||||
```markdown
|
||||
你在 Issue 中被 @mention
|
||||
|
||||
Issue: {issue_url}
|
||||
评论者: {commenter}
|
||||
评论内容:
|
||||
{comment_body}
|
||||
|
||||
📋 获取完整上下文:
|
||||
- Issue 详情: GET {gitea_api}/repos/{repo}/issues/{issue_number}
|
||||
- Issue 评论: GET {gitea_api}/repos/{repo}/issues/{issue_number}/comments
|
||||
```
|
||||
|
||||
**改动 `_handle_issue_comment`**:
|
||||
|
||||
```python
|
||||
async def _handle_issue_comment(payload):
|
||||
comment = payload.get("comment", {})
|
||||
body = comment.get("body", "")
|
||||
sender = comment.get("user", {}).get("login", "")
|
||||
repo = _repo_fullname(payload)
|
||||
issue = payload.get("issue", {})
|
||||
|
||||
# 原有 CI 失败逻辑(不变)
|
||||
if "[CI]" in body or "CI 失败" in body:
|
||||
# ... 原有逻辑 ...
|
||||
|
||||
# 新增:@mention 通知
|
||||
mentions = extract_mentions(body, sender)
|
||||
if mentions:
|
||||
issue_number = issue.get("number", 0)
|
||||
issue_title = issue.get("title", "")
|
||||
text = render_template("mention", {
|
||||
"repo": repo,
|
||||
"issue_number": str(issue_number),
|
||||
"issue_url": issue.get("html_url", ""),
|
||||
"commenter": sender,
|
||||
"comment_body": body[:500],
|
||||
"gitea_api": "http://192.168.2.154:3000/api/v1",
|
||||
})
|
||||
title = f"@mention: {issue_title} ({repo}#{issue_number})"
|
||||
for agent_id in mentions:
|
||||
_send_mail(agent_id, title, text)
|
||||
```
|
||||
|
||||
### 3.3 去重
|
||||
|
||||
- 同一条评论 @多人:每人一封 Mail(不同 to,内容相同)
|
||||
- 同一事件 org webhook + repo webhook 双触发:现有 delivery UUID 去重机制覆盖
|
||||
- 同一人被 @多次:`extract_mentions` 返回 set,自动去重
|
||||
|
||||
---
|
||||
|
||||
## 四、Mail Spawn Prompt 改造
|
||||
|
||||
### 4.1 问题
|
||||
|
||||
当前工具链 Mail 走 Mail 通道,spawn prompt 是:
|
||||
|
||||
```
|
||||
你收到一封飞鸽传书(纯通知)。
|
||||
发件者: system
|
||||
主题: Issue 指派: xxx
|
||||
内容: [工具链模板]
|
||||
已阅即可。
|
||||
```
|
||||
|
||||
"已阅即可"直接让 Agent 不做事。
|
||||
|
||||
### 4.2 方案
|
||||
|
||||
**不改 MAIL_INFORM_TEMPLATE / MAIL_REQUEST_TEMPLATE 本身**(那是 Mail 系统通用的)。
|
||||
|
||||
改为:**工具链 Mail 使用 `type=request`(而不是默认的 inform)**。
|
||||
|
||||
在 `_send_mail()` 中,工具链事件创建的 Mail 默认 `performative=request`,这样 Agent 收到时走 `MAIL_REQUEST_TEMPLATE`,知道需要处理。
|
||||
|
||||
具体改动在 `_send_mail()` 函数或其调用处:工具链路由调用 `_send_mail` 时传入 `performative="request"`。
|
||||
|
||||
---
|
||||
|
||||
## 五、完整改动清单
|
||||
|
||||
| # | 改什么 | 改动内容 | 层 | 风险 |
|
||||
|---|--------|---------|---|------|
|
||||
| 1 | 6 个 Agent 的 `AGENTS.md` | 加"工具链协作"段(内容统一) | L1 | 低(纯追加) |
|
||||
| 2 | `templates/toolchain/issue_assigned.md` | 精简 + 加仓库上下文 + Gitea API 引导 | L2 | 低 |
|
||||
| 3 | `templates/toolchain/review_request.md` | 精简 + 加 Gitea API 引导 | L2 | 低 |
|
||||
| 4 | `templates/toolchain/review_result.md` | 精简 | L2 | 低 |
|
||||
| 5 | `templates/toolchain/ci_failure.md` | 精简 + 加 CI 日志链接 | L2 | 低 |
|
||||
| 6 | `templates/toolchain/deploy_failure.md` | 精简 + 加排查步骤 | L2 | 低 |
|
||||
| 7 | **新建** `templates/toolchain/mention.md` | @mention 通知模板 | L2 | 低 |
|
||||
| 8 | `src/api/toolchain_routes.py` | 提取 clone_url/html_url 传入模板;issue_comment 增加 @mention 解析;工具链 Mail 改为 request 类型 | L2 | 中 |
|
||||
| 9 | 不改 | git-workflow 等 Skill 保持不变 | L3 | — |
|
||||
| 10 | 不改 | daemon 核心逻辑、BootstrapBuilder、Skill 路由 | — | — |
|
||||
|
||||
---
|
||||
|
||||
## 六、验证方案
|
||||
|
||||
### 6.1 单元验证
|
||||
|
||||
| 验证点 | 方法 |
|
||||
|--------|------|
|
||||
| `extract_mentions()` 提取 `@zhangfei-dev` | unit test |
|
||||
| `extract_mentions()` 别名匹配 `@张飞` → zhangfei-dev | unit test |
|
||||
| `extract_mentions()` 前缀匹配 `@zhangfei` → zhangfei-dev | unit test |
|
||||
| `extract_mentions()` 过滤自己 | unit test |
|
||||
| 模板渲染新变量不报错 | unit test |
|
||||
|
||||
### 6.2 真实场景 E2E 验证
|
||||
|
||||
重复 Issue #32 的场景:
|
||||
1. 创建 Issue 指派张飞
|
||||
2. **验证**:张飞收到的 Mail 含 clone URL + Gitea API 引导
|
||||
3. **验证**:张飞 spawn 后知道该做什么(L1 AGENTS.md 有流程引导)
|
||||
4. **验证**:张飞有疑问时去 Gitea Issue 评论(而不是 Control UI)
|
||||
5. 在 Issue 评论 @赵云
|
||||
6. **验证**:赵云收到 @mention Mail
|
||||
|
||||
---
|
||||
|
||||
## 七、不做的事(标记为后续)
|
||||
|
||||
| 标记 | 描述 | 原因 |
|
||||
|------|------|------|
|
||||
| 后续-1 | Agent 离开工具链讨论后,是否有意识回到工具链 | 需要更多真实场景观察 |
|
||||
| 后续-2 | 工具链使用标准在所有 Agent 间的一致性验证 | L1 统一段落是第一步,需要 E2E 验证 |
|
||||
| 后续-3 | Mail 通道接入 BootstrapBuilder L2 注入 | 改动大,当前方案(L1 统一段落 + 模板引导)够用 |
|
||||
| 后续-4 | Skill 路由器自动触发(引擎注入) | 改动 daemon 核心,当前靠 L1 文案触发 |
|
||||
|
||||
---
|
||||
|
||||
## 八、变更记录
|
||||
|
||||
| 日期 | 版本 | 变更 |
|
||||
|------|------|------|
|
||||
| 2026-06-09 | v1.0 | 初版:E2E 真实场景暴露问题 → 四层改造方案 + @mention 通知 + Mail type 改造 |
|
||||
Reference in New Issue
Block a user