Spring AI Alibaba 项目源码学习(七)-Agent、BaseAgent、ReactAgent 分析

news/2025/11/15 23:20:36/文章来源:https://www.cnblogs.com/wasp520/p/19226501

Agent、BaseAgent、ReactAgent 分析

请关注微信公众号:阿呆-bot

概述

本文档分析 Spring AI Alibaba Agent Framework 中的核心 Agent 类层次结构,包括 Agent 基类、BaseAgent 抽象类和 ReactAgent 具体实现,重点分析其关键方法、功能职责和类间关系。

入口类说明

Agent - 所有 Agent 的基类

Agent 是所有 Agent 实现的抽象基类,提供了 Agent 的通用能力和 Graph 管理功能。

核心职责

  • 管理 Agent 的名称和描述
  • 提供 Graph 的初始化和编译
  • 提供统一的调用接口(invoke、stream)
  • 支持调度执行

关键代码

public abstract class Agent {/** The agent's name. Must be a unique identifier within the graph. */protected String name;/*** One line description about the agent's capability. The system can use this for* decision-making when delegating control to different agents.*/protected String description;protected CompileConfig compileConfig;protected volatile CompiledGraph compiledGraph;protected volatile StateGraph graph;/*** Protected constructor for initializing all base agent properties.* @param name the unique name of the agent* @param description the description of the agent's capability*/protected Agent(String name, String description) throws GraphStateException {this.name = name;this.description = description;}/*** Default protected constructor for subclasses that need to initialize properties* differently.*/protected Agent() {// Allow subclasses to initialize properties through other means}/*** Gets the agent's unique name.* @return the unique name of the agent.*/public String name() {return name;}/*** Gets the one-line description of the agent's capability.* @return the description of the agent.*/public String description() {return description;}public StateGraph getGraph() {if (this.graph == null) {try {this.graph = initGraph();}catch (GraphStateException e) {throw new RuntimeException(e);}}return this.graph;}public synchronized CompiledGraph getAndCompileGraph() {if (compiledGraph != null) {return compiledGraph;}StateGraph graph = getGraph();try {if (this.compileConfig == null) {this.compiledGraph = graph.compile();}else {this.compiledGraph = graph.compile(this.compileConfig);}} catch (GraphStateException e) {throw new RuntimeException(e);}return this.compiledGraph;}

关键方法

  • initGraph():抽象方法,子类实现以创建状态图
  • getAndCompileGraph():获取并编译 Graph(线程安全)
  • invoke():同步调用 Agent
  • stream():流式调用 Agent

BaseAgent - Agent 的抽象扩展

BaseAgent 继承自 Agent,提供了输入输出管理和节点转换能力。

核心职责

  • 管理输入输出 Schema 和类型
  • 管理输出键和键策略
  • 提供 asNode() 方法将 Agent 转换为 Graph 节点

关键代码

public abstract class BaseAgent extends Agent {protected String inputSchema;protected Type inputType;protected String outputSchema;protected Class<?> outputType;/** The output key for the agent's result */protected String outputKey;protected KeyStrategy outputKeyStrategy;protected boolean includeContents;protected boolean returnReasoningContents;public BaseAgent(String name, String description, boolean includeContents, boolean returnReasoningContents, String outputKey,KeyStrategy outputKeyStrategy) throws GraphStateException {super(name, description);this.includeContents = includeContents;this.returnReasoningContents = returnReasoningContents;this.outputKey = outputKey;this.outputKeyStrategy = outputKeyStrategy;}public abstract Node asNode(boolean includeContents, boolean returnReasoningContents, String outputKeyToParent);public boolean isIncludeContents() {return includeContents;}public String getOutputKey() {return outputKey;}public void setOutputKey(String outputKey) {this.outputKey = outputKey;}public KeyStrategy getOutputKeyStrategy() {return outputKeyStrategy;}public void setOutputKeyStrategy(KeyStrategy outputKeyStrategy) {this.outputKeyStrategy = outputKeyStrategy;}String getInputSchema() {return inputSchema;}void setInputSchema(String inputSchema) {this.inputSchema = inputSchema;}Type getInputType() {return inputType;}void setInputType(Type inputType) {this.inputType = inputType;}String getOutputSchema() {return outputSchema;}void setOutputSchema(String outputSchema) {this.outputSchema = outputSchema;}void setIncludeContents(boolean includeContents) {this.includeContents = includeContents;}public boolean isReturnReasoningContents() {return returnReasoningContents;}public void setReturnReasoningContents(boolean returnReasoningContents) {this.returnReasoningContents = returnReasoningContents;}
}

关键方法

  • asNode():抽象方法,将 Agent 转换为 Graph 节点,用于嵌套 Agent

ReactAgent - ReAct 模式实现

ReactAgentBaseAgent 的具体实现,实现了经典的 ReAct(Reasoning + Acting)模式。

核心职责

  • 实现 ReAct 循环:推理 → 行动 → 观察
  • 管理 LLM 节点和工具节点
  • 支持 Hook 和 Interceptor 扩展
  • 控制最大迭代次数

关键代码

public class ReactAgent extends BaseAgent {private static final int DEFAULT_MAX_ITERATIONS = 10;private final AgentLlmNode llmNode;private final AgentToolNode toolNode;private CompiledGraph compiledGraph;private List<Hook> hooks;private List<ModelInterceptor> modelInterceptors;private List<ToolInterceptor> toolInterceptors;private int maxIterations;private int iterations = 0;private String instruction;private Function<OverAllState, Boolean> shouldContinueFunc;public ReactAgent(AgentLlmNode llmNode, AgentToolNode toolNode, CompileConfig compileConfig, Builder builder) throws GraphStateException {super(builder.name, builder.description, builder.includeContents, builder.returnReasoningContents, builder.outputKey, builder.outputKeyStrategy);this.instruction = builder.instruction;this.llmNode = llmNode;this.toolNode = toolNode;this.compileConfig = compileConfig;this.shouldContinueFunc = builder.shouldContinueFunc;this.hooks = builder.hooks;this.modelInterceptors = builder.modelInterceptors;this.toolInterceptors = builder.toolInterceptors;this.includeContents = builder.includeContents;this.inputSchema = builder.inputSchema;this.inputType = builder.inputType;this.outputSchema = builder.outputSchema;this.outputType = builder.outputType;this.maxIterations = builder.maxIterations <= 0 ? DEFAULT_MAX_ITERATIONS : builder.maxIterations;// Set interceptors to nodesif (this.modelInterceptors != null && !this.modelInterceptors.isEmpty()) {this.llmNode.setModelInterceptors(this.modelInterceptors);}if (this.toolInterceptors != null && !this.toolInterceptors.isEmpty()) {this.toolNode.setToolInterceptors(this.toolInterceptors);}}

Graph 初始化

@Overrideprotected StateGraph initGraph() throws GraphStateException {KeyStrategyFactory keyStrategyFactory = buildMessagesKeyStrategyFactory();if (hooks == null) {hooks = new ArrayList<>();}// Validate hook uniquenessSet<String> hookNames = new HashSet<>();for (Hook hook : hooks) {if (!hookNames.add(hook.getName())) {throw new IllegalArgumentException("Duplicate hook instances found");}}// Create graphStateGraph graph = new StateGraph(name, keyStrategyFactory);graph.addNode("model", node_async(this.llmNode));graph.addNode("tool", node_async(this.toolNode));// some hooks may need tools so they can do some initialization/cleanup on start/end of agent loopsetupToolsForHooks(hooks, toolNode);// Add hook nodesfor (Hook hook : hooks) {if (hook instanceof AgentHook agentHook) {graph.addNode(hook.getName() + ".before", agentHook::beforeAgent);graph.addNode(hook.getName() + ".after", agentHook::afterAgent);} else if (hook instanceof ModelHook modelHook) {graph.addNode(hook.getName() + ".beforeModel", modelHook::beforeModel);if (modelHook instanceof HumanInTheLoopHook humanInTheLoopHook) {graph.addNode(hook.getName() + ".afterModel", humanInTheLoopHook);} else {graph.addNode(hook.getName() + ".afterModel", modelHook::afterModel);}}else {throw new UnsupportedOperationException("Unsupported hook type: " + hook.getClass().getName());}}// Categorize hooks by positionList<Hook> beforeAgentHooks = filterHooksByPosition(hooks, HookPosition.BEFORE_AGENT);List<Hook> afterAgentHooks = filterHooksByPosition(hooks, HookPosition.AFTER_AGENT);List<Hook> beforeModelHooks = filterHooksByPosition(hooks, HookPosition.BEFORE_MODEL);List<Hook> afterModelHooks = filterHooksByPosition(hooks, HookPosition.AFTER_MODEL);// Determine node flowString entryNode = determineEntryNode(beforeAgentHooks, beforeModelHooks);String loopEntryNode = determineLoopEntryNode(beforeModelHooks);String loopExitNode = determineLoopExitNode(afterModelHooks);String exitNode = determineExitNode(afterAgentHooks);// Set up edgesgraph.addEdge(START, entryNode);setupHookEdges(graph, beforeAgentHooks, afterAgentHooks, beforeModelHooks, afterModelHooks,entryNode, loopEntryNode, loopExitNode, exitNode, true, this);return graph;}

关键类关系

以下 PlantUML 类图展示了 Agent 类层次结构:
image.png

关键流程

以下 PlantUML 时序图展示了 ReactAgent 的执行流程:
image.png

实现关键点说明

1. 模板方法模式

Agent 使用模板方法模式:

  • initGraph() 是抽象方法,子类实现具体的图构建逻辑
  • getAndCompileGraph() 是模板方法,定义了 Graph 的获取和编译流程

2. Builder 模式

ReactAgent 使用 Builder 模式构建:

  • Builder 类提供流畅的 API
  • 支持链式调用配置各种参数
  • build() 时创建 ReactAgent 实例

3. ReAct 循环实现

ReactAgent 通过 Graph 实现 ReAct 循环:

  • model 节点:LLM 推理,决定下一步行动
  • tool 节点:执行工具调用
  • 条件边:根据 LLM 输出决定是否继续循环

4. Hook 集成机制

Hook 被集成到 Graph 中:

  • AgentHook:在 Agent 执行前后插入节点
  • ModelHook:在模型调用前后插入节点
  • 通过 HookPosition 控制执行时机

5. Interceptor 链式调用

Interceptor 通过链式调用实现:

  • ModelInterceptor:拦截模型调用请求和响应
  • ToolInterceptor:拦截工具调用请求和响应
  • 支持请求修改和响应处理

6. 状态管理

使用 OverAllState 管理状态:

  • messages 键存储对话历史
  • input 键存储用户输入
  • 支持键策略控制状态更新方式

总结说明

核心设计理念

  1. 分层抽象:Agent → BaseAgent → ReactAgent 三层设计,职责清晰
  2. ReAct 模式:通过 Graph 实现推理-行动循环
  3. 可扩展性:通过 Hook 和 Interceptor 机制支持扩展
  4. 状态驱动:基于状态图的状态驱动执行

关键优势

  • 灵活性:支持多种调用方式(同步、异步、流式)
  • 可扩展性:Hook 和 Interceptor 机制支持功能扩展
  • 可组合性:通过 asNode() 方法支持 Agent 嵌套
  • 类型安全:支持输入输出 Schema 和类型定义

使用场景

  • 单 Agent 应用:直接使用 ReactAgent 构建单 Agent 应用
  • 多 Agent 编排:通过 asNode() 方法将 ReactAgent 转换为节点,用于 FlowAgent 编排
  • 嵌套 Agent:支持 Agent 嵌套,实现复杂的 Agent 层次结构

关键方法说明

Agent 基类方法

  • initGraph():抽象方法,子类必须实现,用于创建状态图
  • getAndCompileGraph():获取并编译 Graph,使用双重检查锁定确保线程安全
  • invoke():同步调用 Agent,返回 Optional<OverAllState>
  • stream():流式调用 Agent,返回 Flux<NodeOutput>

BaseAgent 方法

  • asNode():抽象方法,将 Agent 转换为 Graph 节点,用于嵌套场景
  • getOutputKey():获取输出键,用于从状态中提取结果
  • getOutputKeyStrategy():获取输出键策略,控制状态更新方式

ReactAgent 方法

  • call():同步调用,返回 AssistantMessage
  • initGraph():创建 ReAct 循环的状态图
  • setupToolsForHooks():为 Hook 注入工具
  • filterHooksByPosition():根据位置过滤 Hook

Agent Framework 通过这种层次化的设计,实现了灵活、可扩展的 Agent 开发框架,为构建智能应用提供了坚实的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/966573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AtCoder Beginner Contest 432 ABCDEG 题目解析

A - Permute to Maximize 题意 给定三个个位数 \(A,B,C\)。 请找出将 \(A,B,C\) 按任意顺序排列并连接起来可以形成的所有三位数中的最大值。 思路 贪心可知,当数字长度一定时,可以优先让高位更大。因此对三个正整数…

fireworks

fireworks https://github.com/materialsproject/fireworks FireWorks stores, executes, and manages calculation workflows.Website (including documentation): https://materialsproject.github.io/fireworks/ He…

KEYDIY KD ZB28-3 Universal Hyundai Smart Remote Key (5pcs/lot) – Reliable Replacement

## Hyundai Smart Key Woes? Meet the KEYDIY KD ZB28-3 Universal Solution ### Problem: The Frustration of Hyundai Smart Key Replacement When a Hyundai owner’s smart remote key fails, or a mechanic need…

Yanhua Mini ACDP-2 A303 Volvo 2022+ IMMO License for ACDP-2 Module20

**Tackling Modern Volvo IMMO Challenges: The Yanhua Mini ACDP-2 A303 License** Modern Volvo vehicles (2022 and newer) are equipped with advanced Immobilizer (IMMO) systems designed to enhance security.…

西电TIC带鱼杯新生训练赛复盘

传送门 A 最大子树和 P1122 最大子树和 - 洛谷几天前看过一眼,大概知道思路,但是因为忘记ans可能取负而没有一次切掉分析题目要求一个节点带权的树的最大子树和我们用 f [ i ]记录子树以节点 i 为根节点时的最大子树…

20251115 - 从零到1详细剖析STM32的CAN架构【以STM32F407为例】

从零到1详细剖析STM32的CAN架构【以STM32F407为例】 1 概览:bxCAN 在 STM32F407 中的位置与作用bxCAN(Basic extended CAN) 是 STM32F4 系列内部实现的 CAN 控制器硬件 IP,用来在物理 CAN 差分总线上收/发 CAN 帧(…

2025.11.15 测试

2025.11.15 测试改题 策略 7:10 顺序开题 T1 随便猜了个结论然后不小心过了大杨利,还好这种题我有经验,没有被吓到 7:49 写完 full_speed 说这个是模板快速读入 因为他写快读但没用,T 到 35pt。。。 T2 额,一眼数…

鸿蒙应用审核被拒?常见原因与避坑指南来了

一个个精心开发的应用,却总在审核关卡折戟,问题究竟出在哪里?鸿蒙生态正迎来前所未有的爆发期。数据显示,HarmonyOS 5 终端数已突破 2300 万,鸿蒙开发者数量超过 800 万,上架应用及元服务超过 3 万款。 在“鸿蒙…

C++篇(13)计算器实现 - 指南

C++篇(13)计算器实现 - 指南2025-11-15 22:58 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importan…

20232306 2025-2026-1 《网络与系统攻防技术》实验五实验报告

1.实验要求 (1)从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取如下信息: DNS注册人及联系方式 该域名对应IP地址 IP地址注册人及联系方式 IP地址所在国家、城市和具体地理位置 PS:使…

ABC432E sol

eazy ds problem. 题意 给你一个序列 $a$,需要支持单点加 & 全局求 $\max\left(l,\min(r,a_i)\right)$(也就是对于每个 $a_i$,当 $a_i<l$,造成 $l$ 的贡献;当 $a_i \ge r$ 时,造成 $r$ 的贡献;否则造成 …

完整教程:linux离线环境局域网远程ssh连接vscode

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025 CSP-S 趋势记

“去年 CSP-S 就没过初赛,今年呢?” 2025 CSP-S 寄。 T1 60min才发现可以反悔贪心,跑完大样例觉得是对的然后有个等号少判了就 100 -> 65。 T2 连正解都没想出来,只会 \(O(2^k \times (m + nk) + (m + nk) \log…

鲜花:记梦4

“你考虑得怎么样了?” 对面的声音打断了我的思考。我抬头一看,居然是,她。 我竭力克制住我的情绪变化,把悸动深深藏在了心底。 她递给我一个信封:“如果考虑好了的话,就在里面的合同上签字吧。”我连忙伸手去接…

第26天(简单题中等题 二分查找、贪心算法)

打卡第二十六天 1道简单题+2道中等题题目:思路: 贪心+二分查找,维护一个"潜在的最优递增序列集合",让每个长度的递增子序列的末尾元素尽可能小,从而为后续元素提供更多的增长可能性。代码: class Solution…

invalid literal for int() with base 10: abc中的base 10是什么意思? 另外它是怎么知道abc的?

在 Python 错误信息 invalid literal for int() with base 10: abc 中,涉及两个关键问题:base 10 的含义,以及程序如何知道错误输入是 abc。下面分别详细解释: 一、base 10 是什么意思? base 在这里表示“进制”,…

byd秘钥 - MKT

byd秘钥 https://rcorex.github.io/nuttyb-config/

NSubstitute之Substitute.ForT

_currentUser = Substitute.For<ICurrentUser>(); 这是 NSubstitute(一个 .NET 单元测试 mock 框架)中的标准用法,用来“创建一个假的 ICurrentUser 实例”。 下面详细解释它的逻辑。✅ 到底发生了什么? Sub…

DAY1 JAVA PreLearning

day1 markdown leaning font 1.set the title: # title---first class subject,## title---second class subject and so on 2.set the text to bold: text 3.set the text to italic: text 4.perform like deleting …