@huntlovell: https://x.com/huntlovell/status/2057166131924988002
摘要
Deep Agents 引入了解释器:小型嵌入式运行时,允许智能体在智能体循环内编写和执行代码,实现多步逻辑和中间状态管理,无需完整的沙箱开销。
查看缓存全文
缓存时间: 2026/05/21 13:35
给智能体装上解释器
TL;DR 我们正在为 Deep Agents 加入解释器:一种小巧的嵌入式运行时,智能体可以在其循环内部编写并执行代码。它为智能体提供了介于逐一工具调用与完整沙箱之间的中间地带,让智能体能够表达多步骤工作、将中间状态保留在模型上下文之外,并以更可预测的方式执行代码和操作。
什么是解释器?
解释器是一种小巧的嵌入式运行时,智能体在工作期间可以针对它编写代码。从功能上讲,就像给了智能体一个 Python 或 Node REPL:它可以定义变量、检查值、编写辅助函数,并在多次调用之间复用状态。
如今许多智能体已经通过向宿主或沙箱环境发出命令来执行代码。当任务是环境层面的工作(运行命令、安装依赖、操作文件系统)时,这非常有用。解释器则针对不同的层次:智能体编写的代码运行在智能体循环内部,用于协调委派、组合工具调用、转换结构化数据,并决定哪些信息应该返回给模型。
这为智能体提供了新的表达空间,以适应那些不能干净地融入一串工具调用的行为。智能体获得了一个用于多步骤逻辑的工作空间,而框架依然控制着这个工作空间能够触及的范围。解释器可以保存临时状态,并只返回重要的部分。
解释器的定位
说到智能体,通常你会想到给它挂载工具。
在最简单的智能体形式中,智能体在一个循环中使用这些工具:模型调用一个工具,检查观察结果,然后决定下一步做什么。这种逐个步骤的方式便于调试和评估,许多工作流也确实需要一种基于即时观察进行推理的方法。
沙箱在此基础上更进一步,给智能体提供一个 bash 工具,让它能在环境中运行命令、安装依赖和处理文件。
但两种方式都有缺点:沙箱可以处理本地过程(因为它可以编写代码来完成),但配置和扩展起来可能更困难;而纯粹串行的工具循环在那些中间步骤主要服务于下一步时会显得笨拙。
某些智能体工作介于这两个极端之间,这正是解释器擅长的领域。它让智能体在限定的能力范围内进行代码级组合,而不必提供完整的环境。模型可以编写一个小程序来表达对现有能力的控制流,而框架则决定哪些能力通过宿主环境可用。
有意为之的受限设计
我们称之为解释器,而不仅仅是代码运行时,因为解释器是有意受限的。默认情况下,它没有普通编程环境应有的 API:没有文件系统、没有网络、没有 shell、没有包安装,也没有实时的挂钟时间访问。智能体起步时只有基本的控制流和对象操作:对象、数组、映射、JSON 以及其他小型语言运行时功能。
这些能力通过显式的桥接暴露给宿主运行时。如果智能体需要调用工具、从受限的文件系统 API 读取数据、获取 URL,或者委派给子智能体,框架必须有意识地暴露这些能力。例如,只有当我们明确地将 fetch、read_file 和 task 工具直接桥接到解释器时,这个脚本才能工作:
宿主运行时(也就是运行框架的那个)包含了智能体使用解释器所能执行的所有操作,并明确决定哪些操作是解释器代码可以调用的。解释器是智能体这一侧的可编程边界。
默认情况下,解释器只具备语言特性,而不是像沙箱那样提供通用的宿主访问。任何与外部世界的接触都必须通过你指定的显式桥接。
我们这样做有几个原因:
-
更小的攻击面:使用 bash 或沙箱,起点是宽泛的:智能体拥有一个类似计算机的东西,你从这里开始限制它能做什么。而使用解释器,起点是狭窄的:智能体只有语言运行时,能力被有意地添加回来。这并不能替代当你需要进程或虚拟机隔离时的沙箱功能,但意味着智能体默认不会继承广泛的宿主访问权限。
-
可预测性:一个小而固定的运行时使得智能体行为更容易预期和评估。如果解释器拥有广泛的宿主访问权限或丰富的库支持,同样的目标可以通过多种不同策略达成,导致输出一致性降低、更难测试。通过保持默认环境最小化,并强制额外能力通过显式桥接,你让智能体的动作空间更窄、失败模式更清晰、结果更可复现。
在 Figma、Shopify、AWS 等系统的架构中,你可以看到同样的设计模式:受限的代码在一侧运行,而宿主在另一侧暴露受控的 API 边界。
解释器解锁的能力
近期一些系统已经收敛到类似的模式:给模型一个小巧、受限的运行时,让它能写一点代码来管理控制流和中间状态。Cloudflare 的 Code Mode、Anthropic 的程序化工具调用(PTC)以及 RLM 风格的工作流,都从不同角度指向了这个想法。在 Deep Agents 中,解释器是让你以模型无关的方式获得这种模式的方法。以下是一些已经证明有用的场景:
解释器状态作为上下文表面
智能体框架已经在几个表面上组织上下文:
-
消息历史是模型立即可用的上下文。它昂贵且受注意力限制:模型能接受一百万个 token,并不意味着它对每个 token 都能同样良好地推理(例如上下文腐烂问题)。
-
文件系统为智能体提供了存储持久化制品、笔记、中间文件以及更长期工作记忆的地方。它持久且灵活,但迫使智能体将工作状态序列化为文件,然后再重建。 框架的部分职责是控制文件系统与消息历史之间的上下文流动。
解释器状态为智能体提供了另一种选择。值可以保留在运行时中,作为数组、对象、映射、计数器、队列和辅助函数。模型不需要看到每一个中间值作为提示文本,但它仍然可以要求解释器稍后检查或复用这些值。
这类似于 REPL 与运行一次性命令的区别。如果你在 REPL 中定义了一个变量,在提交下一个命令时它仍然存在。你无需将其转为 stdout、写入文件或重建它再执行下一步。同样的原理适用于智能体多次调用解释器时,因为它可以直接复用前一次调用的值。
这使得解释器对于智能体循环状态非常有用。消息历史用于模型现在需要推理的内容,文件系统用于持久化制品和环境层面的工作,而解释器状态用于当前活跃的工作值——这些值稍后可能有用,但暂时不需要成为模型输入。
程序化工具调用
Anthropic 的程序化工具调用(PTC)是这个模式的另一个版本:工具调用发生在智能体编写的代码内部,而不是作为一系列模型中介的操作。
如果模型调用一个工具、接收完整结果、推理它、再调用下一个工具,那么每一个小步骤都会变成一次额外的模型往返。如果智能体可以编写直接调用工具的代码,它就可以将中间输出保留在运行时中,只返回最终结果或选定的证据。
在 Deep Agents 中,PTC 作为中间件实现,而不是作为模型提供商的行为。开发者传入一个允许列表,允许列表中的工具出现在全局的 tools 命名空间下,每个工具都被暴露为解释器可以用 await 调用的异步函数。这意味着你可以为任何模型(包括开源模型)启用 PTC。
在我们的一些早期测试中,这种工具调用风格在某些任务上节省了高达 35% 的 token。(我们在 OOLONG trec-coarse 数据集的一个收集任务集上评估了这一点)
处理大型数据集
以一个文档密集型任务为例:一个智能体需要从 10,000 份文档中进行分类、提取或综合信息。
使用标准的工具调用智能体,自然的形式是一长串模型中介操作。模型搜索、将结果放回上下文、决定下一步检查什么、调用另一个工具、获取更多结果、重复。对于小任务,这个循环足够了。但在规模变大时,它开始出现问题:
- 难以验证智能体是否真的遵循了预期流程。
- 太多的中间上下文被路由回模型。
- 容易遇到延迟、上下文长度或工具调用限制。
- 因为模型被迫通过历史管理太多工作状态,响应质量可能下降。
一个使用解释器的版本看起来不同。模型可以编写代码,将文档和搜索状态保留在运行时中,程序化地遍历批次,对候选进行评分或过滤,并仅对选定的子集调用子智能体。解释器不会将每个中间结果都返回给模型,而是返回一个紧凑的证据集:匹配的文档、提取的字段、未解决的情况,或值得推理的少量摘要。
解释器并不会神奇地推理所有 10,000 份文档。它给智能体提供了更好的控制搜索空间和决定哪些内容应该进入模型上下文的方法。
递归语言模型
另一个相关思想是递归语言模型(RLM)。RLM 将长提示视为外部 REPL 环境的一部分,然后让模型编写代码来检查、分解,并在选定的代码片段上递归调用模型。
Deep Agents 的解释器并没有在模型层实现 RLM,但在框架层面仍然存在相关联系:代码可以将工作状态保留在模型上下文之外,选择该状态的一部分,并只将那一部分传递给下一个模型或子智能体调用。
在 Deep Agents 中,tools.task 就是用于此目的的桥接。解释器代码可以选择一部分工作,将其委派给子智能体,将结果与现有运行时状态合并,只将综合后的输出返回给主模型。
在 Deep Agents 中的工作原理
在框架层面,解释器是智能体循环与小型运行时之间的中间件。这个中间件:
- 为智能体添加一个
eval工具 - 创建并维护一个 QuickJS 上下文
- 执行智能体的 TypeScript 代码
- 在配置时捕获
console.log输出 - 将最终的表达式返回给模型上下文
eval 工具并不是“在宿主机上运行任意代码”。代码在解释器上下文内部执行。如果需要与外部世界通信,它通过宿主运行时暴露的桥接进行。
程序化工具调用就是这些宿主桥接之一。开发者传入一个 PTC 允许列表,允许列表中的工具出现在解释器内的 tools 命名空间下(例如 tools.getWeather(...)),每个工具都暴露为解释器可以用 await 调用的异步函数。真实的工具调用仍然由宿主运行时执行。
大致流程如下:
- 模型编写代码并调用
eval - QuickJS 在解释器上下文中执行代码
- 解释器代码可选地调用允许列表中的工具
- 宿主运行时执行真实的工具调用
- 结果传回解释器
- 最终的表达式传回模型上下文
在一次运行中重复的 eval 调用可以共享同一个活跃的解释器上下文,这正是让值表现得像 REPL 状态的原因。在对话轮次之间也可以进行快照,但这应该被视为保留可序列化工作数据的一种方式,而不是保留活跃句柄或宿主资源。
运行时的控制也位于这个边界:
- 内存限制
- 每次 eval 的超时
- 最大程序化工具调用次数
- 最大结果大小
- 控制台捕获
- 轮次之间的快照
如何在 Deep Agents 中使用
你可以安装解释器,并使用 create_deep_agent 添加中间件:
(以及 TypeScript 版本)
要让解释器代码调用智能体工具,启用带允许列表的程序化工具调用。工具不会自动暴露给解释器代码,你必须选择哪些工具可以跨越宿主运行时的桥接。
一旦启用 PTC,允许列表中的工具会出现在全局的 tools 命名空间下。每个工具都是一个异步函数,模型接收的是解释器的最终输出,而不是每个中间工具结果。
Deep Agents 支持 Python 和 TypeScript。更多关于解释器、中间件选项和运行时控制的信息,请参阅文档。
特别感谢 @sydneyrunkle、@hwchase17 和 @veryboldbagel 对本稿件的审阅。
相似文章
@sydneyrunkle: https://x.com/sydneyrunkle/status/2056419909941522687
Deep Agents v0.6 引入了代码解释器、用于按模型调优的测试配置文件、流式支持、用于检查点存储的 DeltaChannel 以及用于版本化代理记忆的 ContextHubBackend,实现了模型无关的编程式工具调用和递归工作流。
@sydneyrunkle: https://x.com/sydneyrunkle/status/2071629451712983319
Deep Agents 引入了动态子代理,它们通过代码脚本进行程序化编排,而不是使用工具调用,从而实现了可靠的扩展和复杂的工作流程。该功能集成了 QuickJS 代码解释器以实现轻量级执行。
@LangChain:让代理具备编写代码的能力,能让它们变得极其强大。这也使得安全性问题变得更加棘手。在L…
LangChain的Deep Agents允许不受信任的代理编写的代码通过基于WebAssembly的代码解释器安全运行,提供执行和能力隔离,无需传统沙盒。
如何让代理运行数小时,以及哪些架构真正对代理友好?#深度探讨 #氛围程序员问题
作者探讨了AI编码代理的两个关键挑战:确保长时间自主执行(数小时)以及为本地应用设计对代理友好的架构。他们提出在规划和执行之前,增加一个显式的知识组织阶段来管理混乱的上下文。
@ShrekOverflow:我对这个非常兴奋,它的工作方式自早期代理以来就一直是我所坚信的!快来试试吧
Kent C. Dodds 构建了 Kody,一个个人助手,它能增强 AI 代理,使其在沙箱中运行可确定性触发的代码。