refactor(ci): consolidate bug-hunter personas to 3 orthogonal roles (#2177)

Merge the previous 8-agent persona matrix into 3 orthogonal roles to cut token waste while keeping defect-class coverage:

- Security & Concurrency Sentinel (weight 4.0): security + concurrency
- Logic & Correctness Reviewer (weight 3.0): logic / boundary / error handling
- System & Performance Reviewer (weight 3.0): performance + resource lifecycle + Linux semantic contracts

Embed the persona matrix in weighted_vote.py (DEFAULT_WEIGHTS) and drop the external persona_matrix.json loading path. Update the stage 1/2/4 skill docs accordingly and add Python artifacts to .gitignore.

Signed-off-by: longjin <longjin@DragonOS.org>
This commit is contained in:
LoGin
2026-08-11 18:15:06 +08:00
committed by GitHub
parent 974c9044ac
commit 1715d828a3
16 changed files with 87 additions and 95 deletions
+4 -4
View File
@@ -42,12 +42,12 @@ description: 分布式多智能体缺陷检测总控技能。基于输入随机
- `references/TROUBLESHOOTING.md`:常见失败排查。
- `references/METRICS.md`:质量指标与阈值调优。
- `references/finding_schema.json`Finding 对象 schema。
- `references/persona_matrix.json`角色矩阵与默认权重。
- 角色矩阵与默认权重:内置于 `scripts/weighted_vote.py``DEFAULT_WEIGHTS`3 角色)
## 执行顺序
1. **Stage 1 输入处理**:提取 diff,脱敏,按文件/块级生成 N 轮随机输入。
2. **Stage 2 并行评审**:⚠️ **必须使用 Agent 工具并行启动 8 个子智能体;每个子智能体从 `shuffled_passes.json` 随机抽取 1 个 pass,并按固定 persona 分工输出 findings,禁止手工编写 findings**
2. **Stage 2 并行评审**:⚠️ **必须使用 Agent 工具并行启动 3 个子智能体;每个子智能体从 `shuffled_passes.json` 随机抽取 1 个 pass,并按固定 persona 分工输出 findings,禁止手工编写 findings**
3. **Stage 3 证据融合**:将 JSON 发现项做语义去重与冲突标记。
4. **Stage 4 共识裁决**:按权重计算共识分,筛选过阈值问题并格式化输出。
5. **Stage 5 闭环学习**:记录建议被接受/拒绝情况,更新人格权重参考。
@@ -78,7 +78,7 @@ description: 分布式多智能体缺陷检测总控技能。基于输入随机
"description": "问题描述",
"fix_code": "建议修复代码",
"confidence": 0.0,
"agent": "Security Sentinel"
"agent": "Security & Concurrency Sentinel"
}
]
```
@@ -138,7 +138,7 @@ python3 .agents/skills/bug-hunter/scripts/run_pipeline.py \
## 规则
- ⚠️ **Stage 2 必须使用 Agent 工具并行启动 8 个子智能体,禁止手工编写 findings!**
- ⚠️ **Stage 2 必须使用 Agent 工具并行启动 3 个子智能体,禁止手工编写 findings!**
- 不允许跳过 Stage 3 和 Stage 4。
-`fix_code` 的发现项默认降权。
- 不报告纯格式问题或命名偏好。
@@ -154,7 +154,7 @@
```json
[
{
"agent": "Security Sentinel",
"agent": "Security & Concurrency Sentinel",
"status": "accepted",
"bucket_id": "BUG-001",
"reason": "fixed in follow-up patch",
@@ -173,8 +173,8 @@
```json
{
"suggested_weights": {
"Security Sentinel": 4.8,
"Diverse Reviewer A": 2.4
"Security & Concurrency Sentinel": 4.2,
"Logic & Correctness Reviewer": 3.1
}
}
```
@@ -182,4 +182,4 @@
`weighted_vote.py` 同时兼容:
- `{"suggested_weights": {...}}`
- `{"Security Sentinel": 5.0, "Concurrency Engineer": 4.0}`
- `{"Security & Concurrency Sentinel": 4.0, "Logic & Correctness Reviewer": 3.0}`
@@ -25,17 +25,17 @@ python3 .agents/skills/bug-hunter/scripts/redact_sensitive.py \
python3 .agents/skills/bug-hunter/scripts/shuffle_diff.py \
artifacts/redacted.diff \
--passes 8 \
--passes 4 \
-o artifacts/shuffled_passes.json
```
说明:`artifacts/shuffled_passes.json` 交给外部 Stage2 编排器。编排器应为每个 persona 随机抽取 1 个 `passes[*].diff`,并把 8 个 agent 的输出汇总为 `artifacts/raw_findings.json`
说明:`artifacts/shuffled_passes.json` 交给外部 Stage2 编排器。编排器应为每个 persona 随机抽取 1 个 `passes[*].diff`,并把 3 个 agent 的输出汇总为 `artifacts/raw_findings.json`
3) 并行评审(外部编排器)后写入 `raw_findings.json`
最小编排要求:
- 8 个 agent 并行启动
- 3 个 agent 并行启动
- 每个 agent persona 固定
- 每个 agent 从 `shuffled_passes.json` 随机选取 1 个 pass
- 每个 agent 只返回 JSON findings
@@ -76,7 +76,7 @@ python3 .agents/skills/bug-hunter/scripts/run_pipeline.py \
"description": "wait4 path forgets to propagate rusage error code",
"fix_code": "return_errno!(Errno::ECHILD);",
"confidence": 0.78,
"agent": "Diverse Reviewer C",
"agent": "Logic & Correctness Reviewer",
"pass_id": 4
}
]
@@ -95,12 +95,12 @@ python3 .agents/skills/bug-hunter/scripts/run_pipeline.py \
```json
[
{
"agent": "Security Sentinel",
"agent": "Security & Concurrency Sentinel",
"status": "accepted",
"bucket_id": "BUG-001"
},
{
"agent": "Diverse Reviewer B",
"agent": "Logic & Correctness Reviewer",
"status": "rejected",
"bucket_id": "BUG-007",
"reason": "not reproducible"
@@ -20,7 +20,7 @@
## Developer TODO
- [ ] `BUG-001` `major` `kernel/src/foo.rs:42` owner=`Concurrency Engineer`: ... | 修复建议: ...
- [ ] `BUG-001` `major` `kernel/src/foo.rs:42` owner=`Security & Concurrency Sentinel`: ... | 修复建议: ...
## Disputed Findings
@@ -24,7 +24,7 @@ python3 .agents/skills/bug-hunter/scripts/run_pipeline.py \
## 2. 推荐参数
- `--threshold 0.60`:默认通过阈值。
- `--passes 8`:随机化轮次,适合中大型变更。
- `--passes 4`:随机化轮次,适合中大型变更(应 ≥ agent 数 3 以保留输入多样性)
- `--strict-validation`:严格校验输入契约,发现错误立即失败。
- `--weights`:指定历史学习产出的权重文件。
- `--fail-on-severity critical`:若通过项出现 critical,命令返回非 0(CI 门禁)。
@@ -35,8 +35,8 @@
排查:
- 优先使用 `{"suggested_weights": {...}}`
- 或使用扁平映射 `{"Security Sentinel": 5.0, ...}`
- 检查角色名是否与 `persona_matrix.json` 一致
- 或使用扁平映射 `{"Security & Concurrency Sentinel": 4.0, ...}`
- 检查角色名是否与内置 3 角色一致:`Security & Concurrency Sentinel``Logic & Correctness Reviewer``System & Performance Reviewer`
## 5. 报告为空(Accepted findings = 0
@@ -1,18 +1,18 @@
[
{
"agent": "Security Sentinel",
"agent": "Security & Concurrency Sentinel",
"status": "accepted",
"bucket_id": "BUG-001",
"reason": "fixed by permission check patch"
},
{
"agent": "Diverse Reviewer C",
"agent": "Logic & Correctness Reviewer",
"status": "accepted",
"bucket_id": "BUG-002",
"reason": "error path fixed"
},
{
"agent": "Concurrency Engineer",
"agent": "Security & Concurrency Sentinel",
"status": "rejected",
"bucket_id": "BUG-003",
"reason": "cannot reproduce race"
@@ -9,7 +9,7 @@
"description": "wait4 path forgets to propagate rusage related errno",
"fix_code": "return_errno!(Errno::ECHILD);",
"confidence": 0.82,
"agent": "Diverse Reviewer C"
"agent": "Logic & Correctness Reviewer"
},
{
"file": "kernel/src/process/exit.rs",
@@ -19,7 +19,7 @@
"description": "missing errno propagation in wait4 rusage branch",
"fix_code": "return_errno!(Errno::ECHILD);",
"confidence": 0.76,
"agent": "Diverse Reviewer A"
"agent": "Logic & Correctness Reviewer"
},
{
"file": "kernel/src/sched/futex.rs",
@@ -29,7 +29,7 @@
"description": "futex wake path may race with waiter unlink sequence",
"fix_code": "",
"confidence": 0.58,
"agent": "Concurrency Engineer"
"agent": "Security & Concurrency Sentinel"
},
{
"file": "kernel/src/net/socket.rs",
@@ -39,7 +39,7 @@
"description": "socket credential check can be bypassed on fast path",
"fix_code": "if !task.can_access(sock_cred) { return_errno!(Errno::EACCES); }",
"confidence": 0.92,
"agent": "Security Sentinel"
"agent": "Security & Concurrency Sentinel"
},
{
"file": "kernel/src/mm/slab.rs",
@@ -49,7 +49,7 @@
"description": "allocator hot path does repeated small vector realloc",
"fix_code": "",
"confidence": 0.35,
"agent": "Performance Analyst"
"agent": "System & Performance Reviewer"
}
]
}
@@ -60,7 +60,7 @@ def main() -> int:
"--out-dir", default="artifacts", help="Artifact output directory"
)
parser.add_argument(
"--passes", type=int, default=8, help="Shuffle pass count for Stage1"
"--passes", type=int, default=4, help="Shuffle pass count for Stage1"
)
parser.add_argument(
"--threshold", type=float, default=0.6, help="Consensus threshold"
@@ -156,7 +156,7 @@ def main() -> int:
)
parser.add_argument("input", nargs="?", help="Input diff file (defaults to stdin)")
parser.add_argument(
"--passes", type=int, default=8, help="Number of shuffled passes to emit"
"--passes", type=int, default=4, help="Number of shuffled passes to emit"
)
parser.add_argument(
"--seed",
@@ -140,7 +140,7 @@ def normalize_item(
if require_agent:
errors.append(f"item[{idx}] agent is required in strict mode")
else:
normalized["agent"] = "Diverse Reviewer A"
normalized["agent"] = "Logic & Correctness Reviewer"
elif not isinstance(agent, str):
errors.append(f"item[{idx}] agent must be string")
else:
@@ -10,20 +10,21 @@ import sys
from typing import Any
# 3-agent persona matrix. Each agent owns a distinct, non-overlapping defect
# class; weights preserve the original "security/concurrency > correctness >
# system/performance" ordering while keeping the lower tiers at parity.
DEFAULT_WEIGHTS = {
"Security Sentinel": 5.0,
"Concurrency Engineer": 4.0,
"Performance Analyst": 3.0,
"Diverse Reviewer A": 2.0,
"Diverse Reviewer B": 2.0,
"Diverse Reviewer C": 2.0,
"Diverse Reviewer D": 2.0,
"Diverse Reviewer E": 2.0,
"Security & Concurrency Sentinel": 4.0,
"Logic & Correctness Reviewer": 3.0,
"System & Performance Reviewer": 3.0,
}
# Default agent name when a finding omits `agent`. Picked from the mid-weight
# general-correctness persona so missing-agent findings get a neutral vote.
DEFAULT_AGENT = "Logic & Correctness Reviewer"
ROOT = Path(__file__).resolve().parent
DEFAULT_PERSONA_MATRIX = ROOT.parent / "references" / "persona_matrix.json"
def load_json(path: str) -> dict[str, Any]:
@@ -57,16 +58,6 @@ def normalize_weight_map(payload: dict[str, Any]) -> dict[str, float]:
return mapped
def load_default_weights(persona_matrix: str | None) -> dict[str, float]:
path = Path(persona_matrix) if persona_matrix else DEFAULT_PERSONA_MATRIX
if path.exists():
data = load_json(str(path))
weights = normalize_weight_map(data)
if weights:
return weights
return DEFAULT_WEIGHTS.copy()
def main() -> int:
parser = argparse.ArgumentParser(description="Weighted vote for semantic buckets")
parser.add_argument("input", help="Buckets JSON from semantic_bucket.py")
@@ -77,11 +68,6 @@ def main() -> int:
"--threshold", type=float, default=0.60, help="Accept threshold in [0,1]"
)
parser.add_argument("--weights", help="Optional JSON file for persona weights")
parser.add_argument(
"--persona-matrix",
default=str(DEFAULT_PERSONA_MATRIX),
help="Persona matrix JSON used as default weights",
)
args = parser.parse_args()
if not (0.0 <= args.threshold <= 1.0):
raise ValueError("threshold must be in [0,1]")
@@ -90,7 +76,7 @@ def main() -> int:
buckets = data.get("buckets", [])
if not isinstance(buckets, list):
raise ValueError("buckets must be a list")
weights = load_default_weights(args.persona_matrix)
weights = DEFAULT_WEIGHTS.copy()
if args.weights:
weights.update(normalize_weight_map(load_json(args.weights)))
# Never allow non-positive or NaN-like effective weight in voting.
@@ -107,7 +93,7 @@ def main() -> int:
for finding in bucket.get("findings", []):
if not isinstance(finding, dict):
continue
agent = str(finding.get("agent", "Diverse Reviewer A"))
agent = str(finding.get("agent", DEFAULT_AGENT))
conf = max(0.0, min(1.0, float(finding.get("confidence", 0.5))))
weight = float(weights.get(agent, 1.0))
penalty = 0.9 if not str(finding.get("fix_code", "")).strip() else 1.0
@@ -14,7 +14,7 @@ description: bug-hunter 阶段 1 技能。负责提取代码改动、执行敏
1.`git diff --cached` 获取改动;若为空,回退为“upstream/origin/HEAD/HEAD~1”自适应基线:
`BASE_REF="$(git rev-parse --abbrev-ref --symbolic-full-name @{upstream} 2>/dev/null || git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || echo HEAD~1)"`,再执行 `git diff "$(git merge-base HEAD "$BASE_REF")"...HEAD`
2. 将 diff 传入 `scripts/redact_sensitive.py` 完成脱敏。
3. 将脱敏后的 diff 传入 `scripts/shuffle_diff.py --passes 8` 生成 8 轮随机序列。
3. 将脱敏后的 diff 传入 `scripts/shuffle_diff.py --passes 4` 生成 4 轮随机序列(应 ≥ Stage2 的 3 个 agent 以保留输入多样性)
4. 将输出 JSON 保存为 `artifacts/shuffled_passes.json`
## 验收
@@ -1,34 +1,35 @@
---
name: bug-hunter-stage2-parallel-review
description: bug-hunter 阶段 2 技能。负责将随机化后的 diff 按 persona 矩阵分发给 8 个子智能体并行评审,并收集统一 JSON 结果。
description: bug-hunter 阶段 2 技能。负责将随机化后的 diff 按 persona 矩阵分发给 3 个子智能体并行评审,并收集统一 JSON 结果。
---
# Stage 2 并行评审
## ⚠️ 强制约束 - 禁止手工替代
**本阶段必须使用 Agent 工具并行启动 8 个子智能体,严禁手工编写 findings!**
**本阶段必须使用 Agent 工具并行启动 3 个子智能体,严禁手工编写 findings!**
### 验证检查
- 必须调用 `Agent` 工具至少 **8**
- 每次 Agent 调用必须使用不同的 `description`
- 必须调用 `Agent` 工具恰好 **3**
- 每次 Agent 调用必须使用不同的 `description`(对应固定 persona
- 每个 Agent 必须从 `shuffled_passes.json``passes[*]` 中随机选择 1 个 pass 作为输入
- 每个 Agent 必须返回有效的 JSON findings
- 禁止直接写入或手工构造 `raw_findings.json`
### 违规检测
如果 `raw_findings.json` 是手工编写的(而非从 8 个 Agent 收集),后续阶段将拒绝处理。
如果 `raw_findings.json` 是手工编写的(而非从 3 个 Agent 收集),后续阶段将拒绝处理。
## 角色矩阵(固定 8 个)
## 角色矩阵(固定 3 个)
1. **Security Sentinel** - 权重 5.0 - 专注安全漏洞(ReDoS、注入、路径遍历)
2. **Concurrency Engineer** - 权重 4.0 - 专注并发问题(死锁、竞态、原子性)
3. **Performance Analyst** - 权重 3.0 - 专注性能问题(算法复杂度、内存、I/O)
4. **Diverse Reviewer A** - 权重 2.0 - 专注逻辑错误
5. **Diverse Reviewer B** - 权重 2.0 - 专注边界条件
6. **Diverse Reviewer C** - 权重 2.0 - 专注错误处理
7. **Diverse Reviewer D** - 权重 2.0 - 专注代码质量
8. **Diverse Reviewer E** - 权重 2.0 - 专注可维护性
3 个角色按缺陷类别正交划分,覆盖原 8 角色的全部关注点。每个角色职责互斥、可独立召回,避免低权重复角色造成的 token 浪费。
| # | Persona | 权重 | 合并自 | 关注类别 |
|---|---------|------|--------|----------|
| 1 | **Security & Concurrency Sentinel** | 4.0 | Security Sentinel + Concurrency Engineer | security + concurrency |
| 2 | **Logic & Correctness Reviewer** | 3.0 | Diverse Reviewer A/B/C | logic + boundary + error handling |
| 3 | **System & Performance Reviewer** | 3.0 | Performance Analyst + Diverse Reviewer D/E | performance + 资源生命周期 + Linux 语义契约 |
权重保留“安全/并发 > 正确性 > 系统与性能”的原始优先序,但下层两级对齐为 3.0,反映现代模型在宽域评审上的均衡能力。
## 执行步骤
@@ -40,11 +41,12 @@ description: bug-hunter 阶段 2 技能。负责将随机化后的 diff 按 pers
- 只能从 `passes[*].diff` 中选取评审输入
- 默认每个 Agent 抽取 1 个 pass
- 允许不同 Agent 抽到同一个 pass,但禁止所有 Agent 固定使用同一个 pass
- `shuffled_passes.json``passes` 数量应 ≥ 3(默认 `--passes 4`)以保留输入多样性
- 应记录每个 Agent 实际使用的 `pass_id`
### 步骤 2: 并行启动 8 个 Agent(必须)
### 步骤 2: 并行启动 3 个 Agent(必须)
在**单次响应**中并行调用 Agent 工具 8 次,每个使用不同的 persona 提示词。
在**单次响应**中并行调用 Agent 工具 3 次,每个使用不同的 persona 提示词。
每个 Agent 必须同时满足:
@@ -53,28 +55,28 @@ description: bug-hunter 阶段 2 技能。负责将随机化后的 diff 按 pers
- 只关注当前 persona 相关问题
- 输出统一 finding schema
推荐 persona 关注点
### 角色职责与提示词关注点
1. `Security Sentinel`
重点看权限边界、输入校验、越界访问、信息泄漏、路径遍历、注入面。
2. `Concurrency Engineer`
重点看锁顺序、竞态、原子性、可见性、死锁、丢唤醒。
3. `Performance Analyst`
重点看热点路径、复杂度、无谓拷贝、阻塞等待、缓存失效。
4. `Diverse Reviewer A`
重点看核心逻辑正确性、状态迁移、条件分支遗漏。
5. `Diverse Reviewer B`
重点看边界条件、空值/极值、长度与容量、资源上限
6. `Diverse Reviewer C`
重点看错误处理、返回码传播、回滚与清理路径。
7. `Diverse Reviewer D`
重点看 Linux 语义一致性、接口契约、行为兼容性
8. `Diverse Reviewer E`
重点看资源生命周期、引用关系、释放时机、泄漏风险
#### 1. `Security & Concurrency Sentinel`(权重 4.0
高危且需要对抗性/时序推理的两类缺陷,合并以提升单 agent 信息密度:
- **安全**:权限边界、输入校验、越界访问、信息泄漏、路径遍历、注入面(含 ReDoS)。
- **并发**锁顺序、竞态、原子性、可见性、死锁、丢唤醒。
#### 2. `Logic & Correctness Reviewer`(权重 3.0
“代码是否做了它应该做的事”的正交正确性域:
- **逻辑正确性**:核心逻辑、状态迁移、条件分支遗漏、控制流回归
- **边界条件**:空值/极值、长度与容量、资源上限、off-by-one。
- **错误处理**:返回码传播、错误路径回滚与清理、部分失败语义
#### 3. `System & Performance Reviewer`(权重 3.0
系统级健康度与效率,合并原 Performance Analyst 与 Diverse D/E
- **性能**:热点路径、复杂度、无谓拷贝、阻塞等待、缓存失效
- **资源生命周期**:引用关系、释放时机、RAII/作用域、泄漏风险。
- **Linux 语义一致性**:接口契约、行为兼容性、POSIX/内核语义对齐
### 步骤 3: 收集并合并结果
- 收集所有 Agent 返回的 JSON
- 为每条 finding 保留 `agent`
- 为每条 finding 保留 `agent`(值必须为上述 3 个角色名之一)
- 建议额外记录 `pass_id` 作为调试元数据;后续脚本会忽略未知字段
- 合并为单个 findings 数组或 `{"schema_version":"1.0","findings":[...]}` 包装对象
- 写入 `artifacts/raw_findings.json`
@@ -93,7 +95,7 @@ description: bug-hunter 阶段 2 技能。负责将随机化后的 diff 按 pers
"description": "error path forgets to release inode reference",
"fix_code": "drop(inode);",
"confidence": 0.81,
"agent": "Diverse Reviewer E",
"agent": "System & Performance Reviewer",
"pass_id": 3
}
]
@@ -113,7 +115,7 @@ description: bug-hunter 阶段 2 技能。负责将随机化后的 diff 按 pers
"description": "问题描述",
"fix_code": "修复代码片段",
"confidence": 0.9,
"agent": "Security Sentinel"
"agent": "Security & Concurrency Sentinel"
}
]
```
@@ -121,7 +123,7 @@ description: bug-hunter 阶段 2 技能。负责将随机化后的 diff 按 pers
## 约束
- ✅ **必须**使用 Agent 工具并行启动
- ✅ **必须**启动至少 8 个不同的 Agent
- ✅ **必须**启动恰好 3 个不同的 Agent(每个对应一个固定 persona
- ✅ **必须**从 `shuffled_passes.json` 中抽样输入,而不是直接评审原始 diff
-**每个** Agent 必须返回有效的 JSON
- ❌ **禁止**手工编写 findings
@@ -130,5 +132,5 @@ description: bug-hunter 阶段 2 技能。负责将随机化后的 diff 按 pers
- ❌ **禁止**所有 Agent 默认共用同一个 pass 作为输入
- 每个发现必须提供 `file:line`
- 置信度范围限定在 `[0, 1]`
- `agent` 字段必填,值必须是当前角色名
- `agent` 字段必填,值必须是上述 3 个角色名之一(缺失时由校验阶段回退到 `Logic & Correctness Reviewer`
- 纯风格建议直接过滤
@@ -9,7 +9,7 @@ description: bug-hunter 阶段 4 技能。负责对缺陷桶执行加权共识
1. 读取 `artifacts/buckets.json`
2. 根据 persona 权重运行 `scripts/weighted_vote.py --threshold 0.6`
- 默认`references/persona_matrix.json` 读取权重
- 默认使用脚本内置 `DEFAULT_WEIGHTS`3 个角色:Security & Concurrency Sentinel 4.0、Logic & Correctness Reviewer 3.0、System & Performance Reviewer 3.0
- 可用 `--weights` 叠加历史学习权重(兼容扁平映射与 `suggested_weights` 包装格式)。
3. 运行 `scripts/render_report.py` 生成 Markdown 报告。
4. 输出:
+4
View File
@@ -24,6 +24,10 @@ compile_commands.json
dadk-manifest.generated.toml
config/rootfs.generated.toml
# Python
__pycache__/
*.pyc
# initram
*.cpio
*.cpio.xz