Anthropic:如何建立有效的Agent

在过去的一年里,我们与数十个团队合作,构建了跨行业的大型语言模型(LLM)代理。一直以来,最成功的实现都没有使用复杂的框架或专门的库。相反,他们使用简单的、可组合的模式进行构建。

在这篇文章中,我们分享了我们从与客户和自己构建代理的工作中学到的东西,并为开发人员提供构建有效代理的实用建议。

什么是代理?

“Agent”可以用几种方法来定义。一些客户将代理定义为完全自主的系统,这些系统在较长的时间内独立运行,使用各种工具来完成复杂的任务。
其他人使用这个术语来描述遵循预定义工作流的更具规范性的实现。在Anthropic,我们将所有这些变化归类为智能体系统,但在工作流和智能体之间做出了重要的架构区分:

工作流是通过预定义的代码路径编排llm和工具的系统。
另一方面,代理是llm动态指导自己的过程和工具使用的系统,保持对它们如何完成任务的控制。
下面,我们将详细探讨这两种类型的智能体系统。在附录1(“实践中的代理”)中,我们描述了两个领域,客户在使用这些类型的系统时发现了特别的价值。

何时(何时不)使用代理

在使用llm构建应用程序时,我们建议找到尽可能简单的解决方案,并只在需要时增加复杂性。这可能意味着根本不需要建立智能体系统。智能体系统通常会权衡延迟和成本来获得更好的任务性能,您应该考虑这种权衡何时有意义。

当需要更多的复杂性时,工作流为定义良好的任务提供了可预测性和一致性,而当大规模需要灵活性和模型驱动决策时,代理是更好的选择。然而,对于许多应用程序来说,使用检索和上下文示例优化单个LLM调用通常就足够了。

何时以及如何使用框架

有许多框架可以使智能体系统更容易实现,包括:

  • 基于LangChain的LangGraph;
  • Amazon Bedrock的AI Agent框架;
  • Rivet,一个拖放GUI LLM工作流构建器;和
  • Vellum,另一个用于构建和测试复杂工作流的GUI工具。

这些框架简化了标准的底层任务,如调用llm,定义和解析工具,并将调用链接在一起,从而使入门变得容易。

然而,它们经常创建额外的抽象层,这些抽象层可能会掩盖底层的提示和响应,使它们更难调试。当一个简单的设置就足够了时,它们还会诱人地增加复杂性。

我们建议开发人员从直接使用LLM api开始:许多模式可以在几行代码中实现。如果你使用框架,请确保你理解底层代码。

对底层的错误假设是客户错误的一个常见来源。

有关示例实现,请参阅我们的“cookbook”。

构建block、工作流和Agent

在本节中,我们将探索我们在生产中看到的智能体系统的常见模式。我们将从我们的基础构建块(增强的llm)开始,并逐步增加复杂性,从简单的组合工作流到自主代理。

构建block:增强的LLM

智能体系统的基本构建模块是一个经过增强的LLM,如检索工具记忆。我们目前的模型可以积极地使用这些功能——生成自己的搜索查询,选择适当的工具,并确定要保留哪些信息。

The augmented LLM

我们建议关注实现的两个关键方面:

  1. 根据您的特定用例定制这些功能;
  2. 确保它们为您的LLM提供一个简单的、文档良好的接口。

虽然有许多方法来实现这些扩展,其中一种方法是通过我们最近发布的Model Context协议,它允许开发人员使用简单的客户端实现来集成不断增长的第三方工具生态系统。

对于本文的其余部分,我们将假设每个LLM调用都可以访问这些增强的功能。

工作流:提示链(prompt chaining)

提示链将任务分解为一系列步骤,其中每个LLM调用处理前一个步骤的输出。您可以在任何中间步骤上添加程序化检查(请参见下图中的“gate”),以确保过程仍然在正轨上。

The prompt chaining workflow

何时使用此工作流:此工作流非常适合于任务可以轻松且清晰地分解为固定子任务的情况

主要目标是通过使每个LLM调用更容易的任务来权衡延迟和更高的精度。

提示链很有用的例子:

  • 生成营销文案,然后将其翻译成不同的语言。
  • 写一个文档的提纲,检查提纲是否符合一定的标准,然后根据提纲写文档。

工作流:路由

路由对输入进行分类,并将其定向到专门的后续任务。

该工作流允许分离关注点,并构建更专门的提示。如果没有这种工作流程,优化一种输入数据可能会影响其他输入数据的性能。

The routing workflow

何时使用此工作流:路由适用于复杂任务,其中有不同的类别,可以更好地单独处理,并且可以通过LLM或更传统的分类模型/算法准确地处理分类

路由有用的例子:

  • 将不同类型的客户服务查询(一般问题、退款请求、技术支持)引导到不同的下游流程、提示和工具。
  • 将简单/常见的问题转换为更小的模型,如Claude 3.5 Haiku,将困难/不寻常的问题转换为更有能力的模型,如Claude 3.5 Sonnet,以优化成本和速度。

工作流:并行化

llm有时可以同时处理一个任务,并以编程方式聚合其输出。这种工作流程,即并行化,表现为两种主要变体:

  • 分段(section):将任务分解为独立的、并行运行的子任务。
  • 投票:多次运行相同的任务以获得不同的输出。

The parallelization workflow

何时使用此工作流:

  1. 当划分的子任务可以并行化以提高速度,或者当需要多个视角或尝试以获得更高的置信度结果时,并行化是有效的。

  2. 对于具有多种考虑因素的复杂任务,当每个考虑因素都由单独的LLM调用处理时,LLM通常表现得更好,允许将注意力集中在每个特定方面。

并行化有用的例子:

  • 分段:

    • 实现护栏,一个模型实例处理用户查询,而另一个实例屏蔽用户不合适的内容或请求。这往往比让相同的LLM调用同时处理护栏和核心响应表现得更好。

    • 自动评估LLM性能,其中每个LLM调用在给定提示上评估模型性能的不同方面。

  • 投票:

    • 检查一段代码的漏洞,如果他们发现了问题,几个不同的提示会检查并标记代码。

    • 评估给定的内容是否不合适,通过多个提示评估不同方面或需要不同的投票阈值来平衡误报和漏报。

工作流:Orchestrator-workers(编排器-工作器)

在编排器-工作器工作流中,中心LLM动态地分解任务,将它们委托给worker LLM,并合成它们的结果。

何时使用此工作流:此工作流非常适合于无法预测所需子任务的复杂任务(例如,在编码中,需要更改的文件数量以及每个文件更改的性质可能取决于任务)。

虽然它在拓扑结构上是相似的,但与并行化的关键区别在于它的灵活性——子任务不是预定义的,而是由协调器根据特定的输入确定的。

使用orchestrator-workers的例子:

  • 编写每次对多个文件进行复杂更改的产品。
  • 涉及从多个来源收集和分析可能相关信息的搜索任务。

工作流:Evaluator-optimizer(评估器-优化器)

在评估器-优化器工作流中,一个LLM调用生成响应,而另一个LLM调用在循环中提供评估和反馈。

The evaluator-optimizer workflow

何时使用此工作流:当我们有明确的评估标准,并且当迭代精化提供可衡量的价值时,此工作流特别有效。

良好拟合的两个标志是,

  1. 首先,当人类清晰地表达他们的反馈时,LLM响应可以明显改善;
  2. 其次,LLM可以提供这样的反馈。

这类似于人类作家在生成/优化的文档时可能经历的迭代写作过程。

evaluate-optimizer有用的例子:

  • 文学翻译中有一些细微差别,译者LLM最初可能无法捕捉,但评估者LLM可以提供有用的批评。

  • 复杂的搜索任务,需要多轮搜索和分析来收集全面的信息,评估者决定是否需要进一步搜索。

Agents

随着llm关键功能的成熟,智能体正在生产中出现——理解复杂的输入,进行推理和规划,可靠地使用工具,以及从错误中恢复。

智能体以来自人类用户的命令或与人类用户进行交互式讨论开始其工作。

一旦任务明确,智能体独立计划和操作,可能会返回人类以获取进一步的信息或判断。

在执行过程中,智能体在每一步(如工具调用结果或代码执行)从环境中获取“基本事实”以评估其进展是至关重要的。然后,在检查站或遇到障碍物时,智能体可以暂停等待人类反馈。

任务通常在完成时终止,但通常也包括停止条件(例如最大迭代次数)以保持控制。

智能体可以处理复杂的任务,但它们的实现通常很简单。

它们通常只是llm在一个循环中使用基于环境反馈的工具

因此,清楚而周到地设计工具集及其文档至关重要。

我们会在附录2(“提示你的工具工程”)中详细介绍工具开发的最佳实践。

何时使用智能体:智能体可用于难以或不可能预测所需步数的开放式问题,以及无法硬编码固定路径的情况。LLM可能会运行许多回合,你必须对它的决策有一定程度的信任。agent的自治性使其非常适合在可信环境中扩展任务。

代理的自主性意味着更高的成本,以及潜在的复合错误。我们建议在沙箱环境中进行广泛的测试,并设置适当的护栏。

代理有用的例子:

下面的例子是我们自己实现的:

  • 一个编码代理,用于解决基于一个任务描述对多个文件进行编辑的SWE-bench tasks任务;

  • 我们的“computer use”参考实现,Claude使用计算机完成任务。

High-level flow of a coding agent

组合和定制这些模式

这些构建模块并不是规范性的。它们是开发人员可以塑造和组合的常见模式,以适应不同的用例。

与任何LLM功能一样,成功的关键是测量性能迭代实现

重申一下:只有当增加复杂性可以明显改善结果时,才应该考虑增加复杂性

概要

LLM领域的成功不是建立最复杂的系统。它是关于为你的需求建立正确的系统。从简单的提示开始,通过综合评估进行优化,只有在简单的解决方案不满足时才添加多步骤的智能体系统。

在实现智能体时,我们尝试遵循三个核心原则:

  1. 保持Agent设计的简单性。
  2. 通过明确显示Agent的规划步骤来优先考虑透明度。
  3. 通过彻底的工具文档和测试,仔细制作您的代理-计算机接口(ACI)。

框架可以帮助你快速入门,但当你迁移到生产环境时,不要犹豫减少抽象层并使用基本组件进行构建。

通过遵循这些原则,您可以创建不仅强大而且可靠、可维护并受其用户信任的代理。

感谢

由Erik Schluntz和Barry Zhang撰写。这项工作借鉴了我们在Anthropic建立Agent的经验,以及客户分享的宝贵见解,对此我们深表感谢。

附录1:实践中的Agents

我们与客户的合作揭示了人工智能代理的两个特别有前途的应用,证明了上述模式的实用价值。

这两个应用都说明了智能体如何为既需要对话又需要行动的任务增加最大价值,具有明确的成功标准,实现反馈循环,并集成有意义的人工监督。

A.客户支持

客户支持通过工具集成将熟悉的聊天机器人界面与增强的功能结合起来。这自然适合更开放的智能体,因为:

  • 支持自然地遵循对话流的交互,同时需要访问外部信息和操作;
  • 可以集成工具来拉取客户数据、订单历史和知识库文章;
  • 发放退款或更新票证等操作可以程序化处理;
  • 通过用户定义的分辨率可以清楚地衡量成功。

几家公司已经通过基于使用的定价模型证明了这种方法的可行性,这些模型只对成功的解决方案收取费用,显示出他们对代理的有效性的信心。

B.编码Agent

软件开发领域已经显示出LLM功能的巨大潜力,其功能从代码完成发展到自主解决问题。代理特别有效,因为:

  • 代码解决方案可以通过自动化测试进行验证;
  • 智能体可以使用测试结果作为反馈对解决方案进行迭代;
  • 问题空间是定义良好和结构化的;和
  • 输出质量可以被客观地衡量。

在我们自己的实现中,代理现在可以仅根据拉请求描述来解决 SWE-bench验证的基准中的真实GitHub问题。

然而,尽管自动化测试有助于验证功能,人工审查对于确保解决方案与更广泛的系统需求保持一致仍然至关重要。

附录2:工具的提示工程

无论您构建的是哪种智能体系统,工具都可能是智能体的重要组成部分。

工具使Claude能够通过在我们的API中指定它们的确切结构和定义来与外部服务和API交互。

当Claude响应时,如果它计划调用一个工具,它将在API响应中包含一个工具使用块。工具定义和规范应该与您的总体提示一样得到工程方面的及时关注。在这个简短的附录中,我们将描述如何提示工程师使用工具。

通常有几种方法可以指定相同的操作。

例如,您可以通过编写diff或重写整个文件来指定文件编辑。对于结构化输出,你可以在markdown或JSON中返回代码。

在软件工程中,这样的差异只是表面上的,可以无损地从一个差异转换到另一个差异。

然而,对于LLM来说,有些格式比其他格式更难编写。写入diff需要在写入新代码之前知道分块头中有多少行发生了更改。

在JSON中编写代码(与markdown相比)需要对换行符和引号进行额外的转义。

我们对确定工具格式的建议如下。

  • 在模型把自己写进一个角落之前,给它足够的标记来“思考”。
  • 保持格式接近模型在互联网上自然出现的文本。
  • 确保没有格式“开销”,例如必须精确记录数千行代码,或者对编写的代码进行字符串转义。

一个经验法则是考虑在人机界面(HCI)上投入多少精力,并计划在创建良好的代理-计算机界面(ACI)上投入同样多的精力。下面是一些关于如何做到这一点的想法。

  • 设身处地为模型着想。根据描述和参数,该如何使用这个工具是显而易见的吗?还是需要仔细考虑?如果是这样,那么模型可能也是如此。一个好的工具定义通常包括:

    1. 示例使用
    2. 边界情况
    3. 输入格式要求
    4. 以及来自其他工具的清晰边界。
  • 如何设置参数名称或描述以使效果更明显呢?可以将其视为为团队中的初级开发人员编写一个很棒的文档字符串。当使用许多类似的工具时,这一点尤其重要。

  • 测试模型如何使用您的工具:在我们的工作台中运行许多示例输入,以查看模型犯了什么错误,并迭代。

  • 防错化(Poka-yoke)你的工具:修改参数设计,使其更难出错。

在为SWE-bench构建代理时,我们实际上花费了更多时间来优化我们的工具,而不是整体提示。

例如,我们发现当代理移出根目录后,模型在使用相对文件路径的工具时会犯错。为了解决这个问题,我们修改了工具,要求始终使用绝对文件路径——我们发现模型使用这种方法时完全没有出错。