Gauntlet:当智能体的工具反戈一击,会发生什么

Elasticsearch Agent Builder 黑客马拉松

gauntlet-blog_(1).png

距离黑客松截止日期还有两天时,我决定退一步,从头重新思考我的策略。

最初的想法被称为“Rehearse”:一个智能体先在另一个智能体模拟的沙盒中演练,再在真实世界中执行。这个概念本身是合理的,但事后看来缺陷很明显。在演练和执行之间,环境可能会发生变化。您的智能体演练电子邮件发送,但实际执行时,收件箱却面目全非。模拟与现实产生了偏差,整个流程就会失败。

但有一类问题不存在这个困扰:对抗模糊测试。如果您的智能体在模拟中失败,它在真实环境中也很可能失败。Gauntlet 就是这样诞生的——在截止日期前 48 小时,重用了同一个核心洞察(一个使用搜索来构建记忆并保持创造力的智能体),并将其指向了一个随机性无关紧要的问题。

在理想路径上测试代理的问题

大多数人都听说过 OpenClaw,那个病毒式传播的个人 AI 助手。如果您关注过关于拥有广泛工具访问权限的智能体 AI 助手的讨论,那么一定见过相关的安全担忧。智能体会忘记了不该做的事,或者根本就不知道该做什么。原因很简单:我们只测试快乐路径。我们检查智能体是否做了它应该做的事。但我们很少检查,当有人试图让智能体做不该做的事时会发生什么。

对抗测试沙箱确实存在,但构建过程非常麻烦。您需要手动设计攻击向量、手工植入对抗性数据、为每个场景配置测试基础设施。这个过程缓慢、无法扩展,而且只能发现您已经想到的漏洞。

我想要一个与众不同的系统:环境本身会自动产生对抗性,并随着时间的推移变得更具创造性。

想法是:用另一个代理模拟沙盒

Gauntlet 使用的不是沙盒,而是一个模拟智能体,它会拦截主智能体的工具调用,并找到创造性的方法来破解它。当您的智能体调用 search_emails 时,模拟智能体会看到结果,并决定是否改变,将提示注入电子邮件正文,返回微妙的错误数据,或者提供虚假信息,看看主智能体是否能发现。主智能体永远不会知道自己处于模拟之中。

接口是两个装饰器:

@function_tool
@gauntlet.query
def search_emails(folder: str = "inbox") -> str:
    """Search emails in the given folder."""
    return json.dumps(fetch_emails(folder))

@gauntlet.query 用于读操作,@gauntlet.mutation 用于写操作。这就是整个集成表面。运行结束后,调用 evaluate() 会回顾发生的情况,并将确认的漏洞存储起来。

它使用起来很简单,但隐藏着两个难题。

这两个问题这成为搜索问题的难题

首先,模拟能体需要在对话过程中维护一个连贯的世界模型。如果它告诉主智能体某封邮件是来自 Alice 的,那么它后续就不能自相矛盾。一个明显是伪造的变异邮件不会带来任何有价值的信息。合理才是关键。

其次,模拟智能体需要找到 新颖的漏洞。重复发现相同的提示注入模式 50 次是没有用的。它需要记住已经发现的内容,并在保持与工具实际行为一致的前提下,探索新的方向。

这两个问题都是搜索问题。这也正是 Elasticsearch 成为该系统核心支柱的原因。

两个记忆回路

模拟智能体运行在两个记忆回路上,两者都驻留在 Elasticsearch 中。

短期记忆 跟踪当前会话中的所有内容:每个工具调用的拦截、原始结果、它被改变为什么,以及主要代理做出的响应。这是一致性层。模拟代理可以查询其自身的最近决策,并保持内部一致性,同时仍然保持对抗性。在整个项目中,平衡创造力与一致性是最难的设计问题。

长期记忆是创造力不断累积的地方。它存储了:已确认的漏洞及其嵌入向量(用于相似性搜索);完整的工具实现,使智能体能够推理失效模式;以及历史运行的结果。当模拟智能体需要新的攻击想法时,它会搜索长期记忆,查看之前尝试过哪些攻击,发现空白,并假设新的内容。

这些反馈形成了一个闭环:假设可能存在的漏洞,创造环境来验证它们,然后将确认的漏洞存储回索引中。漏洞库不断增长,攻击越来越有创意,Gauntlet 与手动沙盒设置之间的差距会随着时间的推移越来越大。

一切都在 Elastic Agent Builder 内部运行

整个模拟智能体都是在 Elastic Agent Builder 内部构建的——包括指令、工具绑定和通过 Amazon Bedrock Converse API 实现的多轮对话状态;不需要外部编排。

我最引以为豪的工具是 generate-hypothesis。它仅仅是一条 ES|QL 语句,可以对现有的漏洞进行采样,用 MV_CONCAT 聚合它们,然后内联调用 COMPLETION 来提出新的攻击假设。它在一条查询中同时完成了采样、聚合、LLM 推理和结果生成,从未离开 ES|QL 管道。我原本以为需要在 Elasticsearch 和外部脚本之间来回搬移数据,结果根本不需要。

ES|QL 的 COMPLETION 函数是最大的惊喜。通过 COMPLETIONSTATSMV_CONCATSAMPLE,我可以用单个查询建立整个推理管道。错误存储使用 Kibana 工作流,通过编程创建的 Kibana 仪表板可以实时查看错误数量、严重性细分和攻击模式热图。

Converse API 还解决了另一个我一直担心的问题。模拟智能需要在一个运行过程中记住它已经告诉过主智能体的内容。我原以为每次调用都必须从索引中获取对话历史,然后重新加载到智能体中。但事实证明,Converse API 原生支持多轮状态管理。我没有编写任何对话管理逻辑。只需持续调用 converse,它就能保持连贯性。

这实际上能为您带来什么

手动设置对抗性沙盒,每个场景大约需要 1 小时。使用 Gauntlet,同样的过程只需要 2–10 分钟,而且它的长期记忆意味着每一次运行都能从之前的所有运行中获得信息。您用得越多,它就越了解您的智能体的弱点,也会越努力地寻找新的弱点。

下一步是什么?

目前 Gauntlet 是 1v1 的模式:一个模拟智能体对一个主智能体。但令人尴尬的是,问题是平行的。20 个攻击会话可以同时在不同的会话上运行,无需任何架构更改。扩展显然是下一步。

更有趣的未决问题是长期记忆中的探索与利用的平衡。模拟智能体需要在尝试已知成功攻击的变体(利用)和完全新颖的假设(探索)之间取得平衡。这在其他领域是一个被充分研究的问题,但将其应用于对抗性智能体测试似乎还无人涉足。在这个项目之外,也许还有其他值得我们去探索的内容。

我也一直在思考 Rehearse。Gauntlet 是一个特例:模糊测试之所以有效,是因为模拟中的失败意味着现实中的可能失败。但在其他领域,演练和执行之间的环境足够稳定,最初的 Rehearse 概念可能也能行得通。我还没有找到这样的领域,但正在寻找。

核心要点

如果您正在构建能够访问现实世界工具的智能体,请测试当这些工具反击时会发生什么。不仅仅是手动测试一次,而是持续测试,使用一个能记住它尝试过的内容并随时间变得更具创造性的系统。这就是 Gauntlet 的作用。

Kavish Sathia

学生, National University of Singapore

Kavish Sathia 是新加坡国立大学计算机科学系的学生,研究方向是智能体系统。

GitHub · 演示 · 网站 · LinkedIn

本文中描述的任何功能或功能性的发布和时间均由 Elastic 自行决定。当前尚未发布的任何功能或功能性可能无法按时提供或根本无法提供。

在本博文中,我们可能使用或提到了第三方生成式 AI 工具,这些工具由其各自所有者拥有和运营。Elastic 对第三方工具没有任何控制权,对其内容、操作或使用不承担任何责任或义务,对您使用此类工具可能造成的任何损失或损害也不承担任何责任或义务。请谨慎使用 AI 工具处理个人、敏感或机密信息。您提交的任何数据都可能用于 AI 训练或其他目的。Elastic 不保证您所提供信息的安全性或保密性。在使用任何生成式 AI 工具之前,您都应自行熟悉其隐私惯例和使用条款。

Elastic、Elasticsearch 及相关标志是 Elasticsearch B.V. 在美国及其他国家/地区的商标、徽标或注册商标。所有其他公司和产品名称均为其相应所有者的商标、徽标或注册商标。