Skip to content

Commit f32fac7

Browse files
committed
Add skills code review agent example
1 parent cbb6979 commit f32fac7

73 files changed

Lines changed: 10075 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/skills_code_review_agent/ARCHITECTURE.md

Lines changed: 393 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# CR Agent 实现阶段 Spec 总览
2+
3+
把整体架构(见 `../ARCHITECTURE.md`)拆成 **7 个可交付阶段(P0–P6)**,每阶段一份 spec,标明目标、前置依赖、交付物、接口契约、验收标准(Definition of Done)。团队可按阶段并行推进、按阶段验收。
4+
5+
## 阶段总览
6+
7+
| 阶段 | 名称 | 关键交付物 | 前置依赖 | 并行机会 |
8+
|------|------|-----------|----------|----------|
9+
| P0 | 基础设施 | `schema.sql` + `storage.py` + `init_db.py` |||
10+
| P1 | 输入与 Skill | `parse_diff.py` + `SKILL.md` + `skill_load` | P0 | P2 规则文档可并行 |
11+
| P2 | 规则引擎 | 6 类规则 + `run_checks.py` + `mask_secrets.py` | P1 | 规则文档与 P1 并行 |
12+
| P3 | 沙箱与 Filter | `runtime.py` + `policy.py` + `governance.py` | P2 | sandbox 与 filter 内部并行 |
13+
| P4 | 去重结构化 | `dedupe.py` + finding 组装 | P2, P3 ||
14+
| P5 | 编排与报告 | `agent.py` + 报告生成 + dry-run | P0–P4 ||
15+
| P6 | 测试验收 | 8 fixtures + `test_cr_agent.py` | P5 ||
16+
17+
## 依赖关系
18+
19+
```
20+
P0 → P1 → P2 → P3 → P4 → P5 → P6
21+
│ │
22+
规则文档可与 P1 并行
23+
24+
sandbox 与 filter 可内部并行
25+
```
26+
27+
主线串行,两处并行:
28+
- **P2 规则文档**纯文本编写,不依赖解析器,可与 P1 同时启动。
29+
- **P3 sandbox runtime****filter 治理**职责正交(一个管执行隔离,一个管前置决策),可两人并行实现,最后在 P3 末尾集成。
30+
31+
## 里程碑
32+
33+
| 里程碑 | 完成判据 |
34+
|--------|----------|
35+
| P0 完成 | 可建库、可 CRUD、按 task_id join 查询 |
36+
| P3 完成 | 沙箱能带约束跑脚本,Filter 能拦截高风险脚本 |
37+
| P5 完成 | 完整链路 dry-run 跑通,产出 `review_report.json` + `.md` |
38+
| P6 完成 | 8 条样本全过,验收标准 8 条达标 |
39+
40+
## Spec 文件清单
41+
42+
- [`phase-0-foundation.md`](phase-0-foundation.md) — 基础设施层
43+
- [`phase-1-input-skill.md`](phase-1-input-skill.md) — 输入与 Skill 加载
44+
- [`phase-2-rules-engine.md`](phase-2-rules-engine.md) — 规则引擎
45+
- [`phase-3-sandbox-filter.md`](phase-3-sandbox-filter.md) — 沙箱与 Filter
46+
- [`phase-4-dedupe-structuring.md`](phase-4-dedupe-structuring.md) — 去重结构化
47+
- [`phase-5-orchestration-report.md`](phase-5-orchestration-report.md) — 编排与报告
48+
- [`phase-6-test-acceptance.md`](phase-6-test-acceptance.md) — 测试与验收
49+
50+
## 阶段交付建议
51+
52+
- 每个 spec 的 **接口契约**是跨阶段协作的硬约束,改动需同步上下游。
53+
- 每个 spec 的 **验收标准(DoD)**是该阶段完成的判定,未达标不进入下一阶段。
54+
- **风险与注意事项**列了该阶段最容易踩的坑,实现前先看。
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Phase 0 — 基础设施层 (Foundation)
2+
3+
> 里程碑阶段。建立数据库 schema 和存储抽象层,为所有后续阶段提供持久化基础。
4+
5+
## 阶段目标
6+
7+
实现七表 schema 和 `ReviewStore` 存储接口。所有后续阶段(P1–P6)都依赖本阶段的接口来落库。本阶段完成后,数据库可建、可写、可按 `task_id` join 查询。
8+
9+
## 前置依赖
10+
11+
无。本阶段是整条流水线的起点。
12+
13+
## 交付物
14+
15+
| 文件 | 职责 |
16+
|------|------|
17+
| `db/schema.sql` | 七表建表 DDL + 索引 |
18+
| `db/storage.py` | `ReviewStore` 抽象 + `SQLiteStore` 实现 |
19+
| `db/init_db.py` | 初始化脚本(建库 + 建表) |
20+
21+
## 接口契约
22+
23+
### 数据库表
24+
25+
七表定义见 `../ARCHITECTURE.md` 第 5.2 节 DDL。表清单:`review_task` / `input_diff` / `sandbox_run` / `finding` / `filter_block` / `monitor_summary` / `review_report`
26+
27+
### ReviewStore 接口(`storage.py`)
28+
29+
```python
30+
from typing import Protocol
31+
32+
class ReviewStore(Protocol):
33+
# —— task 生命周期 ——
34+
def create_task(self, input_type: str, input_ref: str, mode: str) -> str: ...
35+
def update_task_status(self, task_id: str, status: str,
36+
total_duration_ms: int | None = None) -> None: ...
37+
38+
# —— 子表写入(均返回记录 id)——
39+
def add_input_diff(self, task_id, file_path, sha256,
40+
hunk_count, line_count, summary) -> str: ...
41+
def add_sandbox_run(self, task_id, runtime, script, status,
42+
duration_ms, exit_code, output_bytes,
43+
timed_out, masked_count) -> str: ...
44+
def add_finding(self, task_id, severity, category, file, line,
45+
title, evidence, recommendation, confidence,
46+
source, bucket) -> str: ...
47+
def add_filter_block(self, task_id, reason, target,
48+
decision, detail) -> str: ...
49+
def set_monitor_summary(self, task_id, summary: dict) -> None: ...
50+
def set_report(self, task_id, json_path, md_path, summary) -> str: ...
51+
52+
# —— 查询 ——
53+
def get_task(self, task_id: str) -> dict: ...
54+
# join 返回完整记录:
55+
# {task, input_diffs[], sandbox_runs[],
56+
# findings[], filter_blocks[], monitor_summary, report}
57+
```
58+
59+
### SQLiteStore 实现要点
60+
61+
- 连接:`sqlite3.connect(db_path)`,`row_factory = sqlite3.Row`
62+
- 切换后端:只需新实现同一 Protocol(如 `PostgresStore`),上层无感
63+
- 事务:单条写入自动 commit;批量 finding 用 `executemany`
64+
- id 生成:`uuid4().hex`
65+
66+
## 实现要点
67+
68+
- `schema.sql` 全部用 `CREATE TABLE IF NOT EXISTS`,可重复执行
69+
- 索引 `idx_finding_dedup (task_id, file, line, category)` 必须建,直接服务 P4 去重
70+
- `monitor_summary.exception_types` 存 JSON 字符串(如 `{"timeout":2,"oom":1}`)
71+
- `sandbox_run.timed_out` 用 INTEGER 0/1(SQLite 无原生 bool)
72+
- `finding.confidence` 用 REAL(浮点)
73+
74+
## 验收标准 (DoD)
75+
76+
- [ ] `init_db.py` 能建库建表,重复执行不报错
77+
- [ ] `create_task` 返回 task_id,`update_task_status` 可改状态
78+
- [ ] 七个 `add_*` / `set_*` 方法可写入对应表
79+
- [ ] `get_task(task_id)` 能 join 返回完整记录(task + input_diffs + sandbox_runs + findings + filter_blocks + monitor_summary + report)
80+
- [ ] `SQLiteStore` 实现同一 Protocol,后续可替换为其他 SQL 后端而不改上层
81+
82+
## 风险与注意事项
83+
84+
- **SQLite 并发写**:单文件 SQLite 写锁粒度大,生产高并发需切 Postgres。接口已预留,实现时不要在 Protocol 里暴露 SQLite 特有 API。
85+
- **大 diff 的 input_diff 行数**:`summary` 字段存摘要(文件数/hunk 数/行数),**不存全量 diff**
86+
- **finding 批量插入**:单次评审可能产数百条 finding,用 `executemany` 而非循环单插。
87+
- **外键约束**:SQLite 默认关闭外键,`init_db``PRAGMA foreign_keys = ON`
88+
89+
## 关联文件
90+
91+
- 上游契约:`../ARCHITECTURE.md` 第 5 节(schema + ER)
92+
- 下游消费:P1–P6 全部阶段(都调 `ReviewStore`)
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Phase 1 — 输入与 Skill 加载
2+
3+
> 把原始 diff 变成结构化 ChangeSet,并加载 code-review Skill 的规则清单与脚本目录。
4+
5+
## 阶段目标
6+
7+
实现 unified diff 解析器和 Skill 加载器。产出 `ChangeSet`(文件/hunk/行号)和 `RuleSet`(规则 + 脚本清单),供 P2 规则引擎和 P3 沙箱消费。
8+
9+
## 前置依赖
10+
11+
- **P0**:`ReviewStore` 可用(解析结果写入 `input_diff` 表)
12+
13+
## 交付物
14+
15+
| 文件 | 职责 |
16+
|------|------|
17+
| `skills/code-review/SKILL.md` | Skill 契约 frontmatter |
18+
| `skills/code-review/scripts/parse_diff.py` | unified diff → `ChangeSet` |
19+
| `agent.py``skill_load()` | 读 SKILL.md → 规则 + 脚本 + 沙箱配置 |
20+
21+
## 接口契约
22+
23+
### ChangeSet 数据结构
24+
25+
```python
26+
@dataclass
27+
class DiffLine:
28+
type: str # "add" | "del" | "ctx"
29+
content: str
30+
new_line_no: int | None # add/ctx 行有,del 行为 None
31+
32+
@dataclass
33+
class Hunk:
34+
old_start: int
35+
new_start: int
36+
old_count: int
37+
new_count: int
38+
lines: list[DiffLine]
39+
40+
@dataclass
41+
class ChangedFile:
42+
path: str
43+
status: str # "added" | "modified" | "deleted"
44+
hunks: list[Hunk]
45+
46+
@dataclass
47+
class ChangeSet:
48+
files: list[ChangedFile]
49+
```
50+
51+
### parse_diff 接口
52+
53+
```python
54+
def parse_diff(diff_text: str) -> ChangeSet: ...
55+
```
56+
57+
支持标准 unified diff:
58+
```
59+
diff --git a/foo.py b/foo.py
60+
+++ b/foo.py
61+
@@ -10,3 +10,4 @@
62+
context line
63+
-old line
64+
+new line
65+
+added line
66+
```
67+
68+
行号规则:`new_line_no``+c`(`@@ -a,b +c,d @@`)开始递增,只在 **add / ctx** 行赋值,del 行为 None。
69+
70+
### skill_load 接口
71+
72+
```python
73+
def skill_load(skill_dir: str) -> dict:
74+
return {
75+
"name": "code-review",
76+
"rules": ["rules/security.md", ...], # rules/*.md
77+
"scripts": ["scripts/run_checks.py", ...], # scripts/*.py
78+
"sandbox_config": {
79+
"default_runtime": "container",
80+
"fallback": "local",
81+
"timeout_s": 30,
82+
"max_output_bytes": 1048576,
83+
"env_whitelist": ["PATH", "HOME", "LANG"]
84+
}
85+
}
86+
```
87+
88+
### SKILL.md frontmatter
89+
90+
`../ARCHITECTURE.md` 第 7.1 节。
91+
92+
## 实现要点
93+
94+
- 解析 `@@ -a,b +c,d @@`:正则 `^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@`
95+
- 多文件分割:按 `diff --git``+++ b/` 行切分
96+
- `skill_load``yaml` 解析 frontmatter(`---` 之间)
97+
- 脚本清单只收集 `scripts/*.py`,规则清单只收集 `rules/*.md`
98+
- repo-path 模式:调 `git diff HEAD`(已暂存+未暂存),需在 `agent.py` 处理
99+
100+
## 验收标准 (DoD)
101+
102+
- [ ] `parse_diff` 能解析标准 unified diff,正确提取文件/hunk/行号
103+
- [ ] add 行的 `new_line_no` 正确(用已知 diff 对照测试)
104+
- [ ] `skill_load` 能读 SKILL.md frontmatter,产出 `rules` + `scripts` + `sandbox_config`
105+
- [ ] `ChangeSet` 可落库:file_path / sha256 / hunk_count / line_count / summary 写入 `input_diff`
106+
- [ ] 支持 `--diff-file` / `--repo-path` / fixture 三种输入
107+
108+
## 风险与注意事项
109+
110+
- **git diff 格式变种**:binary 文件、rename、mode change 需容错跳过,不报错。
111+
- **大 diff 性能**:行级解析,避免全量字符串复制;用生成器/迭代器处理行。
112+
- **repo-path 模式**:`git diff` 取 unstaged 还是 `git diff HEAD` 需明确,建议默认 `HEAD`(含已暂存)。
113+
- **空 diff**:返回空 `ChangeSet`,不报错(对应 P6 的"无问题 diff"样本)。
114+
115+
## 并行机会
116+
117+
P2 的规则文档(`rules/*.md`)是纯文本,不依赖解析器,可由另一人与本阶段同时编写。
118+
119+
## 关联文件
120+
121+
- 上游:用户输入(diff 文件 / repo / fixture)
122+
- 下游:P2(ChangeSet 喂规则引擎)、P3(脚本清单喂 Filter)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Phase 2 — 规则引擎
2+
3+
> 实现 6 类规则文档与匹配脚本,把 ChangeSet 变成原始诊断列表,并提供敏感信息脱敏。
4+
5+
## 阶段目标
6+
7+
实现 6 类规则(超出要求的 4 类)和 `run_checks.py` 规则匹配器,产出 `RawFinding` 列表;实现 `mask_secrets.py` 敏感信息脱敏。本阶段产出的是**未去重、未分流**的原始诊断,交 P4 处理。
8+
9+
## 前置依赖
10+
11+
- **P1**:`ChangeSet` + `RuleSet` 可用
12+
13+
## 交付物
14+
15+
| 文件 | 职责 |
16+
|------|------|
17+
| `skills/code-review/rules/security.md` | 安全风险规则 |
18+
| `skills/code-review/rules/async_errors.md` | 异步错误规则 |
19+
| `skills/code-review/rules/resource_leak.md` | 资源泄漏规则 |
20+
| `skills/code-review/rules/missing_tests.md` | 测试缺失规则 |
21+
| `skills/code-review/rules/sensitive_info.md` | 敏感信息规则 |
22+
| `skills/code-review/rules/db_lifecycle.md` | 数据库生命周期规则 |
23+
| `skills/code-review/scripts/run_checks.py` | 规则匹配 → `RawFinding` |
24+
| `skills/code-review/scripts/mask_secrets.py` | 敏感信息脱敏 |
25+
26+
## 接口契约
27+
28+
### RawFinding 结构
29+
30+
```python
31+
@dataclass
32+
class RawFinding:
33+
category: str # security|async|resource|tests|sensitive|db
34+
file: str
35+
line: int
36+
title: str
37+
evidence: str
38+
severity_hint: str # critical|high|medium|low (规则建议,最终 severity 在 P4 定)
39+
confidence: float # 0.0-1.0
40+
source: str = "rule" # rule|sandbox|llm
41+
```
42+
43+
### run_checks 接口
44+
45+
```python
46+
def run_checks(changeset: ChangeSet, ruleset: dict) -> list[RawFinding]: ...
47+
```
48+
49+
读取 `ruleset["rules"]` 的规则文档,对 `changeset`**add 行**做模式匹配 / AST 分析,产出 `RawFinding` 列表。
50+
51+
### mask_secrets 接口
52+
53+
```python
54+
def mask_secrets(text: str) -> tuple[str, int]:
55+
"""返回 (脱敏后文本, 命中数)"""
56+
```
57+
58+
### 六类规则覆盖
59+
60+
| 规则文档 | 覆盖问题 | 检测方式 | severity_hint |
61+
|----------|----------|----------|---------------|
62+
| `security.md` | SQL 注入 / 命令注入 / 硬编码密钥 / 不安全反序列化 | 静态模式 + (沙箱)semgrep | critical / high |
63+
| `async_errors.md` | 未 await 协程 / 未处理 rejection / async 资源泄漏 | AST 解析 | high / medium |
64+
| `resource_leak.md` | 未关闭文件连接 / try 无 finally | AST + 控制流 | high / medium |
65+
| `missing_tests.md` | 新增公开函数无对应测试 | diff 关联分析 | low |
66+
| `sensitive_info.md` | 明文 API key / token / password | 正则 + 熵值 | critical |
67+
| `db_lifecycle.md` | 连接未关闭 / 事务未提交回滚 / 连接池泄漏 | AST | high / medium |
68+
69+
## 实现要点
70+
71+
- **规则文档格式**:每条规则含 `id` / `pattern` / `severity_hint` / `confidence` / `description`
72+
- **只分析 add 行**:del 行是删除的代码,不报新问题
73+
- **confidence 由匹配强度决定**:精确模式 0.9,模糊/启发式 0.6
74+
- **mask_secrets 正则集**:
75+
- `AKIA[0-9A-Z]{16}`(AWS key)
76+
- `sk-[a-zA-Z0-9]{20,}`(OpenAI)
77+
- `ghp_[a-zA-Z0-9]{36}`(GitHub token)
78+
- `password\s*=\s*['"].*['"]`
79+
- `-----BEGIN .* PRIVATE KEY-----`
80+
- **熵值检测**:连续高熵字符串(疑似密钥)Shannon entropy > 4.5
81+
- **脱敏替换**:命中片段替换为 `***REDACTED***`
82+
83+
## 验收标准 (DoD)
84+
85+
- [ ] 6 类规则文档齐全,每类至少 3 条具体规则
86+
- [ ] `run_checks` 对每类规则的样本 diff 能检出对应问题
87+
- [ ] `RawFinding` 字段齐全,`confidence` 合理
88+
- [ ] `mask_secrets` 能脱敏常见密钥格式,`masked_count` 正确
89+
- [ ] 无问题的 diff 产出空列表(不误报,对应 P6 的"无问题 diff"样本)
90+
91+
## 风险与注意事项
92+
93+
- **误报控制**:模式匹配易误报,`confidence` 偏低让 P4 分流到 `warnings`,避免污染高置信 findings。
94+
- **AST 解析依赖**:Python`ast` 模块;其他语言可降级为模式匹配。
95+
- **性能**:大 diff 行数多,规则匹配用编译后的正则(`re.compile`)。
96+
- **规则文档可读性**:文档要让人能读懂规则意图,不只是机器格式。
97+
98+
## 并行机会
99+
100+
规则文档(`rules/*.md`)是纯文本,不依赖解析器,可与 P1 并行编写。
101+
102+
## 关联文件
103+
104+
- 上游:P1 `ChangeSet` + `RuleSet`
105+
- 下游:P3(脚本在沙箱跑)、P4(`RawFinding` 喂去重)

0 commit comments

Comments
 (0)