AI生成小游戏如何烧掉6.9亿token?低成本复刻贪吃蛇的成本控制实战

AI生成小游戏如何烧掉6.9亿token?低成本复刻贪吃蛇的成本控制实战 看到“Opus 5 狂烧 6.9 亿 token 做游戏GPT-5.6 用 5 美元复刻了”这个标题时我的第一反应不是“谁家模型更强”而是“token 账单太可怕了”。做 AI 工程的人都知道token 对普通用户来说是个模糊概念但真到项目落地时它就是真金白银。一次对话看似只生成了几百行代码但如果整个开发过程反复把几万行代码、几十轮历史消息、报错日志全部喂回模型token 消耗会以指数级速度膨胀。你想要的只是一个贪吃蛇小游戏最后却可能烧掉足够写一本长篇小说量的 token。这篇文章不打算争论标题里的两个模型谁更厉害也不对具体版本做性能评测。我想借这个热点把 token 计量、成本估算、AI 生成游戏代码的完整流程以及如何用低成本方式复刻一个小游戏这四件事讲清楚。内容覆盖环境准备、核心代码、token 统计脚本、成本控制方法和常见问题。无论你是刚接触大模型 API 的初学者还是正在为项目控制 API 成本的后端开发者都可以按步骤实操一遍。1. 背景与核心概念token 是什么为什么做游戏会“烧 token”1.1 token 的基本定义token 是大型语言模型处理文本时的最小单位。它可以是一个完整的单词、一个汉字、一个标点也可能是被切分开的半个单词。不同模型的分词器不同同一个字符串在不同模型下的 token 数量也会有差异。以中文为例通常 1 个汉字大约对应 1 到 2 个 token。英文里一个常见单词可能对应 1 个 token但长单词可能被拆成多个 token。代码场景更特殊空格、缩进、符号、变量名都会被计入 token。比如snake_game这种命名可能被拆成若干片段实际消耗比肉眼看到的字符更多。对着这个标题里的“6.9 亿 token”做一个粗略换算如果按中文 1 个汉字约 1.5 token 估算6.9 亿 token 大约相当于 4 到 6 亿字。这个量级相当于几十本长篇小说。哪怕模型单次生成游戏代码只需要几千 token也说明整个过程中一定存在海量的重复输入和多轮交互而不是一次生成就结束。1.2 大模型 API 的成本模式使用大模型 API 时成本通常由两部分组成输入 token 和输出 token。输入 token 包括系统提示词、用户消息、历史对话、可能附带的文件内容输出 token 是模型本次生成的内容。计算公式可以写成本次请求成本 输入 token 数 / 1,000,000 × 输入单价 输出 token 数 / 1,000,000 × 输出单价不同平台、不同模型的单价完全不一样而且价格调整频率很高所以这里不写死具体数字。重要的是输入 token 和输出 token 通常是分别计费的输出 token 往往比输入 token 更贵。对于代码生成类任务模型通常要输出大量代码输出端的成本占比会很高。很多开发者在第一次接入大模型 API 时容易忽略一个细节对话接口不是只发送当前这一句话而是会把整个历史消息全部重新发送一遍。也就是说第 50 轮对话时前 49 轮的内容都要作为输入重新计费。这就是“烧 token”最直接的原因。1.3 为什么 AI 生成小游戏会消耗大量 token生成一个小游戏表面需求很简单比如“用 HTML 写一个贪吃蛇”。但在实际开发过程中你可能需要经历这些阶段需求讨论让模型理解规则、界面布局、操作方式。原型生成生成第一版代码。多轮修改增加分数、难度等级、音效、移动端适配。调 Bug把浏览器控制台报错复制给模型。代码合并如果项目拆成多个文件每次修改都重新发送所有文件。每个阶段都会产生新的输入 token 和输出 token。尤其是调 Bug 阶段报错信息看起来只有几十行但为了让模型理解上下文往往要把完整的 HTML 文件重新发送一遍。如果这个 HTML 文件本身就有 500 行每次调试都相当于重新读一遍 500 行代码。多来几轮token 数量自然暴涨。所以标题里“狂烧 6.9 亿 token 做游戏”虽然极端但它揭示了一个真实问题不做 token 成本控制AI 编程工具能把小项目做成“大账单”。反过来“5 美元复刻”说明只要控制好上下文长度、提示词结构、生成轮次和输出规模这个成本是可以被压到很低的。2. 实验场景解读低成本复刻的思路是什么这里我把标题里的两个模型当成两种开发策略的代号不讨论具体版本的真实能力。更值得关注的是它们背后的成本差异实验方案token 消耗特征成本控制方式适合场景全自动重试方案长上下文、多轮修改、反复发送完整文件不设上限追求一次成功模型能力验证、复杂任务探索低成本复刻方案短上下文、结构化提示词、单次或少量轮次生成限制输出长度缓存复用提前裁剪历史明确需求、目标产物清晰的小游戏从工程角度看低成本复刻一个游戏并不需要“魔法”它依赖的是一套可复制的成本控制方法需求写清楚把游戏规则、技术栈、文件约束一次性描述完整。限制生成范围只生成单个 HTML 文件避免多文件项目带来的重复上下文。少用多轮修改尽量把需求一次性提到位而不是让模型反复重写。控制输出长度设置max_tokens避免模型输出冗长解释。统计日志记录每次请求的 token 用量及时发现问题。这篇文章后面的实战部分会带你用一套简单的 Python 脚本生成一个贪吃蛇游戏并且统计整个过程中的 token 消耗。你不需要真的拥有标题里的模型账号只需要一个支持 OpenAI SDK 格式的大模型服务即可。关键是掌握这个成本计算和开发流程换成任何模型都能套用。3. 环境准备与版本说明3.1 基础环境本文示例以 Python 3.10 和 Node.js 18 为主要环境。Node.js 用来启动一个本地静态服务器Python 用来编写 token 统计脚本和 API 调用脚本。如果你的电脑里还没有装这些工具可以先去对应官网下载安装。版本不需要严格一致但 Python 版本建议 3.10 以上因为新版openaiSDK 对旧版本 Python 支持不够友好。3.2 Python 依赖在项目目录下创建requirements.txtopenai1.30.0 tiktoken0.6.0 python-dotenv1.0.0openai是官方 SDK也可以兼容很多第三方兼容服务。tiktoken是用来做本地 token 预估的工具。python-dotenv用来读取.env配置文件中的 API Key避免把密钥写死在代码里。安装命令pip install -r requirements.txt3.3 项目结构整个项目建议按下面的目录结构组织ai-snake/ ├── .env.example ├── requirements.txt ├── count_tokens.py ├── generate_snake.py └── snake.html其中.env.example是一个模板文件里面填写环境变量名不填真实密钥。.env文件存放真实的 API Key提交代码时不要提交它。.env.example内容如下API_BASEhttps://api.example.com API_KEYyour-api-key-here MODEL_NAMEyour-model-name PRICE_PER_M_INPUT0.15 PRICE_PER_M_OUTPUT0.60这里的API_BASE和MODEL_NAME需要根据你实际使用的服务来填。如果你使用的是 OpenAI 官方服务API_BASE可以留空SDK 默认会访问官方地址。如果使用的是国内合规平台或企业内部网关则需要填写对应的 base url。请务必遵守平台的服务条款和当地法律法规。4. 核心原理拆解token 是怎么被“烧”掉的4.1 使用 tiktoken 预估 token 数量在调用 API 之前我们可以先用tiktoken在本地预估一段文本的 token 数量。这样不需要真实调用模型就能知道一个提示词大概会消耗多少输入 token。下面这个脚本会读取一段文本并打印 token 数量# count_tokens.py import tiktoken def count_tokens(text: str, encoding_name: str cl100k_base) - int: enc tiktoken.get_encoding(encoding_name) tokens enc.encode(text) return len(tokens) if __name__ __main__: sample 用 HTML、CSS 和 JavaScript 实现一个贪吃蛇小游戏要求使用 Canvas 绘制。 token_count count_tokens(sample) print(f文本{sample}) print(ftoken 数量预估{token_count})需要注意cl100k_base是很多常见模型使用的编码器。如果你使用的是某一款特定模型请以模型官方文档中的 tokenizer 为准。tiktoken的本地预估结果只用于成本估算不能完全替代 API 返回的usage字段。4.2 对话 API 的 token 累计方式大模型对话接口通常接受一个messages列表列表里的每条消息都会作为输入发送给模型。这意味着每多一轮对话系统提示词、历史用户消息、历史助手回复都会被重新发送。用伪代码表示就是messages [ {role: system, content: system_prompt}, {role: user, content: 第一轮需求}, {role: assistant, content: 第一轮生成的代码}, {role: user, content: 加一个分数显示功能}, {role: assistant, content: 第二轮生成的代码}, {role: user, content: 现在报错了报错信息是...}, ]到了第三轮前面 5 条消息都会作为输入重新发送。如果每次助手消息都包含完整的 500 行代码那一轮请求的输入 token 可能就有几千甚至上万。几十轮下来总 token 量就会远超你的直觉。常用的应对方法是对话摘要把历史消息压缩成一条摘要不再每次发送完整历史。对于生成代码这类任务还可以直接在提示词里要求“输出完整代码不要解释”减少助手端的废话输出。4.3 输出长度限制max_tokensmax_tokens是一个非常重要的参数。它限制模型在一次请求中最多生成的 token 数量。如果不设置模型可能生成很长的解释、Markdown 说明、重复内容从而增加输出成本。但也不能设置得太小否则生成一个 200 行的游戏时代码会被截断。合理的做法是根据目标文件的预估行数设定一个足够大的上限同时配合提示词要求“只输出代码不要多余解释”。比如创建一个贪吃蛇单文件游戏目标代码可能是 200 到 400 行那么max_tokens可以设置为 4000。如果目标更复杂再适当调大。5. 完整实战低成本生成并复刻贪吃蛇游戏5.1 需求拆分在调用模型之前先把需求拆成可量化的清单游戏名称贪吃蛇。技术栈HTML CSS JavaScript单文件。游戏界面Canvas 画布、分数区域、开始提示。操作方式键盘方向键控制蛇的移动。核心逻辑蛇吃到食物后变长分数加一撞墙或撞到自己时游戏结束。美化要求简洁配色游戏过程中显示当前分数。把这个需求清单整理成一段完整的中文提示词一次性发给模型能有效减少后续修改轮次。5.2 调用 API 生成游戏代码创建一个 Python 脚本generate_snake.py用来调用大模型 API 并保存生成的 HTML 文件。# generate_snake.py import os import re from dotenv import load_dotenv load_dotenv() from openai import OpenAI client OpenAI( base_urlos.getenv(API_BASE) or None, api_keyos.getenv(API_KEY), ) SYSTEM_PROMPT 你是一个资深前端开发者只输出可直接运行的 HTML 文件不要输出额外解释。 USER_PROMPT 请你用 HTML CSS JavaScript 实现一个贪吃蛇小游戏要求 1. 使用 Canvas 绘制游戏区域 2. 使用键盘方向键控制蛇的移动 3. 显示当前分数 4. 蛇撞墙或撞到自己时游戏结束并显示重新开始提示 5. 所有代码写在一个 HTML 文件中不要使用外部图片或第三方库。 def extract_html(content: str) - str: code_match re.search(rhtml\n(.*?), content, re.S) if code_match: return code_match.group(1).strip() return content.strip() response client.chat.completions.create( modelos.getenv(MODEL_NAME), messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: USER_PROMPT}, ], max_tokens4000, temperature0.4, ) content response.choices[0].message.content html_content extract_html(content) with open(snake.html, w, encodingutf-8) as f: f.write(html_content) print(f输入 tokens: {response.usage.prompt_tokens}) print(f输出 tokens: {response.usage.completion_tokens}) print(f总 tokens: {response.usage.total_tokens}) print(已经生成 snake.html)这里的extract_html函数会处理模型返回 Markdown 代码块的情况。如果模型直接输出纯 HTML那么原样保存。注意temperature0.4这样生成结果相对稳定不会每次生成完全不同的布局。5.3 一个可直接运行的贪吃蛇 HTML 文件如果你不想依赖模型 API也可以直接使用下面这份代码作为目标产物。这份代码是完整可运行的也是我们后面统计 token 的对象。!-- snake.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title贪吃蛇/title style body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #1e1e2e; } .game-wrapper { text-align: center; color: #cdd6f4; } canvas { background: #11111b; border: 2px solid #89b4fa; border-radius: 8px; } .score { font-size: 24px; margin-bottom: 12px; } .restart-hint { margin-top: 12px; font-size: 14px; color: #a6adc8; } /style /head body div classgame-wrapper div classscore当前分数span idscore0/span/div canvas idgameCanvas width400 height400/canvas div classrestart-hint按方向键控制移动游戏结束后刷新页面重新开始/div /div script const canvas document.getElementById(gameCanvas); const ctx canvas.getContext(2d); const scoreElement document.getElementById(score); const gridSize 20; const gridWidth canvas.width / gridSize; const gridHeight canvas.height / gridSize; let snake [{ x: 8, y: 8 }]; let direction { x: 1, y: 0 }; let nextDirection { x: 1, y: 0 }; let food { x: 5, y: 5 }; let score 0; let gameOver false; function generateFood() { let newFood; do { newFood { x: Math.floor(Math.random() * gridWidth), y: Math.floor(Math.random() * gridHeight) }; } while (snake.some(segment segment.x newFood.x segment.y newFood.y)); food newFood; } function drawGame() { ctx.fillStyle #11111b; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle #a6e3a1; snake.forEach((segment, index) { if (index 0) { ctx.fillStyle #89b4fa; } ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 2, gridSize - 2); }); ctx.fillStyle #f38ba8; ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize - 2, gridSize - 2); } function step() { if (gameOver) { return; } direction nextDirection; const head { x: snake[0].x direction.x, y: snake[0].y direction.y }; const hitWall head.x 0 || head.y 0 || head.x gridWidth || head.y gridHeight; const hitSelf snake.some(segment segment.x head.x segment.y head.y); if (hitWall || hitSelf) { gameOver true; ctx.fillStyle #f38ba8; ctx.font 32px Arial; ctx.textAlign center; ctx.fillText(游戏结束, canvas.width / 2, canvas.height / 2); return; } snake.unshift(head); if (head.x food.x head.y food.y) { score; scoreElement.textContent score; generateFood(); } else { snake.pop(); } drawGame(); } document.addEventListener(keydown, (event) { const key event.key; if ((key ArrowUp || key w || key W) direction.y ! 1) { nextDirection { x: 0, y: -1 }; } else if ((key ArrowDown || key s || key S) direction.y ! -1) { nextDirection { x: 0, y: 1 }; } else if ((key ArrowLeft || key a || key A) direction.x ! 1) { nextDirection { x: -1, y: 0 }; } else if ((key ArrowRight || key d || key D) direction.x ! -1) { nextDirection { x: 1, y: 0 }; } }); generateFood(); drawGame(); setInterval(step, 150); /script /body /html这份代码实现了贪吃蛇的完整核心逻辑蛇移动、转向、吃食物、加分、撞墙和撞自身结束。你可以在终端运行下面命令启动一个本地静态服务器然后在浏览器里查看效果python -m http.server 8080打开浏览器访问http://localhost:8080点击snake.html即可运行。5.4 运行验证如果你使用generate_snake.py生成代码运行后会在终端看到类似这样的输出输入 tokens: 312 输出 tokens: 2184 总 tokens: 2496 已经生成 snake.html这里的数字会根据模型和提示词不同而变化。重点不是精确数值而是整个过程可以量化。只要你看到了usage数据说明这次调用的成本已经被记录下来。后续优化只需要关注如何降低这两个数字。6. 成本统计与预算控制6.1 统计一次生成的 token 成本创建count_tokens.py用来在本地统计一个生成文件的 token 预估成本。# count_tokens.py import os import tiktoken from dotenv import load_dotenv load_dotenv() SYSTEM_PROMPT 你是一个资深前端开发者只输出可直接运行的 HTML 文件不要输出额外解释。 USER_PROMPT 请你用 HTML CSS JavaScript 实现一个贪吃蛇小游戏要求 1. 使用 Canvas 绘制游戏区域 2. 使用键盘方向键控制蛇的移动 3. 显示当前分数 4. 蛇撞墙或撞到自己时游戏结束并显示重新开始提示 5. 所有代码写在一个 HTML 文件中不要使用外部图片或第三方库。 def count_tokens(text: str, encoding_name: str cl100k_base) - int: enc tiktoken.get_encoding(encoding_name) return len(enc.encode(text)) def estimate_cost(prompt_tokens: int, completion_tokens: int) - float: price_in float(os.getenv(PRICE_PER_M_INPUT, 0.15)) price_out float(os.getenv(PRICE_PER_M_OUTPUT, 0.60)) cost_in prompt_tokens * price_in / 1_000_000 cost_out completion_tokens * price_out / 1_000_000 return cost_in cost_out if __name__ __main__: with open(snake.html, r, encodingutf-8) as f: code f.read() prompt_tokens count_tokens(SYSTEM_PROMPT USER_PROMPT) completion_tokens count_tokens(code) total_tokens prompt_tokens completion_tokens cost estimate_cost(prompt_tokens, completion_tokens) print(f提示词 token 数{prompt_tokens}) print(f生成代码 token 数{completion_tokens}) print(f总 token 数{total_tokens}) print(f预估成本${cost:.6f})这个脚本的作用是让你在调用 API 之前或之后都能用本地方式估算 token 成本。PRICE_PER_M_INPUT和PRICE_PER_M_OUTPUT只是示例价格实际请以你使用的服务商定价为准。6.2 模拟多轮迭代的 token 膨胀实际开发时模型不会一次性生成完美代码多轮调试很常见。我们可以把单次成本放到一个迭代模型里来看轮次输入 token输出 token累计总 token说明130022002500第一次生成227008006000重复发送历史 修补代码33600120010800继续调试4500060016400优化样式5620040023000最终微调从上表可以看出输入 token 会随着轮次增加而快速增长。第一轮输入只有 300 token但第二轮就变成 2700 token因为完整的 2200 token 代码被当作历史消息重新发送了。到第五轮时单次输入已经超过 6000 token。这就是为什么“6.9 亿 token”并非不可能如果一个人每天让模型反复生成、修改、调试大量项目文件并且每次都把完整代码塞进上下文token 消耗就会非常恐怖。6.3 成本控制手段要避免 token 浪费常用的手段包括设置max_tokens避免模型输出超长废话。使用消息摘要不把全部历史发送给模型。需求一次性讲清楚减少无意义的多轮交互。使用缓存机制相同请求避免重复调用。生成后把结果保存到本地文件后续修改只发送差异部分而不是完整文件。对于小游戏这类目标产物单一的场景最有效的方式是“单次生成 人工微调”。让模型生成完整代码然后由开发者在编辑器里手动调整数值和样式而不是把每次微调都交给模型。这个策略能大幅降低 token 消耗也是低成本复刻方案的核心。7. 常见问题与排查思路在实际操作中你可能会遇到下面这些典型问题。我整理了常见现象、可能原因和解决思路方便开发时对照排查。问题现象常见原因解决思路token 消耗远高于预期每轮都发送完整历史消息和完整文件使用简洁历史摘要只发送必要上下文接口返回 403 region not supported当前网络地域不在平台支持范围内确认平台服务条款使用企业合规接入渠道上下文长度超限历史消息过多或文件过长截断历史消息拆分大文件生成模型返回 Markdown 代码块提示词没有明确“只输出代码”在提示词中强调“不要额外解释”snake.html保存了多余的说明文字没有清洗模型输出使用正则提取 html 代码块成本超出预期没有设置max_tokens限制输出长度并记录每轮 usageAPI Key 被提交到仓库.env没有加入.gitignore创建.gitignore轮换泄露的密钥如果你遇到接口返回 403 region not supported 这类错误我的建议是先仔细查看平台文档确认当前项目和网络环境是否符合平台的服务条款。不要尝试绕过地区限制正确处理方式是切换到合规的企业服务网关或选择在你所在地区合法可用的模型服务。另一个常见问题是“模型生成的代码可以运行但和预期不完全一致”。这通常不是模型能力问题而是提示词不够具体。你可以尝试在提示词里加入更明确的约束比如“游戏区域宽度 400 像素”“食物颜色为红色”“分数显示在画布上方”。需求越具体输出越接近预期。8. 最佳实践与工程建议8.1 先做最小可玩版本做任何 AI 生成类项目都建议先让模型生成一个最小可玩版本而不是第一次就要求完整功能。贪吃蛇的最小版本只需要蛇、食物、移动、碰撞四个要素。先把这些跑通再逐步加分、加音效、加移动端适配。这样做的好处很明显每次新增功能的上下文是可控的模型不会被一大串需求搞乱token 消耗也更低。同时人工验证成本也会下降因为代码量少问题更容易定位。8.2 把提示词当成需求文档管理提示词不是随手写的一句话它本质上是一份需求文档。长期维护 AI 编程项目时建议把提示词保存在版本控制系统里方便回溯。示例建议系统提示词固定不要频繁改动。每次新增需求时只修改用户提示词的增量部分。如果需求复杂拆成多个小提示词分别生成。同一类任务复用同一套提示词模板。8.3 日志与监控不可或缺每次 API 调用都应该记录model、prompt_tokens、completion_tokens、total_tokens、latency_ms、时间戳等信息。最简单的做法是把输出追加到 CSV 文件或日志文件。示例日志格式timestamp,model,prompt_tokens,completion_tokens,total_tokens,latency_ms 2026-01-15T10:11:12Z,demo-model,312,2184,2496,1800当成本异常增长时这些日志能帮你快速定位是哪一轮请求出了问题是提示词过长还是历史消息堆积还是某个脚本在循环调用 API。8.4 安全与合规使用大模型 API 时需要特别注意安全和合规API Key 永远不要提交到代码仓库。生产环境使用密钥管理服务或环境变量注入。不要把用户的隐私数据发送给大模型。模型输出内容需要进行安全过滤不能直接上线到正式业务。使用模型时遵守平台服务条款和当地法律法规。对于游戏开发这类低风险项目安全压力可能不大但一旦把 AI 接入业务系统这些底线不能省。8.5 不要迷信“一次生成”低成本复刻的核心不是让模型一次生成完美代码而是“生成 人工可控修改 再生成”的闭环里尽量压缩不必要的 token 消耗。我的建议是让模型承担“把需求变成代码”的工作让工程师承担“代码走查、功能验证、性能调优”的工作。这才是把 AI 用在正确位置的做法。如果你能完全理解 token 的计费逻辑并且愿意在项目初始阶段就做预算控制那么哪怕只是一个小游戏也能用很少的成本完成复刻。希望这篇文章能帮你把 token 花在刀刃上。