构建代理系统涉及协调 LLM 调用、数据检索和外部动作如何协同工作。 可以将代理系统的设计模式视为 复杂性和自主性的连续性:从 确定性链到可以做出动态决策的单代理系统(并可能涉及多个 LLM 调用的幕后),到协调多个专用 代理 的多 代理 体系结构。
工具调用
在深入了解设计模式之前,必须了解工具调用,这是一项基本功能,可在任何代理系统中使用,从简单到复杂。 工具调用是一种机制,允许代理系统与外部函数、数据源或服务进行交互。 这可以启用:
- 实时数据查找,例如 SQL 查询、CRM 提取或矢量索引检索。
- 执行发送电子邮件或更新记录等操作。
- 通过 Python 函数或 API 进行任意逻辑或转换。
因此,工具调用提供了一种强大的机制,使 LLM“了解”外部数据或 API,无论选择哪种设计模式。
若要详细了解代理工具,请参阅 AI 代理工具。
以下各节讨论了三种代理系统设计模式,每个模式都可以利用不同程度的工具调用。
比较第一代 AI 应用设计模式
第一代 AI 应用(代理)设计模式按复杂性顺序呈现。
设计模式 | 何时使用 | 优点 | 缺点 |
---|---|---|---|
确定性链 |
|
|
|
单代理系统 |
|
|
|
多代理系统 | 大型或跨职能域;多个“专家”代理;不同的逻辑或聊天上下文;高级反射模式。 |
|
|
单代理系统
单代理系统具有一个协调的逻辑流(通常协调多个 LLM 调用)来处理传入请求。 代理可以:
- 接收请求,例如用户查询,以及任何相关的上下文信息,如对话历史记录。
- 分析如何做出最佳响应,并选择是否调用工具以获取外部数据或采取行动。
- 如果需要,请重复调用 LLM(和/或相同工具),直到达到目标或满足特定条件(例如接收有效数据或解决错误)。
- 将工具输出集成到对话中。
- 返回一个连贯的回应作为输出。
在许多用例中,一轮 LLM 推理(通常使用工具调用)就足够了。 但是,更高级的代理可以循环执行多个步骤,直到达到所需的结果。
即使只有“一个”代理,你仍然可以在后台进行多个 LLM 和工具调用(用于规划、生成、验证等),所有这些调用都由此单一统一流管理。
示例:技术支持助理
- 如果用户提出一个简单的问题(“我们的返回策略是什么?”),代理可能会直接从 LLM 的知识做出响应。
- 如果用户想要其订单状态,代理将调用函数
lookup_order(customer_id, order_id)
。 如果该工具以“无效订单号”进行响应,代理可能会重试或提示用户输入正确的 ID,继续,直到提供最终答案。
何时使用:
- 你期望多样化的用户查询,但仍在一个统一的领域或产品范畴内。
- 某些查询或条件可能保证工具的使用,例如决定何时提取客户数据。
- 需要比确定性链更具灵活性,但不需要针对不同的任务使用单独的专用代理。
优点:
- 代理可以通过选择要调用的工具(如果有)来适应新的或意外的查询。
- 代理可以循环遍历重复的 LLM 调用或工具调用来优化结果 - 无需完全多代理设置。
- 这种设计模式通常是企业用例的甜蜜点-比多代理设置更容易调试,同时仍允许动态逻辑和有限自治。
注意事项:
- 与硬编码链相比,你必须防范重复或无效的工具调用。 (无限循环可能发生在任何工具调用方案中,因此设置迭代限制或超时。
- 如果应用程序跨越了完全不同的子域(财务、devops、marketing 等),单个代理可能会变得笨拙或功能要求过载。
- 你仍然需要精心设计的提示和约束来使代理保持专注和相关。
确定性链(硬编码步骤)
在此模式中,开发人员定义以何种顺序和参数调用哪些组件。 没有关于调用哪些工具或以何种顺序的动态决策。 系统遵循所有请求的预定义工作流,使其高度可预测。
通常称为“链”,流本质上是一个固定的步骤链,例如:
- 始终检索用户的请求,并从相关上下文的向量索引中检索。
- 将该上下文与用户的请求合并到最终 LLM 提示符。
- 调用 LLM 并返回响应。
示例:基本 RAG 链
确定性 RAG 链可能总是这样:
- 使用用户传入的请求,从向量索引中检索前k个结果。
- 将检索的区块格式化为提示模板(扩充)。
- 将扩充式提示传递给 LLM(生成)。
- 返回 LLM 的响应。
何时使用:
- 这适用于具有可预测工作流的明确定义任务。
- 当一致性和审核是首要任务时。
- 如果想要通过避免多次 LLM 调用以做出编排决策来最大程度地减少延迟。
优点:
- 最高可预测性和可审核性。
- 通常具有较低延迟(LLM 调用用于编排的次数更少)。
- 更易于测试和验证。
注意事项:
- 处理各种或意外请求的灵活性有限。
- 随着逻辑分支的增长,可能会变得复杂且难以维护。
- 可能需要进行重大重构才能适应新功能。
多代理系统
多代理系统涉及两个或多个专用代理,这些代理交换消息和/或协作处理任务。 每个代理都有自己的域或任务专业知识、上下文和可能不同的工具集。 单独的“协调器”(可能是另一个 LLM 或基于规则的路由器)将请求定向到相应的代理,或决定何时将请求从一个代理移交到另一个代理。
示例:具有专用代理的企业助手
- 客户支持代理: 处理 CRM 查找、返回和发货。
- 分析代理: 重点介绍 SQL 查询和数据汇总。
- 监督器/路由器: 选择哪个代理最适合给定的用户查询,或何时切换。
每个子代理都可以在其自己的域中(例如 lookup_customer_account
或 run_sql_query
)内执行工具调用,这通常需要唯一的提示或对话历史记录。
何时使用:
- 你有不同的问题领域或技能集,如编码代理或财务代理。
- 每个代理都需要访问会话历史记录或特定于域的提示。
- 你有这么多工具,将它们全部拟合到一个代理的架构是不切实际的;每个代理都可以拥有子集。
- 你想要在专门的智能体之间实现反思、评论或来回协作。
优点:
- 这种模块化方法意味着每个代理都可以由单独的团队开发或维护,专门处理窄域。
- 可以处理单个代理难以一致管理的大型复杂企业工作流。
- 促进高级多步骤或多透视推理 - 例如,一个生成答案的代理,另一个代理验证答案。
注意事项:
- 需要一种策略来在代理之间进行路由,并处理跨多个终结点的日志记录、跟踪和调试的开销。
- 如果你有许多子代理和工具,则确定哪个代理有权访问哪些数据或 API 可能会变得复杂。
- 如果未仔细约束,代理可以无限期地在相互之间推卸任务,而不解决任务。
- 单代理工具调用中也存在无限循环风险,但多代理设置增加了另一层调试复杂性。
实用建议
无论选择哪种设计模式,请考虑以下用于开发稳定且可维护的代理系统的最佳做法。
- 简单开始: 如果只需要简单的链条,确定性链可以快速构建。
- 逐渐添加复杂性: 由于需要更多动态查询或灵活的数据源,请使用工具调用迁移到单代理系统。
- 转到多代理: 仅当您有明确区分的域或任务、多个对话上下文,或出于工具集过于庞大无法由单个代理提示的情况。
如果用例从小型开始(如简单的 RAG 链)以硬编码链开头。 随着需求的发展,可以为动态决策添加工具调用逻辑,甚至将任务细分为多个专用代理。 在实践中,许多实际代理系统将模式组合在一起。 例如,使用最确定性的链,但如果需要,LLM 可在单个步骤中动态调用某些 API。
马赛克 AI 代理框架 与你选择的任何模式无关,因此随着应用程序的发展,可以轻松改进设计模式。
开发指南
-
提示
- 保持提示清晰和最少,以避免矛盾的说明,分散信息,减少幻觉。
- 仅提供代理所需的工具和上下文,而不是一组未绑定的 API 或大型无关上下文。
-
日志记录和可观测性
- 为每个用户请求、代理计划和工具调用实现详细的日志记录。 MLflow 跟踪等工具可以帮助捕获结构化日志进行调试。
- 安全地存储日志,并注意对话数据中的个人身份信息(PII)。
-
模型更新和版本锁定
- 当提供程序在后台更新模型时,LLM 行为可能会改变。 使用版本固定和频繁的回归测试来确保代理逻辑保持可靠且稳定。
- 将 MLflow 与 马赛克 AI 代理评估 相结合提供了一种简化的版本控制代理的方式,并定期评估质量和性能。
测试和迭代指南
-
错误处理和回退逻辑
- 规划应对工具或 LLM 故障。 超时、格式不正确的响应或空结果可能会中断工作流。 在高级功能失败时,包括重试策略、回退逻辑或更简单的回退链。
- 迭代提示设计
- 预期会随着时间的推移优化提示和链逻辑。 使用 Git 和 MLflow 对每次更改进行版本化控制,从而可以回滚或比较不同版本的性能。
- 考虑 DSPy 等框架以编程方式优化代理系统中的提示和其他组件。
生产指南
-
延迟与成本优化
- 每增加一个额外的 LLM 或工具调用都会增加令牌消耗和响应时间。 尽可能合并步骤或缓存重复查询,以保持性能和成本可管理。
-
安全和沙盒技术
- 如果代理可以更新记录或运行代码,请对这些操作进行沙盒处理,或在必要时强制实施人工审批。 这在企业或受监管的环境中至关重要,以避免意外的伤害。
- Databricks 建议使用 Unity Catalog 工具进行沙盒化执行。 请参阅 Unity 目录函数工具与代理代码工具。 Unity 目录可实现任意代码执行的隔离,并防止恶意执行者欺骗代理生成和运行干扰或窃听其他请求的代码。
通过遵循这些准则,可以缓解许多最常见的故障模式,例如工具错误调用、偏移 LLM 性能或意外成本峰值,以及构建更可靠、可缩放的代理系统。