diff --git a/decisions/ADR-0008-archetype-v2-ten.md b/decisions/ADR-0008-archetype-v2-ten.md new file mode 100644 index 0000000..0de8fc3 --- /dev/null +++ b/decisions/ADR-0008-archetype-v2-ten.md @@ -0,0 +1,53 @@ +# ADR-0008: archetype v2——补 planner/judge/researcher,十原型封顶 + +- status: accepted +- date: 2026-08-18 +- deciders: owner 质询 + AI 独立回复 + +## 背景 + +owner 三个质询击穿 v1 七分类: +1. 工作任务的源头是谁?人类只给意图,谁定波次/设计测试/写工作卡?谁保证 `make check` 真有效? +2. 遇到决策分歧怎么办?不可能事事上升人类。 +3. researcher 可加;且听说模型可专门作 tool 干"找代码翻文档",省 worker 上下文。 + +## 决策 + +### 1. planner(认同,从 orchestrator 拆出) + +- 边界:**planner=设计时**(意图→波次计划+工作卡+测试规格),orchestrator=运行时(分派/监控/汇总)。混在一起会让"既排计划又管执行"失去制衡。 +- 工作流位置:planner 产计划 → **计划须过 checker 评审**(防弱标准)→ checker 按规格**编写真测试代码**(测试作者权归 checker,builder 永不写自身验收——no-self-test 的组织级延伸)→ builder 实现 → checker 判决。 +- checker 相应扩权:可写 tests/** 不可写实现(路径级权限分离,tiered_policy pattern 可执行)。 +- **make check 有效性的元答案**:不靠"再多加一个审查者"(审查塔无限回归),靠客观指标短路——mutation score(testing.yaml T-10)验证测试杀变异能力,差分测试(T-09)验证行为等价。测试好不好,指标说话。 +- 新约束:planner 与 builder 不得同一声明(标准制定者不可施工)。 + +### 2. judge(认同,但严格划界) + +- 管辖权来自团队声明的授权域(枚举:测试抖动判定/评审分歧/规格歧义解释),**越域即改判"上升人类"**;治理/安全/生产动作永远人类(RL-1)。 +- 独立性:validate 强制仲裁者模型别名与争议双方(builder/checker 成员)均不同——不能是任何一方同脑。 +- 判决=结构化输出(decision+rationale+jurisdiction_basis),落 decision_made 事件形成判例库;owner 可推翻任何判决(破玻璃同哲学)。 +- 升级阶梯:组内分歧 → judge(域内,自动)→ owner(域外/政策/生产,唯一人工口)。 + +### 3. researcher(认同,as_tool 是关键形态) + +- 权限特征:读外部网络+读任意仓库,只产结构化报告(每条结论带 source 引用),不写仓库不碰生产。 +- **上下文经济**:以 `expose.as_tool: true` 被 builder/planner 调用——全文留在 researcher 自己的上下文,worker 只收结论+引用。这正是"模型作为 tool 翻文档找代码"的机制化,schema 原生支持(io_contract 约束往返体积)。 +- 修正一处设计错误:agent-as-tool 消费发生在 team 组装层(members.as_tool),agent 的 capabilities.tools 只收 tool: 注册项。 + +### 4. 克制:拒绝清单(十原型封顶) + +| 拒绝加 | 理由 | +|---|---| +| critic | =checker(验收判决) | +| negotiator | =orchestrator 的协调协议,非信任边界 | +| memory-keeper | 记忆属 agent.memory + 事件流,非角色 | +| auditor | =curator(治理审计)+ judge(分歧裁决)组合 | +| teacher/trainer | 技能演进=Skill 自演进机制 + handoff.skill-extract,非角色 | + +**封顶规则:提出第 11 个原型必须走 ADR,论证其权限/凭据/审计特征无法被现有十类覆盖。** + +## 后果 + +- schema enum 十项;validate 增 judge 独立性强制;registry 增三个 proposed 条目(wave-planner/arbiter/researcher-code,未经实战,首引用时提级)+ web_search 工具。 +- planner 用的 planning skill、judge 管辖域的正式枚举 → 留待 teams 轮(下一轮讨论)定义。 +- GOVERNANCE AR-8 intent 同步更新。 diff --git a/registry/agents/arbiter.yaml b/registry/agents/arbiter.yaml new file mode 100644 index 0000000..35328eb --- /dev/null +++ b/registry/agents/arbiter.yaml @@ -0,0 +1,36 @@ +id: arbiter +version: 0.1.0 +status: proposed # 首个 team 引用时随 PR 提级 +archetype: judge +role: "域内分歧仲裁:测试抖动判定/评审分歧/规格歧义解释;输出=判决+理由+管辖依据,可被 owner 推翻" +identity: + prompt_ref: identities/arbiter.md +model: + alias: coder-deep # 须与争议双方(builder/checker)别名不同——validate 强制 + temperature: 0.1 + reasoning_level: high +capabilities: + skills: [] + tools: [tool:read_file] # 只读:看争议材料,不改任何东西 +permissions: + inherit: org-default + mode: strict + overrides: + - {tools: [tool:read_file], pattern: "*", action: allow} +memory: + types: [semantic] # 判决理由入语义记忆形成判例库 + retention: 365d +guardrails: + output_schema_strict: true # 判决 schema:{decision, rationale, jurisdiction_basis, reversible_by} + forbidden: [no-secret-output, no-bypass-gate] + post_conditions: + - "[llm] 每份判决注明管辖依据(团队授权域条目),越域即改判'上升人类'" + - "[ci] 判决写入 decision_made 事件(含双方主张摘要)" +io_contract: + input: {schema_ref: schemas/dispute-in.json} + output: {schema_ref: schemas/verdict.json} +expose: + as_tool: true + a2a_card: none +workspace: + scope: private diff --git a/registry/agents/researcher-code.yaml b/registry/agents/researcher-code.yaml new file mode 100644 index 0000000..f76447d --- /dev/null +++ b/registry/agents/researcher-code.yaml @@ -0,0 +1,36 @@ +id: researcher-code +version: 0.1.0 +status: proposed # 首个引用时随 PR 提级 +archetype: researcher +role: "检索即工具:找代码/翻文档/查资料,返回结构化结论+引用;全文留在自己上下文,worker 只收摘要(上下文经济)" +identity: + prompt_ref: identities/researcher-code.md +model: + alias: coder-fast # 检索是广度任务,用快档;结论质量靠引用可回溯保证 + temperature: 0.2 + reasoning_level: low +capabilities: + skills: [] + tools: [tool:web_search, tool:read_file] +permissions: + inherit: org-default + mode: normal + overrides: + - {tools: [tool:read_file], pattern: "*", action: allow} # 只读任何仓库 +memory: + types: [semantic] # 检索结论缓存,同题复用 + retention: 30d +guardrails: + output_schema_strict: true # findings schema:每条结论必附 source 引用 + forbidden: [no-secret-output] + post_conditions: + - "[llm] 无引用支撑的结论必须标注 confidence: low" + - "[ci] 输出过 findings schema(含 sources 数组非空校验)" +io_contract: + input: {schema_ref: schemas/question-in.json} + output: {schema_ref: schemas/findings.json} +expose: + as_tool: true # 核心用法:被 builder/planner 当工具调用 + a2a_card: auto +workspace: + scope: private diff --git a/registry/agents/wave-planner.yaml b/registry/agents/wave-planner.yaml new file mode 100644 index 0000000..8398d47 --- /dev/null +++ b/registry/agents/wave-planner.yaml @@ -0,0 +1,39 @@ +id: wave-planner +version: 0.1.0 +status: proposed # 首个 team 引用时随 PR 提级 approved(本轮为定义落盘,未经实战) +archetype: planner +role: "工作源头:意图→波次计划+工作卡+测试规格;只写计划产物,不写实现" +identity: + prompt_ref: identities/wave-planner.md +model: + alias: coder-deep # 规划是重认知任务,用旗舰档 + temperature: 0.3 + reasoning_level: high +capabilities: + skills: [] # planning skill 待 teams 轮设计时随流程定义 + tools: [tool:read_file, tool:write_file, tool:web_search] + # 检索增强(researcher-code)经 team 组装层以 as_tool 接入,不进本白名单(agent≠tool) +permissions: + inherit: org-default + mode: normal + overrides: + # 仅允许写顶层 plans/|cards/|specs/(相对本 agent 工作区根);其余显式 deny + # (normal 模式下 HIGH 仅 ask 不够;顶层锚定避免 src/plans/ 之类任意深度绕过) + - {tools: [tool:write_file], pattern: "re:^(?!(plans|cards|specs)/).*$", action: deny, severity: HIGH} +memory: + types: [semantic] + retention: 90d +guardrails: + output_schema_strict: true + forbidden: [no-secret-output] + post_conditions: + - "[llm] 每张工作卡含验收引用([ci] 优先,指向 testing.yaml 条目或 golden)" + - "[llm] 测试规格独立于实现描述(描述'验证什么'而非'怎么实现')" +io_contract: + input: {schema_ref: schemas/intent-in.json} + output: {schema_ref: schemas/wave-plan.json} +expose: + as_tool: true # orchestrator/judge 可调"补一个波次计划" + a2a_card: none +workspace: + scope: team diff --git a/registry/tools/web_search.yaml b/registry/tools/web_search.yaml new file mode 100644 index 0000000..071b1e2 --- /dev/null +++ b/registry/tools/web_search.yaml @@ -0,0 +1,10 @@ +id: web_search +version: 1.0.0 +kind: builtin +severity: LOW +side_effects: [network] +input: {schema_ref: schemas/query-in.json} +output: {schema_ref: schemas/search-results.json} +implementation: {repo: openJiuwen-ai/jiuwenswarm, path: builtin/web_search} +status: approved +owner: team:governance-core diff --git a/scripts/validate.py b/scripts/validate.py index 321d2cd..24b435b 100644 --- a/scripts/validate.py +++ b/scripts/validate.py @@ -16,6 +16,11 @@ def fail(msg: str) -> None: errors.append(msg) +def alias_of(a: dict): + """读取 agent 的 model.alias;model 为 null/缺失 时返回 None(不抛异常)""" + return (a.get("model") or {}).get("alias") + + def load_yaml(path: Path): try: with open(path, encoding="utf-8") as f: @@ -58,7 +63,7 @@ def frontmatter(path: Path) -> dict: ACTIVE = {"approved", "active"} # ---- agent 校验 ---- -ARCHETYPES = {"builder", "checker", "orchestrator", "curator", "interface", "observer", "operator"} +ARCHETYPES = {"builder", "planner", "checker", "judge", "orchestrator", "curator", "interface", "observer", "researcher", "operator"} for aid, a in agents.items(): arch = a.get("archetype") if arch not in ARCHETYPES: @@ -80,7 +85,7 @@ def frontmatter(path: Path) -> dict: fail(f"agent:{aid} 引用不存在的 tool:{tid}") elif tools[tid].get("status") not in OK: fail(f"agent:{aid} 引用未批准的 tool:{tid} (status={tools[tid].get('status')})") - alias = a.get("model", {}).get("alias") + alias = alias_of(a) if alias and alias not in model_aliases: fail(f"agent:{aid} 引用未注册的模型 alias: {alias}") @@ -103,6 +108,22 @@ def frontmatter(path: Path) -> dict: fail(f"team:{tid} 引用不存在的 agent:{aid}") elif agents[aid].get("status") not in OK: fail(f"team:{tid} 引用未批准的 agent:{aid}") + # AR-8 v2:judge 独立性——仲裁者模型别名不得与争议双方(builder/checker 成员)相同 + judges = [a for a in member_ids if agents.get(a, {}).get("archetype") == "judge"] + disputants = [a for a in member_ids if agents.get(a, {}).get("archetype") in ("builder", "checker")] + for j in judges: + ja = alias_of(agents[j]) + if not ja: + fail(f"team:{tid} 仲裁者 {j} 缺少 model.alias,无法验证裁决独立性(ADR-0008)") + for d in disputants: + if not alias_of(agents[d]): + fail(f"team:{tid} 争议方 {d} 缺少 model.alias,无法验证裁决独立性(ADR-0008)") + for j in judges: + ja = alias_of(agents[j]) + for d in disputants: + da = alias_of(agents[d]) + if ja and da and ja == da: + fail(f"team:{tid} 仲裁者 {j} 与争议方 {d} 模型别名相同({ja}),裁决不独立(ADR-0008)") # AR-9 验证链:含 builder 的团队必须独立 checker 验收 + 外部审计 builders = [a for a in member_ids if agents.get(a, {}).get("archetype") == "builder"] ver = t.get("verification", {}) @@ -118,9 +139,9 @@ def frontmatter(path: Path) -> dict: fail(f"team:{tid} 的验收者 agent:{c} 不是 checker 原型(AR-8/9)") if c in builders: fail(f"team:{tid} 中 agent:{c} 既是 builder 又是验收者(利益分离)") - ca = agents[c].get("model", {}).get("alias") + ca = alias_of(agents[c]) for b in builders: - ba = agents[b].get("model", {}).get("alias") + ba = alias_of(agents[b]) if ca and ba and ca == ba: fail(f"team:{tid} 验收者 {c} 与 builder {b} 使用相同模型别名 {ca}(独立性不足)") ea = ver.get("external_audit", {})