ai-battle 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -140
- package/ai-battle.sh +200 -19
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -10,17 +10,41 @@
|
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://www.npmjs.com/package/ai-battle"><img src="https://img.shields.io/npm/v/ai-battle?style=flat-square&logo=npm&logoColor=white&color=CB3837" alt="npm version" /></a>
|
|
13
|
+
<a href="https://github.com/Alfonsxh/ai-battle/actions/workflows/publish.yml"><img src="https://img.shields.io/github/actions/workflow/status/Alfonsxh/ai-battle/publish.yml?style=flat-square&logo=githubactions&logoColor=white" alt="publish" /></a>
|
|
13
14
|
<img src="https://img.shields.io/badge/Bash-4%2B-4EAA25?style=flat-square&logo=gnubash&logoColor=white" alt="Bash 4+" />
|
|
14
15
|
<img src="https://img.shields.io/badge/Dep-jq-blue?style=flat-square" alt="jq" />
|
|
15
16
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow?style=flat-square" alt="MIT License" /></a>
|
|
16
17
|
</p>
|
|
17
18
|
|
|
18
19
|
<p align="center">
|
|
19
|
-
<a href="
|
|
20
|
+
<a href="README_CN.md">中文</a> ·
|
|
21
|
+
<a href="https://www.npmjs.com/package/ai-battle">NPM</a> ·
|
|
22
|
+
<a href="https://github.com/Alfonsxh/ai-battle/issues">Issues</a> ·
|
|
23
|
+
<a href="https://github.com/Alfonsxh/ai-battle/pulls">PRs</a> ·
|
|
24
|
+
<a href="LICENSE">License</a>
|
|
20
25
|
</p>
|
|
21
26
|
|
|
22
27
|
---
|
|
23
28
|
|
|
29
|
+
<details>
|
|
30
|
+
<summary><b>Table of Contents</b></summary>
|
|
31
|
+
|
|
32
|
+
- [Features](#features)
|
|
33
|
+
- [Quick Start](#quick-start)
|
|
34
|
+
- [Installation](#installation)
|
|
35
|
+
- [Prerequisites](#prerequisites)
|
|
36
|
+
- [Usage](#usage)
|
|
37
|
+
- [Examples](#examples)
|
|
38
|
+
- [How It Works](#how-it-works)
|
|
39
|
+
- [Built-in Agents](#built-in-agents)
|
|
40
|
+
- [Output Structure](#output-structure)
|
|
41
|
+
- [Extend Agent](#extend-agent)
|
|
42
|
+
- [Environment Variables](#environment-variables)
|
|
43
|
+
- [Contributing](#contributing)
|
|
44
|
+
- [License](#license)
|
|
45
|
+
|
|
46
|
+
</details>
|
|
47
|
+
|
|
24
48
|
## ✨ Features
|
|
25
49
|
|
|
26
50
|
| Feature | Description |
|
|
@@ -70,7 +94,7 @@ npm install -g ai-battle
|
|
|
70
94
|
|
|
71
95
|
## 📖 Usage
|
|
72
96
|
|
|
73
|
-
```
|
|
97
|
+
```text
|
|
74
98
|
ai-battle [options]
|
|
75
99
|
ai-battle help
|
|
76
100
|
```
|
|
@@ -245,141 +269,3 @@ register_agent "myagent"
|
|
|
245
269
|
## 📄 License
|
|
246
270
|
|
|
247
271
|
[MIT](LICENSE) © [Alfons](https://github.com/Alfonsxh)
|
|
248
|
-
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
# 📖 中文文档
|
|
252
|
-
|
|
253
|
-
## ✨ 特性
|
|
254
|
-
|
|
255
|
-
| 特性 | 说明 |
|
|
256
|
-
| :--- | :--- |
|
|
257
|
-
| 🤖 **多 Agent 圆桌** | 支持 Claude / Codex / Gemini 自由组合 |
|
|
258
|
-
| 🔁 **同类自辩** | 同一 Agent 可参加多席位(如 `gemini,gemini`) |
|
|
259
|
-
| 🔨 **裁判模式** | 独立裁判每轮总结差异、自动检测共识、生成最终报告 |
|
|
260
|
-
| 👁️ **上帝视角** | 每轮结束后人工注入补充信息引导讨论方向 |
|
|
261
|
-
| 💾 **Session 录制** | 保存 Agent CLI 原始输出(stream-json / json / raw) |
|
|
262
|
-
| 🔄 **断点续讨** | 中断后自动恢复到上次轮次继续讨论 |
|
|
263
|
-
| 🔌 **可扩展** | 实现 3 个函数 + 注册即可接入新 Agent |
|
|
264
|
-
|
|
265
|
-
## 🚀 快速开始
|
|
266
|
-
|
|
267
|
-
```bash
|
|
268
|
-
# 创建讨论目录
|
|
269
|
-
mkdir my-topic && cd my-topic
|
|
270
|
-
|
|
271
|
-
# 写入问题
|
|
272
|
-
echo "微服务 vs 单体架构的优缺点?" > problem.md
|
|
273
|
-
|
|
274
|
-
# 启动讨论(自动拉取最新版)
|
|
275
|
-
npx ai-battle --agents claude,gemini --rounds 8
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
## 📦 安装
|
|
279
|
-
|
|
280
|
-
**推荐:无需安装,直接使用 npx**
|
|
281
|
-
|
|
282
|
-
```bash
|
|
283
|
-
npx ai-battle --agents claude,gemini --rounds 5
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
> npx 每次执行自动拉取最新版本,无需手动更新。
|
|
287
|
-
|
|
288
|
-
**全局安装:**
|
|
289
|
-
|
|
290
|
-
```bash
|
|
291
|
-
npm install -g ai-battle
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### 前置依赖
|
|
295
|
-
|
|
296
|
-
- `bash` 4+
|
|
297
|
-
- [`jq`](https://jqlang.github.io/jq/)
|
|
298
|
-
- Agent CLI 工具(至少安装 2 个):`claude` / `codex` / `gemini`
|
|
299
|
-
|
|
300
|
-
## 📖 用法
|
|
301
|
-
|
|
302
|
-
```
|
|
303
|
-
ai-battle [options]
|
|
304
|
-
ai-battle help
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
| 参数 | 说明 | 默认值 |
|
|
308
|
-
| :--- | :--- | :--- |
|
|
309
|
-
| `--agents, -a <a1,a2>` | 选择参与的 Agent,支持同类 | `claude,codex` |
|
|
310
|
-
| `--rounds, -r <N>` | 最大讨论轮次 | `10` |
|
|
311
|
-
| `--god, -g` | 开启上帝视角(每轮可注入补充信息) | — |
|
|
312
|
-
| `--referee [agent]` | 开启裁判模式(每轮总结 + 生成 SUMMARY.md) | — |
|
|
313
|
-
|
|
314
|
-
### 💡 示例
|
|
315
|
-
|
|
316
|
-
```bash
|
|
317
|
-
# 同类 Agent 自我辩论
|
|
318
|
-
ai-battle --agents gemini,gemini
|
|
319
|
-
|
|
320
|
-
# 三方圆桌讨论
|
|
321
|
-
ai-battle --agents claude,codex,gemini --rounds 5
|
|
322
|
-
|
|
323
|
-
# 裁判模式
|
|
324
|
-
ai-battle --agents claude,codex,gemini --referee --rounds 5
|
|
325
|
-
|
|
326
|
-
# 指定 claude 做裁判
|
|
327
|
-
ai-battle --agents codex,gemini --referee claude --rounds 5
|
|
328
|
-
|
|
329
|
-
# 上帝视角 + 裁判
|
|
330
|
-
ai-battle --agents claude,codex --referee --god
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
## 📁 产出结构
|
|
334
|
-
|
|
335
|
-
```text
|
|
336
|
-
my-topic/
|
|
337
|
-
├── problem.md # 讨论问题(用户创建)
|
|
338
|
-
├── referee.md # 裁判自定义提示词(可选)
|
|
339
|
-
├── SUMMARY.md # 最终总结(裁判自动生成)
|
|
340
|
-
├── .env # 环境变量(启动时自动加载)
|
|
341
|
-
└── .ai-battle/ # 所有运行时产物
|
|
342
|
-
├── rounds/ # 讨论轮次记录
|
|
343
|
-
│ ├── round_1_claude.md
|
|
344
|
-
│ ├── round_1_gemini.md
|
|
345
|
-
│ ├── referee_round_2.md # 裁判总结(--referee)
|
|
346
|
-
│ └── god_round_1.md # 上帝注入(--god)
|
|
347
|
-
├── sessions/ # Agent CLI 原始输出
|
|
348
|
-
├── agents/ # Agent 指令文件
|
|
349
|
-
├── consensus.md # 共识结论(如达成)
|
|
350
|
-
├── config.json # 会话配置
|
|
351
|
-
└── battle.log # 运行日志(tail -f 实时查看)
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
## 🔌 扩展 Agent
|
|
355
|
-
|
|
356
|
-
只需实现 3 个函数并注册:
|
|
357
|
-
|
|
358
|
-
```bash
|
|
359
|
-
# 1. 实现函数
|
|
360
|
-
check_myagent() { ... } # 可用性检查,返回 0/1
|
|
361
|
-
call_myagent() { ... } # 调用 Agent: $1=system_prompt $2=user_msg $3=session_tag
|
|
362
|
-
generate_myagent_md() { ... } # 生成指令文件: $1=max_rounds $2=problem
|
|
363
|
-
|
|
364
|
-
# 2. 注册
|
|
365
|
-
register_agent "myagent"
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## 🔑 环境变量
|
|
369
|
-
|
|
370
|
-
| 变量 | 说明 | 默认值 |
|
|
371
|
-
| :--- | :--- | :--- |
|
|
372
|
-
| `ANTHROPIC_BASE_URL` | Claude API 地址 | — |
|
|
373
|
-
| `ANTHROPIC_AUTH_TOKEN` | Claude 认证 Token | — |
|
|
374
|
-
| `ANTHROPIC_DEFAULT_SONNET_MODEL` | Claude 模型名称 | — |
|
|
375
|
-
| `API_TIMEOUT_MS` | Claude 超时时间(毫秒) | — |
|
|
376
|
-
| `CODEX_MODEL` | Codex 模型名称 | `gpt-5.3-codex` |
|
|
377
|
-
| `GEMINI_API_KEY` | Gemini API Key | — |
|
|
378
|
-
|
|
379
|
-
## 🤝 参与贡献
|
|
380
|
-
|
|
381
|
-
欢迎提交 [Issue](https://github.com/Alfonsxh/ai-battle/issues) 和 [Pull Request](https://github.com/Alfonsxh/ai-battle/pulls)!
|
|
382
|
-
|
|
383
|
-
## 📄 许可
|
|
384
|
-
|
|
385
|
-
[MIT](LICENSE) © [Alfons](https://github.com/Alfonsxh)
|
package/ai-battle.sh
CHANGED
|
@@ -23,12 +23,31 @@ if [ -f ".env" ]; then
|
|
|
23
23
|
fi
|
|
24
24
|
|
|
25
25
|
# ======================== 版本(从 package.json 读取) ========================
|
|
26
|
-
|
|
26
|
+
# 解析脚本真实路径(支持全局安装后经由软链接启动)
|
|
27
|
+
resolve_script_dir() {
|
|
28
|
+
local src="${BASH_SOURCE[0]}"
|
|
29
|
+
|
|
30
|
+
while [ -L "$src" ]; do
|
|
31
|
+
local dir target
|
|
32
|
+
dir="$(cd -P "$(dirname "$src")" && pwd)"
|
|
33
|
+
target="$(readlink "$src")"
|
|
34
|
+
if [[ "$target" != /* ]]; then
|
|
35
|
+
src="${dir}/${target}"
|
|
36
|
+
else
|
|
37
|
+
src="$target"
|
|
38
|
+
fi
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
cd -P "$(dirname "$src")" && pwd
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
SCRIPT_DIR="$(resolve_script_dir)"
|
|
45
|
+
|
|
27
46
|
# 优先使用 npm/npx 注入的环境变量,fallback 到 node 读取,最后 grep 提取
|
|
28
47
|
if [ -n "${npm_package_version:-}" ]; then
|
|
29
48
|
VERSION="$npm_package_version"
|
|
30
49
|
else
|
|
31
|
-
VERSION=$(node -p "require(
|
|
50
|
+
VERSION=$(node -p "require(process.argv[1]).version" "${SCRIPT_DIR}/package.json" 2>/dev/null \
|
|
32
51
|
|| grep -o '"version": *"[^"]*"' "${SCRIPT_DIR}/package.json" 2>/dev/null | head -1 | grep -o '[0-9][^"]*' \
|
|
33
52
|
|| echo "0.0.0")
|
|
34
53
|
fi
|
|
@@ -50,6 +69,8 @@ DEFAULT_MAX_ROUNDS=10
|
|
|
50
69
|
WORK_DIR=".ai-battle"
|
|
51
70
|
PROBLEM_FILE="problem.md"
|
|
52
71
|
ROUNDS_DIR="${WORK_DIR}/rounds"
|
|
72
|
+
ORDERS_DIR="${WORK_DIR}/orders"
|
|
73
|
+
ORDER_HISTORY_FILE="${WORK_DIR}/order_history.jsonl"
|
|
53
74
|
CONSENSUS_FILE="${WORK_DIR}/consensus.md"
|
|
54
75
|
LOG_FILE="${WORK_DIR}/battle.log"
|
|
55
76
|
CONFIG_FILE="${WORK_DIR}/config.json"
|
|
@@ -566,6 +587,138 @@ agent_md_file() {
|
|
|
566
587
|
echo "${AGENTS_DIR}/${filename}"
|
|
567
588
|
}
|
|
568
589
|
|
|
590
|
+
# 查找 agent 在数组中的索引
|
|
591
|
+
# 参数: $1=目标 agent $2...=agent 列表
|
|
592
|
+
# 输出: 索引(找不到返回 -1)
|
|
593
|
+
find_agent_index() {
|
|
594
|
+
local target="$1"
|
|
595
|
+
shift
|
|
596
|
+
local agents=("$@")
|
|
597
|
+
|
|
598
|
+
for ((idx=0; idx<${#agents[@]}; idx++)); do
|
|
599
|
+
if [ "${agents[$idx]}" = "$target" ]; then
|
|
600
|
+
echo "$idx"
|
|
601
|
+
return 0
|
|
602
|
+
fi
|
|
603
|
+
done
|
|
604
|
+
|
|
605
|
+
echo "-1"
|
|
606
|
+
return 0
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
# 校验轮次顺序 CSV 是否与当前 agent 列表一一对应
|
|
610
|
+
# 参数: $1=order_csv $2...=agent 列表
|
|
611
|
+
# 返回: 0=有效, 1=无效
|
|
612
|
+
validate_round_order_csv() {
|
|
613
|
+
local order_csv="$1"
|
|
614
|
+
shift
|
|
615
|
+
local agents=("$@")
|
|
616
|
+
local order=()
|
|
617
|
+
|
|
618
|
+
[ -n "$order_csv" ] || return 1
|
|
619
|
+
IFS=',' read -ra order <<< "$order_csv"
|
|
620
|
+
|
|
621
|
+
if [ "${#order[@]}" -ne "${#agents[@]}" ]; then
|
|
622
|
+
return 1
|
|
623
|
+
fi
|
|
624
|
+
|
|
625
|
+
for ((idx=0; idx<${#order[@]}; idx++)); do
|
|
626
|
+
order[$idx]=$(echo "${order[$idx]}" | xargs)
|
|
627
|
+
done
|
|
628
|
+
|
|
629
|
+
# 每个 agent 必须且仅出现一次,避免顺序文件损坏导致错位
|
|
630
|
+
for expected in "${agents[@]}"; do
|
|
631
|
+
local count=0
|
|
632
|
+
for got in "${order[@]}"; do
|
|
633
|
+
if [ "$got" = "$expected" ]; then
|
|
634
|
+
count=$((count + 1))
|
|
635
|
+
fi
|
|
636
|
+
done
|
|
637
|
+
if [ "$count" -ne 1 ]; then
|
|
638
|
+
return 1
|
|
639
|
+
fi
|
|
640
|
+
done
|
|
641
|
+
|
|
642
|
+
return 0
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
# Fisher-Yates 洗牌,生成当前轮次的随机顺序
|
|
646
|
+
# 参数: $@=agent 列表
|
|
647
|
+
# 输出: csv 字符串(如 a,b,c)
|
|
648
|
+
shuffle_round_order_csv() {
|
|
649
|
+
local shuffled=("$@")
|
|
650
|
+
local n=${#shuffled[@]}
|
|
651
|
+
|
|
652
|
+
if [ "$n" -eq 0 ]; then
|
|
653
|
+
echo ""
|
|
654
|
+
return 0
|
|
655
|
+
fi
|
|
656
|
+
|
|
657
|
+
for ((i=n-1; i>0; i--)); do
|
|
658
|
+
local j=$((RANDOM % (i + 1)))
|
|
659
|
+
local tmp="${shuffled[$i]}"
|
|
660
|
+
shuffled[$i]="${shuffled[$j]}"
|
|
661
|
+
shuffled[$j]="$tmp"
|
|
662
|
+
done
|
|
663
|
+
|
|
664
|
+
local csv="${shuffled[0]}"
|
|
665
|
+
for ((i=1; i<n; i++)); do
|
|
666
|
+
csv+=",${shuffled[$i]}"
|
|
667
|
+
done
|
|
668
|
+
echo "$csv"
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
# 记录轮次顺序历史(jsonl),用于兜底追踪和恢复审计
|
|
672
|
+
# 参数: $1=round $2=order_csv $3=source(random|recovered)
|
|
673
|
+
record_round_order() {
|
|
674
|
+
local round="$1"
|
|
675
|
+
local order_csv="$2"
|
|
676
|
+
local source="${3:-random}"
|
|
677
|
+
local ts
|
|
678
|
+
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
679
|
+
|
|
680
|
+
local order_json
|
|
681
|
+
order_json=$(printf '%s' "$order_csv" | jq -R 'split(",")')
|
|
682
|
+
|
|
683
|
+
jq -cn \
|
|
684
|
+
--arg ts "$ts" \
|
|
685
|
+
--arg source "$source" \
|
|
686
|
+
--argjson round "$round" \
|
|
687
|
+
--argjson order "$order_json" \
|
|
688
|
+
'{ts: $ts, round: $round, source: $source, order: $order}' \
|
|
689
|
+
>> "$ORDER_HISTORY_FILE"
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
# 读取或生成某一轮的顺序:
|
|
693
|
+
# 1) 若顺序文件存在且有效,复用;2) 否则随机生成并落盘
|
|
694
|
+
# 参数: $1=round $2...=agent 列表
|
|
695
|
+
# 输出: order_csv
|
|
696
|
+
resolve_round_order_csv() {
|
|
697
|
+
local round="$1"
|
|
698
|
+
shift
|
|
699
|
+
local agents=("$@")
|
|
700
|
+
local order_file="${ORDERS_DIR}/round_${round}.order"
|
|
701
|
+
local order_csv=""
|
|
702
|
+
local source="recovered"
|
|
703
|
+
|
|
704
|
+
if [ -f "$order_file" ] && [ -s "$order_file" ]; then
|
|
705
|
+
order_csv=$(tr -d '\r\n' < "$order_file")
|
|
706
|
+
if ! validate_round_order_csv "$order_csv" "${agents[@]}"; then
|
|
707
|
+
log_and_print "${YELLOW}⚠️ Round ${round} 顺序文件无效,已重新随机${NC}"
|
|
708
|
+
order_csv=""
|
|
709
|
+
fi
|
|
710
|
+
fi
|
|
711
|
+
|
|
712
|
+
if [ -z "$order_csv" ]; then
|
|
713
|
+
order_csv=$(shuffle_round_order_csv "${agents[@]}")
|
|
714
|
+
echo "$order_csv" > "$order_file"
|
|
715
|
+
source="random"
|
|
716
|
+
fi
|
|
717
|
+
|
|
718
|
+
record_round_order "$round" "$order_csv" "$source"
|
|
719
|
+
echo "$order_csv"
|
|
720
|
+
}
|
|
721
|
+
|
|
569
722
|
# 上帝视角: 等待用户输入补充信息
|
|
570
723
|
# 参数: $1=当前轮次
|
|
571
724
|
# 输出: stdout 用户输入的补充信息(可为空)
|
|
@@ -1069,8 +1222,8 @@ cmd_run() {
|
|
|
1069
1222
|
exit 1
|
|
1070
1223
|
fi
|
|
1071
1224
|
|
|
1072
|
-
#
|
|
1073
|
-
mkdir -p "$ROUNDS_DIR" "$SESSIONS_DIR" "$AGENTS_DIR"
|
|
1225
|
+
# 创建运行目录(包含顺序记录目录)
|
|
1226
|
+
mkdir -p "$ROUNDS_DIR" "$SESSIONS_DIR" "$AGENTS_DIR" "$ORDERS_DIR"
|
|
1074
1227
|
|
|
1075
1228
|
# 动态生成各 Agent 的指令文件(新建和恢复模式都需要更新)
|
|
1076
1229
|
for agent in "${available_agents[@]}"; do
|
|
@@ -1117,6 +1270,7 @@ cmd_run() {
|
|
|
1117
1270
|
log_and_print " 📝 问题: $(head -1 "$PROBLEM_FILE")"
|
|
1118
1271
|
log_and_print " 🤖 Agent: ${available_agents[*]}"
|
|
1119
1272
|
log_and_print " 🔄 最大轮次: $max_rounds"
|
|
1273
|
+
log_and_print " 🔀 发言顺序: 每轮随机(自动记录并可恢复)"
|
|
1120
1274
|
if $god_mode; then
|
|
1121
1275
|
log_and_print "${CYAN} 👁️ 上帝视角: 开启${NC}"
|
|
1122
1276
|
fi
|
|
@@ -1160,7 +1314,7 @@ cmd_run() {
|
|
|
1160
1314
|
round=$((prev_round + 1))
|
|
1161
1315
|
|
|
1162
1316
|
# 更新配置: 状态为 running,max_rounds 使用命令行参数
|
|
1163
|
-
jq --argjson m "$max_rounds" '.status = "running" | .max_rounds = $m' \
|
|
1317
|
+
jq --argjson m "$max_rounds" '.status = "running" | .max_rounds = $m | .order_mode = "round_random"' \
|
|
1164
1318
|
"$CONFIG_FILE" > "${CONFIG_FILE}.tmp" \
|
|
1165
1319
|
&& mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
|
1166
1320
|
|
|
@@ -1197,7 +1351,7 @@ cmd_run() {
|
|
|
1197
1351
|
--argjson max_rounds "$max_rounds" \
|
|
1198
1352
|
--arg problem "$problem" \
|
|
1199
1353
|
'{agents: $agents, max_rounds: $max_rounds, problem: $problem,
|
|
1200
|
-
status: "running", current_round: 0}' \
|
|
1354
|
+
status: "running", current_round: 0, order_mode: "round_random", last_round_order: ""}' \
|
|
1201
1355
|
> "$CONFIG_FILE"
|
|
1202
1356
|
|
|
1203
1357
|
# 清空日志
|
|
@@ -1353,10 +1507,21 @@ ${all_responses_r1}请进行裁判总结。" "referee_round_1")
|
|
|
1353
1507
|
# 提取为函数逻辑,供主循环和追加轮次复用
|
|
1354
1508
|
while [ "$round" -le "$max_rounds" ]; do
|
|
1355
1509
|
local remaining=$((max_rounds - round))
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1510
|
+
local round_order_csv
|
|
1511
|
+
round_order_csv=$(resolve_round_order_csv "$round" "${available_agents[@]}")
|
|
1512
|
+
local round_order=()
|
|
1513
|
+
IFS=',' read -ra round_order <<< "$round_order_csv"
|
|
1514
|
+
|
|
1515
|
+
log_and_print "${CYAN}🔀 Round $round 顺序: ${round_order[*]}${NC}"
|
|
1516
|
+
|
|
1517
|
+
# 每轮默认随机顺序发言,优先复用已落盘顺序(便于恢复)
|
|
1518
|
+
for agent in "${round_order[@]}"; do
|
|
1519
|
+
local i
|
|
1520
|
+
i=$(find_agent_index "$agent" "${available_agents[@]}")
|
|
1521
|
+
if [ "$i" -lt 0 ]; then
|
|
1522
|
+
log_and_print "${YELLOW}⚠️ 跳过未知 agent: ${agent}${NC}"
|
|
1523
|
+
continue
|
|
1524
|
+
fi
|
|
1360
1525
|
local base
|
|
1361
1526
|
base=$(agent_base "$agent")
|
|
1362
1527
|
local color
|
|
@@ -1368,8 +1533,8 @@ ${all_responses_r1}请进行裁判总结。" "referee_round_1")
|
|
|
1368
1533
|
# 构建其他 agent 回复的 XML 块
|
|
1369
1534
|
local others_responses=""
|
|
1370
1535
|
for ((j=0; j<agent_count; j++)); do
|
|
1371
|
-
|
|
1372
|
-
|
|
1536
|
+
local other="${available_agents[$j]}"
|
|
1537
|
+
if [ "$other" != "$agent" ]; then
|
|
1373
1538
|
others_responses+="<${other}_response>
|
|
1374
1539
|
${responses[$j]}
|
|
1375
1540
|
</${other}_response>
|
|
@@ -1502,7 +1667,8 @@ ${all_responses}请进行裁判总结。" "referee_round_${round}")
|
|
|
1502
1667
|
fi
|
|
1503
1668
|
|
|
1504
1669
|
# 更新配置
|
|
1505
|
-
jq --argjson r "$round"
|
|
1670
|
+
jq --argjson r "$round" --arg o "$round_order_csv" \
|
|
1671
|
+
'.current_round = $r | .status = "running" | .last_round_order = $o' \
|
|
1506
1672
|
"$CONFIG_FILE" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
|
1507
1673
|
|
|
1508
1674
|
# 上帝视角: 每轮结束后注入(裁判总结后再让 god 输入)
|
|
@@ -1554,9 +1720,20 @@ ${all_responses}请进行裁判总结。" "referee_round_${round}")
|
|
|
1554
1720
|
# 继续讨论循环(逻辑与上方 Round 2+ 完全相同)
|
|
1555
1721
|
while [ "$round" -le "$max_rounds" ]; do
|
|
1556
1722
|
local remaining=$((max_rounds - round))
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1723
|
+
local round_order_csv
|
|
1724
|
+
round_order_csv=$(resolve_round_order_csv "$round" "${available_agents[@]}")
|
|
1725
|
+
local round_order=()
|
|
1726
|
+
IFS=',' read -ra round_order <<< "$round_order_csv"
|
|
1727
|
+
|
|
1728
|
+
log_and_print "${CYAN}🔀 Round $round 顺序: ${round_order[*]}${NC}"
|
|
1729
|
+
|
|
1730
|
+
for agent in "${round_order[@]}"; do
|
|
1731
|
+
local i
|
|
1732
|
+
i=$(find_agent_index "$agent" "${available_agents[@]}")
|
|
1733
|
+
if [ "$i" -lt 0 ]; then
|
|
1734
|
+
log_and_print "${YELLOW}⚠️ 跳过未知 agent: ${agent}${NC}"
|
|
1735
|
+
continue
|
|
1736
|
+
fi
|
|
1560
1737
|
local base
|
|
1561
1738
|
base=$(agent_base "$agent")
|
|
1562
1739
|
local color
|
|
@@ -1568,8 +1745,8 @@ ${all_responses}请进行裁判总结。" "referee_round_${round}")
|
|
|
1568
1745
|
# 构建其他 agent 回复的 XML 块
|
|
1569
1746
|
local others_responses=""
|
|
1570
1747
|
for ((j=0; j<agent_count; j++)); do
|
|
1571
|
-
|
|
1572
|
-
|
|
1748
|
+
local other="${available_agents[$j]}"
|
|
1749
|
+
if [ "$other" != "$agent" ]; then
|
|
1573
1750
|
others_responses+="<${other}_response>
|
|
1574
1751
|
${responses[$j]}
|
|
1575
1752
|
</${other}_response>
|
|
@@ -1697,7 +1874,8 @@ ${all_responses}请进行裁判总结。" "referee_round_${round}")
|
|
|
1697
1874
|
fi
|
|
1698
1875
|
|
|
1699
1876
|
# 更新配置
|
|
1700
|
-
jq --argjson r "$round"
|
|
1877
|
+
jq --argjson r "$round" --arg o "$round_order_csv" \
|
|
1878
|
+
'.current_round = $r | .status = "running" | .last_round_order = $o' \
|
|
1701
1879
|
"$CONFIG_FILE" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
|
1702
1880
|
|
|
1703
1881
|
# 上帝视角: 每轮结束后注入(裁判总结后再让 god 输入)
|
|
@@ -1751,6 +1929,7 @@ cmd_help() {
|
|
|
1751
1929
|
--agents, -a <a1,a2> Select participating agents (default: claude,codex)
|
|
1752
1930
|
Supports same-type agents: --agents gemini,gemini
|
|
1753
1931
|
--rounds, -r <N> Max discussion rounds (default: 10)
|
|
1932
|
+
Speaking order is randomized every round by default
|
|
1754
1933
|
--god, -g Enable god mode (inject instructions after each round)
|
|
1755
1934
|
--referee [agent] Enable referee mode (summarize each round, detect
|
|
1756
1935
|
consensus, generate final summary)
|
|
@@ -1788,6 +1967,8 @@ cmd_help() {
|
|
|
1788
1967
|
.ai-battle/rounds/ Per-round records (round_N_<agent>.md)
|
|
1789
1968
|
.ai-battle/rounds/referee_*.md Referee summaries (--referee)
|
|
1790
1969
|
.ai-battle/rounds/god_*.md God mode injections (--god)
|
|
1970
|
+
.ai-battle/orders/round_*.order Per-round speaking order (fallback/recovery)
|
|
1971
|
+
.ai-battle/order_history.jsonl Full order history (audit trail)
|
|
1791
1972
|
.ai-battle/sessions/ Raw Agent CLI output
|
|
1792
1973
|
.ai-battle/consensus.md Consensus conclusion (if reached)
|
|
1793
1974
|
.ai-battle/battle.log Full log
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-battle",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "让多个 AI Agent 对同一问题进行结构化圆桌讨论",
|
|
5
5
|
"bin": {
|
|
6
|
-
"ai-battle": "
|
|
6
|
+
"ai-battle": "ai-battle.sh"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"ai-battle.sh",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
],
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
25
|
-
"url": "https://github.com/Alfonsxh/ai-battle.git"
|
|
25
|
+
"url": "git+https://github.com/Alfonsxh/ai-battle.git"
|
|
26
26
|
},
|
|
27
27
|
"author": "Alfons",
|
|
28
28
|
"license": "MIT"
|
|
29
|
-
}
|
|
29
|
+
}
|