Agent学习

一.深入理解 Alibaba ReactAgent 原理

核心原理

ReactAgent 将模型调用、工具执行和流程控制组织成一张 Graph,由图执行引擎根据状态和条件边驱动循环。

复制代码
用户问题
   ↓
Model:调用大模型
   ↓
是否需要调用工具?
   ├── 否 → 输出答案,结束
   └── 是 → Tool:执行工具
                  ↓
             保存工具结果
                  ↓
             返回Model
                  ↓
          基于工具结果继续处理

可以将其理解为:Graph 负责调度,节点负责执行,State 负责保存信息,条件边负责决定下一步。

以下方法名和调用关系按本节源码示例整理,具体实现随版本可能有所变化。

调试入口
复制代码
ToolCallback weatherTool = FunctionToolCallback
        .builder("weather", new WeatherQueryTool())
        .description("查询天气")
        .inputType(String.class)
        .build();

ReactAgent agent = ReactAgent.builder()
        .name("demo_agent")
        .model(chatModel)
        .tools(weatherTool)
        .hooks(new LoggingHook())
        .systemPrompt("你是一个助手。")
        .build();

System.out.println(
        agent.call("查询南京天气").getText()
);

执行过程:

复制代码
用户:查询南京天气
        ↓
模型返回weather工具调用请求
        ↓
工具查询天气并返回结果
        ↓
模型结合工具结果生成回答
核心调用链
复制代码
ReactAgent.call()
        ↓
doInvoke()
        ↓
Graph.invoke()
        ↓
runner.run()
        ↓
mainGraphExecutor.execute()
        ↓
nodeExecutor.execute()
        ↓
action.apply()

各层职责:

组件 职责
ReactAgent 构建 Agent 并提供调用入口
Graph 定义节点、边和执行流程
Runner / Executor 调度并执行节点
Node Action 执行当前节点的具体逻辑
OverAllState 保存消息、工具结果等运行状态
条件边 根据状态决定下一个节点
节点如何执行

节点执行器会从 GraphRunnerContext 中取得当前节点及其对应的 Action。

复制代码
获取当前节点
      ↓
获取节点Action
      ↓
处理需要的中断或恢复逻辑
      ↓
执行action.apply()
      ↓
处理节点结果并更新状态
      ↓
根据边选择下一个节点

action.apply() 并不一定对应某个固定实现类,也可能对应构建 Graph 时注册的 Lambda。

为什么追踪不到固定的实现类

AsyncNodeActionWithConfig 是函数式接口,可以接收符合其签名的 Lambda 或实现对象。

因此,调试时需要回到节点注册的位置,查看传入的具体执行逻辑。

复制代码
initGraph注册节点
        ↓
绑定节点执行逻辑
        ↓
Graph运行到该节点
        ↓
调用注册时传入的Action

node_async() 用于将同步节点逻辑适配为异步接口,让图引擎统一处理返回结果。

注意:异步接口不等于多个节点一定并行执行,节点顺序仍由 Graph 的边决定。

ReactAgent 的核心节点

ReactAgent 在初始化 Graph 时,注册两个核心节点:

复制代码
model → AgentLlmNode
tool  → AgentToolNode

概念性伪代码:

复制代码
// 将模型节点和工具节点注册到Graph
graph.addNode("model", node_async(llmNode));
graph.addNode("tool", node_async(toolNode));

// 再配置节点之间的跳转规则
AgentLlmNode:模型节点

模型节点负责执行一次大模型调用。

主要过程:

复制代码
读取State中的消息和上下文
        ↓
构建模型请求
        ↓
调用ChatModel
        ↓
获得ChatResponse
        ↓
将模型输出写入State

模型输出可能包含:

  • 最终回答
  • 工具调用请求 tool_calls

例如,模型要求调用天气工具:

复制代码
{
  "tool_calls": [
    {
      "name": "weather",
      "arguments": {
        "city": "南京"
      }
    }
  ]
}

这里是结构示意,实际参数格式取决于工具定义。

AgentToolNode:工具节点

工具节点负责执行模型提出的工具调用。

复制代码
读取最近一次AssistantMessage
        ↓
提取tool_calls
        ↓
找到对应工具
        ↓
传入参数并执行工具
        ↓
封装ToolResponseMessage
        ↓
将工具结果写入State

工具执行结果会成为下一次模型调用的上下文。

复制代码
模型:请调用weather查询南京天气
工具:南京,多云,气温26℃
模型:根据工具结果组织最终回答

模型负责决定调用哪个工具,工具节点负责真正执行。

OverAllState:全局状态

OverAllState 用于在节点之间共享运行信息。

例如:

复制代码
用户问题
模型输出
工具调用请求
工具返回结果
其他流程控制信息

消息会随着循环不断补充:

复制代码
UserMessage:查询南京天气
        ↓
AssistantMessage:请求调用weather
        ↓
ToolResponseMessage:天气查询结果
        ↓
AssistantMessage:最终回答

因此,下一轮模型能够看到上一轮工具的结果,而不是从头开始。

循环如何实现

ReAct 循环由 Graph 的条件边实现。

本节源码中的相关入口包括:

复制代码
initGraph()
    ↓
setupHookEdges()
    ↓
setupToolRouting()

关键是两组路由:

复制代码
Model → Tools / Loop / Exit
Tools → Model / Exit
Model 执行后的路由

模型节点执行后,路由逻辑检查当前状态,决定:

  • 进入工具节点

  • 继续下一轮模型调用

  • 结束执行

    Model执行完成

    检查模型输出和控制状态
    ├── 需要工具 → Tool
    ├── 需要继续 → Loop
    └── 可以结束 → Exit

普通工具调用场景下:

复制代码
存在tool_calls → 执行工具
没有tool_calls → 返回模型答案

实际路由还可能受到 Hook 等控制逻辑影响。

Tool 执行后的路由

工具节点完成后,默认返回模型节点,让模型读取工具结果并继续处理。

复制代码
Tool执行完成
      ↓
检查是否直接返回
   ├── 是 → Exit
   └── 否 → Model

按本节源码描述,如果本轮所有已执行工具均满足 return_direct=true,路由会直接结束,而不再交给模型加工。

复制代码
普通工具:
Model → Tool → Model → 最终回答

直接返回工具:
Model → Tool → 结束
Hooks 和 Interceptors 的位置

两者都是扩展机制,但关注点不同。

扩展机制 作用位置 典型用途
Hooks Agent 生命周期节点 开始、结束、每轮前后的状态处理
Interceptors 模型或工具调用边界 改写请求、修改响应、重试、缓存

理解源码时:

复制代码
Hooks:
关注扩展逻辑在Graph流程中的执行位置。

Interceptors:
关注模型或工具节点内部的调用如何被包装。

不能仅根据"记录日志"判断是哪一种,两者都可以记录日志。

完整执行示例

用户输入:

复制代码
查询南京天气。

执行步骤:

  1. 用户消息进入 OverAllState

  2. Graph 调度 AgentLlmNode

  3. 模型返回 weather 工具调用请求。

  4. 条件边将流程转到 AgentToolNode

  5. 工具执行,并将结果写入状态。

  6. 若不满足直接返回条件,流程回到模型节点。

  7. 模型结合天气结果生成答案。

  8. 路由判断可以结束,返回结果。

    用户问题

    State保存用户消息

    Model生成tool_calls

    Tool执行weather

    State保存工具结果

    Model生成最终答案

    Exit

重点总结
  1. ReactAgent 的底层是 Graph 驱动的执行流程,可以理解为一套状态机。

  2. 四个核心角色:

    Graph:定义整体流程
    Node:执行具体操作
    State:共享消息和运行数据
    条件边:决定下一步执行哪个节点

  3. 两个核心节点:

    AgentLlmNode → 调用大模型
    AgentToolNode → 执行工具

  4. ReAct 的循环由条件边完成:

    Model → Tool → Model → ...

  5. 工具结果会写入状态,下一轮模型基于这些结果继续处理。

  6. action.apply() 执行的是构建 Graph 时注册的逻辑,不一定存在一个固定实现类。

  7. node_async() 用于适配异步执行接口,不代表所有节点都会并行运行。

  8. 普通工具执行后返回模型;满足 return_direct 条件时可以直接结束。

  9. Hooks 关注生命周期节点,Interceptors 关注具体调用的执行行为。

三. Agent常用架构:Human-in-the-Loop

HITL 简介

Human-in-the-Loop(HITL,人在回路)是在 Agent 执行流程中引入人工审核,让人在关键节点批准、修改或拒绝 Agent 的操作。

复制代码
Agent自动执行
      ↓
准备执行需要审批的操作
      ↓
暂停并保存状态
      ↓
人工审核
   ├── 批准 → 执行原操作
   ├── 修改 → 按修改后的参数执行
   └── 拒绝 → 不执行该操作,反馈给模型

HITL 本质上是一种流程控制机制,通过中断、状态保存和恢复控制 Agent 的行为。

适用场景
  • 删除文件、修改重要资源
  • 执行数据库修改或运维指令
  • 下单、转账、封禁账号等外部操作
  • 需要人工确认的业务审批
  • 信息不足、决策存在较大不确定性的任务

是否需要审批应根据业务规则配置,不是所有工具都必须人工确认。

Spring AI Alibaba 的实现

本节使用 HumanInTheLoopHook 实现工具审批。

主要包含三个阶段:

复制代码
配置中断
   ↓
响应中断
   ↓
人工反馈并恢复执行

核心组件:

组件 作用
HumanInTheLoopHook 声明受控工具,处理中断和人工反馈
ToolConfig 配置工具审批说明
MemorySaver 在内存中保存执行检查点
threadId 关联同一次执行的暂停与恢复
InterruptionMetadata 保存待审核工具的信息
ToolFeedback 表示针对工具调用的人工反馈
RunnableConfig 传递执行标识和人工反馈

以下代码按课程中的 API 写法整理。

第一步:配置中断

指定哪些工具需要审批,并为 Agent 配置检查点保存器。

复制代码
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.agent.hook.hip.HumanInTheLoopHook;
import com.alibaba.cloud.ai.graph.agent.hook.hip.ToolConfig;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;

import java.util.List;

// 保存执行检查点,支持暂停与恢复
MemorySaver memorySaver = new MemorySaver();

// 配置需要审批的工具
HumanInTheLoopHook humanInTheLoopHook =
        HumanInTheLoopHook.builder()
                .approvalOn(
                        "write_file",
                        ToolConfig.builder()
                                .description("文件写入操作需要审批")
                                .build()
                )
                .approvalOn(
                        "execute_sql",
                        ToolConfig.builder()
                                .description("SQL执行操作需要审批")
                                .build()
                )
                .build();

// chatModel和工具回调由项目提前定义
ReactAgent agent = ReactAgent.builder()
        .name("approval_agent")
        .model(chatModel)
        .tools(writeFileTool, executeSqlTool, readDataTool)
        .hooks(List.of(humanInTheLoopHook))
        .saver(memorySaver)
        .build();

这里:

复制代码
write_file  → 需要人工审批
execute_sql → 需要人工审批
readDataTool → 未在该Hook中配置审批

approvalOn() 中的名字必须与工具注册名称一致。

第二步:执行并接收中断

调用时提供 threadId,用于关联检查点和后续恢复。

复制代码
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.NodeOutput;
import com.alibaba.cloud.ai.graph.action.InterruptionMetadata;

import java.util.Optional;

String threadId = "user-session-123";

RunnableConfig config = RunnableConfig.builder()
        .threadId(threadId)
        .build();

Optional<NodeOutput> result = agent.invokeAndGetOutput(
        "删除数据库中的旧记录",
        config
);

if (result.isPresent()
        && result.get() instanceof InterruptionMetadata interruption) {

    for (InterruptionMetadata.ToolFeedback feedback
            : interruption.toolFeedbacks()) {

        System.out.println("工具:" + feedback.getName());
        System.out.println("参数:" + feedback.getArguments());
        System.out.println("说明:" + feedback.getDescription());
    }
}

示例中断信息:

复制代码
工具:execute_sql
参数:{"query":"DELETE FROM records WHERE ..."}
说明:SQL执行操作需要审批

此时工具尚未执行,Agent 处于可恢复的暂停状态,并不是执行失败。

第三步:人工反馈并恢复

用户完成审批后,将反馈通过相同的 threadId 传回。

下面代码演示"用户已明确批准全部待审核操作"的情况:

复制代码
InterruptionMetadata.Builder feedbackBuilder =
        InterruptionMetadata.builder()
                .nodeId(interruption.node())
                .state(interruption.state());

for (InterruptionMetadata.ToolFeedback toolFeedback
        : interruption.toolFeedbacks()) {

    InterruptionMetadata.ToolFeedback approvedFeedback =
            InterruptionMetadata.ToolFeedback.builder(toolFeedback)
                    .result(
                            InterruptionMetadata.ToolFeedback
                                    .FeedbackResult.APPROVED
                    )
                    .build();

    feedbackBuilder.addToolFeedback(approvedFeedback);
}

InterruptionMetadata approvalMetadata =
        feedbackBuilder.build();

RunnableConfig resumeConfig = RunnableConfig.builder()
        .threadId(threadId)
        .addMetadata(
                RunnableConfig.HUMAN_FEEDBACK_METADATA_KEY,
                approvalMetadata
        )
        .build();

Optional<NodeOutput> resumedResult =
        agent.invokeAndGetOutput("", resumeConfig);

if (resumedResult.isPresent()) {
    if (resumedResult.get() instanceof InterruptionMetadata) {
        System.out.println("执行再次暂停,需要继续人工审批");
    } else {
        System.out.println("执行结果:" + resumedResult.get());
    }
}

注意:

  • 批准动作应来自真实用户或审批系统。
  • 恢复时使用原来的 threadId 和检查点存储。
  • 恢复后可能再次触发审批,不能认为有返回值就一定执行完成。
三种人工决策
决策 处理方式
APPROVED 保留原工具调用,允许执行
EDITED 使用人工修改后的参数执行
REJECTED 不执行该工具,将拒绝信息反馈给模型

例如:

复制代码
模型计划:
删除90天前的数据。

人工批准:
按原条件执行。

人工修改:
改成删除180天前的数据。

人工拒绝:
取消删除,要求先查询记录数量。

拒绝某个工具调用,不一定意味着整个 Agent 结束;模型可以根据拒绝原因继续回答或重新规划。

为什么需要检查点

暂停后,框架需要知道:

  • 当前执行到了哪个节点

  • 用户和模型已经说了什么

  • 模型准备调用哪些工具

  • 哪些工具尚未执行

  • 恢复时应该从哪里继续

    触发中断

    保存Graph执行状态

    等待人工反馈

    通过threadId找到检查点

    恢复原执行流程

MemorySaver 保存的是执行检查点,与仅保存聊天消息的对话记忆不同。

复制代码
对话记忆:
让模型知道之前聊了什么。

执行检查点:
让框架知道流程停在哪里、如何继续。

MemorySaver 是内存实现,应用重启后不能依靠它恢复原来的执行状态。

HumanInTheLoopHook 的执行位置

该 Hook 位于:

复制代码
模型输出之后
      ↓
HumanInTheLoopHook
      ↓
工具真正执行之前

对应生命周期位置:

复制代码
@HookPositions(HookPosition.AFTER_MODEL)

此时模型已经生成 tool_calls,但工具还没有执行,所以可以展示具体工具和参数供人审批。

这也说明:Hook 同样可以控制流程是否继续,功能不局限于记录日志。

interrupt:判断是否中断

节点执行器在节点的 action.apply() 之前检查中断条件。

复制代码
interrupt()
    ↓
判断是否暂停
    ├── 暂停 → 返回中断信息
    └── 放行 → action.apply()

两种返回值:

复制代码
// 不触发中断,继续执行
Optional.empty();

// 触发中断,将控制权交还调用方
Optional.of(interruptionMetadata);

首次执行时:

复制代码
读取最近的AssistantMessage
      ↓
检查是否包含Tool Call
      ↓
检查工具是否命中approvalOn
      ↓
命中受控工具
      ↓
构造ToolFeedback和InterruptionMetadata
      ↓
暂停Graph

恢复执行时:

复制代码
读取HUMAN_FEEDBACK_METADATA_KEY
      ↓
校验人工反馈
   ├── 不合法或不完整 → 再次中断
   └── 合法 → 放行节点

放行表示允许进入反馈处理逻辑,并不意味着所有工具都已获批准。

afterModel:处理人工反馈

恢复后,afterModel() 读取人工反馈,并调整之前尚未执行的工具调用。

复制代码
读取人工反馈
      ↓
定位原AssistantMessage中的Tool Call
      ↓
逐个匹配审批结果
   ├── APPROVED → 保留调用
   ├── EDITED   → 替换参数
   └── REJECTED → 生成拒绝的工具响应
      ↓
更新State中的消息
      ↓
Graph继续执行

按原文中的实现,旧消息会被标记删除,再加入调整后的消息,避免后续继续执行未经批准的原始调用。

完整原理
复制代码
Model生成工具调用
        ↓
HumanInTheLoopHook.interrupt()
        ↓
发现受控工具,返回InterruptionMetadata
        ↓
Graph暂停并保存检查点
        ↓
外部系统展示工具名称、参数和审批说明
        ↓
用户批准、修改或拒绝
        ↓
相同threadId + 人工反馈恢复Graph
        ↓
interrupt()校验反馈并放行
        ↓
afterModel()调整工具调用
        ↓
执行批准后的工具,或向模型反馈拒绝结果
        ↓
Agent继续运行
重点总结
  1. HITL 是在 Agent 的关键执行节点引入人工审核,本质是流程控制。

  2. 审批发生在模型已生成工具调用、工具尚未执行的时候。

  3. 三个使用步骤:

    配置受控工具 → 接收中断 → 提交人工反馈并恢复

  4. approvalOn() 指定需要审批的工具,InterruptionMetadata 提供待审批的工具名称、参数和说明。

  5. 暂停和恢复依赖检查点及相同的 threadId;聊天记录本身不足以恢复执行流程。

  6. 两个核心方法:

    interrupt:
    判断是否暂停,并校验恢复时的人工反馈。

    afterModel:
    根据批准、修改、拒绝结果调整工具调用。

  7. 示例中的自动构造 APPROVED 只是演示,真实流程需要接入人工审批结果。

  8. Agent 恢复后仍可能再次中断,需要继续处理后续审批。

四. Agent常用架构:Multi-Agent

Multi-Agent 简介

Multi-Agent(多智能体)是将一个复杂任务交给多个具有不同职责的 Agent,通过分工与协作完成。

复制代码
复杂任务
   ↓
任务拆分
   ↓
多个专业Agent分别处理
   ↓
协调和汇总结果
   ↓
完成任务

例如:

复制代码
研究Agent → 搜集资料
写作Agent → 撰写报告
审核Agent → 检查内容

多个 Agent 可以使用同一个模型,也可以分别使用不同模型。区别主要体现在提示词、工具、上下文和职责上。

为什么需要多智能体

单个 Agent 承担过多职责时,可能出现:

  • 上下文过长,关键信息容易被忽略。
  • 工具太多,模型容易选错工具。
  • 任务不够聚焦,专业任务处理效果下降。
  • 独立子任务串行执行,整体耗时较长。
  • 无法针对不同任务灵活选择模型。

多智能体通过专业化分工、上下文隔离和任务编排缓解这些问题,但会增加调用成本和协调开销。

常见协作模式
模式 核心方式 谁负责控制流程
SubAgents 主 Agent 将子 Agent 当作工具调用 主 Agent
Handoff 当前 Agent 把任务和控制权交给另一个 Agent 当前接管的 Agent
Group Chat 多个 Agent 围绕共享消息协作 群聊管理器
自定义工作流 按业务规则组织串行、并行、条件和循环 工作流引擎

这些模式可以组合使用,不是互斥关系。

SubAgents:子智能体模式

主 Agent 将其他子 Agent 封装成工具,需要时调用它们,再接收结果并继续处理。

复制代码
用户
 ↓
主Agent
 ├── 调用研究Agent → 返回研究结果
 ├── 调用写作Agent → 返回文章
 └── 汇总结果
        ↓
     回复用户

职责划分:

  • 主 Agent:分配任务、选择子 Agent、组织最终结果。
  • 子 Agent:专注完成某一类任务,并向主 Agent 返回结果。

典型模式下,子 Agent 不直接与用户对话。主 Agent 可以只传入必要信息,避免每个子 Agent 都接收完整对话历史。

复制代码
主Agent的完整上下文
        ↓
提取子任务所需信息
        ↓
子Agent在独立上下文中执行
        ↓
返回精简结果

子 Agent 是否保存记忆取决于配置,并不是必须无状态。

Spring AI Alibaba 示例

以下沿用课程中的 API,chatModel 为项目已有模型实例。

复制代码
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.agent.AgentTool;

// 创建写作子Agent
ReactAgent writerAgent = ReactAgent.builder()
        .name("writer_agent")
        .model(chatModel)
        .description("根据主题撰写文章")
        .instruction("""
                你是一名作家。
                请根据收到的主题和要求完成写作,
                返回文章正文。
                """)
        .build();

// 主Agent将子Agent作为工具调用
ReactAgent blogAgent = ReactAgent.builder()
        .name("blog_agent")
        .model(chatModel)
        .instruction("""
                根据用户给定的主题完成文章。
                使用写作工具处理写作任务,
                根据结果回复用户。
                """)
        .tools(
                AgentTool.getFunctionToolCallback(writerAgent)
        )
        .build();

System.out.println(
        blogAgent.call("写一篇100字左右的秋日散文").getText()
);

核心是:

复制代码
AgentTool.getFunctionToolCallback(writerAgent)

它将子 Agent 转换成主 Agent 可调用的工具。

复制代码
主Agent决定调用工具
        ↓
实际执行子Agent
        ↓
子Agent返回结果
        ↓
主Agent继续处理
Handoff:交接模式

Handoff 是当前 Agent 发现另一个 Agent 更适合处理任务时,将任务及控制权转交过去。

复制代码
用户问题
   ↓
接待Agent
   ↓
识别为售后问题
   ↓
交接给售后Agent
   ↓
售后Agent继续处理

与 SubAgents 的主要区别:

复制代码
SubAgents:
调用子Agent → 子Agent返回结果 → 主Agent继续负责。

Handoff:
转交给另一个Agent → 新Agent接管后续流程。

交接时通常需要传递:

  • 用户目标
  • 已完成的工作
  • 必要的历史信息
  • 当前任务状态
  • 后续需要处理的问题

不一定要传递全部历史,重点是让接管方具备继续执行所需的信息。

Handoff 协作示例
复制代码
研究Agent
   ↓ 资料收集完成
写作Agent
   ↓ 初稿完成
审核Agent
   ├── 通过 → 结束
   └── 需要修改 → 交回写作Agent
                         ↓
                     修改后再次审核

交接也可以回到前面的 Agent,不一定是单向流程。

复制代码
写作Agent发现资料不足
        ↓
交回研究Agent补充资料
        ↓
再由写作Agent继续撰写

需要设置终止条件或最大轮次,避免反复交接。

Group Chat:群聊模式

多个 Agent 围绕共享的任务和消息进行协作,由群聊管理器决定下一位发言者。

复制代码
共享任务和消息
      ↓
群聊管理器选择下一位Agent
      ↓
被选中的Agent执行并发言
      ↓
结果加入共享消息
      ↓
管理器选择下一位或结束

例如:

复制代码
产品Agent:提出需求
技术Agent:分析实现方案
测试Agent:提出风险和测试建议
产品Agent:根据反馈调整需求

课程描述的是轮流发言的群聊模式,同一时刻由被选中的 Agent 工作。

与 Handoff 的区别:

复制代码
Handoff:
当前Agent决定交给谁。

Group Chat:
管理器决定接下来由谁处理。

共享消息有利于协作,但讨论轮次过多也可能导致上下文膨胀。

自定义工作流

当固定模式无法满足业务需求时,可以自行编排多个 Agent。

常用流程包括:

复制代码
串行:
研究Agent → 写作Agent → 审核Agent
并行:
       ┌→ 技术分析Agent ─┐
任务 ──┼→ 成本分析Agent ─┼→ 汇总Agent
       └→ 风险分析Agent ─┘
条件路由:
用户问题
   ├── 技术问题 → 技术Agent
   ├── 财务问题 → 财务Agent
   └── 售后问题 → 售后Agent
循环:
生成Agent → 审核Agent
    ↑          ↓
    └── 不通过 ┘
               ↓
             通过后结束

这些流程也可以结合 HITL,在关键步骤加入人工审核。

模式选择
需求 适合的模式
统一入口,由主 Agent 分派专业任务 SubAgents
不同专家接管不同阶段的任务 Handoff
多角色围绕同一任务讨论 Group Chat
流程固定,需要精确控制执行顺序 自定义工作流
多个子任务彼此独立 并行工作流
需要人工批准后继续 工作流结合 HITL
注意事项
  • 每个 Agent 的职责和输出要求应明确。
  • 传递必要上下文,避免反复复制全部历史。
  • 独立任务可以并行,有依赖的任务需要按顺序执行。
  • 设置最大轮次、超时和终止条件。
  • 共享状态需要明确由谁更新,避免结果相互覆盖。
  • 多 Agent 不一定更快、更准确,需要评估实际效果和成本。
重点总结
  1. 多智能体是在单智能体基础上增加任务分工与协作。

  2. 多个 Agent 不要求使用不同模型,可以通过不同提示词、工具和上下文实现专业化。

  3. 四种常见模式:

    SubAgents:主Agent调用子Agent,结果返回主Agent。
    Handoff:当前Agent将控制权交给另一个Agent。
    Group Chat:管理器决定下一个发言的Agent。
    自定义工作流:按业务规则编排多个Agent。

  4. SubAgents 的关键是"调用后返回",Handoff 的关键是"交接后接管"。

  5. 多智能体可以采用串行、并行、条件路由和循环,也可以加入人工审核。

  6. 多智能体增加了协调成本,应在任务足够复杂、分工能够带来收益时使用。

SubAgents和Group Chat的区别?

因为它们都有一个"负责人"选择接下来由谁工作,所以看起来很像。

区别主要在协作方式:

  • SubAgents 像领导派活:主 Agent 单独布置任务,子 Agent 做完向主 Agent 汇报。子 Agent 通常只看到自己的任务,不知道其他子 Agent 在做什么。

  • Group Chat 像开会讨论:协调者安排谁发言,各 Agent 能参考共享讨论记录,接着前面其他 Agent 的意见继续处理。

    SubAgents:
    主Agent → A做调研 → 结果交回主Agent
    主Agent → B写文章 → 结果交回主Agent

    Group Chat:
    协调者安排A发言 → A提出调研结果
    协调者安排B发言 → B根据A的结果写文章
    协调者安排C发言 → C根据前面的讨论提出修改意见

一个主要通过主 Agent 分发任务、收集结果;另一个主要通过共享讨论协作。 实际框架也可以混合这两种方式。

五. 使用AutoGen构建代码生成器

AutoGen 简介

AutoGen 是微软发起的开源多智能体框架,主要通过多个 Agent 的对话与协作完成复杂任务。

本例通过不同角色实现代码生成和验证:

复制代码
用户提出需求
      ↓
产品经理明确需求
      ↓
工程师编写代码
      ↓
审查员检查代码
      ↓
执行器运行测试
      ↓
根据结果修改或结束

本文按照课程中的经典 autogen.GroupChat API 整理,其他版本的接口可能不同。

智能体分工
智能体 作用
ProductManager 明确需求、检查功能是否符合要求
SoftwareEngineer 编写代码和测试用例
CodeReviewer 审查代码,结合执行结果判断是否通过
UserProxyAgent 发起任务、执行代码、反馈运行结果
GroupChatManager 协调群聊,选择下一位发言者

前三个角色使用 AssistantAgent 创建,通过不同的系统提示词区分职责。

GroupChat:定义群聊

GroupChat 保存参与者、共享消息和调度配置。

复制代码
groupchat = autogen.GroupChat(
    agents=[
        user_proxy,
        product_manager,
        software_engineer,
        code_reviewer,
    ],
    messages=[],
    max_round=12,
    speaker_selection_method="auto",
)

主要参数:

参数 含义
agents 参与群聊的 Agent
messages 群聊消息记录
max_round 最大群聊轮数
speaker_selection_method 下一位发言者的选择方式
allow_repeat_speaker 是否允许连续选择同一个 Agent
send_introductions 是否发送成员介绍

发言选择方式:

复制代码
auto        → 由模型根据上下文选择
round_robin → 按固定顺序轮流发言
random      → 随机选择
自定义函数  → 根据业务规则选择

auto 不保证一定按照"产品经理 → 工程师 → 审查员"的顺序执行。

GroupChatManager:协调群聊

GroupChatManager 使用群聊配置组织对话:

复制代码
接收消息
   ↓
根据策略选择下一位Agent
   ↓
传递上下文并触发响应
   ↓
收集新消息
   ↓
继续调度或结束

创建方式:

复制代码
manager = autogen.GroupChatManager(
    groupchat=groupchat,
    llm_config=llm_config,
)

区别:

复制代码
GroupChat:
保存成员、消息和调度规则。

GroupChatManager:
根据规则实际组织和推进对话。
模型配置

各角色可以共享同一个模型配置,通过系统提示词承担不同职责。

复制代码
import os

config_list = [
    {
        "model": os.environ["LLM_MODEL"],
        "api_key": os.environ["LLM_API_KEY"],
        "base_url": os.environ["LLM_BASE_URL"],
    }
]

llm_config = {
    "config_list": config_list
}

其中,模型名称、API Key 和接口地址需要与实际提供服务的平台对应。

创建专业 Agent
复制代码
import autogen

product_manager = autogen.AssistantAgent(
    name="ProductManager",
    system_message=(
        "你是一名产品经理。"
        "明确用户需求、边界条件和验收标准。"
        "不要凭空增加用户未要求的功能。"
    ),
    llm_config=llm_config,
)

software_engineer = autogen.AssistantAgent(
    name="SoftwareEngineer",
    system_message=(
        "你是一名Python工程师。"
        "根据需求编写完整可执行代码和测试用例。"
        "使用python代码块输出代码。"
        "收到错误反馈后修改代码并重新提交验证。"
    ),
    llm_config=llm_config,
)

code_reviewer = autogen.AssistantAgent(
    name="CodeReviewer",
    system_message=(
        "你是一名代码审查员。"
        "检查代码是否满足需求,是否存在明显错误。"
        "未执行测试时,不要宣称代码已通过验证。"
        "代码可供验证时,请User执行完整代码和测试。"
        "若执行失败,要求SoftwareEngineer修改。"
        "只有执行结果符合验收要求时,"
        "才单独回复TERMINATE。"
    ),
    llm_config=llm_config,
)

仅靠审查员阅读代码不等于测试通过,需要执行环境提供实际结果。

UserProxyAgent:执行代码

课程示例中的 UserProxyAgent 同时负责发起任务和运行生成的代码。

复制代码
user_proxy = autogen.UserProxyAgent(
    name="User",
    human_input_mode="NEVER",
    max_consecutive_auto_reply=10,
    code_execution_config={
        "work_dir": "coding",
        "use_docker": False,
    },
    is_termination_msg=lambda message: (
        (message.get("content") or "").strip()
        == "TERMINATE"
    ),
)

参数说明:

  • human_input_mode="NEVER":运行时不等待人工输入。
  • max_consecutive_auto_reply=10:限制连续自动回复次数。
  • work_dir="coding":代码执行工作目录。
  • use_docker=False:在本机执行,没有 Docker 隔离。
  • is_termination_msg:判断消息是否为终止信号。

本例配置会执行模型生成的代码,适合受控的演示环境;需要隔离执行时应配置对应的执行环境。

组建群聊并启动任务
复制代码
groupchat = autogen.GroupChat(
    agents=[
        user_proxy,
        product_manager,
        software_engineer,
        code_reviewer,
    ],
    messages=[],
    max_round=12,
    speaker_selection_method="auto",
)

manager = autogen.GroupChatManager(
    groupchat=groupchat,
    llm_config=llm_config,
)

user_proxy.initiate_chat(
    manager,
    message=(
        "请实现一个Python函数:"
        "输入字符串列表,返回最长的字符串。"
        "如果多个字符串长度相同,返回第一个。"
        "空列表应抛出ValueError。"
        "请提供完整代码,并使用断言测试正常情况、"
        "相同长度情况和空列表情况。"
        "审查后由User执行验证,确认通过后再结束。"
    ),
)

运行已有兼容依赖环境中的脚本:

复制代码
uv run main.py
生成代码示例
复制代码
def find_longest_string(strings):
    if not strings:
        raise ValueError("Input list cannot be empty")

    longest = strings[0]

    for item in strings:
        if len(item) > len(longest):
            longest = item

    return longest


if __name__ == "__main__":
    # 正常情况
    assert find_longest_string(
        ["apple", "hi", "banana", "cat"]
    ) == "banana"

    # 长度相同时,保留第一个
    assert find_longest_string(
        ["apple", "banana", "orange"]
    ) == "banana"

    # 空列表应抛出异常
    try:
        find_longest_string([])
    except ValueError:
        pass
    else:
        raise AssertionError("空列表没有抛出ValueError")

    print("所有测试通过")

使用 > 而不是 >=,可以保留第一个最长字符串:

复制代码
if len(item) > len(longest):
    longest = item
执行与反馈闭环
复制代码
SoftwareEngineer生成代码
          ↓
CodeReviewer审查
   ├── 有问题 → 返回工程师修改
   └── 可验证 → UserProxy执行
                       ↓
                  返回执行结果
                       ↓
                 审查结果是否符合要求
                 ├── 否 → 修改并重试
                 └── 是 → TERMINATE

执行器返回的信息通常包括:

复制代码
exitcode: 0
Code output:
所有测试通过

退出码为 0 只说明程序没有以错误状态退出,是否符合需求还需要结合测试断言和输出判断。

终止条件

本例有两类结束条件:

复制代码
任务通过验证 → 返回TERMINATE
达到最大轮数或自动回复限制 → 停止继续对话

达到轮次上限不等于任务成功,需要区分"验证通过"和"因限制停止"。

提示词中的"由谁宣布通过"属于行为约定;如果业务必须严格控制结束条件,应增加确定性的流程判断。

重点总结
  1. AutoGen 通过多个角色共享消息、轮流协作,实现代码生成任务。

  2. 各角色分工:

    产品经理 → 明确需求
    工程师 → 编写代码
    审查员 → 检查代码和验证结果
    用户代理 → 执行代码并反馈结果
    管理器 → 调度下一位发言者

  3. GroupChat 定义群聊配置,GroupChatManager 负责推进对话。

  4. speaker_selection_method="auto" 表示由模型选择下一位发言者,不保证固定顺序。

  5. UserProxyAgent 将生成代码接入执行环境,形成"生成 → 执行 → 反馈 → 修改"的闭环。

  6. use_docker=False 表示本机执行,不具备 Docker 隔离。

  7. 代码看起来正确、程序正常退出,都不能代替测试和需求验收。

  8. TERMINATE 用于主动结束,最大轮数用于防止对话无限进行。

六. Spring AI Alibaba中的多智能体支持

核心思路

Spring AI Alibaba 可以通过两种方式组织多个 Agent:

  • Agent Tool:将子 Agent 包装成工具,由主 Agent 调用。(就是subAgent!!!)
  • FlowAgent:通过 Graph 编排多个 Agent,实现顺序、并行、路由和监督等流程。(就是Hand Off!!!)

课程将后一类放在 Handoff 下介绍,但严格区分时,顺序和并行属于工作流编排,不一定是 Agent 自主决定交接控制权。

以下代码按课程中的 API 写法整理,chatModel 为项目已有模型实例。

Agent Tool:将Agent作为工具

核心方法:

复制代码
AgentTool.getFunctionToolCallback(agent)

作用:

复制代码
ReactAgent
    ↓
包装成ToolCallback
    ↓
注册到主Agent的tools中
    ↓
主Agent根据用户需求调用
创建专业子Agent
复制代码
// 写作Agent
ReactAgent writerAgent = ReactAgent.builder()
        .name("writer_agent")
        .model(chatModel)
        .description("专门负责文章创作和内容生成")
        .instruction("你是一名专业作家,擅长各类文章创作。")
        .build();

// 翻译Agent
ReactAgent translatorAgent = ReactAgent.builder()
        .name("translator_agent")
        .model(chatModel)
        .description("专门负责文本翻译")
        .instruction("你是一名专业翻译,请准确翻译收到的文本。")
        .build();

// 总结Agent
ReactAgent summarizerAgent = ReactAgent.builder()
        .name("summarizer_agent")
        .model(chatModel)
        .description("专门负责内容总结和关键信息提炼")
        .instruction("你是一名总结专家,请提炼收到内容的核心信息。")
        .build();

其中:

  • name:Agent 名称。
  • description:说明能力,帮助主 Agent 选择工具。
  • instruction:指导子 Agent 如何完成任务。
创建主Agent
复制代码
ReactAgent multiToolAgent = ReactAgent.builder()
        .name("multi_tool_coordinator")
        .model(chatModel)
        .instruction("""
                你可以使用写作、翻译和总结工具。
                根据用户需求选择工具,并将前一步的结果
                作为后一步所需的输入,完成整个任务。
                """)
        .tools(
                AgentTool.getFunctionToolCallback(writerAgent),
                AgentTool.getFunctionToolCallback(translatorAgent),
                AgentTool.getFunctionToolCallback(summarizerAgent)
        )
        .build();

Optional<OverAllState> result = multiToolAgent.invoke(
        "请写一篇关于AI的文章,然后翻译成英文,最后给出摘要"
);

典型执行流程:

复制代码
用户需求
   ↓
主Agent调用写作工具
   ↓
writerAgent返回文章
   ↓
主Agent调用翻译工具
   ↓
translatorAgent返回英文
   ↓
主Agent调用总结工具
   ↓
summarizerAgent返回摘要
   ↓
主Agent组织最终回答

工具调用顺序由主 Agent 决定;如果必须严格保证顺序,可以使用顺序编排。

Agent Tool 实现原理

按课程源码,getFunctionToolCallback() 使用 MethodToolCallback 包装子 Agent 的执行方法。

复制代码
ReactAgent
   ↓
提取Agent名称和描述
   ↓
创建MethodToolCallback
   ↓
绑定AgentToolExecutor.executeAgent()
   ↓
主Agent调用该工具
   ↓
executeAgent()调用子Agent底层Graph.invoke()
   ↓
执行结果返回主Agent

本质上仍然是调用子 Agent,只是将调用入口转换成模型能够选择的工具。

FlowAgent:多Agent流程编排

FlowAgent 将多个 Agent 组合成一个执行流程。

课程介绍了四种实现:

类型 调度方式 适用场景
SequentialAgent 按预定顺序执行 写作后审核、翻译后总结
ParallelAgent 多个子 Agent 并行执行 多角度分析、独立任务
LlmRoutingAgent 模型选择一个子 Agent 按问题类型分流
SupervisorAgent 监督者反复选择下一步 动态、多步骤协作
SequentialAgent:顺序执行

多个 Agent 按预先定义的顺序依次执行。

复制代码
输入 → 写作Agent → 审核Agent → 输出
ReactAgent reviewerAgent = ReactAgent.builder()
        .name("reviewer_agent")
        .model(chatModel)
        .description("审核文章并给出修改建议")
        .instruction("请检查文章的逻辑、表达和内容质量。")
        .build();

SequentialAgent blogAgent = SequentialAgent.builder()
        .name("blog_agent")
        .description("先写文章,再交给审核Agent检查")
        .subAgents(List.of(writerAgent, reviewerAgent))
        .build();

关键点:

  • 执行顺序由 subAgents 列表确定。
  • 后续 Agent 使用前面产生的结果。
  • 实际输入、输出的传递需要结合框架的状态字段配置。
ParallelAgent:并行执行

多个 Agent 并行处理任务,再收集并合并结果。

复制代码
          ┌→ 散文Agent ─┐
输入 ─────┼→ 诗歌Agent ─┼→ 合并结果 → 输出
          └→ 总结Agent ─┘
// proseWriterAgent、poemWriterAgent、summaryAgent需提前创建
ParallelAgent parallelAgent = ParallelAgent.builder()
        .name("parallel_creative_agent")
        .description("并行执行散文、诗歌和总结任务")
        .mergeOutputKey("merged_results")
        .subAgents(
                List.of(
                        proseWriterAgent,
                        poemWriterAgent,
                        summaryAgent
                )
        )
        .mergeStrategy(
                new ParallelAgent.DefaultMergeStrategy()
        )
        .build();

参数说明:

  • subAgents:参与并行执行的 Agent。
  • mergeStrategy:合并各个 Agent 的输出。
  • mergeOutputKey:合并结果保存到状态中的字段名。

并行适合彼此独立的任务。如果翻译必须等待文章生成,就不适合将这两步直接并行。

LlmRoutingAgent:路由选择

由模型根据用户需求选择合适的子 Agent。

复制代码
用户问题
   ↓
路由Agent
   ├── 写作需求 → writerAgent
   ├── 审核需求 → reviewerAgent
   └── 翻译需求 → translatorAgent
                      ↓
                    输出
LlmRoutingAgent routingAgent = LlmRoutingAgent.builder()
        .name("content_routing_agent")
        .description("根据用户需求选择合适的专家Agent")
        .model(chatModel)
        .subAgents(
                List.of(
                        writerAgent,
                        reviewerAgent,
                        translatorAgent
                )
        )
        .build();

子 Agent 的描述需要清晰,便于路由模型判断职责。

SupervisorAgent:监督者协调

监督者根据当前任务和执行结果,动态选择下一个 Agent。

子 Agent 执行完成后返回监督者,由监督者决定继续分配任务还是结束。

复制代码
用户任务
   ↓
监督者
   ├── 选择写作Agent → 返回文章 ─┐
   ├── 选择翻译Agent → 返回译文 ─┤
   ↑                            │
   └──────── 接收结果 ──────────┘
   ↓
判断任务完成 → 输出
SupervisorAgent supervisorAgent = SupervisorAgent.builder()
        .name("content_supervisor")
        .description("协调写作和翻译,直到完成用户任务")
        .model(chatModel)
        .subAgents(
                List.of(writerAgent, translatorAgent)
        )
        .build();

与路由模式的区别:

复制代码
LlmRoutingAgent:
选择一个合适的Agent处理请求。

SupervisorAgent:
选择Agent → 接收结果 → 再次决定下一步。
Agent Tool和Supervisor的区别

两者都可以由一个主控角色协调多个子 Agent,区别主要在组织方式:

复制代码
Agent Tool:
将子Agent包装成工具,通过工具调用机制执行。

SupervisorAgent:
将子Agent纳入多Agent流程,由监督者进行路由调度。

不能仅通过"有一个主 Agent"来区分,需要看底层使用的是工具调用还是流程路由。

底层实现:Graph

这些编排方式最终都依靠 Graph 的节点和边实现。

复制代码
Agent → 节点或子图
执行顺序 → 普通边
动态选择 → 条件边
中间结果 → 共享状态

顺序模式:

复制代码
START → Agent A → Agent B → END

并行模式:

复制代码
         ┌→ Agent A ─┐
START ───┤           ├→ 合并 → END
         └→ Agent B ─┘

路由模式:

复制代码
START → 路由判断
           ├→ Agent A → END
           └→ Agent B → END

监督者模式:

复制代码
START → Supervisor → 子Agent
             ↑          ↓
             └── 结果 ──┘
             ↓
            END

因此,多智能体编排主要是在构建不同的图结构,再交给 Graph 引擎执行。

代码编写流程
复制代码
准备ChatModel
      ↓
创建各个专业子Agent
配置name、description、instruction、tools
      ↓
选择协作方式
   ├── Agent Tool → 包装成工具,注册到主Agent
   └── FlowAgent  → 配置subAgents和编排方式
      ↓
调用组合后的Agent
      ↓
获取最终结果
重点总结
  1. Agent Tool 将子 Agent 转换成工具:

    AgentTool.getFunctionToolCallback(childAgent)

  2. 工具执行时,最终会调用子 Agent 的 Graph,结果再返回主 Agent。

  3. 四种流程编排:

    SequentialAgent → 按顺序执行
    ParallelAgent → 并行执行并合并
    LlmRoutingAgent → 选择合适的子Agent
    SupervisorAgent → 根据结果循环调度

  4. 路由模式侧重一次选择,监督者模式支持多步骤协调。

  5. 顺序、并行等是编排方式,不应全部理解为"当前 Agent 自主交接控制权"。

  6. 底层原理一致:将 Agent 组织成节点或子图,通过边定义执行顺序,并使用状态传递结果。

相关推荐
程序员老刘3 小时前
Android Studio Quail 4发布,看日志我以为谷歌放弃Flutter了
flutter·android studio·ai编程
VIP_CQCRE3 小时前
在 Visual Studio 里接入 Ace Data Cloud:用 OpenAI 兼容接口提升 AI 编程效率
openai·api·ai编程·visual studio·ace data cloud
Young丶4 小时前
讲透 Claude Code 系列 (四):Claude Skills 完全指南:可复用的“专业能力包”从入门到精通
人工智能·ai·ai编程·ai coding
Crazy_MT4 小时前
Flutter 本地大模型实战:做一个自然语言记账工具
flutter·llm·ai编程
全栈弄潮儿4 小时前
用 AI 拆一个真实需求:从模糊描述到开发任务清单
aigc·openai·ai编程
小狼154546 小时前
浏览器插件怎样实现可靠的批量网页操作?以发票申请任务为例
chrome·ai编程
小狼154547 小时前
拼多多订单多时如何批量申请发票:筛选、提交和补漏方法
chrome·ai编程
plainGeekDev8 小时前
Harness Engineering 入门:Agent = Model + Harness
aigc·ai编程·claude
可以想象8 小时前
Agent 基础设施比模型能力更卷了?从本周三大旗舰更新看 API 设计的新范式
aigc·ai编程