shadow:AI编程,本质上就是在构建Agent:Context、LLM、Tools、GUI。
今年初兴起的 Vibe Coding(凭感觉编程)正是这一趋势的鲜明体现——开发者只需描述软件的大致功能与使用体验,即可自动生成可运行的应用,编程过程从“精确地实现逻辑”转向“直观地描绘意图”。
这标志着软件开发范式迎来了根本性转变,人机交互也随之进入实时响应与高度个性化的新阶段。我们不禁反思:软件为何必须被预先完整构建?未来的软件或许将不再是一个个冻结的二进制实体,而更像是一篇篇动态文档,清晰阐述了其核心功能与交互逻辑。最终,将由如 OS.ai 这样的智能系统进行实时解析、渲染与交互,让软件从“制造出来的产品”彻底转变为“随时可被唤起的能力”。
Gemini OS 这个案例,代表着这一发展趋势 ⤵️ :


一个创新的桌面操作系统模拟器。其核心特点是,所有应用程序的用户界面(UI)并非预先编码,而是由Gemini大语言模型根据用户交互实时生成。当用户点击图标或按钮时,应用会捕捉该操作,连同历史记录一起发送给Gemini。模型随即以流式传输方式返回新的HTML内容,动态渲染出应用界面,实现了高度动态和可扩展的人机交互体验。
该应用使用 React 和 TypeScript 构建,并采用 Tailwind CSS 进行样式设计。其核心逻辑可以分解为一个持续循环的流程:
1 用户互动: 您点击屏幕上的一个可交互元素(例如,“记事本”图标)。
2 互动捕捉: GeneratedContent 组件中的一个中央事件监听器会捕捉到这次点击。它会将关于被点击元素的重要信息收集到一个 InteractionData 对象中,包括:一个唯一的 ID (data-interaction-id),元素的类型和文本内容,以及当前的应用程序上下文(例如,'notepad_app')。
3 状态管理:InteractionData 对象被发送到主组件 App 中。这次新的互动被添加到一个名为 interactionHistory 的数组中。这个历史记录至关重要,因为它为 LLM 提供了您之前操作的上下文。在等待下一步时,应用程序会显示一个加载动画。
4 API 服务:interactionHistory 数组被传递给 streamAppContent 函数。这个函数会构建一个详细的提示(Prompt) 发送给 Gemini API。这个提示是整个操作的“大脑”,它包含:
5 内容生成与流式传输:Gemini 模型处理提示并开始为新的 UI 生成 HTML。因为是流式传输,响应会以小数据块(chunks)的形式被逐步发送回来,而不是等整个响应完成后一次性发送。App 组件接收这些数据块,并将它们追加到其 llmContent 状态中。这使得新的 UI 会在屏幕上逐块出现,就像文本被逐字打印出来一样。
6 渲染:llmContent 字符串(HTML)通过 React 的 dangerouslySetInnerHTML 属性直接渲染到一个 div 中。
7 特殊的脚本处理:如果来自 LLM 的 HTML 包含任何 <script> 标签(例如在“游戏”应用中使用),它会找到这些标签并重新执行它们。正是这个机制,使得模型能够即时生成功能齐全、自包含的 JavaScript 游戏。
8 循环重复:新的 UI 现在已经显示在屏幕上,应用程序等待您的下一次互动,届时整个周期将再次开始。
如何让语言模型生成的、包含在HTML里的<script>标签(例如游戏代码)能够被浏览器执行。
这是一个非常关键的环节,因为出于安全原因,浏览器不会执行通过dangerouslySetInnerHTML(或innerHTML)插入到页面中的script>标签内的JavaScript代码。我们的应用必须绕过这个限制。
下面是具体的处理流程:
脚本处理逻辑只在两个条件同时满足时才会执行:
A 加载完成: 必须等待所有HTML内容从Gemini API流式传输完毕。如果在内容还没加载完时就处理,可能会执行不完整的代码。
B 内容已更新: 组件会记录上一次处理过的htmlContent。只有当新的htmlContent与上一次不同时,才会重新执行脚本处理,这是一种性能优化,避免了不必要的重复执行。
一旦触发,代码会执行以下操作:查找所有脚本 (container.getElementsByTagName('script')):
首先,代码会在刚刚通过dangerouslySetInnerHTML渲染出来的DOM内容中,查找并获取所有的<script>元素。
此时,这些脚本标签虽然存在于DOM树中,但它们是“惰性”的,里面的代码不会被执行。 遍历并“激活”脚本: 代码会遍历找到的每一个“惰性”脚本(我们称之为oldScript)。
对于每一个oldScript,它会执行一个“克隆替换”的技巧:
a. 创建新脚本 (document.createElement('script')): 创建一个全新的、空的<script>元素(newScript)。
b. 复制属性: 将oldScript上的所有HTML属性(例如 id, type等)都复制到newScript上。
c. 复制内容 (newScript.text = oldScript.innerHTML): 将oldScript标签内部的JavaScript源代码(innerHTML)复制到newScript的text属性中。
d. 替换节点 (oldScript.parentNode.replaceChild(newScript, oldScript)): 这是最关键的一步。在DOM中,用我们刚刚创建的newScript替换掉原来的oldScript。
这个“克隆替换”技巧之所以有效,是因为:通过 document.createElement 创建并手动插入到DOM中的脚本节点,会被浏览器视为合法的、需要执行的脚本。 因此,通过这个替换操作,我们欺骗了浏览器,让它认为这是一个正常的、动态添加的脚本,从而触发了JavaScript的解析和执行。
简单来说,整个流程就是:等待LLM生成完整的HTML。在渲染出的内容里找到所有不会自动执行的<script>标签。为每个标签创建一个“替身”,把代码和属性都复制过去。用这个能被浏览器执行的“替身”换掉原来的标签。
正是这个机制,使得由语言模型动态生成的、包含完整逻辑的JavaScript游戏(如贪吃蛇)能够在我们的应用中真正地运行起来。

github.com/shadowcz007/gemini-os
现在,你可以用 vibe coding 试试了!