Add archives, import dev-pipeline skill, and complete Hermes Agent spec
- Add archives/ directory for immutable external skill snapshots - Strict immutability rule: only README/CHANGELOG can be modified - First import: dev-pipeline-universal v1.0.0 (Hermes Agent) - Import dev-pipeline-universal into hermes-agent/skills/ with CHANGELOG - Update hermes-agent/publish/registry.json with imported skill - Rewrite meta-skills/create-hermes-agent-skill/ with full spec: - SKILL.md frontmatter fields and validator constraints - Recommended body structure (Overview → When to Use → Pitfalls → Verification) - Progressive disclosure (3-level loading) - 8 writing quality principles - Official reference skills from GitHub repo - Tool chain docs (skill_manage, /learn, skill_view) - Update README.md with archives/ in directory structure
This commit is contained in:
parent
df4635a1da
commit
8dafa5bc52
@ -91,6 +91,11 @@ SkillSpace/
|
||||
├── versions/
|
||||
├── publish/
|
||||
└── docs/
|
||||
|
||||
archives/ # 外部技能归档(从其他地方开发的技能压缩包基础版)
|
||||
├── <skill-name>/ # 解压后的技能目录
|
||||
├── <skill-name>.skill # 原始压缩包(.skill / .zip / .tar.gz)
|
||||
└── README.md # 归档说明和导入流程
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
54
archives/README.md
Normal file
54
archives/README.md
Normal file
@ -0,0 +1,54 @@
|
||||
# SkillSpace Archives — 外部技能归档
|
||||
|
||||
本目录用于存放从其他地方开发的技能压缩包基础版,作为参考和导入来源。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
archives/
|
||||
├── README.md # 本说明文档
|
||||
├── <skill-name>/ # 解压后的技能目录(便于查看和引用)
|
||||
│ ├── SKILL.md
|
||||
│ ├── references/
|
||||
│ └── ...
|
||||
├── <skill-name>.skill # 原始压缩包(.skill / .zip / .tar.gz)
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 命名规范
|
||||
|
||||
- 解压目录与压缩包使用相同的 `<skill-name>` 前缀
|
||||
- 如有多个版本,追加版本号:`<skill-name>-v1.0.0.skill`
|
||||
|
||||
## 不可变规则(强制)
|
||||
|
||||
> **`archives/` 是最原始的数据来源,技能后续的版本迭代和优化严禁修改此目录下的任何技能文件。**
|
||||
|
||||
| 允许修改 | 禁止修改 |
|
||||
|---------|---------|
|
||||
| `<skill-name>/README.md`(归档说明日志) | `SKILL.md`、`prompt.md`、`_meta.json` 等技能核心文件 |
|
||||
| `<skill-name>/CHANGELOG.md`(归档日志) | `references/`、`scripts/`、`templates/` 等所有子目录和文件 |
|
||||
| 本文件 `archives/README.md` | 原始压缩包(`.skill`、`.zip`、`.tar.gz`) |
|
||||
| | `skill.json`、`tools/`、`tests/` 等所有文件 |
|
||||
|
||||
**原因**:archives/ 是技能的**原始快照**,用于:
|
||||
- 与迭代后的版本进行 diff 对比
|
||||
- 在导入版本出问题时回溯到原始状态
|
||||
- 作为不可变的审计基线
|
||||
|
||||
如需修改技能内容,必须在对应框架目录(`<framework>/skills/<skill-name>/`)中操作,而非 archives/。
|
||||
|
||||
---
|
||||
|
||||
## 用途
|
||||
|
||||
- **参考学习**:查看其他团队或社区的优秀技能实现
|
||||
- **导入复用**:将外部技能适配后纳入 SkillSpace 生命周期管理
|
||||
- **基础版本归档**:保留技能的初始版本快照,便于对比迭代
|
||||
|
||||
## 导入到 SkillSpace 流程
|
||||
|
||||
1. 从 `archives/` 中选取技能
|
||||
2. 复制到对应框架目录:`<framework>/skills/<skill-name>/`
|
||||
3. 按需适配格式(参考 `skill-publisher` 的格式适配规则)
|
||||
4. 进入生命周期管理(测试 → 审计 → 发布)
|
||||
BIN
archives/dev-pipeline-universal-v1.0.0.tar.gz
Normal file
BIN
archives/dev-pipeline-universal-v1.0.0.tar.gz
Normal file
Binary file not shown.
60
archives/dev-pipeline-universal/README.md
Normal file
60
archives/dev-pipeline-universal/README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# 🔧 通用研发流水线技能包
|
||||
|
||||
> 版本: v1.0.0 | 适用: Hermes Agent / OpenClaw / AgentSkills 兼容平台
|
||||
|
||||
## 简介
|
||||
|
||||
一套完整的研发流水线技能包,覆盖从需求分析到功能上线的全流程。包含:
|
||||
|
||||
- **7 个阶段**:需求分析 → 技术方案 → UI设计(可选) → 编码实现(结对) → 代码审查 → 测试验证 → 部署上线
|
||||
- **4 道质量门禁**:确保每个阶段产出质量
|
||||
- **7 个角色**:总管、架构师、设计师、主程序员、结对程序员、审查员、测试员
|
||||
- **测试分层策略**:单元测试 + 集成测试 + 冒烟测试
|
||||
- **实战陷阱文档**:FastAPI、前端构建、Excel导入等
|
||||
|
||||
## 安装
|
||||
|
||||
### OpenClaw
|
||||
|
||||
```bash
|
||||
# 复制到 OpenClaw 技能目录
|
||||
cp -r dev-pipeline-universal ~/.openclaw/skills/dev-pipeline
|
||||
|
||||
# 或复制到工作区技能目录(更高优先级)
|
||||
cp -r dev-pipeline-universal <workspace>/skills/dev-pipeline
|
||||
```
|
||||
|
||||
### Hermes Agent
|
||||
|
||||
```bash
|
||||
# 复制到 Hermes 技能目录
|
||||
cp -r dev-pipeline-universal ~/.hermes/skills/software-development/dev-pipeline
|
||||
```
|
||||
|
||||
## 使用前配置
|
||||
|
||||
1. 复制 `references/roles.example.yaml` 为 `references/roles.yaml`
|
||||
2. 修改模型名称为您实际可用的模型
|
||||
3. 根据您的技术栈调整测试框架配置
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
dev-pipeline-universal/
|
||||
├── SKILL.md # 主技能文档
|
||||
├── templates/
|
||||
│ └── stage-artifacts.md # 7阶段产物YAML模板
|
||||
├── references/
|
||||
│ ├── roles.example.yaml # 角色模型配置示例
|
||||
│ ├── quality-gates.md # 质量门禁详情
|
||||
│ ├── testing-strategy.md # 测试分层策略
|
||||
│ ├── fastapi-pitfalls.md # FastAPI 实战陷阱
|
||||
│ ├── frontend-build-optimization.md # 前端构建优化
|
||||
│ ├── operation-log-pattern.md # 操作日志系统模板
|
||||
│ ├── framework-comparison.md # AI框架调研对比
|
||||
│ ├── excel-data-import.md # Excel批量导入
|
||||
│ ├── revenue-allocation-pattern.md # 收益归属矩阵
|
||||
│ └── doc-sync-pattern.md # 文档同步模板
|
||||
└── scripts/
|
||||
└── show_roles.py # 角色对照表生成脚本
|
||||
```
|
||||
378
archives/dev-pipeline-universal/SKILL.md
Normal file
378
archives/dev-pipeline-universal/SKILL.md
Normal file
@ -0,0 +1,378 @@
|
||||
---
|
||||
name: dev-pipeline
|
||||
description: 通用研发流水线 — 从需求分析到功能上线的全流程 Agent 协作方案。定义角色、阶段、质量门禁、测试分层和流转规则。
|
||||
version: 1.0.0
|
||||
date: 2026-06-30
|
||||
author: 马总管 🐎
|
||||
metadata:
|
||||
openclaw:
|
||||
emoji: "🔧"
|
||||
tags: [研发流程, subagent, 多模型协作, 流水线, devops, 质量门禁, 测试分层]
|
||||
requires:
|
||||
bins: []
|
||||
hermes:
|
||||
tags: [研发流程, subagent, 多模型协作, 流水线, devops, 质量门禁, 测试分层]
|
||||
---
|
||||
|
||||
# 通用研发流水线(Dev Pipeline)
|
||||
|
||||
> **版本**: v1.0.0(通用版)
|
||||
> **创建日期**: 2026-06-30
|
||||
> **维护者**: 马总管 🐎
|
||||
> **适用平台**: Hermes Agent / OpenClaw / 其他 AgentSkills 兼容平台
|
||||
|
||||
## 一、概述
|
||||
|
||||
组建一个完整的研发小单元,覆盖从需求分析到功能上线的全流程。由总管负责调度,通过 delegate_task / subagent 机制将各阶段任务分派给不同角色的子 Agent,每个角色使用最适合的模型。
|
||||
|
||||
### 流程总览
|
||||
|
||||
```
|
||||
用户(提需求)
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ① 需求分析 — 总管 │
|
||||
│ 理解业务意图,输出结构化需求文档,跟用户确认 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼ [QG1: 需求门禁]
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ② 技术方案 — 架构师 │
|
||||
│ 读代码库 + 需求文档,输出设计方案 │
|
||||
│ 总管 review 方案,必要时跟用户确认 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼ [QG2: 设计门禁]
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ③ UI设计 — 设计师 (可选) │
|
||||
│ 输出设计规范(配色/字体/间距/组件样式代码) │
|
||||
│ 仅面向用户的项目需要,纯后端/API可跳过 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ④ 编码实现(结对编程模式,可选) │
|
||||
│ ④a 主程序员写代码 │
|
||||
│ ④b 结对程序员 review+改进 │
|
||||
│ 大型/中型启用结对,小型/热修复单程序员 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼ [QG3: 编码门禁]
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ⑤ 代码审查 — 审查员 │
|
||||
│ Review 代码变更,输出结构化审查报告 │
|
||||
│ ├─ REQUEST_CHANGES → 回到④让程序员修改 │
|
||||
│ └─ APPROVED → 进入下一阶段 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ⑥ 测试验证 — 测试员 │
|
||||
│ 分层编写测试(单元+集成+冒烟)并执行 │
|
||||
│ ├─ PARTIAL_FAIL → 回到④让程序员修复 │
|
||||
│ └─ ALL_PASS → 进入下一阶段 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼ [QG4: 测试门禁]
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ⑦ 部署上线 — 总管 │
|
||||
│ 构建、部署、验证,汇报结果给用户 │
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 角色定义
|
||||
|
||||
| 角色 | 代号 | 流程阶段 | 推荐模型类型 |
|
||||
|------|------|---------|-------------|
|
||||
| 🧑💼 总管 | PM | ①需求分析 ⑦部署上线 | 快速响应模型(日常对话) |
|
||||
| 🏗️ 架构师 | ARCH | ②技术方案 | 强推理模型(复杂推理) |
|
||||
| 🎨 设计师 | UI | ③UI设计 | 编码版模型(设计+编码) |
|
||||
| 💻 主程序员 | DEV | ④编码实现 | 深度推理编码模型 |
|
||||
| 👥 结对程序员 | DEV2 | ④编码实现(review) | 与主程序员不同家族模型 |
|
||||
| 🔍 审查员 | REVIEW | ⑤代码审查 | 深度推理模型 |
|
||||
| 🧪 测试员 | QA | ⑥测试验证 | Agent/Tool 能力强的模型 |
|
||||
|
||||
**模型选择原则**:
|
||||
- 编码/审查/测试使用**不同模型家族**,消除单模型偏见
|
||||
- 主程序员和结对程序员用不同厂商模型,交叉审视
|
||||
- 测试员与编码员信息隔离(测试员不看实现思路)
|
||||
|
||||
### 流程裁剪规则
|
||||
|
||||
并非所有需求都必须走完全部阶段。由总管根据需求规模判断裁剪:
|
||||
|
||||
| 需求规模 | 定义 | 走哪些阶段 | 结对编程 | 示例 |
|
||||
|---------|------|-----------|---------|------|
|
||||
| 🔴 大型 | 新功能/新模块/架构变更 | ①②③④⑤⑥⑦ 全流程 | ✅ 启用 | 新增模块、系统重构 |
|
||||
| 🟡 中型 | 现有功能扩展/较复杂 Bug | ①④⑤⑥ 跳过②③设计 | ✅ 启用 | 加导出功能、修复复杂 Bug |
|
||||
| 🟡 中型(含UI) | 现有功能扩展+UI改版 | ①③④⑤⑥ 跳过②方案设计 | ✅ 启用 | 界面改版、新增交互页面 |
|
||||
| 🟢 小型 | 简单修改/配置变更 | ①④⑥ 最小流程 | ❌ 单程序员 | 改字段校验、加配置项 |
|
||||
| ⚪ 微型 | 一行代码/纯配置 | 总管直接做,不 delegate | — | 改文案、调参数 |
|
||||
| 🔥 热修复 | 线上紧急 Bug | ④⑥⑦ 跳过需求和审查 | ❌ 单程序员 | 线上崩溃、数据错误 |
|
||||
|
||||
**裁剪原则**:
|
||||
- ①需求分析(总管)永远不跳过——再小的需求也要先确认理解对了(热修复除外)
|
||||
- ⑤代码审查在中型以上需求中必须执行
|
||||
- ⑥测试验证在有代码变更时必须执行(至少验证不破坏现有功能)
|
||||
- 总管负责判断需求规模,有争议时向用户确认
|
||||
|
||||
### 项目目录规范(建议)
|
||||
|
||||
- 所有研发项目统一放在 `~/projects-dev/` 下(可自定义)
|
||||
- 每个项目一个子目录,git 初始化管理
|
||||
- 便于代码审查阶段做 diff 和回滚
|
||||
|
||||
## 二、质量门禁
|
||||
|
||||
> 详细规范见 `references/quality-gates.md`
|
||||
|
||||
### 门禁总览
|
||||
|
||||
| 门禁 | 位置 | 关键检查 | 通过条件 |
|
||||
|------|------|---------|---------|
|
||||
| QG1 | 需求→设计 | 产物完整性、验收标准可测试、用户确认 | 全部通过 |
|
||||
| QG2 | 设计→编码 | 文件变更清单、API与需求对齐 | 无阻断问题 |
|
||||
| QG3 | 编码→审查 | 编译通过、lint通过、变更与设计一致 | 自检全通过 |
|
||||
| QG4 | 测试→部署 | 测试全通过、覆盖率达标、无回归、用户确认 | 全部通过 |
|
||||
|
||||
### 重试与超时机制
|
||||
|
||||
**重试策略:**
|
||||
```
|
||||
第1次失败 → 同模型重试,附带失败原因
|
||||
第2次失败 → 换备用模型重试
|
||||
第3次失败 → 上报用户,进入人工介入
|
||||
```
|
||||
|
||||
**超时保护:**
|
||||
|
||||
| 阶段 | 超时时间 | 超时处理 |
|
||||
|------|---------|---------|
|
||||
| ②设计 | 30分钟 | 终止,记录已有产出,人工介入 |
|
||||
| ③编码 | 50分钟 | 终止,保留已写代码,人工介入 |
|
||||
| ④审查 | 25分钟 | 终止,标记"审查超时",直接进测试 |
|
||||
| ⑤测试 | 40分钟 | 终止,保留已有测试结果,人工介入 |
|
||||
|
||||
## 三、阶段产物规范
|
||||
|
||||
> 完整模板见 `templates/stage-artifacts.md`
|
||||
|
||||
每个阶段的子 Agent **必须**输出结构化 YAML 产物。总管负责校验产物格式。
|
||||
|
||||
### 产物设计原则
|
||||
|
||||
1. **结构化优先**:用 YAML Schema 约束,减少解析歧义
|
||||
2. **摘要辅助**:每个产物附带自然语言摘要,帮助 LLM 快速定位
|
||||
3. **决策可追溯**:记录关键决策的理由和替代方案
|
||||
4. **问题前传**:显式列出 open_issues,确保下游关注
|
||||
5. **信息隔离**:审查员不看编码 prompt,测试员不看实现思路
|
||||
|
||||
### 产物传递规则
|
||||
|
||||
```
|
||||
① 需求产物 ──→ ②架构师(完整) + ④程序员(验收标准)
|
||||
② 设计产物 ──→ ③设计师(页面/组件清单) + ④程序员(完整)+ ⑤审查员(摘要+文件清单)
|
||||
③ UI设计产物 ──→ ④程序员(完整设计规范+组件样式)
|
||||
④ 编码产物 ──→ ⑤审查员(git diff + 产物) + ⑥测试员(签名+验收标准)
|
||||
⑤ 审查产物 ──→ ④程序员(如需修改:issues列表)
|
||||
⑥ 测试产物 ──→ ④程序员(如有失败:failed_tests)/ ⑦总管(如全通过)
|
||||
```
|
||||
|
||||
**关键**:测试员只接收接口签名和验收标准,不接收编码思路和实现 prompt。
|
||||
|
||||
## 四、测试分层策略
|
||||
|
||||
> 详细规范见 `references/testing-strategy.md`
|
||||
|
||||
### 测试金字塔
|
||||
|
||||
```
|
||||
╱╲
|
||||
╱ 冒烟 ╲ ← 5-10%
|
||||
╱────────╲
|
||||
╱ 集成测试 ╲ ← 20-30%
|
||||
╱──────────────╲
|
||||
╱ 单元测试 ╲ ← 60-70%
|
||||
╱──────────────────╲
|
||||
```
|
||||
|
||||
### 覆盖率阈值
|
||||
|
||||
| 项目规模 | 行覆盖 | 分支覆盖 | 函数覆盖 |
|
||||
|---------|--------|---------|---------|
|
||||
| 大型(新项目) | ≥ 70% | ≥ 60% | ≥ 80% |
|
||||
| 中型(新功能) | ≥ 60% | ≥ 50% | ≥ 75% |
|
||||
| 小型(bug修复) | 修改行100% | — | — |
|
||||
|
||||
## 五、各阶段执行规范
|
||||
|
||||
### ① 需求分析(总管直接执行)
|
||||
|
||||
**输入**: 用户的口头/文字需求
|
||||
**输出**: 结构化需求产物(见 `templates/stage-artifacts.md` ①)
|
||||
|
||||
**规则**: 必须跟用户确认需求文档后才进入下一阶段。
|
||||
|
||||
### ② 技术方案(delegate → 架构师)
|
||||
|
||||
**输入**: 需求产物(完整) + 现有代码库上下文
|
||||
**输出**: 结构化设计产物(见 `templates/stage-artifacts.md` ②)
|
||||
|
||||
**规则**: 总管 review 方案,重大决策需跟用户确认。
|
||||
|
||||
### ③ UI设计(delegate → 设计师)— 可选
|
||||
|
||||
**适用条件**: 面向用户的项目(H5、小程序、Web应用)。纯后端/API/CLI项目跳过此阶段。
|
||||
|
||||
**输入**: 需求产物 + 技术方案中的页面/组件清单 + 用户指定的风格偏好
|
||||
**输出**: 结构化UI设计产物(见 `templates/stage-artifacts.md` ③)
|
||||
|
||||
**设计产物包含**:
|
||||
1. 设计规范(Design Token):配色、字体、间距、圆角、阴影
|
||||
2. 组件样式清单:每个组件的具体 CSS/Tailwind 类名
|
||||
3. 页面布局描述:各页面的结构和组件组合方式
|
||||
4. 交互说明:关键交互的状态变化
|
||||
|
||||
**规则**:
|
||||
- 设计师不需要画图,输出可直接使用的代码级设计规范
|
||||
- 风格偏好由用户指定(如微信风格、Material Design 等)
|
||||
- 设计规范以 Tailwind CSS 类名为主要表达方式
|
||||
|
||||
### ④ 编码实现(结对编程模式,可选)
|
||||
|
||||
**模式**: 主程序员写代码 → 结对程序员 review+改进 → 产出最终代码
|
||||
|
||||
**④a 主程序员(delegate)**
|
||||
|
||||
**输入**: 技术方案产物(完整) + UI设计产物(如有) + 需求产物中的验收标准
|
||||
**输出**: 实现代码 + 结构化编码产物(见 `templates/stage-artifacts.md` ④)
|
||||
|
||||
**④b 结对程序员(delegate)— 大型/中型项目启用**
|
||||
|
||||
**输入**: 主程序员产出的代码(git diff) + 技术方案摘要 + 验收标准
|
||||
**输出**: review 意见 + 改进后的代码
|
||||
|
||||
**结对编程规则**:
|
||||
- 大型/中型项目:必须启用结对,主程序员写完 → 结对程序员 review+改进
|
||||
- 小型/热修复:单主程序员即可,跳过④b
|
||||
- 结对程序员的改动也会进入⑤代码审查,形成三道审视(结对→审查→测试)
|
||||
- 结对程序员不重写代码,只做 review 和针对性改进
|
||||
|
||||
**规则**:
|
||||
- 一次只实现一个功能点,避免大规模变更
|
||||
- 单次 delegate 涉及文件 ≤ 10 个,超过则拆分
|
||||
- 严禁在前台运行常驻进程(如 `npm run dev`),必须后台运行
|
||||
- 完成后必须运行 `git add -A && git commit`
|
||||
|
||||
### ⑤ 代码审查(delegate → 审查员)
|
||||
|
||||
**输入**: 代码变更(git diff) + 需求摘要 + 技术方案摘要(**不含编码 prompt**)
|
||||
**输出**: 结构化审查产物(见 `templates/stage-artifacts.md` ⑤)
|
||||
|
||||
**审查维度(七维度)**:
|
||||
|
||||
| 维度 | 严重级别 | 检查重点 |
|
||||
|------|---------|---------|
|
||||
| 安全性 | 🔴 阻断 | SQL注入、XSS、硬编码密钥、路径遍历 |
|
||||
| 正确性 | 🔴 阻断 | 逻辑错误、边界条件、空值处理、资源泄露 |
|
||||
| 数据契约 | 🔴 阻断 | 前端 TS 接口与 API 实际返回字段一致性 |
|
||||
| 性能 | 🟡 警告 | 算法复杂度、N+1查询、内存泄漏 |
|
||||
| 可维护性 | 🟡 警告 | 函数长度、命名规范、DRY原则 |
|
||||
| 规范 | 🟢 建议 | 代码风格、项目约定 |
|
||||
| 测试 | 🟡 警告 | 测试覆盖、Mock合理性 |
|
||||
|
||||
**规则**: 审查不通过 → 问题反馈给程序员修改 → 修改后重新审查。最多 3 轮。
|
||||
|
||||
### ⑥ 测试验证(delegate → 测试员)
|
||||
|
||||
**输入**: 接口签名 + 验收标准(**不含实现思路和编码 prompt**)
|
||||
**输出**: 测试代码 + 结构化测试产物(见 `templates/stage-artifacts.md` ⑥)
|
||||
|
||||
**规则**: 测试不通过 → 失败信息反馈给程序员修复 → 修复后重新测试。最多 3 轮。
|
||||
|
||||
### ⑦ 部署上线(总管直接执行)
|
||||
|
||||
**输入**: 通过审查和测试的代码
|
||||
**输出**: 结构化部署产物(见 `templates/stage-artifacts.md` ⑦)
|
||||
|
||||
**规则**: 部署前跟用户确认。部署后验证核心功能。
|
||||
|
||||
## 六、设计理念与参考文献
|
||||
|
||||
### 核心设计理念
|
||||
|
||||
| 理念 | 来源 | 落地方式 |
|
||||
|------|------|---------|
|
||||
| SOP驱动+结构化输出 | MetaGPT (arXiv:2308.00352) | 7阶段YAML产物模板 |
|
||||
| 测试与编码分离 | AgentCoder (arXiv:2312.13010) | 不同模型+信息隔离 |
|
||||
| 多模型交叉审查 | 业界最佳实践 | 编码/审查/测试各用不同模型家族 |
|
||||
| Task级重试 | CrewAI | 每阶段 max 3 次+备用模型降级 |
|
||||
| 事件流快照 | OpenHands | git checkpoint 用于崩溃恢复 |
|
||||
| ACI精简工具 | SWE-agent | 子 agent 按需分配最小工具集 |
|
||||
|
||||
### 参考文献
|
||||
|
||||
1. MetaGPT — SOP驱动多角色协作,结构化中间产物减少30%信息损失
|
||||
2. AgentCoder — 独立测试Agent比自测通过率高12-18%
|
||||
3. ChatDev — 角色扮演对话提升20%完成率
|
||||
4. SWE-bench — 60%修复失败源于定位错误,强调代码搜索能力
|
||||
5. Agentless — 简单流水线有时比复杂Agent更有效
|
||||
|
||||
> 完整框架对比调研见 `references/framework-comparison.md`
|
||||
|
||||
## 七、实战陷阱与最佳实践
|
||||
|
||||
### 子 Agent 超时恢复模式
|
||||
|
||||
子 Agent 可能在执行过程中超时,但代码文件可能已经写入。超时后总管应执行以下恢复步骤:
|
||||
|
||||
1. **检查文件系统** — 查看已生成的文件
|
||||
2. **检查 git 状态** — 确认子 Agent 是否已自动 commit
|
||||
3. **验证后端** — 启动服务并测试 API 端点
|
||||
4. **验证前端** — 确认构建通过
|
||||
5. **修复关键问题** — 子 Agent 超时前可能来不及处理边界情况
|
||||
6. **提交修复** — 完成收尾
|
||||
|
||||
超时不代表失败——子 Agent 可能完成了 80% 的工作,总管收尾比重新 delegate 更高效。
|
||||
|
||||
### 前后端数据契约不一致(高频致命问题)
|
||||
|
||||
程序员 Agent 同时写 API 和前端时,TypeScript 接口定义经常与 API 实际返回字段对不上,导致客户端 JS 崩溃。
|
||||
|
||||
**防范措施**:
|
||||
1. 编码完成后,总管用 curl API 抓取实际响应
|
||||
2. 对比前端 TypeScript 接口定义与 API 实际字段
|
||||
3. 重点检查:字段名差异、字段是否存在、类型是否匹配
|
||||
4. 对可能为 undefined 的字段,检查是否有安全访问(`?.`、`|| 默认值`)
|
||||
|
||||
### 常驻进程防护规则
|
||||
|
||||
子 Agent 禁止前台运行 `npm run dev` / `next start` / `python -m http.server` 等常驻命令,必须后台运行或设超时。违反此规则会导致整个 delegation 链路卡死。
|
||||
|
||||
### 代码修改必须提交后再通知构建
|
||||
|
||||
修改代码后,必须先 `git status` 确认已 staging、`git log --oneline -1` 确认已提交,再通知用户触发 CI/CD。本地工作区的修改不会自动进入远程仓库。
|
||||
|
||||
## 八、附带参考文档
|
||||
|
||||
本技能包附带以下参考文档,按通用性分类:
|
||||
|
||||
### 通用技术参考(适用于任何项目)
|
||||
|
||||
| 文档 | 内容 |
|
||||
|------|------|
|
||||
| `references/fastapi-pitfalls.md` | FastAPI 实战陷阱(路由顺序、N+1、NULL处理等) |
|
||||
| `references/frontend-build-optimization.md` | Vite 代码分割 + 路由懒加载优化 |
|
||||
| `references/operation-log-pattern.md` | 操作日志系统四层架构模板 |
|
||||
| `references/testing-strategy.md` | 测试分层策略详情 |
|
||||
| `references/quality-gates.md` | 质量门禁详情 |
|
||||
| `references/framework-comparison.md` | AI 编码框架调研对比 |
|
||||
|
||||
### 业务模式参考(按需选用)
|
||||
|
||||
| 文档 | 适用场景 |
|
||||
|------|---------|
|
||||
| `references/excel-data-import.md` | 从 Excel 批量导入业务数据 |
|
||||
| `references/revenue-allocation-pattern.md` | 人×项目收益归属矩阵 |
|
||||
| `references/doc-sync-pattern.md` | 里程碑文档同步到仓库 |
|
||||
|
||||
## Changelog
|
||||
|
||||
| 版本 | 日期 | 变更内容 |
|
||||
|------|------|---------|
|
||||
| v1.0.0 | 2026-06-30 | 通用版首发。剥离环境特定信息(API key、服务器IP、具体项目名),保留通用流水线逻辑。适配 OpenClaw/Hermes 等多平台。 |
|
||||
@ -0,0 +1,59 @@
|
||||
# 里程碑文档同步模式(通用模板)
|
||||
|
||||
**创建日期**: 2026-06-30
|
||||
**适用场景**: MVP/里程碑完成后,需要同步代码和文档到远程仓库
|
||||
|
||||
---
|
||||
|
||||
## 触发条件
|
||||
|
||||
- 重大功能完成(如 MVP v1.0)
|
||||
- 里程碑文档创建完成
|
||||
- 需要备份/共享项目文档
|
||||
|
||||
## 同步流程
|
||||
|
||||
### 步骤 1: 代码和里程碑文档 → 远程仓库
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd <项目目录>
|
||||
|
||||
# 查看未跟踪文件
|
||||
git status
|
||||
|
||||
# 添加里程碑文档和关键代码
|
||||
git add milestone/ <其他关键文件>
|
||||
|
||||
# 提交
|
||||
git commit -m "feat: <版本> <功能描述>
|
||||
|
||||
- <成果1>
|
||||
- <成果2>"
|
||||
|
||||
# 推送到远程仓库
|
||||
git push origin <分支>
|
||||
```
|
||||
|
||||
### 步骤 2: 核心文档 → 文档库(可选)
|
||||
|
||||
如有独立的文档仓库,同步核心 .md 文档:
|
||||
|
||||
```bash
|
||||
# 进入文档库
|
||||
cd <文档库目录>
|
||||
|
||||
# 复制核心文档
|
||||
cp -r <项目目录>/milestone/ <文档库目录>/<项目名>/
|
||||
|
||||
# 提交
|
||||
git add <项目名>/
|
||||
git commit -m "feat: 添加 <项目名> <版本> 里程碑文档"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **先主仓库后文档库** — 确保代码和文档先在版本控制中
|
||||
2. **选择性同步** — 文档库只同步核心 .md 文档,不同步二进制/大文件
|
||||
3. **milestone 命名** — 格式 `YYYY-MM-DD-v<版本>-<标签>`
|
||||
177
archives/dev-pipeline-universal/references/excel-data-import.md
Normal file
177
archives/dev-pipeline-universal/references/excel-data-import.md
Normal file
@ -0,0 +1,177 @@
|
||||
# Excel 数据导入实战参考
|
||||
|
||||
> **适用场景**: MVP/里程碑完成后,从业务 Excel 报表批量导入测试数据做效果验证
|
||||
> **创建日期**: 2026-06-24
|
||||
> **更新日期**: 2026-06-30
|
||||
|
||||
---
|
||||
|
||||
## 两种导入方式
|
||||
|
||||
### 方式一:CLI 脚本(适合首次全量导入)
|
||||
|
||||
`scripts/import_excel_data.py` — 从文件系统读取 Excel,一次性导入全部数据。
|
||||
|
||||
**优点**: 无网络开销,可调试,适合大数据量
|
||||
**缺点**: 需要服务器文件系统访问权限
|
||||
|
||||
### 方式二:Web API(适合日常增量导入)
|
||||
|
||||
`POST /api/import/excel` — 前端上传 Excel 文件,后端解析导入。
|
||||
|
||||
**优点**: 浏览器操作,无需服务器登录,有导入结果统计
|
||||
**缺点**: 文件大小受 HTTP 限制,大文件上传慢
|
||||
|
||||
**前端页面**: `/import` 路径,拖拽上传 + 导入结果展示
|
||||
|
||||
**API 端点**:
|
||||
```python
|
||||
@router.post("/api/import/excel")
|
||||
async def import_excel(file: UploadFile = File(...), db: Session = Depends(get_db)):
|
||||
# 保存临时文件 → openpyxl 解析 → 导入各表 → 删除临时文件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心挑战
|
||||
|
||||
业务 Excel 通常有 15-20 个 sheet,列布局因项目而异,且存在公式、合并单元格、同名项目等陷阱。一次性全量导入比逐条录入高效百倍,但需要处理以下问题。
|
||||
|
||||
---
|
||||
|
||||
## 实战陷阱与对策
|
||||
|
||||
### 1. 列布局因 sheet 而异
|
||||
|
||||
每个项目明细 sheet 的列数不同:
|
||||
|
||||
| Sheet | 列布局 | 人日列 | 描述列 |
|
||||
|-------|--------|:------:|:------:|
|
||||
| 标准 | 序号\|模块\|功能项\|人日\|负责人\|占比\|... | col 3 | — |
|
||||
| 工健健康小屋 | 序号\|模块\|功能项\|说明1\|详细说明\|人日\|负责人\|... | col 5 | col 4 |
|
||||
| 基层社区AI医助 | 序号\|模块\|功能项\|描述\|人日\|负责人\|... | col 4 | col 3 |
|
||||
| 汤原县AI医助 | 序号\|功能项\|描述\|人日\|负责人\|... | col 3 | col 2(无模块列) |
|
||||
|
||||
**对策**: 为每个 sheet 定义独立的列映射配置,而非通用解析逻辑。
|
||||
|
||||
```python
|
||||
sheet_configs = [
|
||||
("项目-百度-珠江医院", "2026-BD-1", 1, 2, 3, 4, None, [...]),
|
||||
("项目-工健健康小屋", "2026-YZ-1", 1, 2, 5, 6, 4, [...]),
|
||||
("项目-汤原县AI医助", "2026-AI-2", None, 1, 3, 4, 2, [...]),
|
||||
]
|
||||
```
|
||||
|
||||
### 2. 同名项目(不同项目编号)
|
||||
|
||||
Excel 中可能有多个同名项目(如"南方医科大学珠江医院"有 `20260205-A` 和 `2026-BD-1` 两个编号)。用 `project_name` 做 dict key 会覆盖。
|
||||
|
||||
**对策**: 始终用 `project_code` 做映射,不要用 `project_name`。
|
||||
|
||||
```python
|
||||
projects_by_code = {p.project_code: {"name": p.project_name, "id": p.id} for p in db.query(Project).all()}
|
||||
```
|
||||
|
||||
### 3. 人员不在人员概况 sheet 中
|
||||
|
||||
新 Excel 可能新增了人员(如"陈天然"),但只出现在收益分析 sheet 中,不在人员概况 sheet 里。
|
||||
|
||||
**对策**: 扫描所有 sheet 发现新人员,手动补加到人员表。
|
||||
|
||||
### 4. 公式字段 openpyxl 读不到
|
||||
|
||||
Excel 中的 `=SUM(...)`、`=C2/D2` 等公式,openpyxl 读取时返回公式字符串而非计算结果。
|
||||
|
||||
**对策**:
|
||||
- 预期收益从收益分析 sheet 的"总计"行读取(那里是数值)
|
||||
- 投产比等计算字段在数据库端用 ROI 计算器重新计算
|
||||
|
||||
### 5. 自由文本 → 结构化关联
|
||||
|
||||
"人员项目负荷情况" sheet 的工作描述是自由文本(如"1、百度医院智能体-南方医科大学珠江医院-近一个月工作占比50%"),需要从中提取项目关联和分配比例。
|
||||
|
||||
**对策**: 两阶段匹配:
|
||||
1. 精确匹配:项目全名在文本中
|
||||
2. 关键词回退:`"健康小屋" → "工会健康小屋"`, `"AI医助" → "佳木斯中医院AI医助"`
|
||||
|
||||
分配比例从文本中 `占比(\d+)%` 正则提取,无比例时按项目关联人数均分。
|
||||
|
||||
### 6. 增量更新 vs 全量重来
|
||||
|
||||
**原则**: 首次导入用全量清空重来;后续更新用增量 upsert(按 project_code/personnel_id 匹配)。
|
||||
|
||||
**增量 upsert 模式**:
|
||||
```python
|
||||
existing = db.query(Model).filter(Model.code == code).first()
|
||||
if existing:
|
||||
for key, val in new_data.items():
|
||||
setattr(existing, key, val)
|
||||
else:
|
||||
db.add(Model(**new_data))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. openpyxl 样式解析 bug(TypeError: expected Fill)
|
||||
|
||||
openpyxl 3.1.5 在解析某些 Excel 文件的 `styles.xml` 时,遇到不兼容的 Fill 样式对象会抛出 `TypeError: expected <class 'openpyxl.styles.fills.Fill'>`。
|
||||
|
||||
**无效尝试**:
|
||||
- `data_only=True` — 只影响公式计算,不跳过样式解析
|
||||
- `read_only=True` — 同样需要解析样式表
|
||||
- 升级 openpyxl — 3.1.5 是当前最新版,bug 尚未修复
|
||||
|
||||
**根治方案**:改用 **python-calamine**(Rust 实现的 Excel 解析器),它完全不解析样式,只读数据。
|
||||
|
||||
```python
|
||||
from python_calamine import CalamineWorkbook
|
||||
|
||||
wb = CalamineWorkbook.from_path(path)
|
||||
sheets = wb.sheet_names
|
||||
ws = wb.get_sheet_by_name("Sheet1")
|
||||
rows = list(ws.to_python()) # 返回 list[list],每个 cell 是 Python 原生类型
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- `python-calamine` 返回的单元格值是 Python 原生类型(float/int/str/None),无需额外转换
|
||||
- 不解析公式,返回的是缓存的计算结果(与 `data_only=True` 类似)
|
||||
- 不支持写 Excel,只用于读取
|
||||
- 安装:`pip install python-calamine` 或加到 requirements.txt
|
||||
|
||||
**⚠️ employee_id 空值陷阱**:
|
||||
`python-calamine` 返回的空单元格是 `None`,但 Excel 中写了空字符串的单元格也会被 `str(val).strip()` 转为空字符串。用 `clean()` 函数处理时,空字符串会变成 `None`,导致 NOT NULL 约束失败。
|
||||
|
||||
```python
|
||||
# ❌ 错误:clean(row[2]) 对空单元格返回 None
|
||||
emp_id = clean(row[2]) if len(row) > 2 else f"EMP{name}"
|
||||
|
||||
# ✅ 正确:显式检查 clean() 结果是否为空
|
||||
emp_id = clean(row[2]) if len(row) > 2 and clean(row[2]) else f"EMP{name}"
|
||||
```
|
||||
|
||||
**规律**:任何 `clean()` 的返回值都可能为 `None`,不能因为列存在就假定值非空。在 NOT NULL 字段上使用 `clean()` 时,必须加 `and clean(val)` 二次检查。
|
||||
|
||||
**Web API 中的完整模式**:
|
||||
```python
|
||||
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
|
||||
try:
|
||||
content = await file.read()
|
||||
tmp.write(content)
|
||||
tmp.close()
|
||||
wb = CalamineWorkbook.from_path(tmp.name)
|
||||
# ... 解析各 sheet ...
|
||||
finally:
|
||||
os.unlink(tmp.name)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 导入后验证清单
|
||||
|
||||
- [ ] 项目数是否匹配 Excel 项目概况行数
|
||||
- [ ] 人员数是否覆盖所有出现的人名
|
||||
- [ ] 预期收益是否与 Excel 收益分析 sheet 的"总计"行一致
|
||||
- [ ] WBS 任务数是否合理(每个项目应有 >0 条)
|
||||
- [ ] 人员-项目关联数是否覆盖主要参与关系
|
||||
- [ ] 收益矩阵 API 返回的数据与 Excel 收益分析 sheet 交叉验证
|
||||
- [ ] 仪表盘 OKR 完成率是否与 Excel 部门概况一致
|
||||
315
archives/dev-pipeline-universal/references/fastapi-pitfalls.md
Normal file
315
archives/dev-pipeline-universal/references/fastapi-pitfalls.md
Normal file
@ -0,0 +1,315 @@
|
||||
# FastAPI 实战陷阱与最佳实践
|
||||
|
||||
> **适用于 dev-pipeline ④编码实现阶段。程序员(主程序员/结对程序员)在编写 FastAPI 代码时必须注意以下陷阱。
|
||||
|
||||
---
|
||||
|
||||
## 1. 路由注册顺序
|
||||
|
||||
### 问题
|
||||
FastAPI 按注册顺序匹配路由。**具体路由必须在参数化路由之前注册**,否则会被错误匹配。
|
||||
|
||||
```python
|
||||
# ❌ 错误:/{personnel_id} 先注册,/workload 被匹配为 personnel_id="workload"
|
||||
@router.get("/{personnel_id}") # 第63行
|
||||
def get_personnel(...)
|
||||
|
||||
@router.get("/{personnel_id}/workload") # 第151行 → 永远匹配不到
|
||||
def get_personnel_workload(...)
|
||||
|
||||
# ✅ 正确:具体路由先注册
|
||||
@router.get("/{personnel_id}/workload") # 先注册
|
||||
def get_personnel_workload(...)
|
||||
|
||||
@router.get("/{personnel_id}") # 后注册
|
||||
def get_personnel(...)
|
||||
```
|
||||
|
||||
### 排查方法
|
||||
当某个路由返回 404 但确信路径正确时:
|
||||
1. 检查路由注册顺序(`grep -n "@router\." file.py`)
|
||||
2. 确认具体路由(含固定路径段)在参数化路由之前
|
||||
|
||||
### 同类陷阱
|
||||
- `/milestones/all` 必须在 `/{project_id}/milestones` 之前
|
||||
- `/tasks/all` 必须在 `/{project_id}/tasks` 之前
|
||||
- `/export/projects` 必须在 `/{project_id}` 之前
|
||||
|
||||
---
|
||||
|
||||
## 2. Excel 导出中文文件名编码
|
||||
|
||||
### 问题
|
||||
`Content-Disposition: attachment; filename=中文.xlsx` 中的中文字符在 Starlette TestClient 中触发 `UnicodeEncodeError`,某些浏览器也无法正确解析。
|
||||
|
||||
### 修复
|
||||
使用 RFC 5987 编码:
|
||||
|
||||
```python
|
||||
from urllib.parse import quote
|
||||
|
||||
def _excel_response(wb, filename):
|
||||
output = io.BytesIO()
|
||||
wb.save(output)
|
||||
output.seek(0)
|
||||
encoded_filename = quote(filename)
|
||||
return StreamingResponse(
|
||||
output,
|
||||
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"},
|
||||
)
|
||||
```
|
||||
|
||||
### 测试注意事项
|
||||
- Starlette TestClient 的 `UnicodeEncodeError` 是已知问题,测试中需 try/except 捕获
|
||||
- 验证 Excel 内容时检查 `content[:2] == b"PK"`(ZIP 签名)即可
|
||||
- 用 `openpyxl.load_workbook(io.BytesIO(resp.content))` 验证内容正确性
|
||||
|
||||
---
|
||||
|
||||
## 3. 依赖声明
|
||||
|
||||
### 问题
|
||||
新增 Python 依赖(如 `openpyxl`)后忘记更新 `requirements.txt`,导致部署时崩溃。
|
||||
|
||||
### 规则
|
||||
每次④编码实现阶段新增依赖后,必须:
|
||||
1. 确认依赖已安装(`pip list | grep <pkg>`)
|
||||
2. 更新 `requirements.txt`(`pip freeze | grep <pkg> >> requirements.txt` 或手动添加)
|
||||
3. 结对程序员 review 时检查 requirements.txt 变更
|
||||
|
||||
---
|
||||
|
||||
## 4. SQLAlchemy N+1 查询
|
||||
|
||||
### 问题
|
||||
遍历 ORM 对象时访问关联属性会触发额外 SQL 查询。
|
||||
|
||||
```python
|
||||
# ❌ N+1:对每个任务查一次 project
|
||||
for task in tasks:
|
||||
project_name = task.project.project_name # 触发额外 SQL
|
||||
|
||||
# ✅ 修复:使用 joinedload 批量加载
|
||||
from sqlalchemy.orm import joinedload
|
||||
tasks = db.query(ProjectTask).options(joinedload(ProjectTask.project)).all()
|
||||
```
|
||||
|
||||
### 批量查询替代方案
|
||||
当无法使用 joinedload 时,用 `IN` 查询替代循环查询:
|
||||
|
||||
```python
|
||||
# ❌ N+1
|
||||
for task in tasks:
|
||||
progress = db.query(TaskMonthlyProgress).filter(
|
||||
TaskMonthlyProgress.task_id == task.id
|
||||
).first()
|
||||
|
||||
# ✅ 批量
|
||||
all_progress = db.query(TaskMonthlyProgress).filter(
|
||||
TaskMonthlyProgress.task_id.in_(task_ids)
|
||||
).all()
|
||||
task_latest = {p.task_id: p for p in all_progress}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. SQLAlchemy NULL 处理
|
||||
|
||||
### 问题
|
||||
SQLAlchemy 中 `column < 100` 遇到 NULL 时返回 UNKNOWN(不匹配),导致 NULL 值被遗漏。
|
||||
|
||||
```python
|
||||
# ❌ 遗漏 overall_progress IS NULL 的任务
|
||||
tasks = db.query(ProjectTask).filter(ProjectTask.overall_progress < 100).all()
|
||||
|
||||
# ✅ 正确:显式处理 NULL
|
||||
from sqlalchemy import or_
|
||||
tasks = db.query(ProjectTask).filter(
|
||||
or_(ProjectTask.overall_progress < 100, ProjectTask.overall_progress.is_(None))
|
||||
).all()
|
||||
# 或使用 | 运算符(注意括号)
|
||||
tasks = db.query(ProjectTask).filter(
|
||||
(ProjectTask.overall_progress < 100) | (ProjectTask.overall_progress.is_(None))
|
||||
).all()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 前端导出绕过认证
|
||||
|
||||
### 问题
|
||||
用 `<a>` 标签直接访问 API 路径会跳过 axios 的 `Authorization: Bearer` 拦截器,导致 401。
|
||||
|
||||
```javascript
|
||||
// ❌ 错误:绕过认证
|
||||
const link = document.createElement('a')
|
||||
link.href = '/api/reports/export/projects'
|
||||
link.click()
|
||||
|
||||
// ✅ 正确:通过 axios 获取 blob 后下载
|
||||
api.get('/reports/export/projects', { responseType: 'blob' }).then(res => {
|
||||
const url = URL.createObjectURL(new Blob([res.data]))
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = 'filename.xlsx'
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
})
|
||||
```
|
||||
|
||||
### DRY 原则
|
||||
多个导出函数应提取为通用函数:
|
||||
|
||||
```javascript
|
||||
function downloadExcel(url, filename) {
|
||||
exporting.value = true
|
||||
api.get(url, { responseType: 'blob' }).then(res => {
|
||||
const blobUrl = URL.createObjectURL(new Blob([res.data]))
|
||||
const link = document.createElement('a')
|
||||
link.href = blobUrl
|
||||
link.download = filename
|
||||
link.click()
|
||||
URL.revokeObjectURL(blobUrl)
|
||||
}).finally(() => { exporting.value = false })
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. ECharts 生命周期管理
|
||||
|
||||
### 问题
|
||||
Vue 组件销毁时未释放 ECharts 实例导致内存泄漏;路由参数变化(如 `/personnel/1` → `/personnel/2`)时图表不刷新。
|
||||
|
||||
### 修复
|
||||
```javascript
|
||||
import { onBeforeUnmount, onBeforeRouteUpdate } from 'vue-router'
|
||||
|
||||
// 销毁时释放
|
||||
onBeforeUnmount(() => {
|
||||
chart?.dispose()
|
||||
})
|
||||
|
||||
// 路由参数变化时重新加载
|
||||
onBeforeRouteUpdate(() => {
|
||||
chart?.dispose()
|
||||
loadData()
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 浮点精度
|
||||
|
||||
### 问题
|
||||
Python 浮点累加可能产生尾数误差(如 `49.9999999` 而非 `50.0`)。
|
||||
|
||||
### 修复
|
||||
```python
|
||||
# 在最终输出时统一 round
|
||||
total = round(sum(values), 2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Pydantic Schema 字段同步
|
||||
|
||||
### 问题
|
||||
后端 API 返回字典中新增了字段,但 Pydantic `response_model` schema 中没有声明,导致字段被静默丢弃。前端收到 `undefined`,页面渲染异常。
|
||||
|
||||
```python
|
||||
# ❌ 后端返回了 project_count,但 PersonnelOut schema 没有这个字段
|
||||
class PersonnelOut(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
# ... 没有 project_count → 被 Pydantic 过滤掉
|
||||
|
||||
# ✅ 必须在 schema 中声明
|
||||
class PersonnelOut(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
project_count: Optional[int] = 0
|
||||
total_allocation: Optional[float] = 0.0
|
||||
```
|
||||
|
||||
### 排查方法
|
||||
当前端收到 `undefined` 但确信后端有返回时:
|
||||
1. 去掉 `response_model` 参数,看原始返回是否包含该字段
|
||||
2. 检查 schema 定义是否包含新字段
|
||||
3. 注意 `from_attributes = True` 只影响 ORM→Pydantic 转换,不影响 dict→Pydantic 过滤
|
||||
|
||||
### 关联陷阱
|
||||
- 修改 `_build_personnel_dict` 等 helper 函数后,必须同步更新对应的 schema
|
||||
- 结对程序员 review 时重点检查:helper 返回字段 ↔ schema 字段 的一致性
|
||||
|
||||
---
|
||||
|
||||
## 11. 部署验证脚本模式
|
||||
|
||||
### 问题
|
||||
部署验证时使用 shell 管道(`curl | python3 -c`)容易触发安全防护(命令超时/阻断),尤其是涉及变量引用和 URL 编码时。
|
||||
|
||||
### 推荐方案
|
||||
用 Python 脚本替代 shell 管道,一个文件完成全部验证:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import urllib.request, json
|
||||
|
||||
BASE = "http://127.0.0.1:8001"
|
||||
|
||||
def api(method, path, data=None, token=None):
|
||||
url = BASE + path
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if token:
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
body = json.dumps(data).encode() if data else None
|
||||
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
||||
resp = urllib.request.urlopen(req, timeout=10)
|
||||
return resp.status, resp.read()
|
||||
|
||||
# 1. 登录
|
||||
status, data = api("POST", "/api/auth/login", {"username": "admin", "password": "admin123"})
|
||||
token = json.loads(data)["access_token"]
|
||||
|
||||
# 2. 验证端点
|
||||
status, data = api("GET", "/api/projects?project_type=创新项目", token=token)
|
||||
projects = json.loads(data)
|
||||
print(f"类型筛选: {len(projects)}个")
|
||||
|
||||
# 3. 验证 Excel 导出
|
||||
status, data = api("GET", "/api/reports/export/projects", token=token)
|
||||
assert data[:2] == b"PK" # ZIP 签名
|
||||
import openpyxl, io
|
||||
wb = openpyxl.load_workbook(io.BytesIO(data))
|
||||
print(f"导出: {wb.active.title}, {wb.active.max_row-1}行")
|
||||
```
|
||||
|
||||
### 优势
|
||||
- 避免 shell 变量注入和管道超时
|
||||
- 可在虚拟环境中直接运行
|
||||
- 断言清晰,失败时立即知道哪个端点出问题
|
||||
- 可复用(保存为 `scripts/verify_deploy.py`)
|
||||
|
||||
---
|
||||
|
||||
## 9. 时区一致性
|
||||
|
||||
### 问题
|
||||
项目中混用 timezone-naive 和 timezone-aware 的 datetime 导致比较偏差。
|
||||
|
||||
### 规范
|
||||
```python
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# 统一使用 UTC aware datetime
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# 解析字符串时也设为 aware
|
||||
from calendar import monthrange
|
||||
parts = month_str.split("-")
|
||||
y, m = int(parts[0]), int(parts[1])
|
||||
last_day = monthrange(y, m)[1]
|
||||
progress_date = datetime(y, m, last_day, tzinfo=timezone.utc)
|
||||
```
|
||||
@ -0,0 +1,44 @@
|
||||
# AI 编码框架多代理协作调研(2026-04-19)
|
||||
|
||||
> 本文档记录了 v2.0 设计决策的调研依据。用于未来架构演进参考。
|
||||
|
||||
## 三种多Agent协作范式
|
||||
|
||||
| 范式 | 代表框架 | 特点 | 我们的借鉴 |
|
||||
|------|---------|------|-----------|
|
||||
| **SOP驱动** | MetaGPT | 模拟软件公司,角色固定,结构化输出 | → 6阶段YAML产物模板 |
|
||||
| **任务编排** | CrewAI | Agent/Task/Crew抽象,sequential/hierarchical | → delegate_task per-task模型路由 |
|
||||
| **群聊协商** | AutoGen | GroupChat多Agent讨论,Manager分配 | 未采用(开销大) |
|
||||
|
||||
## 框架与Hermes集成潜力排序
|
||||
|
||||
| 优先级 | 框架 | 集成方式 | 适合场景 |
|
||||
|--------|------|---------|---------|
|
||||
| 🥇 | **Aider** | 作为编码执行引擎 | git深度集成、repo map、双模型模式 |
|
||||
| 🥈 | **CrewAI** | 借鉴编排模式 | Agent/Task/Crew抽象 |
|
||||
| 🥉 | **MetaGPT** | 借鉴SOP+角色设计 | 结构化产物、发布订阅通信 |
|
||||
| 4 | **OpenHands** | 借鉴事件流+浏览器能力 | 全栈开发(前端+后端) |
|
||||
| 5 | **Claude Code** | 借鉴subagent+worktree并行 | 大任务并行编码 |
|
||||
|
||||
## 关键学术发现(影响v2.0设计)
|
||||
|
||||
| 论文 | 发现 | 落地到v2.0 |
|
||||
|------|------|-----------|
|
||||
| MetaGPT (2308.00352) | 结构化输出减少30%信息损失 | templates/stage-artifacts.md |
|
||||
| AgentCoder (2312.13010) | 独立测试Agent比自测通过率高12-18% | 测试员信息隔离原则 |
|
||||
| ChatDev (2307.07924) | 角色对话提升20%完成率,需设轮次上限 | 每阶段max 3轮重试 |
|
||||
| SWE-bench | 60%失败源于定位错误 | 强调代码搜索能力(未来P2) |
|
||||
| Agentless | 简单流水线有时比复杂Agent更好 | 小型需求总管直接做,不过度编排 |
|
||||
|
||||
## 我们选择"不换框架"的理由
|
||||
|
||||
1. Hermes 的 `delegate_task` + custom_providers 已是成熟的多Agent编排
|
||||
2. 已有5角色6阶段,与MetaGPT的SOP模式架构等价
|
||||
3. 完全开源、模型灵活(火山方舟9个模型可切换)、可控性最高
|
||||
4. 不引入额外框架依赖,降低维护成本
|
||||
|
||||
## 未来演进方向(P2+)
|
||||
|
||||
- **短期**: 集成 Aider 的 repo map(tree-sitter AST索引)提升代码定位能力
|
||||
- **中期**: 借鉴 Claude Code worktree 并行模式,大任务拆分后多分支并行编码
|
||||
- **长期**: 代码库向量索引(RAG),经验池积累成功案例
|
||||
@ -0,0 +1,94 @@
|
||||
# 前端构建优化(Vite 代码分割 + 路由懒加载)
|
||||
|
||||
> 适用于 Vue 3 + Vite 项目,Element Plus + ECharts 技术栈
|
||||
|
||||
## 问题
|
||||
|
||||
Vue 3 + Element Plus + ECharts 项目构建后,默认所有代码打包成一个 JS 文件(通常 2~3MB),首屏加载全量代码,严重影响首次加载速度。
|
||||
|
||||
## 优化方案
|
||||
|
||||
### 1. Vite `manualChunks` 拆包
|
||||
|
||||
在 `vite.config.js` 中配置 `build.rollupOptions.output.manualChunks`,将大体积第三方库拆为独立 chunk:
|
||||
|
||||
```js
|
||||
// vite.config.js
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
// echarts 单独拆包(~1MB,仅访问图表页面时加载)
|
||||
if (id.includes('node_modules/echarts')) {
|
||||
return 'echarts'
|
||||
}
|
||||
// element-plus 单独拆包(~1MB,浏览器缓存)
|
||||
if (id.includes('node_modules/element-plus')) {
|
||||
return 'element-plus'
|
||||
}
|
||||
// vue 核心库单独拆包(~120KB)
|
||||
if (id.includes('node_modules/vue') || id.includes('node_modules/@vue')) {
|
||||
return 'vue-core'
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
chunkSizeWarningLimit: 500, // 提高告警阈值,避免拆包后仍有大 chunk 告警
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
**效果**:
|
||||
| Chunk | 大小 | 加载时机 |
|
||||
|:------|:----:|:--------|
|
||||
| `vue-core` | ~120KB | 首屏 |
|
||||
| `element-plus` | ~1MB | 首屏(浏览器缓存后不重复下载) |
|
||||
| `echarts` | ~1MB | 仅访问图表页面时按需加载 |
|
||||
| 各页面组件 | 2~15KB | 按需加载 |
|
||||
|
||||
### 2. 路由懒加载(动态 import)
|
||||
|
||||
将 `router/index.js` 中的静态 import 改为动态 `() => import(...)`:
|
||||
|
||||
```js
|
||||
// ❌ 静态 import(全量加载)
|
||||
import Dashboard from '../views/dashboard/Index.vue'
|
||||
|
||||
// ✅ 动态 import(按需加载)
|
||||
const routes = [
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: () => import('../views/dashboard/Index.vue'),
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
Vite 会自动为每个动态 import 生成独立 chunk,文件名基于组件名(如 `Index-abc123.js`)。
|
||||
|
||||
### 3. 验证优化效果
|
||||
|
||||
```bash
|
||||
# 构建前
|
||||
ls -lh dist/assets/
|
||||
# 输出:index-DoEo0YCm.js 2.3M ← 单文件
|
||||
|
||||
# 配置后构建
|
||||
npm run build
|
||||
ls -lh dist/assets/
|
||||
# 输出:
|
||||
# vue-core-BuIKajZo.js 119KB
|
||||
# element-plus-C03w9GoR.js 1.0MB
|
||||
# echarts-Bb6yjXMn.js 1.0MB
|
||||
# Index-xxx.js 2~15KB(每个页面)
|
||||
# ...
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **manualChunks 函数**接收的是模块 ID(绝对路径),用 `id.includes()` 匹配 `node_modules` 中的包名
|
||||
2. **路由懒加载**只在用户访问对应路由时才加载 chunk,首次访问某页面会有短暂加载延迟(通常 < 200ms)
|
||||
3. **echarts 拆包后**,只有访问包含 ECharts 图表的页面(仪表盘、经营数据等)才会加载 echarts chunk
|
||||
4. **chunkSizeWarningLimit** 建议设为 500KB,避免拆包后 element-plus/echarts 仍触发告警(它们确实很大)
|
||||
5. **浏览器缓存**:element-plus 和 vue-core 拆为独立 chunk 后,只要版本不变,浏览器会缓存,后续页面访问不再下载
|
||||
@ -0,0 +1,212 @@
|
||||
# 操作日志系统(审计追踪模式)
|
||||
|
||||
> **适用场景**: 任何需要审计追踪的 CRUD 管理系统
|
||||
> **创建日期**: 2026-06-29
|
||||
|
||||
---
|
||||
|
||||
## 四层架构
|
||||
|
||||
| 层次 | 文件 | 职责 |
|
||||
|:----|:-----|:-----|
|
||||
| 模型 | `models/operation_log.py` | 定义数据库表结构 |
|
||||
| 服务 | `services/operation_log.py` | 工具函数 `log_operation()` |
|
||||
| API | `routers/logs.py` | `GET /api/logs` 查询接口 |
|
||||
| 埋点 | 各 CRUD 路由中 | 在 create/update/delete 后调用 |
|
||||
|
||||
---
|
||||
|
||||
## 模型定义
|
||||
|
||||
```python
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Text
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class OperationLog(Base):
|
||||
__tablename__ = "operation_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user = Column(String(64), default="admin", comment="操作人")
|
||||
action = Column(String(32), nullable=False, comment="操作类型: create/update/delete")
|
||||
entity_type = Column(String(32), nullable=False, comment="实体类型")
|
||||
entity_id = Column(Integer, nullable=True, comment="实体ID")
|
||||
entity_name = Column(String(128), nullable=True, comment="实体名称(冗余,方便展示)")
|
||||
detail = Column(Text, nullable=True, comment="操作详情")
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), comment="操作时间")
|
||||
```
|
||||
|
||||
## 服务层
|
||||
|
||||
```python
|
||||
from sqlalchemy.orm import Session
|
||||
from app.models.operation_log import OperationLog
|
||||
|
||||
|
||||
def log_operation(
|
||||
db: Session,
|
||||
action: str,
|
||||
entity_type: str,
|
||||
entity_id: int = None,
|
||||
entity_name: str = None,
|
||||
detail: str = None,
|
||||
user: str = "admin",
|
||||
):
|
||||
log = OperationLog(
|
||||
user=user,
|
||||
action=action,
|
||||
entity_type=entity_type,
|
||||
entity_id=entity_id,
|
||||
entity_name=entity_name,
|
||||
detail=detail,
|
||||
)
|
||||
db.add(log)
|
||||
db.commit()
|
||||
```
|
||||
|
||||
## API 路由
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from app.database import get_db
|
||||
from app.models.operation_log import OperationLog
|
||||
from typing import Optional
|
||||
|
||||
router = APIRouter(prefix="/api/logs", tags=["操作日志"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
def list_logs(
|
||||
entity_type: Optional[str] = Query(None),
|
||||
action: Optional[str] = Query(None),
|
||||
limit: int = Query(100, ge=1, le=500),
|
||||
offset: int = Query(0, ge=0),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
q = db.query(OperationLog).order_by(OperationLog.created_at.desc())
|
||||
if entity_type:
|
||||
q = q.filter(OperationLog.entity_type == entity_type)
|
||||
if action:
|
||||
q = q.filter(OperationLog.action == action)
|
||||
|
||||
total = q.count()
|
||||
logs = q.offset(offset).limit(limit).all()
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"logs": [
|
||||
{
|
||||
"id": log.id,
|
||||
"user": log.user,
|
||||
"action": log.action,
|
||||
"entity_type": log.entity_type,
|
||||
"entity_id": log.entity_id,
|
||||
"entity_name": log.entity_name,
|
||||
"detail": log.detail,
|
||||
"created_at": log.created_at.isoformat() if log.created_at else None,
|
||||
}
|
||||
for log in logs
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## 埋点注入模式
|
||||
|
||||
### 创建后
|
||||
|
||||
```python
|
||||
log_operation(db, "create", "project", p.id, p.project_name, f"创建项目: {p.project_code}")
|
||||
```
|
||||
|
||||
### 更新后
|
||||
|
||||
```python
|
||||
log_operation(db, "update", "project", p.id, p.project_name, f"更新项目: {p.project_code}")
|
||||
```
|
||||
|
||||
### 删除前(需要 entity_name 做记录)
|
||||
|
||||
```python
|
||||
log_operation(db, "delete", "project", project_id, p.project_name if p else None, f"删除项目: ID={project_id}")
|
||||
```
|
||||
|
||||
### 批量导入
|
||||
|
||||
```python
|
||||
log_operation(db, "create", "import", None, filename, f"Excel导入: {stats}")
|
||||
```
|
||||
|
||||
## 注册路由
|
||||
|
||||
在 `main.py` 中:
|
||||
|
||||
```python
|
||||
from app.routers import auth, groups, personnel, projects, settings, dashboard, reports, search, finance, logs
|
||||
|
||||
# 受保护路由
|
||||
app.include_router(logs.router, dependencies=[Depends(verify_token)])
|
||||
```
|
||||
|
||||
## 前端页面
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-card>
|
||||
<template #header>
|
||||
<span>📝 操作日志</span>
|
||||
<!-- 筛选:实体类型 + 操作类型 -->
|
||||
</template>
|
||||
<el-table :data="logs" stripe>
|
||||
<el-table-column prop="created_at" label="时间" />
|
||||
<el-table-column prop="user" label="操作人" />
|
||||
<el-table-column label="操作">
|
||||
<el-tag :type="actionTagType(row.action)" size="small">
|
||||
{{ row.action === 'create' ? '创建' : row.action === 'update' ? '更新' : '删除' }}
|
||||
</el-tag>
|
||||
</el-table-column>
|
||||
<el-table-column label="实体" />
|
||||
<el-table-column prop="entity_name" label="实体名称" />
|
||||
<el-table-column prop="detail" label="操作详情" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 路由注册 + 侧边栏
|
||||
|
||||
```javascript
|
||||
// router/index.js
|
||||
{
|
||||
path: 'logs',
|
||||
name: 'Logs',
|
||||
component: () => import('../views/logs/Index.vue'),
|
||||
}
|
||||
|
||||
// Layout.vue 侧边栏
|
||||
<el-menu-item index="/logs">
|
||||
<el-icon><Tickets /></el-icon>
|
||||
<span>操作日志</span>
|
||||
</el-menu-item>
|
||||
```
|
||||
|
||||
## 适用实体类型
|
||||
|
||||
| entity_type | 含义 | 埋点位置 |
|
||||
|:------------|:-----|:---------|
|
||||
| project | 项目 | projects.py CRUD |
|
||||
| task | WBS 任务 | projects.py task CRUD |
|
||||
| milestone | 里程碑 | projects.py milestone CRUD |
|
||||
| income | 月度收益 | projects.py income CRUD |
|
||||
| personnel | 人员 | personnel.py CRUD |
|
||||
| group | 组别 | groups.py CRUD |
|
||||
| setting | 系统设置 | settings.py CRUD |
|
||||
| import | 数据导入 | import_data.py |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **删除操作需在 delete 前记录**:`db.delete()` 后对象属性仍可访问,但 `db.commit()` 后不可用
|
||||
2. **SQLAlchemy Column 类型警告**:Pyright 会报 `Column[int]` 不能赋值给 `int` 的参数类型错误,这是已知假阳性,不影响运行
|
||||
3. **不要过度埋点**:GET 查询操作不需要记录,只记录 create/update/delete
|
||||
4. **entity_name 冗余存储**:即使关联实体被删除,日志中仍保留名称用于展示
|
||||
109
archives/dev-pipeline-universal/references/quality-gates.md
Normal file
109
archives/dev-pipeline-universal/references/quality-gates.md
Normal file
@ -0,0 +1,109 @@
|
||||
# 研发流水线 — 质量门禁规范
|
||||
# 每个阶段完成后,由马总管自动执行质量门禁检查
|
||||
|
||||
---
|
||||
|
||||
## 质量门禁总览
|
||||
|
||||
```
|
||||
需求 ──→ [QG1] ──→ 设计 ──→ [QG2] ──→ 编码 ──→ [QG3] ──→ 测试 ──→ [QG4] ──→ 部署
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## QG1: 需求分析门禁
|
||||
|
||||
**执行时机:** ①需求分析完成后,进入②设计之前
|
||||
|
||||
| 检查项 | 通过标准 | 执行方式 |
|
||||
|--------|---------|---------|
|
||||
| 产物格式完整 | YAML所有必填字段非空 | 自动 |
|
||||
| 验收标准可测试 | 每条AC标记testable=true | 自动 |
|
||||
| 范围边界清晰 | in_scope和out_of_scope均非空 | 自动 |
|
||||
| 用户确认 | 乔确认需求理解正确 | 人工 |
|
||||
|
||||
**不通过处理:** 补充缺失信息,重新跟乔确认
|
||||
|
||||
---
|
||||
|
||||
## QG2: 技术方案门禁
|
||||
|
||||
**执行时机:** ②设计完成后,进入③编码之前
|
||||
|
||||
| 检查项 | 通过标准 | 执行方式 |
|
||||
|--------|---------|---------|
|
||||
| 产物格式完整 | YAML所有必填字段非空 | 自动 |
|
||||
| 文件变更清单 | file_changes列表非空 | 自动 |
|
||||
| API设计与需求对齐 | 每个AC至少对应一个API或功能点 | AI审查 |
|
||||
| 无遗留阻断问题 | open_issues中无阻断级问题 | 自动 |
|
||||
| 重大决策确认 | 涉及架构变更时需乔确认 | 条件人工 |
|
||||
|
||||
**不通过处理:** 反馈问题给架构师Agent重新设计(最多2轮)
|
||||
|
||||
---
|
||||
|
||||
## QG3: 编码实现门禁
|
||||
|
||||
**执行时机:** ③编码完成后,进入④审查之前
|
||||
|
||||
| 检查项 | 通过标准 | 执行方式 |
|
||||
|--------|---------|---------|
|
||||
| 产物格式完整 | YAML所有必填字段非空 | 自动 |
|
||||
| 代码可编译/无语法错误 | self_check.compiles=true | 自动 |
|
||||
| lint通过 | self_check.lint_passes=true | 自动 |
|
||||
| 文件变更与设计一致 | 实际变更文件 ⊆ 设计中的file_changes | AI审查 |
|
||||
| 无常驻进程残留 | 无未关闭的后台服务 | 自动 |
|
||||
|
||||
**不通过处理:** 反馈给程序员Agent修复(最多3轮)
|
||||
|
||||
---
|
||||
|
||||
## QG4: 测试验证门禁
|
||||
|
||||
**执行时机:** ⑤测试完成后,进入⑥部署之前
|
||||
|
||||
| 检查项 | 通过标准 | 执行方式 |
|
||||
|--------|---------|---------|
|
||||
| 所有测试通过 | verdict=ALL_PASS | 自动 |
|
||||
| 覆盖率达标 | line_percent >= 阈值(见测试规范) | 自动 |
|
||||
| 无回归 | existing_tests_still_pass=true | 自动 |
|
||||
| 冒烟测试通过 | smoke.failed=0 | 自动 |
|
||||
| 部署确认 | 乔确认可以部署 | 人工 |
|
||||
|
||||
**不通过处理:** 失败信息反馈给程序员Agent修复 → 重新测试(最多3轮)
|
||||
|
||||
---
|
||||
|
||||
## 门禁执行规则
|
||||
|
||||
### 重试策略
|
||||
```
|
||||
第1次失败 → 同模型重试,附带失败原因
|
||||
第2次失败 → 换备用模型重试
|
||||
第3次失败 → 上报乔,进入人工介入
|
||||
```
|
||||
|
||||
### 备用模型降级链(示例 — 根据实际可用模型替换)
|
||||
|
||||
降级原则:主模型失败时,按以下优先级替换:
|
||||
1. 同平台其他模型(相同 API 端点)
|
||||
2. 不同平台同等级模型
|
||||
3. 快速响应模型兜底
|
||||
|
||||
| 角色 | 降级策略 |
|
||||
|------|---------|
|
||||
| 架构师 | 强推理模型 → 同平台次强模型 → 通用模型 |
|
||||
| 设计师 | 编码版模型 → 通用强模型 → 快速模型 |
|
||||
| 主程序员 | 深度编码模型 → 其他编码模型 → 快速模型 |
|
||||
| 结对程序员 | 与主程序员不同家族模型 → 主程序员模型 |
|
||||
| 审查员 | 深度推理模型 → 架构师模型 |
|
||||
| 测试员 | Agent能力强的模型 → 编码模型 |
|
||||
|
||||
### 超时保护
|
||||
| 阶段 | 超时时间 | 超时处理 |
|
||||
|------|---------|---------|
|
||||
| ②设计 | 30分钟 | 终止,记录已有产出,人工介入 |
|
||||
| ③UI设计 | 10分钟 | 换模型重试,精简prompt |
|
||||
| ④编码 | 50分钟 | 终止,保留已写代码,人工介入 |
|
||||
| ⑤审查 | 25分钟 | 终止,标记"审查超时",进入测试 |
|
||||
| ⑥测试 | 40分钟 | 终止,保留已有测试结果,人工介入 |
|
||||
@ -0,0 +1,113 @@
|
||||
# 收益归属矩阵(业务报表模式)
|
||||
|
||||
> **适用场景**: 需要按人×项目维度展示收益归属矩阵
|
||||
> **创建日期**: 2026-06-24
|
||||
|
||||
---
|
||||
|
||||
## 业务含义
|
||||
|
||||
行 = 人员,列 = 项目,交叉单元格 = 该人在该项目的收益贡献。类似 Excel 透视表。
|
||||
|
||||
```
|
||||
项目A 项目B 项目C 小计
|
||||
郑明义 ¥14,983 ¥4,142 ¥1,839 ¥20,964
|
||||
薛文 ¥11,600 ¥884 ¥3,678 ¥16,162
|
||||
总计 ¥82,031 ¥7,070 ¥9,196 ¥98,297
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 计算逻辑
|
||||
|
||||
### 数据源
|
||||
|
||||
- `project_monthly_income` — 项目月度总收益(按项目×月份)
|
||||
- `personnel_projects` — 人员-项目关联(含 `allocation_ratio` 分配比例)
|
||||
|
||||
### 核心公式
|
||||
|
||||
```
|
||||
个人在项目的收益 = 项目总收益 × (该人的分配比例 / 100)
|
||||
```
|
||||
|
||||
### 特殊情况
|
||||
|
||||
| 情况 | 处理方式 |
|
||||
|------|---------|
|
||||
| 人员有关联比例 | 按比例分摊项目总收益 |
|
||||
| 人员无关联比例 | 收益为 0(不显示在矩阵中) |
|
||||
| 项目无收益记录 | 收益为 0(不显示在矩阵中) |
|
||||
| 月份筛选 | 只计算该月的收益记录 |
|
||||
|
||||
### API 设计
|
||||
|
||||
```
|
||||
GET /api/reports/income-matrix?month=2026-06
|
||||
|
||||
Response:
|
||||
{
|
||||
"rows": [
|
||||
{
|
||||
"name": "郑明义",
|
||||
"group_name": "四组",
|
||||
"projects": {"南方医科大学珠江医院": 9666.67, "苏州市立医院": 1325.58},
|
||||
"subtotal": 12831.38
|
||||
}
|
||||
],
|
||||
"project_totals": {"南方医科大学珠江医院": 82031.34, ...},
|
||||
"grand_total": 442598.68,
|
||||
"month": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 前端实现要点
|
||||
|
||||
### 动态列
|
||||
|
||||
项目列是动态的(随数据变化),需要用 `v-for` 在 `<el-table-column>` 上渲染:
|
||||
|
||||
```vue
|
||||
<el-table-column v-for="proj in projectNames" :key="proj" :label="proj" min-width="140" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ row.projects[proj] > 0 ? '¥' + row.projects[proj].toLocaleString() : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
```
|
||||
|
||||
### 总计行
|
||||
|
||||
用 `<el-descriptions>` 展示项目总计,放在表格下方:
|
||||
|
||||
```vue
|
||||
<el-descriptions :column="4" border size="small">
|
||||
<el-descriptions-item label="总计">
|
||||
¥{{ grandTotal.toLocaleString() }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-for="(val, key) in projectTotals" :key="key" :label="key">
|
||||
¥{{ val.toLocaleString() }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
```
|
||||
|
||||
### 月份筛选
|
||||
|
||||
提供月份下拉框,不传参时汇总所有月份。筛选在 API 层完成,前端只传参数。
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 为什么矩阵中的项目总收益不等于项目列表中的预期收益?
|
||||
|
||||
A: 矩阵中的收益来自 `project_monthly_income`(实际实现的月度收益),不等于项目的预期收益(合同金额)。两者是不同概念。
|
||||
|
||||
### Q: 为什么某些人没有出现在矩阵中?
|
||||
|
||||
A: 只有 `subtotal > 0` 的行才会显示。如果人员有关联项目但项目没有收益记录,该人员不会出现。
|
||||
|
||||
### Q: 分配比例从哪里来?
|
||||
|
||||
A: 从 `personnel_projects.allocation_ratio` 字段读取。初始导入时从 Excel 工作描述中的"占比 N%"正则提取,无比例时按项目关联人数均分。
|
||||
@ -0,0 +1,41 @@
|
||||
# 研发流水线 — 角色模型配置示例
|
||||
#
|
||||
# 使用前请复制为 roles.yaml,修改模型名称为您实际可用的模型。
|
||||
# 每个角色使用不同模型家族可获得更好的交叉审视效果。
|
||||
|
||||
roles:
|
||||
- name: "架构师"
|
||||
code: "ARCH"
|
||||
emoji: "🏗️"
|
||||
stages: ["②技术方案"]
|
||||
model: "<强推理模型,如 glm-5.2 / claude-sonnet-4 / deepseek-v4-pro>"
|
||||
|
||||
- name: "设计师"
|
||||
code: "UI"
|
||||
emoji: "🎨"
|
||||
stages: ["③UI设计"]
|
||||
model: "<编码版模型,如 kimi-k2.7-code / claude-sonnet-4>"
|
||||
|
||||
- name: "主程序员"
|
||||
code: "DEV"
|
||||
emoji: "💻"
|
||||
stages: ["④编码实现"]
|
||||
model: "<深度编码模型,如 deepseek-v4-pro / claude-sonnet-4>"
|
||||
|
||||
- name: "结对程序员"
|
||||
code: "DEV2"
|
||||
emoji: "👥"
|
||||
stages: ["④编码实现(review)"]
|
||||
model: "<与主程序员不同家族,如 kimi-k2.7-code / gpt-4o>"
|
||||
|
||||
- name: "审查员"
|
||||
code: "REVIEW"
|
||||
emoji: "🔍"
|
||||
stages: ["⑤代码审查"]
|
||||
model: "<深度推理模型,如 deepseek-v4-pro / claude-sonnet-4>"
|
||||
|
||||
- name: "测试员"
|
||||
code: "QA"
|
||||
emoji: "🧪"
|
||||
stages: ["⑥测试验证"]
|
||||
model: "<Agent能力强的模型,如 minimax-m3 / claude-sonnet-4>"
|
||||
125
archives/dev-pipeline-universal/references/testing-strategy.md
Normal file
125
archives/dev-pipeline-universal/references/testing-strategy.md
Normal file
@ -0,0 +1,125 @@
|
||||
# 研发流水线 — 测试分层策略
|
||||
|
||||
---
|
||||
|
||||
## 测试金字塔
|
||||
|
||||
```
|
||||
╱╲
|
||||
╱ 冒烟 ╲ ← 5-10% (部署后关键路径验证)
|
||||
╱────────╲
|
||||
╱ 集成测试 ╲ ← 20-30% (API/服务间交互)
|
||||
╱──────────────╲
|
||||
╱ 单元测试 ╲ ← 60-70% (函数/类级别)
|
||||
╱──────────────────╲
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 各层测试规范
|
||||
|
||||
### 单元测试
|
||||
|
||||
**目标:** 验证单个函数/类的行为正确性
|
||||
**覆盖率要求:** 行覆盖 ≥ 70%,函数覆盖 ≥ 80%
|
||||
|
||||
**测试Agent生成规则:**
|
||||
1. 只看接口签名+文档+验收标准,不看实现细节(黑盒测试优先)
|
||||
2. 必须覆盖:正常路径、边界值(空/null/最大/最小)、异常路径
|
||||
3. 每个测试函数只验证一个行为点
|
||||
4. 测试命名格式:`test_<功能>_<场景>_<预期结果>`
|
||||
|
||||
### 集成测试
|
||||
|
||||
**目标:** 验证模块间/服务间交互正确性
|
||||
**覆盖率要求:** 所有API接口100%覆盖
|
||||
|
||||
**测试Agent生成规则:**
|
||||
1. 每个API端点至少:1个成功用例 + 1个参数校验失败 + 1个权限失败
|
||||
2. 测试数据库/外部服务交互的完整流程
|
||||
3. 使用真实的数据库(SQLite内存模式)或合理的Mock
|
||||
|
||||
### 冒烟测试
|
||||
|
||||
**目标:** 部署后快速验证核心功能可用
|
||||
**覆盖率要求:** 覆盖所有核心用户路径
|
||||
|
||||
**测试Agent生成规则:**
|
||||
1. 从验收标准(AC)中提取关键路径
|
||||
2. 只验证"能不能用",不验证边界条件
|
||||
3. 执行时间 < 30秒
|
||||
4. 对于前端项目:启动服务 → 访问关键页面 → 验证无崩溃
|
||||
|
||||
---
|
||||
|
||||
## 测试质量保障
|
||||
|
||||
### 独立性原则(参考AgentCoder论文 arXiv:2312.13010)
|
||||
- 测试Agent(minimax-m3)与编码Agent(deepseek-v4-pro)使用不同模型
|
||||
- 测试Agent的prompt中**不包含**编码Agent的prompt和实现思路
|
||||
- 测试Agent只接收:接口文档 + 验收标准 + 代码文件(只看签名,不解释逻辑)
|
||||
|
||||
### 测试Agent的Context模板
|
||||
```
|
||||
你是一个QA测试工程师。根据以下信息编写测试:
|
||||
|
||||
## 需求验收标准
|
||||
{acceptance_criteria}
|
||||
|
||||
## 接口/函数签名
|
||||
{api_signatures}
|
||||
|
||||
## 项目测试约定
|
||||
- 框架: {test_framework}
|
||||
- 运行命令: {test_command}
|
||||
- 测试目录: {test_directory}
|
||||
|
||||
## 规则
|
||||
1. 优先编写能发现bug的测试(边界值、异常路径)
|
||||
2. 不要写"同义反复"测试(只是重复实现逻辑)
|
||||
3. 每个测试必须有明确的断言
|
||||
4. 测试之间相互独立,无执行顺序依赖
|
||||
5. 编写完成后运行全部测试并输出结果
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 覆盖率阈值
|
||||
|
||||
| 项目规模 | 行覆盖 | 分支覆盖 | 函数覆盖 |
|
||||
|---------|--------|---------|---------|
|
||||
| 大型(新项目) | ≥ 70% | ≥ 60% | ≥ 80% |
|
||||
| 中型(新功能) | ≥ 60% | ≥ 50% | ≥ 75% |
|
||||
| 小型(bug修复) | 修改行100%覆盖 | — | — |
|
||||
|
||||
---
|
||||
|
||||
## 前端项目特殊规则
|
||||
|
||||
前端项目(React/Vue/Next.js等)的测试策略有所不同:
|
||||
|
||||
| 层级 | 内容 | 工具建议 |
|
||||
|------|------|---------|
|
||||
| 单元 | 组件渲染测试、工具函数测试 | Jest + Testing Library |
|
||||
| 集成 | 页面级交互测试 | Playwright / Cypress |
|
||||
| 冒烟 | 构建+启动+关键页面可达+无JS错误 | curl + Playwright |
|
||||
|
||||
**前端冒烟测试最低要求:**
|
||||
1. `npm run build` 成功(无编译错误)
|
||||
2. 构建产物包含所有预期路由(检查 `.next/routes-manifest.json`)
|
||||
3. 关键页面路由可访问(无404)
|
||||
4. 浏览器控制台无未捕获错误
|
||||
|
||||
**⚠️ 陷阱:不要在vitest中spawn服务器**
|
||||
在测试中用 `spawn('npm', ['run', 'start'])` 启动Next.js再fetch会超时(Next.js启动慢)且进程清理不干净。替代方案:解析build产物中的 `routes-manifest.json`,验证静态路由和动态路由是否完整。示例:
|
||||
```typescript
|
||||
it('should have all expected routes after build', () => {
|
||||
const routesManifest = JSON.parse(
|
||||
fs.readFileSync('.next/routes-manifest.json', 'utf-8')
|
||||
)
|
||||
const dynamicRoutes = routesManifest.dynamicRoutes?.map(r => r.page) || []
|
||||
const staticRoutes = routesManifest.staticRoutes?.map(r => r.page) || []
|
||||
expect(staticRoutes).toContain('/')
|
||||
expect(dynamicRoutes).toContain('/departments/[id]')
|
||||
})
|
||||
```
|
||||
49
archives/dev-pipeline-universal/scripts/show_roles.py
Normal file
49
archives/dev-pipeline-universal/scripts/show_roles.py
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""研发流水线 — 角色模型对照表生成脚本(通用模板)
|
||||
|
||||
读取自定义的 roles.yaml 配置文件,输出角色模型对照表。
|
||||
用户需根据实际环境修改 references/roles.yaml 中的模型配置。
|
||||
|
||||
用法: python scripts/show_roles.py
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_roles():
|
||||
"""加载 roles.yaml"""
|
||||
script_dir = Path(__file__).parent.parent / "references"
|
||||
roles_path = script_dir / "roles.yaml"
|
||||
if not roles_path.exists():
|
||||
print(f"❌ 未找到 roles.yaml: {roles_path}")
|
||||
print("请复制 references/roles.example.yaml 为 references/roles.yaml 并修改配置")
|
||||
sys.exit(1)
|
||||
with open(roles_path) as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def main():
|
||||
roles_config = load_roles()
|
||||
roles = roles_config.get("roles", [])
|
||||
|
||||
print("=" * 80)
|
||||
print("研发流水线 — 角色模型对照表")
|
||||
print("=" * 80)
|
||||
|
||||
print("\n## 当前角色分配\n")
|
||||
print(f"{'角色':<12} {'代号':<8} {'阶段':<20} {'模型':<25}")
|
||||
print("-" * 70)
|
||||
for role in roles:
|
||||
stages = ", ".join(role.get("stages", []))
|
||||
print(f"{role.get('emoji', '')} {role['name']:<10} {role['code']:<8} {stages:<20} {role['model']:<25}")
|
||||
|
||||
# 一致性检查
|
||||
print(f"\n## 一致性检查\n")
|
||||
print("✅ 请手动检查 roles.yaml 中的模型配置是否与您的 API 平台一致。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
334
archives/dev-pipeline-universal/templates/stage-artifacts.md
Normal file
334
archives/dev-pipeline-universal/templates/stage-artifacts.md
Normal file
@ -0,0 +1,334 @@
|
||||
# 研发流水线 — 阶段产物模板
|
||||
# 每个阶段的子Agent必须按此格式输出产物
|
||||
# 总管(小研)负责校验产物格式完整性
|
||||
|
||||
---
|
||||
|
||||
## ① 需求分析产物
|
||||
|
||||
```yaml
|
||||
stage: requirements
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
# 核心产物
|
||||
artifacts:
|
||||
goal: "一句话描述功能目标"
|
||||
user_stories:
|
||||
- as: "角色"
|
||||
want: "功能"
|
||||
so_that: "价值"
|
||||
acceptance_criteria:
|
||||
- id: AC-1
|
||||
description: "验收条件描述"
|
||||
testable: true
|
||||
constraints:
|
||||
technical: ["技术约束"]
|
||||
timeline: "时间要求"
|
||||
resources: "资源限制"
|
||||
scope:
|
||||
in_scope: ["包含的功能点"]
|
||||
out_of_scope: ["明确排除的功能点"]
|
||||
|
||||
# 元数据
|
||||
metadata:
|
||||
complexity: "large|medium|small|micro"
|
||||
estimated_stages: ["①", "②", "③", "④", "⑤", "⑥"]
|
||||
risk_level: "high|medium|low"
|
||||
|
||||
# 遗留问题
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ② 技术方案产物
|
||||
|
||||
```yaml
|
||||
stage: design
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
architecture:
|
||||
pattern: "架构模式(如 monolith/microservice/modular)"
|
||||
modules:
|
||||
- name: "模块名"
|
||||
responsibility: "职责"
|
||||
dependencies: ["依赖的其他模块"]
|
||||
data_flow: "数据流描述"
|
||||
|
||||
tech_stack:
|
||||
language: "编程语言"
|
||||
framework: "框架"
|
||||
database: "数据库"
|
||||
other: ["其他技术选型"]
|
||||
reasons: "选型理由"
|
||||
|
||||
data_models:
|
||||
- name: "模型名"
|
||||
fields:
|
||||
- {name: "字段名", type: "类型", required: true, description: "说明"}
|
||||
relations: ["与其他模型的关系"]
|
||||
|
||||
api_design:
|
||||
- method: "HTTP方法"
|
||||
path: "/路径"
|
||||
description: "接口说明"
|
||||
request_schema: "请求体结构"
|
||||
response_schema: "响应体结构"
|
||||
|
||||
file_changes:
|
||||
new_files: ["需要新建的文件路径"]
|
||||
modified_files: ["需要修改的文件路径"]
|
||||
deleted_files: ["需要删除的文件路径"]
|
||||
|
||||
impact_analysis:
|
||||
affected_modules: ["受影响的模块"]
|
||||
breaking_changes: ["破坏性变更"]
|
||||
migration_needed: false
|
||||
|
||||
# 关键决策
|
||||
decisions:
|
||||
- decision: "决策内容"
|
||||
reason: "理由"
|
||||
alternatives: ["考虑过的替代方案"]
|
||||
|
||||
# 风险点
|
||||
risks:
|
||||
- description: "风险描述"
|
||||
probability: "high|medium|low"
|
||||
mitigation: "缓解措施"
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ③ UI设计产物(v2.1新增)
|
||||
|
||||
```yaml
|
||||
stage: ui_design
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
style_reference: "设计风格参考(如 微信原生风格/Material Design/Ant Design)"
|
||||
|
||||
design_tokens:
|
||||
colors:
|
||||
primary: "主色 (如 #07C160)"
|
||||
primary_light: "主色浅 (如 #E8F8EE)"
|
||||
secondary: "辅助色"
|
||||
background: "背景色"
|
||||
surface: "卡片/面板背景色"
|
||||
text_primary: "主文字色"
|
||||
text_secondary: "次要文字色"
|
||||
text_hint: "提示文字色"
|
||||
border: "边框色"
|
||||
error: "错误色"
|
||||
success: "成功色"
|
||||
warning: "警告色"
|
||||
typography:
|
||||
font_family: "字体"
|
||||
heading_1: "标题1样式"
|
||||
heading_2: "标题2样式"
|
||||
body: "正文样式"
|
||||
caption: "说明文字样式"
|
||||
spacing:
|
||||
page_padding: "页面内边距"
|
||||
card_padding: "卡片内边距"
|
||||
section_gap: "区块间距"
|
||||
item_gap: "列表项间距"
|
||||
border_radius:
|
||||
card: "卡片圆角"
|
||||
button: "按钮圆角"
|
||||
input: "输入框圆角"
|
||||
avatar: "头像圆角"
|
||||
shadows:
|
||||
card: "卡片阴影"
|
||||
elevated: "浮起阴影"
|
||||
|
||||
component_styles:
|
||||
- name: "组件名(如 DoctorCard)"
|
||||
description: "组件用途"
|
||||
tailwind_classes: "具体的Tailwind类名组合"
|
||||
states:
|
||||
default: "默认状态样式"
|
||||
hover: "悬停状态样式(如有)"
|
||||
active: "按下状态样式(如有)"
|
||||
disabled: "禁用状态样式(如有)"
|
||||
|
||||
page_layouts:
|
||||
- page: "页面路径(如 /departments/[id])"
|
||||
structure: "页面结构描述"
|
||||
key_elements: ["页面包含的关键UI元素"]
|
||||
|
||||
interaction_notes:
|
||||
- element: "交互元素"
|
||||
behavior: "交互行为描述"
|
||||
|
||||
# 自然语言摘要
|
||||
summary: "设计方案一句话总结"
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ④ 编码实现产物
|
||||
|
||||
```yaml
|
||||
stage: implementation
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
files_changed:
|
||||
- path: "文件路径"
|
||||
action: "created|modified|deleted"
|
||||
description: "变更说明"
|
||||
|
||||
implementation_notes:
|
||||
approach: "实现思路简述"
|
||||
key_decisions: ["实现中的关键决策"]
|
||||
deviations_from_design: ["与设计方案的偏差及原因"]
|
||||
|
||||
dependencies_added:
|
||||
- name: "依赖名"
|
||||
version: "版本"
|
||||
reason: "添加原因"
|
||||
|
||||
commands_to_run:
|
||||
build: "构建命令"
|
||||
start: "启动命令(如有)"
|
||||
|
||||
# 自检结果
|
||||
self_check:
|
||||
compiles: true
|
||||
lint_passes: true
|
||||
basic_smoke_test: "描述基本冒烟验证结果"
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⑤ 代码审查产物
|
||||
|
||||
```yaml
|
||||
stage: code_review
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
verdict: "APPROVED|REQUEST_CHANGES"
|
||||
|
||||
issues:
|
||||
- id: "CR-1"
|
||||
severity: "CRITICAL|IMPORTANT|MINOR"
|
||||
category: "security|correctness|performance|maintainability|convention|testing"
|
||||
file: "文件路径"
|
||||
line: 42
|
||||
description: "问题描述"
|
||||
suggestion: "修改建议"
|
||||
confidence: 0.9
|
||||
|
||||
dimensions_checked:
|
||||
security: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
correctness: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
performance: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
maintainability: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
convention: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
test_coverage: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
|
||||
# 信息隔离声明
|
||||
review_context:
|
||||
saw_implementation_prompt: false # 审查者不应看到编码prompt
|
||||
saw_only: ["代码变更(git diff)", "需求文档", "技术方案"]
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⑥ 测试验证产物
|
||||
|
||||
```yaml
|
||||
stage: testing
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
verdict: "ALL_PASS|PARTIAL_FAIL|ALL_FAIL"
|
||||
|
||||
test_layers:
|
||||
unit:
|
||||
total: 0
|
||||
passed: 0
|
||||
failed: 0
|
||||
skipped: 0
|
||||
coverage_percent: 0
|
||||
test_files: ["测试文件路径"]
|
||||
integration:
|
||||
total: 0
|
||||
passed: 0
|
||||
failed: 0
|
||||
skipped: 0
|
||||
test_files: []
|
||||
smoke:
|
||||
total: 0
|
||||
passed: 0
|
||||
failed: 0
|
||||
description: "冒烟测试说明"
|
||||
|
||||
failed_tests:
|
||||
- test_name: "失败的测试名"
|
||||
layer: "unit|integration|smoke"
|
||||
error_message: "错误信息"
|
||||
root_cause: "初步分析"
|
||||
suggested_fix: "建议修复方式"
|
||||
|
||||
coverage:
|
||||
line_percent: 0
|
||||
branch_percent: 0
|
||||
function_percent: 0
|
||||
meets_threshold: true
|
||||
|
||||
regression_check:
|
||||
existing_tests_still_pass: true
|
||||
broken_tests: []
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⑦ 部署上线产物
|
||||
|
||||
```yaml
|
||||
stage: deployment
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
deployment_method: "描述部署方式"
|
||||
environment: "production|staging|development"
|
||||
|
||||
steps_executed:
|
||||
- step: "步骤描述"
|
||||
status: "success|failed"
|
||||
output: "关键输出"
|
||||
|
||||
verification:
|
||||
service_running: true
|
||||
health_check_passed: true
|
||||
smoke_test_passed: true
|
||||
key_features_verified: ["已验证的核心功能"]
|
||||
|
||||
rollback_plan:
|
||||
method: "回滚方式"
|
||||
command: "回滚命令"
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
@ -1,5 +1,16 @@
|
||||
{
|
||||
"framework": "hermes-agent",
|
||||
"skills": [],
|
||||
"lastUpdated": "2026-07-01"
|
||||
"skills": [
|
||||
{
|
||||
"name": "dev-pipeline-universal",
|
||||
"version": "1.0.0",
|
||||
"description": "通用研发流水线 — 从需求分析到功能上线的全流程 Agent 协作方案",
|
||||
"author": "马总管",
|
||||
"source": "archives/dev-pipeline-universal-v1.0.0.tar.gz",
|
||||
"importedAt": "2026-07-02",
|
||||
"platform": "local",
|
||||
"note": "SkillHub 暂不支持 Hermes Agent,仅本地管理"
|
||||
}
|
||||
],
|
||||
"lastUpdated": "2026-07-02"
|
||||
}
|
||||
|
||||
8
hermes-agent/skills/dev-pipeline-universal/CHANGELOG.md
Normal file
8
hermes-agent/skills/dev-pipeline-universal/CHANGELOG.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0 - 2026-06-30
|
||||
|
||||
- 初始版本(由外部导入)
|
||||
- 来源:archives/dev-pipeline-universal-v1.0.0.tar.gz
|
||||
- 作者:马总管
|
||||
- 通用研发流水线:从需求分析到功能上线的全流程 Agent 协作方案
|
||||
60
hermes-agent/skills/dev-pipeline-universal/README.md
Normal file
60
hermes-agent/skills/dev-pipeline-universal/README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# 🔧 通用研发流水线技能包
|
||||
|
||||
> 版本: v1.0.0 | 适用: Hermes Agent / OpenClaw / AgentSkills 兼容平台
|
||||
|
||||
## 简介
|
||||
|
||||
一套完整的研发流水线技能包,覆盖从需求分析到功能上线的全流程。包含:
|
||||
|
||||
- **7 个阶段**:需求分析 → 技术方案 → UI设计(可选) → 编码实现(结对) → 代码审查 → 测试验证 → 部署上线
|
||||
- **4 道质量门禁**:确保每个阶段产出质量
|
||||
- **7 个角色**:总管、架构师、设计师、主程序员、结对程序员、审查员、测试员
|
||||
- **测试分层策略**:单元测试 + 集成测试 + 冒烟测试
|
||||
- **实战陷阱文档**:FastAPI、前端构建、Excel导入等
|
||||
|
||||
## 安装
|
||||
|
||||
### OpenClaw
|
||||
|
||||
```bash
|
||||
# 复制到 OpenClaw 技能目录
|
||||
cp -r dev-pipeline-universal ~/.openclaw/skills/dev-pipeline
|
||||
|
||||
# 或复制到工作区技能目录(更高优先级)
|
||||
cp -r dev-pipeline-universal <workspace>/skills/dev-pipeline
|
||||
```
|
||||
|
||||
### Hermes Agent
|
||||
|
||||
```bash
|
||||
# 复制到 Hermes 技能目录
|
||||
cp -r dev-pipeline-universal ~/.hermes/skills/software-development/dev-pipeline
|
||||
```
|
||||
|
||||
## 使用前配置
|
||||
|
||||
1. 复制 `references/roles.example.yaml` 为 `references/roles.yaml`
|
||||
2. 修改模型名称为您实际可用的模型
|
||||
3. 根据您的技术栈调整测试框架配置
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
dev-pipeline-universal/
|
||||
├── SKILL.md # 主技能文档
|
||||
├── templates/
|
||||
│ └── stage-artifacts.md # 7阶段产物YAML模板
|
||||
├── references/
|
||||
│ ├── roles.example.yaml # 角色模型配置示例
|
||||
│ ├── quality-gates.md # 质量门禁详情
|
||||
│ ├── testing-strategy.md # 测试分层策略
|
||||
│ ├── fastapi-pitfalls.md # FastAPI 实战陷阱
|
||||
│ ├── frontend-build-optimization.md # 前端构建优化
|
||||
│ ├── operation-log-pattern.md # 操作日志系统模板
|
||||
│ ├── framework-comparison.md # AI框架调研对比
|
||||
│ ├── excel-data-import.md # Excel批量导入
|
||||
│ ├── revenue-allocation-pattern.md # 收益归属矩阵
|
||||
│ └── doc-sync-pattern.md # 文档同步模板
|
||||
└── scripts/
|
||||
└── show_roles.py # 角色对照表生成脚本
|
||||
```
|
||||
378
hermes-agent/skills/dev-pipeline-universal/SKILL.md
Normal file
378
hermes-agent/skills/dev-pipeline-universal/SKILL.md
Normal file
@ -0,0 +1,378 @@
|
||||
---
|
||||
name: dev-pipeline
|
||||
description: 通用研发流水线 — 从需求分析到功能上线的全流程 Agent 协作方案。定义角色、阶段、质量门禁、测试分层和流转规则。
|
||||
version: 1.0.0
|
||||
date: 2026-06-30
|
||||
author: 马总管 🐎
|
||||
metadata:
|
||||
openclaw:
|
||||
emoji: "🔧"
|
||||
tags: [研发流程, subagent, 多模型协作, 流水线, devops, 质量门禁, 测试分层]
|
||||
requires:
|
||||
bins: []
|
||||
hermes:
|
||||
tags: [研发流程, subagent, 多模型协作, 流水线, devops, 质量门禁, 测试分层]
|
||||
---
|
||||
|
||||
# 通用研发流水线(Dev Pipeline)
|
||||
|
||||
> **版本**: v1.0.0(通用版)
|
||||
> **创建日期**: 2026-06-30
|
||||
> **维护者**: 马总管 🐎
|
||||
> **适用平台**: Hermes Agent / OpenClaw / 其他 AgentSkills 兼容平台
|
||||
|
||||
## 一、概述
|
||||
|
||||
组建一个完整的研发小单元,覆盖从需求分析到功能上线的全流程。由总管负责调度,通过 delegate_task / subagent 机制将各阶段任务分派给不同角色的子 Agent,每个角色使用最适合的模型。
|
||||
|
||||
### 流程总览
|
||||
|
||||
```
|
||||
用户(提需求)
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ① 需求分析 — 总管 │
|
||||
│ 理解业务意图,输出结构化需求文档,跟用户确认 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼ [QG1: 需求门禁]
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ② 技术方案 — 架构师 │
|
||||
│ 读代码库 + 需求文档,输出设计方案 │
|
||||
│ 总管 review 方案,必要时跟用户确认 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼ [QG2: 设计门禁]
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ③ UI设计 — 设计师 (可选) │
|
||||
│ 输出设计规范(配色/字体/间距/组件样式代码) │
|
||||
│ 仅面向用户的项目需要,纯后端/API可跳过 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ④ 编码实现(结对编程模式,可选) │
|
||||
│ ④a 主程序员写代码 │
|
||||
│ ④b 结对程序员 review+改进 │
|
||||
│ 大型/中型启用结对,小型/热修复单程序员 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼ [QG3: 编码门禁]
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ⑤ 代码审查 — 审查员 │
|
||||
│ Review 代码变更,输出结构化审查报告 │
|
||||
│ ├─ REQUEST_CHANGES → 回到④让程序员修改 │
|
||||
│ └─ APPROVED → 进入下一阶段 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ⑥ 测试验证 — 测试员 │
|
||||
│ 分层编写测试(单元+集成+冒烟)并执行 │
|
||||
│ ├─ PARTIAL_FAIL → 回到④让程序员修复 │
|
||||
│ └─ ALL_PASS → 进入下一阶段 │
|
||||
└──────────┬───────────────────────────────────┘
|
||||
▼ [QG4: 测试门禁]
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ⑦ 部署上线 — 总管 │
|
||||
│ 构建、部署、验证,汇报结果给用户 │
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 角色定义
|
||||
|
||||
| 角色 | 代号 | 流程阶段 | 推荐模型类型 |
|
||||
|------|------|---------|-------------|
|
||||
| 🧑💼 总管 | PM | ①需求分析 ⑦部署上线 | 快速响应模型(日常对话) |
|
||||
| 🏗️ 架构师 | ARCH | ②技术方案 | 强推理模型(复杂推理) |
|
||||
| 🎨 设计师 | UI | ③UI设计 | 编码版模型(设计+编码) |
|
||||
| 💻 主程序员 | DEV | ④编码实现 | 深度推理编码模型 |
|
||||
| 👥 结对程序员 | DEV2 | ④编码实现(review) | 与主程序员不同家族模型 |
|
||||
| 🔍 审查员 | REVIEW | ⑤代码审查 | 深度推理模型 |
|
||||
| 🧪 测试员 | QA | ⑥测试验证 | Agent/Tool 能力强的模型 |
|
||||
|
||||
**模型选择原则**:
|
||||
- 编码/审查/测试使用**不同模型家族**,消除单模型偏见
|
||||
- 主程序员和结对程序员用不同厂商模型,交叉审视
|
||||
- 测试员与编码员信息隔离(测试员不看实现思路)
|
||||
|
||||
### 流程裁剪规则
|
||||
|
||||
并非所有需求都必须走完全部阶段。由总管根据需求规模判断裁剪:
|
||||
|
||||
| 需求规模 | 定义 | 走哪些阶段 | 结对编程 | 示例 |
|
||||
|---------|------|-----------|---------|------|
|
||||
| 🔴 大型 | 新功能/新模块/架构变更 | ①②③④⑤⑥⑦ 全流程 | ✅ 启用 | 新增模块、系统重构 |
|
||||
| 🟡 中型 | 现有功能扩展/较复杂 Bug | ①④⑤⑥ 跳过②③设计 | ✅ 启用 | 加导出功能、修复复杂 Bug |
|
||||
| 🟡 中型(含UI) | 现有功能扩展+UI改版 | ①③④⑤⑥ 跳过②方案设计 | ✅ 启用 | 界面改版、新增交互页面 |
|
||||
| 🟢 小型 | 简单修改/配置变更 | ①④⑥ 最小流程 | ❌ 单程序员 | 改字段校验、加配置项 |
|
||||
| ⚪ 微型 | 一行代码/纯配置 | 总管直接做,不 delegate | — | 改文案、调参数 |
|
||||
| 🔥 热修复 | 线上紧急 Bug | ④⑥⑦ 跳过需求和审查 | ❌ 单程序员 | 线上崩溃、数据错误 |
|
||||
|
||||
**裁剪原则**:
|
||||
- ①需求分析(总管)永远不跳过——再小的需求也要先确认理解对了(热修复除外)
|
||||
- ⑤代码审查在中型以上需求中必须执行
|
||||
- ⑥测试验证在有代码变更时必须执行(至少验证不破坏现有功能)
|
||||
- 总管负责判断需求规模,有争议时向用户确认
|
||||
|
||||
### 项目目录规范(建议)
|
||||
|
||||
- 所有研发项目统一放在 `~/projects-dev/` 下(可自定义)
|
||||
- 每个项目一个子目录,git 初始化管理
|
||||
- 便于代码审查阶段做 diff 和回滚
|
||||
|
||||
## 二、质量门禁
|
||||
|
||||
> 详细规范见 `references/quality-gates.md`
|
||||
|
||||
### 门禁总览
|
||||
|
||||
| 门禁 | 位置 | 关键检查 | 通过条件 |
|
||||
|------|------|---------|---------|
|
||||
| QG1 | 需求→设计 | 产物完整性、验收标准可测试、用户确认 | 全部通过 |
|
||||
| QG2 | 设计→编码 | 文件变更清单、API与需求对齐 | 无阻断问题 |
|
||||
| QG3 | 编码→审查 | 编译通过、lint通过、变更与设计一致 | 自检全通过 |
|
||||
| QG4 | 测试→部署 | 测试全通过、覆盖率达标、无回归、用户确认 | 全部通过 |
|
||||
|
||||
### 重试与超时机制
|
||||
|
||||
**重试策略:**
|
||||
```
|
||||
第1次失败 → 同模型重试,附带失败原因
|
||||
第2次失败 → 换备用模型重试
|
||||
第3次失败 → 上报用户,进入人工介入
|
||||
```
|
||||
|
||||
**超时保护:**
|
||||
|
||||
| 阶段 | 超时时间 | 超时处理 |
|
||||
|------|---------|---------|
|
||||
| ②设计 | 30分钟 | 终止,记录已有产出,人工介入 |
|
||||
| ③编码 | 50分钟 | 终止,保留已写代码,人工介入 |
|
||||
| ④审查 | 25分钟 | 终止,标记"审查超时",直接进测试 |
|
||||
| ⑤测试 | 40分钟 | 终止,保留已有测试结果,人工介入 |
|
||||
|
||||
## 三、阶段产物规范
|
||||
|
||||
> 完整模板见 `templates/stage-artifacts.md`
|
||||
|
||||
每个阶段的子 Agent **必须**输出结构化 YAML 产物。总管负责校验产物格式。
|
||||
|
||||
### 产物设计原则
|
||||
|
||||
1. **结构化优先**:用 YAML Schema 约束,减少解析歧义
|
||||
2. **摘要辅助**:每个产物附带自然语言摘要,帮助 LLM 快速定位
|
||||
3. **决策可追溯**:记录关键决策的理由和替代方案
|
||||
4. **问题前传**:显式列出 open_issues,确保下游关注
|
||||
5. **信息隔离**:审查员不看编码 prompt,测试员不看实现思路
|
||||
|
||||
### 产物传递规则
|
||||
|
||||
```
|
||||
① 需求产物 ──→ ②架构师(完整) + ④程序员(验收标准)
|
||||
② 设计产物 ──→ ③设计师(页面/组件清单) + ④程序员(完整)+ ⑤审查员(摘要+文件清单)
|
||||
③ UI设计产物 ──→ ④程序员(完整设计规范+组件样式)
|
||||
④ 编码产物 ──→ ⑤审查员(git diff + 产物) + ⑥测试员(签名+验收标准)
|
||||
⑤ 审查产物 ──→ ④程序员(如需修改:issues列表)
|
||||
⑥ 测试产物 ──→ ④程序员(如有失败:failed_tests)/ ⑦总管(如全通过)
|
||||
```
|
||||
|
||||
**关键**:测试员只接收接口签名和验收标准,不接收编码思路和实现 prompt。
|
||||
|
||||
## 四、测试分层策略
|
||||
|
||||
> 详细规范见 `references/testing-strategy.md`
|
||||
|
||||
### 测试金字塔
|
||||
|
||||
```
|
||||
╱╲
|
||||
╱ 冒烟 ╲ ← 5-10%
|
||||
╱────────╲
|
||||
╱ 集成测试 ╲ ← 20-30%
|
||||
╱──────────────╲
|
||||
╱ 单元测试 ╲ ← 60-70%
|
||||
╱──────────────────╲
|
||||
```
|
||||
|
||||
### 覆盖率阈值
|
||||
|
||||
| 项目规模 | 行覆盖 | 分支覆盖 | 函数覆盖 |
|
||||
|---------|--------|---------|---------|
|
||||
| 大型(新项目) | ≥ 70% | ≥ 60% | ≥ 80% |
|
||||
| 中型(新功能) | ≥ 60% | ≥ 50% | ≥ 75% |
|
||||
| 小型(bug修复) | 修改行100% | — | — |
|
||||
|
||||
## 五、各阶段执行规范
|
||||
|
||||
### ① 需求分析(总管直接执行)
|
||||
|
||||
**输入**: 用户的口头/文字需求
|
||||
**输出**: 结构化需求产物(见 `templates/stage-artifacts.md` ①)
|
||||
|
||||
**规则**: 必须跟用户确认需求文档后才进入下一阶段。
|
||||
|
||||
### ② 技术方案(delegate → 架构师)
|
||||
|
||||
**输入**: 需求产物(完整) + 现有代码库上下文
|
||||
**输出**: 结构化设计产物(见 `templates/stage-artifacts.md` ②)
|
||||
|
||||
**规则**: 总管 review 方案,重大决策需跟用户确认。
|
||||
|
||||
### ③ UI设计(delegate → 设计师)— 可选
|
||||
|
||||
**适用条件**: 面向用户的项目(H5、小程序、Web应用)。纯后端/API/CLI项目跳过此阶段。
|
||||
|
||||
**输入**: 需求产物 + 技术方案中的页面/组件清单 + 用户指定的风格偏好
|
||||
**输出**: 结构化UI设计产物(见 `templates/stage-artifacts.md` ③)
|
||||
|
||||
**设计产物包含**:
|
||||
1. 设计规范(Design Token):配色、字体、间距、圆角、阴影
|
||||
2. 组件样式清单:每个组件的具体 CSS/Tailwind 类名
|
||||
3. 页面布局描述:各页面的结构和组件组合方式
|
||||
4. 交互说明:关键交互的状态变化
|
||||
|
||||
**规则**:
|
||||
- 设计师不需要画图,输出可直接使用的代码级设计规范
|
||||
- 风格偏好由用户指定(如微信风格、Material Design 等)
|
||||
- 设计规范以 Tailwind CSS 类名为主要表达方式
|
||||
|
||||
### ④ 编码实现(结对编程模式,可选)
|
||||
|
||||
**模式**: 主程序员写代码 → 结对程序员 review+改进 → 产出最终代码
|
||||
|
||||
**④a 主程序员(delegate)**
|
||||
|
||||
**输入**: 技术方案产物(完整) + UI设计产物(如有) + 需求产物中的验收标准
|
||||
**输出**: 实现代码 + 结构化编码产物(见 `templates/stage-artifacts.md` ④)
|
||||
|
||||
**④b 结对程序员(delegate)— 大型/中型项目启用**
|
||||
|
||||
**输入**: 主程序员产出的代码(git diff) + 技术方案摘要 + 验收标准
|
||||
**输出**: review 意见 + 改进后的代码
|
||||
|
||||
**结对编程规则**:
|
||||
- 大型/中型项目:必须启用结对,主程序员写完 → 结对程序员 review+改进
|
||||
- 小型/热修复:单主程序员即可,跳过④b
|
||||
- 结对程序员的改动也会进入⑤代码审查,形成三道审视(结对→审查→测试)
|
||||
- 结对程序员不重写代码,只做 review 和针对性改进
|
||||
|
||||
**规则**:
|
||||
- 一次只实现一个功能点,避免大规模变更
|
||||
- 单次 delegate 涉及文件 ≤ 10 个,超过则拆分
|
||||
- 严禁在前台运行常驻进程(如 `npm run dev`),必须后台运行
|
||||
- 完成后必须运行 `git add -A && git commit`
|
||||
|
||||
### ⑤ 代码审查(delegate → 审查员)
|
||||
|
||||
**输入**: 代码变更(git diff) + 需求摘要 + 技术方案摘要(**不含编码 prompt**)
|
||||
**输出**: 结构化审查产物(见 `templates/stage-artifacts.md` ⑤)
|
||||
|
||||
**审查维度(七维度)**:
|
||||
|
||||
| 维度 | 严重级别 | 检查重点 |
|
||||
|------|---------|---------|
|
||||
| 安全性 | 🔴 阻断 | SQL注入、XSS、硬编码密钥、路径遍历 |
|
||||
| 正确性 | 🔴 阻断 | 逻辑错误、边界条件、空值处理、资源泄露 |
|
||||
| 数据契约 | 🔴 阻断 | 前端 TS 接口与 API 实际返回字段一致性 |
|
||||
| 性能 | 🟡 警告 | 算法复杂度、N+1查询、内存泄漏 |
|
||||
| 可维护性 | 🟡 警告 | 函数长度、命名规范、DRY原则 |
|
||||
| 规范 | 🟢 建议 | 代码风格、项目约定 |
|
||||
| 测试 | 🟡 警告 | 测试覆盖、Mock合理性 |
|
||||
|
||||
**规则**: 审查不通过 → 问题反馈给程序员修改 → 修改后重新审查。最多 3 轮。
|
||||
|
||||
### ⑥ 测试验证(delegate → 测试员)
|
||||
|
||||
**输入**: 接口签名 + 验收标准(**不含实现思路和编码 prompt**)
|
||||
**输出**: 测试代码 + 结构化测试产物(见 `templates/stage-artifacts.md` ⑥)
|
||||
|
||||
**规则**: 测试不通过 → 失败信息反馈给程序员修复 → 修复后重新测试。最多 3 轮。
|
||||
|
||||
### ⑦ 部署上线(总管直接执行)
|
||||
|
||||
**输入**: 通过审查和测试的代码
|
||||
**输出**: 结构化部署产物(见 `templates/stage-artifacts.md` ⑦)
|
||||
|
||||
**规则**: 部署前跟用户确认。部署后验证核心功能。
|
||||
|
||||
## 六、设计理念与参考文献
|
||||
|
||||
### 核心设计理念
|
||||
|
||||
| 理念 | 来源 | 落地方式 |
|
||||
|------|------|---------|
|
||||
| SOP驱动+结构化输出 | MetaGPT (arXiv:2308.00352) | 7阶段YAML产物模板 |
|
||||
| 测试与编码分离 | AgentCoder (arXiv:2312.13010) | 不同模型+信息隔离 |
|
||||
| 多模型交叉审查 | 业界最佳实践 | 编码/审查/测试各用不同模型家族 |
|
||||
| Task级重试 | CrewAI | 每阶段 max 3 次+备用模型降级 |
|
||||
| 事件流快照 | OpenHands | git checkpoint 用于崩溃恢复 |
|
||||
| ACI精简工具 | SWE-agent | 子 agent 按需分配最小工具集 |
|
||||
|
||||
### 参考文献
|
||||
|
||||
1. MetaGPT — SOP驱动多角色协作,结构化中间产物减少30%信息损失
|
||||
2. AgentCoder — 独立测试Agent比自测通过率高12-18%
|
||||
3. ChatDev — 角色扮演对话提升20%完成率
|
||||
4. SWE-bench — 60%修复失败源于定位错误,强调代码搜索能力
|
||||
5. Agentless — 简单流水线有时比复杂Agent更有效
|
||||
|
||||
> 完整框架对比调研见 `references/framework-comparison.md`
|
||||
|
||||
## 七、实战陷阱与最佳实践
|
||||
|
||||
### 子 Agent 超时恢复模式
|
||||
|
||||
子 Agent 可能在执行过程中超时,但代码文件可能已经写入。超时后总管应执行以下恢复步骤:
|
||||
|
||||
1. **检查文件系统** — 查看已生成的文件
|
||||
2. **检查 git 状态** — 确认子 Agent 是否已自动 commit
|
||||
3. **验证后端** — 启动服务并测试 API 端点
|
||||
4. **验证前端** — 确认构建通过
|
||||
5. **修复关键问题** — 子 Agent 超时前可能来不及处理边界情况
|
||||
6. **提交修复** — 完成收尾
|
||||
|
||||
超时不代表失败——子 Agent 可能完成了 80% 的工作,总管收尾比重新 delegate 更高效。
|
||||
|
||||
### 前后端数据契约不一致(高频致命问题)
|
||||
|
||||
程序员 Agent 同时写 API 和前端时,TypeScript 接口定义经常与 API 实际返回字段对不上,导致客户端 JS 崩溃。
|
||||
|
||||
**防范措施**:
|
||||
1. 编码完成后,总管用 curl API 抓取实际响应
|
||||
2. 对比前端 TypeScript 接口定义与 API 实际字段
|
||||
3. 重点检查:字段名差异、字段是否存在、类型是否匹配
|
||||
4. 对可能为 undefined 的字段,检查是否有安全访问(`?.`、`|| 默认值`)
|
||||
|
||||
### 常驻进程防护规则
|
||||
|
||||
子 Agent 禁止前台运行 `npm run dev` / `next start` / `python -m http.server` 等常驻命令,必须后台运行或设超时。违反此规则会导致整个 delegation 链路卡死。
|
||||
|
||||
### 代码修改必须提交后再通知构建
|
||||
|
||||
修改代码后,必须先 `git status` 确认已 staging、`git log --oneline -1` 确认已提交,再通知用户触发 CI/CD。本地工作区的修改不会自动进入远程仓库。
|
||||
|
||||
## 八、附带参考文档
|
||||
|
||||
本技能包附带以下参考文档,按通用性分类:
|
||||
|
||||
### 通用技术参考(适用于任何项目)
|
||||
|
||||
| 文档 | 内容 |
|
||||
|------|------|
|
||||
| `references/fastapi-pitfalls.md` | FastAPI 实战陷阱(路由顺序、N+1、NULL处理等) |
|
||||
| `references/frontend-build-optimization.md` | Vite 代码分割 + 路由懒加载优化 |
|
||||
| `references/operation-log-pattern.md` | 操作日志系统四层架构模板 |
|
||||
| `references/testing-strategy.md` | 测试分层策略详情 |
|
||||
| `references/quality-gates.md` | 质量门禁详情 |
|
||||
| `references/framework-comparison.md` | AI 编码框架调研对比 |
|
||||
|
||||
### 业务模式参考(按需选用)
|
||||
|
||||
| 文档 | 适用场景 |
|
||||
|------|---------|
|
||||
| `references/excel-data-import.md` | 从 Excel 批量导入业务数据 |
|
||||
| `references/revenue-allocation-pattern.md` | 人×项目收益归属矩阵 |
|
||||
| `references/doc-sync-pattern.md` | 里程碑文档同步到仓库 |
|
||||
|
||||
## Changelog
|
||||
|
||||
| 版本 | 日期 | 变更内容 |
|
||||
|------|------|---------|
|
||||
| v1.0.0 | 2026-06-30 | 通用版首发。剥离环境特定信息(API key、服务器IP、具体项目名),保留通用流水线逻辑。适配 OpenClaw/Hermes 等多平台。 |
|
||||
@ -0,0 +1,59 @@
|
||||
# 里程碑文档同步模式(通用模板)
|
||||
|
||||
**创建日期**: 2026-06-30
|
||||
**适用场景**: MVP/里程碑完成后,需要同步代码和文档到远程仓库
|
||||
|
||||
---
|
||||
|
||||
## 触发条件
|
||||
|
||||
- 重大功能完成(如 MVP v1.0)
|
||||
- 里程碑文档创建完成
|
||||
- 需要备份/共享项目文档
|
||||
|
||||
## 同步流程
|
||||
|
||||
### 步骤 1: 代码和里程碑文档 → 远程仓库
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd <项目目录>
|
||||
|
||||
# 查看未跟踪文件
|
||||
git status
|
||||
|
||||
# 添加里程碑文档和关键代码
|
||||
git add milestone/ <其他关键文件>
|
||||
|
||||
# 提交
|
||||
git commit -m "feat: <版本> <功能描述>
|
||||
|
||||
- <成果1>
|
||||
- <成果2>"
|
||||
|
||||
# 推送到远程仓库
|
||||
git push origin <分支>
|
||||
```
|
||||
|
||||
### 步骤 2: 核心文档 → 文档库(可选)
|
||||
|
||||
如有独立的文档仓库,同步核心 .md 文档:
|
||||
|
||||
```bash
|
||||
# 进入文档库
|
||||
cd <文档库目录>
|
||||
|
||||
# 复制核心文档
|
||||
cp -r <项目目录>/milestone/ <文档库目录>/<项目名>/
|
||||
|
||||
# 提交
|
||||
git add <项目名>/
|
||||
git commit -m "feat: 添加 <项目名> <版本> 里程碑文档"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **先主仓库后文档库** — 确保代码和文档先在版本控制中
|
||||
2. **选择性同步** — 文档库只同步核心 .md 文档,不同步二进制/大文件
|
||||
3. **milestone 命名** — 格式 `YYYY-MM-DD-v<版本>-<标签>`
|
||||
@ -0,0 +1,177 @@
|
||||
# Excel 数据导入实战参考
|
||||
|
||||
> **适用场景**: MVP/里程碑完成后,从业务 Excel 报表批量导入测试数据做效果验证
|
||||
> **创建日期**: 2026-06-24
|
||||
> **更新日期**: 2026-06-30
|
||||
|
||||
---
|
||||
|
||||
## 两种导入方式
|
||||
|
||||
### 方式一:CLI 脚本(适合首次全量导入)
|
||||
|
||||
`scripts/import_excel_data.py` — 从文件系统读取 Excel,一次性导入全部数据。
|
||||
|
||||
**优点**: 无网络开销,可调试,适合大数据量
|
||||
**缺点**: 需要服务器文件系统访问权限
|
||||
|
||||
### 方式二:Web API(适合日常增量导入)
|
||||
|
||||
`POST /api/import/excel` — 前端上传 Excel 文件,后端解析导入。
|
||||
|
||||
**优点**: 浏览器操作,无需服务器登录,有导入结果统计
|
||||
**缺点**: 文件大小受 HTTP 限制,大文件上传慢
|
||||
|
||||
**前端页面**: `/import` 路径,拖拽上传 + 导入结果展示
|
||||
|
||||
**API 端点**:
|
||||
```python
|
||||
@router.post("/api/import/excel")
|
||||
async def import_excel(file: UploadFile = File(...), db: Session = Depends(get_db)):
|
||||
# 保存临时文件 → openpyxl 解析 → 导入各表 → 删除临时文件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心挑战
|
||||
|
||||
业务 Excel 通常有 15-20 个 sheet,列布局因项目而异,且存在公式、合并单元格、同名项目等陷阱。一次性全量导入比逐条录入高效百倍,但需要处理以下问题。
|
||||
|
||||
---
|
||||
|
||||
## 实战陷阱与对策
|
||||
|
||||
### 1. 列布局因 sheet 而异
|
||||
|
||||
每个项目明细 sheet 的列数不同:
|
||||
|
||||
| Sheet | 列布局 | 人日列 | 描述列 |
|
||||
|-------|--------|:------:|:------:|
|
||||
| 标准 | 序号\|模块\|功能项\|人日\|负责人\|占比\|... | col 3 | — |
|
||||
| 工健健康小屋 | 序号\|模块\|功能项\|说明1\|详细说明\|人日\|负责人\|... | col 5 | col 4 |
|
||||
| 基层社区AI医助 | 序号\|模块\|功能项\|描述\|人日\|负责人\|... | col 4 | col 3 |
|
||||
| 汤原县AI医助 | 序号\|功能项\|描述\|人日\|负责人\|... | col 3 | col 2(无模块列) |
|
||||
|
||||
**对策**: 为每个 sheet 定义独立的列映射配置,而非通用解析逻辑。
|
||||
|
||||
```python
|
||||
sheet_configs = [
|
||||
("项目-百度-珠江医院", "2026-BD-1", 1, 2, 3, 4, None, [...]),
|
||||
("项目-工健健康小屋", "2026-YZ-1", 1, 2, 5, 6, 4, [...]),
|
||||
("项目-汤原县AI医助", "2026-AI-2", None, 1, 3, 4, 2, [...]),
|
||||
]
|
||||
```
|
||||
|
||||
### 2. 同名项目(不同项目编号)
|
||||
|
||||
Excel 中可能有多个同名项目(如"南方医科大学珠江医院"有 `20260205-A` 和 `2026-BD-1` 两个编号)。用 `project_name` 做 dict key 会覆盖。
|
||||
|
||||
**对策**: 始终用 `project_code` 做映射,不要用 `project_name`。
|
||||
|
||||
```python
|
||||
projects_by_code = {p.project_code: {"name": p.project_name, "id": p.id} for p in db.query(Project).all()}
|
||||
```
|
||||
|
||||
### 3. 人员不在人员概况 sheet 中
|
||||
|
||||
新 Excel 可能新增了人员(如"陈天然"),但只出现在收益分析 sheet 中,不在人员概况 sheet 里。
|
||||
|
||||
**对策**: 扫描所有 sheet 发现新人员,手动补加到人员表。
|
||||
|
||||
### 4. 公式字段 openpyxl 读不到
|
||||
|
||||
Excel 中的 `=SUM(...)`、`=C2/D2` 等公式,openpyxl 读取时返回公式字符串而非计算结果。
|
||||
|
||||
**对策**:
|
||||
- 预期收益从收益分析 sheet 的"总计"行读取(那里是数值)
|
||||
- 投产比等计算字段在数据库端用 ROI 计算器重新计算
|
||||
|
||||
### 5. 自由文本 → 结构化关联
|
||||
|
||||
"人员项目负荷情况" sheet 的工作描述是自由文本(如"1、百度医院智能体-南方医科大学珠江医院-近一个月工作占比50%"),需要从中提取项目关联和分配比例。
|
||||
|
||||
**对策**: 两阶段匹配:
|
||||
1. 精确匹配:项目全名在文本中
|
||||
2. 关键词回退:`"健康小屋" → "工会健康小屋"`, `"AI医助" → "佳木斯中医院AI医助"`
|
||||
|
||||
分配比例从文本中 `占比(\d+)%` 正则提取,无比例时按项目关联人数均分。
|
||||
|
||||
### 6. 增量更新 vs 全量重来
|
||||
|
||||
**原则**: 首次导入用全量清空重来;后续更新用增量 upsert(按 project_code/personnel_id 匹配)。
|
||||
|
||||
**增量 upsert 模式**:
|
||||
```python
|
||||
existing = db.query(Model).filter(Model.code == code).first()
|
||||
if existing:
|
||||
for key, val in new_data.items():
|
||||
setattr(existing, key, val)
|
||||
else:
|
||||
db.add(Model(**new_data))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. openpyxl 样式解析 bug(TypeError: expected Fill)
|
||||
|
||||
openpyxl 3.1.5 在解析某些 Excel 文件的 `styles.xml` 时,遇到不兼容的 Fill 样式对象会抛出 `TypeError: expected <class 'openpyxl.styles.fills.Fill'>`。
|
||||
|
||||
**无效尝试**:
|
||||
- `data_only=True` — 只影响公式计算,不跳过样式解析
|
||||
- `read_only=True` — 同样需要解析样式表
|
||||
- 升级 openpyxl — 3.1.5 是当前最新版,bug 尚未修复
|
||||
|
||||
**根治方案**:改用 **python-calamine**(Rust 实现的 Excel 解析器),它完全不解析样式,只读数据。
|
||||
|
||||
```python
|
||||
from python_calamine import CalamineWorkbook
|
||||
|
||||
wb = CalamineWorkbook.from_path(path)
|
||||
sheets = wb.sheet_names
|
||||
ws = wb.get_sheet_by_name("Sheet1")
|
||||
rows = list(ws.to_python()) # 返回 list[list],每个 cell 是 Python 原生类型
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- `python-calamine` 返回的单元格值是 Python 原生类型(float/int/str/None),无需额外转换
|
||||
- 不解析公式,返回的是缓存的计算结果(与 `data_only=True` 类似)
|
||||
- 不支持写 Excel,只用于读取
|
||||
- 安装:`pip install python-calamine` 或加到 requirements.txt
|
||||
|
||||
**⚠️ employee_id 空值陷阱**:
|
||||
`python-calamine` 返回的空单元格是 `None`,但 Excel 中写了空字符串的单元格也会被 `str(val).strip()` 转为空字符串。用 `clean()` 函数处理时,空字符串会变成 `None`,导致 NOT NULL 约束失败。
|
||||
|
||||
```python
|
||||
# ❌ 错误:clean(row[2]) 对空单元格返回 None
|
||||
emp_id = clean(row[2]) if len(row) > 2 else f"EMP{name}"
|
||||
|
||||
# ✅ 正确:显式检查 clean() 结果是否为空
|
||||
emp_id = clean(row[2]) if len(row) > 2 and clean(row[2]) else f"EMP{name}"
|
||||
```
|
||||
|
||||
**规律**:任何 `clean()` 的返回值都可能为 `None`,不能因为列存在就假定值非空。在 NOT NULL 字段上使用 `clean()` 时,必须加 `and clean(val)` 二次检查。
|
||||
|
||||
**Web API 中的完整模式**:
|
||||
```python
|
||||
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
|
||||
try:
|
||||
content = await file.read()
|
||||
tmp.write(content)
|
||||
tmp.close()
|
||||
wb = CalamineWorkbook.from_path(tmp.name)
|
||||
# ... 解析各 sheet ...
|
||||
finally:
|
||||
os.unlink(tmp.name)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 导入后验证清单
|
||||
|
||||
- [ ] 项目数是否匹配 Excel 项目概况行数
|
||||
- [ ] 人员数是否覆盖所有出现的人名
|
||||
- [ ] 预期收益是否与 Excel 收益分析 sheet 的"总计"行一致
|
||||
- [ ] WBS 任务数是否合理(每个项目应有 >0 条)
|
||||
- [ ] 人员-项目关联数是否覆盖主要参与关系
|
||||
- [ ] 收益矩阵 API 返回的数据与 Excel 收益分析 sheet 交叉验证
|
||||
- [ ] 仪表盘 OKR 完成率是否与 Excel 部门概况一致
|
||||
@ -0,0 +1,315 @@
|
||||
# FastAPI 实战陷阱与最佳实践
|
||||
|
||||
> **适用于 dev-pipeline ④编码实现阶段。程序员(主程序员/结对程序员)在编写 FastAPI 代码时必须注意以下陷阱。
|
||||
|
||||
---
|
||||
|
||||
## 1. 路由注册顺序
|
||||
|
||||
### 问题
|
||||
FastAPI 按注册顺序匹配路由。**具体路由必须在参数化路由之前注册**,否则会被错误匹配。
|
||||
|
||||
```python
|
||||
# ❌ 错误:/{personnel_id} 先注册,/workload 被匹配为 personnel_id="workload"
|
||||
@router.get("/{personnel_id}") # 第63行
|
||||
def get_personnel(...)
|
||||
|
||||
@router.get("/{personnel_id}/workload") # 第151行 → 永远匹配不到
|
||||
def get_personnel_workload(...)
|
||||
|
||||
# ✅ 正确:具体路由先注册
|
||||
@router.get("/{personnel_id}/workload") # 先注册
|
||||
def get_personnel_workload(...)
|
||||
|
||||
@router.get("/{personnel_id}") # 后注册
|
||||
def get_personnel(...)
|
||||
```
|
||||
|
||||
### 排查方法
|
||||
当某个路由返回 404 但确信路径正确时:
|
||||
1. 检查路由注册顺序(`grep -n "@router\." file.py`)
|
||||
2. 确认具体路由(含固定路径段)在参数化路由之前
|
||||
|
||||
### 同类陷阱
|
||||
- `/milestones/all` 必须在 `/{project_id}/milestones` 之前
|
||||
- `/tasks/all` 必须在 `/{project_id}/tasks` 之前
|
||||
- `/export/projects` 必须在 `/{project_id}` 之前
|
||||
|
||||
---
|
||||
|
||||
## 2. Excel 导出中文文件名编码
|
||||
|
||||
### 问题
|
||||
`Content-Disposition: attachment; filename=中文.xlsx` 中的中文字符在 Starlette TestClient 中触发 `UnicodeEncodeError`,某些浏览器也无法正确解析。
|
||||
|
||||
### 修复
|
||||
使用 RFC 5987 编码:
|
||||
|
||||
```python
|
||||
from urllib.parse import quote
|
||||
|
||||
def _excel_response(wb, filename):
|
||||
output = io.BytesIO()
|
||||
wb.save(output)
|
||||
output.seek(0)
|
||||
encoded_filename = quote(filename)
|
||||
return StreamingResponse(
|
||||
output,
|
||||
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"},
|
||||
)
|
||||
```
|
||||
|
||||
### 测试注意事项
|
||||
- Starlette TestClient 的 `UnicodeEncodeError` 是已知问题,测试中需 try/except 捕获
|
||||
- 验证 Excel 内容时检查 `content[:2] == b"PK"`(ZIP 签名)即可
|
||||
- 用 `openpyxl.load_workbook(io.BytesIO(resp.content))` 验证内容正确性
|
||||
|
||||
---
|
||||
|
||||
## 3. 依赖声明
|
||||
|
||||
### 问题
|
||||
新增 Python 依赖(如 `openpyxl`)后忘记更新 `requirements.txt`,导致部署时崩溃。
|
||||
|
||||
### 规则
|
||||
每次④编码实现阶段新增依赖后,必须:
|
||||
1. 确认依赖已安装(`pip list | grep <pkg>`)
|
||||
2. 更新 `requirements.txt`(`pip freeze | grep <pkg> >> requirements.txt` 或手动添加)
|
||||
3. 结对程序员 review 时检查 requirements.txt 变更
|
||||
|
||||
---
|
||||
|
||||
## 4. SQLAlchemy N+1 查询
|
||||
|
||||
### 问题
|
||||
遍历 ORM 对象时访问关联属性会触发额外 SQL 查询。
|
||||
|
||||
```python
|
||||
# ❌ N+1:对每个任务查一次 project
|
||||
for task in tasks:
|
||||
project_name = task.project.project_name # 触发额外 SQL
|
||||
|
||||
# ✅ 修复:使用 joinedload 批量加载
|
||||
from sqlalchemy.orm import joinedload
|
||||
tasks = db.query(ProjectTask).options(joinedload(ProjectTask.project)).all()
|
||||
```
|
||||
|
||||
### 批量查询替代方案
|
||||
当无法使用 joinedload 时,用 `IN` 查询替代循环查询:
|
||||
|
||||
```python
|
||||
# ❌ N+1
|
||||
for task in tasks:
|
||||
progress = db.query(TaskMonthlyProgress).filter(
|
||||
TaskMonthlyProgress.task_id == task.id
|
||||
).first()
|
||||
|
||||
# ✅ 批量
|
||||
all_progress = db.query(TaskMonthlyProgress).filter(
|
||||
TaskMonthlyProgress.task_id.in_(task_ids)
|
||||
).all()
|
||||
task_latest = {p.task_id: p for p in all_progress}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. SQLAlchemy NULL 处理
|
||||
|
||||
### 问题
|
||||
SQLAlchemy 中 `column < 100` 遇到 NULL 时返回 UNKNOWN(不匹配),导致 NULL 值被遗漏。
|
||||
|
||||
```python
|
||||
# ❌ 遗漏 overall_progress IS NULL 的任务
|
||||
tasks = db.query(ProjectTask).filter(ProjectTask.overall_progress < 100).all()
|
||||
|
||||
# ✅ 正确:显式处理 NULL
|
||||
from sqlalchemy import or_
|
||||
tasks = db.query(ProjectTask).filter(
|
||||
or_(ProjectTask.overall_progress < 100, ProjectTask.overall_progress.is_(None))
|
||||
).all()
|
||||
# 或使用 | 运算符(注意括号)
|
||||
tasks = db.query(ProjectTask).filter(
|
||||
(ProjectTask.overall_progress < 100) | (ProjectTask.overall_progress.is_(None))
|
||||
).all()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 前端导出绕过认证
|
||||
|
||||
### 问题
|
||||
用 `<a>` 标签直接访问 API 路径会跳过 axios 的 `Authorization: Bearer` 拦截器,导致 401。
|
||||
|
||||
```javascript
|
||||
// ❌ 错误:绕过认证
|
||||
const link = document.createElement('a')
|
||||
link.href = '/api/reports/export/projects'
|
||||
link.click()
|
||||
|
||||
// ✅ 正确:通过 axios 获取 blob 后下载
|
||||
api.get('/reports/export/projects', { responseType: 'blob' }).then(res => {
|
||||
const url = URL.createObjectURL(new Blob([res.data]))
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = 'filename.xlsx'
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
})
|
||||
```
|
||||
|
||||
### DRY 原则
|
||||
多个导出函数应提取为通用函数:
|
||||
|
||||
```javascript
|
||||
function downloadExcel(url, filename) {
|
||||
exporting.value = true
|
||||
api.get(url, { responseType: 'blob' }).then(res => {
|
||||
const blobUrl = URL.createObjectURL(new Blob([res.data]))
|
||||
const link = document.createElement('a')
|
||||
link.href = blobUrl
|
||||
link.download = filename
|
||||
link.click()
|
||||
URL.revokeObjectURL(blobUrl)
|
||||
}).finally(() => { exporting.value = false })
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. ECharts 生命周期管理
|
||||
|
||||
### 问题
|
||||
Vue 组件销毁时未释放 ECharts 实例导致内存泄漏;路由参数变化(如 `/personnel/1` → `/personnel/2`)时图表不刷新。
|
||||
|
||||
### 修复
|
||||
```javascript
|
||||
import { onBeforeUnmount, onBeforeRouteUpdate } from 'vue-router'
|
||||
|
||||
// 销毁时释放
|
||||
onBeforeUnmount(() => {
|
||||
chart?.dispose()
|
||||
})
|
||||
|
||||
// 路由参数变化时重新加载
|
||||
onBeforeRouteUpdate(() => {
|
||||
chart?.dispose()
|
||||
loadData()
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 浮点精度
|
||||
|
||||
### 问题
|
||||
Python 浮点累加可能产生尾数误差(如 `49.9999999` 而非 `50.0`)。
|
||||
|
||||
### 修复
|
||||
```python
|
||||
# 在最终输出时统一 round
|
||||
total = round(sum(values), 2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Pydantic Schema 字段同步
|
||||
|
||||
### 问题
|
||||
后端 API 返回字典中新增了字段,但 Pydantic `response_model` schema 中没有声明,导致字段被静默丢弃。前端收到 `undefined`,页面渲染异常。
|
||||
|
||||
```python
|
||||
# ❌ 后端返回了 project_count,但 PersonnelOut schema 没有这个字段
|
||||
class PersonnelOut(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
# ... 没有 project_count → 被 Pydantic 过滤掉
|
||||
|
||||
# ✅ 必须在 schema 中声明
|
||||
class PersonnelOut(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
project_count: Optional[int] = 0
|
||||
total_allocation: Optional[float] = 0.0
|
||||
```
|
||||
|
||||
### 排查方法
|
||||
当前端收到 `undefined` 但确信后端有返回时:
|
||||
1. 去掉 `response_model` 参数,看原始返回是否包含该字段
|
||||
2. 检查 schema 定义是否包含新字段
|
||||
3. 注意 `from_attributes = True` 只影响 ORM→Pydantic 转换,不影响 dict→Pydantic 过滤
|
||||
|
||||
### 关联陷阱
|
||||
- 修改 `_build_personnel_dict` 等 helper 函数后,必须同步更新对应的 schema
|
||||
- 结对程序员 review 时重点检查:helper 返回字段 ↔ schema 字段 的一致性
|
||||
|
||||
---
|
||||
|
||||
## 11. 部署验证脚本模式
|
||||
|
||||
### 问题
|
||||
部署验证时使用 shell 管道(`curl | python3 -c`)容易触发安全防护(命令超时/阻断),尤其是涉及变量引用和 URL 编码时。
|
||||
|
||||
### 推荐方案
|
||||
用 Python 脚本替代 shell 管道,一个文件完成全部验证:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import urllib.request, json
|
||||
|
||||
BASE = "http://127.0.0.1:8001"
|
||||
|
||||
def api(method, path, data=None, token=None):
|
||||
url = BASE + path
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if token:
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
body = json.dumps(data).encode() if data else None
|
||||
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
||||
resp = urllib.request.urlopen(req, timeout=10)
|
||||
return resp.status, resp.read()
|
||||
|
||||
# 1. 登录
|
||||
status, data = api("POST", "/api/auth/login", {"username": "admin", "password": "admin123"})
|
||||
token = json.loads(data)["access_token"]
|
||||
|
||||
# 2. 验证端点
|
||||
status, data = api("GET", "/api/projects?project_type=创新项目", token=token)
|
||||
projects = json.loads(data)
|
||||
print(f"类型筛选: {len(projects)}个")
|
||||
|
||||
# 3. 验证 Excel 导出
|
||||
status, data = api("GET", "/api/reports/export/projects", token=token)
|
||||
assert data[:2] == b"PK" # ZIP 签名
|
||||
import openpyxl, io
|
||||
wb = openpyxl.load_workbook(io.BytesIO(data))
|
||||
print(f"导出: {wb.active.title}, {wb.active.max_row-1}行")
|
||||
```
|
||||
|
||||
### 优势
|
||||
- 避免 shell 变量注入和管道超时
|
||||
- 可在虚拟环境中直接运行
|
||||
- 断言清晰,失败时立即知道哪个端点出问题
|
||||
- 可复用(保存为 `scripts/verify_deploy.py`)
|
||||
|
||||
---
|
||||
|
||||
## 9. 时区一致性
|
||||
|
||||
### 问题
|
||||
项目中混用 timezone-naive 和 timezone-aware 的 datetime 导致比较偏差。
|
||||
|
||||
### 规范
|
||||
```python
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# 统一使用 UTC aware datetime
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# 解析字符串时也设为 aware
|
||||
from calendar import monthrange
|
||||
parts = month_str.split("-")
|
||||
y, m = int(parts[0]), int(parts[1])
|
||||
last_day = monthrange(y, m)[1]
|
||||
progress_date = datetime(y, m, last_day, tzinfo=timezone.utc)
|
||||
```
|
||||
@ -0,0 +1,44 @@
|
||||
# AI 编码框架多代理协作调研(2026-04-19)
|
||||
|
||||
> 本文档记录了 v2.0 设计决策的调研依据。用于未来架构演进参考。
|
||||
|
||||
## 三种多Agent协作范式
|
||||
|
||||
| 范式 | 代表框架 | 特点 | 我们的借鉴 |
|
||||
|------|---------|------|-----------|
|
||||
| **SOP驱动** | MetaGPT | 模拟软件公司,角色固定,结构化输出 | → 6阶段YAML产物模板 |
|
||||
| **任务编排** | CrewAI | Agent/Task/Crew抽象,sequential/hierarchical | → delegate_task per-task模型路由 |
|
||||
| **群聊协商** | AutoGen | GroupChat多Agent讨论,Manager分配 | 未采用(开销大) |
|
||||
|
||||
## 框架与Hermes集成潜力排序
|
||||
|
||||
| 优先级 | 框架 | 集成方式 | 适合场景 |
|
||||
|--------|------|---------|---------|
|
||||
| 🥇 | **Aider** | 作为编码执行引擎 | git深度集成、repo map、双模型模式 |
|
||||
| 🥈 | **CrewAI** | 借鉴编排模式 | Agent/Task/Crew抽象 |
|
||||
| 🥉 | **MetaGPT** | 借鉴SOP+角色设计 | 结构化产物、发布订阅通信 |
|
||||
| 4 | **OpenHands** | 借鉴事件流+浏览器能力 | 全栈开发(前端+后端) |
|
||||
| 5 | **Claude Code** | 借鉴subagent+worktree并行 | 大任务并行编码 |
|
||||
|
||||
## 关键学术发现(影响v2.0设计)
|
||||
|
||||
| 论文 | 发现 | 落地到v2.0 |
|
||||
|------|------|-----------|
|
||||
| MetaGPT (2308.00352) | 结构化输出减少30%信息损失 | templates/stage-artifacts.md |
|
||||
| AgentCoder (2312.13010) | 独立测试Agent比自测通过率高12-18% | 测试员信息隔离原则 |
|
||||
| ChatDev (2307.07924) | 角色对话提升20%完成率,需设轮次上限 | 每阶段max 3轮重试 |
|
||||
| SWE-bench | 60%失败源于定位错误 | 强调代码搜索能力(未来P2) |
|
||||
| Agentless | 简单流水线有时比复杂Agent更好 | 小型需求总管直接做,不过度编排 |
|
||||
|
||||
## 我们选择"不换框架"的理由
|
||||
|
||||
1. Hermes 的 `delegate_task` + custom_providers 已是成熟的多Agent编排
|
||||
2. 已有5角色6阶段,与MetaGPT的SOP模式架构等价
|
||||
3. 完全开源、模型灵活(火山方舟9个模型可切换)、可控性最高
|
||||
4. 不引入额外框架依赖,降低维护成本
|
||||
|
||||
## 未来演进方向(P2+)
|
||||
|
||||
- **短期**: 集成 Aider 的 repo map(tree-sitter AST索引)提升代码定位能力
|
||||
- **中期**: 借鉴 Claude Code worktree 并行模式,大任务拆分后多分支并行编码
|
||||
- **长期**: 代码库向量索引(RAG),经验池积累成功案例
|
||||
@ -0,0 +1,94 @@
|
||||
# 前端构建优化(Vite 代码分割 + 路由懒加载)
|
||||
|
||||
> 适用于 Vue 3 + Vite 项目,Element Plus + ECharts 技术栈
|
||||
|
||||
## 问题
|
||||
|
||||
Vue 3 + Element Plus + ECharts 项目构建后,默认所有代码打包成一个 JS 文件(通常 2~3MB),首屏加载全量代码,严重影响首次加载速度。
|
||||
|
||||
## 优化方案
|
||||
|
||||
### 1. Vite `manualChunks` 拆包
|
||||
|
||||
在 `vite.config.js` 中配置 `build.rollupOptions.output.manualChunks`,将大体积第三方库拆为独立 chunk:
|
||||
|
||||
```js
|
||||
// vite.config.js
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
// echarts 单独拆包(~1MB,仅访问图表页面时加载)
|
||||
if (id.includes('node_modules/echarts')) {
|
||||
return 'echarts'
|
||||
}
|
||||
// element-plus 单独拆包(~1MB,浏览器缓存)
|
||||
if (id.includes('node_modules/element-plus')) {
|
||||
return 'element-plus'
|
||||
}
|
||||
// vue 核心库单独拆包(~120KB)
|
||||
if (id.includes('node_modules/vue') || id.includes('node_modules/@vue')) {
|
||||
return 'vue-core'
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
chunkSizeWarningLimit: 500, // 提高告警阈值,避免拆包后仍有大 chunk 告警
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
**效果**:
|
||||
| Chunk | 大小 | 加载时机 |
|
||||
|:------|:----:|:--------|
|
||||
| `vue-core` | ~120KB | 首屏 |
|
||||
| `element-plus` | ~1MB | 首屏(浏览器缓存后不重复下载) |
|
||||
| `echarts` | ~1MB | 仅访问图表页面时按需加载 |
|
||||
| 各页面组件 | 2~15KB | 按需加载 |
|
||||
|
||||
### 2. 路由懒加载(动态 import)
|
||||
|
||||
将 `router/index.js` 中的静态 import 改为动态 `() => import(...)`:
|
||||
|
||||
```js
|
||||
// ❌ 静态 import(全量加载)
|
||||
import Dashboard from '../views/dashboard/Index.vue'
|
||||
|
||||
// ✅ 动态 import(按需加载)
|
||||
const routes = [
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: () => import('../views/dashboard/Index.vue'),
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
Vite 会自动为每个动态 import 生成独立 chunk,文件名基于组件名(如 `Index-abc123.js`)。
|
||||
|
||||
### 3. 验证优化效果
|
||||
|
||||
```bash
|
||||
# 构建前
|
||||
ls -lh dist/assets/
|
||||
# 输出:index-DoEo0YCm.js 2.3M ← 单文件
|
||||
|
||||
# 配置后构建
|
||||
npm run build
|
||||
ls -lh dist/assets/
|
||||
# 输出:
|
||||
# vue-core-BuIKajZo.js 119KB
|
||||
# element-plus-C03w9GoR.js 1.0MB
|
||||
# echarts-Bb6yjXMn.js 1.0MB
|
||||
# Index-xxx.js 2~15KB(每个页面)
|
||||
# ...
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **manualChunks 函数**接收的是模块 ID(绝对路径),用 `id.includes()` 匹配 `node_modules` 中的包名
|
||||
2. **路由懒加载**只在用户访问对应路由时才加载 chunk,首次访问某页面会有短暂加载延迟(通常 < 200ms)
|
||||
3. **echarts 拆包后**,只有访问包含 ECharts 图表的页面(仪表盘、经营数据等)才会加载 echarts chunk
|
||||
4. **chunkSizeWarningLimit** 建议设为 500KB,避免拆包后 element-plus/echarts 仍触发告警(它们确实很大)
|
||||
5. **浏览器缓存**:element-plus 和 vue-core 拆为独立 chunk 后,只要版本不变,浏览器会缓存,后续页面访问不再下载
|
||||
@ -0,0 +1,212 @@
|
||||
# 操作日志系统(审计追踪模式)
|
||||
|
||||
> **适用场景**: 任何需要审计追踪的 CRUD 管理系统
|
||||
> **创建日期**: 2026-06-29
|
||||
|
||||
---
|
||||
|
||||
## 四层架构
|
||||
|
||||
| 层次 | 文件 | 职责 |
|
||||
|:----|:-----|:-----|
|
||||
| 模型 | `models/operation_log.py` | 定义数据库表结构 |
|
||||
| 服务 | `services/operation_log.py` | 工具函数 `log_operation()` |
|
||||
| API | `routers/logs.py` | `GET /api/logs` 查询接口 |
|
||||
| 埋点 | 各 CRUD 路由中 | 在 create/update/delete 后调用 |
|
||||
|
||||
---
|
||||
|
||||
## 模型定义
|
||||
|
||||
```python
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Text
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class OperationLog(Base):
|
||||
__tablename__ = "operation_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user = Column(String(64), default="admin", comment="操作人")
|
||||
action = Column(String(32), nullable=False, comment="操作类型: create/update/delete")
|
||||
entity_type = Column(String(32), nullable=False, comment="实体类型")
|
||||
entity_id = Column(Integer, nullable=True, comment="实体ID")
|
||||
entity_name = Column(String(128), nullable=True, comment="实体名称(冗余,方便展示)")
|
||||
detail = Column(Text, nullable=True, comment="操作详情")
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), comment="操作时间")
|
||||
```
|
||||
|
||||
## 服务层
|
||||
|
||||
```python
|
||||
from sqlalchemy.orm import Session
|
||||
from app.models.operation_log import OperationLog
|
||||
|
||||
|
||||
def log_operation(
|
||||
db: Session,
|
||||
action: str,
|
||||
entity_type: str,
|
||||
entity_id: int = None,
|
||||
entity_name: str = None,
|
||||
detail: str = None,
|
||||
user: str = "admin",
|
||||
):
|
||||
log = OperationLog(
|
||||
user=user,
|
||||
action=action,
|
||||
entity_type=entity_type,
|
||||
entity_id=entity_id,
|
||||
entity_name=entity_name,
|
||||
detail=detail,
|
||||
)
|
||||
db.add(log)
|
||||
db.commit()
|
||||
```
|
||||
|
||||
## API 路由
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from app.database import get_db
|
||||
from app.models.operation_log import OperationLog
|
||||
from typing import Optional
|
||||
|
||||
router = APIRouter(prefix="/api/logs", tags=["操作日志"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
def list_logs(
|
||||
entity_type: Optional[str] = Query(None),
|
||||
action: Optional[str] = Query(None),
|
||||
limit: int = Query(100, ge=1, le=500),
|
||||
offset: int = Query(0, ge=0),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
q = db.query(OperationLog).order_by(OperationLog.created_at.desc())
|
||||
if entity_type:
|
||||
q = q.filter(OperationLog.entity_type == entity_type)
|
||||
if action:
|
||||
q = q.filter(OperationLog.action == action)
|
||||
|
||||
total = q.count()
|
||||
logs = q.offset(offset).limit(limit).all()
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"logs": [
|
||||
{
|
||||
"id": log.id,
|
||||
"user": log.user,
|
||||
"action": log.action,
|
||||
"entity_type": log.entity_type,
|
||||
"entity_id": log.entity_id,
|
||||
"entity_name": log.entity_name,
|
||||
"detail": log.detail,
|
||||
"created_at": log.created_at.isoformat() if log.created_at else None,
|
||||
}
|
||||
for log in logs
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## 埋点注入模式
|
||||
|
||||
### 创建后
|
||||
|
||||
```python
|
||||
log_operation(db, "create", "project", p.id, p.project_name, f"创建项目: {p.project_code}")
|
||||
```
|
||||
|
||||
### 更新后
|
||||
|
||||
```python
|
||||
log_operation(db, "update", "project", p.id, p.project_name, f"更新项目: {p.project_code}")
|
||||
```
|
||||
|
||||
### 删除前(需要 entity_name 做记录)
|
||||
|
||||
```python
|
||||
log_operation(db, "delete", "project", project_id, p.project_name if p else None, f"删除项目: ID={project_id}")
|
||||
```
|
||||
|
||||
### 批量导入
|
||||
|
||||
```python
|
||||
log_operation(db, "create", "import", None, filename, f"Excel导入: {stats}")
|
||||
```
|
||||
|
||||
## 注册路由
|
||||
|
||||
在 `main.py` 中:
|
||||
|
||||
```python
|
||||
from app.routers import auth, groups, personnel, projects, settings, dashboard, reports, search, finance, logs
|
||||
|
||||
# 受保护路由
|
||||
app.include_router(logs.router, dependencies=[Depends(verify_token)])
|
||||
```
|
||||
|
||||
## 前端页面
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-card>
|
||||
<template #header>
|
||||
<span>📝 操作日志</span>
|
||||
<!-- 筛选:实体类型 + 操作类型 -->
|
||||
</template>
|
||||
<el-table :data="logs" stripe>
|
||||
<el-table-column prop="created_at" label="时间" />
|
||||
<el-table-column prop="user" label="操作人" />
|
||||
<el-table-column label="操作">
|
||||
<el-tag :type="actionTagType(row.action)" size="small">
|
||||
{{ row.action === 'create' ? '创建' : row.action === 'update' ? '更新' : '删除' }}
|
||||
</el-tag>
|
||||
</el-table-column>
|
||||
<el-table-column label="实体" />
|
||||
<el-table-column prop="entity_name" label="实体名称" />
|
||||
<el-table-column prop="detail" label="操作详情" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 路由注册 + 侧边栏
|
||||
|
||||
```javascript
|
||||
// router/index.js
|
||||
{
|
||||
path: 'logs',
|
||||
name: 'Logs',
|
||||
component: () => import('../views/logs/Index.vue'),
|
||||
}
|
||||
|
||||
// Layout.vue 侧边栏
|
||||
<el-menu-item index="/logs">
|
||||
<el-icon><Tickets /></el-icon>
|
||||
<span>操作日志</span>
|
||||
</el-menu-item>
|
||||
```
|
||||
|
||||
## 适用实体类型
|
||||
|
||||
| entity_type | 含义 | 埋点位置 |
|
||||
|:------------|:-----|:---------|
|
||||
| project | 项目 | projects.py CRUD |
|
||||
| task | WBS 任务 | projects.py task CRUD |
|
||||
| milestone | 里程碑 | projects.py milestone CRUD |
|
||||
| income | 月度收益 | projects.py income CRUD |
|
||||
| personnel | 人员 | personnel.py CRUD |
|
||||
| group | 组别 | groups.py CRUD |
|
||||
| setting | 系统设置 | settings.py CRUD |
|
||||
| import | 数据导入 | import_data.py |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **删除操作需在 delete 前记录**:`db.delete()` 后对象属性仍可访问,但 `db.commit()` 后不可用
|
||||
2. **SQLAlchemy Column 类型警告**:Pyright 会报 `Column[int]` 不能赋值给 `int` 的参数类型错误,这是已知假阳性,不影响运行
|
||||
3. **不要过度埋点**:GET 查询操作不需要记录,只记录 create/update/delete
|
||||
4. **entity_name 冗余存储**:即使关联实体被删除,日志中仍保留名称用于展示
|
||||
@ -0,0 +1,109 @@
|
||||
# 研发流水线 — 质量门禁规范
|
||||
# 每个阶段完成后,由马总管自动执行质量门禁检查
|
||||
|
||||
---
|
||||
|
||||
## 质量门禁总览
|
||||
|
||||
```
|
||||
需求 ──→ [QG1] ──→ 设计 ──→ [QG2] ──→ 编码 ──→ [QG3] ──→ 测试 ──→ [QG4] ──→ 部署
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## QG1: 需求分析门禁
|
||||
|
||||
**执行时机:** ①需求分析完成后,进入②设计之前
|
||||
|
||||
| 检查项 | 通过标准 | 执行方式 |
|
||||
|--------|---------|---------|
|
||||
| 产物格式完整 | YAML所有必填字段非空 | 自动 |
|
||||
| 验收标准可测试 | 每条AC标记testable=true | 自动 |
|
||||
| 范围边界清晰 | in_scope和out_of_scope均非空 | 自动 |
|
||||
| 用户确认 | 乔确认需求理解正确 | 人工 |
|
||||
|
||||
**不通过处理:** 补充缺失信息,重新跟乔确认
|
||||
|
||||
---
|
||||
|
||||
## QG2: 技术方案门禁
|
||||
|
||||
**执行时机:** ②设计完成后,进入③编码之前
|
||||
|
||||
| 检查项 | 通过标准 | 执行方式 |
|
||||
|--------|---------|---------|
|
||||
| 产物格式完整 | YAML所有必填字段非空 | 自动 |
|
||||
| 文件变更清单 | file_changes列表非空 | 自动 |
|
||||
| API设计与需求对齐 | 每个AC至少对应一个API或功能点 | AI审查 |
|
||||
| 无遗留阻断问题 | open_issues中无阻断级问题 | 自动 |
|
||||
| 重大决策确认 | 涉及架构变更时需乔确认 | 条件人工 |
|
||||
|
||||
**不通过处理:** 反馈问题给架构师Agent重新设计(最多2轮)
|
||||
|
||||
---
|
||||
|
||||
## QG3: 编码实现门禁
|
||||
|
||||
**执行时机:** ③编码完成后,进入④审查之前
|
||||
|
||||
| 检查项 | 通过标准 | 执行方式 |
|
||||
|--------|---------|---------|
|
||||
| 产物格式完整 | YAML所有必填字段非空 | 自动 |
|
||||
| 代码可编译/无语法错误 | self_check.compiles=true | 自动 |
|
||||
| lint通过 | self_check.lint_passes=true | 自动 |
|
||||
| 文件变更与设计一致 | 实际变更文件 ⊆ 设计中的file_changes | AI审查 |
|
||||
| 无常驻进程残留 | 无未关闭的后台服务 | 自动 |
|
||||
|
||||
**不通过处理:** 反馈给程序员Agent修复(最多3轮)
|
||||
|
||||
---
|
||||
|
||||
## QG4: 测试验证门禁
|
||||
|
||||
**执行时机:** ⑤测试完成后,进入⑥部署之前
|
||||
|
||||
| 检查项 | 通过标准 | 执行方式 |
|
||||
|--------|---------|---------|
|
||||
| 所有测试通过 | verdict=ALL_PASS | 自动 |
|
||||
| 覆盖率达标 | line_percent >= 阈值(见测试规范) | 自动 |
|
||||
| 无回归 | existing_tests_still_pass=true | 自动 |
|
||||
| 冒烟测试通过 | smoke.failed=0 | 自动 |
|
||||
| 部署确认 | 乔确认可以部署 | 人工 |
|
||||
|
||||
**不通过处理:** 失败信息反馈给程序员Agent修复 → 重新测试(最多3轮)
|
||||
|
||||
---
|
||||
|
||||
## 门禁执行规则
|
||||
|
||||
### 重试策略
|
||||
```
|
||||
第1次失败 → 同模型重试,附带失败原因
|
||||
第2次失败 → 换备用模型重试
|
||||
第3次失败 → 上报乔,进入人工介入
|
||||
```
|
||||
|
||||
### 备用模型降级链(示例 — 根据实际可用模型替换)
|
||||
|
||||
降级原则:主模型失败时,按以下优先级替换:
|
||||
1. 同平台其他模型(相同 API 端点)
|
||||
2. 不同平台同等级模型
|
||||
3. 快速响应模型兜底
|
||||
|
||||
| 角色 | 降级策略 |
|
||||
|------|---------|
|
||||
| 架构师 | 强推理模型 → 同平台次强模型 → 通用模型 |
|
||||
| 设计师 | 编码版模型 → 通用强模型 → 快速模型 |
|
||||
| 主程序员 | 深度编码模型 → 其他编码模型 → 快速模型 |
|
||||
| 结对程序员 | 与主程序员不同家族模型 → 主程序员模型 |
|
||||
| 审查员 | 深度推理模型 → 架构师模型 |
|
||||
| 测试员 | Agent能力强的模型 → 编码模型 |
|
||||
|
||||
### 超时保护
|
||||
| 阶段 | 超时时间 | 超时处理 |
|
||||
|------|---------|---------|
|
||||
| ②设计 | 30分钟 | 终止,记录已有产出,人工介入 |
|
||||
| ③UI设计 | 10分钟 | 换模型重试,精简prompt |
|
||||
| ④编码 | 50分钟 | 终止,保留已写代码,人工介入 |
|
||||
| ⑤审查 | 25分钟 | 终止,标记"审查超时",进入测试 |
|
||||
| ⑥测试 | 40分钟 | 终止,保留已有测试结果,人工介入 |
|
||||
@ -0,0 +1,113 @@
|
||||
# 收益归属矩阵(业务报表模式)
|
||||
|
||||
> **适用场景**: 需要按人×项目维度展示收益归属矩阵
|
||||
> **创建日期**: 2026-06-24
|
||||
|
||||
---
|
||||
|
||||
## 业务含义
|
||||
|
||||
行 = 人员,列 = 项目,交叉单元格 = 该人在该项目的收益贡献。类似 Excel 透视表。
|
||||
|
||||
```
|
||||
项目A 项目B 项目C 小计
|
||||
郑明义 ¥14,983 ¥4,142 ¥1,839 ¥20,964
|
||||
薛文 ¥11,600 ¥884 ¥3,678 ¥16,162
|
||||
总计 ¥82,031 ¥7,070 ¥9,196 ¥98,297
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 计算逻辑
|
||||
|
||||
### 数据源
|
||||
|
||||
- `project_monthly_income` — 项目月度总收益(按项目×月份)
|
||||
- `personnel_projects` — 人员-项目关联(含 `allocation_ratio` 分配比例)
|
||||
|
||||
### 核心公式
|
||||
|
||||
```
|
||||
个人在项目的收益 = 项目总收益 × (该人的分配比例 / 100)
|
||||
```
|
||||
|
||||
### 特殊情况
|
||||
|
||||
| 情况 | 处理方式 |
|
||||
|------|---------|
|
||||
| 人员有关联比例 | 按比例分摊项目总收益 |
|
||||
| 人员无关联比例 | 收益为 0(不显示在矩阵中) |
|
||||
| 项目无收益记录 | 收益为 0(不显示在矩阵中) |
|
||||
| 月份筛选 | 只计算该月的收益记录 |
|
||||
|
||||
### API 设计
|
||||
|
||||
```
|
||||
GET /api/reports/income-matrix?month=2026-06
|
||||
|
||||
Response:
|
||||
{
|
||||
"rows": [
|
||||
{
|
||||
"name": "郑明义",
|
||||
"group_name": "四组",
|
||||
"projects": {"南方医科大学珠江医院": 9666.67, "苏州市立医院": 1325.58},
|
||||
"subtotal": 12831.38
|
||||
}
|
||||
],
|
||||
"project_totals": {"南方医科大学珠江医院": 82031.34, ...},
|
||||
"grand_total": 442598.68,
|
||||
"month": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 前端实现要点
|
||||
|
||||
### 动态列
|
||||
|
||||
项目列是动态的(随数据变化),需要用 `v-for` 在 `<el-table-column>` 上渲染:
|
||||
|
||||
```vue
|
||||
<el-table-column v-for="proj in projectNames" :key="proj" :label="proj" min-width="140" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ row.projects[proj] > 0 ? '¥' + row.projects[proj].toLocaleString() : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
```
|
||||
|
||||
### 总计行
|
||||
|
||||
用 `<el-descriptions>` 展示项目总计,放在表格下方:
|
||||
|
||||
```vue
|
||||
<el-descriptions :column="4" border size="small">
|
||||
<el-descriptions-item label="总计">
|
||||
¥{{ grandTotal.toLocaleString() }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-for="(val, key) in projectTotals" :key="key" :label="key">
|
||||
¥{{ val.toLocaleString() }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
```
|
||||
|
||||
### 月份筛选
|
||||
|
||||
提供月份下拉框,不传参时汇总所有月份。筛选在 API 层完成,前端只传参数。
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 为什么矩阵中的项目总收益不等于项目列表中的预期收益?
|
||||
|
||||
A: 矩阵中的收益来自 `project_monthly_income`(实际实现的月度收益),不等于项目的预期收益(合同金额)。两者是不同概念。
|
||||
|
||||
### Q: 为什么某些人没有出现在矩阵中?
|
||||
|
||||
A: 只有 `subtotal > 0` 的行才会显示。如果人员有关联项目但项目没有收益记录,该人员不会出现。
|
||||
|
||||
### Q: 分配比例从哪里来?
|
||||
|
||||
A: 从 `personnel_projects.allocation_ratio` 字段读取。初始导入时从 Excel 工作描述中的"占比 N%"正则提取,无比例时按项目关联人数均分。
|
||||
@ -0,0 +1,41 @@
|
||||
# 研发流水线 — 角色模型配置示例
|
||||
#
|
||||
# 使用前请复制为 roles.yaml,修改模型名称为您实际可用的模型。
|
||||
# 每个角色使用不同模型家族可获得更好的交叉审视效果。
|
||||
|
||||
roles:
|
||||
- name: "架构师"
|
||||
code: "ARCH"
|
||||
emoji: "🏗️"
|
||||
stages: ["②技术方案"]
|
||||
model: "<强推理模型,如 glm-5.2 / claude-sonnet-4 / deepseek-v4-pro>"
|
||||
|
||||
- name: "设计师"
|
||||
code: "UI"
|
||||
emoji: "🎨"
|
||||
stages: ["③UI设计"]
|
||||
model: "<编码版模型,如 kimi-k2.7-code / claude-sonnet-4>"
|
||||
|
||||
- name: "主程序员"
|
||||
code: "DEV"
|
||||
emoji: "💻"
|
||||
stages: ["④编码实现"]
|
||||
model: "<深度编码模型,如 deepseek-v4-pro / claude-sonnet-4>"
|
||||
|
||||
- name: "结对程序员"
|
||||
code: "DEV2"
|
||||
emoji: "👥"
|
||||
stages: ["④编码实现(review)"]
|
||||
model: "<与主程序员不同家族,如 kimi-k2.7-code / gpt-4o>"
|
||||
|
||||
- name: "审查员"
|
||||
code: "REVIEW"
|
||||
emoji: "🔍"
|
||||
stages: ["⑤代码审查"]
|
||||
model: "<深度推理模型,如 deepseek-v4-pro / claude-sonnet-4>"
|
||||
|
||||
- name: "测试员"
|
||||
code: "QA"
|
||||
emoji: "🧪"
|
||||
stages: ["⑥测试验证"]
|
||||
model: "<Agent能力强的模型,如 minimax-m3 / claude-sonnet-4>"
|
||||
@ -0,0 +1,125 @@
|
||||
# 研发流水线 — 测试分层策略
|
||||
|
||||
---
|
||||
|
||||
## 测试金字塔
|
||||
|
||||
```
|
||||
╱╲
|
||||
╱ 冒烟 ╲ ← 5-10% (部署后关键路径验证)
|
||||
╱────────╲
|
||||
╱ 集成测试 ╲ ← 20-30% (API/服务间交互)
|
||||
╱──────────────╲
|
||||
╱ 单元测试 ╲ ← 60-70% (函数/类级别)
|
||||
╱──────────────────╲
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 各层测试规范
|
||||
|
||||
### 单元测试
|
||||
|
||||
**目标:** 验证单个函数/类的行为正确性
|
||||
**覆盖率要求:** 行覆盖 ≥ 70%,函数覆盖 ≥ 80%
|
||||
|
||||
**测试Agent生成规则:**
|
||||
1. 只看接口签名+文档+验收标准,不看实现细节(黑盒测试优先)
|
||||
2. 必须覆盖:正常路径、边界值(空/null/最大/最小)、异常路径
|
||||
3. 每个测试函数只验证一个行为点
|
||||
4. 测试命名格式:`test_<功能>_<场景>_<预期结果>`
|
||||
|
||||
### 集成测试
|
||||
|
||||
**目标:** 验证模块间/服务间交互正确性
|
||||
**覆盖率要求:** 所有API接口100%覆盖
|
||||
|
||||
**测试Agent生成规则:**
|
||||
1. 每个API端点至少:1个成功用例 + 1个参数校验失败 + 1个权限失败
|
||||
2. 测试数据库/外部服务交互的完整流程
|
||||
3. 使用真实的数据库(SQLite内存模式)或合理的Mock
|
||||
|
||||
### 冒烟测试
|
||||
|
||||
**目标:** 部署后快速验证核心功能可用
|
||||
**覆盖率要求:** 覆盖所有核心用户路径
|
||||
|
||||
**测试Agent生成规则:**
|
||||
1. 从验收标准(AC)中提取关键路径
|
||||
2. 只验证"能不能用",不验证边界条件
|
||||
3. 执行时间 < 30秒
|
||||
4. 对于前端项目:启动服务 → 访问关键页面 → 验证无崩溃
|
||||
|
||||
---
|
||||
|
||||
## 测试质量保障
|
||||
|
||||
### 独立性原则(参考AgentCoder论文 arXiv:2312.13010)
|
||||
- 测试Agent(minimax-m3)与编码Agent(deepseek-v4-pro)使用不同模型
|
||||
- 测试Agent的prompt中**不包含**编码Agent的prompt和实现思路
|
||||
- 测试Agent只接收:接口文档 + 验收标准 + 代码文件(只看签名,不解释逻辑)
|
||||
|
||||
### 测试Agent的Context模板
|
||||
```
|
||||
你是一个QA测试工程师。根据以下信息编写测试:
|
||||
|
||||
## 需求验收标准
|
||||
{acceptance_criteria}
|
||||
|
||||
## 接口/函数签名
|
||||
{api_signatures}
|
||||
|
||||
## 项目测试约定
|
||||
- 框架: {test_framework}
|
||||
- 运行命令: {test_command}
|
||||
- 测试目录: {test_directory}
|
||||
|
||||
## 规则
|
||||
1. 优先编写能发现bug的测试(边界值、异常路径)
|
||||
2. 不要写"同义反复"测试(只是重复实现逻辑)
|
||||
3. 每个测试必须有明确的断言
|
||||
4. 测试之间相互独立,无执行顺序依赖
|
||||
5. 编写完成后运行全部测试并输出结果
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 覆盖率阈值
|
||||
|
||||
| 项目规模 | 行覆盖 | 分支覆盖 | 函数覆盖 |
|
||||
|---------|--------|---------|---------|
|
||||
| 大型(新项目) | ≥ 70% | ≥ 60% | ≥ 80% |
|
||||
| 中型(新功能) | ≥ 60% | ≥ 50% | ≥ 75% |
|
||||
| 小型(bug修复) | 修改行100%覆盖 | — | — |
|
||||
|
||||
---
|
||||
|
||||
## 前端项目特殊规则
|
||||
|
||||
前端项目(React/Vue/Next.js等)的测试策略有所不同:
|
||||
|
||||
| 层级 | 内容 | 工具建议 |
|
||||
|------|------|---------|
|
||||
| 单元 | 组件渲染测试、工具函数测试 | Jest + Testing Library |
|
||||
| 集成 | 页面级交互测试 | Playwright / Cypress |
|
||||
| 冒烟 | 构建+启动+关键页面可达+无JS错误 | curl + Playwright |
|
||||
|
||||
**前端冒烟测试最低要求:**
|
||||
1. `npm run build` 成功(无编译错误)
|
||||
2. 构建产物包含所有预期路由(检查 `.next/routes-manifest.json`)
|
||||
3. 关键页面路由可访问(无404)
|
||||
4. 浏览器控制台无未捕获错误
|
||||
|
||||
**⚠️ 陷阱:不要在vitest中spawn服务器**
|
||||
在测试中用 `spawn('npm', ['run', 'start'])` 启动Next.js再fetch会超时(Next.js启动慢)且进程清理不干净。替代方案:解析build产物中的 `routes-manifest.json`,验证静态路由和动态路由是否完整。示例:
|
||||
```typescript
|
||||
it('should have all expected routes after build', () => {
|
||||
const routesManifest = JSON.parse(
|
||||
fs.readFileSync('.next/routes-manifest.json', 'utf-8')
|
||||
)
|
||||
const dynamicRoutes = routesManifest.dynamicRoutes?.map(r => r.page) || []
|
||||
const staticRoutes = routesManifest.staticRoutes?.map(r => r.page) || []
|
||||
expect(staticRoutes).toContain('/')
|
||||
expect(dynamicRoutes).toContain('/departments/[id]')
|
||||
})
|
||||
```
|
||||
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""研发流水线 — 角色模型对照表生成脚本(通用模板)
|
||||
|
||||
读取自定义的 roles.yaml 配置文件,输出角色模型对照表。
|
||||
用户需根据实际环境修改 references/roles.yaml 中的模型配置。
|
||||
|
||||
用法: python scripts/show_roles.py
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_roles():
|
||||
"""加载 roles.yaml"""
|
||||
script_dir = Path(__file__).parent.parent / "references"
|
||||
roles_path = script_dir / "roles.yaml"
|
||||
if not roles_path.exists():
|
||||
print(f"❌ 未找到 roles.yaml: {roles_path}")
|
||||
print("请复制 references/roles.example.yaml 为 references/roles.yaml 并修改配置")
|
||||
sys.exit(1)
|
||||
with open(roles_path) as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def main():
|
||||
roles_config = load_roles()
|
||||
roles = roles_config.get("roles", [])
|
||||
|
||||
print("=" * 80)
|
||||
print("研发流水线 — 角色模型对照表")
|
||||
print("=" * 80)
|
||||
|
||||
print("\n## 当前角色分配\n")
|
||||
print(f"{'角色':<12} {'代号':<8} {'阶段':<20} {'模型':<25}")
|
||||
print("-" * 70)
|
||||
for role in roles:
|
||||
stages = ", ".join(role.get("stages", []))
|
||||
print(f"{role.get('emoji', '')} {role['name']:<10} {role['code']:<8} {stages:<20} {role['model']:<25}")
|
||||
|
||||
# 一致性检查
|
||||
print(f"\n## 一致性检查\n")
|
||||
print("✅ 请手动检查 roles.yaml 中的模型配置是否与您的 API 平台一致。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,334 @@
|
||||
# 研发流水线 — 阶段产物模板
|
||||
# 每个阶段的子Agent必须按此格式输出产物
|
||||
# 总管(小研)负责校验产物格式完整性
|
||||
|
||||
---
|
||||
|
||||
## ① 需求分析产物
|
||||
|
||||
```yaml
|
||||
stage: requirements
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
# 核心产物
|
||||
artifacts:
|
||||
goal: "一句话描述功能目标"
|
||||
user_stories:
|
||||
- as: "角色"
|
||||
want: "功能"
|
||||
so_that: "价值"
|
||||
acceptance_criteria:
|
||||
- id: AC-1
|
||||
description: "验收条件描述"
|
||||
testable: true
|
||||
constraints:
|
||||
technical: ["技术约束"]
|
||||
timeline: "时间要求"
|
||||
resources: "资源限制"
|
||||
scope:
|
||||
in_scope: ["包含的功能点"]
|
||||
out_of_scope: ["明确排除的功能点"]
|
||||
|
||||
# 元数据
|
||||
metadata:
|
||||
complexity: "large|medium|small|micro"
|
||||
estimated_stages: ["①", "②", "③", "④", "⑤", "⑥"]
|
||||
risk_level: "high|medium|low"
|
||||
|
||||
# 遗留问题
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ② 技术方案产物
|
||||
|
||||
```yaml
|
||||
stage: design
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
architecture:
|
||||
pattern: "架构模式(如 monolith/microservice/modular)"
|
||||
modules:
|
||||
- name: "模块名"
|
||||
responsibility: "职责"
|
||||
dependencies: ["依赖的其他模块"]
|
||||
data_flow: "数据流描述"
|
||||
|
||||
tech_stack:
|
||||
language: "编程语言"
|
||||
framework: "框架"
|
||||
database: "数据库"
|
||||
other: ["其他技术选型"]
|
||||
reasons: "选型理由"
|
||||
|
||||
data_models:
|
||||
- name: "模型名"
|
||||
fields:
|
||||
- {name: "字段名", type: "类型", required: true, description: "说明"}
|
||||
relations: ["与其他模型的关系"]
|
||||
|
||||
api_design:
|
||||
- method: "HTTP方法"
|
||||
path: "/路径"
|
||||
description: "接口说明"
|
||||
request_schema: "请求体结构"
|
||||
response_schema: "响应体结构"
|
||||
|
||||
file_changes:
|
||||
new_files: ["需要新建的文件路径"]
|
||||
modified_files: ["需要修改的文件路径"]
|
||||
deleted_files: ["需要删除的文件路径"]
|
||||
|
||||
impact_analysis:
|
||||
affected_modules: ["受影响的模块"]
|
||||
breaking_changes: ["破坏性变更"]
|
||||
migration_needed: false
|
||||
|
||||
# 关键决策
|
||||
decisions:
|
||||
- decision: "决策内容"
|
||||
reason: "理由"
|
||||
alternatives: ["考虑过的替代方案"]
|
||||
|
||||
# 风险点
|
||||
risks:
|
||||
- description: "风险描述"
|
||||
probability: "high|medium|low"
|
||||
mitigation: "缓解措施"
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ③ UI设计产物(v2.1新增)
|
||||
|
||||
```yaml
|
||||
stage: ui_design
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
style_reference: "设计风格参考(如 微信原生风格/Material Design/Ant Design)"
|
||||
|
||||
design_tokens:
|
||||
colors:
|
||||
primary: "主色 (如 #07C160)"
|
||||
primary_light: "主色浅 (如 #E8F8EE)"
|
||||
secondary: "辅助色"
|
||||
background: "背景色"
|
||||
surface: "卡片/面板背景色"
|
||||
text_primary: "主文字色"
|
||||
text_secondary: "次要文字色"
|
||||
text_hint: "提示文字色"
|
||||
border: "边框色"
|
||||
error: "错误色"
|
||||
success: "成功色"
|
||||
warning: "警告色"
|
||||
typography:
|
||||
font_family: "字体"
|
||||
heading_1: "标题1样式"
|
||||
heading_2: "标题2样式"
|
||||
body: "正文样式"
|
||||
caption: "说明文字样式"
|
||||
spacing:
|
||||
page_padding: "页面内边距"
|
||||
card_padding: "卡片内边距"
|
||||
section_gap: "区块间距"
|
||||
item_gap: "列表项间距"
|
||||
border_radius:
|
||||
card: "卡片圆角"
|
||||
button: "按钮圆角"
|
||||
input: "输入框圆角"
|
||||
avatar: "头像圆角"
|
||||
shadows:
|
||||
card: "卡片阴影"
|
||||
elevated: "浮起阴影"
|
||||
|
||||
component_styles:
|
||||
- name: "组件名(如 DoctorCard)"
|
||||
description: "组件用途"
|
||||
tailwind_classes: "具体的Tailwind类名组合"
|
||||
states:
|
||||
default: "默认状态样式"
|
||||
hover: "悬停状态样式(如有)"
|
||||
active: "按下状态样式(如有)"
|
||||
disabled: "禁用状态样式(如有)"
|
||||
|
||||
page_layouts:
|
||||
- page: "页面路径(如 /departments/[id])"
|
||||
structure: "页面结构描述"
|
||||
key_elements: ["页面包含的关键UI元素"]
|
||||
|
||||
interaction_notes:
|
||||
- element: "交互元素"
|
||||
behavior: "交互行为描述"
|
||||
|
||||
# 自然语言摘要
|
||||
summary: "设计方案一句话总结"
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ④ 编码实现产物
|
||||
|
||||
```yaml
|
||||
stage: implementation
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
files_changed:
|
||||
- path: "文件路径"
|
||||
action: "created|modified|deleted"
|
||||
description: "变更说明"
|
||||
|
||||
implementation_notes:
|
||||
approach: "实现思路简述"
|
||||
key_decisions: ["实现中的关键决策"]
|
||||
deviations_from_design: ["与设计方案的偏差及原因"]
|
||||
|
||||
dependencies_added:
|
||||
- name: "依赖名"
|
||||
version: "版本"
|
||||
reason: "添加原因"
|
||||
|
||||
commands_to_run:
|
||||
build: "构建命令"
|
||||
start: "启动命令(如有)"
|
||||
|
||||
# 自检结果
|
||||
self_check:
|
||||
compiles: true
|
||||
lint_passes: true
|
||||
basic_smoke_test: "描述基本冒烟验证结果"
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⑤ 代码审查产物
|
||||
|
||||
```yaml
|
||||
stage: code_review
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
verdict: "APPROVED|REQUEST_CHANGES"
|
||||
|
||||
issues:
|
||||
- id: "CR-1"
|
||||
severity: "CRITICAL|IMPORTANT|MINOR"
|
||||
category: "security|correctness|performance|maintainability|convention|testing"
|
||||
file: "文件路径"
|
||||
line: 42
|
||||
description: "问题描述"
|
||||
suggestion: "修改建议"
|
||||
confidence: 0.9
|
||||
|
||||
dimensions_checked:
|
||||
security: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
correctness: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
performance: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
maintainability: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
convention: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
test_coverage: {score: "PASS|WARN|FAIL", notes: ""}
|
||||
|
||||
# 信息隔离声明
|
||||
review_context:
|
||||
saw_implementation_prompt: false # 审查者不应看到编码prompt
|
||||
saw_only: ["代码变更(git diff)", "需求文档", "技术方案"]
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⑥ 测试验证产物
|
||||
|
||||
```yaml
|
||||
stage: testing
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
verdict: "ALL_PASS|PARTIAL_FAIL|ALL_FAIL"
|
||||
|
||||
test_layers:
|
||||
unit:
|
||||
total: 0
|
||||
passed: 0
|
||||
failed: 0
|
||||
skipped: 0
|
||||
coverage_percent: 0
|
||||
test_files: ["测试文件路径"]
|
||||
integration:
|
||||
total: 0
|
||||
passed: 0
|
||||
failed: 0
|
||||
skipped: 0
|
||||
test_files: []
|
||||
smoke:
|
||||
total: 0
|
||||
passed: 0
|
||||
failed: 0
|
||||
description: "冒烟测试说明"
|
||||
|
||||
failed_tests:
|
||||
- test_name: "失败的测试名"
|
||||
layer: "unit|integration|smoke"
|
||||
error_message: "错误信息"
|
||||
root_cause: "初步分析"
|
||||
suggested_fix: "建议修复方式"
|
||||
|
||||
coverage:
|
||||
line_percent: 0
|
||||
branch_percent: 0
|
||||
function_percent: 0
|
||||
meets_threshold: true
|
||||
|
||||
regression_check:
|
||||
existing_tests_still_pass: true
|
||||
broken_tests: []
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⑦ 部署上线产物
|
||||
|
||||
```yaml
|
||||
stage: deployment
|
||||
version: "1.0"
|
||||
timestamp: "{{ISO_TIMESTAMP}}"
|
||||
|
||||
artifacts:
|
||||
deployment_method: "描述部署方式"
|
||||
environment: "production|staging|development"
|
||||
|
||||
steps_executed:
|
||||
- step: "步骤描述"
|
||||
status: "success|failed"
|
||||
output: "关键输出"
|
||||
|
||||
verification:
|
||||
service_running: true
|
||||
health_check_passed: true
|
||||
smoke_test_passed: true
|
||||
key_features_verified: ["已验证的核心功能"]
|
||||
|
||||
rollback_plan:
|
||||
method: "回滚方式"
|
||||
command: "回滚命令"
|
||||
|
||||
open_issues: []
|
||||
```
|
||||
@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## 0.2.0 - 2026-07-02
|
||||
|
||||
- 基于官方文档和 GitHub 仓库完整重构规范
|
||||
- 新增完整的 SKILL.md frontmatter 字段定义和验证器约束
|
||||
- 新增推荐正文结构(Overview → When to Use → Pitfalls → Verification)
|
||||
- 新增渐进式披露三层加载机制
|
||||
- 新增写作质量 8 项原则
|
||||
- 新增官方参考技能列表和 GitHub 链接
|
||||
- 新增工具链说明(skill_manage、/learn、skill_view)
|
||||
- 新增 Skill Bundles 和 Skills Hub 说明
|
||||
- 新增常见陷阱(9 项)
|
||||
|
||||
## 0.1.0 - 2026-07-01
|
||||
|
||||
- 初始版本
|
||||
|
||||
@ -1,21 +1,192 @@
|
||||
# 如何创建 Hermes Agent 技能
|
||||
|
||||
本技能指导你为 Hermes Agent 框架创建符合规范的技能,特别是结构化输出和工具调用协议。
|
||||
本技能指导你为 Hermes Agent 框架创建符合规范的技能,基于官方文档和 GitHub 仓库中的最佳实践。
|
||||
|
||||
## 核心概念
|
||||
|
||||
Hermes Agent 技能(Skill)是代理的**程序化记忆**(Procedural Memory),代理在运行时创建和复用。技能遵循 [agentskills.io](https://agentskills.io) 开放标准,可跨平台共享。
|
||||
|
||||
## 技能存放位置
|
||||
|
||||
| 位置 | 路径 | 用途 |
|
||||
|------|------|------|
|
||||
| **用户本地** | `~/.hermes/skills/<name>/SKILL.md` | 个人技能,不共享,通过 `skill_manage(action='create')` 创建 |
|
||||
| **项目仓库内** | `skills/<category>/<name>/SKILL.md` | 随项目提交,团队共享 |
|
||||
|
||||
## 创建流程
|
||||
|
||||
1. **了解 Hermes Agent 规范**:阅读 `spec.md`,了解结构化输出和工具调用协议
|
||||
2. **参考官方示例**:查看 `examples/` 目录中的参考实现
|
||||
3. **创建技能目录**:在 `hermes-agent/skills/<skill-name>/` 下新建目录
|
||||
4. **编写技能文件**:
|
||||
- `skill.json`:技能元信息
|
||||
- `prompt.md`:技能指令和输出格式定义
|
||||
- 按需添加工具定义和测试用例
|
||||
5. **验证技能**:在 Hermes Agent 环境中加载并测试
|
||||
6. **发布技能**:更新版本号,归档到 `versions/`
|
||||
### 1. 调研已有技能
|
||||
|
||||
## 注意事项
|
||||
```bash
|
||||
# 查看目标分类下已有的技能
|
||||
ls skills/<category>/
|
||||
```
|
||||
|
||||
- Hermes Agent 强调结构化输出,技能需定义输出 Schema
|
||||
- 工具调用协议有特定格式要求
|
||||
- 建议参考 `examples/` 中的示例了解输出格式
|
||||
阅读 2-3 个同类技能的 SKILL.md,匹配风格和结构。避免创建重复技能。
|
||||
|
||||
### 2. 创建 SKILL.md
|
||||
|
||||
文件必须:
|
||||
- 以 `---` 开头(不能有空行或 BOM)
|
||||
- YAML frontmatter 以 `\n---\n` 闭合
|
||||
- frontmatter 后紧跟 Markdown 正文
|
||||
|
||||
### 3. 编写 Frontmatter
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: my-skill-name # 小写+连字符,≤64 字符
|
||||
description: "Use when <触发条件>. <一句话行为描述>." # ≤1024 字符
|
||||
version: 1.0.0
|
||||
author: <作者名>
|
||||
license: MIT
|
||||
platforms: [linux, macos, windows]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [短, 描述性, 标签]
|
||||
related_skills: [other-skill, another-skill]
|
||||
---
|
||||
```
|
||||
|
||||
**必填字段**(验证器强制):
|
||||
- `name`:≤64 字符,正则 `^[a-z][a-z0-9_-]*$`
|
||||
- `description`:≤1024 字符,以 "Use when ..." 开头
|
||||
|
||||
**推荐字段**(所有官方技能都包含):
|
||||
- `version`、`author`、`license`、`platforms`
|
||||
- `metadata.hermes.tags`、`metadata.hermes.related_skills`
|
||||
|
||||
### 4. 编写正文结构
|
||||
|
||||
```markdown
|
||||
# <技能标题>
|
||||
|
||||
## Overview
|
||||
一到两段:做什么,为什么需要。
|
||||
|
||||
## When to Use
|
||||
- 触发条件(bullet 列表)
|
||||
- "Don't use for:" 反触发条件
|
||||
|
||||
## <技能特定章节>
|
||||
- 快速参考表
|
||||
- 精确命令的代码块
|
||||
- Hermes 特有的 recipes
|
||||
|
||||
## Common Pitfalls
|
||||
编号列表:常见错误和修复方法。
|
||||
|
||||
## Verification Checklist
|
||||
- [ ] 操作后验证的 checkbox 列表
|
||||
|
||||
## One-Shot Recipes(可选)
|
||||
命名场景 → 具体命令序列。
|
||||
```
|
||||
|
||||
### 5. 添加辅助文件
|
||||
|
||||
- `references/` — 按需加载的参考文档
|
||||
- `templates/` — 模板文件
|
||||
- `scripts/` — 辅助脚本
|
||||
- `assets/` — 静态资源
|
||||
|
||||
### 6. 本地验证
|
||||
|
||||
```python
|
||||
import yaml, re, pathlib
|
||||
content = pathlib.Path("skills/<category>/<name>/SKILL.md").read_text()
|
||||
assert content.startswith("---")
|
||||
m = re.search(r'\n---\s*\n', content[3:])
|
||||
fm = yaml.safe_load(content[3:m.start()+3])
|
||||
assert "name" in fm and "description" in fm
|
||||
assert len(fm["description"]) <= 1024
|
||||
assert len(content) <= 100_000
|
||||
```
|
||||
|
||||
### 7. 提交
|
||||
|
||||
```bash
|
||||
git add skills/<category>/<name>/
|
||||
git commit -m "Add skill: <name>"
|
||||
```
|
||||
|
||||
> 注意:当前会话的技能加载器在会话开始时缓存,新技能需要在新会话中才能被 `skill_view` / `skills_list` 看到。
|
||||
|
||||
## 关键规范
|
||||
|
||||
### Description 编写规范
|
||||
|
||||
- ≤ 1024 字符
|
||||
- 以 "Use when ..." 开头,描述**触发类别**而非单一任务
|
||||
- 示例:
|
||||
- ✅ "Use when debugging test failures. Enforce RED-GREEN-REFACTOR cycle."
|
||||
- ❌ "Debug tests"
|
||||
- ❌ "Be thorough when testing"
|
||||
|
||||
### 文件大小限制
|
||||
|
||||
| 限制项 | 值 | 说明 |
|
||||
|--------|---|------|
|
||||
| Description | ≤ 1024 字符 | 验证器强制 |
|
||||
| 完整 SKILL.md | ≤ 100,000 字符 | 约 36k tokens |
|
||||
| 推荐范围 | 8-14k 字符 | 官方技能平均水平 |
|
||||
| 超过 20k | 拆分到 `references/*.md` | 渐进式披露 |
|
||||
|
||||
### 渐进式披露(三层加载)
|
||||
|
||||
| 层级 | 调用方式 | 内容 |
|
||||
|------|---------|------|
|
||||
| L0 | `skills_list()` | 仅加载基础元数据 |
|
||||
| L1 | `skill_view(name)` | 加载完整 SKILL.md |
|
||||
| L2 | `skill_view(name, path)` | 加载特定参考文件 |
|
||||
|
||||
### 触发机制
|
||||
|
||||
- 斜杠命令:`/<skill-name>`
|
||||
- 自然语言对话
|
||||
- `fallback_for_toolsets`:仅在特定高级工具缺失时显示
|
||||
- `platforms`:自动隐藏不兼容操作系统的技能
|
||||
|
||||
### 写作质量原则
|
||||
|
||||
1. **优化过程可预测性** — 每一行都应改变代理行为,否则删除
|
||||
2. **正确的上下文负载** — description 每次都会被加载,保持精炼
|
||||
3. **信息层级** — 常用步骤放 SKILL.md,详细内容放 references/
|
||||
4. **完成标准** — 每个步骤都要有可检查的完成条件
|
||||
5. **规则与概念共存** — 定义、警告、示例、验证放在一起
|
||||
6. **使用强引导词** — "tight loop"、"root cause"、"regression test" 比长解释更省 token
|
||||
7. **消除重复和无效内容** — "Be careful"、"use best practices" 不改变行为,删除
|
||||
8. **防止过早完成** — 如果代理倾向于跳过某步骤,先锐化该步骤的完成标准
|
||||
|
||||
## 官方参考技能
|
||||
|
||||
| 技能 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| [Skill Authoring](https://github.com/nousresearch/hermes-agent/blob/main/skills/software-development/hermes-agent-skill-authoring/SKILL.md) | `skills/software-development/hermes-agent-skill-authoring/` | 官方技能编写指南 |
|
||||
| [Test-Driven Development](https://github.com/nousresearch/hermes-agent/blob/main/skills/software-development/test-driven-development/SKILL.md) | `skills/software-development/test-driven-development/` | TDD 工作流 |
|
||||
| [Systematic Debugging](https://github.com/nousresearch/hermes-agent/blob/main/skills/software-development/systematic-debugging/SKILL.md) | `skills/software-development/systematic-debugging/` | 系统化调试 |
|
||||
| [Plan](https://github.com/nousresearch/hermes-agent/blob/main/skills/software-development/plan/SKILL.md) | `skills/software-development/plan/` | 任务规划 |
|
||||
|
||||
## 工具链
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `skill_manage(action='create')` | 创建用户本地技能(`~/.hermes/skills/`) |
|
||||
| `skill_manage(action='patch')` | 补丁更新(支持 in-repo 和本地) |
|
||||
| `skill_manage(action='write_file')` | 写入辅助文件(references/templates/scripts/assets) |
|
||||
| `/learn` 命令 | 从 URL 或本地上下文自动生成技能 |
|
||||
| `skill_view(name)` | 查看技能完整内容 |
|
||||
| `skills_list()` | 列出所有可用技能 |
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 陷阱 | 说明 |
|
||||
|------|------|
|
||||
| 用 `skill_manage(create)` 创建 in-repo 技能 | 会写到 `~/.hermes/skills/`,应用 `write_file` |
|
||||
| `---` 前有空行 | 验证器检查 `startswith("---")`,会失败 |
|
||||
| Description 太通用 | 应以 "Use when ..." 开头描述触发类别 |
|
||||
| 缺少 author/license/metadata | 验证器不强制,但所有官方技能都有 |
|
||||
| 创建重复技能 | 先 `ls skills/<category>/` 检查 |
|
||||
| 期望当前会话看到新技能 | 技能加载器在会话开始时缓存 |
|
||||
| 积累沉淀内容 | 技能应越来越精炼,添加新规则时删除旧措辞 |
|
||||
| 无效内容 | "Be careful" 不改变行为,用可检查的完成标准替代 |
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
{
|
||||
"name": "create-hermes-agent-skill",
|
||||
"version": "0.1.0",
|
||||
"description": "指导如何为 Hermes Agent 创建技能(Skill)",
|
||||
"version": "0.2.0",
|
||||
"description": "指导如何为 Hermes Agent 创建技能(Skill),包含完整规范、官方参考和最佳实践",
|
||||
"targetFramework": "hermes-agent",
|
||||
"type": "meta-skill",
|
||||
"tags": ["hermes-agent", "skill-creation", "guide"]
|
||||
"tags": ["hermes-agent", "skill-creation", "guide"],
|
||||
"sources": [
|
||||
"https://hermes-agent.nousresearch.com/docs/user-guide/features/skills",
|
||||
"https://github.com/nousresearch/hermes-agent/blob/main/skills/software-development/hermes-agent-skill-authoring/SKILL.md",
|
||||
"https://github.com/nousresearch/hermes-agent/blob/main/skills/software-development/test-driven-development/SKILL.md"
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,56 +1,124 @@
|
||||
# Hermes Agent 技能规范摘要
|
||||
|
||||
> 本文件摘要整理 Hermes Agent 技能的核心规范,供快速参考。完整规范请参阅 Hermes Agent 官方文档。
|
||||
> 基于 Hermes Agent 官方文档和 GitHub 仓库(`nousresearch/hermes-agent`)整理。
|
||||
|
||||
## 技能目录结构
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
<skill-name>/
|
||||
├── skill.json # 技能元信息(必填)
|
||||
├── prompt.md # 技能指令和输出定义(必填)
|
||||
├── tools/ # 工具定义(可选)
|
||||
├── tests/ # 验证用例(可选)
|
||||
└── CHANGELOG.md # 版本变更记录(推荐)
|
||||
├── SKILL.md # 核心指令文件(必填)
|
||||
├── references/ # 按需加载的参考文档(可选)
|
||||
├── templates/ # 模板文件(可选)
|
||||
├── scripts/ # 辅助脚本(可选)
|
||||
├── assets/ # 静态资源(可选)
|
||||
└── CHANGELOG.md # 版本变更记录(推荐)
|
||||
```
|
||||
|
||||
## skill.json 字段
|
||||
## SKILL.md 规范
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `name` | string | 是 | 技能名称 |
|
||||
| `version` | string | 是 | 语义化版本号 |
|
||||
| `description` | string | 是 | 技能功能描述 |
|
||||
| `outputSchema` | object | 否 | 结构化输出的 JSON Schema |
|
||||
| `tools` | array | 否 | 依赖的工具列表 |
|
||||
| `tags` | array | 否 | 标签 |
|
||||
### YAML Frontmatter
|
||||
|
||||
## prompt.md 格式
|
||||
文件必须以 `---` 开头(第一个字节),无空行或 BOM:
|
||||
|
||||
Hermes Agent 的技能指令定义:
|
||||
```yaml
|
||||
---
|
||||
name: my-skill-name # 必填,≤64 字符,^[a-z][a-z0-9_-]*$
|
||||
description: "Use when ..." # 必填,≤1024 字符
|
||||
version: 1.0.0 # 推荐
|
||||
author: <作者> # 推荐
|
||||
license: MIT # 推荐
|
||||
platforms: [linux, macos, windows] # 推荐,不兼容平台自动隐藏
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [tag1, tag2] # 推荐,搜索标签
|
||||
category: software-development # 可选
|
||||
related_skills: [other-skill] # 推荐,跨技能引用
|
||||
fallback_for_toolsets: [] # 可选,仅在特定工具缺失时显示
|
||||
requires_toolsets: [] # 可选,依赖的工具集
|
||||
config: [] # 可选,配置项
|
||||
---
|
||||
```
|
||||
|
||||
- 使用 Markdown 格式编写
|
||||
- **必须明确定义输出格式**(JSON Schema 或示例)
|
||||
- 描述技能的执行规则和约束
|
||||
- 指定工具调用的条件和参数
|
||||
- 建议包含输入输出示例
|
||||
### 验证器硬约束
|
||||
|
||||
## 结构化输出
|
||||
| 约束 | 值 | 来源 |
|
||||
|------|---|------|
|
||||
| `name` 必填 | — | `skill_manager_tool.py::_validate_frontmatter` |
|
||||
| `name` 最大长度 | 64 字符 | `MAX_NAME_LENGTH` |
|
||||
| `description` 必填 | — | 同上 |
|
||||
| `description` 最大长度 | 1024 字符 | `MAX_DESCRIPTION_LENGTH` |
|
||||
| SKILL.md 最大长度 | 100,000 字符 | `MAX_SKILL_CONTENT_CHARS` |
|
||||
| Frontmatter 开头 | `---`(第 0 字节) | 同上 |
|
||||
| Frontmatter 闭合 | `\n---\n` | 同上 |
|
||||
| Frontmatter 后正文 | 非空 | 同上 |
|
||||
|
||||
Hermes Agent 的核心特性:
|
||||
### 推荐正文结构
|
||||
|
||||
- 技能输出必须符合预定义的 JSON Schema
|
||||
- 在 `skill.json` 的 `outputSchema` 字段定义
|
||||
- 或在 `prompt.md` 中明确描述输出格式
|
||||
- 支持嵌套对象和数组类型
|
||||
```markdown
|
||||
# <标题>
|
||||
|
||||
## 工具调用协议
|
||||
## Overview
|
||||
做什么,为什么。
|
||||
|
||||
- 工具以标准化接口定义
|
||||
- 支持同步和异步调用
|
||||
- 工具定义可放在 `tools/` 目录
|
||||
- 需声明工具参数的 JSON Schema
|
||||
## When to Use
|
||||
- 触发条件
|
||||
- "Don't use for:" 反触发
|
||||
|
||||
## 参考链接
|
||||
## <特定章节>
|
||||
命令、表格、recipes...
|
||||
|
||||
- [Hermes Agent 官方文档](https://docs.hermes-agent.dev)(待确认)
|
||||
- `examples/` 目录中的参考实现
|
||||
## Common Pitfalls
|
||||
编号列表。
|
||||
|
||||
## Verification Checklist
|
||||
- [ ] 验证项
|
||||
```
|
||||
|
||||
## 存放位置
|
||||
|
||||
| 类型 | 路径 | 创建方式 |
|
||||
|------|------|---------|
|
||||
| 用户本地 | `~/.hermes/skills/<name>/` | `skill_manage(action='create')` |
|
||||
| 项目仓库 | `skills/<category>/<name>/` | `write_file` + `git add` |
|
||||
| 外部导入 | `~/.hermes/skills/openclaw-imports/` | 从 OpenClaw 迁移 |
|
||||
|
||||
## 分类目录(官方)
|
||||
|
||||
```
|
||||
autonomous-ai-agents creative data-science devops dogfood
|
||||
email gaming github leisure mcp media mlops
|
||||
note-taking productivity red-teaming research
|
||||
smart-home social-media software-development
|
||||
```
|
||||
|
||||
选择最接近的已有分类,不要随意创建新分类。
|
||||
|
||||
## 渐进式披露
|
||||
|
||||
| 层级 | API | 内容 |
|
||||
|------|-----|------|
|
||||
| L0 | `skills_list()` | 基础元数据(name + description) |
|
||||
| L1 | `skill_view(name)` | 完整 SKILL.md |
|
||||
| L2 | `skill_view(name, path)` | 特定 references/ 文件 |
|
||||
|
||||
## Skill Bundles
|
||||
|
||||
多个技能可打包为一个 Bundle:
|
||||
|
||||
- 存放路径:`~/.hermes/skill-bundles/`
|
||||
- 格式:YAML 文件
|
||||
- 通过单个斜杠命令调用一组技能
|
||||
|
||||
## Skills Hub
|
||||
|
||||
- 官方 Hub:[agentskills.io](https://agentskills.io)
|
||||
- 支持来源:`official`、`skills-sh`、`well-known`、`github`
|
||||
- 所有第三方安装会经过安全扫描
|
||||
|
||||
## 编辑已有技能
|
||||
|
||||
| 操作 | 方法 |
|
||||
|------|------|
|
||||
| 小修复 | `skill_manage(action='patch', name=..., old_string=..., new_string=...)` |
|
||||
| 大重写 | `write_file` 整个 SKILL.md |
|
||||
| 添加辅助文件 | `write_file` 到 references/templates/scripts/assets |
|
||||
|
||||
Loading…
Reference in New Issue
Block a user