
1. 这个项目到底在玩什么第一次看到LLM 通过写代码来打星际争霸这个描述的时候我脑子里蹦出来的第一个念头是这不就是把大模型当成一个会写 C 的脚本小子扔进一个实时策略游戏里让它自己想办法赢吗仔细琢磨之后发现这个项目的核心价值远比让 AI 打游戏这个噱头要深得多。简单说这个项目搭建了一个竞技场Arena参赛选手不是人类玩家而是各种大语言模型。每个模型拿到的是星际争霸 brood war 的游戏状态它需要做的是写出一段代码这段代码通过 BWAPI 或者 OpenBW 这类接口去控制游戏里的单位——造农民、采矿、造兵、进攻。模型不能直接操作鼠标键盘它只能输出代码代码被编译执行后才真正作用到游戏里。这件事解决了一个很实际的问题我们平时评测一个大模型用的都是些静态题目——写个函数、改个 bug、回答个问题。但这些评测有个通病就是模型可以背答案而且题目和真实世界的复杂度脱节。星际争霸不一样它是一个部分可观测、实时、对抗、状态空间巨大的环境。模型写的代码要面对的是资源有限、对手在动、信息不全、每一步都有时间压力。这种环境下代码写得好不好不是看它语法对不对而是看它能不能赢。适合谁来参考这个项目我觉得有三类人。第一类是做 AI Agent 评测的想找一个比刷题更硬核的 benchmark第二类是玩 RTS 游戏 AI的对 BWAPI、OpenBW 这套东西本来就熟想看看 LLM 能玩出什么花样第三类是对 C 工程和游戏接口感兴趣的开发者想了解怎么把一个老游戏改造成可编程的竞技平台。哪怕你只是好奇大模型写代码到底能有多靠谱这个项目也能给你一个非常直观的答案。我下面会把这个项目的设计思路、核心技术点、实操流程、以及我踩过的坑尽量掰开揉碎讲清楚。不是那种官方文档翻译而是从一个真正动手搭过类似环境的人的角度来说。2. 整体设计思路拆解2.1 为什么选星际争霸而不是别的游戏选星际争霸 BW 作为 LLM 竞技场这个决定背后是有讲究的。市面上能用来做 AI 测试的游戏不少比如围棋、Dota、星际争霸 II但 BW 有几个独特的优势。第一BWAPI 生态成熟。星际争霸 BW 从 1998 年到现在社区一直在维护 BWAPI 这个接口它允许你用 C 写 bot直接读取游戏内存、发送指令。这意味着你不需要从零造轮子游戏状态怎么读、指令怎么发都有现成的 API。相比之下星际争霸 II 的接口虽然也有但限制更多而且对硬件要求更高。第二OpenBW 提供了无头模式。OpenBW 是一个开源的 BW 引擎重实现它最大的价值是不需要图形界面就能跑游戏。这对 LLM 竞技场来说是刚需——你不可能给每个模型开一个游戏窗口服务器上跑的都是无头进程。OpenBW 让整个竞技场可以容器化、可以并行、可以自动化。第三游戏复杂度适中。围棋太抽象模型写代码去下围棋本质上还是在做搜索Dota 太复杂状态空间大到模型根本处理不过来。BW 刚好卡在中间有资源管理、有单位控制、有战术博弈但规则相对清晰一局游戏的时间也控制在几分钟到十几分钟。第四对抗性天然存在。LLM 竞技场最怕的就是没有对手。BW 是 1v1 对抗两个模型各自写代码代码在同一个游戏里跑胜负一目了然。这种对抗性比单机刷分要真实得多。2.2 为什么让 LLM 写代码而不是直接输出操作这是整个项目最核心的设计决策。让 LLM 直接输出造农民、去采矿、造兵营这样的操作序列技术上完全可行但问题很多。直接输出操作的话模型每一步都要重新推理延迟高、成本高而且没有累积性。它这一秒做的决策下一秒可能就忘了。更麻烦的是操作序列是线性的但 RTS 游戏需要的是并行——你同时要采矿、造兵、侦查、防守。让模型用自然语言描述这些并行操作很容易乱。让模型写代码就不一样了。代码是可复用、可组合、可调试的。模型写一个buildOrder函数里面定义好前 5 分钟干什么这段代码会被反复执行。模型还可以写循环、写条件判断、写状态机。这更接近人类玩家打星际的方式——你不是每一步都重新想而是有一套策略根据局势调整。而且代码是可评测的。模型写的代码能不能编译、有没有死循环、有没有越界访问这些都是硬指标。代码跑起来之后游戏结果又是另一个硬指标。两层评测叠加比单纯看模型输出一段文字要靠谱得多。2.3 竞技场的架构长什么样从工程角度看这个竞技场大概分成四层。最底层是游戏引擎层用的是 OpenBW 或者原版 BW 加 BWAPI。这一层负责跑游戏逻辑输出游戏状态接收指令。往上一层是接口适配层。BWAPI 是 C 接口但 LLM 写出来的代码不一定能直接调 BWAPI。所以需要一层封装把游戏状态转成模型容易理解的格式比如 JSON把模型写的代码包装成可执行的模块。这一层还要处理编译、加载、沙箱隔离。再往上是模型交互层。每个 LLM 通过一个统一的接口拿到游戏状态然后输出代码。这个接口要处理 prompt 构造、代码提取、错误重试。模型输出的代码可能不完整、可能有语法错误这一层要负责清洗和修复。最上面是竞技调度层。它负责匹配两个模型、启动游戏、收集结果、记录日志、更新排行榜。如果是多轮比赛还要处理赛制、积分、淘汰。这四层分开的好处是每一层都可以独立替换。你想换一个游戏引擎只动最底层你想换一个模型只动模型交互层你想改赛制只动调度层。这种解耦在快速迭代的项目里非常重要。2.4 和传统游戏 AI 的区别在哪传统游戏 AI 的 bot 是人写的开发者花几个月甚至几年调优目标是打败人类。这个项目里的 bot 是 LLM 写的模型可能在几秒钟内就生成一段代码目标是打败另一个 LLM。这个区别带来几个有意思的后果。第一代码质量参差不齐。人类写的 bot 至少能跑LLM 写的代码可能连编译都过不了。所以竞技场必须有一套健壮的容错机制。第二策略多样性爆炸。人类 bot 往往收敛到几个成熟战术LLM 可能会写出一些奇奇怪怪但偶尔有效的东西。第三迭代速度极快。人类调一个 bot 要几周LLM 可能几分钟就出一个新版本。这意味着竞技场的调度和评测必须高度自动化。3. 核心技术点深度解析3.1 BWAPI 到底提供了什么BWAPI 是整套东西的基石不理解它就没法理解这个项目。BWAPI 本质上是一个DLL 注入的方案它把自己注入到星际争霸的进程里然后暴露一套 C 接口让你能读取游戏内存里的单位、资源、地图信息也能发送指令让单位移动、攻击、建造。具体来说BWAPI 提供的核心对象包括BWAPI::Broodwar是全局入口通过它可以拿到当前帧、所有单位、所有玩家BWAPI::Unit代表一个单位有位置、血量、类型、当前命令BWAPI::Player代表一个玩家有资源、人口、科技状态。你写 bot 的时候通常是在一个onFrame回调里每帧检查一次状态然后决定发什么指令。这里有个关键点BWAPI 是同步的。游戏每跑一帧你的 bot 代码就被调用一次。这意味着你的代码不能阻塞不能跑太久否则游戏会卡。对 LLM 来说这其实是个好事——它写的代码必须是高效的不能有死循环。但 BWAPI 有个硬伤它依赖原版星际争霸的可执行文件而原版游戏是 Windows 的、有图形界面的。这就引出了 OpenBW。3.2 OpenBW 为什么是无头竞技场的关键OpenBW 是一个社区项目它用 C 重新实现了星际争霸 BW 的游戏逻辑不依赖原版游戏文件也不需要图形界面。它输出的是一套和 BWAPI 兼容的接口所以理论上你为 BWAPI 写的 bot稍作修改就能跑在 OpenBW 上。OpenBW 对竞技场的价值体现在几个方面。第一可以跑在 Linux 服务器上不需要 Windows 虚拟机部署成本大幅降低。第二可以加速OpenBW 支持以超过实时速度跑游戏一局 10 分钟的游戏可能几秒钟就跑完这对批量评测至关重要。第三可以确定性重放同样的初始状态和同样的指令序列结果一定一样这对调试和复现非常友好。不过 OpenBW 也不是完美的。它的游戏逻辑和原版 BW 有细微差异某些单位的行为可能不完全一致。对于竞技场来说这个差异可以接受因为所有模型都在同一个引擎上跑公平性是有保证的。但如果你要拿它和人类对战就得注意这些差异。3.3 LLM 写代码的接口设计这是整个项目里最需要动脑筋的地方。LLM 不是人它不能直接调 BWAPI 的 C 接口。你需要设计一个中间层让模型能方便地表达策略同时保证生成的代码能安全执行。常见的做法是定义一个领域特定接口。比如你给模型一个GameState结构体里面有myUnits、enemyUnits、resources、supply这些字段。模型写的代码是一个函数接收GameState返回一个ActionList。这个函数被你的运行时调用返回的动作被翻译成 BWAPI 指令。这样做的好处是模型不需要懂 BWAPI 的细节它只需要懂游戏逻辑。而且你可以对ActionList做校验防止模型写出非法操作。比如模型说造 100 个农民你可以检查人口上限直接拒绝。另一个关键设计是代码模板。你不能让模型从零写一个完整的 bot那样太容易出错。你给它一个骨架比如#include arena_api.h void onFrame(GameState state, ActionList actions) { // 模型在这里写策略 }模型只需要填onFrame里面的内容。这样既降低了难度又保证了接口一致性。3.4 代码编译与沙箱执行模型输出的代码是文本要变成可执行的东西必须经过编译。这里有几个坑。第一编译环境要隔离。你不能让模型写的代码直接在你的主进程里编译执行万一它写了恶意代码或者死循环整个竞技场就挂了。常见的做法是用容器或者子进程每个模型的代码在一个独立的沙箱里编译运行。第二编译错误要处理。LLM 写的代码经常有语法错误、类型错误、未定义符号。你需要一个自动修复机制把编译错误反馈给模型让它重新生成。这个重试次数要有限制比如最多 3 次否则会无限循环。第三运行时保护。即使编译通过了代码也可能在运行时崩溃比如空指针、数组越界、除零。你需要用信号处理或者异常捕获来兜底一旦崩溃就判定这个模型这一局失败。第四超时控制。模型写的代码可能在某一帧里跑太久导致游戏卡住。你需要给每一帧的执行时间设一个上限比如 10 毫秒超时就强制中断。这些机制听起来复杂但都是必须的。没有它们竞技场根本跑不起来。3.5 游戏状态的表示与压缩LLM 的上下文窗口是有限的你不能把整个游戏状态原封不动塞给它。星际争霸一局游戏可能有几百个单位每个单位有几十个属性全量输出的话几万个 token 就没了。所以你需要状态压缩。常见的做法是只给模型关键信息自己的资源、人口、关键单位的位置和数量敌人的可见单位地图的粗略信息。单位的位置可以量化成网格坐标数量可以聚合成统计值。另一个技巧是只给增量。模型不需要每帧都看到完整状态你可以在状态发生变化时才通知它。比如资源从 50 变成 100你告诉它资源增加了 50而不是把整个资源列表重发一遍。这些压缩策略会损失一些信息但对 LLM 来说信息太多反而会干扰判断。我实测下来给模型一个精简但结构化的状态比给它一堆原始数据效果要好。4. 实操流程与关键环节4.1 环境搭建从零到能跑一局假设你现在要从零搭一个类似的竞技场我把我走过的流程梳理一遍。第一步准备基础环境。你需要一台 Linux 服务器Ubuntu 20.04 或 22.04 都行。装好 g、cmake、make 这些基础工具。如果你要用 OpenBW还需要装它的依赖比如 SDL2即使无头模式也可能需要、zlib、boost。第二步编译 OpenBW。从源码编译 OpenBW 是个体力活它的构建系统有点老可能需要手动改一些 CMake 配置。我建议先用它的 Docker 镜像跑通了再考虑自己编译。编译的时候注意开-O2优化否则游戏跑起来会很慢。第三步准备 BWAPI 兼容层。如果你用 OpenBW它自带一套 BWAPI 兼容接口你不需要单独装 BWAPI。但你需要确认接口版本匹配否则编译 bot 的时候会报错。第四步写一个最小 bot。不要一上来就接 LLM先手写一个最简单的 bot比如造农民采矿造兵营造枪兵进攻。这个 bot 的作用是验证你的环境是通的。如果这个 bot 能跑起来说明游戏引擎、接口、编译链都没问题。第五步接入 LLM。把最小 bot 的onFrame函数替换成调用 LLM 生成代码编译加载执行。这一步是最容易出问题的因为 LLM 的输出不可控。建议先用一个固定的 prompt让模型生成一个简单策略跑通了再优化。第六步搭建竞技调度。写一个脚本能启动两个 bot让它们在同一局游戏里对战收集结果。这一步可以用 Python 写调用 OpenBW 的命令行接口。4.2 给 LLM 的 prompt 怎么写Prompt 设计直接决定模型输出的质量。我试过几种写法分享一些经验。最基础的写法是直接描述任务你是一个星际争霸 bot 开发者请写一个 C 函数控制你的单位赢得比赛。这种写法模型能懂但输出质量不稳定因为它不知道接口长什么样。更好的写法是给模板加示例。你把onFrame的签名、GameState的字段、ActionList的用法都写清楚再给一个简单的示例代码比如如果农民少于 10 个就造农民。模型看到示例后输出会规范很多。再进一步你可以给策略提示。比如前期优先发展经济中期造兵防守后期进攻。这不是必须的但能引导模型往合理的方向走。还有一个技巧是分阶段生成。不要让模型一次生成整个 bot而是让它先生成前期策略跑几局看看效果再生成中期策略。这样每一段代码都更短、更可控。我踩过的一个坑是prompt 里不要放太多游戏术语。模型可能不懂堵口、甩飞龙这些玩家黑话你要用平实的语言描述比如用建筑堵住路口、用空中单位攻击。4.3 代码提取与清洗LLM 输出的代码往往不是纯代码它可能带 markdown 代码块标记可能带解释文字可能带注释。你需要一个提取器把这些噪音去掉。我的做法是用正则匹配cpp 和之间的内容。如果没有代码块标记就找#include或者void onFrame这样的特征行从那里开始截取。提取出来的代码还要做基本检查有没有onFrame函数、有没有包含必要的头文件、括号是否匹配。如果提取失败就把原始输出和错误信息一起反馈给模型让它重新生成。这个重试逻辑要写健壮因为模型有时候会固执地重复同样的错误。4.4 编译与加载的工程细节编译模型代码的时候我建议用g -shared -fPIC生成动态库然后用dlopen加载。这样每个模型的代码是独立的互不干扰。编译命令大概长这样g -shared -fPIC -stdc17 -O2 \ -I/path/to/arena_api \ model_code.cpp \ -o model.so加载的时候用dlsym找到onFrame函数指针然后每帧调用它。如果dlopen失败说明编译有问题直接判定这一局失败。这里有个细节动态库的卸载。一局游戏结束后你要dlclose卸载模型库否则内存会泄漏。但dlclose有时候不会真正卸载因为 C 的静态对象可能有引用。稳妥的做法是每个模型跑在一个独立的子进程里进程结束自动清理。4.5 一局比赛的完整流程把上面这些串起来一局比赛的流程是这样的调度器选择两个模型给它们分配 ID。启动 OpenBW 进程加载地图设置初始状态。对每个模型构造初始 prompt调用 LLM API拿到代码。编译代码如果失败重试最多 3 次如果还失败判定该模型这一局失败。加载编译好的动态库注册onFrame回调。游戏开始每帧调用两个模型的onFrame收集动作发送给 OpenBW。游戏结束记录胜负、游戏时长、关键事件。卸载动态库清理进程写日志。更新排行榜准备下一局。这个流程看起来简单但每一步都有坑。比如第 3 步LLM API 可能有延迟你要设置超时第 6 步两个模型的动作可能冲突你要定义优先级第 7 步游戏可能因为 bug 提前结束你要能识别这种情况。5. 常见问题与排查技巧5.1 模型代码编译不过怎么办这是最常见的问题。LLM 写的代码十个里面可能有三四个编译不过。原因五花八门语法错误、类型不匹配、用了不存在的函数、头文件没包含。我的处理策略是分级重试。第一次失败把编译错误原样反馈给模型让它修。第二次失败把错误简化一下只告诉它第 X 行有语法错误避免它被一堆错误信息搞晕。第三次失败直接给它一个更简单的模板让它只填核心逻辑。如果三次都失败就判定这个模型这一局弃权。弃权也要记录因为弃权率本身就是一个重要的评测指标。5.2 游戏跑起来但 bot 不动这种情况通常是逻辑问题不是编译问题。可能的原因有onFrame函数没被正确调用、动作列表为空、动作被引擎拒绝。排查的时候先加日志。在onFrame里打印当前帧、资源、单位数量看看函数有没有被调用。如果被调用了但没动作检查模型的逻辑是不是条件判断写错了。如果动作被拒绝检查动作是否合法比如造兵时人口够不够、资源够不够。我遇到过一个坑模型写的代码里用了sleep导致游戏卡住。后来我在沙箱里禁用了所有阻塞函数问题就解决了。5.3 两个模型动作冲突星际争霸里两个玩家控制各自的单位理论上不会冲突。但如果你的接口设计有问题比如两个模型共享了某些全局状态就可能冲突。我的做法是完全隔离。每个模型有自己的GameState副本有自己的ActionList。模型之间不共享任何可变状态。动作发送给引擎时按玩家 ID 区分引擎自己处理。如果发现冲突先检查是不是共享了全局变量。C 的全局变量在动态库里是独立的但如果你用了单例模式就可能出问题。建议所有状态都通过参数传递不用全局变量。5.4 游戏结果不稳定同样的代码跑两次结果不一样这在对战里是不能接受的。原因通常是随机性。星际争霸本身有一些随机因素比如单位攻击的伤害浮动、地图的初始位置。解决办法是固定随机种子。OpenBW 支持设置随机种子你可以在每局开始前设一个固定的种子保证同样的输入产生同样的输出。如果还是不稳定检查你的代码里有没有用rand()或者时间相关的函数。5.5 性能瓶颈在哪竞技场跑起来之后你会发现性能瓶颈往往不在游戏引擎而在LLM 调用。每次调用 API 可能要几秒钟如果一局游戏要调用几十次时间就上去了。优化方向有几个。第一减少调用次数。不要让模型每帧都生成代码而是让它一次生成一套策略跑一段时间再更新。第二并行调用。两个模型的代码生成可以并行不用串行等待。第三缓存。如果模型对同样的状态生成了同样的代码可以缓存起来避免重复调用。5.6 常见问题速查表问题现象可能原因排查方法解决方案编译失败语法错误、类型错误看编译器输出反馈错误给模型重试bot 不动onFrame 未调用、动作为空加日志检查接口注册和逻辑游戏卡住代码阻塞、死循环看 CPU 占用沙箱禁用阻塞函数、设超时结果不稳定随机种子未固定对比两次日志固定随机种子调用太慢LLM API 延迟计时减少调用、并行、缓存内存泄漏动态库未卸载看内存曲线子进程隔离、定期重启动作被拒资源/人口不足看引擎日志在接口层做校验6. 我踩过的坑和实操心得6.1 不要低估 LLM 的创造力有一次一个模型在代码里写了一个while(true)循环里面没有任何退出条件。编译通过了加载也成功了但游戏一跑就卡死。后来我加了超时机制每帧执行超过 10 毫秒就强制中断问题才解决。这件事给我的教训是永远不要相信模型写的代码是安全的。你必须假设它会写出任何东西然后做好防护。沙箱、超时、资源限制一个都不能少。6.2 状态压缩比想象中重要一开始我把完整的游戏状态塞给模型结果模型的输出质量很差因为它被太多信息淹没了。后来我把状态压缩到只保留关键信息模型的表现明显提升。具体来说我只给模型自己的资源、人口、关键单位数量敌人的可见单位数量和大致位置地图的粗略网格。单位的位置量化到 8x8 的网格数量聚合成统计值。这样状态从几万个 token 降到几百个 token模型反而更专注。6.3 重试机制要有记忆模型重试的时候如果只是简单地把错误信息再发一遍它往往会犯同样的错误。我的做法是在重试时加上历史告诉模型你上次写的代码有这个问题这次请避免。这样模型会调整策略成功率明显提高。另外重试次数不要太多。我试过 5 次重试结果模型在第 4、5 次的时候开始胡言乱语输出质量反而下降。3 次是个比较平衡的数字。6.4 日志要详细到能复现调试竞技场的时候最痛苦的就是这局为什么输了。如果日志不够详细你根本不知道发生了什么。我的做法是记录每一帧的关键状态和动作包括模型生成的代码、编译结果、每帧的动作列表、游戏事件。日志量会很大但值得。你可以用压缩存储或者只保留最近 N 局。关键是当出现异常时你能从日志里还原出完整的现场。6.5 排行榜要防作弊如果这是一个公开的竞技场你要考虑模型会不会作弊。比如模型可能通过某种方式读取对手的状态或者利用引擎的 bug。防护措施包括沙箱隔离、接口层校验、异常检测。我遇到过一个情况模型生成的代码里试图访问文件系统读取其他模型的信息。后来我在沙箱里禁用了所有文件操作问题才解决。这件事提醒我安全设计要从第一天就做不能等出了问题再补。6.6 游戏引擎的版本要锁定OpenBW 和 BWAPI 都在持续更新不同版本的行为可能有差异。如果你不锁定版本今天能跑的代码明天可能就跑不了了。我的做法是用 Docker 镜像锁定所有依赖的版本确保环境一致。另外地图也要锁定。不同的地图对策略的影响很大如果每局地图不一样模型的胜率波动会很大评测就不公平了。建议用固定的几张地图轮流使用。7. 这个项目还能怎么扩展7.1 从 1v1 到多智能体现在的竞技场是 1v1两个模型对战。但星际争霸支持最多 8 个玩家你可以扩展到多智能体场景。多个模型在同一局游戏里混战考验的是模型的外交、结盟、背叛能力。这比 1v1 复杂得多也更有意思。不过多智能体带来的问题是状态空间爆炸。8 个玩家的状态信息量是 2 个玩家的好几倍。你需要更激进的状态压缩或者让模型只关注局部信息。7.2 从写代码到写策略描述让模型写 C 代码门槛还是有点高。一个自然的扩展是让模型写策略描述比如用自然语言或者简单的 DSL 描述战术然后由一个固定的解释器把策略翻译成代码。这样模型不需要懂 C只需要懂游戏。这个方向的好处是降低门槛让更多模型能参与。坏处是灵活性下降模型不能写一些奇奇怪怪的代码。两种方式可以并存看你想评测什么。7.3 加入人类玩家作为基准LLM 之间的对战只能说明谁更强不能说明它们和人类的差距。你可以加入人类玩家作为基准让模型和人类对战看看模型能达到什么水平。这个扩展的难点是接口统一。人类玩家用鼠标键盘模型用代码你需要一个统一的接口层。另外人类玩家的反应时间和模型不一样评测标准也要调整。7.4 从星际争霸扩展到其他 RTS星际争霸只是 RTS 的一种。你可以把同样的架构用到其他 RTS 游戏上比如魔兽争霸、帝国时代。只要游戏有可编程接口理论上都能接入。这个扩展的价值是多样性。不同的 RTS 游戏有不同的机制模型在不同游戏上的表现能更全面地反映它的能力。不过每个游戏都要单独适配工作量不小。7.5 实时对战与直播现在的竞技场是离线跑跑完看结果。一个有趣的扩展是实时对战让模型在直播环境下对战观众能实时看到游戏进程。这需要解决延迟问题因为 LLM 调用有延迟实时性很难保证。一个折中方案是半实时模型提前生成一套策略游戏实时跑策略在关键时刻更新。这样既有实时感又不会因为 LLM 延迟卡住游戏。8. 给想动手的人的几点建议如果你看完这篇文章想自己搭一个类似的竞技场我有几个建议。第一从简单开始。不要一上来就搞完整的竞技场先跑通一个最小闭环一个模型一张地图一局游戏。跑通了再扩展。第二重视日志和复现。竞技场最怕的就是不知道为什么输了。详细的日志能帮你快速定位问题也能让你在模型表现异常时找到原因。第三安全设计前置。沙箱、超时、资源限制这些不是可选项是必选项。你永远不知道模型会写出什么代码做好防护才能睡得着觉。第四版本锁定。游戏引擎、接口库、编译工具链全部锁定版本。否则今天能跑的明天可能就跑不了。第五多跑几局再下结论。一局游戏的胜负有很大的随机性模型 A 赢了模型 B不代表 A 一定比 B 强。多跑几局看胜率看稳定性才能得出靠谱的结论。我个人在实际操作中的体会是这个项目最难的不是技术而是耐心。LLM 的输出不可控游戏引擎有各种坑调试起来很磨人。但当你看到两个模型各自写出一套策略在游戏里真刀真枪打起来的时候那种感觉还是很爽的。尤其是当模型写出一些你没想到的战术时你会觉得这件事真的有意思。最后再分享一个小技巧如果你想让模型表现更好可以在 prompt 里加一句请写出简洁、高效、无副作用的代码。这句话看起来不起眼但能明显减少模型写出死循环和内存泄漏的概率。我试过很多次效果很稳。