代理设计基础
代理的基本组成部分有三类:
- 模型:为代理提供推理和决策能力的 LLM
- 工具:代理用来采取行动的外部函数或 API
- 指令:明确定义代理行为的指导方针和安全边界
以下是使用 OpenAI 的 Agents SDK 时的代码示例。你也可以使用自己喜欢的库或从头开始构建来实现相同的概念。
from agents import Agent
weather_agent = Agent(
name="Weather agent",
instructions="You are a helpful agent who can talk to users about the weather.",
tools=[get_weather],
)
选择模型
不同的模型在任务复杂性、延迟和成本等方面各有优劣。正如我们在下一节“编排”中将看到的,你可能希望针对工作流中的不同任务考虑使用多种模型。
并不是每个任务都需要最强大的模型——简单的检索或意图分类任务可能由较小、更快的模型处理,而像决定是否批准退款这样困难的任务则可能从更强大的模型中受益。
一种有效的方法是为每个任务使用最强大的模型构建代理原型,以建立性能基准。然后,尝试替换为较小的模型,以查看它们是否仍能达到可接受的结果。通过这种方式,你不会过早限制代理的能力,并且可以诊断出较小模型成功或失败的地方。
总之,选择模型的原则很简单:
- 设置评估以建立性能基准
- 专注于使用最佳可用模型满足准确性目标
- 在可能的情况下,通过用更小的模型替换较大的模型来优化成本和延迟
你可以在这里找到关于选择 OpenAI 模型的详细指南。
定义工具
工具通过使用底层应用程序或系统的 API,扩展了代理的功能。对于没有 API 的遗留系统,代理可以依靠计算机使用模型,通过网络和应用程序 UI 直接与这些应用程序和系统进行交互,就像人类一样。
每个工具都应该有一个标准化的定义,以实现工具和代理之间灵活的多对多关系。文档完善、经过充分测试和可重用的工具,提高了可发现性,简化了版本管理,并防止了冗余定义。
大体而言,代理需要三种类型的工具:
类型 (Type) | 描述 (Description) | 示例 (Examples) |
---|---|---|
数据 (Data) | 使代理能够获取执行工作流所需的上下文和信息。 | 查询交易数据库或 CRM 系统、读取 PDF 文档、或在网络上搜索信息。 |
动作 (Action) | 使代理能够与系统交互并执行操作,例如向数据库添加新信息、更新记录或发送消息。 | 发送电子邮件和短信、更新 CRM 记录、将客户服务工单转交给人工处理。 |
编排 (Orchestration) | 代理本身可以作为其他代理的工具——参见“编排”章节中的“管理者模式(Manager Pattern)”。 | 退款代理(Refund agent)、研究代理(Research agent)、写作代理(Writing agent)。 |
例如,以下是当使用 Agents SDK 时,如何为上述代理定义一系列工具:
from agents import Agent, WebSearchTool, function_tool
from datetime import datetime
@function_tool
def save_results(output):
db.insert({"output": output, "timestamp": datetime.now()})
return "File saved"
search_agent = Agent(
name="Search agent",
instructions="Help the user search the internet and save results if asked.",
tools=[WebSearchTool(), save_results],
)
随着所需工具数量的增加,考虑将任务拆分为多个代理(见编排)。
配置指令
高质量的指令对于任何 LLM 驱动的应用都是至关重要的,但对于代理来说尤其重要。清晰的指令可以减少歧义,提高代理的决策能力,从而实现更顺畅的工作流执行和更少的错误。
代理指令最佳实践
使用现有文档
在创建流程时,尽量利用已有的操作规程、支持脚本或政策文件来构建适合大语言模型(LLM)的流程。
例如,在客户服务场景中,这些流程可以大致映射到知识库中的单篇文章。
提示代理拆分任务
将复杂、密集的内容拆解为更小、更明确的步骤,可以减少歧义,帮助模型更准确地遵循指令。
定义清晰的动作
确保流程中的每一步都对应具体的操作或输出。
例如:
- 向用户询问订单号
- 调用 API 获取账户详情
对动作(包括面向用户的提示语)明确说明,可以最大限度减少理解错误。
捕捉边缘情况
在真实交互中,常会遇到信息不完整或意外问题。
健壮的流程应提前预判这些情况,并给出应对策略:
- 设计条件步骤或分支路径
- 在缺少关键信息时,执行备用步骤
这样可以确保代理在各种情况下都能顺利完成任务。
你可以使用高级模型(如 o1 或 o3-mini)从现有文档自动生成指令。以下是用于此方法的示例提示:
你是一名编写 LLM 代理指令的专家。
请将以下帮助中心文档转换为一组清晰的指令,并以编号列表形式呈现。
该文档将作为 LLM 遵循的政策。
确保指令没有歧义,并以代理执行的方向进行编写。
待转换的帮助中心文档如下:{{help_center_doc}}
编排
在基础组件就位后,你可以考虑编排模式,以使代理能够有效地执行工作流。
虽然很容易就想立即构建一个具有复杂架构的完全自主代理,但客户通常会发现采用渐进式的方法更为成功。
一般来说,编排模式分为两类:
- 单代理系统:由单个配备适当工具和指令的模型循环执行工作流
- 多代理系统:工作流执行分布在多个协调的代理之间
让我们详细探讨每种模式。
单代理系统
一个代理可以通过逐步添加工具来处理许多任务,从而保持复杂性可控,并简化评估和维护。每添加一个新工具,代理的能力就会扩展,而无需过早地强迫你编排多个代理。

每种编排方法都需要一个“运行”的概念,通常实现为一个循环,让代理在达到退出条件之前持续操作。常见的退出条件包括工具调用、特定结构化输出、错误或达到最大轮次。
例如,在 Agents SDK 中,代理是通过Runner.run()
方法启动的,该方法循环 LLM,直到满足以下条件之一:
- 调用最终输出工具,由特定输出类型定义
- 模型返回没有任何工具调用的响应(例如,直接用户消息)
示例用法
Agents.run(agent, [UserMessage( )]) "What's the capital of the USA?"
这种 while 循环的概念是代理功能的核心。在接下来的多代理系统中,你可以在代理之间进行一系列工具调用和交接,但允许模型运行多个步骤,直到满足退出条件。
管理复杂性的一个有效策略是使用提示模板。与其为不同的用例维护众多单独的提示,不如使用一个灵活的基础提示,接受策略变量。这种模板方法可以轻松适应各种上下文,显著简化维护和评估。随着新用例的出现,你可以更新变量,而不是重写整个工作流。
"""
你是一名呼叫中心客服专员。你正在与 {{user_first_name}} 交流,对方已成为会员 {{user_tenure}}。
用户最常见的投诉类别包括:{{user_complaint_categories}}。
请先向用户问好,感谢他们一直以来的忠诚支持,并耐心解答用户的所有问题!
"""
何时考虑创建多个代理
我们的普遍建议是首先最大化单个代理的能力。更多代理可以提供直观的概念分离,但可能会引入额外的复杂性和开销,因此通常一个配备工具的单一代理就足够了。
对于许多复杂的工作流,将提示和工具拆分到多个代理中,可以提高性能和可扩展性。当你的代理无法遵循复杂指令或始终选择错误工具时,可能需要进一步拆分系统,引入更多独立的代理。
拆分代理的实用指南包括:
复杂逻辑
当提示词包含大量条件语句(多个 if-then-else 分支)并且提示模板难以扩展时,可以考虑将每个逻辑片段拆分到不同的代理中处理。
工具过载
问题不仅在于工具数量,还在于它们的相似性或功能重叠。
有些实现可以成功管理 15 个以上定义明确、功能独立的工具,而有些即使少于 10 个功能重叠的工具也会遇到困难。
如果仅通过提供描述性名称、清晰参数和详细说明来提高工具的清晰度,但没有减少重叠,性能也不会提升。
建议使用多个代理来分担这些工具的使用和管理。
多代理系统的两种常见模式
管理者模式(Manager,代理作为工具)
由一个中央“管理者”代理,通过工具调用协调多个专门化的代理,每个代理负责处理特定任务或领域。
去中心化模式(Decentralized,代理交由代理处理)
多个代理以平级方式协作,根据各自的专业领域将任务交接给其他代理。
多代理系统可以建模为图结构:
- 在管理者模式中,边表示工具调用。
- 在去中心化模式中,边表示在代理之间传递执行的任务交接。
无论采用哪种编排模式,都应遵循相同原则:
- 保持组件灵活、可组合
- 使用清晰且结构良好的提示词驱动执行
管理者模式
管理者模式通过工具调用使中央 LLM——“管理者”——能够无缝地编排专门化代理的网络。管理者智能地将任务委派给合适的代理,并在适当时机进行结果合成,从而确保流畅、统一的用户体验,随时提供专业化能力。
此模式非常适合仅希望一个代理控制工作流执行并访问用户的场景。

例如,以下是如何在 Agents SDK 中实现此模式的示例:
from agents import Agent, Runner
manager_agent = Agent(
name="manager_agent",
instructions=(
"You are a translation agent. You use the tools given to you to translate."
" If asked for multiple translations, you call the relevant tools."
),
tools=[
spanish_agent.as_tool(
tool_name="translate_to_spanish",
tool_description="Translate the user's message to Spanish",
),
french_agent.as_tool(
tool_name="translate_to_french",
tool_description="Translate the user's message to French",
),
italian_agent.as_tool(
tool_name="translate_to_italian",
tool_description="Translate the user's message to Italian",
),
],
)
async def main():
msg = input("Translate 'hello' to Spanish, French and Italian for me!")
orchestrator_output = await Runner.run(
manager_agent, msg
)
for message in orchestrator_output.new_messages:
print(f" - Translation step: {message.content}")
声明式与非声明式图
一些框架是声明式的,要求开发人员通过由节点(代理)和边(确定性或动态交接)组成的图,明确地定义工作流中的每个分支、循环和条件。尽管这种方法有利于可视化清晰,但随着工作流变得更加动态和复杂,它可能会变得繁琐和具有挑战性,通常需要学习专门的领域特定语言。
相比之下,Agents SDK 采用了更灵活的代码优先方法。开发人员可以直接使用熟悉的编程构造来表达工作流逻辑,而无需预先定义整个图,从而实现更动态和可适应的代理编排。
去中心化模式
在去中心化模式中,代理可以相互“交接”工作流执行。交接是一种单向转移,允许一个代理委派给另一个代理。在 Agents SDK 中,交接是一种工具或函数。如果一个代理调用了交接函数,我们会立即在被交接到的新代理上开始执行,同时转移最新的对话状态。
这种模式涉及许多平级代理的使用,其中一个代理可以直接将工作流的控制权交给另一个代理。当你不需要单个代理保持中央控制或合成时,这种模式是最优的——允许每个代理根据需要接管执行并与用户互动。

例如,以下是如何在 Agents SDK 中为处理销售和支持的客户服务工作流实现去中心化模式的示例:
from agents import Agent, Runner
# 专业技术支持代理
technical_support_agent = Agent(
name="Technical Support Agent",
instructions=(
"You provide expert assistance with resolving technical issues, "
"system outages, or product troubleshooting."
),
tools=[search_knowledge_base],
)
# 销售助理代理
sales_assistant_agent = Agent(
name="Sales Assistant Agent",
instructions=(
"You help enterprise clients browse the product catalog, recommend "
"suitable solutions, and facilitate purchase transactions."
),
tools=[initiate_purchase_order],
)
# 订单管理代理
order_management_agent = Agent(
name="Order Management Agent",
instructions=(
"You assist clients with inquiries regarding order tracking, "
"delivery schedules, and processing returns or refunds."
),
tools=[track_order_status, initiate_refund_process],
)
# 分流代理,负责初步判断并分派给合适的专属代理
triage_agent = Agent(
name="Triage Agent",
instructions=(
"You act as the first point of contact, assessing customer queries "
"and directing them promptly to the correct specialized agent."
),
handoffs=[technical_support_agent, sales_assistant_agent, order_management_agent],
)
# 运行分流代理,处理用户输入
await Runner.run(
triage_agent,
"Could you please provide an update on the delivery timeline for our recent purchase?"
)
在上述示例中,初始用户消息被发送到triage_agent
。triage_agent
识别到该消息与最近的购买有关,因此会调用交接,将控制权转移给order_management_agent
。
这种模式在场景如对话分流,或每当你希望专门代理完全接管某些任务而不需要原代理继续参与时,特别有效。可选地,你可以为第二个代理配备一个返回原代理的交接,允许它在必要时再次转移控制权。