构建高效的 AI 智能体【译】

Friday, April 4, 2025 - AI Agent - LLM AI Agent Design Patterns

在过去的一年里,我们与各行各业的数十个团队紧密合作,共同构建基于大型语言模型(LLM)的智能体。我们的实践经验表明,最成功的项目往往采用简单且可组合的设计模式,而非依赖于复杂的专用框架或库。

本文旨在分享我们与客户合作以及自身构建智能体的经验,为开发者提供构建高效能智能体的实用建议。

什么是智能体(Agent)?

“智能体”这个概念存在多种定义。有些客户将其视为能够长时间自主运行、利用多种工具完成复杂任务的全自主系统。另一些客户则用这个词来描述遵循预定义流程、更具规范性的系统实现。在 Anthropic,我们将这些不同形态的系统统称为 智能体系统 (Agentic Systems) 。然而,在架构层面,我们对 工作流 (Workflow)智能体 (Agent) 做了关键区分:

- 工作流 (Workflow) :指 LLM 和工具通过预定义的逻辑路径进行编排的系统。
- 智能体 (Agent) :指 LLM 能够动态地引导自身执行流程、决定工具使用,并自主掌控任务完成方式的系统。

下文将详细探讨这两种智能体系统。在 附录 1:“智能体的实践应用” 中,我们介绍了客户认为这些系统特别有价值的两个应用领域。

何时(以及何时不)使用智能体

在使用 LLM 构建应用时,我们始终建议从最简单的解决方案入手,仅在确实必要时才增加系统的复杂性。有时,这意味着你可能根本不需要构建一个智能体系统。智能体系统通常以牺牲一定的延迟和成本为代价,来换取在复杂任务上更好的性能表现。因此,你需要仔细评估这种权衡是否值得。

如果确实需要更高的复杂性:
* 对于目标明确、步骤清晰的任务,工作流能提供更好的可预测性和一致性。
* 当任务需要灵活性和模型驱动的动态决策时,智能体是更优的选择。

不过请注意,对于许多应用场景而言,通过检索增强和提供上下文示例来优化单次 LLM 调用,往往就已经足够了。

何时以及如何使用框架

市面上有许多框架可以简化智能体系统的实现,例如:

- LangChain 的 LangGraph
- 亚马逊 Bedrock 的 AI Agent 框架
- Rivet,一个拖拽式的图形化 LLM 工作流构建器
- Vellum,另一个用于构建和测试复杂工作流的图形化工具

这些框架通过简化诸如调用 LLM、定义和解析工具、链接调用等标准的基础任务,降低了上手的门槛。然而,它们也常常引入额外的抽象层,可能会隐藏底层的提示(Prompt)和响应细节,从而增加调试的难度。有时,这些框架甚至会诱导开发者过度设计,在简单的场景下引入不必要的复杂性。

我们的建议是: 优先尝试直接使用 LLM API 进行开发。许多常见的模式仅需几行代码即可实现。如果你确实选择使用框架,请务必深入理解其底层实现。我们发现,对框架底层机制的错误假设是导致客户项目出错的一个常见原因。

可以参考我们的 Cookbook 获取一些模式的示例实现。

构建模块、工作流与智能体

在本节中,我们将探讨在生产环境中观察到的常见的智能体系统模式。我们将从最基本的构建模块(增强型 LLM)开始,逐步增加复杂性,从简单的组合式工作流,一直到自主型智能体。

构建模块:增强型 LLM (Augmented LLM)

智能体系统的基础是 增强型 LLM。这里的“增强”指的是为 LLM 配备了如检索 (Retrieval)工具调用 (Tool Use)记忆 (Memory) 等附加能力。我们的模型(如 Claude)能够主动运用这些能力,例如自行生成搜索查询、选择合适的工具,以及决定需要保留哪些信息以备后续使用。

!增强型 LLM 示意图

在实现增强型 LLM 时,我们建议重点关注两个方面:
1. 根据你的具体用例定制化这些增强功能。
2. 确保为 LLM 提供一个简单且文档清晰的接口 (Interface) 来使用这些功能。

实现这些增强有多种方式。其中一种是利用我们最近发布的 模型上下文协议 (Model Context Protocol, MCP) (https://www.anthropic.com/news/model-context-protocol)。该协议允许开发者通过简单的 客户端实现 与不断增长的第三方工具生态系统集成。

在本文后续部分,我们将默认假定每次 LLM 调用都具备这些增强能力。

工作流:提示链 (Prompt Chaining)

提示链将一个任务分解为多个顺序执行的步骤,其中每个 LLM 调用处理上一步的输出。你可以在任意中间步骤加入程序化的检查点(见下图中的 "Gate"),以确保整个流程按预期进行。

!提示链工作流示意图

何时使用: 适用于那些可以被清晰地分解为固定子任务序列的任务。其主要目的是通过将复杂任务拆解成一系列更简单的 LLM 调用,牺牲一定的延迟来换取更高的准确性。

适用示例:
- 先生成营销文案,然后将其翻译成另一种语言。
- 先编写文档大纲,然后检查大纲是否满足特定标准,最后根据合格的大纲生成完整文档。

工作流:路由 (Routing)

路由工作流首先对输入进行分类,然后将其分发给专门处理该类输入的下游任务或提示。这种模式有助于实现关注点分离,并允许你为不同类型的输入构建更具针对性的优化提示。若不进行路由,为优化某一类输入而做的调整,有时可能会损害模型在处理其他类型输入时的性能。

!路由工作流示意图

何时使用: 适用于处理一个复杂任务,该任务包含几种截然不同的子类别,且这些类别最好被分开处理。同时,分类步骤本身可以通过 LLM 或传统的分类模型/算法准确完成。

适用示例:
- 将不同类型的客户服务请求(如一般咨询、退款申请、技术支持)路由到不同的处理流程、提示和工具集。
- 将简单或常见问题路由给速度更快、成本更低的模型(如 Claude 3.5 Haiku),而将复杂或罕见问题路由给能力更强的模型(如 Claude 3.5 Sonnet),以此来优化成本和响应速度。

工作流:并行化 (Parallelization)

有时,LLM 可以同时处理一个任务的多个方面或多个子任务,然后通过编程方式将它们的输出聚合起来。这种并行化工作流主要有两种变体:

- 分治 (Map / Scatter): 将一个大任务分解为多个可以并行处理的独立子任务。
- 投票/多路召回 (Reduce / Gather / Voting): 让多个 LLM 实例(或使用不同提示的同一个 LLM)并行处理同一个任务,然后综合多个输出来得到最终结果。

!并行化工作流示意图

何时使用: 当任务可以分解为能够并行执行的子任务以提高处理速度时,或者当需要从多个角度审视问题、或进行多次尝试以获得更可靠、置信度更高的结果时,并行化非常有效。对于涉及多个考量因素的复杂任务,让不同的 LLM 调用专注于处理特定的方面,通常比让单个 LLM 调用同时兼顾所有方面效果更好。

适用示例:
- 分治 (Map / Scatter):
- 实施安全护栏 (Guardrails):一个模型实例处理用户查询,同时另一个实例并行地检查查询内容是否包含不当信息或请求。这通常比让同一个 LLM 调用既要响应又要执行安全检查效果更好。
- 自动化评估 LLM 性能:让多个 LLM 调用分别从不同维度评估模型在某个特定提示下的表现。
- 投票/多路召回 (Reduce / Gather / Voting):
- 审查一段代码是否存在安全漏洞:使用几个不同的提示让模型分别审查代码,并在发现问题时进行标记,最后综合判断。
- 评估一段内容是否不当:使用多个不同的提示,从不同角度或依据不同严格程度的标准进行评估,通过投票机制来平衡误报(False Positives)和漏报(False Negatives)。

工作流:协调器-工作者 (Coordinator-Worker)

协调器-工作者工作流中,一个中心的“协调器”LLM 负责动态地将任务分解,并将子任务分配给多个“工作者”LLM 执行,最后综合它们返回的结果。

!协调器-工作者工作流示意图

何时使用: 适用于那些无法预先确定所有必需子任务的复杂问题(例如,在编码任务中,需要修改哪些文件以及每个文件具体如何修改,可能取决于任务的细节和初始分析结果)。虽然它在结构上与并行化有些相似,但关键区别在于其灵活性:子任务不是预先定义好的,而是由协调器根据具体输入动态决定的。

适用示例:
- 涉及一次性修改多个文件的复杂编码任务。
- 需要从多个信息源搜集和分析数据,以探索可能关联信息的探索性研究任务。

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

评估器-优化器工作流中,一个 LLM 调用负责生成初步响应(“优化器”),而另一个 LLM 调用则负责对该响应进行评估并提供反馈(“评估器”),这个过程可以在一个循环中进行迭代。

!评估器-优化器工作流示意图

何时使用: 当存在明确的评估标准,并且迭代改进能够带来可衡量的价值提升时,此工作流特别有效。以下两个迹象表明该模式可能适用:1) 人类在给出反馈后,LLM 的响应质量能得到显著改善;2) LLM 本身有能力生成类似的有价值的反馈。这类似于人类作者在创作和打磨一篇高质量文档时所经历的反复修改过程。

适用示例:
- 文学翻译:初次翻译的 LLM 可能无法捕捉所有细微的语境和风格,但评估器 LLM 可以提供有建设性的评论来指导改进。
- 复杂的文献综述或研究任务:需要多轮搜索和分析才能收集全面的信息,其中评估器负责判断当前信息是否足够,以及是否需要进行进一步的搜索和分析。

智能体 (Agent)

随着 LLM 在理解复杂指令、进行推理规划、可靠使用工具以及从错误中恢复等关键能力上的日趋成熟,真正的智能体 (Agent) 正逐渐在生产环境中崭露头角。智能体通常从接收人类用户的指令或通过交互式对话开始工作。一旦任务目标明确,智能体便开始自主地规划和执行,期间可能会在需要澄清或判断时再次与人类交互。在执行的每一步,智能体都必须从环境中获取真实的反馈 (ground truth) (例如,工具调用的实际结果或代码执行的输出),以此来评估进展。智能体可以在预设的检查点暂停,或在遇到困难时寻求人工介入和反馈。任务通常在完成后终止,但也常常会设定停止条件(如最大迭代次数)以确保可控性。

智能体能够处理高度复杂的任务,但其核心实现通常可以非常简单:很多时候,它本质上就是一个在循环中运行的 LLM,根据环境反馈来决定下一步行动(通常是调用工具)。因此,清晰、周到地设计工具集及其说明文档至关重要。关于工具开发的最佳实践,我们在 附录 2:“工具的提示工程” 中有更详细的阐述。

!自主智能体示意图

何时使用: 智能体适用于解决开放式问题 (open-ended problems) ,即那些难以甚至无法预知所需确切步骤,也无法预先编码固定执行路径的任务。在这种模式下,LLM 可能会执行多轮思考和行动,因此你必须对其决策能力有一定程度的信任。智能体的自主性使其非常适合在受信任的环境中用于扩展自动化任务的边界。

需要注意: 智能体的自主性也意味着更高的运行成本,以及潜在的复合错误 (compounding errors) 风险。我们强烈建议在沙盒环境中进行充分的测试,并部署适当的安全护栏措施。

适用示例:
以下是我们内部实现的一些例子:
- 一个用于解决 SWE-bench 任务 的编码智能体,该任务要求智能体根据任务描述修改多个代码文件。
- 我们的 “使用计算机”参考实现,其中 Claude 通过模拟使用计算机来完成用户指定的任务。

!编码智能体流程示意图

组合与定制这些模式

以上介绍的并非是僵化的规定,而是开发者可以根据不同用例进行调整和组合的常见模式。与任何 LLM 应用开发一样,成功的关键在于衡量性能表现并持续迭代你的实现方式。再次强调:只有当增加复杂性确实能带来显著的成果提升时,才应该考虑这样做。

总结

在 LLM 应用开发领域,成功的关键并非构建最复杂的系统,而是构建最适合你需求的系统。从简单的提示开始,通过全面的评估来优化它。只有当更简单的方案无法满足要求时,才考虑引入多步骤的工作流或智能体系统。

在实施智能体时,我们努力遵循三个核心原则:

1. 保持简单 (Simplicity): 在智能体的设计中力求简洁。
2. 追求透明 (Transparency): 通过清晰地展示智能体的思考过程和计划步骤来优先考虑可理解性。
3. 精心设计接口 (Interface Design): 通过全面的工具文档和充分的测试,仔细打磨你的人工智能体-计算机接口 (Agent-Computer Interface, ACI)。

框架可以帮助你快速起步,但当你准备将应用投入生产时,不要害怕剥离不必要的抽象层,回归基础组件进行构建。遵循这些原则,你将能创建出不仅功能强大,而且可靠、易于维护并能赢得用户信任的智能体。

附录 1:智能体的实践应用

通过与客户的合作,我们观察到 AI 智能体在以下两个领域展现出尤为广阔的应用前景,这些领域也印证了上文所述模式的实际价值。这两个应用场景都说明了智能体在何种任务中能发挥最大价值:那些需要对话与行动相结合、具有明确的成功标准、能够利用反馈循环,并且能整合有效的人工监督的任务。

A. 客户支持

客户支持场景天然地将人们熟悉的聊天机器人界面与通过工具集成实现的增强功能结合起来。这非常适合采用更开放的智能体方法,因为:

- 支持互动本身就是一种对话流程,同时又需要访问外部信息和执行操作。
- 可以集成工具来查询客户数据、订单历史、知识库文章等。
- 可以程序化地执行诸如发起退款、更新工单状态等操作。
- 任务的成功与否可以通过用户问题是否得到解决来清晰衡量。

一些公司已经开始尝试基于使用效果的定价模型(例如,仅对成功解决问题的交互收费),这体现了他们对其智能体有效性的信心。

B. 编码智能体

软件开发是 LLM 能力展现出巨大潜力的另一个领域,其应用已从简单的代码补全发展到能够自主解决问题的智能体。智能体在此领域特别有效,因为:

- 代码解决方案的正确性可以通过自动化测试进行验证。
- 智能体可以利用测试结果作为反馈,迭代改进其解决方案。
- 问题空间(代码库、依赖关系等)通常是定义良好且结构化的。
- 输出(代码)的质量可以在一定程度上进行客观衡量。

在我们自己的实现中,基于 SWE-bench Verified 基准测试,我们的智能体现在已经能够仅根据 Pull Request 的描述来解决真实的 GitHub Issues。然而,需要注意的是,尽管自动化测试有助于验证代码的功能性,但人工审查对于确保解决方案符合更广泛的系统设计要求和代码规范仍然至关重要。

附录 2:工具的提示工程 (Prompt Engineering Your Tools)

无论你构建的是哪种类型的智能体系统,工具 (Tools) 都是其不可或缺的关键组成部分。工具(在 Anthropic API 中通过 Tool Use 功能 实现)允许 Claude 与外部服务和 API 进行交互。你需要在 API 请求中指定工具的结构和描述。当 Claude 决定调用某个工具时,它会在 API 响应中返回一个 工具使用内容块 (tool_use content block)

关键在于:工具的定义和说明文档,应该像你为 LLM 设计的整体提示一样,得到同等程度的“提示工程”般的关注。 在这个简短的附录中,我们将探讨如何对你的工具进行“提示工程”。

通常,实现同一个操作有多种方式来定义工具。例如,对于文件编辑,你可以设计一个工具来接受差异 (diff) 格式的变更,也可以设计成接受重写后的整个文件内容。对于结构化输出,你可以让模型在 Markdown 代码块中返回代码,也可以要求返回 JSON 格式。在传统软件工程中,这些差异可能只是表面形式的不同,并且可以无损地相互转换。然而,对于 LLM 来说,某些格式比其他格式更难准确生成。例如,生成 diff 格式需要模型在生成新代码之前,准确计算出代码块头部需要标记的原始行号和变更行数。与直接在 Markdown 中编写代码相比,在 JSON 字符串中嵌入代码需要处理额外的换行符和引号转义。

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

- 给模型留出“思考”的空间: 在最终确定输出前,如果需要复杂推理,确保模型有足够的 Token 预算来进行内部思考(可以在 标签内)。
- 贴近自然语言和常见格式: 使工具的输入输出格式尽可能接近模型在其训练数据(如互联网文本)中常见的格式。
- 避免不必要的“格式开销”: 确保工具的使用方式不需要模型承担额外的认知负担,例如精确计算数千行代码的行号,或者对它生成的任何代码进行复杂的字符串转义。

一个实用的经验法则是:思考一下我们在人机交互界面 (Human-Computer Interface, HCI) 设计上投入了多少精力,然后计划投入同等级别的精力来打磨你的人工智能体-计算机接口 (Agent-Computer Interface, ACI)。 以下是一些具体建议:

- 换位思考: 站在模型的角度审视你的工具定义。仅仅基于工具描述和参数列表,是否能清晰无误地理解如何使用这个工具?还是需要仔细琢磨?如果人类都需要思考,那么模型很可能也会感到困惑。一个好的工具定义通常包含清晰的用途说明、参数解释、用法示例、对边缘情况的处理说明、输入格式要求,以及与其他工具功能的明确界限
- 优化命名和描述: 思考如何修改参数名称或工具描述,使其更加清晰易懂?把它想象成在为团队里的初级开发者编写优秀的文档字符串 (docstring)。当你的系统包含许多功能相似的工具时,这一点尤其重要。
- 测试工具使用情况: 在我们的 Workbench 或类似环境中,针对多种不同的输入场景,测试模型实际是如何调用你的工具的。观察模型容易犯哪些错误,并据此迭代优化工具定义。
- 进行“防错设计” (Poka-yoke / Error-Proofing): 调整工具的参数设计或输入验证逻辑,使其更难被误用。

在为 SWE-bench 构建编码智能体时,我们花在优化工具设计上的时间,实际上比优化整体系统提示的时间还要多。例如,我们最初发现,当智能体需要操作非根目录的文件时,模型在使用相对路径调用文件操作工具时常常出错。为了解决这个问题,我们将工具修改为始终要求使用绝对文件路径——我们发现模型能够完美地遵循这一更严格的要求。

---

由 Anthropic 工程团队撰写,原文链接:https://www.anthropic.com/engineering/building-effective-agents