构建时用AI,运行时用软件
好文翻译,原文链接
https://softwarefordays.com/post/software-is-mostly-all-you-need/
过去6个月,尤其是最近6周,AI编程代理展现出了极其强大的编写软件能力。那些传统上需要人类数周劳动的任务,现在可以在几天甚至几小时内完成。更令人惊叹的是,那些从一开始就为利用AI编程代理而设计的软件系统,展现出了与最初助力其创建的神经网络许多相似的特征。这些AI原生的软件系统是学习而来的,而非设计出来的。代码是策略,部署是回合,错误报告是奖励信号——架构良好的编程代理可以在很少的人为干预下驱动这个循环。与传统的强化学习架构不同,它们被编码在CPU指令集里,而不是神经网络的权重中,但学习的方式如出一辙。
编程代理以及在其之上构建的软件系统的成功,也为我们普遍应该在何处应用AI代理提供了借鉴。编程,像许多其他创造性任务一样,需要 判断力 。如何最好地实现某个输入为A、输出为B的函数;如何给某个变量命名;是共享某个函数还是实现一个新版本等等。神经网络擅长判断(原因见下文)。然而,我们在现实中看到的许多代理应用,却是在处理那些可以完全被明确指令所指定的任务。当然,传统软件擅长 执行 明确的指令。任何编程语言都能在今天机器上以每秒数十亿条指令的速度被执行。
编程代理恰好抓住了这个要点,因为根据定义,它们在构建时编写代码时做出一系列判断,而将这些代码的执行留给运行时操作的机器。即使是完全由AI生成可执行产物的系统,其表现最佳的架构也是遵循这个原则:将判断委托给神经网络,将执行委托给传统软件。
历史中的判断与执行
人类历来同时承担判断和执行的角色,但原因各不相同。
判断 是一种模糊的分类,无法被指定为明确的规则。这个变量应该是私有的,而非公有的;这个手写字母是”B”,不是”P”;这个客户投诉是关于退款,而不是欺诈;这张图片包含一张收据;某个不熟悉页面上的这个元素是”登录按钮”。人类做这些事情,是因为传统的基于CPU的冯·诺依曼机器根本无法胜任。规则无法被写下来,即使在今天,它们也只存在于高维空间中作为学习到的边界。通过在巨大维度空间中进行梯度下降来最小化损失函数,这些边界在神经网络内部被勾画出来,不受限于英语、C语言甚至Rust(开玩笑的)的词汇和语法。
执行 是离散的逻辑,是_可以_被指定为明确规则的。如果投诉类型是退款且购买天数小于30天,则批准;如果设备类型是CPAP且机构代码是X,则库存单位(SKU)是ABC-123;点击选择器为 a[href="/login"] 的元素。人类做这些事情,尽管冯·诺依曼机器理论上可以并且更可靠、更快速,是因为编写和运行编码这些规则的软件系统_确实_成本高昂。投资不值得,并非因为任务本身有任何模糊性。
当今常见的混淆
当前主流的代理架构混淆了判断与执行,常常将神经网络同时用于两者。代理的共识定义——“一个大型语言模型(LLM)在循环中运行工具以达成目标”——阐明了机制,但没有界定问题空间。
像 browser-use 和 Stagehand 这样的框架就体现了这种混淆。看 browser-use:
agent = Agent(task="找到Hacker News排名第一的帖子", llm=llm, browser=browser) await agent.run()
或者 Stagehand:
await stagehand.act("点击 stagehand 仓库"); await agent.execute("进入最新的拉取请求(PR)");
在这两种情况下,LLM既执行判断(哪个元素是”stagehand 仓库”?)_也_执行操作(点击它,找出下一步,再点击)。整个循环都是神经网络。没有产生持久的产物。LLM_本身就是_运行时。
为什么执行能从传统软件中受益
神经网络缺乏执行通常所需的特性:确定性、可审计性以及对边缘情况的精确处理。
考虑一个处理医疗设备订单系统中的这段业务逻辑(来自Docflow Labs,我的初创公司):
// 备选方案1:尝试 scriptedMachine 字段 const scriptedMachineCode = extractMachineCodeFromScripted(scriptedMachine); if (scriptedMachineCode && machineType) { const machineMake = lookupMachineSku( machineType, scriptedMachineCode, classification ); if (machineMake) { machineSku = machineMake; } } // 备选方案2:如果方案1没成功,尝试 facility 字段 if (!machineSku || machineSku.trim() === "") { const facilityMachineCode = extractMachineCodeFromFacility(facility); if (facilityMachineCode && machineType) { const machineMake = lookupMachineSku( machineType, facilityMachineCode, classification ); if (machineMake) { machineSku = machineMake; } } }
这段代码处理可能一年只发生一次的组合——一个罕见的机构,一个不寻常的设备类型,一个特定的分类。这段代码即使是针对边缘情况也能提供100%的精确性。当出现计费争议,有人问为什么系统为某个特定患者选择了租赁而非购买时,可以逐行追溯逻辑。它存在于版本控制中,并且是语义透明的、确定性的、可审计的。
一个近似此功能的神经网络无法提供这些属性。稀疏的训练数据永远无法覆盖这个组合空间。而且,它模糊了业务要求必须清晰的边界。它的失败是不透明的——梯度和激活函数对调试没有任何帮助。在这个基底上的决策是语义不透明的、非确定性的、无法追溯的。
人类也做不好。与冯·诺依曼机器相比,我们在重复执行规则方面速度慢、成本高、不一致且容易出错。一个人处理这个备选逻辑链可能会读错机构代码,累了跳过一步,或者周一和周五应用规则的方式不同。机器则以相同的方式执行相同的指令,每秒数十亿次,成本极低。
Stagehand示例:对了一半
Stagehand的 act("点击 stagehand 仓库") 在某种意义上正确地通过神经网络实现了判断。[7] 在任何动态选择的页面上,哪个元素对应”stagehand 仓库”是无法用传统软件来表述的。页面布局的排列组合太多了。这些边界的模糊性,最适合通过神经网络在海量多维空间中,针对大量样本最小化某个损失函数来处理。
然而,在另一种意义上,Stagehand的架构是有限的。我们可能事先知道我们试图点击的是哪个网页,而且它可能不常变化,只需要一次性的(或偶尔几次)判断。
然而,Stagehand的设计本身并不产生可执行的产物。相反,LLM返回一个选择器,该选择器以不透明的方式缓存在版本控制之外。当缓存未命中时,LLM在运行时重新介入以重新解释指令,再次调用神经网络。
一个更好的架构可能仍然允许LLM做出判断并返回一个选择器,但允许将这个判断完全放在构建时。选择器作为代码被输出到一个Playwright脚本中。脚本被提交到版本控制,经过审查,然后部署。如果失败——因为网站变了,选择器失效了——那么_开发过程_会重新介入。一个AI代理重写脚本。同样的判断,不同的产物。选择器变成了底层软件系统中一个语义透明的部分,而不是短暂的运行时状态。[8]
更好的架构
当处理那些只能在运行时动态做出的判断时,神经网络可以留在运行时。所有其他的LLM代理都应该属于构建时,用于加速可执行软件的生成。
工作流协调器是传统软件。它调用神经网络来处理判断任务:分类、提取、解读——那些无法被指定为规则的模糊模式匹配。然后,它自身确定性地执行业务逻辑。执行路径是明确的、可审计的,并且受版本控制的。
这并不是一个新模式。生产级别的机器学习系统早已如此运作:模型进行分类,代码执行操作。新颖之处在于,AI代理现在可以编写这些代码,从而消解了RPA(机器人流程自动化,确定但脆弱)和AI代理(适应性强但不可预测)之间看似存在的取舍。[9]
开发时间趋近运行时
历史上,软件系统在两个领域之间保持着清晰的界限:开发(人类编写代码,数天或数周)和执行(CPU运行代码,纳秒级)。AI编程代理正在缩小这个差距。当开发时间趋近于零时的理论极限就是运行时:
lim开发时间→0构建时间 = 运行时
即使AI永远无法达到纳秒级编写软件的速度,但几小时、几分钟甚至几秒的时间尺度也能让软件系统在反馈到达时进行适应。
随着AI代理能力的增强,”编写代码”和”运行代码”之间的区别可能会消失。这类似于强化学习,只是基底不同。在传统的强化学习中,神经网络观察状态,输出一个动作,接收奖励信号,并更新其权重。网络_是_自适应元素。
用软件替代神经网络,结构保持不变。系统观察数据——请求、错误、指标、用户投诉——代码执行响应,反馈到达,AI代理更新代码。同样的自适应循环,不同的可计算基底。
表征上的差异至关重要。神经网络将行为编码在不透明的权重矩阵中,而软件将行为编码在符号化的、人类可读的形式中。与神经网络不同,软件可以被审计、调试,必要时甚至可以像外科手术般精确修改。可以更改单个备选逻辑链,而无需重新训练整个模型并期望它能正确泛化。可解释性、可调试性、可审计性和精确可修改性往往是生产系统所要求的,而当学习到的更新机制提供了适应性时,你就能获得强化学习的好处而无需付出其代价。
自适应的软件系统
具有讽刺意味的是,在运行时,软件仍然几乎是你所需的一切。
神经网络最好保留用于判断——那些我们无法用语言明确指定的模糊任务——以及用于构建时的加速。神经网络不会取代传统软件,而是使其能够以前所未有的低成本,渗透到那些可以从可靠、离散的逻辑执行中受益的经济角落。
一种让神经网络处理运行时判断、让软件处理执行、让AI代理加速构建时的架构,创造了一个符号化的、但却是自适应的基底——在具备学习系统适应性的同时,也拥有了可审计性、确定性和精确性。
夜雨聆风