翻译:Building effective agents

207 阅读15分钟

翻译:原文

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

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

什么是代理?

What are agents?

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

  • 工作流

    工作流是通过预定义的代码路径编排 LLMs 和工具的系统。

  • 代理

    代理是 LLMs 动态指导自己的流程和对工具使用,保持对如何完成任务的控制。

下面,我们将详细探讨这两种类型的代理系统。在附录 1(“代理实践”)中,我们描述了客户发现使用这些类型系统具有特殊价值的两个领域。

何时(以及何时不使用)代理

When (and when not) to use agents

使用 LLMs,我们建议尽可能使用简单的解决方案,并且仅在需要时增加复杂性。这意味着不能一味的构建代理系统。主要原因在于:代理系统通常会以延迟和成本为代价来获得更好的任务性能,在构建代理系统时,您应该考虑权衡这些是非常有意义的。

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

何时以及如何使用框架

When and how to use frameworks

有许多框架使代理系统更易于实现,包括:

这些框架通过简化标准的低级任务(如调用 LLMs、定义和解析工具以及将调用链接在一起)来轻松入门。但是,它们通常会创建额外的抽象层,这可能会掩盖底层提示和响应,使它们更难调试。当更简单的设置就足够时,它们也可能使增加复杂性变得诱人。

我们建议开发人员直接从使用 LLM:许多模式可以在几行代码中实现。如果您确实使用框架,请确保您了解底层代码。对引擎盖下内容的错误假设是客户错误的常见来源。

有关一些示例实施,请参阅我们的说明书

构建基块、工作流和代理

Building blocks, workflows, and agents

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

构建基块:增强的 LLM

Building block: The augmented LLM

代理系统的基本构建块是 LLM,通过检索、工具和内存等增强功能进行了增强。我们当前的模型可以主动使用这些功能 — 生成自己的搜索查询、选择适当的工具并确定要保留的信息。

image.png

我们建议关注实施的两个关键方面:根据您的特定使用案例定制这些功能,并确保它们为您的 LLM。虽然有很多方法可以实现这些增强,但一种方法是通过我们最近发布的 Model Context Protocol,它允许开发人员通过简单的客户端实现与不断增长的第三方工具生态系统集成。

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

工作流:提示链接

Workflow: Prompt chaining

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

image.png

何时使用此工作流程: 此工作流程非常适合可以轻松、干净地将任务分解为固定子任务的情况。主要目标是通过使每个 LLM。

提示链接使用场景示例:

  • 生成营销副本,然后将其翻译成不同的语言。
  • 编写文档的大纲,检查大纲是否满足特定条件,然后根据大纲编写文档。

工作流:路由

Workflow: Routing

路由 对输入进行分类并将其定向到专门的后续任务。此工作流允许分离关注点,并构建更专业的提示。如果没有此工作流程,针对一种输入进行优化可能会损害其他输入的性能。

image.png

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

路由使用场景示例:

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

工作流:并行化

Workflow: Parallelization

LLMs 有时可以同时处理一项任务,并以编程方式聚合其输出。此工作流 (并行化) 表现为两个关键变体:

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

image.png

何时使用此工作流程: 当可以并行化已划分的子任务以提高速度时,或者当需要多个视角或尝试以获得更高的置信度结果时,并行化非常有效。对于具有多个考虑因素的复杂任务,当每个考虑因素都由单独的 LLM 通常会表现得更好,从而可以将注意力集中在每个特定方面。

并行化使用场景示例:

  • 切片
    • 实施护栏,其中一个模型实例处理用户查询,而另一个模型实例筛选用户查询是否存在不适当的内容或请求。这往往比使用相同的 LLM 调用同时处理护栏和核心响应的性能要好。
    • 自动评估以评估 LLM 性能,其中每个 LLM 调用都会评估模型在给定提示符下性能的不同方面。
  • 投票
    • 检查一段代码是否存在漏洞,如果发现问题,则几个不同的提示会检查并标记代码。
    • 评估给定的内容是否不合适,多个提示评估不同的方面或要求不同的投票阈值来平衡误报和漏报。

工作流:Orchestrator-worker

Workflow: Orchestrator-workers

在 orchestrator-workers 工作流中,中央 LLM 动态分解任务,将它们委托给 worker LLMs,并综合其结果。

image.png

何时使用此工作流程: 此工作流程非常适合于无法预测所需子任务的复杂任务(例如,在编码中,需要更改的文件数以及每个文件中更改的性质可能取决于任务)。虽然它在拓扑上相似,但与并行化的主要区别在于它的灵活性 — 子任务不是预定义的,而是由编排器根据特定输入确定的。

orchestrator-workers 使用场景示例:

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

工作流:Evaluator-optimizer

Workflow: Evaluator-optimizer

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

image.png

何时使用此工作流程: 当我们有明确的评估标准并且迭代优化提供可衡量的价值时,此工作流程特别有效。良好拟合的两个标志是,首先,当人类表达他们的反馈时,LLM 响应可以得到明显的改进;其次,LLM 可以提供此类反馈。这类似于人类作家在制作精美的文档时可能经历的迭代写作过程。

evaluator-optimizer 使用场景示例:

  • 文学翻译,其中有细微差别,译者 LLM) 最初可能无法捕捉到,但评估者 LLM 可以提供有用的批评。
  • 复杂的搜索任务,需要多轮搜索和分析以收集全面的信息,其中评估者决定是否需要进一步搜索。

Agents  代理

随着 LLMs 在关键功能方面日趋成熟,代理正在生产中出现 — 理解复杂的输入、参与推理和规划、可靠地使用工具以及从错误中恢复。代理从来自人类用户的命令或与人类用户的交互式讨论开始他们的工作。一旦任务明确,特工就会独立计划和操作,并可能返回给人类以获取更多信息或做出判断。在执行过程中,代理在每个步骤(例如工具调用结果或代码执行)从环境中获取“基本事实”以评估其进度至关重要。然后,代理可以在检查点或遇到障碍时暂停以获得人工反馈。任务通常在完成时终止,但包括停止条件(例如最大迭代次数)以保持控制也很常见。

代理可以处理复杂的任务,但其实现通常很简单。它们通常只是 LLMs,使用基于循环环境反馈的工具。因此,清晰而周到地设计工具集及其文档至关重要。我们在附录 2(“Prompt Engineering your Tools”)中扩展了工具开发的最佳实践。

image.png

何时使用代理: 代理可用于难以或不可能预测所需步骤数以及无法对固定路径进行硬编码的开放式问题。LLM 可能会运行多个回合,您必须对其决策有一定程度的信任。代理的自主性使其成为在可信环境中扩展任务的理想选择。

代理的自主性意味着更高的成本,并且可能会使错误复杂化。我们建议在沙盒环境中进行广泛测试,并使用适当的防护机制。

代理使用场景示例:

image.png

组合和自定义这些模式

这些构建块不是规范性的。它们是开发人员可以塑造和组合以适应不同用例的常见模式。与任何 LLM是衡量性能和迭代实现。重复一遍:只有当它明显改善结果时,您才应考虑增加复杂性。

总结

在 LLM 并不是构建最复杂的系统。这是关于根据您的需求构建正确的系统。从简单的提示开始,通过综合评估对其进行优化,并且仅在更简单的解决方案不足时才添加多步骤代理系统。

在实施代理时,我们尽量遵循三个核心原则:

  • 保持代理设计的简单性。
  • 通过明确显示代理的规划步骤来确定透明度的优先级。
  • 通过全面的工具文档和测试,精心设计您的代理计算机接口 (ACI)。

框架可以帮助您快速入门,但当您转向生产环境时,请不要犹豫,减少抽象层并使用基本组件进行构建。通过遵循这些原则,您可以创建不仅功能强大,而且可靠、可维护且受用户信任的代理。

附录 1:实践中的代理

Appendix 1: Agents in practice

我们与客户的合作揭示了 AI 代理的两个特别有前途的应用,它们证明了上述模式的实用价值。这两个应用程序都说明了代理如何为需要对话和行动的任务增加最大价值,具有明确的成功标准,启用反馈循环,并集成有意义的人工监督。

A. 客户支持

客户支持通过工具集成将熟悉的聊天机器人界面与增强功能相结合。这很自然地适合更多的开放式代理,因为:

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

几家公司已经通过基于使用量的定价模型证明了这种方法的可行性,这些模型仅对成功的解决方案收费,表明对其代理的有效性充满信心。

B. 编码代理

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

  • 代码解决方案可通过自动化测试进行验证;
  • 代理可以使用测试结果作为反馈来迭代解决方案;
  • 问题空间定义明确且结构化;
  • 输出质量可以客观地衡量。

在我们自己的实施中,代理现在可以仅根据拉取请求描述在 SWE-bench Verified 基准测试中解决实际的 GitHub 问题。然而,虽然自动化测试有助于验证功能,但人工审核对于确保解决方案符合更广泛的系统要求仍然至关重要。

附录 2:及时设计您的工具

Appendix 2: Prompt engineering your tools

无论您正在构建哪个代理系统,工具都可能是您代理的重要组成部分。工具使 Claude 能够通过在我们的 API 中指定它们的确切结构和定义来与外部服务和 API 进行交互。当 Claude 响应时,如果它计划调用工具,它将在 API 响应中包含一个工具使用块。工具定义和规范应与总体提示一样受到及时的工程关注。在这个简短的附录中,我们描述了如何对工具进行提示工程。

通常有几种方法可以指定相同的操作。例如,您可以通过编写 diff 或重写整个文件来指定文件编辑。对于结构化输出,您可以在 Markdown 或 JSON 中返回代码。在软件工程中,像这样的差异是表面的,可以无损地从一个差异转换为另一个差异。但是,某些格式对于 LLM写入。编写 diff 需要知道在编写新代码之前 chunk header 中有多少行发生了变化。在 JSON 中编写代码(与 markdown 相比)需要额外转义换行符和引号。

我们决定工具格式的建议如下:

  • 给模型足够的 token 来 “思考” ,然后再将自己写到角落里。
  • 使格式接近模型在 Internet 上的文本中自然出现的格式。
  • 确保没有格式设置的 “开销”,例如必须保持数千行代码的准确计数,或者对它编写的任何代码进行字符串转义。

一条经验法则是考虑在人机界面 (HCI) 上投入了多少精力,并计划投入同样多的精力来创建良好的代理-计算机界面 (ACI)。以下是有关如何执行此操作的一些想法:

  • 设身处地为模特着想。根据描述和参数,如何使用此工具是否很明显,还是需要仔细考虑?如果是这样,那么模型也可能如此。一个好的工具定义通常包括示例用法、边缘情况、输入格式要求以及与其他工具的明确界限。
  • 如何更改参数名称或描述以使其更明显?将此视为为团队中的初级开发人员编写出色的文档字符串。这在使用许多类似工具时尤其重要。
  • 测试模型如何使用您的工具:在我们的工作台中运行许多示例输入,以查看模型犯了哪些错误,然后迭代。
  • Poka-yoke 你的工具。更改参数,以便更难出错。

在为 SWE-bench 构建代理时,我们实际上花费了比整体提示更多的时间来优化我们的工具。例如,我们发现,在代理移出根目录后,模型会使用使用相对文件路径的工具出错。为了解决这个问题,我们将工具更改为始终需要绝对文件路径,我们发现该模型完美地使用了这种方法。