强化学习与进化算法训练Factorio智能体:从蓝图到实战
最近在折腾通过强化学习训练智能体玩 Factorio 的项目,说实话踩坑不少。Factorio 这类自动化工厂游戏,表面上是在“搭产线”,实际上对 AI 智能体来说,是一个包含连续规划、物流调度、多目标优化和稀疏奖励的复杂决策环境。网上相关的资料大多散落在 Reddit、各类 arXiv 论文和游戏社区里,系统性讲怎么把强化学习和进化算法落到 Factorio 上的中文资料很少。这篇文章围绕 “Teaching agents how to play Factorio (using RLM, GEPA)” 这条主线,整理一份可复现的实战笔记,覆盖环境搭建、RLM 与 GEPA 两条技术路线的基本原理、factorio 蓝图网站的先验知识利用方式,以及一套最小可运行的训练工程骨架。无论你是想入门游戏 AI,还是对强化学习在仿真环境中的应用感兴趣,都可以照着本文的流程动手试一遍。
1. 项目背景与核心概念
1.1 Factorio:为什么是 AI 研究的“完美沙盘”
先解释一下 Factorio 是什么。它是一款以自动化流水线为核心玩法的模拟经营游戏,玩家扮演一名坠落到陌生星球的工程师,需要采集铁矿、铜矿、煤炭和石油,通过冶炼、组装、化工等工序,逐步搭建出从采矿到科研的完整自动化产线。游戏的核心目标是把手动操作一步步替换为机器自动完成,最终发射火箭。整个过程中,玩家需要不断扩张工厂、优化物流网络、研究科技树,并抵御虫群的攻击。
从 AI 研究视角来看,Factorio 有几个非常典型的特点:
- 状态空间连续且高维:地图上每个建筑、每条传送带、每个物流机器人的位置和状态共同构成一个动态、稀疏的大规模状态空间。
- 动作空间巨大:玩家可以在地图任意位置放置建筑、连接电线、设置物流请求,动作的组合数远超传统 Atari 游戏。
- 奖励稀疏:游戏前期几分钟内,智能体可能完全得不到任何正向反馈,必须通过中间奖励塑形来解决。
- 长期规划要求高:一条完整的绿瓶生产线往往需要十几道工序,智能体必须学会“为了未来几个小时的收益,忍受当前的低收益决策”。
这些特性让 Factorio 成了一个极具挑战性的测试场。相比围棋、星际争霸等棋牌或即时战略游戏,Factorio 的“复杂度”更多体现在长程决策和资源调度上,和真实世界的供应链优化、工业自动化和物流调度问题有很强的迁移价值。
1.2 RLM 与 GEPA:标题里的两个关键词
标题中的 RLM 和 GEPA 并不是 Factorio 内置的标准术语,而是指两类常用训练策略。
- RLM(Reinforcement Learning Model / Module):泛指以强化学习为核心的训练模块。智能体通过与环境交互,根据奖励信号更新策略,常见算法包括 PPO、DQN、SAC 等。在实际项目中,RLM 通常对应一个封装好的训练器,它接收状态输入,输出动作概率分布,并借助经验回放或在线策略更新来优化网络参数。
- GEPA(Genetic Evolutionary Programming Algorithm):指遗传/进化算法一类的群体搜索策略。它不依赖梯度回传,而是通过选择、交叉、变异来迭代更新一组候选策略(种群)。GEPA 在因子类任务中最大的优点是探索能力强,不容易陷入局部最优,但缺点是采样效率通常低于 RL 方法。
把两者结合,是当前游戏 AI 领域一个很自然的思路:先用 GEPA 的强探索能力在策略空间里找到有潜力的“种子”,再用 RLM 的梯度更新对种子进行精细化训练。这种“先搜索、后精调”的模式,能有效平衡探索与利用,也是标题中项目最核心的技术亮点。
1.3 蓝图网站:社区知识库的价值
在 Factorio 社区里,蓝图(Blueprint) 是一段字符串编码,记录了若干个建筑的坐标、方向、连接关系等信息。玩家可以在游戏内复制自己的产线设计,导出为蓝图字符串,也可以把其他玩家分享的蓝图书签导入到自己的存档中。而 factorio 蓝图网站,正是这一类资源的聚合平台,上面有大量经过验证的熔炉阵列、绿瓶产线、铁路网络等设计。
对于 AI 训练来说,蓝图网站提供了非常好的先验知识来源。因为 Factorio 的最优产线布局往往具有很强的规律性,与其让智能体从零开始随机尝试,不如先从社区蓝图中提取常见“布局模式”,让智能体具备一个合理的初值。这就好比下围棋时先让智能体学习人类高手的高段位棋谱,再用自对弈去超越,两者并不冲突。
2. 环境准备与版本说明
在正式开始前,先把环境准备清楚。由于 Factorio 的 AI 训练涉及游戏本体、Mod 接口、Python 算法库三部分,版本兼容问题是第一个大坑。下面给出本文使用的环境清单,但需要注意:版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。
2.1 游戏环境准备
| 组件 | 说明 |
|---|---|
| 操作系统 | 推荐 Ubuntu 20.04 / 22.04 或 Windows 10/11 |
| Factorio 版本 | 建议使用 1.1 以上稳定版,Mod API 兼容性更好 |
| Headless 模式 | 如果需要服务器集群训练,可准备 Factorio Headless 版本 |
Factorio 的游戏本体可以从官方渠道购买。Headless 版本不包含图形界面,只提供游戏逻辑,非常适合在服务器上批量运行训练环境。
2.2 Python 与强化学习框架
| 组件 | 版本建议 |
|---|---|
| Python | 3.9 或 3.10 |
| PyTorch | 2.0 以上 |
| gymnasium | 0.28 以上 |
| numpy | 1.24 以上 |
| 可视化工具 | TensorBoard 或 wandb |
如果你的显卡支持 CUDA,建议优先使用 GPU 训练。Factorio 环境本身不消耗 GPU,但神经网络的前向传播和反向传播在 GPU 上会明显加速。
2.3 示例项目结构
为了让后面的代码更有条理,我们先规划项目目录:
解释一下几个核心部分:
mods/目录存放 Factorio 的 Mod,负责在游戏内读取状态、执行动作,并与 Python 端通信。src/env/是 Python 侧的环境封装,负责把 Mod 传来的 JSON 状态转成张量,把动作 ID 转成游戏内坐标。src/agent/是算法侧代码,包含 RLM 训练循环和 GEPA 种群进化逻辑。src/blueprint/负责解析蓝图字符串,把它变成智能体的先验记忆或动作参考。runs/存放训练日志和模型权重。
3. 核心框架拆解
3.1 RLM:强化学习模块设计思路
在 Factorio 这种复杂环境中,直接使用标准的 DQN 或 PPO 往往不够,原因是环境状态高维且动作空间巨大。更合适的做法是基于 Actor-Critic 架构,让 Actor 网络负责决策,Critic 网络负责评估当前状态的价值。
下面是一个最简 PPO 训练循环的骨架:
这里我将网络分为特征提取、策略头、价值头三部分。特征提取层相当于智能体的“感知系统”,策略头输出每个动作的 logits,价值头输出当前状态的标量价值。实际使用中,输入特征可能是地图块、建筑列表、物流网络状态等拼接而成的一维向量。
PPO 的核心更新公式利用了新旧策略的比率裁剪,这里不深入数学推导,只需要明白:RLM 模块维护一个策略网络,通过环境交互采样一批轨迹,再利用 PPO 的裁剪目标更新网络参数,如此循环。
3.2 GEPA:遗传进化策略
GEPA 的思路比 RL 更简单粗暴。它不反向传播梯度,而是直接维护一个“策略种群”。每个个体是一组网络权重或一组规则参数,通过让每个个体在环境中试玩若干局,得到适应度分数(例如产线产量、火箭发射进度等),再通过选择、交叉、变异生成下一代。
下面用一段简化的进化循环来说明:
这个示例中的“个体”是一个一维向量,相当于线性策略的权重。真实项目中,你可以让个体代表一个多层神经网络的全部参数,也可以让个体代表一组更高级的决策规则(比如“何时扩建熔炉阵列”的阈值条件)。GEPA 的优点是它的搜索过程天然具备探索性,适合在 RLM 训练的冷启动阶段生成较好的初始策略。
3.3 两阶段训练流程:先进化,再强化
结合 RLM 和 GEPA 的完整流程大致如下:
- GEPA 阶段:使用进化算法在低分辨率、简化规则的 Factorio 环境里搜索可行策略。此阶段不追求精细操作,只希望找到“能跑起来”的产线设计方案和基础决策逻辑。
- 策略迁移:筛选出适应度最高的若干个体,把它们的网络参数作为 RLM 策略网络的初始权重。
- RLM 阶段:在完整 Factorio 环境中,用 PPO 等强化学习算法对初始策略做精细调优。此时智能体已经具备一定的“常识”,不会一开始就乱放建筑。
- 迭代验证:每隔一定训练轮数,用验证地图测试当前策略的产线吞吐量、火箭发射时间等指标,观察是否出现退化。
这种两阶段设计的好处是明显的:GEPA 阶段不需要 GPU 梯度计算,可以在 CPU 集群上大规模并行;RLM 阶段则利用梯度信息高效精调,二者互补。
4. 完整实战案例
下面从零开始搭建一个最小的 Factorio AI 训练闭环。这个流程不会立刻训练出能通关的智能体,但它能把“状态读取 → 决策 → 动作执行 → 奖励返回”四个环节串起来,是后续扩展的基础。
4.1 通过 Mod 获取游戏状态
Factorio 允许使用 Lua 编写 Mod,Mod 可以在每个 tick(游戏刻)里读取地图上的实体信息。我们要做的第一步,是用一个 Mod 把游戏状态序列化为 JSON,并监听来自 Python 端的控制信号。
这个 Mod 在游戏每 tick 时,扫描所有玩家附近熔炉、组装机、传送带三种实体,把它们的名称、坐标和方向记录到全局变量中。这只是最简示例,实际项目还应该包括资源储量、流体管道状态、电力系统等信息。
4.2 Python 侧环境封装
Python 侧需要模拟一个类似 gymnasium 的接口,让强化学习算法能够直接调用。这里用 HTTP 或文件轮询的方式从 Mod 读取状态(因为示例不引入外部通信库,先用文件轮询简化)。
需要注意,上面的文件读写方式只是为了方便理解环境与游戏之间的“握手协议”。在生产项目中,推荐改为本地 WebSocket 或 HTTP 接口,避免大量磁盘 IO 导致训练速度下降。
4.3 动作空间与奖励塑形
Factorio 的动作空间设计是一个重要问题。如果每次动作都指定“在地图某个格子放置某个建筑”,动作空间会爆炸。工程上的做法通常是分层设计:
- 高层动作:选择“建一个熔炉阵”、“扩建公路”、“增加科研瓶产线”。
- 低层动作:根据当前蓝图模板,自动算出具体建筑的摆放位置和连接关系。
为了训练可用,我们先定义一组离散动作 ID,并在 step 中调用蓝图模板库来生成建筑布局。这样动作空间从“地图尺寸 × 建筑类型”压缩到十几个策略动作。
奖励塑形是另一个关键点。稀疏的火箭发射奖励根本无法驱动前期训练,需要拆分成多个中间指标:
- 每分钟铁板产量
- 每分钟电路板产量
- 电力系统余量
- 主要产线是否发生堵塞
- 科技解锁进度
最简单的方式是加权求和,但如果权重设置不合理,智能体可能会只追求单一指标,忽略整体发展。建议在训练早期侧重“产量增长”,后期侧重“科技进度”。
4.4 训练循环骨架
下面把前面两部分拼起来,形成一个可运行的训练骨架。
这是一个非常朴素的 PPO 实现,省略了 GAE(广义优势估计)、梯度裁剪、学习率调度等细节,但它已经能展示完整的“采样 → 计算回报 → 更新策略”流程。实际项目中建议直接使用 Stable-Baselines3 或 RLlib 等成熟库。
4.5 运行与验证
把项目启动分为三个终端:
- 终端一:启动 Factorio 并加载 Mod:
-
终端二:启动 Python 环境通信桥(示例中是文件轮询,因此无需额外进程)。
-
终端三:启动训练脚本:
正常运行后,你会在控制台看到每个 epoch 的平均奖励逐渐上升。如果奖励长期不增长,优先检查三个地方:
- 奖励是否太稀疏?调高中间奖励权重。
- 状态读取是否正常?打印观察张量,确认建筑信息确实被编码到了网格中。
- 动作是否真的执行了?在 Mod 端加上日志输出,确认 action.txt 被读取并触发建筑放置。
5. 常见问题与排查思路
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 训练不收敛,奖励一直为 0 | 奖励过于稀疏,智能体探索不到正向反馈 | 增加阶段性奖励,例如首次建造熔炉、首次产出铁板都给予奖励 |
| 动作空间过大,训练速度极慢 | 直接指定地图坐标导致组合数爆炸 | 改用蓝图模板 + 高层动作,让建筑布局由先验模板决定 |
| Mod 返回状态延迟高 | 文件轮询 IO 开销大,或每 tick 序列化全部实体 | 降低序列化频率,只序列化智能体附近区域;改用 WebSocket 通信 |
| 蓝图字符串解析失败 | 蓝图版本不兼容或字符串被截断 | 确认蓝图字符串完整,使用官方蓝图导入工具验证后再传给解析器 |
| 智能体陷入局部最优 | 策略网络过早收敛到某个低效产线布局 | 引入 GEPA 阶段的种群多样性,或者在 PPO 中提高熵系数 |
| 训练过程中游戏崩溃 | Mod 中访问了不存在的实体或索引越界 | 在 Lua 端对实体做空值判断,并打印错误堆栈 |
针对“训练不收敛”这类最常见的问题,再展开一个排查 checklist:
- 先做一个最小闭环测试:固定策略随机决策,确认环境能正常返回不同奖励。
- 用随机策略跑 1000 个 step,观察奖励均值是否稳定,确定环境没有 NaN 或异常跳变。
- 检查观察值是否经过了归一化,输入过大或过小都会让神经网络训练变慢。
- 如果 reward 范围跨度过大,使用 reward clipping 或 normalization。
6. 最佳实践与工程建议
6.1 用蓝图注入先验知识
项目初期最容易犯的错误,是让智能体从零学习所有建筑布局。而实际上,factorio 蓝图网站已经沉淀了大量经过玩家验证的高效产线设计。建议在训练环境中加入一个“蓝图记忆库”,每次需要新建产线时,智能体可以从记忆库中选择合适的蓝图模板,而不是计算每个建筑的位置。
具体实现时,可以先把蓝图字符串解析成建筑列表,再把建筑列表编码成“动作参数”:
如果智能体能输出“使用 1 号蓝图”,那么动作空间就变成了“蓝图编号 + 放置位置”,比直接输出每个建筑坐标简化很多。
6.2 分层决策架构
在复杂环境里,单一神经网络很难同时完成“宏观规划”和“微观摆放”两个任务。工程上推荐做两层决策:
- 上层策略:每隔一段较长时间(例如游戏内 30 秒)决策一次,决定当前是扩建铁矿、建设科研还是布防。
- 下层控制器:在收到上层指令后,调用预定义的脚本或蓝图模板,完成具体建设。
这种架构和真实游戏 AI 中常用的行为树思想一致。优点是每个模块职责清晰,调试时可以直接观察上层决策是否正确,而不需要从杂乱的动作序列中反推。
6.3 训练稳定性与复现性
RL 训练最大的痛点是不稳定。同一个代码,两次运行可能得到完全不同的结果。为了减少这种不确定性,建议从以下几点入手:
- 固定随机种子,包括 Python、NumPy、PyTorch 和 Factorio 地图生成种子。
- 对游戏运行速度做限制,避免因 tick 不固定导致经验分布偏移。
- 使用统一的场景评估脚本,每隔 N 个 epoch 在固定地图上跑一次完整评估。
- 记录所有关键超参数,并保存每次训练的模型快照。
关于生产环境的安全性,这里也补充一点:如果你把训练环境部署到服务器上,务必使用单独的 Factorio 存档,不要在正式存档上做实验。Mod 中的动作执行逻辑要做好校验,防止越界放置建筑或破坏原有工厂结构。
6.4 性能优化与并行训练
Factorio 的模拟速度是训练效率的最大瓶颈。一个可行思路是:
- 使用多个 Factorio Headless 实例并行采集经验,每个实例绑定一个 CPU 核心。
- Python 端使用多进程分别连到不同实例,将采集到的轨迹存入共享回放缓冲区。
- 训练主进程在每个 epoch 结束时从缓冲区中采样更新策略。
这种架构在工程上并不复杂,核心是把环境交互和网络训练解耦。网络训练在 GPU 上进行,环境交互在 CPU 上并行,二者互不阻塞。
7. 总结与后续学习路线
这篇文章从 Factorio 作为 AI 环境的特点出发,解释了 RLM 强化学习模块和 GEPA 进化算法在其中的组合思路,并给出了从环境准备、Mod 状态读取、动作设计到训练循环的完整工程骨架。你如果跟着文章把代码跑通,至少能掌握一个最基本的多智能体训练闭环:游戏状态经过 Mod 序列化,Python 端解析为张量,神经网络输出动作,动作再回到游戏端执行,奖励驱动网络更新。
接下来值得深入的方向有这些:
- 熟悉一个成熟的 RL 库,把文章中的朴素 PPO 替换成 Stable-Baselines3 或 RLlib 的实现,重点理解 GAE、经验回放、分布式采样等在生产环境中的作用。
- 研究如何解析和筛选 factorio 蓝图网站上的高质量蓝图,把社区先验系统地转换成训练模板库。
- 尝试把 GEPA 阶段的搜索空间从“网络权重”扩展到“产线设计规则”,例如用遗传规划自动发现一套高效的扩展策略。
- 如果你对多智能体感兴趣,可以把场景扩展为多个智能体分别管理铁矿、电路板、科研等模块,并用 RLM 学习它们之间的协作策略。
有几点风险需要特别关注:第一,不要在未备份的正式存档上跑训练,防止 Mod 动作破坏存档数据;第二,采集训练数据前确认状态序列化逻辑的完整性,数据缺失会导致模型学到错误关联;第三,训练集群环境搭建时注意不同 Factorio 实例的存档路径隔离,避免多进程写同一份文件。
把 Factorio 当作 AI 实验场是一件很有趣的事,它的复杂性远比 Gym 经典环境更接近真实工程问题。希望这篇文章能帮你节省一些前期踩坑的时间,也期待看到更多好玩的项目分享。