
前沿AI模型来当足球俱乐部经理机器人球员负责在比赛引擎里跑完90分钟这样一个机器人足球联赛最近在 Show HN 上被不少开发者讨论。很多人第一眼会把它当成足球游戏但拆开看它本质上是一个多智能体决策沙盒模型的输出变成阵容和战术比赛引擎算出结果数据再回流给模型做下一轮决策。如果你在折腾 LLM Agent 落地、多智能体模拟或者体育数据类产品这类项目值得花时间自己复刻一遍。这个项目的核心看点不是“哪个模型赢”而是大模型面对长期赛季、伤病名单、对手风格和积分压力时能不能像真实教练一样持续做出合理决策。它比单纯的答题型 Agent 测试更接近真实业务有状态、有延迟反馈、有成本约束、有并行请求。下面按我建议的落地顺序拆一遍。1. 先理解联赛角色模型是经理机器人是球员1.1 管理决策和比赛动作是两层不要混在一起第一件要搞清的事情是不要让大模型直接控制机器人踢球。如果让 LLM 去输出每一个传球、射门、跑位动作延迟会高得离谱而且模型在实时控制上并不可靠。这类项目更合理的设计是分层大模型作为俱乐部经理决定首发名单、阵型、战术倾向和换人方向机器人或比赛引擎作为执行层根据这些决策模拟比赛。为什么要这样拆因为决策层的输出频率低比赛前一次、某些事件节点一次而执行层的输出频率高需要实时计算。LLM 只适合低频率、高语义的决策。把层拆开之后比赛引擎可以先用规则模拟之后换成更复杂的仿真器模型层无需改动。1.2 和 RoboCup、足球经理游戏的区别如果接触过 RoboCup会发现它在做的是机器人底层控制、协作路径规划和实时对抗更多是强化学习或传统控制问题。传统足球经理游戏里经理决策由游戏内置函数和数值计算完成规则是写死的。而这个项目的差别在于经理决策由大模型以自然语言理解状态后生成。模型能读“后卫主力受伤了”“对手最近三场都是防守反击”然后基于这些语义信息给出自己的安排。这种能力是传统写死规则很难覆盖的也是这类项目最值得观察的地方。1.3 这个方向适合谁适合的人群很具体在写 Agent 应用、想让模型做复杂决策但不想一开始上厚重环境的人或者做游戏 AI、体育数据产品想验证大模型能不能生成有实际价值战术的人。基础要求是 Python、能调大模型 API比赛引擎用一个简单概率计算就能跑。不需要一开始就买机器人硬件。用规则引擎、表格数据、一个排行榜页面就能把一个赛季跑起来。等核心链路稳定再考虑视觉化或物理仿真。1.4 项目价值判断标准判断这类项目成不成功不能只看积分榜。我会关注模型是否真的在做决策比如主力受伤后有没有换人连续输球后有没有换阵型遇到强队时有没有更保守的战术。如果模型每轮都输出同一套阵容、同一个战术比赛结果完全靠随机数决定那这个项目就退化成了随机数生成器大模型这个环节就没有意义。所以在一开始设计时就要给模型制造“必须做选择”的状态差异。比如每轮加入身体疲劳偶尔让某一个主力伤停或者给不同对手设计不同风格。这些差异是观察模型能力的前提没有差异就没有决策。2. 架构先拆成四块状态、决策、比赛、记录2.1 四个模块各管一段一个最小联赛系统可以抽象成四个模块状态模块保存每支球队的球员、伤病、积分、近期战绩和联赛赛程。决策模块把球队状态和对手信息组装成 prompt调用大模型返回阵容和战术。比赛模块利用决策和球员能力模拟比赛产出比分、进球事件和球员状态变化。记录模块把输入、输出、比赛结果和积分变化写入日志与数据库。四个模块之间用标准数据结构传递不要为了省事直接在模块里互相读私有字段。后期调模型、换比赛引擎时数据结构稳定能省很多时间。2.2 为什么不建议一开始就接仿真器很多人看到“机器人足球”就想去接 Webots、ROS 或者强化学习环境这个想法没错但不适合起步。第一版跑不起来的常见原因不是比赛不够真实而是模型返回的 JSON 无法解析、prompt 状态不全、数据库字段对不上。这些和仿真器没有任何关系。先用规则引擎做比赛模块能让调试链路短很多。等到规则引擎能把一个赛季完整跑完再考虑把比赛模块替换成仿真环境。替换时只要保证接口一致模型层和记录层都不用动。我自己的习惯是先让比赛模块做成一个纯函数输入是两支球队的阵容和战术输出是比赛事件和比分。这样一个函数可以在不调模型的情况下单独测试。2.3 环境准备组件最低要求备注Python3.10 以上类型提示和标准库更顺手大模型 API能访问前沿模型接口选一个模型先用别贪多数据存储SQLite 或 JSON 文件初期用 JSON赛季变长换 SQLite比赛引擎一段代码即可不需要物理仿真起步另外提前准备好 API Key并在配置里把模型名、base_url、超时时间写成环境变量。不要硬编码在代码里因为后续可能要对比多个模型。3. 先跑最小闭环一轮比赛不要先做联赛3.1 定义球队数据最小闭环里只需要两个球队对象。每支球队有球员列表、教练模型配置和当前状态。球员字段不用太复杂够决策用就行{ team_id: thunder, players: [ {id: p1, name: GK-1, position: GK, ability: 78, fatigue: 30, injured: false}, {id: p2, name: DF-1, position: DF, ability: 74, fatigue: 40, injured: false}, {id: p11, name: FW-3, position: FW, ability: 82, fatigue: 55, injured: true} ], formation: 4-4-2 }ability 是综合能力fatigue 是疲劳值injured 表示是否伤停。第一版不需要特别复杂的球员属性等模型能稳定决策后再加年龄、身价、薪酬这些字段。很多人习惯先定义 League、Team、Match 类再写主循环这个抽象没问题但会让第一轮调试延迟到来。单场比赛脚本能让你在十几分钟内看到第一份结果这个正反馈非常重要。3.2 构造一个能产出结构化决策的 prompt调用模型之前要把球队状态转成自然语言。不要直接把 JSON 原样堆进 prompt模型虽然能读但长 JSON 会分散注意力。更有效的做法是生成一段像球队报告一样的文本然后在末尾固定要求 JSON 输出你是俱乐部经理。球队当前状态如下 球队thunder联赛排名第3。 伤病FW-3 伤停。 体能DF-1 疲劳较高。 最近5场战绩胜-平-负-负-胜。 对手storm擅长防守反击客场作战。 请选择首发11人给出阵型、战术倾向和换人思路。 只输出 JSON不要额外解释。JSON 结构 {formation: ..., starting_lineup: [p1, ...], tactic: ..., reason: ...}这里最关键的是最后一句。如果不写“只输出 JSON”很多模型会返回一段解释解析代码就会崩。这个坑在跑第一版时几乎一定会遇到所以 prompt 里尽早明确。3.3 解析模型输出模型输出不一定干净。我一般会先尝试提取最外层 JSON再调用 json.loadsdef parse_model_output(text): start text.find({) end text.rfind(}) if start -1 or end -1: raise ValueError(no json found) return json.loads(text[start:end1])这个函数只适合简单场景。真正跑联赛时模型可能把 JSON 放在 markdown 代码块里或者中间有逗号缺失。所以我建议每次原始返回都写进日志解析失败时把原始文本打印出来不要直接让程序崩溃。更稳妥的做法是分两步解析先尝试 json.loads失败后再尝试提取 json 代码块内容最后才把多余说明去掉再解析。每多一层容错批量运行时就能少中断一次。3.4 用规则引擎模拟比赛第一步不用做复杂的物理模拟。可以按球员综合能力计算出两队战力再加入主客场加成、战术克制和随机波动模拟出比分。例如每隔 5 分钟计算一次进球概率进球概率 基础进攻概率 战术加成 随机波动。关键是必须引入随机性并且随机波动不能太小否则强队永远大比分赢弱队永远输联赛结果没有观察价值。这里要注意规则引擎不能只输出一个比分最好输出比赛事件列表比如第几分钟进球、谁进球、有没有红黄牌。这些事件后续可以喂给解说生成器也能让模型看到“为什么这轮赢了或输了”的细节。3.5 验证成功标准最小闭环跑通的标准不是“比赛很真实”而是下面几点都满足模型返回能被解析成 JSON。阵容里包含 11 个有效球员 id。比赛引擎返回比分和事件。日志里能同时看到模型决策、比分和解析结果。同一场比赛重新跑一次模型决策可能不同但程序不会中断。如果某个环节报错先单独测试用固定文本测试 prompt 和解析器再用固定决策测试比赛引擎。别一上来就怀疑模型能力。很多时候问题出在球员 id 不匹配或者比赛引擎读不到阵容。4. 做成联赛轮次、状态摘要和断点续跑4.1 赛程和轮次怎么设计赛程要提前生成。最简单的做法是让每支球队和所有对手碰一次单循环想观察更多轮就加一个双循环。赛程表可以用列表存每一项都包含 round、home_team、away_team。每轮开始前遍历赛程依次处理每场比赛。不要边跑边动态生成赛程否则一旦断点续跑后续轮次的顺序会混乱。赛程主要数据结构是稳定的联赛模块只需要按顺序取下一场对局。4.2 用状态摘要代替完整历史模型上下文有限赛季越长历史数据越多。如果每一轮的每场比赛都让模型看完整