物理仿真击剑对抗:盲评大模型推理能力的新方法
这个项目的玩法一句话就能说清:两个 LLM 分别控制一个剑客,在物理仿真场景里互相对战,人类观众看不到模型身份,只凭战斗过程和双方给出的决策理由,盲投票判断谁“更会推理”。它看起来像娱乐向的 AI 对战游戏,但实际是在做一件更难的事:把大模型的推理能力放进一个需要空间感知、实时决策、风险权衡的动态环境里做评测。
这类实验最适合谁看?如果你正在做 LLM Agent、多智能体对抗、仿真环境评测,或者你只是想判断“哪个模型更值得在真实任务里信任”,这个思路比单纯刷排行榜更有参考价值。它最值得关注的点不是谁赢,而是评判标准从“答案对不对”变成了“决策过程合不合理”。
下面我按实际落地顺序拆一遍:先讲这个实验到底在测什么,再讲环境怎么搭,然后讲怎么跑出可复用的结论,最后讲我会重点盯哪些坑。
1. 这个项目真正值得关注的是“评测方式”,不是打斗本身
1.1 传统基准测的是知识,这个实验测的是“移动中的推理”
常规的 LLM 评测,比如回答数学题、写代码、做知识问答,本质上是测模型在静态输入上的能力。你把问题喂进去,模型给答案,然后比对正确率。这类评测有个天然短板:模型不需要理解“先后顺序”和“状态变化”也能蒙对部分题目。
物理仿真里的击剑对抗不一样。两个 LLM 面对的不是一次性的问题,而是一连串不断变化的状态:对手位置、自己的血量和体力、攻击距离、地图障碍、上一次动作造成的结果。模型必须根据当前状态做出动作选择,动作执行后又会产生新状态,再被模型感知,形成闭环。这种“感知—决策—执行—再感知”的循环,才是真实 Agent 任务里最常遇到的结构。
我见过很多模型在单轮问答上分数很高,但放进这种循环环境里就露馅:要么只盯着最近一句状态描述,忘了三回合前自己已经丢了半管血;要么反复输出同样的攻击指令,完全不管动作已经被物理引擎判定为落空。这个项目把这种差距放到了观众面前,而且是让人直观看到的。
1.2 盲评到底在消除什么偏差
如果直接告诉评委“这场是模型 A 对模型 B”,投票结果几乎一定会被品牌偏好污染。人对知名模型的预期会影响判断,哪怕提示词里写“只评估现场表现”,也挡不住先入为主。
盲评的核心动作是:评委只能看到战斗界面、双方的动作序列,以及模型在关键节点给出的决策说明。哪个模型说了哪句话、出了哪只角色,全部打乱加密。这样一来,评委被迫把注意力放在“这个决策在当时状态下是否合理”,而不是“这句话听起来像某某模型”。
这里有个容易忽略的点:盲评不是盲猜。评审仍然需要规则,否则就成了凭观感投票。所以这个项目的隐藏设计是“谁的推理更好”,不是“谁的操作更炫”。推理好不好,需要一套可解释的维度,我后面会专门展开。
我个人的判断是,这种盲评比传统的人工打分更接近真实使用场景。真实业务里,用户根本不关心你底层用的是哪个模型,只关心这个 Agent 在动态环境里会不会做出让人放心的决策。而盲评恰好可以模拟这种“不知道底牌、只看表现”的状态。
2. 搭一个可复现的 LLM 物理对抗 Demo,核心是分层
如果你也想复现类似玩法,不要一上来就把所有东西堆在一起。我建议把系统拆成三个独立模块:物理仿真层、LLM 决策层、盲评展示层。每一层单独调试,最后再串联。
2.1 物理仿真层:动作空间越简单,结果越好归因
物理仿真层负责角色移动、碰撞、攻击判定、血量体力变化。它本身不需要多复杂,一个 2D 场景加简单刚体碰撞就够用,关键是动作空间要收敛。
常见的动作集可以这样设计:
| 动作 | 说明 | 对推理的影响 |
|---|---|---|
| 前进/后退 | 控制距离 | 体现对攻击距离的判断 |
| 左移/右移 | 横向走位 | 体现对空间位置的理解 |
| 攻击 | 有前摇和后摇 | 体现对时机和体力的规划 |
| 格挡 | 减少伤害但消耗体力 | 体现防守意识和资源管理 |
| 等待 | 不做动作恢复体力 | 体现节奏判断 |
动作空间越小,越容易把“模型为什么这么选”归因到具体能力。如果你把动作设计成连续坐标加自由挥剑,看起来更炫,但最后出了问题很难判断是模型推理不行,还是动作映射有 bug。
仿真层的另一个重要设计是确定性。同一个种子、同一套输入,应该得到同样的物理结果。否则两个 LLM 明明做出了相同决策,却因为随机碰撞出现了不同结果,盲评阶段就完全没法归因。
2.2 LLM 决策层:状态描述和输出格式决定成败
LLM 不直接操作键盘或鼠标,它只接收状态文本,输出动作指令。中间需要一个封装模块,把物理仿真的状态翻译成模型能理解的文本。
状态描述我建议采用结构化 JSON,而不是自然语言长段落:
{ "round": 12, "my_health": 65, "my_stamina": 40, "my_position": [3.2, 4.1], "enemy_health": 80, "enemy_stamina": 55, "enemy_position": [4.0, 4.8], "distance": 1.2, "last_action": "attack", "last_result": "miss", "cooldown": 2 }模型拿到这个结构后,输出也要求固定格式:
{ "action": "move_back", "reason": "距离只有1.2,攻击判定容易命中,但体力不足,先拉开距离恢复体力" }这里最容易被坑的是输出解析。模型偶尔会输出多余解释、Markdown 代码块标记,或者把 action 拼错。所以封装层一定要做容错解析:先尝试 JSON 解析,失败就按关键字匹配,再失败就默认执行“等待”。不要因为一次解析失败把整个对局崩溃掉。
为什么要用 JSON 而不是自然语言?因为后续统计需要结构化字段。你要分析“模型在低血量时是否更倾向防守”“体力不足时是否还盲目攻击”,如果理由是一段自由文本,清洗成本会非常高。
2.3 运行部署:本地与远程不是非此即彼
说到部署,很多人会问这种 Demo 是不是必须把所有东西放在同一台电脑上。其实这跟以前纠结“ComfyUI 与 LLM 是否必须在同一台电脑上”是一样的逻辑:如果 LLM 走远程 API,跨机器只多一次网络延迟;如果走本地模型,同机部署主要是省传输时间,不影响决策逻辑本身。
真正要考虑的是延迟和稳定性:
- 本地模型:延迟低,但显存占用高,且推理速度影响对局节奏。
- 远程 API:部署简单,但每一回合都要等网络返回,超时和限流要专门处理。
- 混合方案:仿真层本地跑,LLM 决策层封装成统一接口,底层随便切换。
我更推荐一开始就把 LLM 调用封装成统一接口。这样同一套物理仿真代码,可以分别接本地模型、远程模型,甚至换不同框架实现。很多现成的 LLM Agent 框架已经帮你把工具调用、记忆、重试封装好了,直接用框架能省掉不少重复工作,但要注意框架本身的日志和重试策略可能影响对局节奏。
3. 从最小样例到稳定评测,建议按三个台阶走
3.1 先把一局跑通,再谈对抗
我第一次跑这种项目时犯过两个错误:一是直接上两个模型对战,结果两边都在输出无效动作,根本分不清是仿真问题还是模型问题;二是让模型每回合都输出超长理由,导致整个对局拖了接近二十分钟。
正确顺序应该是这样:
- 先用固定脚本控制一个角色,比如每两回合攻击一次,验证物理仿真正常。
- 再让一个 LLM 控制一个角色,对手用简单规则脚本,验证状态输入和动作输出链路。
- 确认单 LLM 能完成“移动—攻击—防守”闭环后,才让两个 LLM 真正对上。
每一步都要看日志。仿真层日志记录每回合坐标和血量,决策层日志记录模型收到的状态和输出的动作。两边能对上,才能继续往下走。
3.2 控制变量与轮次设计
当你开始正式采集数据,必须控制变量。最基础的控制项包括:
- 同一组模型必须对战多个不同种子,不能只跑一局就下结论。
- 两个模型在同一回合制下运行,不能一个实时、一个回合制。
- 状态描述格式必须完全一致,不能给模型 A 更详细的信息,给模型 B 简略信息。
- 温度参数要固定,一般评测场景建议调低,比如 0.2 以下,减少随机性干扰。
回合制比实时对战更适合评测。实时对战看起来很刺激,但每回合的响应时间完全取决于模型推理速度,导致模型之间竞争不公平。回合制把响应时间抹平,让所有人把注意力放在决策质量上。
轮次数量上,我建议每个模型组合至少跑 20 局以上,每局回合数至少 50 回合。样本太少时,一两次运气好就能完全改变胜率。
3.3 投票数据怎么记录和清洗
盲评阶段要收集的不是简单的“谁赢”,而是评委的选择和理由。我的建议是至少为每位评委记录这些字段:
| 字段 | 示例 |
|---|---|
| 对局 ID | battle_012 |
| 评委 ID | voter_08 |
| 选择 | A / B / 平局 |
| 判断维度 | 战术 / 风险 / 防御 / 解释 |
| 一句话理由 | “A 在低血量时选择撤退,我认为更合理” |
| 置信度 | 高 / 中 / 低 |
清洗投票数据时要注意几个常见问题。如果一位评委把大多数对局都评给同一侧,要看是不是盲评机制泄露了模型身份;如果大量评委选择“平局”,说明模型之间差异不明显,而不是评委不认真;如果理由和选择矛盾,比如选了 A 但理由夸的是 B,那就需要剔除这条记录。
我比较推荐在投票界面强制要求填写“判断维度”和“一句话理由”,避免评委只凭“看起来厉害”随便点一下。理由多了以后,你还能做文本聚类,看看大众普遍认为的“合理推理”集中在哪些行为模式上。
4. 评判标准:谁的推理好,不能只看谁的血条先空
4.1 战术意图是否连贯
只凭胜负判断推理质量,是这个项目最大的误区。一个模型可能因为初始位置优势赢得比赛,但它的每一步决策都缺乏连贯性;另一个模型虽然输了,却在每个状态转换点都做出了合理的战术调整。
连贯性怎么看?核心是检查模型是否能记住自己的长期目标。比如模型一开始说“我要保持距离,消耗对手体力”,那接下来几个回合的动作应该围绕这个目标展开。如果它第一回合拉开距离,第二回合又莫名其妙冲到对手脸上攻击,这就是战术意图断裂。
这里可以给模型加一个“策略宣言”字段。每 10 回合让模型输出一次当前策略,比如“目前血量优势,采取压制打法”。然后再看它的动作是否匹配自己的宣言。这个字段对后期评估特别有用,因为它直接把模型的“内在意图”暴露出来了,比事后让模型总结要可靠得多,因为事后再解释很容易变成编故事。
4.2 风险判断是否匹配状态
击剑对局里,风险判断比操作精度更值得评审。一个典型的合理决策是:对手血量很低,自己体力不足,此时强行攻击可能被反杀,所以选择保守恢复,等下一轮再终结。一个典型的不合理决策是:自己只剩 10% 血量,体力为 0,仍然选择攻击,理由是“我还有一次机会”。
第二种情况在模型输出里非常常见,本质上是因为模型只看局部状态,没有把血量、体力、攻击距离组合起来做全局评估。评审时应该重点关注“在低血量下是否降低进攻频率”“在体力不足时是否优先恢复”“在对手高冷却时是否增加进攻频率”这三类行为。
这也是物理仿真相比纯文本问答最有优势的地方:风险是可视化的,是能量化的。一个决策合不合理,可以直接对应到最终存活概率的变化,而不是评审的主观猜测。
4.3 解释质量与事后复盘
模型每回合给出的 reason 字段,是最容易被人忽视的评测资产。同一个动作,解释质量可以天差地别:
- 差的解释:“因为我想攻击。”
- 中等的解释:“对手距离近,所以攻击。”
- 好的解释:“对手攻击后有 2 回合冷却,我体力足够,此时主动贴近可以打一套连击,即使被格挡,也能消耗对手体力。”
后一种解释说明模型理解了时间窗口、资源消耗和交换比。盲评时,这种“可验证的推理过程”应该比动作本身占更高权重。也正因为如此,这个项目才会强调“谁 reasoning 更好”,而不是“谁的手速更快”。
建议在评审界面里,把每个关键节点的动作和 reason 并排展示,评委可以针对某个节点打分,也可以对整局投票。关键节点的识别可以自动完成:比如血量首次低于 30%、攻击连续落空两次、体力耗尽等,这些节点最能检验模型的风险判断能力。
5. 常见坑与排查顺序
5.1 两边模型都表现很“笨”
如果你看到两个 LLM 都在原地发呆、反复打空、或者做出一眼看起来离谱的动作,先不要急着换更强的模型。按照下面的顺序排查:
- 先看状态文本是否完整。模型是否收到了自己当前坐标、对手坐标、血量、体力、冷却时间。
- 再看动作映射。模型输出的动作是否被正确翻译成物理引擎指令,有没有方向颠倒、坐标单位不一致的问题。
- 查 Prompt 是否给了充分的“游戏规则”。模型可能根本不知道攻击有前摇、移动会消耗体力,这是提示词缺失,不是模型能力问题。
- 最后再换模型对比。如果换了模型还是表现一致地差,那大概率是环境设计问题,不是某个模型的问题。
这类问题里,最常见的是状态文本里用了浮点数坐标但没给单位,或者把“距离”写成向量而不是标量,导致模型对距离没有直观判断。我一般会在状态 JSON 里额外加一栏“distance_to_enemy”,直接给出标量距离,模型的表现立刻会提升一截。
5.2 投票结果明显偏向某一边
盲评出现严重偏向时,先检查是不是身份泄露了。常见泄露渠道包括:模型 A 的响应速度明显更快,评委通过节奏判断出身份;模型 A 的输出格式和模型 B 不同,一个有详细理由一个只有动作;某个模型频繁报错重试,导致它实际参与有效决策的回合数更少。
如果身份没有泄露,再看评委结构。评委如果都是同一类背景,比如全是开发者,可能会更偏好“技术性解释”;如果全是普通玩家,可能会更偏好“看似激进的操作”。这不是 bug,但结论要标注限定范围,不能说这个模型在所有人群眼里都更会推理。更稳妥的做法是采用多个来源的评委,并分别做统计。
还有一种情况:某个模型在一局里做出了一次惊人的翻盘,评委因为这次“高光时刻”忽略了整局决策的不稳定。所以我不建议只看最终投票结果,要同时统计“决策合理率”,也就是把各回合动作交给多个标注者做一致性打分,再和盲评总投票对照。如果两者冲突,说明评委可能被极端事件带偏了。
5.3 版本、种子和随机性带来的不可复现
做对抗评测最怕的是结果不可复现。今天跑出来模型 A 赢,明天因为模型版本更新、框架升级、随机种子变化,结果就反过来了。
我的建议是:
- 固定模型版本和温度参数,记录到对局配置里。
- 固定物理仿真种子,每一组对战使用同一批种子。
- 对 LLM 输出做确定性增强。如果模型 API 不支持种子,至少把温度设为最低,并增加请求重试时的超时控制。
- 每次评测前记录依赖版本、Agent 框架版本、物理引擎版本,方便回头排查。
这里尤其要提醒:不要因为“模型版本更新了”就把旧结果直接丢弃,而是要另开一组对照。新旧版本对比本身也是有价值的评测结论,但如果你混在一起跑,最后得到的就是一团乱账。
还有一个小坑:日志里别只记录最终胜负,要记录每一回合的完整历史和投票原始数据。很多问题到复盘阶段才发现,如果当时没有保留回合级数据,就只能重跑对局,成本非常高。我在项目里会习惯把每回合的状态、动作、理由、执行结果都存成独立 JSON 行,一个对局一个文件,复盘时直接按对局 ID 查询。
最后留几个我会优先记住的判断
这类项目真正落地时,最该盯住的不是模型打斗多精彩,而是三件事:状态描述是否完整、动作输出是否符合格式、投票数据是否干净。只要有一环偷懒,得出的结论就会被质疑。
对普通学习者来说,把两个 LLM 放进物理仿真里对打,其实是很低成本理解 Agent 机制的方式。它能逼你去处理状态管理、提示词设计、输出解析、数据统计,这些能力在真实 Agent 应用里全都会用到。对已经做 Agent 开发的人来说,这个项目的参考价值在于提供了一种新评测思路:用动态环境里的决策过程评价模型,而不是只看一次性的答案正确率。
如果只是跑着玩,默认的简单动作集和 20 局对局量就够用了。如果想要形成可信结论,建议把盲评规则、评委来源、回合级日志这三样提前设计好,它们比任何参数都更重要。我踩过几次坑之后最大的感受是:这类实验很少有真正“模型不行”的结论,更多时候是环境没描述清楚、日志没留全、评审规则没有固定下来。先把这些底层问题处理干净,再去讨论哪个模型更会推理,才有意义。
