原文地址 :www.anthropic.com/research/bu...
原作者 :Erik Schluntz & Barry Zhang (Anthropic)
发布日期 :2024 年 12 月 19 日
译注:本文完整还原了 Anthropic 原文的所有章节、案例以及 7 张核心架构图(使用 Mermaid 重新绘制),并保留了关键英文术语对照。
导言
在过去的一年里,我们与各行各业的数十个团队合作,共同构建大语言模型(LLM)智能体(Agents)。我们一致发现:最成功的落地实现并没有使用复杂的框架或专门的库,而是采用简单、可组合的设计模式(simple, composable patterns)构建的。
在本文中,我们将分享从客户合作以及自身构建智能体过程中沉淀下来的经验,并为开发者提供构建高效智能体的实用建议。
什么是智能体(What are agents?)
"智能体(Agent)"可以有多种定义。一些客户将其定义为能够长时间独立运行、使用各种工具完成复杂任务的完全自主系统;另一些人则用这个词来描述遵循预定义工作流、更具规范性的实现。在 Anthropic,我们将所有这些变体统称为智能体系统(Agentic systems) ,但在架构上对 工作流(Workflows) 和 智能体(Agents) 做了关键区分:
- 工作流(Workflows):通过**预定义的代码路径(predefined code paths)**来编排 LLM 和工具的系统。
- 智能体(Agents) :由 LLM 动态地自主指导其自身流程和工具使用,并自主控制如何完成任务的系统。
下文将详细探讨这两类智能体系统。在附录 1(智能体实战)中,我们还将展示客户在使用这类系统时发现极高价值的两个具体领域。
何时(以及何时不该)使用智能体
在使用 LLM 构建应用时,我们建议尽可能寻找最简单的解决方案 ,仅在确有必要时才增加复杂度。这往往意味着根本不需要构建智能体系统。智能体系统通常会以延迟(latency)和成本(cost)为代价,来换取更好的任务表现,你应当仔细权衡这种交换是否值得。
当更高的复杂度确实合理时:
- 工作流(Workflows) 为定义明确的任务提供了可预测性和一致性;
- 智能体(Agents) 则是在需要大规模灵活性和模型驱动决策时的更好选择。
然而,对于许多应用而言,通过检索(Retrieval)和上下文示例(In-context examples)来优化单次 LLM 调用通常就已经足够了。
何时以及如何使用框架(When and how to use frameworks)
市面上有许多框架可以让智能体系统的实现变得更容易,包括:
- LangChain 的 LangGraph;
- Amazon Bedrock 的 AI Agent framework;
- Rivet(拖拽式 GUI LLM 工作流构建器);
- Vellum(另一个用于构建和测试复杂工作流的 GUI 工具)。
这些框架通过简化底层任务(如调用 LLM、定义和解析工具、以及将调用串联在一起)帮助开发者快速上手。然而,它们往往会制造额外的抽象层,从而掩盖底层的提示词(Prompts)和响应结果,使调试变得更加困难。此外,当一个更简单的原生设置就能满足需求时,它们很容易诱使开发者增加不必要的复杂性。
我们建议开发者直接从使用 LLM API 开始:许多模式只需几行代码即可实现。如果你确实决定使用框架,请务必确保你真正理解其底层的代码逻辑。对框架"黑盒"之下的内部原理做出错误假设,是客户最常见的报错根源之一。
(相关的代码示例可参考 Anthropic 官方的 Cookbook 实现。)
基础构建块、工作流与智能体(Building blocks, workflows, and agents)
在本节中,我们将深入探讨我们在生产环境中见过的智能体系统的常见模式。我们将从最基础的构建块------增强型 LLM(Augmented LLM) 开始,逐步增加复杂度,从简单的组合式工作流一直讲到自主智能体。
基础构建块:增强型 LLM(Building block: The augmented LLM)
智能体系统的基本构建块是一个通过**检索(Retrieval)、工具(Tools)和记忆(Memory)**进行增强的 LLM。我们目前的模型能够主动使用这些能力------自主生成搜索查询、选择适当的工具,并决定保留哪些信息。
我们建议重点关注实现的两个核心方面:
- 根据你的具体使用场景定制这些能力;
- 确保它们为你的 LLM 提供一个易于使用、文档清晰的接口。
虽然实现这些增强功能的方法有很多,但其中一种方式是通过我们近期发布的 模型上下文协议(Model Context Protocol, MCP),它允许开发者通过简单的客户端实现与不断壮大的第三方工具生态进行集成。
在本文后续内容中,我们将假设每次 LLM 调用都具备这些增强能力。
工作流 1:提示词链(Workflow: Prompt chaining)
提示词链(Prompt chaining) 将一个任务分解为一系列步骤,其中每次 LLM 调用都会处理前一次调用的输出。你可以在任何中间步骤添加编程式的检查(参见下图中的"Gate / 门控"),以确保流程仍在正轨上。
- 何时使用此工作流 :当一个任务可以很容易且清晰地分解为固定的子任务时,此工作流最为理想。其主要目标是通过让每次 LLM 调用处理更简单的任务,来以延迟换取更高的准确率(trade off latency for higher accuracy)。
- 提示词链的典型适用场景 :
- 先生成营销文案,然后将其翻译成不同的语言。
- 先编写文档大纲,检查大纲是否符合特定标准,然后根据大纲撰写完整文档。
工作流 2:路由分发(Workflow: Routing)
路由(Routing) 对输入进行分类,并将其引导至专门的后续任务。这种工作流实现了关注点分离(separation of concerns),并允许构建更具针对性的提示词。如果不使用路由,针对某一种输入进行优化往往会导致其他输入类型的性能下降。
- 何时使用此工作流:路由非常适用于存在明显不同类别、分开处理效果更好,且可以通过 LLM 或传统分类模型/算法准确完成分类的复杂任务。
- 路由的典型适用场景 :
- 将不同类型的客服查询(一般问题、退款请求、技术支持)引导至不同的下游流程、提示词和工具。
- 将简单/常见的问题路由给像 Claude 3.5 Haiku 这样的小模型,而将困难/罕见的问题路由给像 Claude 3.5 Sonnet 这样更强大的模型,以优化成本和速度。
工作流 3:并行化(Workflow: Parallelization)
LLM 有时可以同时处理一个任务,并通过编程方式聚合它们的输出。这种工作流(并行化 Parallelization)主要表现为两种关键变体:
- 分段(Sectioning):将任务拆分为彼此独立的子任务并同时运行。
- 投票(Voting):多次运行相同的任务以获得多样化的输出。
- 何时使用此工作流:当拆分后的子任务可以并行化以提高速度(分段),或者当需要多个视角或多次尝试来获得更高置信度的结果(投票)时,并行化非常有效。对于具有多重考量的复杂任务,如果每个考量都由单独的 LLM 调用来处理,使每个调用能够集中注意力,LLM 的表现通常会更好。
- 并行化的典型适用场景 :
- 分段(Sectioning) :
- 实现护栏(Guardrails):一个模型实例处理用户查询,而另一个模型实例同时筛查该查询是否包含不当内容或安全违规。这通常比让同一个 LLM 调用同时处理护栏和核心响应表现得更好。
- 自动化评估 LLM 性能:每个 LLM 调用在同一提示词上评估模型表现的不同维度。
- 投票(Voting) :
- 审查代码漏洞:由多个不同的提示词同时审查同一段代码,只要发现问题就进行标记。
- 评估内容是否违规:使用多个提示词评估不同维度,或设置不同的投票阈值,以平衡误报(false positives)和漏报(false negatives)。
- 分段(Sectioning) :
工作流 4:编排器-执行器(Workflow: Orchestrator-workers)
在 编排器-执行器(Orchestrator-workers) 工作流中,一个中央 LLM(编排器)动态地拆解任务,将其委派给多个 Worker LLM,并综合它们的结果。
- 何时使用此工作流 :此工作流非常适合无法提前预测所需子任务 的复杂任务(例如在编程中,需要修改的文件数量以及每个文件中的修改性质,完全取决于具体任务)。虽然它在拓扑结构上与"并行化"相似,但关键区别在于其灵活性------子任务不是预先定义好的,而是由编排器根据具体输入动态决定的。
- 编排器-执行器的典型适用场景 :
- 每次都需要对多个文件进行复杂修改的编程产品。
- 涉及从多个信息源收集并分析可能相关的信息的搜索任务。
工作流 5:评估器-优化器(Workflow: Evaluator-optimizer)
在 评估器-优化器(Evaluator-optimizer) 工作流中,一个 LLM 调用生成响应,而另一个 LLM 调用在循环中提供评估和反馈。
- 何时使用此工作流 :当我们有明确的评估标准,且迭代改进能带来可衡量的价值时,此工作流特别有效。适合该工作流的两个标志是:首先,当人类清楚地表达出反馈意见时,LLM 的响应能够得到明显改善;其次,LLM 本身也能够提供此类反馈。这就像人类作者在撰写一篇精良文档时所经历的反复修改过程。
- 评估器-优化器的典型适用场景 :
- 文学翻译:译者 LLM 起初可能无法捕捉到所有细微差别,但评估者 LLM 可以提供有用的润色批评。
- 复杂的搜索任务:需要多轮搜索和分析以收集全面信息,由评估器决定是否需要进一步搜索。
智能体(Agents)
随着 LLM 在关键能力上的成熟------理解复杂输入、进行推理和规划、可靠地使用工具以及从错误中恢复------智能体正在生产环境中涌现。
智能体从人类用户的指令或交互式讨论开始工作。一旦任务明确,智能体就会独立规划并运作,期间也可能会返回向人类寻求更多信息或判断。在执行过程中,智能体需要在每个步骤从环境中获取"真实基准反馈"(ground truth)(例如工具调用结果或代码执行结果)以评估其进展。随后,智能体可以在检查点或遇到阻碍时暂停以等待人工反馈。任务通常在完成时终止,但通常也会包含停止条件(例如最大迭代次数)以保持控制权。
智能体可以处理复杂的任务,但它们的实现通常很简单------它们通常只是基于环境反馈在循环中使用工具的 LLM。因此,清晰且深思熟虑地设计工具集及其文档至关重要(详见附录 2:为你的工具做提示词工程)。
- 何时使用智能体 :智能体可用于开放式问题(open-ended problems),在这类问题中,很难甚至不可能预测所需的步骤数量,也无法硬编码固定路径。LLM 可能会运行很多轮,因此你必须对其决策能力有一定程度的信任。智能体的自主性使其成为在受信任环境中扩展任务的理想选择。
- 风险与护栏:智能体的自主特性意味着更高的成本,以及**错误累积(compounding errors)**的潜在风险。我们建议在沙盒环境中进行广泛测试,并配备适当的护栏(Guardrails)。
- 智能体的典型适用场景 :
- 解决 SWE-bench 任务的编程智能体(涉及根据任务描述编辑许多文件);
- 我们的"Computer Use(计算机使用)"参考实现,其中 Claude 使用计算机来完成任务。
组合与定制这些模式(Combining and customizing these patterns)
上述这些构建块并非死板的教条。它们是开发者可以用来塑造和组合以适应不同用例的常见模式。与其他任何 LLM 功能一样,成功的关键在于衡量性能并对实现进行迭代 。请再次记住:只有当增加复杂度能够显著改善结果时,才应该考虑增加它。
总结(Summary)
在 LLM 领域取得成功,并不在于构建最精密复杂的系统,而在于构建最适合你需求的系统。从简单的提示词开始,通过全面评估进行优化,仅在更简单的方案力不从心时才引入多步智能体系统。
在实现智能体时,我们遵循 三条核心原则:
- 保持智能体设计的简单性(Maintain simplicity in your agent's design)。
- 优先保障透明性(Prioritize transparency):明确展示智能体的规划步骤。
- 精心打造你的智能体-计算机接口(ACI,Agent-Computer Interface):通过详尽的工具文档和测试来打磨工具接口。
框架可以帮助你快速起步,但在迈向生产环境时,请毫不犹豫地减少抽象层,并使用基础组件进行构建。通过遵循这些原则,你可以创建出不仅强大,而且可靠、可维护并受用户信任的智能体。
附录 1:智能体实战(Appendix 1: Agents in practice)
我们与客户的合作揭示了两个极具前景的 AI 智能体应用领域,它们很好地展示了上文讨论的模式的实际价值。这两个应用都说明了:智能体在那些既需要对话和动作、具有明确的成功标准、能够形成反馈循环、又整合了有意义的人工监督的任务中,能创造最大的价值。
A. 客户支持(Customer support)
客户支持将熟悉的聊天机器人界面与通过工具集成增强的能力结合在一起。这非常适合更开放式的智能体,因为:
- 支持交互天然遵循对话流,同时又需要访问外部信息并执行操作;
- 可以集成工具来拉取客户数据、订单历史和知识库文章;
- 诸如发放退款或更新工单等操作可以通过编程方式处理;
- 成功与否可以通过用户定义的解决情况来清晰衡量。
多家公司已通过基于使用量的定价模式证明了这种方法的可行性------仅对成功解决的案例收费,这充分体现了对智能体有效性的信心。
B. 编程智能体(Coding agents)
软件开发领域展现出了 LLM 功能的巨大潜力,其能力已从代码补全演进为自主解决问题。智能体在此领域特别有效,因为:
- 代码解决方案可以通过**自动化测试(automated tests)**进行验证;
- 智能体可以使用测试结果作为反馈,对解决方案进行迭代;
- 问题空间定义明确且高度结构化;
- 输出质量可以被客观地衡量。
在我们自己的实现中,智能体已经能够仅凭拉取请求(PR)描述就解决 SWE-bench Verified 基准测试中的真实 GitHub 问题。然而,尽管自动化测试有助于验证功能,**人工审查(human review)**对于确保解决方案符合更广泛的系统需求仍然至关重要。
附录 2:为你的工具做提示词工程(Appendix 2: Prompt engineering your tools)
无论你构建的是哪种智能体系统,工具(Tools) 都很可能是其中的重要组成部分。工具使 Claude 能够通过在我们 API 中指定其确切结构和定义来与外部服务和 API 交互。当 Claude 响应时,如果它计划调用工具,就会在 API 响应中包含一个工具使用块(tool use block)。
工具的定义和规范应当得到与整体提示词同等程度的提示词工程关注。在这篇简短的附录中,我们将介绍如何对工具进行提示词工程:
通常有几种不同的方式可以指定同一个动作。例如,你可以通过编写 diff(差异补丁)或重写整个文件来指定文件编辑。对于结构化输出,你可以在 markdown 或 JSON 内返回代码。在软件工程中,这些差异只是表面上的,可以无损地相互转换。然而,对于 LLM 来说,某些格式比其他格式难写得多 。编写 diff 要求模型在实际写出新代码之前,就先在块头(chunk header)中知道有多少行正在发生变化。而在 JSON 内编写代码(相比于 markdown)则需要对换行符和引号进行额外的转义。
我们决定工具格式的建议如下:
- 给模型足够的 token 来"思考"(Give the model enough tokens to "think"),然后再让它把自己逼进死胡同。
- 保持格式接近模型在互联网文本中自然见过的形式。
- 确保没有格式"开销"(formatting "overhead"),例如不得不准确计算数千行代码的行数,或者对编写的任何代码进行字符串转义。
经验法则 :想想我们在人机交互界面(HCI,Human-Computer Interface)上投入了多少精力,就应该在打造优秀的 智能体-计算机接口(ACI,Agent-Computer Interface) 上投入同样多的精力。以下是如何做到这一点的几个实用建议:
- 设身处地为模型着想 :根据描述和参数,使用这个工具的方式是否显而易见?还是需要仔细推敲?如果连人类都需要仔细思考,那么对模型来说也注定很难。一个好的工具定义通常包含:示例用法、边缘情况(edge cases)、输入格式要求,以及与其他工具的清晰边界。
- 你如何更改参数名称或描述,使其更加一目了然? 把这看作是为团队中的初级开发人员编写一份极佳的文档字符串(docstring)。当你使用许多相似的工具时,这一点尤为重要。
- 测试模型如何使用你的工具:在我们的工作台(Workbench)中运行大量示例输入,看看模型会犯什么错误,并进行迭代。
- 对你的工具进行"防呆设计"(Poka-yoke your tools):修改参数,使其更难犯错。
真实案例(SWE-bench) :在为 SWE-bench 构建智能体时,我们实际花在优化工具上的时间比优化整体提示词的时间还要多。例如,我们发现当智能体移出根目录后,模型在使用相对路径的工具时经常出错。为了修复这个问题,我们将工具修改为始终要求使用绝对路径(absolute filepaths)------随后我们发现模型能够完美无瑕地使用这一方法。