资讯动态

smol developer 全程序生成实战:从 shared_deps.md 共享依赖契约解读 AI 生成的 Pong 游戏架构

发布时间:2026/10/14 2:34:50 来源:尧图企业网站定制
AI Agent代码智能体【免费下载链接】developerthe first library to let you embed a developer agent in your own app!项目地址https://gitcode.com/gh_mirrors/de/developer点击查看免费下载shared_deps.md是 smol developer当前仓库即其 v1 版本在多文件程序生成管线中产出的关键中间产物一份用 Markdown 写成的“共享依赖契约”。本文以仓库中 examples/v1_pong_game/shared_deps.md 为骨架逐文件拆解这份契约如何描述一个由单条提示词生成的 Canvas Pong 游戏并结合 index.html、style.css、main.js、ai.js 的真实源码说明“变量清单、DOM id、函数命名”等一致性约束如何在生成阶段约束每个文件的代码最后给出在本地复现整个生成过程的完整命令。读完本文你将理解 smol developer 保证“整个程序全局一致”的核心机制并掌握用一条提示词生成并运行一个多文件前端应用的完整方法。一、shared_deps.md 是什么多文件程序的一致性契约1.1 全程序合成的痛点跨文件间接依赖smol developer 的目标是“whole program synthesis”——给定产品规格prompt直接生成一整个代码库。但多文件程序尤其是 Chrome 扩展、前后端分离这类应用存在大量跨文件的间接依赖index.html里的元素 id 要被main.js用getElementById引用CSS 选择器要和 HTML 结构对应多个 JS 文件之间共享函数名与数据格式。只要模型在某个文件里“幻觉”出一个不一致的名字整个程序就会报错。仓库根目录的 readme.md 在 “innovations and insights” 一节明确记录了这一解法We solved this by adding an intermediate step asking GPT to think throughshared_dependencies.md, and then insisting on using that in generating each file. This basically means GPT is able to talk to itself...也就是说先生成一份描述“文件之间共享什么”的清单再让模型在生成每个文件时严格遵守这份清单从而让“模型与自己对话”达成跨文件一致性。shared_deps.md就是这个中间步骤的产物。1.2 在生成管线中的位置plan → specify_file_paths → generate_codesmol developer v1 的生成管线是一个清晰的三段式流程主控逻辑见 smol_dev/main.pyplan规划调用plan(prompt, ...)让模型输出整份代码的规划直接以 Markdown 形式实时写入输出目录的shared_deps.md文件。对应的核心实现是 smol_dev/prompts.py 中的plan()。specify_file_paths定文件清单调用specify_file_paths(prompt, shared_deps)通过 OpenAI Function Calling 强制模型返回一个 JSON 文件路径数组实现见 smol_dev/prompts.py。generate_code逐文件生成遍历文件路径数组对每个文件调用generate_code_sync(prompt, shared_deps, file_path)把“规划”和“当前要生成的文件名”一起交给模型生成单一文件的代码实现见 smol_dev/prompts.py。关键点在于第 3 步的系统提示词会要求模型“不得偏离我们已经商定的文件名和规划”原文do not stray from the names of the files and the plan we have decided on。因此shared_deps.md不只是给人类看的文档更是约束每次代码生成的事实来源。1.3 plan() 提示词规定了 shared_deps.md 的内容结构为什么shared_deps.md里恰好是“变量、数据 schema、DOM id、函数名”这些内容因为plan()的系统提示词smol_dev/prompts.py明确要求In this plan, please name and briefly describe the structure of code that will be generated, including, for each file we are generating,what variables they export, data schemas, id names of every DOM elements that javascript functions will use, message names, and function names.对照本示例的 shared_deps.md可以看到它正是按这一模板组织的先是 4 个文件的职责描述随后是main.js的变量清单playerPaddle、aiPaddle、ball、score、函数清单startGame、updateGame、drawGame、playerMove、aiMove、checkCollision、updateScore、increaseBallSpeed以及ai.js的参数与函数aiSpeed、aiError、aiDecision。仓库中另一个同类产物 v0/exampleChromeExtension/shared_dependencies.mdChrome 扩展示例采用了完全一致的契约格式DOM 元素 id 列表、消息名、函数名、数据 schema。这说明shared_deps.md是 smol developer 在 v0/v1 全系列中一以贯之的核心机制Pong 游戏只是它的一次具体落地。二、Pong 示例的文件全景shared_deps.md 将整个应用划分为 4 个文件职责清晰文件职责关键元素 / 接口index.html页面骨架画布 挡板 div 脚本装载idgameArea、idplayerPaddle、idaiPaddlestyle.css样式画布、挡板、球的视觉契约400×400 黑色画布、100px 黄色挡板、红色小球main.js主逻辑鼠标控制、AI 跟随、碰撞检测、计分4 个全局对象 8 个函数ai.js简单 AI 算法带误差概率的决策aiSpeed、aiError、aiDecision()四者通过index.html底部的两个script标签链接在一起先加载main.js再加载ai.js。两个 JS 文件均不使用import/export关键字完全通过全局函数与 DOM API 协作——这是提示词中明确要求的 Chrome 兼容约束下文会详细展开。三、index.html页面骨架与 DOM 契约shared_deps.md 对它的描述是包含游戏画布和两个挡板 div 的主 HTML 文件。真实源码examples/v1_pong_game/index.html仅 15 行!DOCTYPE html html head titlePong Game/title link relstylesheet typetext/css hrefstyle.css /head body canvas idgameArea width400 height400/canvas div idplayerPaddle styleheight:100px; width:10px; background-color:yellow;/div div idaiPaddle styleheight:100px; width:10px; background-color:yellow;/div script typetext/javascript srcmain.js/script script typetext/javascript srcai.js/script /body /html这份文件定义了全程序最重要的跨文件契约——三个 DOM idDOM id用途被谁引用gameArea400×400 画布游戏渲染区域main.js通过getElementById(gameArea)获取并绘制playerPaddle玩家挡板 divstyle.css定位与着色aiPaddleAI 挡板 divstyle.css定位与着色shared_deps.md要求所有 JS 函数“only uses the DOM API to interact with the HTML elements based on their id names”而 main.js 的第一行就兑现了这一约定const canvas document.getElementById(gameArea)。值得注意的细节两个挡板 div 带有与 CSS 一致的内联样式height:100px; width:10px; background-color:yellow。从源码看main.js并未通过getElementById获取这两个 div游戏画面中的挡板实际由 canvas 绘制见第五节drawGame()div 更多承担了样式层的占位职责——这种“计划与实现之间的轻微出入”在 AI 生成代码中很常见我们会在第九节再讨论。四、style.css画布与挡板的样式契约shared_deps.md 对样式的描述是画布为 400×400 黑色方块并在页面居中挡板 100px 长、黄色球小而红。真实实现examples/v1_pong_game/style.css逐条对应body { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: black; } #gameArea { background-color: black; height: 400px; width: 400px; position: relative; } #playerPaddle, #aiPaddle { position: absolute; background-color: yellow; width: 10px; height: 100px; } #playerPaddle { left: 0; } #aiPaddle { right: 0; } .ball { position: absolute; background-color: red; height: 10px; width: 10px; border-radius: 50%; }可以看到契约的完整落地路径“400×400 黑色”同时出现在 HTML 的width/height属性和 CSS 的#gameArea中双重保险“居中”由body的 flex 布局实现“挡板 100px、黄色”由#playerPaddle, #aiPaddle规则落实并借助position: absolute分别贴左left: 0贴右right: 0“球红色”由.ball类给出 10×10 红色圆形样式。这里的“样式契约”与main.js的绘制逻辑也有呼应main.js绘制挡板时用的是fillStyle yellow、绘制球时用fillStyle red与 CSS 中的颜色值完全一致——这正是shared_deps.md所承诺的“视觉方案全局统一”。五、main.js主循环与 8 个核心函数shared_deps.md 将 main.js 定位为“控制游戏功能的主 JavaScript 文件”并列出 4 个变量对象和 8 个函数。下面逐项对照真实源码展开。5.1 全局变量4 个游戏状态对象const canvas document.getElementById(gameArea); const ctx canvas.getContext(2d); let playerPaddle { x: 0, y: 200, width: 10, height: 100 }; let aiPaddle { x: 390, y: 200, width: 10, height: 100 }; let ball { x: 200, y: 200, radius: 5, dx: 2, dy: 2 }; let score { player: 0, ai: 0 };与shared_deps.md的清单一一对应playerPaddle玩家挡板位于x: 0左侧初始纵向居中y: 200尺寸 10×100aiPaddleAI 挡板位于x: 390右侧尺寸相同ball小球半径 5初始速度dx: 2, dy: 2每帧水平、垂直各移动 2 像素score双方计分对象player与ai两个字段。注意这里挡板的width: 10 / height: 100与 CSS、HTML 中的挡板尺寸完全一致画布 400×400 也与 HTML 属性一致——尺寸契约在多文件间保持了统一。5.2 startGame()初始化游戏状态function startGame() { playerPaddle.y 200; aiPaddle.y 200; ball.x 200; ball.y 200; ball.dx 2; ball.dy 2; score.player 0; score.ai 0; }负责把挡板复位到中线、球放回画布中心、速度恢复初值、比分清零。它在脚本末尾被调用一次startGame();并在每次得分后由updateScore()再次调用以重置对局。5.3 playerMove(event)鼠标驱动的玩家挡板canvas.addEventListener(mousemove, playerMove); function playerMove(event) { let rect canvas.getBoundingClientRect(); playerPaddle.y event.clientY - rect.top - playerPaddle.height / 2; }通过监听画布的mousemove事件把鼠标在页面中的clientY换算成画布坐标系内的y减去rect.top再减去挡板高度的一半使挡板中心跟随鼠标纵坐标。这正是shared_deps.md中“players paddle following the mouse”的实现。5.4 aiMove()AI 挡板追踪小球function aiMove() { let targetY ball.y - (aiPaddle.height - ball.radius) / 2; aiPaddle.y (targetY - aiPaddle.y) * 0.1; }AI 的“慢速跟随”在这里实现先算出理想位置targetY让挡板中心对准球心然后每帧只移动剩余距离的 10%系数 0.1。这是一个平滑的追赶算法——越接近目标移动越慢天然形成“slowly moves the paddle toward the ball”的观感。5.5 checkCollision()碰撞检测function checkCollision() { // Ball and player paddle if (ball.y ball.radius playerPaddle.y ball.y - ball.radius playerPaddle.y playerPaddle.height ball.dx 0) { if (ball.x - ball.radius playerPaddle.x playerPaddle.width) { ball.dx * -1; increaseBallSpeed(); } } // Ball and AI paddle if (ball.y ball.radius aiPaddle.y ball.y - ball.radius aiPaddle.y aiPaddle.height ball.dx 0) { if (ball.x ball.radius aiPaddle.x) { ball.dx * -1; increaseBallSpeed(); } } // Ball and top or bottom if (ball.y ball.radius canvas.height || ball.y - ball.radius 0) { ball.dy * -1; } }三段逻辑对应三类碰撞玩家挡板球纵向范围与挡板重叠ball.y ± radius落在playerPaddle.y ~ yheight内且球向左飞dx 0时若球左缘越过挡板右缘ball.x - radius playerPaddle.x width则dx取反——球反弹AI 挡板对称逻辑条件为球向右飞dx 0且球右缘越过 AI 挡板左缘上下边界球碰到画布顶部或底部时dy取反。每次击中挡板都会调用increaseBallSpeed()落实“每次弹板加速”的需求。5.6 updateScore()得分与对局重置function updateScore() { if (ball.x ball.radius canvas.width) { score.player; startGame(); } else if (ball.x - ball.radius 0) { score.ai; startGame(); } }球完全越过右边界ball.x radius 400时玩家得一分越过左边界时 AI 得一分。得分后立即调用startGame()重置对局状态。5.7 increaseBallSpeed()弹板加速function increaseBallSpeed() { ball.dx * 1.1; ball.dy * 1.1; }每次球击中挡板水平与垂直速度同时乘以 1.1——速度按约 10% 的比率递增游戏随时间推进而愈发激烈这正是提示词中“Every time the ball bounces off a paddle, the ball should move faster”的直接实现。5.8 updateGame() 与 requestAnimationFrame 主循环function updateGame() { ball.x ball.dx; // 移动球 ball.y ball.dy; aiMove(); // 移动 AI 挡板 checkCollision(); // 碰撞检测 updateScore(); // 更新比分 drawGame(); // 渲染画面 requestAnimationFrame(updateGame); // 调度下一帧 }这是整个游戏的心脏每一帧依次执行“移动球 → 移动 AI → 碰撞检测 → 计分 → 渲染”最后通过requestAnimationFrame(updateGame)递归调度自身形成 60fps 的游戏循环。shared_deps.md描述的“Updates the game state at every frame”正是此函数。5.9 drawGame()Canvas 渲染function drawGame() { ctx.clearRect(0, 0, canvas.width, canvas.height); // 清屏 ctx.fillStyle yellow; // 绘制挡板 ctx.fillRect(playerPaddle.x, playerPaddle.y, playerPaddle.width, playerPaddle.height); ctx.fillRect(aiPaddle.x, aiPaddle.y, aiPaddle.width, aiPaddle.height); ctx.beginPath(); // 绘制球 ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2); ctx.fillStyle red; ctx.fill(); ctx.font 30px Arial; // 绘制比分 ctx.fillText(score.player, 50, 50); ctx.fillText(score.ai, canvas.width - 50, 50); }渲染层逐项落实视觉契约黑色画布上先清屏然后以黄色fillRect绘制左右挡板、以红色圆形arc绘制小球最后在画布顶部左右各画一个比分数字。shared_deps.md所说的“Renders the game state on the canvas”即此。六、ai.js带误差概率的简单 AIai.js 全文仅 22 行是“简单 AI 算法”的独立载体// Define AIs paddle speed var aiSpeed 2; // Define AIs error rate var aiError 0.05; /** * Function to make a decision on the direction to move the AI paddle based on the balls position and the error factor. * param {object} ball - The ball object * param {object} aiPaddle - The AI paddle object */ function aiDecision(ball, aiPaddle) { // If ball is above the AI paddle and random number is greater than error rate, move the paddle up if (ball.y aiPaddle.y Math.random() aiError) { aiPaddle.y - aiSpeed; } // If ball is below the AI paddle and random number is greater than error rate, move the paddle down else if (ball.y aiPaddle.y Math.random() aiError) { aiPaddle.y aiSpeed; } }对照shared_deps.md的变量说明aiSpeed 2AI 挡板每次决策的移动步长像素/帧aiError 0.05AI 的失误概率。每次决策都会重新生成一个Math.random()只有随机数大于 0.05 时才真正移动因此约有 5% 的帧 AI 会“漏接”一次移动指令让对手有可乘之机aiDecision(ball, aiPaddle)核心决策函数——球在挡板上方则上移、下方则下移方向判断加上随机误差因子。一个值得指出的源码级观察从当前仓库的代码结构看aiDecision()并没有被main.js调用main.js全文中不存在对ai.js函数的引用实际生效的 AI 行为来自main.js中的aiMove()。ai.js定义了“带概率误差的决策算法”以满足提示词中的规格描述但游戏主循环走的是另一条更平滑的路径。这是“计划中的接口”与“最终装配”之间轻微不一致的又一个例子——shared_deps.md中的函数名aiDecision被如实生成但其接线取决于生成main.js时的模型选择。七、一致性契约如何约束代码生成7.1 DOM id 契约贯穿三个文件整个 Pong 示例最典型的一致性示范是gameArea它在index.html中作为canvas的 id 被定义在style.css中被#gameArea选择器赋予 400×400 与黑色背景在main.js中被getElementById(gameArea)获取并作为 2D 绘图上下文。playerPaddle/aiPaddle同样横跨 HTML 与 CSS。这一“一处定义、多处引用”的模式正是plan()提示词要求列出“every DOM elements that javascript functions will use”的原因——id 是跨文件引用的最强耦合点必须先全局商定。7.2 函数命名契约shared_deps.md列出的 8 个函数名startGame、updateGame、drawGame、playerMove、aiMove、checkCollision、updateScore、increaseBallSpeed与main.js中的实际定义逐字一致。在生成阶段generate_code的系统提示词要求“do not stray from the names of the files and the plan we have decided on”即每个文件的代码必须使用契约中的命名这从机制上杜绝了“main.js调用了一个不存在于ai.js的函数”这类跨文件幻觉。7.3 兼容性约束无 import/export、仅 Chrome 特性shared_deps.md末尾特别说明JS 文件不写import/export只使用 Chrome 浏览器支持的特性每个 JS 函数通过 DOM API 按 id 与 HTML 元素交互。这一约束来源于生成该示例的提示词见下一节并在源码中得到了贯彻文件间通信依靠全局作用域函数与getElementById而非 ES Module使用的 APIrequestAnimationFrame、addEventListener、CanvasRenderingContext2D、Math.random全部属于浏览器原生能力没有任何 Node.js 专有 API。这意味着生成的index.html可以直接用 Chrome 打开运行无需构建工具——这是“可在 Chrome 直接运行的前端小游戏”这一产品规格的最低门槛实现。7.4 契约并非强约束计划与实现的细微出入需要客观指出的是shared_deps.md是一份“指南”而非“编译器检查”。对比契约与最终代码可以观察到三处出入HTML 中的playerPaddle/aiPaddlediv 从未被main.js引用实际挡板渲染完全走 canvas 绘制路径style.css中定义了.ball类但 HTML 中并没有classball的元素球同样由 canvas 绘制ai.js的aiDecision()未接入主循环实际 AI 逻辑是main.js的aiMove()。这些出入不影响游戏可玩性但印证了 readme.md 中作者自己的坦诚shared_dependencies.md有时并不完备“is sometimes not comprehensive in understanding what are hard dependencies between files”。因此shared_deps.md的实战定位是生成阶段的命名与结构约束源、以及人类 review 生成的程序的入口文档而非不可违背的规格书。八、亲手重现从一条提示词生成这个 Pong 游戏examples/v1_pong_game/并非手写的示例而是 smol developer 的真实输出产物。用于生成它的提示词就内嵌在仓库入口 main.py 中原文如下a simple JavaScript/HTML/CSS/Canvas app that is a one player game of PONG. The left paddle is controlled by the player, following where the mouse goes. The right paddle is controlled by a simple AI algorithm, which slowly moves the paddle toward the ball at every frame, with some probability of error. Make the canvas a 400 x 400 black square and center it in the app. Make the paddles 100px long, yellow and the ball small and red. Make sure to render the paddles and name them so they can controlled in javascript. Implement the collision detection and scoring as well. Every time the ball bouncess off a paddle, the ball should move faster. It is meant to run in Chrome browser, so dont use anything that is not supported by Chrome, and dont use the import and export keywords.对照可见shared_deps.md 中的每一个要素canvas 400×400、挡板 100px 黄色、球小且红、AI 带误差概率、弹板加速、Chrome 兼容、不用 import/export都能在这条提示词中找到出处。8.1 环境准备在本地克隆该仓库后用 Poetry 安装依赖见 readme.md 的 Basic Usagepoetry install # 需要 poetry依赖声明见 pyproject.toml运行前还需要可用的 OpenAI API Keyopenai库通过环境变量OPENAI_API_KEY读取。8.2 直接复现不传参数即使用内嵌 Pong 提示词根目录的 main.py 在无命令行参数时会使用上述内嵌的 Pong 提示词。指定输出目录与调试模式即可复现python main.py --generate_folder_path pong --debug Truemain.py的注释中也保留了等价写法main.pypython main.py --prompt a simple JavaScript/HTML/CSS/Canvas app that is a one player game of PONG... --generate_folder_path generated --debug True执行后pong/或generated/目录下将出现与examples/v1_pong_game/结构一致的文件集其中就包括新生成的pong/shared_deps.md——也就是本文剖析的这份契约文档的“孪生兄弟”。8.3 关键命令行参数根目录入口 main.py 支持以下参数参数默认值说明--prompt内嵌 Pong 提示词应用规格若以.md结尾且长度小于 100则按文件读取提示词内容--modelgpt-4-0613生成模型也可切换为gpt-3.5-turbo-0613--generate_folder_pathgenerated生成代码的输出目录--debugFalse开启后实时打印流式输出、字符速率与各阶段内容若想把长提示词沉淀为文件参考 prompt.mdChrome 扩展示例的做法将提示词写入 Markdown 后执行python main.py --prompt prompt.md --model gpt-3.5-turbo-06138.4 运行生成的游戏生成完成后直接用 Chrome 打开输出目录中的index.html即可试玩鼠标上下移动控制左侧挡板右侧 AI 挡板自动追踪小球球每击中挡板加速一次任一方丢球则对方得分并重置对局。8.5 生成过程内部视角源码级如果想知道生成过程中shared_deps.md是如何落盘的可以阅读 smol_dev/main.pymain()先调用generate_folder()创建/清空输出目录随后以open(f{generate_folder_path}/shared_deps.md, wb)打开文件把plan()的流式输出 chunk 实时写入规划完成后specify_file_paths()通过 Function Calling 拿到文件路径数组smol_dev/prompts.py再逐个调用generate_code_sync()生成文件并落盘smol_dev/main.py。因此shared_deps.md既是第一步规划的输出也是第二步起每个文件生成时的上下文输入——全程序一致性由此闭环。九、局限与注意事项契约覆盖有限如第七节所述shared_deps.md并不总能穷尽文件间的硬依赖例如aiDecision与aiMove的分叉。作者在 readme.md 中也承认这一点并补充过“在提示词中直接指定具体name”这一兜底手段。因此把它当作“人类 reviewer 的检查清单 模型生成的结构约束”是更稳妥的定位。输出目录会被清空重建generate_folder()smol_dev/utils.py在目录已存在时会先shutil.rmtree再重建因此重复运行会覆盖上一次的全部生成结果。若要保留多版本输出请为每次运行指定不同的--generate_folder_path。模型选择影响质量默认模型为gpt-4-0613见 main.py提示词级生成质量与模型能力强相关示例代码中也可切换到gpt-3.5-turbo-0613以降低成本但一致性表现通常更弱。依赖与版本前提仓库的 pyproject.toml 声明python 3.10,4.0.0并依赖openai ^0.27.8、openai-function-call ^0.0.5、tenacity ^8.2.2等库。复现时请以当前仓库声明为准。结语shared_deps.md是 smol developer“让模型与自己对话”这一核心思想的物质载体。透过 Pong 游戏这份仅 36 行的契约文档我们完整看到了它如何规定 DOM id、变量、函数名与视觉方案如何约束index.html、style.css、main.js、ai.js四个文件的生成也看到了契约与实现之间真实存在的缝隙。理解这份文档就等于理解了 smol developer 从“一条提示词”到“一个可运行的多文件程序”之间的那座桥——它既是生成机制的一部分也是人类介入审查生成结果的第一个入口。赞分享AI Agent代码智能体【免费下载链接】developerthe first library to let you embed a developer agent in your own app!项目地址https://gitcode.com/gh_mirrors/de/developer点击查看免费下载相关推荐CopilotKit 集成 Google ADK 构建 Angular 全栈 AI Agent架构、共享状态与生成式 UI 实战指南CopilotKit 集成 Google ADK 构建 Angular 全栈 AI Agent架构、共享状态与生成式 UI 实战指南 本指南以仓库中的 adk人工智能AI AgentAgent 框架前端后端使用gh_mirrors/de/developer开发游戏Pong游戏实战案例使用gh_mirrors/de/developer开发游戏Pong游戏实战案例 引言为什么选择gh_mirrors/de/developer开发游戏 你是AI Agent代码智能体揭秘tf_ner高性能之谜TensorFlow Estimator与tf.data最佳实践揭秘tf_ner高性能之谜TensorFlow Estimator与tf.data最佳实践 tf_ner是一个基于TensorFlow实现的命名实体识别NE上一篇nix-updateNix包更新的终极瑞士军刀让依赖管理化繁为简下一篇DeepMesh震撼发布革命性AI网格生成技术如何通过强化学习重塑3D创作创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑