关于 arena
arena 是 Claude Code 技能,让多个子 Agent 围绕同一任务进行竞赛。参赛者分轮提出、攻击并修订方案,由裁判决定胜者;编排者不参赛也不评判。
主要功能
- 01运行可配置规模的 Agent 竞赛
- 02向参赛者提供相同任务简报
- 03用策略卡引导不同解题方式
- 04通过攻防轮次迭代方案
- 05裁判评选并记录最终方案
适用场景
- 01比较针对同一任务的多个方案
- 02改进已被否定的回答
- 03探索请求的不同处理策略
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jakeschincariol/ arena-skill --skill arena安装后在 Claude Code 中输入/arena确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
bracket.pyrubric.mdstrategies.json详细介绍
arena
当 Claude 一直给出糟糕的答案时,与其一次次追问,不如来一场锦标赛: N 个子 Agent 收到完全相同的任务,各自采用不同的推理模式、工作流和策略来完成,然后在淘汰赛中互相交锋,直到只剩一个解决方案。你是编排者,绝不参赛,也绝不评判。
用户在 /arena 后输入的内容:$ARGUMENTS
如果为空,或仍然像是占位符,就表示没有传入内容:从对话中获取任务。
工具
所有记录工作都通过此技能文件夹中的 bracket.py 完成:
python3 "${CLAUDE_SKILL_DIR}/bracket.py" <command>
下文中的 ARENA 指的就是该命令。如果路径看起来尚未展开,请使用 Claude Code 在此技能顶部打印的“此技能的基础目录”。状态保存在当前目录的 .arena/<run>/arena.json 中,init 之后的每条命令都会通过 .arena/LATEST 找到它。
步骤 1:确定规模;如果用户没有要求,就先征求同意
从请求中读取标志。所有非标志内容都是任务。
| 标志 | 含义 |
|---|---|
--agents N | N 个参赛者。默认值为 100。 |
--quick | 16 个参赛者。日常使用的设置。 |
--seed S | 固定卡片和配对。默认随机,并会记录。 |
--wave W | 每波的子 Agent 数量。默认值为 10。只有用户提高了 Claude Code 的限制时才提高此值。 |
完全没有任务文本时:任务就是用户在此对话中最近一次提出的请求,而你对此请求的上一个回答就是需要超越的基线。
运行 ARENA plan --agents N(或 --quick)。它会打印轮次、子 Agent 调用次数和波次。
- 用户要求开启竞技场(输入了
/arena、说了“arena”或要求让它们竞争): 用一行说明规模,例如“100 个 Agent,7 轮,595 次子 Agent 调用”,然后开始。 - 此技能因用户不满意而触发(“错了”“再试一次”“答案不好”),但用户从未提到竞技场:花费任何资源前先询问一次。提供三个选项:完整竞技场(100 个 Agent,595 次子 Agent 调用)、
--quick(16 个 Agent,91 次调用),或普通重试。等待用户答复。
子 Agent 会将工作写入当前目录的 .arena/。在默认权限模式下,每个文件都需要批准一次,大规模运行时会有数百次批准。在启动阶段之前,建议用户在本次运行中使用 accept-edits 模式(Shift+Tab)。不要自行更改用户的设置。
步骤 2:编写任务文件
此步骤决定结果。**子 Agent 看不到此对话。**每个参赛者、攻击者和评审者都只了解任务文件中的内容,因此要让 .arena/task.md 内容完整、独立:
- 尽可能使用用户自己的措辞描述请求。
- 写入用户在对话任何位置提出的所有要求和约束:受众、长度、格式、语气、技术栈、截止时间,以及哪些内容不得更改。
- 提供陌生人理解任务所需的上下文:相关文件的绝对路径、粘贴的数据、产品是什么、代码库中的约定。
- 如果用户说明了完成标准,也要写入。
- 如果有需要超越的答案:用用户自己的原话写明他们不满意的地方。
如果有用户不满意的先前回答,请将其逐字写入 .arena/baseline.md。
不要添加用户从未提出的要求。不要把你自己认为正确的答案写进去:这会让 100 个 Agent 都朝同一个方向努力,适得其反。
步骤 3:初始化
ARENA init --agents N --seed S --task-file .arena/task.md --baseline-file .arena/baseline.md
没有需要超越的答案时,不要添加 --baseline-file;如需随机值,则不要添加 --seed。init 会将任务复制到运行文件夹,为每位参赛者发放一张不同的策略卡且不重复,为第 1 轮配对,并写入 arena.json。
第 4 步:循环
始终使用 ARENA next 推进。它会读取磁盘上的状态,并告诉你下一步要做什么以及确切命令。
每个运行子 Agent 的阶段都遵循相同的流程:
-
ARENA prompts <phase>会为每项任务生成一份简报,并按波次列出尚待执行的任务。 -
一次只启动一个波次:发送一条消息,其中包含该波次每项任务各一个 Agent 工具调用(在较旧版本的 Claude Code 中,Agent 工具称为 Task)。每次调用的参数如下:
subagent_type:general-purposedescription:arena <job id>prompt:Read <prompt path> and follow it exactly. It is your whole brief.- 如果工具提供此选项,
run_in_background设为 false,以便整个波次一起返回。
等待该波次中的所有 Agent 都回复后,再启动下一个波次。
-
最后一波结束后,运行
ARENA next。如果有输出缺失,它会让你返回同一阶段,而prompts只会列出缺失的任务。重新运行这些任务一次。如果某项任务连续失败两次,就在它的每个输出文件中写入单独一行NO OUTPUT(ARENA check <phase>会列出这些文件),然后继续。缺失的攻击视为没有攻击。缺失的解决方案会导致该场比赛判负。裁判连续失败两次时,要重新运行第三次:绝不要自行判定比赛结果。
next 会按以下顺序推进:
- spawn,一次:每位参赛者都为任务编写自己的解决方案。
- 然后是每一轮:attack(每场比赛两项)→ defend(每场比赛两项)→ judge(每场比赛一项)→
ARENA collect→ARENA advance。 - final,仅在存在基准答案时运行一次:由裁判盲评冠军方案与用户拒绝的答案。然后运行
ARENA collect。 next显示 DONE 后,进入第 5 步。
每次 advance 后,向用户发送一行消息,例如:“第 2 轮结束:还剩 25 个,共 100 个。”不要发送其他内容。
绝不要在聊天中粘贴配对信息、攻击内容、裁决结果或解决方案。
分波次运行的原因:Claude Code 默认最多同时运行 10 次工具调用(由
CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY 设置)。因此,100 个 Agent 的 spawn 阶段需要分 10 波,每波 10 个;100 个参赛者的竞技场总共需要运行 70 波,最后检查时则为 71 波。
第 5 步:结果
运行 ARENA winner,然后读取它所打印路径中的冠军解决方案文件。这是整个运行过程中唯一可以读取的解决方案文件。向用户提供:
- 获胜解决方案,完整呈现。
- 获胜原因:以简短列表列出它经受住的攻击,依据
winner的信息。再用一行写出它的卡片信息(推理模式 + 工作流 + 策略)。 - 轮数:例如“共 7 轮,100 个 Agent 参赛,最终剩下 1 个。”
- 与被拒绝的答案相比(如果有):如实列出最终检查的分数。如果旧答案得分更高,要明确说明并展示两者的分数。
- 完整记录所在位置:运行文件夹。
如果解决方案会更改用户项目中的文件,不要应用它。询问用户:要应用它,还是要更改它?
编排器规则
- 你负责运行锦标赛,不参与竞争、攻击或裁判,也绝不自行挑选赢家。
collect会记录裁判的判定结果。只有在用户提出要求时,才使用record修正记录。 - 每个子 Agent 都通过自己的简报获取任务;
prompts会基于唯一的任务文件为每个 Agent 生成简报,内容逐字节相同。绝不要为某个 Agent 改述任务,也不要在其调用中添加提示。 - 运行过程中不要读取解决方案、攻击内容或裁决结果。这些文件有数百份。状态保存在磁盘上,只需使用
next、status和pairings。 - 如果运行过程中上下文被压缩,不会丢失任何信息。运行
ARENA status,然后运行ARENA next,继续执行即可。 - 所有
ARENA命令都必须在运行init的目录中执行。.arena/LATEST位于该目录下。 - 子 Agent 只能在
.arena/中写入文件。如果有子 Agent 在其他位置写入文件,要告知用户。 - 如果用户要求停止,就停止。
ARENA status会显示当前进度,之后可以运行ARENA next恢复。
提示词模板
bracket.py 会填入这些内容(即 {{placeholders}}),并为每项任务写一份简报,因此这里的内容就是每个子 Agent 收到的完整指示。切勿为单个 Agent 编辑简报。
竞争者,在生成阶段
You are competitor {{agent}} in an arena of {{n}}. All {{n}} competitors got the exact same task, word for word. The only thing that makes you different is the strategy card below: it decides how you attack the task. Your solution will be attacked by other competitors and scored by a judge, round after round, until one solution is left.
=== THE TASK (identical for every competitor) ===
{{task}}
=== END OF THE TASK ===
{{baseline_note}}
=== YOUR STRATEGY CARD ===
Reasoning mode: {{reasoning_name}}. {{reasoning_how}}
Workflow: {{workflow_name}}. {{workflow_how}}
Strategy: {{strategy_name}}. {{strategy_how}}
=== END OF THE CARD ===
How to work:
1. Use the card for real. Think in the reasoning mode, go through the workflow's steps in order, and let the strategy settle every trade-off. A generic answer with the card's name on top will lose.
2. Meet every requirement the task states. The judge scores you against the task, not against your card.
3. You cannot ask the user anything. Where the task is ambiguous, take the most reasonable reading and state it in a short Assumptions section.
4. Expect attacks: concrete flaws, counterexamples, missed requirements. Close those holes before you submit.
5. Do not create, edit or delete anything outside {{arena_dir}}. Read whatever the task points to. If the task is about code, put the exact changes in your solution (full files or a unified diff) instead of applying them. If your workflow needs scratch space, use {{arena_dir}}/scratch/{{agent}}/.
Write your solution to {{out}}: the solution itself, written for the person who asked. Leave out your drafts and your working. Keep a checklist, tests or trade-off notes only where they help that person use the answer. Say nothing about the arena, your card or your competitor number: the judges score the work blind.
When the file is written, reply with this one line and nothing else:
DONE {{agent}} <number of words in your solution>
攻击者,在每一轮中
You are competitor {{agent}} in round {{round}} of an arena, match {{match}}. Your opponent is {{target}}. Only one of you gets out of this match. Right now your job is to attack your opponent's solution.
=== THE TASK (identical for every competitor) ===
{{task}}
=== END OF THE TASK ===
Your strategy card is the lens you look for flaws through:
Reasoning mode: {{reasoning_name}}. {{reasoning_how}}
Workflow: {{workflow_name}}. {{workflow_how}}
Strategy: {{strategy_name}}. {{strategy_how}}
Read your opponent's solution: {{target_solution}}
You may read your own for comparison: {{own_solution}}. Attack theirs on its merits against the task, not for being different from yours.
Find the real problems:
- WRONG: factual errors, logic errors, bugs, false claims.
- MISSING: a requirement the task states that it skips or only half meets. Quote the requirement.
- BREAKS: a concrete input, scenario or edge case where it fails. Give the exact counterexample.
- VAGUE: a place where the user could not act on it without guessing.
Rules:
- Every attack must be specific and checkable: point at the exact part, say what is wrong and why.
- No praise, no summary, and no style nitpicks unless they stop the user from using it.
- Do not invent requirements the task does not state. Do not attack the approach, only what it gets wrong.
- At most 7 attacks, strongest first. If you only find 2 real ones, write 2.
- Label each FATAL (wrong or unusable for the task), MAJOR (a real gap) or MINOR.
- Do not create, edit or delete any file except the one below.
Write the attacks to {{out}} in this format:
ATTACK 1 [FATAL|MAJOR|MINOR] <one-line title>
Where: <quote or location>
Problem: <what is wrong, with the counterexample or the missed requirement>
(and the same for each attack after that)
When the file is written, reply with this one line and nothing else:
ATTACKED {{target}} <number of attacks> (<number that are FATAL> fatal)
防御者,在每一轮中
You are competitor {{agent}} in round {{round}} of an arena, match {{match}}. Your opponent {{attacker}} has attacked your solution. Now you defend it and revise it. A judge will score your revised solution against your opponent's, including how well each of you dealt with the attacks you took.
=== THE TASK (identical for every competitor) ===
{{task}}
=== END OF THE TASK ===
Your strategy card. Keep your approach: it is why you are still here.
Reasoning mode: {{reasoning_name}}. {{reasoning_how}}
Workflow: {{workflow_name}}. {{workflow_how}}
Strategy: {{strategy_name}}. {{strategy_how}}
Your current solution: {{own_solution}}
The attacks against it: {{attacks}}
Do this:
1. Take every attack in turn and decide honestly. CONCEDE if it is right, and fix it. REBUT if it is wrong, and show why with evidence from the task, your solution or a concrete check. A rebuttal that only insists you are right counts as a concession. Conceding a real flaw and fixing it scores better than defending it.
2. Write your revised solution: the complete solution, standalone, with every conceded point fixed. The judge reads only this file, so never write "see the previous version". Say nothing about the arena or your card.
3. Fix what was attacked and anything the attacks made you notice. Do not start again from scratch and do not copy your opponent.
4. If the attacks file is empty or says NO OUTPUT, you were not attacked: write NO ATTACKS RECEIVED as your defense, and resubmit your solution with only the fixes you know it needs.
5. Do not create, edit or delete anything outside {{arena_dir}}.
Write your point-by-point defense to {{defense_out}} in this format:
ATTACK 1: CONCEDE|REBUT. <one to three lines>
(one entry per attack)
Write your revised solution to {{solution_out}}.
When both files are written, reply with this one line and nothing else:
DEFENDED {{agent}} conceded <n> rebutted <n>
评审,每场比赛一份
You are the judge of match {{match}}, round {{round}}, in an arena. Two solutions to the same task have fought: each attacked the other, then defended and revised its own. Score both against the rubric. The one with the higher score goes through and the other is eliminated.
=== THE TASK (identical for every competitor) ===
{{task}}
=== END OF THE TASK ===
Read the rubric first: {{rubric}}
Solution {{first}}
- revised solution: {{first_solution}}
- attacks it received: {{first_attacks}}
- its defense: {{first_defense}}
Solution {{second}}
- revised solution: {{second_solution}}
- attacks it received: {{second_attacks}}
- its defense: {{second_defense}}
How to judge:
1. Read both revised solutions in full before you score either one.
2. For every attack, check the revised solution yourself and call it FIXED, REBUTTED (only if the rebuttal is actually right) or STANDING. A defense that says "fixed" is not proof. Look.
3. Look for flaws the attackers missed, too.
4. Score each criterion from 0 to 10 using the rubric's anchors. Set fatal to true only for a flaw you have verified that makes the solution wrong or unusable for the task.
5. The winner is the higher weighted total (the weights are in the rubric). A fatal solution cannot beat one that is not fatal. On an exact tie, fewer standing attacks wins, then higher correctness.
6. Judge the work, not the writing about the work. Length is not quality. You do not know either competitor's strategy and should not guess it.
7. Do not create, edit or delete any file except the verdict. If the task is code and running something settles an attack, do it only inside {{arena_dir}}/scratch/judge-{{match}}/, never in the user's project.
Write this JSON, and nothing else, to {{out}}:
{
"match": "{{match}}",
"scores": {
"{{first}}": {"correctness": 0, "completeness": 0, "specificity": 0, "robustness": 0, "clarity": 0, "fatal": false},
"{{second}}": {"correctness": 0, "completeness": 0, "specificity": 0, "robustness": 0, "clarity": 0, "fatal": false}
},
"winner": "{{first}} or {{second}}",
"reason": "one sentence: the decisive difference",
"survived": ["each attack the winner took and beat, in a few words"],
"standing": {"{{first}}": ["attacks still standing"], "{{second}}": ["attacks still standing"]}
}
When the file is written, reply with this one line and nothing else:
WINNER <winner id> <winner total>-<loser total>
最终检查,仅当已有答案可供超越时
You are the final check in an arena. {{n}} competitors fought over one task and a single solution survived {{rounds}} rounds. Before it goes back to the user, it is compared with the answer the user already rejected. You are not told which of the two is which. Score what is in front of you. Either one can win.
=== THE TASK ===
{{task}}
=== END OF THE TASK ===
Read the rubric first: {{rubric}}
Solution X: {{x_solution}}
Solution Y: {{y_solution}}
How to judge:
1. Read both in full before you score either.
2. Attack both yourself: find the strongest concrete flaws in each, the way a hostile expert would. For the robustness score, judge how well each one holds up against those attacks.
3. Score each criterion from 0 to 10 using the rubric's anchors. Set fatal to true only for a flaw you have verified that makes a solution wrong or unusable for the task.
4. The winner is the higher weighted total. A fatal solution cannot beat one that is not fatal.
5. Judge the work, not the writing about the work. Length is not quality.
6. Do not create, edit or delete any file except the verdict.
Write this JSON, and nothing else, to {{out}}:
{
"scores": {
"X": {"correctness": 0, "completeness": 0, "specificity": 0, "robustness": 0, "clarity": 0, "fatal": false},
"Y": {"correctness": 0, "completeness": 0, "specificity": 0, "robustness": 0, "clarity": 0, "fatal": false}
},
"winner": "X or Y",
"reason": "one sentence: the decisive difference",
"fixed": ["each thing the winner gets right that the other gets wrong, in a few words"]
}
When the file is written, reply with this one line and nothing else:
FINAL <X or Y> <X total>-<Y total>