如何为游戏开发设计一个 AI 脚手架(Harness)

Hacker News Top 新闻

摘要

独立游戏开发者 Kyle Kukshtel 发布了他在 Indie Rodeo Calgary 大会上题为《你的游戏就是一个脚手架》的演讲文稿,探讨如何在日常游戏开发中真正实用地使用 Claude Code 等大语言模型(LLM),而非仅仅做一次性演示,同时分析了为什么大语言模型难以处理游戏相关的代码。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/10/13 18:58

# 你的游戏就是一个 Harness - k-hole Source: https://kylekukshtel.com/ai-harness-engineering-game-development-claude-code harness 我在 9/12/26 于卡尔加里的 Indie Rodeo(https://indierodeo.com/)做了一场 45 分钟的演讲,题为"Your Game Is A Harness"。演讲结束后,我和很多人进行了交流,我想到把这些想法以一种更广泛的形式分享出来,让更多不只是参会者也能读到。 另外,因为我在博客上经常谈论 AI,人们会以为我用 AI 写文章。事实并非如此!下面的内容 100% 由人类撰写,我基本上只参考了演讲时展示的幻灯片来写这篇文章(这不是一份转录稿)。虽然我很享受用 AI 写代码和工具,但我同样享受用"老方式"写作的乐趣,尤其是我并没有任何理由想让这个过程变得更高效。 那么,以上就是开场白,下面献上我人生中第一篇将演讲转化为博客文章的作品。请享用! --- ## 引言 \[这里我做了一段关于自己和工作的简要介绍,由于你正在浏览我的网站,故略去\] 我想谈谈在游戏开发中使用 LLM 的话题。不是那种在 Twitter 上看起来很酷的一次性演示,而是*真正的*开发。每天工作八小时、每周五天的那种。 在开始之前,我觉得有必要先对 LLM 做一些基础性的说明。或者至少讲一些基本概念,这样我后面要讲的内容才能让人理解。我知道对很多人来说这些内容可能是老生常谈了,如果你只想看开发循环的部分,可以直接滚动到下一节。 我使用 LLM 进行开发工作的旅程始于我制作的一个情绪板网站 mood.site(https://www.mood.site/)。那是在"agentic"概念出现之前,我只是想测试一下当时的 ChatGPT 能不能真正写点代码。过程基本上就是我盲目地把代码复制粘贴到 ChatGPT 的网页界面里。这个过程很糟糕,但我对结果感到惊讶。我成功做出了这个网站!它能运行!(https://kylekukshtel.com/mood-site-moodboards-spite-driven-development)我仍然需要自己验证很多东西,但这已经足以让我信服了。如果没有 LLM,我不可能在那么短的时间内完成那个网站(大概两三个晚上,总共约 20 小时)。如果真是那样,我大概到今天还在做第一个版本。 从那以后,mood 成了我探索 LLM 各种功能的试验场(它本身也算一个不太成功的项目!),也激发了我启动很多使用类似技术栈的项目。此外,LLM 也在不断进步,尤其是 Claude Code 和后续的 Claude 模型。根据我个人的经验,我觉得现在在基于 Web 的技术栈方面,几乎没有 LLM*做不到*的事情了。 那么很容易就会得出结论说 LLM 能做*一切*,毕竟软件的很大一部分都是 Web 软件。但每次我尝试用 LLM 来构建非 Web 的游戏*东西*时,得到的结论都是截然不同的: Screenshot 2026-09-19 at 2.00.12 PM ## "LLM 不擅长游戏" 每次我尝试用 LLM 做游戏相关的事情,它都会翻车。也许这是我的偏见。我做游戏程序员快 15 年了,我认为我知道这些东西是怎么运作的。我能在一英里之外就闻出"烂"游戏代码的味道。但当涉及 Svelte Web 应用时,我明显没那么在行,倾向于认为 LLM 生成的代码是"好的"。也许实际上也是垃圾。但确实有理由相信 Svelte 和 Web 技术栈方面的东西*是*高质量的,部分原因在于网上有*大量*高质量的 Web 项目开源了它们的代码。相比之下,游戏方面可以用来训练的数据量可能要少几个数量级。 不仅如此,游戏里还有很多不是代码的*东西*。游戏不只是把静态图片和文字放到布局里。游戏里有*大量*不容易用代码表示的东西。*而且*在此之上还有和 Web 一样的那些文字排版工作,但那只是游戏需要处理的 100 个系统中的一个子集。 这就是很多人用 Three.js 做游戏的部分原因——他们可以把素材轻松地表示为简单的方块几何体,跳过游戏通常需要的大量素材生成/适配工作和处理流程。因此,其中很多作品都是粗制滥造的,除了 LLM 的中位水平之外,没有任何真正的创作者个性。 然而,也许他们说的有一定道理。 Screenshot 2026-09-19 at 2.01.19 PM 一次性 Three.js 工作流的关键理念(以及 LLM 构建 Web 应用的熟练程度)在于*一切都*是代码。因为*东西*都是代码,LLM 就能很容易地对其进行推理、进行修改等操作。 Screenshot 2026-09-19 at 2.02.41 PM 但对于传统游戏来说,情况并非如此。或者说这不是全部真相。游戏当然有代码,但还有很多其他*不是*代码的东西。这意味着游戏的关键组成部分对 LLM 来说实际上是不透明的。如果你的游戏依赖于对这些不透明内容的实际操控和变换,那么 LLM 在尝试处理你的游戏时就会翻车。 Screenshot 2026-09-19 at 2.09.12 PM 我认为游戏开发社区可能在不久前尝试过 LLM,然后就直接撞上了这个问题。"LLM 不行/没用/等等"的情绪(这里有一个巨大的限定:只是在讨论能力问题,而非可能终结社会的潜在影响)就来自于直接撞上了这些限制。 Screenshot 2026-09-19 at 2.11.28 PM 也就是说,从结构上讲,LLM 不擅长读取游戏。至少在默认状态下是这样。 ## LLM 基础知识 让我们来谈谈原因,以及我们能做些什么。 对任何 LLM 服务的任何调用/提示都分为三个基本组成部分:system prompt、assistant prompt 和 user prompt: **System prompt:** 在服务的所有用户之间共享的通用文本。这通常描述 LLM 本身的基础信息,比如"你是 Opus 5,一个由 Anthropic 创建的智能大语言模型……"。这并不是说这些不重要(它们很重要),只是说它们与我们的用例关系不大。你可以在这里看到一些已公开(以及被泄露)的 system prompt 示例(https://github.com/asgeirtj/system_prompts_leaks)。 **Assistant prompt:** 这是一个辅助性的提示段落,用于帮助引导 AI 回应的内容方向。你可以把它基本理解为模型与你的提示之间的基本"交互"向量,代表模型基于某些输入对用户的"回应"。通常你不需要考虑这个。 **User prompt:** 这才是真正重要的东西。你放入任何 AI 工具中的大部分输入都进到这里。任何提交的聊天、代码等都会被倾倒到这个段落中。当人们谈论"上下文(context)"时,他们主要指的是"提示的 user(和 assistant)部分存储了多少 token"。 很容易认为每次聊天都是上述过程的"全新"版本,但实际上发生的是,当你在某个会话中与模型合作时("会话"的范围因服务而异——可以是关闭前的一个完整 Claude Code 会话,也可以是 Web 托管模型侧面板中可访问的"聊天"等),你之前发送给模型的每一条消息,都会在你每次发送新消息时被*重新提交*一遍。这有点像玩操场上那种老游戏,轮到你的时候,你得把其他人说过的东西*再说一遍*,然后再加上你自己的东西,让下一个人再来接。 Screenshot 2026-09-24 at 8.35.14 AM ## 上下文与上下文污染 当你反复这样做时,你就积累了当前会话的"上下文"。 Screenshot 2026-09-24 at 8.35.51 AM 这一切都没问题,但这里需要注意的一个关键是,你实际上只能处理上下文中"最新"的内容(即你提交的旧消息)。之前的消息会被反复重新提交给模型。因此,你要确保上下文中的内容是"好的",否则你可能会陷入退化的场景——上下文因为之前"坏"的东西而被污染,不断地影响新的工作。 Screenshot 2026-09-24 at 8.47.56 AM 管理上下文是使用 LLM 时的一个关键环节,于是一种新的工程实践——"上下文工程(context engineering)"——应运而生。上下文工程的核心问题是:"我们如何才能在尽量少污染的情况下,将*最好*的信息放入上下文?" 这里有一篇很棒的博文,讲述了"长上下文失败的方式"(https://www.dbreunig.com/2025/06/22/how-contexts-fail-and-how-to-fix-them.html),对这个问题做了更深入的探讨,我在这里只强调其中的关键部分: Screenshot 2026-09-24 at 8.50.21 AM 上下文工程本身是一个很大的主题,超出了本次演讲/博客的范围,但我们将在此使用其中的一些原则,来探索如何帮助 LLM 更好地处理游戏。 换一种说法,让 LLM 好好地与游戏协作是一个*上下文*问题,而不是与游戏或 LLM 的*本质*(或*不足*)内在关联的问题。 Screenshot 2026-09-24 at 8.52.19 AM 但这里复杂的因素在于,游戏是*很怪*的。或者至少:我们没有任何真正的模板来构建游戏。我们有游戏引擎,但实际上没有任何标准化的工具链,部分原因在于每个游戏的需求表现都各不相同。 Screenshot 2026-09-24 at 8.56.06 AM 这在游戏领域尤其独特(在我看来),但在其他技术领域也有足够的体现,以至于一个新的概念应运而生,旨在让 agent 能够有效地与你的工作产物"协作":Harness。 ## 关于 Harness 概念的一些说明 Harness 的基本理念是,它是一组 agent 针对某种明确定义(或松散定义)的预期输入应采取的特定行动模式。你可能已经在使用像 Claude Code 这样的 harness,但这些 harness 可以被增强、扩展、提炼、抽象等,以更好地支持你正在尝试做的*具体*工作。这个领域最近也*非常*活跃,很多 harness 被构建用于开发各种各样的东西: Screenshot 2026-09-24 at 8.59.31 AM 这些工作都指向同一个关键问题:"我如何才能更好地理解我正在构建的东西,并在开发过程中引导这个过程"。Harness 可以整合诸如与浏览器迭代、遥测、性能分析,甚至完整的 SDLC 工作流(如创建工单、文档等)等功能。 然而,所有这些 harness 都*没有*做好的一件事情是:直接与你的游戏*对话*。你的游戏有点像黑箱,LLM 能看到的大部分只是代码本身,正如我们之前所讨论的,这远远不够。我们知道 LLM 已经擅长处理代码,所以并不是某个 harness 本身就能解决"游戏不只是代码"这个问题。 真正需要做的是,把游戏中的所有*东西*都拿出来,尝试将其*转化为* LLM 能够处理的形式。我们需要撬开你的游戏这个黑箱,让更多的内容能够被 LLM 直接访问。 Screenshot 2026-09-24 at 9.06.06 AM Screenshot 2026-09-24 at 9.06.24 AM 在演讲版本中,我有一张非常搞笑的幻灯片,上面说这里的箭头就是这场演讲的实际内容。这个笑话在博客形式中效果大打折扣,但为了纪念我还是保留下来了: Screenshot 2026-09-24 at 9.06.38 AM Screenshot 2026-09-24 at 9.07.28 AM 那么让我们来谈谈我们可以做些什么。 ## 让游戏对 AI Agent 可读 以下是一些非常具体、可操作的事情,你可以为你的游戏做,使其对 LLM 更易读、更可*操作*。 在开始之前先插一句,值得注意的是,很多这些建议对游戏开发*总体*来说都是有益的。它们在 AI 的加持下会更加高效,但做这些事情大概率会让你制作自己的游戏的体验*更好*。 ## 固定随机性 贯穿整个演讲/文章的一个主题是*确定性*的概念。基本上,就是你能在多大程度上精确地复现你游戏中的某个特定状态。总体目标是这应该非常容易且非常精确。如果在生产过程的最后才去做这件事,将*非常*困难,而且很可能需要在最后一刻对游戏做出根本性的改变。 相反,如果你在写第一行代码之前就假设这个约束条件,并将其作为一个目标坚持下去,你就是在为一个更高效的后期开发铺平道路,而不会陷入与混乱和意大利面条式代码的搏斗。 从这里开始其实很简单。在游戏启动时,创建一个单一的*带种子的*随机源。确保任何对非确定性功能的调用都使用这个随机源。这意味着你的游戏将能够确定性地复现游戏中几乎任何状态。如果你的代码在 30 个地方调用 `Random.Float` 之类的函数来决定某些游戏行为,你知道第 30 次调用每次都会是一样的。很简单。 ## 可截图性 为什么要能复现状态?原因有很多,但对于一个 agent 来说,想象一下你在多次迭代中试图纠正某个东西。agent 能够反复从头开始到达同一个状态而不需要经历状态复现失败,这是很重要的。 而且当你到达那个状态之后,你怎么知道?你如何评估这些变化? 嗯,首先——截图!大多数引擎都有内置的截图 API 或功能: Screenshot 2026-10-01 at 12.22.01 PM 我还建议添加轻松切换 UI 层、游戏玩法层等功能的开关,这样你可以减少视觉杂乱,避免干扰 LLM 关注你试图处理的层(比如,如果你想修某个 UI 样式问题,就不要把 UI 和游戏玩法放在一起)。 在 agent 进行修改前后各截一张图,是让 agent 理解变化的好方法。 ……是吗? Screenshot 2026-10-01 at 12.15.58 PM 当你在一次修改前后截两张图时,LLM(即使是多模态的,即具有视觉能力),很可能会*非常*困难地不仅理解变化是什么,还难以理解它的修改是否朝着自己的最终目标迈进了一步。 来自图像数据的这种噪声本身就是一种上下文腐化(context rot)的形式:天真地向模型扔截图会模糊它们对进展的感知,让它们和你都感到困惑。 一个视觉示例可以帮助理解:假设你截取了这个会议首页的截图: Screenshot 2026-10-01 at 12.18.48 PM 你要求模型"把骷髅做成海盗"。假设(对于文本 LLM 来说不太可能)这个指令可以作为标注附加到图像上——Google Stitch(https://stitch.withgoogle.com/home)可以做到这一点——你的图像会连同你的标签一起发送给模型提供商,但同时也会带上所有*其他*你并不想处理的内容(下图中标绿的部分)。 Screenshot 2026-10-01 at 12.20.46 PM 这意味着绿色区域中的所有东西都类似于"上下文污染"。这是 LLM 在处理你请求的修改的同时还在处理的数据,但它不仅仅是在思考你要求的内容,而是在思考*整个*图像。 你需要让 LLM *只*思考需要改变的东西。那你怎么判断发生了什么变化? 图像差异(Image diffing)! Screenshot 2026-10-02 at 9.32.48 AM 如果你对两张图像做"差异"处理,就像你对代码变更所做的那样,你就能轻松看到发生了什么变化。结合巧妙的裁剪技巧,你可以让 LLM 通过量化指标来理解某个输出在变化之后与目标的接近程度。

相似文章

驾驭之道

Hacker News Top

作者探讨了使用控制工具管理AI编码代理的重要性,并分享了借助Cursor、Claude和Deepseek等工具提升生产力和模型使用成本效益的技巧。

学习Harness Engineering

Hacker News Top

Learn Harness Engineering 是一个免费课程,教授AI编码代理的工程原理,涵盖环境设计、状态管理和验证,使像Codex和Claude Code这样的代理更加可靠。

Own the Loop:Agent Harnesses 现场指南(5分钟阅读)

TLDR AI

随着AI编码模型变得商品化,智能体控制框架——即管理工具和工作流的控制循环——成为关键差异化因素。本指南绘制了控制框架领域的图谱,权衡了供应商原生性能与模型无关工作流的可移植性。