[音乐]

大家好,欢迎来到 Beyond Done。我们将开始这次研讨会。内容很多,我们要确保有足够的时间回答问题并让大家安顿下来。所以我们就开始吧。(清嗓子、咳嗽声)

所谓 Beyond Done,直接切入正题。你们可能看到过之前发生的一起事件推文:一个 AI 智能体开始删除生产环境的数据库,9秒钟内一切消失。这就是我们今天想要帮助大家预防的情况,同时我们仍需信任智能体,无需逐行甚至逐个 PR 地审查。这就是我们认为的发展方向以及如何实现它的方法。我们要做的不是修复那个断裂的环节,而是让大家保持冷静、一切正常运行,并在一定程度上信任智能体,但这种信任是随着时间建立的。

我也想说,这不仅仅是关于防止错误,这也是我们今天要看的内容:如何通过与智能体协作实现真正的规模化。我是 Nick Nisi,WorkOS 的工程经理,住在内布拉斯加州的奥马哈,已经在那里两年了。我做了很多关于智能体的工作。自2025年7月30日以来,我就没写过代码了。我知道是因为那天我从 Laracon 回来看到 PHP 就想:“再也不写了。”但我还是在这里。和我在一起的是我的好朋友 Zach。(清嗓子声)

大家好,我是 Zach Prosser,WorkOS 应用 AI 团队的工程师。很高兴今天能在纽约和大家在一起。(清嗓子声)

之前我们展示过这个仓库,它是你的工作台。所有幻灯片都在这个仓库里,你可以从这里获取,或者访问 workos.cloud/umbeyond-done。我知道这有点拗口,下一个 PR 或 QR 码应该会带你去那里(抱歉,是 talks.workos.cloud/beyond-done)。我提一下这个是因为我要切换到那个页面。所有幻灯片都在那里,还有一个研讨会页面,包含我们要进行的练习步骤指南,你可以按自己的时间安排进行。还有术语表涵盖所有大小术语,如果你不熟悉可以查看;也有“随时提问”按钮,你可以与基于该幻灯片训练的智能体对话获取答案。当然,你也可以直接和我们人类交流。

确实如此。我们会保留这个页面。我们发现很多人想提问但无法做到,所以请务必查看并使用它。(稍等片刻)我看到有些摄像头对准了二维码。安全团队要是看到会生气的。好了,当我们思考“完成工作”或智能体完成任务时,有很多因素让我们信任它们。最容易想到的就是代码是否正确?是否是我会写的代码?生成的 PR 好吗?然后我像正常的代码审查者一样去检查。但这很耗时,而智能体写代码比我们快得多,所以我们不想过度依赖它,因为信噪比开始起作用,我们要审查的代码太多了。我们不再写代码,对代码也不那么熟悉,因此与它越来越抽象隔离。所以我们需要找到其他方法。

有很多方法可以做到这一点。例如使用 Playwright 等浏览器工具,说:“嘿,我有这个网站并提交了表单,提交成功了。我检查了它显示成功消息。”这是一个很好的测试用例,虽然没什么智能体特性,但它是验证工作的工具。它们可以自我报告这些结果,你可以信任大多数时候的结果,但不是所有时候。这就是我们要驱动的:如何让我们真正信任这些报告?

没错。今天研讨会的所有内容正如 Nick 所说,都基于一个仓库且是交互式的,所以请随时运行它进行实验,带回你可能在工作中使用的东西或至少以不同方式体验。是的,就像我刚才展示的那样进入仓库并运行 pnpm snack(因为我有所有快捷键)。它会弹出浏览器,我们可以看到我们制作的 Midnight Snack Club 应用。你可以现在玩它并自己测试。让我输入我的电子邮件地址,[email protected]。(谢谢兄弟)看吧,成功了!它保存了邮件。我在这里看到了它。如果我有测试来确认电子邮件出现在页面上,那就太好了。这就是这些例子的样子。但如果你重新加载页面(就像这样),什么都没有。哦,我搞错了什么?切换回去。(清嗓子声)如果你重新加载它,显示这里没有人。我显然无法驱动 Chrome。这是因为虽然更新了数据,但没有持久化存储在任何地方。我们没有进行任何验证,只是检查 UI 看是否真的做了什么操作。是的它确实对 UI 做了些事情但我们没有检查另一半,所以从未持久化我们就丢失了那封邮件。抱歉 Zach 你没注册成功。(没关系)

我认为这是一个非常关键的点。所以,首先问一个问题:在座有多少人正在大量构建智能体应用?好的很酷超过一半的人。有多少人在运行多个终端标签页或多个聊天会话或智能体?就是刚才举手的那些人对吧?这就是我们所有人正在经历的,至少是我经历的和 Nick 经历的。如果这是你今天驱动的18个工作车道之一,并且你的注意力分散到实际上无法专注于它时这些细微的 bug 就会潜入因为智能体就像……

是的,我照你说的做了:代码看起来没问题,能编译通过也能运行,测试也通过了,但结果依然是错的。因此,我们要在这个工作坊中一起探讨和探索的问题是:我们该如何真正解决这个问题?让我们现在像管理一群疯狂的“令牌机器”一样管理的智能体,能够自己弄清楚该做什么,以及在犯错时如何纠正。

没错。大家会注意到,我们这次并没有深入查看代码。正如我刚才所说,我看到 PHP 就感到害怕,所以我们不再看具体代码了。(清嗓子)不过,这一切都引出了 X 平台(原推特)上著名用户 Lauren10 的一句名言。她说:“如果一个智能体能验证自己的工作,那一切皆有可能;如果它不能验证自己的工 作,其他都无所谓。”

是的。换个说法就是,根据我和其他人的经验,我们正逐渐达成共识:随着模型能力的快速提升,验证——尤其是可靠性、验证和评估(evals)——已成为当前需要关注的核心基础要素。特别是当我们看到越来越多的工具平台开始内置递归自我改进(RSI)概念时,感觉我们正转向一个世界:在那里,我们都将维护多个自我进化的代码库。

在这样的世界里,我们如何建立评估基础,让智能体真正知道什么是“好”的标准,并能够自主进行性能提升?同时,我们如何给智能体提供它所需的反馈,让它明白自己哪里失败了?是的,在我们解决这个问题之前,我们不会完全信任这些智能体以最大潜力加速我们的工作。因此,我们需要在不同程度上解决这个问题。

这不仅仅是从零到一的突破,而是一个渐变的过程;我们的目标是让我们越来越接近“完全信任这些智能体”的那一点。顺便提一下, anecdotal evidence(轶事证据)表明,这也是我们在 WorkOS 普遍经历的现状。我们有一个应用 AI 团队,正在为其他团队和自己构建大量不同的东西。

当你迭代一个已经具备基础评估体系、拥有可运行且可信基线的代码库时,体验简直是天壤之别。我们会分享一些技巧和方法:关键在于能够像人类开发者一样,明确地看到智能体是否正确地完成了某项任务。我们有一个比喻:我们都成为了“智能体工具链管理者”,这就是确保每个智能体都在做正确工作、避免浪费金钱和时间的方法。

是的,这相当于说“不要犯错”。如果你想加入验证机制,就不能靠猜测它需要什么。你需要给它清晰、明确的目标,并提供观察和检查这些目标的方法。否则,智能体可能会做一些事情,消耗一些令牌,最终到达你期望的终点,但这并没有保证。

对于你在结果和实际目标上未填充的任何细节,智能体只会去推断或编造。它所做的假设可能与你作为理性逻辑人类所期望的假设不一致,因为你们尚未对齐。因此,你需要建立这样的系统:允许智能体观察、理解你的需求、执行验证、修复偏差并重新检查。

是的,同样 anecdotal evidence 显示,我们大家都发现,那种陈旧的做法是提交一个巨大的拉取请求(PR),让一个智能体会话运行三四天,不断说“修复这个、修复那个、让它更好”。而目前对人们来说行之有效的方法,是极其清晰地分解任务。

给每个任务的智能体在提示词中精确说明它需要做什么才能算完成,以及如何验证“已完成”的状态。这样做效果极佳。我的团队中有人每月处理 900 个 PR,零事故,并且还在提升软件的关键绩效指标(KPI)。所以从 anecdotal 角度来看,这确实是我们都需要退一步思考的问题。

我不再以传统方式处理代码库了。我几乎像是在照料多个代码库,让它们自我进化。是的,这也是我们构建并坚持使用的工具方法。Zach 和我在 WorkOS 保持高效、完成自身工作的方式就是这样。我们都建立了各自的示例和方法来处理这个问题,我们认为这在当下是件好事。

目前还为时过早,无法对任何方法下定论,因此实验至关重要:找到行之有效的方法并坚持下去。我们两人都在采用的一个核心点是验证环节,或者说一切由这个“验证循环”驱动。正如我引用的那句话所说:“状态来自记录(records),而非模型声明。”

所以,要“出示收据”。给我看实际的、有形的证据,证明它确实做了我期望做的事。不要只是告诉我“是的,通过了,看起来不错”,因为没有任何东西能支持这种说法。我需要实际的截图、视频、测试结果、冒烟测试,或者以上所有证据来向我证明:无需我查看代码,它确实在做我想要的事。

因为归根结底,我不那么关心代码本身。我关心的是结果、它在做什么以及它交付的价值。它的代码写得烂,但可能很好地交付了那个结果;或者代码写得好,却完全没有给我想要的结果。我需要找到其中的平衡点:即交付我想要的结果,而不是仅仅关注代码本身。

是的,确实如此。我也一直在做类似的事情。我构建了一个我叫作 Context(或 CTX)的工具。它最初是一个跨智能体工具链的记忆层,因为我和所有人一样,正在快速实验 ChatGPT、Cloud Code、Pi 等工具。我希望它们都能读写记忆,起初这很有效。

随着我运行更多智能体——平均每天打开 18 到 30 个终端标签页——我意识到,尝试集中化验证的权威可能是一个有趣的实验。

在一个 Web 服务中,CTX 是一个运行在 Cloudflare 上的简单 Web 应用。它最初是用“氛围编程”(vibe coding)生成的,虽然能运行起来,但起初只是作为记忆模块存在。它能够将密钥分发给智能体(Agents),而无需让智能体直接看到或获取凭据,这也运行良好。现在作为额外的实验,我正在创建验证器(Verifiers)。其工作原理是:智能体被分配任务——就像我们使用提示词所做的那样。它们去执行这些工作,然后通过 MCP 连接返回到 CTX,说:“这是我完成的工作。我做完了吗?请验证一下。”

例如在一个工作流中,需要验证 PR(拉取请求)是否创建正确。为此,系统会在已知的验证器上运行评估程序。起初这可能只是一个简单的命令行工具(如我在仓库中所示),随后它会变成一种可以反复重用的加固组件。该验证器会调用 GitHub API,核实 PR 确实存在且状态健康、所有合并检查均通过。只有此时,它才会标记工作流中的该节点为“已满足”。否则,它会重定向智能体并告知:“这些事项仍然失败。”这虽然只是一个实验,但我们正借此尝试不同的模式,观察哪些能落地、哪些行不通。

此外,这也是一种有趣的编码体验,因为我在所有应用中都需要奇怪的像素艺术风格。在这个案例中展示了一个简单的工作流:其中一个验证关卡要求所有拉取请求都已合并。这项工作由中央权威机构完成,它使用自己的验证器去检查 API 或相关工件,最终在右侧边栏(即使向下滚动)显示状态,并引用实际检查过的仓库和 PR。这是一个简单的例子,但你可以想象在库、技能或工具中建立标准化的验证器供组织内部共享。这将是加速 PR 流程并减少人工监督的一种方式之一。

另一个我构建的类似工具叫 Ryker。这更像是一个面向工程的 Grokbot 或 Muse,它在接收任务的方式上极具灵活性,但一旦你交出手头的工作,它的职责就是“ babysit ”(监护/跟进)。我不需要亲自监护所有正在推进的 PR。因为随着工作深入,边际效益递减,我的注意力会被分散——我最终会陷入监护 PR 的琐事中:修复测试、处理审查意见等。

我希望能够记录下“我们应该做什么”以及“如何验证工作已完成”,然后将任务交给 Ryker。它可以去执行确保审查完成的工作;当它根据 PR 审查意见修复代码时,仍需以某种方式匹配我们“构建可用软件”的总体目标。之后它只需验证结果并在需要我介入时发出警报,而不是让我全程监护。

Ryker 会构建检查项,确定需要运行哪些工具并执行它们;验证所有项目是否通过,保留证据并通过不同方式展示给我,然后决定何时停止。例如,如果它能验证“是的,之前有问题,现在修复了 bug”,并将它们组合在一起后能正常运行。这主要是为了让我免于陷入交付最后 10% 细节的琐事中,从而专注于我作为人类的价值所在:即我们为什么要做这项工作以及如何去做。

让我们做一个思维实验:假设在 LLM(大语言模型)出现之前,作为人类开发者,我们如何验证一个 Web 应用?你会做哪些事情来确保其正常运作?我们可以列出一些简单的例子。比如:刷新页面,应用是否仍然可见?打开新标签页查看健康状态;检查从 API 返回的状态码是否符合预期(基于你发送的请求)?

此外还包括:本地主机或本地存储,以某种方式检查数据持久性;控制台日志等网络层面的操作。还有阅读页面上的实际内容,看页面内容是否……与它进行交互。之所以列举这些,是为了展示从“人类开发者始终亲自执行这些核心职责”到 LLM 时代初期(即:“给你三段文字,去执行这些指令”)之间的转变。

这两者之间的差异在于:智能体能否验证它是否真的按照要求在实际应用中完成了任务,且其更改没有破坏任何功能。否则,你得到的只是 LLM 旨在提供的“幻觉”或胡编乱造——因为它们试图处理文本,试图让你满意。仅靠终端本身,你的智能体无法执行上述任何操作;它需要工具来辅助。幸运的是,现在向智能体添加工具非常容易。

我们只需告诉它们如何使用这些工具,并明确说明我们要证明什么。当你这样做时,不要只给出一段模糊的提示,比如“嘿,总是在浏览器中验证更改”,这太笼统了。“记得重新加载并检查”稍微具体一些;“检查数据是否持久化。查看控制台是否有错误。”这更具体了,但即便如此,将所有这些指令放入提示词中仍然很繁琐。虽然很多技能(Skills)或保存的提示词确实是这样做的,但如果遗漏了什么或情况发生变化,这会导致大量的解读空间。

更好的方法可能是拥有一个脚本,可以通过不同方式来证明其有效性。这样你可以反复运行该脚本,对多个方面进行更确定性的检查,然后将输出结果反馈给智能体。这样它就能确切知道哪里出错了以及如何快速修复。这就形成了一个闭环,

速度会快得多,代理只需旋转它、再次运行、检查、验证、再运行一遍,如此反复直到没有错误。从某种意义上说,这也是统一的:我们知道大语言模型是非确定性的,因此即使给30个代理完全相同的提示词,结果仍可能存在差异。将其锁定为单个命令,并由实际的Bash命令和调用支持,这就显著更具确定性了。

据Lauren Tan等人在 anecdotal 层面提到,他们每月看似能实现数千个PR,并构建出以前无法在如此短时间内完成的雄心勃勃的产品。他们表示,这是他们首先集中精力且真正解锁了速度的关键因素。因此,如果你的特定Web应用尚未拥有允许任何代理充分验证你所关心功能的定制CLI,那么仅靠提示词将会面临更艰难的处境和未铺平的道路。

相比之下,如果能让每个代理都能确认“是的,我确实构建了该功能”,并证明其性能良好、未引发错误且应用看起来正确无误,情况就会好得多。在本次研讨会中,我们制作了一个名为“snack”的小工具。虽然我们已经运行过几次,但它并非旨在成为可复用的通用工具,而是作为模板,展示如何为特定应用设置此类环境。

你可以使用“pnpm snack”在该仓库中运行它,然后将其作为与正在共同开发该应用的大语言模型对接的方式。Snack可以执行几项操作:它可以管理应用程序的健康状况,启动或关闭它;运行“doctor”命令以检查数据库是否正在运行、是否有网络连接等快速诊断。

此外,它还有“drive”功能,你可以指示其加入等待列表(这是我们的午夜零食等待名单),重新加载页面或重置所有数据使其无法找到。它还可以查找信息,例如截取快照、查看网络存储和控制台日志、检查本地存储等。

最后,它可以通过两种方式证明其有效性:一是运行“prove”命令以检查确定性输出;二是使用Judge,即利用一个系统模型来判断我们是否走在正确的方向上,而不必要求完全匹配。

为了充分阐述这一观点:当你在一个尚未具备此类机制的仓库中实施这种做法时,所解锁的速度提升不仅属于你个人。你的团队中的其他成员同样受益,因为他们也能以显著更快、更安全的方式提交大量PR。这再次强调了将其作为基础概念的重要性,因为验证和测试应用正确性的方式,往往比软件本身更为关键。

人们正在将代码转换为Rust、Go或TypeScript,虽然不一定是在同等程度的LLM帮助下完成的(例如Bun团队使用LLM辅助转换),但验证机制才是核心。当所有验证器就位时,源代码本身的重要性便降低了许多——你可以重写代码并证明其有效。

这些工具赋予了代理“眼睛”和“身体”。LLM是大脑,而它通过工具在计算机上物理或虚拟地交互。例如,“join”命令让代理实际运行并检查操作是否成功;“check storage”、“reload”和“snapshot”等功能也是如此。让我们实际运行几个命令来看看效果:执行“pnpm snack up”,这会启动我们的浏览器界面。

接着我打开另一个终端,运行“pnpm snack snapshot”。这将生成页面的JSON格式快照供我们查看。如果我在页面上添加“Zack at zack.zack”,希望能看到一些包含Zack的内容。果然出现了,这就是一种验证方式。我们也可以使用截图功能使其更直观,这应该会弹出一张屏幕截图。

这样我们就获得了不仅将数据传递给LLM进行解释,而是向其人类证明结果的能力——这才是最终目标。能够截取屏幕截图、录制视频并将其上传到Pull Request,现在可以通过GitHub CLI实现这一点。

真正的瓶颈在哪里?不要猜测或空转数小时,而是要确切找出导致渲染缓慢的部分。关键在于,不仅可以通过查看代码来解释应用是否正常运行,还可以构建工具向你证明其工作正常,而无需你阅读任何代码。

我们真正关心的是:用户看到了什么?它在特定宽度、深色或浅色模式下是否正确渲染?你可以对此运行无限次的测试,并在循环中反复执行。正如Zach所说,这解锁了整个团队的效率,因为你赋予了代理自行运行这些检查的能力。

如果我们像这样运行:先执行“doctor”命令,然后添加“[email protected]”,再检查存储,数据确实存在。如果我们再次检查存储但尚未执行其他操作,可能会看到本地存储为空;如果重新运行“reload”并再次快照,我们将验证其确实为空。这就是工具本身可以运行的方式。

循环检查所有这些事项,并在反馈给我们之前确认。哦,我发现我们实际上并没有持久化保存这些数据。我忘了做那部分工作。而智能体可以直接从那里继续执行。嗯,我们想停下来回答几个问题,如果有的话。“是的,先生。”“好的。”问题是,我们要不要使用模型来编写 JSON 代码,以便帮助我们使我们的代码具有确定性?我的回答是肯定的。我确实在使用它并运行它,然后我会围绕这些代码编写单元测试来以不同方式进行验证,捕捉边缘情况。接着验证那些真正解释或代表我从系统模型期望返回结果的代码,并以此证明我走在正确的轨道上。同时保留这些测试作为另一种检查手段,以确保代码不会随时间漂移或发生变化。

我们讨论的一般流程是什么?是的,至少在我的团队中,我们要构建大量的内部工具应用程序。我们也有一些面向客户的应用程序。它们通常是 TypeScript 编写的,并部署在 Cloudflare 上。因此,它们是全栈应用程序。这运作得非常好。你甚至可以拥有专注于后端或数据库操作的评估(evals)。你可以随意细化粒度。但绝对是的,它在全栈层面都有效。我期望我们未来与软件交互的方式将类似于园丁确保植物按照我们的意愿正确生长演化。“这就是为什么我们没有提供一个通用工具,因为目前我们无法给出一个适用于房间里每个人所从事的所有应用程序的通用工具。你必须构建适合你正在处理的应用程序的工具,然后将其交给模型,并确保它们知道如何很好地运行这些工具。”

我还想补充一点,作为团队、公司或企业真正需要或关心的评估内容会因行业而异。比如室内设计领域与那些速度是唯一重要因素的领域相比,会有截然不同的评估标准。我认为你们需要建立不同的评估体系,与团队一起构建这些评估是有价值的。“是的,而且有很多不同的方式可以将数据以不同形式传递给模型,以便它进行解释并保持同步。这是让模型保持在循环中了解情况发生的关键部分。”AMP 团队有一篇关于此的优秀文章。我不记得帖子标题了,但我稍后会发出来。我想那篇文章叫“循环驱动开发”,我不确定。

在他们的例子中,他们展示了一个画布,里面有一个圆圈和一个在其中弹跳的球。然后有一条小缝隙,如果球击中位置恰当,就可以从圆圈中掉落出来,游戏结束。这个想法的核心是让事物具备反馈循环的能力。这就是它的本质。模型实际上无法看到正在运行的画布以及事物的状态,但如果你举一个非常牵强的例子,比如打印出球在每个点的坐标。如果发现一个 bug:一旦到达某个特定点,它就跳到屏幕的另一侧。如果它在每次循环中移动超过一个像素,那可能就有问题。因此那里存在 bug。如果你能将此反馈给模型,它就能帮助你调试,尽管它没有眼睛去观察画布和球的行为是否符合物理规律。

而且一旦你这样做,你就可以自由地启动 30 个不同的软件变体来回应这些评估,然后选择你想要的那个。“是的。”“这是它解锁的另一件事。这对于基础建设非常关键,特别是如果我们期望智能体和模型能够改进,令牌成本可能会降低。”你可以想象周一早上我们会说:“我想看到这个新功能或应用程序的 35 个版本。”“对,然后选一个。”那么我们如何实际运行或创建这些呢?正如我们所说,我们不能普遍地证明一切。它必须是针对你应用程序的独特内容。

对于我们午夜零食页面,这就是“proof”命令可能看起来的样子:基本上使用像 Playwright 这样的工具来检查当用户加入等待列表时,电子邮件是否真正被保存。我们可以先确认等待列表中没有任何电子邮件,然后在下一行填写一个邮箱地址,接着获取按钮并点击加入等待列表。然后我们可以期望页面上的状态元素包含“已列入等待名单”,并通过检查其内部文本来查看我们的电子邮件地址是否在其中。这是一种简单的方法,如果你写过端到端测试(如使用 Playwright 或 React Testing Library),你一定用过这类工具。你可以将它们作为工具交给模型,让智能体自行运行并证明其有效性。

你创建了可重现的证据片段,下次该页面发生变化时它可以再次运行并验证没有任何破坏。它起到了回归测试的作用,让团队每个人都能继续前进。“但有一个警告:成功的截图并不是证明。要确保它确实显示了你期望的状态,并确保它实际上运行了命令以达到该状态。”否则这可能只是另一个谎言。你看到 PR 上的截图心想“是的,这是正确的”,但它可能导致不同的副作用或采取 UI 中不可能实现的不同路径。所以要确保它从头到尾实际运行以进行检查。“是的,一旦你建立了这些耐用、可靠、可重复且统一的循环……”

失败开始驱动未来的工作,对吧?如果在一次循环中代理失败了,而你正忙于与同事交谈或开始另一项任务,该代理可以自行判断:“这不太对。让我再试一次。”然后它会再次运行验证命令。“好的,现在没问题了。”如果从规模上思考这意味着什么:单个拉取请求或代理(即单条工作流)能够自我修复、自我纠正并自行解决问题,那么你就不需要在一天结束时回到那个特定的拉取请求说:“哦,我仍然需要把它完全搞定。”如果将其放大计算,这就是我们所说的速度和效率的基础。

是的。>> 在座的有多少人能写出好的错误信息?举手示意一下。 >> 嗯,没人知道。我们早就知道了。 >> [笑声] >> 先生,你的信息是什么? >> 我手小。 >> 就是这样。是的。 >> 很好。 >> 嗯,你应该这样做。我认为它们比以往任何时候都更重要,因为当你的应用程序抛出错误时,猜猜谁能很好地解释并处理该错误?是代理。所以,如果你使失败原因明确,并提供尽可能多的关于它是如何进入该状态的细节,那么代理就可以像自我修复一样处理它。因此,这样做非常容易且非常值得。

是的,这是一个你和尼克经常讨论的概念,对吧?听起来我们在某种程度上试图为大型语言模型包裹确定性,但实际上我们不想这样做。这也符合 Amp 团队所谈论的精神。代理是智能的、有能力的,我们实际上希望它们具有创造性。因为通常情况下,如果给予正确的约束(在某种程度上,约束等于自由),它们将提出创造性的解决方案并真正解决你所说的问题。这就是为什么你要给它们结果的原因,这也是尼克正在实验的项目——以结果为导向的开发(Outcome-driven development)。因此,关键不在于过度规定代理如何实现目标。在某些情况下,这实际上可能导致性能降低。更多的是给它一张地图并说:“你必须到达这里。”当这个命令返回此结果时,你就知道它到了。

是的。你不必规定它是如何到达那里的,这一点在当今的前沿模型中变得越来越真实:Fable、Astra、Saul 6.1、Opus 5.5。它们都非常擅长发挥创造性,这就是我描述的方式。但是,如果你告诉它们:“我想去这里”,而不一定告诉它们需要采取的路径,它们可能会四处探索并确保捕捉到更多的边缘情况。它们可能会发现一些东西或问你一个问题从而解锁某些内容。但是,如果你只是说:“你必须先来这里,然后来那里”,它们可能不会偏离那条路径。你希望它们在某种程度上有所偏差(sway),但最终达到正确的目标。就像我说的,这是我们最近一批前沿模型中才真正开始欣赏到的东西,这使得这一切成为可能。因此,展望未来只会变得更好。

是的。 >> [清嗓子并咳嗽] >> 所以,当它运行时……你想进入典型的测试驱动开发(TDD)循环:红、绿、重构,与你的代理一起。我们谈论过……你已经听过关于这些 AI 事件的循环了,这里也没什么不同,但你可以将其用于验证部分本身。验证这个、复现它、向我展示修复方法,然后向我证明这一点。你如何向我证明?这与即将提交的修复同样重要,因为正是这会让你放心并让你信任模型去真正完成工作。当它说“嘿,我完成了”,你就知道它完成了,因为它以某种方式向你证明了。

绝对正确。 >> 所以,在这个例子中……你可以运行 Snack Doctor(零食医生)。我们就直接快速运行一下。P Snack Doctor。所以,这……它运行了,向我们展示了一系列检查项。它在检查我们是否有 Node、Chromium 是否已安装、会话是否存在、它实际上是否在运行,以及构建是否成功。这就是我们作为快速医生检查运行的内容,即各种各样的东西。然后我们运行下一个命令:Snack Join Ada at example.test(零食加入 Ada 在 example.test)。

抱歉,我们不再打字了。 >> [笑声] 好的。给了我们结果回来。Snack storage(零食存储)。

Storage is empty. It didn't actually put Ada in there. >> 所以,这就是现实世界中代理从整个系统中获得价值的时刻。因为在这一刻,你的代理意识到:“我还没有完成,我不会把这项工作丢弃。” >> 是的。如果你重新加载它并再做一次同样的事情。“砰!”我们现在给了我们的应用程序眼睛(视觉能力)。它可以看见这个。它可以运行这些内容,理解并改变 Midnight Snack 应用程序的世界,看到结果然后解释那里发生的事情。 >> [清嗓子] >> 所以你可以试试那个。如果……我们喜欢应用持久性修复并添加诚实副本到它,那么我们可以运行证明(proof)。让我们运行那个。

然后它启动了一个浏览器并失败了。我实际上没有运行测试,但如果我们运行了修复程序……如果我们运行了修复程序,它会显示出来。 >> [清嗓子并咳嗽] >> 好的,最近我们思考、谈论并开始将其推广到更多应用程序的一个概念是“收据”(receipts)。你知道,你可以像工单系统那样……比如帮助台工单系统,人类回答一个工单并认为它完成得很好作为优秀的客户响应或服务。那么我们要如何以某种方式保留这一点呢?例如,对吧?为什么我们想要这样做?有很多原因,我们会深入探讨其中一些。但是,再次思考一下……如果似乎许多人都在朝着运行回归测试套件和自进化代码库的方向发展,我们今天做的每一件事、你的代理今天所做的每一次运行(一整天都在进行),这些都是非常有价值的。这些数据现在非常宝贵,因为你可以将其喂给大型语言模型并获得所需的修复程序。在未来它将是极其宝贵的数据。

工具链的改进使得设置回归测试循环变得更加容易,因为现在你拥有系统表现的确切数据集。你可以基于此创建非常全面的评估测试集,让代理进行爬山式优化(hill climb)。这意味着通过循环迭代来观察其表现,并持续改进直到性能越来越好。是的,到了那个阶段,它们能够利用之前运行的证据来自主通过测试。

所以现在我们修复了代码,它不仅持久化存在,而且可能会产生误导。重新加载页面后,它可能显示“等待名单”,但截图实际上显示的可能是“你已入选,席位确认”。这两者并不完全匹配,因此这可能被视为一次失败。这开始脱离静态测试的范畴。我们如何证明它有效,而不需要与屏幕上非常具体的文本紧密耦合?

如果我们运行像 P Snack 这样的工具进行判断,它失败了。尽管功能正常且显示“我们已入选”,但模型并没有看到这一点。这是因为确认文本破坏了契约,不是一一对应的关系。那么我们如何在不过度增加耦合度的情况下解决这个问题?答案就是我们要讨论的系统一模型(System One models)。

是的,绝对正确。为了向没听说过的人介绍这个概念,“系统一”和“系统二”源自心理学。这是一个理解人脑如何运作以及人类思考方式的有用模型。系统一是超快速的决策,它是自主的。比如开车时有人突然变道撞向我,我下意识地修正方向甚至没有思考;或者快要失去平衡时自动调整。

而系统二则像是我要决定未来的人生方向,比如预订 Airbnb 并花三天时间深思熟虑。应用到大型语言模型(LLMs)领域,我们现在有了 Jev,紧接着 Cloudflare 两天后推出了 Cleft,速度甚至更快。这是一个令人兴奋的时刻,我们可以思考如何将这些工具结合起来,创造出未来 AI 架构演进中的新原语。

换个说法,如果我说“那边投射的光是什么颜色?”你会很快回答黄色。这就像系统一思维,你直接知道答案。如果我说“为什么它投射出黄色的光?”这就需要思考了。我们可以深入探讨物理原理,解释光线为何发射黄色光子。

“我们为什么在这里?”(笑声)这就是我们今天想与你深入探讨的话题核心。(笑声)在这个框架下,我们可以玩另一个游戏。这是一个与 Snack 相关的快速游戏:我们有“破坏者”和“侦探”。破坏者会选择一种零食,并在其中随机植入一个秘密 bug。

然后作为侦探的你,仅凭 Snack 工具找出它并证明其存在:命名这个 bug 并提供找到它的证据。如果你下载了网站或代码库,可以运行 pnpm snack up --bug random 让它自动运行并尝试找出 bug。让我们试试,给它一点时间。

--bug random 哦,它已经在运行了。让我杀掉那个进程,关掉正确的那个。好的。(清嗓子)在这个演示中,它运行了应用 URL,不是无头模式(headless),所以我们可以直接看到界面。bug 的描述是“隐藏直到你停止”。

把这个留在另一个终端运行,执行 pnpm snack doctor。让我们运行它。好了,整个流程跑完了,我们可以看到 Node 和 Chrome 进程都在运行,并且有一个会话。这里的证明显示最后一次收据是“失败”,但源代码已经不存在了,所以它无法提供证据。那是之前的运行记录。让我们再跑一次同样的情况。

有人能告诉我 bug 是什么吗?有什么想法吗? 邮箱地址错了? 是的,完全正确。端口号不对。

我想它运行在 62174 端口?不,那是对的。让我再试一次。我们不到一分钟了。(对于那个案例)是的,你可以做到。不过这些工具是可选的,即使没有它们你也可以完成所有练习,只是会慢一些。但如果你有的话,这也是实验的好机会。

它们的调用成本很低。(注册时)给你 5 美元的免费额度,我想这足够你用三年了。好的。(清嗓子)嗯,继续吧。下一个案例。是的。我们运行了几次。我觉得我的输出有些问题,我会调试一下。有趣的是,我需要在自己的验证示例周围添加一些验证测试。

可能的结果包括:它命名了 bug 并输出了证据;声称工作正常但实际上没成功;声称失败但实际成功了;归咎于一个不存在的 bug,或者虽然找到了正确的 bug 但没有提供证据。没错。这又回到了那个精心设计的例子(contrived example),当然,你的团队可能会有不同的评分标准或关注点。

这非常灵活。最重要的是开始行动并创建一个评估基线,一致地使用它,并与团队共同演进它。最终达到这样的状态:你的代码更改和 PR 会自动触发这些评估,让你知道哪些更改是安全的、是否在改进、是否变差或毫无变化。是的。

每次运行失败时,那就可以成为地图上的一个新节点。它能给我们提供思路,了解我们到达那里的步骤和原因。每次失败都让我们离修复更近一步,因为我们可以修正它。第一次运行仅是内存存储(memory only),比如输入错误的邮箱地址导致丢失确认信息。

每次我们都能证明捕获到了这个问题,并且以后不会再遇到它。但是对于“过度承诺”的情况,截图或判断器可能会告诉我们它在实际上失败时却通过了测试。控制台错误也是如此:控制台中可能没有任何内容,但在收据中可能存在;或者它可能在被捕获前就通过了。

进入控制台后,嗯是的,任何类似的情况。或者可能什么都没有发现,没有什么可查的,一切都正常运行。这本身也是一个真实的答案。

嗯是的,如果你运行医生模式(doctor),就像我们之前做的那样,它会显示之前的例子。它展示的是不再存在的证据。这是因为你的代码可能在昨天和今天之间发生了很大变化。也可能是不同的情况,比如著名的日期bug等例子。可能是多种不同原因导致的。所以我们要构建足够灵活的工具,以避免被这类问题困住。这有点像回归基本的单元测试示例,但以确定性检查的方式将其应用于模型,使它们能够自我修复和自我改进。

没错。清嗓子并咳嗽声好了,这再次联系到我们以前作为人类开发者所做的事情,对吧?我们过去能够看到什么,如何灵活地测试应用程序,如何检查一切。这实际上就是我们一直在讨论并转化为代码的内容。因此,通过将其放入代码中并提供确定性CLI接口,我们可以达到与开发者在深夜使用Chrome调试时相同的体验和路径。这才是真正解锁速度、进化能力以及扩大代码交付规模并更安全地执行这些操作的关键所在。

再次强调,如果你有一个Web应用且尚未拥有自定义验证器CLI工具,那么这是你可以为代码库采取的最高杠杆率措施之一。这也是展示应用程序中“良好状态”的基础评估(eval)。同时赋予代理查看所有性能、网络流量和渲染等方面细节的能力。如果你处于Web应用领域,就给它Chrome开发者工具的支持;如果是原生客户端如iOS或OSX应用,则有其他替代方案以及每天新增的项目可供集成。但所有这些背后的共同基础是:你需要赋予代理查看你作为开发者通常能看到的一切的能力。

是的,你能给予它的越多——无论是更多的“手臂”还是更多双眼睛”,它就越能接管你的工作。然后你只需要赋予它在何时使用这些工具以及如何使用的直觉。想想你是如何决定:如果这个bug出现了,我该如何复现它?思考这一点,并根据你的应用和代码类型进行分解。例如,对于登录流程较多的情况,你可以让它思考:“是否为Cookie正确设置了过期时间?”等等。

在任务中也是如此,对吧?这适用于你拥有软件支持的每个行业和领域。但这本质上是在问:那个我们需要手动雕刻代码的世界正在快速演变或变化,我们现在应该保留哪些系统基础原则来加速软件开发?

没错。比如昂贵的调用栈追踪很重要的一点是获取输出结果,确保它提供关于发生了什么、其状态的详细报告等信息。这样应用或模型就可以对其进行审计,然后再次运行以修复问题并验证更改是否生效且不再存在问题。

是的,而且不能过分强调保留记录(receipts)的价值。如果你有运行日志——比如整个用户旅程:假设是一个电商应用,当它们登录、访问页面时发生了什么?他们的流程是怎样的?是否看到任何错误?目录加载花了7秒吗?他们能否将商品加入购物车?是否存在购物车放弃现象?现在保存所有这些,因为每一次运行和记录都可以喂给大语言模型。你也可以设置一个整夜运行的系统来查找最新产生的记录并进行聚合,比如:“今天我们在缓存模块中发现了8次失败。因此我今晚将发起一个PR来彻底修复它。”然后,“我有工具可以证明此次提交的修复有效。”

此外,那些实现超大规模无错误PR交付的人们也建议加入人类监督环节。不仅仅是功能截图,还包括一段视频记录:“我这样走过应用流程,这是我的体验路径、状态及最终目标达成情况。”这样当你早上醒来需要快速审查15个重要PR时,可以先观看这些记录确认其确实成功运行。然后再查看代码——如果你打算进行人工审查的话。我认为随着自我进化工具的发展,这一切将极具价值。

想想这意味着什么:当自我进化工具成为默认选项并被广泛使用时,那些保留了干净数据集的人将获得特定优势。他们可以说:“这就是我们要达到的目标”,并要求模型快速改进并达到该状态。如果你没有积累大量此类数据而竞争对手有且拥有类似应用,他们的开发周期将会显著更快。

没错。这就是建立信任的过程,以便我们在持续改进应用的同时拥有针对之前所有内容的检查机制。我们有单元测试、评估(evals)、冒烟测试以及应用所需的一切,并让模型能够解释和运行这些检查。

并改进它们,这样如果我们添加新功能,我们可以运行所有之前的测试并确保没有破坏任何东西。不出现回归问题,并且我们可以逐步构建证据阶梯,在过程中建立证据,确保我们的应用按预期工作。这实际上成为了展示我们应用功能的规范。好的,很酷。

所以,我们还剩大约5分钟。我要暂停一下看看是否有其他问题。是的,先生。>> 那么,规模方面……我个人很好奇你是怎么思考这个问题的。就个人而言,我几乎倾向于避免使用技能(skills),因为根据我的经验,是否遵循该路径并使用它取决于模型本身。相比之下,让持续集成(CI)实际强制执行这些命令的运行会更有效一些。

是的,这就是>> 像答案或问题是关于你通常如何运行这些检查的?有效地。 >> [清嗓子]

是的,你可以定期运行这些检查。例如,可以将它们作为CI的一部分。也可以将它们设置为各种钩子(hooks),比如git hook,尽管这可能是我最后才会考虑的选择。我们可以使用云代码钩子(cloud code hook)。这取决于你的团队正在使用的工具以及他们的工作方式。我可能会构建一个完整的CI扩展,让事情在后台运行,并不断输入信息。

通过多种不同的方式可以做到这一点,但最简单的方法无疑是让所有人都达成共识——我现在可以说出来了:在你的应用中放置一个 agents.md 文件,而不是 cloud.md。然后在其中写明类似这样的话:“当发生变更时,通过运行此命令来验证事物”或者“在每次更改重要内容之前或提交PR之前执行此操作”。无论你想用什么措辞,这都是一种让所有人都达成共识的简单方法。他们真的需要刻意忽略或避免它才行。

如果你进行本地开发,钩子也是相关的,对吧?对于整个团队来说,因为你们可以共享这些钩子。

是的,绝对如此。>> 嗯,CI是它的一个特性,但它不像实际工具那样提供你在本地获得的快速开发体验。所以,我给我的代理一个任务去做,我可以简单地运行它——让代理去执行。基本上告诉它我想要达到的终点:我希望等待列表能够添加电子邮件,然后持久化保存并验证这一点。嗯,这就是你如何验证它的方式:它会进入这个表等等。不管具体是什么情况吧。嗯,是的。好问题。当然可以。

是的。

是的。那么,在CI中运行与本地运行的好处是什么?嗯,好处肯定是更快的反馈循环,这使我们能够更快地运行。这也允许我并行处理工作,因为我不再一次只专注于一件事。我现在至少有三个代理在运行。这对我来说是非常低的一天,但我正在参加一个会议。 >> [笑声]

通常大概是10到15个。 >> 他今天真的很轻松啊。 >> 嗯,整个想法是我要启动它,我不希望它在等待我说“是的,我做到了”,然后我就跑过去发现“不,那实际上是不正确的”。然后我必须再次启动它,我又被另一个代理分散了注意力,就这样继续这个循环。我只是

不断地在项目之间移动。所以我想给它提供工具,让它能够向自己证明它做到了,然后向我展示证据,以一种让我信任的方式。这样当我回来时——可能是在5分钟后,也可能是一小时或几小时后——我希望知道它已经尽最大努力完成了工作。这样我就不会只是重新提示它并重新开始整个循环,因为我不想那样做——那不是我的工作有趣的部分。

是的,我会说另一种表达方式是我们还没有每天都完美地实现这一点,但我希望当天开始的所有不同任务——那些我觉得应该在那天完成的重要事项——在结束时回来查看它们,看到基本正确地完成。我不想做的是花了一整天时间让它们在后台运行去做其他事情、参加会议,然后回来时不得不逐条提示它们。因为这会显著减慢速度并阻止我加快速度。

另一种说法是,我依赖CI以及在其中运行的测试和代码审查——你们有多少人使用像Reptile、Code Rabbit或Codo这样的自动化代码评审工具?是的。我依赖这些工具。我们使用Reptile。Reptile给我一个五分制的评分,如果它给了我五颗星并且我的所有CI都通过了,我就非常有信心在生产环境发布时不会破坏任何东西。

我们已经达到了对Reptile的这种信任程度。对于这一点来说,更重要的是我能更快地达到这种状态,并且用更少的交互轮次完成。是的。而且你知道,我可以添加整整一万行毫无意义的代码,它什么都不做,很好——通过了测试。Reptile出于某种糟糕的原因给了它五颗星,然后它就进入了生产环境。它什么都不做也不破坏任何东西。现在只是多了一万行无用的代码。

所以这也是为了帮助我确保像这样:我正在告诉代理我想要的确切内容,并且它在没有经过大量额外代码的情况下达到了目标。它保持在正轨上,但也知道如何自我修复以保持正轨,而我在做其他事情的时候也是如此。

好的,我们可能还有一点点时间问最后一个快速问题,但我们……是的,是的,先生。

是的。是的。

嗯,你也可以根据技术栈的不同来做——比如你可以先部署到预览环境进行验证,然后再以不同的方式在那里验证它。所以我们然后会去生产环境并立即监控。另一种开始出现的模式是,当部署主要由代理实现时,至少会有一个专门关注该生产环境发布的代理。它负责监控所有指标、观察DataDog数据以及功能标志(feature flags)。能够指出“这实际上有问题”并且我们正在回滚它。

嗯,所以我真的理解并感同身受那种不安感——我们都经历过或正在处理这种情况,但我确实……

看到一条可行的路径,因为看起来这似乎是不可避免的。是的。没错。>> 我们的时间到了,但如果您有更多问题,我们很乐意与您交谈。我们将在展厅的6号展位,就在入口右侧。来找我们要聊天、展示我们正在做的工作并讨论任何您想聊的话题。>> 是的,非常感谢您来听我们的演讲以及您的关注和支持。非常感谢! >> [掌声]