尽管单个工具赋予LLM代理特定功能,但许多任务需要不止一个动作。设想一个负责规划详细旅行行程的代理;这需要获取航班信息、查找酒店住宿、查询当地景点,然后将所有内容整理成一个有条理的计划。每一步都可能涉及一个不同的工具。设计这些工具如何按序列协同工作,即构建多步执行流程,对于构建能处理复杂难题的精巧代理非常重要。构建这些流程的架构方法,例如确保工具按正确顺序调用、数据有效传递以及整个过程符合逻辑,将被详细阐述。顺序工具使用的原理复杂任务通常过于多样化,单个工具难以处理。将一个更大的目标分解成一系列更小、可管理的子任务,每个子任务由特定工具处理,这有几点好处:模块化与可重用性:每个工具都执行一个明确定义的功能。这些工具可以以不同的组合重复使用,以构建各种复杂的流程,就像软件库中的函数一样。处理复杂性:分解问题让LLM更容易进行推理和管理。代理处理的是一系列更直接的工具调用,而非一个庞大的指令。信息链式传递:一个工具的输出通常作为下一个工具的重要输入。例如,fetch_stock_price工具的输出(当前价格)可能会被送入analyze_stock_trend工具。适应性:多步流程可以包含决策点。基于一个工具的执行结果,代理可能会决定接下来执行不同的工具,或更改后续工具的参数。这使得代理的行为更具动态性和响应性。流程设计:原则设计一个有效的多步工具执行流程需要考虑几个方面,确保序列逻辑性强、高效且具有韧性。1. 任务分解与序列逻辑第一步是将总体目标分解为工具可以执行的一系列离散动作。对于每一步,你需要明确:需要哪些信息?哪个工具可以提供或处理这些信息?这个工具的预期输出是什么?这个输出如何促进下一步或总体目标的达成?对于理解透彻、可重复的流程,序列本身可以预先确定。例如,一个“每日新闻报告生成”代理可能总是:获取头条新闻(工具 A)。总结文章(工具 B)。格式化报告(工具 C)。另外,LLM本身可以根据用户的请求和可用工具动态地确定序列。这需要为LLM提供非常清晰的工具描述,并可能提供一个高级策略或计划。本章稍后,我们将更详细地讨论工具选择背景下的代理驱动规划。2. 数据传递与状态管理随着代理按序列执行工具,数据必须在它们之间流动。工具_X的输出成为工具_Y的输入,或输入的一部分。在设计流程时,请考虑:明确映射:明确定义一个工具的哪些输出部分映射到下一个工具的哪些输入参数。这通常由代理的编排逻辑处理。情境状态:有些流程可能需要在多个工具调用之间维护一个共享情境或状态。例如,如果用户提出后续问题,代理需要记住之前的交互和工具输出。这个状态可以由代理框架管理,或者显式地传递。数据转换:有时,一个工具的输出格式与另一个工具的输入格式不直接兼容。流程设计(或代理本身)可能需要包含一个转换步骤,为此甚至可以使用一个小型实用工具或Python函数。考虑一个简单的客户支持场景:get_customer_details(customer_id)返回包含客户信息的JSON对象。get_order_history(customer_email)需要电子邮件,这是步骤1中JSON内的一个字段。create_support_ticket(details, order_id)需要摘要和步骤2中的特定订单ID。流程必须确保customer_email从第一个工具的输出中提取并传递给第二个,以此类推。3. 中间检查点与决策并非所有流程都严格线性。代理采取的路径通常取决于先前工具执行的结果。条件执行:一个工具的输出可能会决定接下来调用哪个工具,或者是否调用某个工具。例如,如果check_inventory工具返回“缺货”,下一步可能是notify_purchasing_department而不是process_order。这为你的流程引入了分支逻辑。验证步骤:在一个重要的工具执行后,你可能会包含一个步骤,让LLM(或验证工具)在继续之前检查输出的合理性或正确性。如果天气API工具突然返回伦敦200摄氏度的温度,流程理想情况下应捕获此异常。这些检查点确保代理不会盲目地处理不正确或无意义的数据,使整个过程更可靠。多步流程图示图表对于可视化和设计这些序列非常有帮助。考虑一个简化流程,用于负责查找信息和起草摘要的研究助理代理:digraph G { rankdir=TB; node [shape=box, style="filled", fontname="sans-serif"]; edge [fontname="sans-serif"]; bgcolor="transparent"; "开始" [shape=ellipse, style=filled, fillcolor="#e9ecef"]; "结束" [shape=ellipse, style=filled, fillcolor="#e9ecef"]; "开始" -> "定义搜索查询" [label="用户请求", color="#495057", fontcolor="#495057"]; "定义搜索查询" [fillcolor="#a5d8ff"]; "定义搜索查询" -> "搜索网络" [label="关键词", color="#1c7ed6", fontcolor="#1c7ed6"]; "搜索网络" [label="工具:网络搜索API", fillcolor="#74c0fc"]; "搜索网络" -> "提取关键信息" [label="搜索结果", color="#1c7ed6", fontcolor="#1c7ed6"]; "提取关键信息" [label="工具:内容提取器", fillcolor="#74c0fc"]; "提取关键信息" -> "生成摘要" [label="提取的事实", color="#1c7ed6", fontcolor="#1c7ed6"]; "生成摘要" [label="工具:文本摘要器", fillcolor="#74c0fc"]; "生成摘要" -> "结束" [label="摘要草稿", color="#495057", fontcolor="#495057"]; }一个简单的研究任务,分解为顺序的工具调用。代理首先定义查询,然后使用网络搜索工具、提取工具,最后使用摘要工具。这个图表清晰展示了操作的序列,并暗示了它们之间的数据依赖关系。“搜索网络”的输出(搜索结果)是“提取信息”的输入,以此类推。常见流程模式尽管每个任务都独特,但在设计多步工具执行流程时,会浮现一些特定模式:管道模式(顺序处理):这是最直接的模式,工具一个接一个地执行,前一个工具的输出作为下一个工具的输入。上面的研究助理例子遵循此模式。示例:获取数据 -> 清洗数据 -> 分析数据 -> 生成报告。收集-处理-行动模式:一种常见结构,代理首先从一个或多个来源收集信息,处理或整合这些信息,然后采取行动。示例:收集:获取天气预报(地点),获取日历事件(日期)。处理:LLM根据天气和日程推理,提出合适的着装建议。行动:发送通知(用户, 建议)。扇出/扇入模式:有时,一个任务可能涉及并行运行多个工具(或在不需要真正并行时顺序运行),然后整合它们的结果。示例:为了全面了解一家公司,代理可能会:扇出:获取股票价格(股票代码),获取最新新闻(公司名称),获取员工评价(公司名称)。扇入:汇总公司概况(股票数据, 新闻文章, 评价)。(“汇总”步骤可以是LLM调用或另一个工具)。迭代优化循环:代理使用一个工具,LLM评估输出,如果结果不满意,它可能会用不同的参数再次调用同一工具,或者调用一个纠正工具。这个过程持续到达到预期状态为止。示例:一个编写代码的代理。生成代码片段(要求)。执行代码(片段)(可能在沙盒中)。LLM审查执行结果/错误。如果出错,返回步骤1,并附带修改后的要求或反馈。理解这些模式可以提供一个好的起点,当你为自己的LLM代理设计流程时。LLM在复杂流程中的作用在许多高级代理系统中,LLM不仅仅是一个由固定流程喂入数据的被动组件。相反,LLM主动参与到流程的运作中:动态规划:基于初始请求和当前状态,LLM可能会动态生成一个多步计划。决策制定:在条件分支处,LLM分析上一个工具的输出,并决定采取哪条路径或接下来使用哪个工具。这非常依赖于编写良好的工具描述和对LLM的清晰指令。参数生成:LLM可以根据目前收集到的信息以及对总体目标的理解,动态生成下一个工具的输入参数。流程越复杂,对动态性的要求越高,LLM在流程编排中的作用就越大。这也意味着你的工具设计,特别是它们的描述和预期输入/输出,对于成功实现LLM驱动的流程执行变得更加重要。流程设计优选方案从简单开始:从任务的核心部分开始一个线性流程。逐步增加分支、循环和错误处理。尽可能原子化:设计工具以良好地执行一个逻辑操作。这使得它们在流程中更容易组合和理解。明确错误路径:对于序列中的每个工具调用,考虑如果它失败会发生什么。整个流程应该终止吗?代理能否尝试替代工具?是否应该向用户请求澄清?设计这些备用路径。日志记录与可观察性:记录序列中每个工具的输入和输出,以及LLM做出的任何决策。这对于调试和理解代理行为非常有价值。逐步测试:在将单个工具集成到流程中之前,彻底测试它们。然后,在测试整个端到端过程之前,测试流程的子序列。幂等性(适用时):如果流程中的某个工具可能会被重试,请确保它尽可能幂等(即,用相同的输入多次调用它的效果与调用一次相同)。这可以防止重试产生意外副作用。通过周全地设计多步执行流程,你可以让LLM代理超越简单的单次操作,处理复杂、多方面的问题。这种结构化的工具序列方法是构建有能力且可靠的AI助手的根本。后续章节将在此基础上,讨论代理如何选择工具以及管理这些流程中自然出现的依赖关系。