1实现原理 · 为什么它能做到
机制核心不是『调用多个模型』而是『Claude 亲自下场』:SKILL.md 要求 Claude 在给出对手模型结论的同时给出自己的独立 critique 与综合裁决,并明确让用户知道这一点。
**Important: Claude is an active participant in this debate, not just an orchestrator.** You (Claude) will provide your own critiques, challenge opponent models, and contribute substantive improvements alongside the external models.
真实执行层是一个自带 Python CLI(debate.py + 5 个模块),skill 通过固定命令行把文档经 stdin 喂给脚本;脚本用 litellm 并行调用各家 provider 并解析 `[AGREE]` / `[SPEC]` 标记。
python3 "$(find ~/.claude -name debate.py -path '*adversarial-spec*' 2>/dev/null | head -1)" critique --models MODEL_LIST --doc-type TYPE <<'SPEC_EOF'
调用通道分两类:API key 走的模型经 litellm 统一出口;订阅制模型(ChatGPT/Google 账号)走本机 CLI 子进程,把文档拼进 argv/stdin 交给 codex / gemini。
cmd = [ "codex", "exec", "--json", "--full-auto", "--skip-git-repo-check", "--model", actual_model, "-c", f'model_reasoning_effort="{reasoning_effort}"', ]
收敛判定靠模型输出里的字面标记,而非语义判断:任何模型没出现 [AGREE] 就继续下一轮;对『前两轮就同意』强制加一轮 --press 反懒惰盘问。
**Handling Early Agreement (Anti-Laziness Check):** If any model says `[AGREE]` within the first 2 rounds, be skeptical.
长辩论可中断可回滚:会话状态、每轮 checkpoint、常用配置各自落盘,session 与 profile 分离存放。
SESSIONS_DIR = Path.home() / ".config" / "adversarial-spec" / "sessions" CHECKPOINTS_DIR = Path.cwd() / ".adversarial-spec-checkpoints"
可选的人工在环通道是 Telegram 机器人:每轮把摘要推给用户,轮询 60 秒内回复并把答复并入下一轮;无需回复则自动继续。
TELEGRAM_API: str = "https://api.telegram.org/bot{token}/{method}"
企业级替代通道:可切到 AWS Bedrock 让所有模型调用统一走 Bedrock 前缀(用于合规/网关场景),配置写回家目录的 config.json。
**When Bedrock mode is enabled, ALL model calls route through Bedrock** - no direct API calls are made.
成品交付由 Claude(宿主)完成而非脚本:skill 要求把最终文档打印并写入当前目录的 spec-output.md(PRD 续做则为 tech-spec-output.md),脚本本身不产出该文件。
1. Print the complete, polished document to terminal 2. Write it to `spec-output.md` in current directory
2核心能力
3外部依赖
| Kind | Dependency / Endpoint |
|---|---|
| package | litellm |
| api | 多家 LLM 供应商 API(经 litellm 统一出口) |
| cli | codex(OpenAI Codex CLI,走 ChatGPT 订阅) |
| cli | gemini(Google Gemini CLI,走 Google 账号) |
| api | AWS Bedrock(可选企业通道,全部调用可改走 bedrock/ 前缀) |
| network | Telegram Bot API(可选人工在环) |
| package | 开发依赖(pytest/pytest-cov/ruff/mypy/pre-commit) |
4管控建议与判定依据
- scripts/: 执行本地脚本或命令: python3 <…>/debate.py critique|diff|export-tasks|send-final|providers|focus
- 网络端点: 声明外部网络访问: litellm → 各供应商官方 API(OpenAI / Anthropic / Google / xAI / Mistral / Groq / O
- 本地工作目录: 文件读写与工件输出: ~/.claude/adversarial-spec/config.json(bedrock enable/disable/add-model/ali
5第二遍独立确认
- [ok] 多模型调用确实存在(非纯提示词) — models.py import litellm + from litellm import completion,标准路径 'response = completion(**completion_kwargs)';providers.py CODEX_AVAILABLE/GEMINI_CLI_AVAILABLE 用 shutil.which 探测。
- [ok] 订阅制模型走 CLI 子进程 — call_codex_model 构造 ['codex','exec','--json','--full-auto','--skip-git-repo-check','--model',…] 并 subprocess.run;call_gemini_cli_model 构造 ['gemini','-m',model,'-y'] 并把 prompt 经 input= 传入。
- [ok] Telegram 端点与轮询 — telegram_bot.py 'TELEGRAM_API: str = "https://api.telegram.org/bot{token}/{method}"' + 'with urlopen(req, timeout=30)'(urllib,非 requests);poll_for_reply(timeout=60 默认) 与 cmd_notify 均存在。
- [ok] Bedrock 模式确实可全量接管调用 — is_bedrock_enabled()/get_bedrock_config() 读 GLOBAL_CONFIG_PATH;models.py 在 bedrock 分支设置 AWS_REGION 并给 model 加 'bedrock/' 前缀;handle_bedrock_command 支持 status/enable/disable/add-model/remove-model/alias。
- [discrepancy] 『Maximum 10 rounds』是否由代码强制 — SKILL.md 写 'Maximum 10 rounds per cycle (ask user to continue if reached)',但对 debate.py 全文检索无任何轮数上限常量/参数(--round 只是当前轮号,--rounds 只用于 send-final 汇报)。结论:轮数上限是提示词纪律,代码不设护栏,已在 how_it_works 第 4 条与 risks 中如实标注。
- [discrepancy] 『Write it to spec-output.md』的归属 — 第一遍曾按『脚本写文件』记录;第二遍在 scripts/ 全目录检索 'spec-output' 零命中,确认该写入由宿主 Write 工具完成(SKILL.md allowed-tools: Bash, Read, Write, AskUserQuestion)。已改写为『成品交付由 Claude 完成』并据此调整 file_writes。
- [discrepancy] Python 版本要求一致性 — SKILL.md Requirements 写 'Python 3.10+ with `litellm` package installed',而 pyproject.toml 写 requires-python = ">=3.9" 且 ruff target-version = "py39"、mypy python_version = "3.10"。属文档与打包元数据的轻微不一致(不影响功能判定)。
- [ok] 成本追踪是否真有实现(非仅文档) — providers.py MODEL_COSTS 价目表 + DEFAULT_COST;debate.py 'Cost: ${cost_tracker.total_cost:.4f}' 与 JSON 输出 'cost': {total, input_tokens, output_tokens, by_model}。
6结论
89a0a6e57cca0f6a…