1. 为什么我要用狼人杀来观察大模型的多轮博弈能力
如果你只让大模型做选择题、写代码、总结文档,你看到的其实是它“静态答题”的一面。可一旦把 9 个模型放进同一张狼人杀牌桌,让它们各自拿到村民、预言家、女巫、猎人、狼人这些身份,情况就完全不一样了:它们要在信息不完整、发言互相矛盾、阵营目标冲突的情况下,连续多轮做判断、投票、伪装和说服。这时候模型暴露出来的,是记忆保持、身份推理、欺骗识别、策略一致性这些更接近真实任务的能力。
我最近在本地复现了一套“AI 狼人杀”多轮博弈评测环境,核心思路是:用 TaoToken 作为统一的大模型 API 通道,把 9 个不同厂商的模型接到同一个对局引擎里,让它们按同一套规则发言、投票、夜间行动,然后把每轮日志落盘,最后做胜负判定和能力维度打分。这样做的好处是,模型之间的差异不再靠主观感觉,而是能通过投票准确率、神职技能、刀法精准、阵营胜率这些可量化指标看出来。
这篇文章会交付三样东西:一是 TaoToken 统一 Key/API 通道的config.toml与settings.json骨架;二是 CC Switch 与 Cline 的接入配置;三是多轮对局日志采集与胜负判定的验证动作。你照着做,可以在自己机器上跑起一套可复现的大模型博弈评测。适合谁?适合想观察模型多轮推理差异的开发者、做 AI 评测的同学,以及想把多模型接入统一通道的工程实践者。
2. TaoToken 前置准备:统一 Key 与 API 通道
狼人杀评测最麻烦的地方不是游戏逻辑,而是模型接入。9 个模型如果各自用不同的 SDK、不同的鉴权方式、不同的返回格式,光适配就能耗掉大半天。我的做法是用 TaoToken 做统一入口,它提供 OpenAI 兼容的 API 通道,模型名通过请求参数区分,这样对局引擎只需要维护一套调用逻辑。
先到官网注册并创建 API Key,地址是https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。注册后在控制台生成 Key,建议单独建一个用于评测项目的 Key,方便后续按项目统计用量和排查问题。控制台入口在https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,API Key 管理页在https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。
API 基础地址是https://taotoken.net/api,注意这个地址后面不加 UTM 参数,直接作为base_url使用。请求格式兼容 OpenAI 的/v1/chat/completions,所以你在对局引擎里可以直接用 openai 官方 SDK,把base_url指过来就行。
注意:不要把 Key 硬编码进对局脚本再提交到公开仓库。我习惯用环境变量
TAOTOKEN_API_KEY注入,配置文件里只写占位符。
模型名这块,狼人杀评测需要 9 个不同模型,你可以在请求的model字段里切换。建议先在模型对话页确认每个模型名可用,入口是https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。确认能正常返回后,再写进对局配置。
3. 可复制配置:config.toml 与 settings.json 骨架
对局引擎我用 Python 写,配置分两层:config.toml管模型列表和对局参数,settings.json管运行时密钥和日志路径。这样模型列表可以随评测轮次调整,密钥不进入版本控制。
先看config.toml:
# config.toml [api] base_url = "https://taotoken.net/api" timeout = 60 max_retries = 3 [game] player_count = 9 max_rounds = 6 log_dir = "./logs/werewolf" save_raw_response = true # 9 个模型玩家,name 用于日志展示,model 用于 API 请求 [[players]] seat = 1 name = "Gemini-Flash" model = "gemini-3.5-flash" [[players]] seat = 2 name = "GPT-5.5" model = "gpt-5.5" [[players]] seat = 3 name = "Qwen-Max" model = "qwen3.7-max" [[players]] seat = 4 name = "Claude-Opus" model = "claude-opus-4.8" [[players]] seat = 5 name = "Model-E" model = "your-model-e" [[players]] seat = 6 name = "Model-F" model = "your-model-f" [[players]] seat = 7 name = "Model-G" model = "your-model-g" [[players]] seat = 8 name = "Model-H" model = "your-model-h" [[players]] seat = 9 name = "Model-I" model = "your-model-i"再看settings.json,它负责运行时注入:
{ "api_key_env": "TAOTOKEN_API_KEY", "base_url": "https://taotoken.net/api", "log": { "dir": "./logs/werewolf", "format": "jsonl", "flush_every_round": true }, "scoring": { "vote_accuracy_weight": 0.25, "skill_weight": 0.25, "wolf_kill_weight": 0.2, "camp_win_weight": 0.2, "overall_weight": 0.1, "bayesian_smooth": true } }这里有几个参数值得说明。max_rounds控制单局最大轮数,狼人杀通常 4 到 6 轮就能分出胜负,设太大只是浪费 token。save_raw_response打开后,每个模型的原始返回都会存下来,方便你回看它到底是推理错了还是表达跑偏了。bayesian_smooth对应 excerpt 里提到的贝叶斯平滑,样本少的时候避免单局极端表现把分数拉飞。
对局引擎调用模型的核心代码大概长这样:
import os import json import tomllib from openai import OpenAI with open("config.toml", "rb") as f: cfg = tomllib.load(f) client = OpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url=cfg["api"]["base_url"], ) def ask_model(model: str, messages: list, temperature: float = 0.7) -> str: resp = client.chat.completions.create( model=model, messages=messages, temperature=temperature, timeout=cfg["api"]["timeout"], ) return resp.choices[0].message.content这段代码的关键点是base_url指向 TaoToken,model字段按座位切换。对局引擎不需要知道每个模型来自哪个厂商,只认model字符串。
4. CC Switch 与 Cline 接入配置
除了自己写对局引擎,我还用 CC Switch 和 Cline 做辅助验证。CC Switch 用来快速切换不同模型的对话环境,Cline 用来在编辑器里直接跑多轮对话测试。这两个工具都能通过自定义 API 通道接到 TaoToken。
CC Switch 的配置思路是新增一个 provider,把 API 地址指向 TaoToken。它的配置文件通常是一个 JSON,核心字段如下:
{ "providers": [ { "name": "taotoken", "base_url": "https://taotoken.net/api", "api_key": "env:TAOTOKEN_API_KEY", "models": [ "gemini-3.5-flash", "gpt-5.5", "qwen3.7-max", "claude-opus-4.8" ] } ], "default_provider": "taotoken" }配好后,你在 CC Switch 里切换模型,实际请求都会走 TaoToken 通道。这样验证单个模型的多轮表现时,不用改对局引擎,直接切模型就能对比。
Cline 的接入更直接,在设置里选 OpenAI Compatible,然后填:
{ "apiProvider": "openai", "openAiBaseUrl": "https://taotoken.net/api", "openAiApiKey": "env:TAOTOKEN_API_KEY", "openAiModelId": "gemini-3.5-flash" }Cline 适合做单模型的多轮对话压力测试。比如你想看某个模型在连续 6 轮发言里会不会前后矛盾,可以直接在 Cline 里模拟狼人杀发言序列,观察它的记忆保持和身份一致性。我实测下来,Cline 的对话历史保留得比较完整,适合做这种长上下文观察。
提示:CC Switch 和 Cline 都只是验证工具,真正的批量评测还是靠对局引擎。工具的作用是让你在写引擎之前,先确认模型通道是通的。
如果你需要长期跑编码类 Agent 任务,可以了解 Coding Plan,入口是https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。狼人杀评测本身不依赖它,但多模型批量调用时,合理的套餐能控制成本。
5. 验证请求与成功结果:多轮对局日志采集
配置写完后,第一步是验证通道是否通。写一个最小请求脚本:
from openai import OpenAI import os client = OpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url="https://taotoken.net/api", ) resp = client.chat.completions.create( model="gemini-3.5-flash", messages=[ {"role": "system", "content": "你是狼人杀玩家,请用一句话表明身份。"}, {"role": "user", "content": "第1轮发言,你是几号玩家?"}, ], ) print(resp.choices[0].message.content)如果返回正常文本,说明 Key、base_url、模型名三者都对上了。如果报 401,检查 Key 是否带上了Bearer前缀;如果报 404,检查base_url是不是写成了https://taotoken.net/api/v1,正确写法是https://taotoken.net/api,SDK 会自动补/v1/chat/completions。
通道验证通过后,开始跑多轮对局。对局引擎每一轮做四件事:收集发言、记录投票、执行夜间行动、更新存活状态。日志用 JSONL 格式落盘,每行一条事件:
import json from datetime import datetime def log_event(log_path: str, event: dict): event["ts"] = datetime.utcnow().isoformat() with open(log_path, "a", encoding="utf-8") as f: f.write(json.dumps(event, ensure_ascii=False) + "\n") # 示例:记录一次发言 log_event("./logs/werewolf/game_001.jsonl", { "round": 1, "phase": "speak", "seat": 3, "model": "qwen3.7-max", "content": "我是预言家,昨晚验了5号,是狼人。", "raw": resp.choices[0].message.content, })日志里我特意保留raw字段,因为模型有时候会在发言里夹带格式标记,比如【发言】、【投票】,解析时要去掉。content是清洗后的文本,raw是原始返回,方便回溯。
胜负判定逻辑单独写一个函数,输入是完整日志,输出是阵营胜负和各维度得分:
def judge_game(events: list) -> dict: alive_wolves = {e["seat"] for e in events if e.get("role") == "wolf" and e.get("alive")} alive_villagers = {e["seat"] for e in events if e.get("role") != "wolf" and e.get("alive")} if not alive_wolves: winner = "good" elif len(alive_wolves) >= len(alive_villagers): winner = "wolf" else: winner = "ongoing" return {"winner": winner, "alive_wolves": len(alive_wolves), "alive_villagers": len(alive_villagers)}跑完一局后,你会得到类似这样的结果:
{ "game_id": "game_001", "winner": "good", "rounds": 5, "scores": { "gemini-3.5-flash": {"vote_accuracy": 0.761, "skill": 0.733, "camp_win": 0.579}, "gpt-5.5": {"wolf_kill": 0.801, "wolf_win": 0.750}, "qwen3.7-max": {"skill": 0.667, "wolf_kill": 0.749} } }这些数字和 excerpt 里观察到的趋势能对上:轻量模型在好人阵营的投票和神职技能上可能更稳,而对抗性强的模型在狼人刀法上更突出。你跑够 20 局以上,贝叶斯平滑后的分数就相对稳定了。
6. 本篇常见错排查
第一个坑是base_url写错。很多人习惯性写成https://taotoken.net/api/v1,结果 SDK 又补一层/v1,变成/api/v1/v1/chat/completions,直接 404。正确写法是https://taotoken.net/api,让 SDK 自己补路径。
第二个坑是模型名不匹配。TaoToken 的模型名和厂商官方名可能不完全一样,比如你写gemini-3.5-flash能通,写gemini-3.5-flash-latest可能就报模型不存在。建议先在模型对话页确认可用模型名,再写进config.toml。
第三个坑是并发太高导致超时。9 个模型同时请求,如果对局引擎用同步调用,一轮发言可能要等 9 次串行返回。我的做法是用asyncio并发,但把max_retries设成 3,timeout设成 60 秒,避免个别模型慢拖垮整局。
import asyncio from openai import AsyncOpenAI aclient = AsyncOpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url="https://taotoken.net/api", ) async def ask_async(model: str, messages: list) -> str: resp = await aclient.chat.completions.create( model=model, messages=messages, timeout=60, ) return resp.choices[0].message.content async def gather_speaks(players, messages): tasks = [ask_async(p["model"], messages) for p in players] return await asyncio.gather(*tasks, return_exceptions=True)第四个坑是日志里模型返回格式不统一。有的模型会输出我投票给3号,有的输出VOTE: 3,解析时要写兼容逻辑。我一般用正则先抽数字,再结合上下文判断是投票还是发言。
第五个坑是 Key 权限问题。如果你在控制台建 Key 时限制了模型范围,但config.toml里写了范围外的模型,会报 403。排查方法是先用最小请求脚本单独测每个模型,确认都能通再跑整局。
7. 继续接入与验证
整套环境跑通后,你可以把对局局数调大,观察模型在多轮博弈里的稳定性。我自己的经验是,单局结果波动很大,跑 20 局以上再看维度分数才有参考价值。日志采集这块,建议每局单独一个 JSONL 文件,文件名带时间戳,方便后续做批量分析。
如果你要接更多模型,只需要在config.toml的[[players]]里加座位,模型名从模型对话页确认后填进去。API Key 和接入文档在https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=和https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,遇到通道问题先查文档里的错误码说明。长期跑多模型评测的话,Coding Plan 入口在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,可以按需了解。
最后留一个实用技巧:对局引擎里给每个模型加一个temperature字段,狼人杀这种博弈场景,温度设 0.7 到 0.9 比较合适,太低会让模型发言过于保守,太高又容易胡言乱语。这个参数在config.toml里按座位单独配,跑几局对比一下,你就能找到每个模型比较“像人”的区间。