全文翻译自 Anthropic Engineering《Building Effective Agents》, 阅读原文。译文仅供学习交流。
打造高效的 AI 智能体
原文:Anthropic Engineering,原文链接:https://www.anthropic.com/engineering/building-effective-agents
发布于 2024 年 12 月 19 日
在过去一年里,我们与数十个不同行业的团队合作,帮助他们构建大语言模型(LLM)智能体。我们始终发现:最成功的落地实践并没有使用复杂的框架或专用库,而是采用简单、可组合的模式。
注:自 2024 年 12 月以来,本文描述的许多工具格局已经发生变化。如需了解我们目前的做法,请参阅 我们如何构建 Claude Managed Agents 以及 Managed Agents 文档。
在过去一年中,我们与数十个行业的团队合作构建大语言模型(LLM)智能体。最成功的应用始终不是用复杂的框架或专用库实现的,而是用简单、可组合的模式搭建出来的。
在这篇文章中,我们分享从客户合作与自身构建智能体过程中学到的经验,并为开发者提供构建高效智能体的实用建议。
什么是智能体?
“Agent”(智能体)有多种定义方式。一些客户把智能体定义为完全自主的系统:长时间独立运行,使用多种工具完成复杂任务。另一些客户则用这个词来描述更具规定性的实现:遵循预定义的工作流。在 Anthropic,我们把所有这些变体都归为 agentic systems(智能体系统),但在架构上对 workflows(工作流)与 agents(智能体)做了重要区分:
- Workflows(工作流):通过预定义的代码路径来编排 LLM 和工具的系统。
- Agents(智能体):由 LLM 动态指挥自身流程和工具使用的系统,由模型自己掌控如何完成任务。
下面我们将详细介绍这两类智能体系统。在附录 1(”Agents in Practice”,智能体的实践)中,我们介绍了客户发现这两类系统特别有价值的两个领域。
何时使用(以及何时不使用)智能体
在用 LLM 构建应用时,我们建议寻找尽可能简单的解决方案,只在必要时才增加复杂度。这可能意味着根本不需要构建智能体系统。智能体系统往往用延迟和成本换取更好的任务表现,你应当权衡这种取舍是否值得。
当确实需要更高复杂度时:对于定义明确的任务,工作流能提供可预测性和一致性;而当需要在大规模场景下保持灵活性、依赖模型自主决策时,智能体是更好的选择。但对许多应用来说,用检索和上下文示例优化单次 LLM 调用通常就足够了。
何时以及如何使用框架
有许多框架可以让智能体系统更容易实现,包括:
- Claude Agent SDK;
- AWS 的 Strands Agents SDK;
- Rivet,一个拖拽式的图形化 LLM 工作流构建器;以及
- Vellum,另一个用于构建和测试复杂工作流的图形化工具。
这些框架通过简化调用 LLM、定义和解析工具、串联调用等标准底层任务,让上手变得容易。但它们也常常引入额外的抽象层,遮蔽了底层的 prompt(提示词)和响应,使调试更困难。它们还容易诱使你增加复杂度,而其实更简单的配置就够了。
我们建议开发者先直接使用 LLM API:许多模式用几行代码就能实现。如果你确实要用框架,请确保理解其底层代码。对底层机制的错误假设是客户犯错的常见来源。
可参考我们的 cookbook 中的示例实现。
构建模块、工作流与智能体
在本节中,我们将探讨在生产环境中常见的智能体系统模式。我们从最基础的构建模块——增强型 LLM(augmented LLM)讲起,逐步增加复杂度,从简单的组合式工作流一直讲到自主智能体。
构建模块:增强型 LLM
智能体系统的基本构建模块,是经过 retrieval(检索)、tools(工具)和 memory(记忆)等能力增强的 LLM。我们当前的模型可以主动使用这些能力——自主生成搜索查询、选择合适的工具、决定保留哪些信息。
(图:The augmented LLM,增强型 LLM)
我们建议重点关注实现的两个方面:针对你的具体用例定制这些能力,并确保为 LLM 提供简单、文档完备的接口。虽然实现这些增强的方式有很多,但其中一种途径是我们最近发布的 Model Context Protocol,开发者只需一个简单的客户端实现,即可接入不断增长的第三方工具生态。
在本文剩余部分,我们假设每次 LLM 调用都能使用这些增强能力。
工作流:提示词链(Prompt chaining)
提示词链把任务分解为一系列步骤,每个 LLM 调用处理上一个调用的输出。你可以在任何中间步骤加入程序化的检查(如下图中的 “gate”),确保流程仍在正轨上。
(图:The prompt chaining workflow,提示词链工作流)
何时使用这个工作流:当任务可以被轻松、干净地分解为固定的子任务时,这个工作流非常理想。主要目标是以延迟换取更高的准确率,让每次 LLM 调用都变成更容易的任务。
提示词链适用的示例:
- 先生成营销文案,再把它翻译成另一种语言。
- 先写文档大纲,检查大纲是否符合某些标准,再根据大纲撰写正文。
工作流:路由(Routing)
路由对输入进行分类,并把它导向专门的后续任务。这个工作流实现了关注点分离,可以构建更专门的 prompt。没有这个工作流,针对某一类输入的优化可能会损害其他输入上的表现。
(图:The routing workflow,路由工作流)
何时使用这个工作流:当复杂任务中存在适合分开处理的明确类别,且分类可以被准确完成(无论用 LLM 还是传统分类模型/算法)时,路由效果很好。
路由适用的示例:
- 把不同类型的客服查询(一般问题、退款请求、技术支持)分流到不同的下游流程、prompt 和工具。
- 把简单/常见问题路由给更小、更经济的模型(如 Claude Haiku 4.5),把困难/罕见问题路由给更强的模型(如 Claude Sonnet 4.5),以实现性能最优。
工作流:并行化(Parallelization)
LLM 有时可以同时处理一项任务,再用程序把输出聚合起来。这种工作流即并行化,主要有两种关键变体:
- Sectioning(分段):把任务拆成可并行运行的独立子任务。
- Voting(投票):把同一任务运行多次,得到多样化的输出。
(图:The parallelization workflow,并行化工作流)
何时使用这个工作流:当拆分出的子任务可以并行以提速,或需要多种视角/多次尝试以提高结果置信度时,并行化很有效。对于涉及多方面考量的复杂任务,如果每个考量由单独的 LLM 调用来处理,让每次调用专注于一个具体方面,LLM 的表现通常更好。
并行化适用的示例:
- Sectioning(分段):
- 实现护栏(guardrails):一个模型实例处理用户查询,另一个实例筛查不当内容或请求。这通常比让同一个 LLM 调用同时处理护栏和核心回复效果更好。
- 自动化评估(evals):评估 LLM 表现时,每次 LLM 调用评估给定 prompt 下模型表现的一个不同方面。
- Voting(投票):
- 审查代码漏洞:用几个不同的 prompt 分别审查代码,发现问题即标记。
- 判断给定内容是否违规:用多个 prompt 从不同方面评估,或要求不同的投票阈值,以平衡误报和漏报。
工作流:编排器-工作器(Orchestrator-workers)
在 orchestrator-workers 工作流中,中央 LLM 动态拆解任务,分发给工作器 LLM,再综合它们的结果。
(图:The orchestrator-workers workflow,编排器-工作器工作流)
何时使用这个工作流:适用于无法预知所需子任务的复杂任务(例如写代码时,需要修改多少文件、每个文件怎么改,都取决于具体任务)。虽然拓扑结构上与并行化相似,但关键区别在于灵活性——子任务不是预定义的,而是由编排器根据具体输入决定的。
编排器-工作器适用的示例:
- 每次都要对多个文件做复杂修改的编程产品。
- 需要从多个来源收集、分析信息以寻找可能相关内容的搜索任务。
工作流:评估器-优化器(Evaluator-optimizer)
在 evaluator-optimizer 工作流中,一次 LLM 调用生成回复,另一次调用在循环中提供评估和反馈。
(图:The evaluator-optimizer workflow,评估器-优化器工作流)
何时使用这个工作流:当评估标准明确、迭代优化能带来可衡量的价值时,这个工作流特别有效。是否合适的两个信号是:第一,当人能清楚表达反馈时,LLM 的回复确实可以明显改进;第二,LLM 自己能给出这样的反馈。这类似于人类作者打磨文档时的反复写作过程。
评估器-优化器适用的示例:
- 文学翻译:翻译 LLM 初稿可能抓不住某些细微差别,但评估器 LLM 能给出有用的批评意见。
- 需要多轮搜索与分析才能收集全面信息的复杂搜索任务,由评估器决定是否需要继续搜索。
智能体(Agents)
随着 LLM 在关键能力上的成熟——理解复杂输入、推理与规划、可靠地使用工具、从错误中恢复——智能体开始出现在生产环境中。智能体的工作始于人类用户的指令或与用户的交互式讨论。任务明确后,智能体独立规划并执行,必要时再向人类索取更多信息或请人类判断。在执行过程中,智能体在每一步都从环境中获取” ground truth”(真实反馈,如工具调用结果或代码执行结果)来评估进展。智能体可以在检查点或遇到障碍时停下来等待人类反馈。任务通常在完成时终止,但也常设置停止条件(如最大迭代次数)以保持控制。
智能体能处理复杂的任务,但实现往往很直接:通常就是 LLM 基于环境反馈循环使用工具。因此,清晰、周到地设计工具集及其文档至关重要。我们在附录 2(”Prompt Engineering your Tools”,为你的工具做提示词工程)中进一步阐述了工具开发的最佳实践。
(图:Autonomous agent,自主智能体)
何时使用智能体:智能体适用于开放性问题:难以或无法预测所需步骤数,也无法把路径硬编码。这时 LLM 可能运行很多轮,你必须对其决策能力有一定信任。智能体的自主性使其非常适合在可信环境中规模化执行任务。
智能体的自主性意味着更高的成本,以及误差累积的风险。我们建议在沙箱环境中充分测试,并设置适当的护栏。
智能体适用的示例:
以下示例来自我们自己的实现:
- 用于解决 SWE-bench 任务的编程智能体:根据任务描述对多个文件做修改;
- 我们的“computer use” 参考实现,Claude 操作计算机完成任务。
(图:High-level flow of a coding agent,编程智能体的高层流程)
组合与定制这些模式
这些构建模块不是硬性规定,而是开发者可以根据不同用例塑形、组合的常见模式。成功的关键与任何 LLM 功能一样:衡量性能并迭代实现。重申一遍:只有在能明确改善效果时,才考虑增加复杂度。
总结
在 LLM 领域取得成功,不在于构建最复杂的系统,而在于为你的需求构建_合适的_系统。从简单的 prompt 开始,用全面的评估优化它们,只在简单方案不够用时才引入多步智能体系统。
在实现智能体时,我们尽量遵循三个核心原则:
- 保持智能体设计的简洁(simplicity)。
- 把智能体的规划步骤显式展示出来,优先保证透明(transparency)。
- 通过完备的工具文档与测试,精心打磨智能体-计算机接口(ACI,agent-computer interface)。
框架能帮你快速起步,但在走向生产的过程中,不要犹豫,减少抽象层级,用基础组件构建。遵循这些原则,你就能打造出不仅强大,而且可靠、可维护、值得用户信赖的智能体。
致谢
本文由 Erik S. 和 Barry Zhang 撰写。这项工作源于我们在 Anthropic 构建智能体的经验,以及客户分享的宝贵见解,对此我们深表感谢。
附录 1:智能体的实践(Agents in practice)
我们与客户的合作揭示了 AI 智能体两个特别有前景的应用,展示了上述模式的实际价值。这两个应用都表明:当任务同时需要对话与行动、有明确的成功标准、能形成反馈闭环、并融入有意义的人类监督时,智能体最能创造价值。
A. 客服(Customer support)
客服把熟悉的聊天机器人界面与工具集成带来的增强能力结合起来。这天然适合更开放的智能体,因为:
- 服务交互天然遵循对话流程,同时需要访问外部信息和执行操作;
- 可以集成工具来拉取客户数据、订单历史和知识库文章;
- 发起退款、更新工单等操作可以用程序化方式处理;以及
- 成功可以用用户定义的解决率清楚衡量。
已有若干公司通过按成功解决量计费的用量定价模式验证了这一做法的可行性,显示出对自家智能体效果的信心。
B. 编程智能体(Coding agents)
软件开发领域展现了 LLM 功能的巨大潜力,能力从代码补全演进到自主解决问题。智能体特别有效,因为:
- 代码方案可以通过自动化测试验证;
- 智能体可以利用测试结果作为反馈迭代方案;
- 问题空间定义明确、结构清晰;以及
- 输出质量可以客观衡量。
在我们自己的实现中,智能体仅根据 pull request 描述,就能解决 SWE-bench Verified 基准中的真实 GitHub issue。不过,虽然自动化测试有助于验证功能,人类评审对于确保方案符合更广泛的系统需求仍然至关重要。
附录 2:为你的工具做提示词工程(Prompt engineering your tools)
无论构建哪种智能体系统,工具(tools)都可能是智能体的重要组成部分。工具让 Claude 通过在 API 中声明确切的结构与定义,与外部服务和 API 交互。当 Claude 回复中包含 tool use block(工具使用块)时,即表示它计划调用工具。工具的定义与说明值得像整体 prompt 一样投入提示词工程。在这个简短的附录中,我们介绍如何为工具做提示词工程。
同一个操作常常有多种声明方式。例如,文件编辑可以用写 diff 的方式指定,也可以用重写整个文件的方式指定;结构化输出可以把代码放在 markdown 里返回,也可以放在 JSON 里返回。在软件工程中,这类差异只是表面的,可以无损地相互转换。但对 LLM 来说,有些格式比另一些难写得多。写 diff 要求在写出新代码之前,先在 chunk header 里写清改了多少行;把代码写在 JSON 里(相比 markdown)需要额外转义换行和引号。
我们对选择工具格式的建议如下:
- 给模型留足 token 先”思考”,别让它还没想清楚就落笔、把自己逼进死角。
- 让格式尽量接近模型在互联网文本中自然见过的样子。
- 确保没有格式上的”额外负担”,比如要精确数出几千行代码的行数,或对写出的代码做字符串转义。
一个经验法则是:想想人们在人机界面(HCI)上花了多少功夫,就准备在好的_智能体_-计算机界面(ACI)上花同样多的功夫。以下是一些具体想法:
- 设身处地站在模型的角度。这个工具的描述和参数是否让人一看就懂怎么用,还是需要仔细琢磨?如果是后者,对模型来说很可能也一样。好的工具定义通常包括使用示例、边界情况、输入格式要求,以及与其他工具的清晰边界。
- 怎样改参数名或描述能让用法更一目了然?就像为团队里一位初级开发者写出色的 docstring 一样。这在使用多个相似工具时尤为重要。
- 测试模型如何使用你的工具:在 workbench 中跑大量示例输入,看看模型会犯什么错,再迭代。
- 用 Poka-yoke(防错)思想设计工具。调整参数,让犯错更难发生。
在构建 SWE-bench 智能体时,我们花在优化工具上的时间实际上比花在整体 prompt 上的还多。例如,我们发现智能体离开根目录后,模型在使用相对文件路径的工具时容易出错。为此,我们把工具改成一律要求绝对文件路径——模型用这种方式几乎零失误。
获取开发者通讯
产品更新、实战教程、社区亮点等,每月投递到你的收件箱。
如果你想收到我们的月度开发者通讯,请留下邮箱地址。你可以随时退订。