Ai Agent 智能体笔记

什么是 AI Agent 智能体

AI 智能体是使用 AI 来实现目标并代表用户完成任务的软件系统。其表现出了推理、规划和记忆能力,并且具有一定的自主性,能够自主学习、适应和做出决定。

Spring AI 框架,支持大语言模型构建 AI Agent 实现。AIAgent是整合多种技术手段的智能实体 ,其实现依赖于 Tools、MCP、Memory、RAG(Retrieval 增强检索生成) 等技术组件,但不是非得依赖全部组件才叫 AI Agent。

AI Agents的定义与类型

AI Agents 是系统 ,它通过赋予 大型语言模型(LLMs) 访问工具 和知识 来扩展其能力,从而使 LLMs 能够执行操作。

  • 系统:将 Agents 视为一个由许多组件组成的系统,而不仅仅是单个组件,这一点很重要。在基本层面上,AI Agent 的组件包括:
    • 环境 - AI Agent 运行的定义空间。例如,如果我们有一个旅行预订 AI Agent,则环境可以是 AI Agent 用来完成任务的旅行预订系统。
    • 传感器 - 环境具有信息并提供反馈。AI Agents 使用传感器来收集和解释有关环境当前状态的信息。在旅行预订 Agent 示例中,旅行预订系统可以提供诸如酒店可用性或航班价格之类的信息。
    • 执行器 - 一旦 AI Agent 接收到环境的当前状态,对于当前任务,Agent 会确定要执行的操作以更改环境。对于旅行预订 Agent,它可能是为用户预订可用房间。
  • 大型语言模型 - Agents 的概念在 LLMs 创建之前就已存在。使用 LLMs 构建 AI Agents 的优势在于它们能够解释人类语言和数据。这种能力使 LLMs 能够解释环境信息并制定改变环境的计划。
  • 执行操作 - 在 AI Agent 系统之外,LLMs 仅限于根据用户提示生成内容或信息的情况。在 AI Agent 系统内部,LLMs 可以通过解释用户请求并使用其环境中可用的工具来完成任务。
  • 访问工具 - LLM 可以访问哪些工具由 1) 它运行的环境和 2) AI Agent 的开发者定义。对于我们的旅行 Agent 示例,Agent 的工具受预订系统中可用操作的限制,开发者可以将 Agent 的工具访问权限限制为航班。
  • 知识 - 除了环境提供的信息外,AI Agents 还可以从其他系统、服务、工具甚至其他 Agents 中检索知识。在旅行 Agent 示例中,此知识可以是位于客户数据库中的用户旅行偏好信息。

【类型】

简单反射、基于模型的反射、基于目标、基于效用、学习、分层、多Agent系统

AI 智能体、AI 助理和聊天器凄然的区别

|AI智能体 | AI助理 |聊天机器人|
|自主执行任务| 协助用户执行任务 | 自动执行简单任务或对话|
|自主性高|被动,响应用户请求|被动,响应命令|

AI 智能体工作原理

每个智能体都定义了角色、个性和沟通风格等

  • 角色:定义良好的角色可以让智能体保持一致到底性格
  • 记忆:智能体具备短期记忆、长期记忆、共识记忆及情景记忆。短期记忆用于即时互动,长期记忆用于历史数据和对话,情景记忆用于过去的互动,共识记忆用于智能体之间的共享信息。
  • 工具:工具是智能体可以利用的函数或外部资源,用于与环境互动并增强功能。工具可让智能体访问信息、处理数据或控制外部系统来执行复杂的任务
  • 模型:大语言模型(LLM)是构建AI智能体的基础,为智能体提供理解、推理和行动的能力。LLM充当智能体的“大脑”,使其能够处理和生成语言,而其他组件则促进推理和行动。

AI 智能体类型

【基于互动】

  • 互动式合作伙伴:由用户查询触发,并执行用户查询或事务
  • 自主后台处理:在后台运行以自动执行常规任务、分析数据等,通常由事件驱动

【基于智能体数量】

  • 单个智能体:独立运作来实现特定目标。利用外部工具和资源来完成任务。
  • 多智能体:这些系统利用各个智能体的不同能力和角色,来处理复杂的任务。多智能体系统可以在互动场景中模拟人类行为,例如人际沟通。每个智能体都可以拥有最适合其需求的基础模型。

RAG和MCP

RAG (Retrieval-Augmented Generation,检索增强生成):指查知识库,然后将查到的片段喂给大模型
MCP (Model Context Protocol,模型上下文协议):提供接口,用来让模型调用工具

SpringAI框架开发MCP

MCP服务开发

MCP就是暴露指定的方法给AI,给AI提供工具

  1. 先引入Spring AI MCP依赖
  2. 然后在Service中需要暴露的方法上加@Tool(description = "方法作用描述")
  3. 配置类中注册一个Tool工具回调提供者的bean,将Service注入,然后将Service中所有用@Tool注解的方法注册到Tool工具回调提供者中
  4. 配置文件里面提供关于MCP协议的内容,Spring:ai:下面有很多配置,使用的sse
  • MCP服务实现中会看到方法的出入参对象加入大量的注解,LLM会根据这些出入参对象组装对象

MCP代理调用

实现一个 MCP 客户端,用于对接 MCP 服务端,并通过代理 AI 接口的方式完成调用。

  • proxy-api定义代理接口:发送Post请求(由Retrofit2Config封装发送请求)
  • proxy-trigger实现代理接口:Controller注入代理接口实现,调用接口方法,发送http请求
  • proxy-app程序启动入口,之后ApiTest层是测试调用MCP服务

MCP通信协议(json-rpc2)

配置CORS过滤器,配置了全局的跨域

初始化流程:

  1. 创建Service实例
  2. 创建ToolCallbackProvider实例,并注入Service实例
  3. 自动注册Service实例中的@Tool方法,或手动注册方法
  4. 然后可以开始使用MCP服务了,接收和处理MCP工具调用请求

之后处理MCP请求的流程:

  1. MCP请求到达端点,请求体包含method和params参数
  2. 工具路由分发,由ToolCallbackProvider负责路由
  3. 具体工具执行,例如提问、字体转换工具,返回响应

MCP请求的两个参数也是由MCP客户端通过AI生成的:

  1. 工具发现:MCP客户端的AI模型事先通过MCP协议从服务端获取可用工具列表
  2. AI决策:AI模型根据上下文决定调用哪个工具和传入参数,例如提问就调用ask,参数就是用户的具体问题
  3. 生成决策过程:
    • method:用户提问 → AI 理解意图 → 匹配可用工具 → 选择最合适的 method
    • params:提取关键信息 → 映射到工具参数结构 → 验证参数完整性 → 生成 params
  • SSE是长连接,服务端实时发送数据给客户端(单向)
  • 例如ask提问工具就会使用sse连接,模型一边推理生成,一边推送生成的内容给客户端

【架构分工】

服务端 = MCP 工具提供者

  • 执行具体的工具方法(如 ask、toUpperCase)
  • 提供工具列表接口
  • 处理工具调用请求

MCP 客户端 = AI 助手层

  • 调用 AI 模型分析用户意图
  • 决策选择合适的工具
  • 构造工具调用参数

前端 = 用户界面层

  • 收集用户输入
  • 展示最终结果
  • 处理用户交互

MCP客户端的AI主要用来决策
服务端的AI可以作为MCP的工具用来回答问题

MCP和RAG

我跳过这个两个阶段直接去Ai Agent,这俩先只做简单了解

MCP(Model Context Protocol)模型上下文协议

提供了AI模型和工具交互的协议,只要遵循这个协议设计的接口就让这个工具被AI使用

MCP架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌─────────────────────────────────────────────────────────┐
│ MCP Client (客户端) │
│ (你的项目中的 AiClientToolMcpNode 创建的对象) │
│ │
│ 职责:向 AI 模型提供工具调用能力 │
│ 例如:告诉 GPT "你可以调用这个工具查询天气" │
└─────────────────────────────────────────────────────────┘
↕ (MCP 协议通信)
┌─────────────────────────────────────────────────────────┐
│ MCP Server (服务端) │
│ (独立部署的服务,如 mcp-server-weixin-app) │
│ │
│ 职责:实现具体工具功能 │
│ 例如:收到"发送微信"请求,调用微信 API 执行 │
└─────────────────────────────────────────────────────────┘

目前支持两种协议

  • SSE:长连接、单向推送消息给客户端
  • Stdio:标准输入输出流、本地进程通信

项目中的MCP的工作流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. 启动阶段 (AiAgentPreheatService)

2. RootNode 并发加载配置

3. AiClientToolMcpNode.doApply()
├─ 读取 ai_client_tool_mcp 表配置
├─ 根据 transportType 选择 SSE/Stdio
├─ 创建 McpSyncClient
└─ 注册 Bean: AiClientToolMcp_1

4. AiClientModelNode.doApply()
├─ 创建 OpenAiChatModel
├─ 获取所有 McpSyncClient
└─ 通过 SyncMcpToolCallbackProvider 注册工具回调

5. 用户对话时
├─ GPT 分析用户意图:"需要发送微信"
├─ GPT 发现可用的 MCP 工具:send_wechat_message
├─ GPT 返回工具调用请求
├─ Spring AI 通过 MCP 协议调用 Server
└─ 执行完成,返回结果给 GPT

RAG(Retrieval-Augmented Generation)检索增强生成

RAG = 检索 + 生成,让 AI 在回答前先从知识库查找相关信息,再基于这些信息生成答案。

1
2
3
4
5
6
7
8
9
10
11
开卷考试 vs 闭卷考试

闭卷考试 (普通 LLM):
- 只靠训练时记忆的知识
- 无法回答训练数据之外的问题
- 容易产生"幻觉"(胡说八道)

开卷考试 (RAG 增强的 LLM):
- 可以先查资料再答题
- 能回答最新、最具体的问题
- 答案有依据,减少幻觉

之后可以上传文档,进行解析、拆分、添加标签,然后文本转换为向量,存到pg数据库(用于检索),记录订单到MySQL(记录有哪些知识库)
然后就可以进行查询:获取ChatModel、查询RAG标签、向量相似度搜索、拼接文档内容、构建System Prompt(带RAG内容)、最后流式返回

二者结合的使用流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
┌─────────────────────────────────────────────────────────────┐
│ 用户提问:"帮我查一下员工手册里的年假规定,然后发微信通知" │
│ 所有人明天开会学习 │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 步骤 1: RAG 检索知识库 │
│ - 查询 "年假规定" │
│ - 从 employee_handbook 知识库检索 │
│ - 找到相关片段:"员工年假为 15 天/年..." │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 步骤 2: GPT 整合 RAG 信息生成答案 │
│ "根据员工手册,年假规定为 15 天/年。现在我帮你发送微信通知。" │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 步骤 3: MCP 调用微信工具 │
│ - GPT 发现可用的 MCP 工具:send_wechat_message │
│ - 构造工具调用参数 │
│ - 通过 MCP 协议调用 mcp-server-weixin-app │
│ - 微信公众号发送模板消息 │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 最终回复:"已完成!年假规定是 15 天/年,微信通知已发送给所有" │
│ 员工" │
└─────────────────────────────────────────────────────────────┘

Ai Agent 业务流程、系统架构、库表设计说明

AI 智能体是使用 AI 来实现目标并代表用户完成任务的软件系统。其表现出了推理、规划和记忆能力,并且具有一定的自主性,能够自主学习、适应和做出决定。
这一节是纯介绍直接给的全代码,后面一节才是从0开始搭建

业务流程

用户发起AI对话的请求调用流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1. HTTP 请求

2. Trigger 层 (AiAgentController.chatAgent)

3. Domain 层 (AiAgentChatService.aiAgentChat)

4. Repository (查询配置)
├─ 查询 AiClient 配置
├─ 查询 AiClientModel(模型信息)
├─ 查询 AiClientToolMcp(MCP 工具)
├─ 查询 AiClientAdvisor(顾问配置)
└─ 查询 AiClientSystemPrompt(系统提示词)

5. Armory 引擎(预热装配)
├─ RootNode → 多线程加载配置
├─ AiClientToolMcpNode → 初始化 MCP 工具
├─ AiClientModelNode → 创建 ChatModel Bean
└─ AiClientAdvisorNode → 注册 Advisor

6. Spring AI 调用
├─ ChatClient.prompt(message)
├─ 应用 System Prompt
├─ 应用 ChatMemory(历史对话)
└─ 调用 OpenAI/GPT 模型

7. 返回响应

系统架构

一样的DDD架构

1
2
3
4
5
6
7
ai-agent-station (父工程)
├── ai-agent-station-api # API 接口层 - 对外暴露的服务接口
├── ai-agent-station-app # 应用层 - 启动类、配置文件
├── ai-agent-station-domain # 领域层 - 核心业务逻辑
├── ai-agent-station-infrastructure # 基础设施层 - 数据库、Redis 等
├── ai-agent-station-trigger # 触发器层 - HTTP Controller
└── ai-agent-station-types # 类型定义层 - 公共数据结构

service包结构,几大领域

  • IAiAgentChatService - AI 对话服务:普通对话、流式对话
  • IAiAgentPreheatService - 服务预热接口:启动时加载所有配置、手动预热单个客户端————构建 ChatClient、ChatModel、Advisor 等 Bean 并注册到 Spring 容器
  • IAiAgentRagService - RAG 知识库服务:上传文件到向量数据库————解析、切分、添加knowledge标签、存储到pgvector、记录到数据库
  • IAiAgentTaskService - 智能体任务调度:查询所有有效的定时任务、查询所有无效的任务 ID————配合 @Scheduled 注解执行定时 AI 任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
service/
├── IAiAgentChatService # 对话服务接口
├── IAiAgentPreheatService # 预热服务接口
├── IAiAgentRagService # RAG 服务接口
├── IAiAgentTaskService # 任务服务接口

├── chat/ # 对话服务实现
│ └── AiAgentChatService

├── preheat/ # 预热服务实现
│ └── AiAgentPreheatService

├── rag/ # RAG 服务实现
│ └── AiAgentRagService

├── task/ # 任务服务实现
│ └── AiAgentTaskService

└── armory/ # 装配引擎(核心)
├── AbstractArmorySupport
├── factory/
│ ├── DefaultArmoryStrategyFactory
│ └── element/
│ └── RagAnswerAdvisor
└── node/ # 树形节点链
├── RootNode
├── AiClientToolMcpNode
├── AiClientModelNode
├── AiClientAdvisorNode
├── AiClientNode
└── RagAnswerAdvisor

装配工厂

1
2
3
4
DefaultArmoryStrategyFactory
├── strategyHandler() - 获取策略处理器
├── chatClient(clientId) - 获取 ChatClient Bean
└── chatModel(modelId) - 获取 ChatModel Bean

树形策略链(责任链模式)

1
2
3
4
5
6
7
8
9
10
11
RootNode (根节点)

AiClientToolMcpNode (MCP 工具节点)

AiClientModelNode (模型配置节点)

AiClientAdvisorNode (顾问节点)

AiClientNode (客户端节点)

RagAnswerAdvisor (RAG 回答顾问)

各个节点功能

  1. RootNode - 根节点,多线程并发加载所有配置数据,包括(模型配置、MCP工具、顾问配置、系统提示词、客户端基础配置)
  2. AiClientToolMcpNode - MCP 工具节点,初始化MCP客户端并注册为Bean,支持SSE和Stdio
  3. AiClientAdvisorNode - 顾问节点,创建 Advisor(增强 AI 行为的组件),支持两种类型(ChatMemory会话记忆,RagAnswerRAG检索增强回答)
  4. AiClientModelNode - 模型节点,创建 OpenAI 聊天模型并集成 MCP 工具
  5. AiClientNode - 客户端节点,构建最终的 ChatClient 对象,整合所有配置————添加ChatModel、Advisors、System Prompt(提示词),最后注册为Bean
  6. RagAnswerAdvisor - RAG 回答顾问(特殊节点),不是树的节点,而是具体的 Advisor 实现

数据模型(VO对象)

  • AiClientVO - 客户端配置
  • AiClientModelVO - 模型配置(API Key、BaseURL 等)
  • AiClientToolMcpVO - MCP 工具配置
  • AiClientAdvisorVO - 顾问配置(ChatMemory、RagAnswer)
  • AiClientSystemPromptVO - 系统提示词

启动流程

1
2
3
4
5
6
7
8
9
10
11
12
13
Application.main()

CommandLineRunner.run()

IAiAgentPreheatService.preheat() // 预热服务

加载所有有效客户端配置

构建 ChatClient、ChatModel Bean

注册到 ApplicationContext

启动完成,等待请求

库表设计说明

设计了14张库表,来承接自动化 Ai Agent 的构建操作。

ai_agent_task_schedule,智能体任务调度配置表

ai_agent,AI智能体配置表

ai_agent_client,智能体-客户端关联表

ai_client,AI客户端配置表

模型配置组;ai_client_model、ai_client_model_config、ai_client_model_tool_config

工具配置组;ai_client_tool_config、ai_client_tool_mcp

顾问配置组;ai_client_advisor、ai_client_advisor_config

提示词配置;ai_client_system_prompt、ai_client_system_prompt_config

知识库配置;ai_rag_order

从0开始建立项目

看第二节,直接从0开始做

Ai Agent 测试案例

环境搭建

安装环境最外层的两个docker-compose文件

  • docker-compose-app.yml启动开发好的mcp服务(一个csdn发帖,一个微信公众号通知)
  • docker-compose-environment.yml启动当前项目需要的各种服务(各种数据库以及管理工具)
    • phpmyadmin是MySQL的网页端管理工具,8899就能直接访问,redis-admin就是redis的
    • 数据库初始化是直接运行指定路径下的sql文件
      (创建自己的镜像(可选):先install打成jar包,然后运行dockerfile,创建镜像)

yml文件中配置数据库地址和模型的key,key可以从小傅哥直接购买
然后就可以测试OpenAI的模型的功能了,和解析上传向量数据库

测试代码

OpenAiTest(5 个测试方法)

  • test_call() - 基础文本问答(1+1)
  • test_call_images() - 多模态图片识别
  • test_stream() - 流式输出
  • upload() - 文档上传到向量数据库
  • chat() - RAG 检索增强生成

AiAgentTest(4 个测试方法)

  • test_chat_model_stream_01() - 流式调用
  • test_chat_model_call() - 同步调用
  • test_02() - ChatClient 对话
  • test_client03() - 多轮对话 + 提示词优化 + Agent 任务

【流式响应】

  1. 先创建一个countDownLatch对象,结尾使用
  2. 然后构建一个Flux对象,用提示词发起流式请求
  3. 然后这个对象订阅响应,持续打印出来,
  4. 流式响应后用countDownLatch.await()阻塞主线程,直到流式响应完全结束
  5. 比如这里问1+1等于几,面就会一个字符一个字符1 + 1 = 2

然后测试AiAgent功能,测试时的key和那两个mcp的地址也改了
我的npm可能有问题,重新配置了一下配置方法
这个stdioParams的能访问的地址也要改/Users/fuzhengwei/Desktop,改成windos的C:\Users\26466\Desktop
npx命令找不到,把npx改成npx.cmd
之后分别验证test_chat_model_stream_01、test_chat_model_call、test_02、test_client03

模型和智能体区别

方面 OpenAiTest(普通调用) AiAgentTest(智能体)
调用方式 openAiChatModel.call(prompt) ChatClient.prompt().system().advisors().call()
角色定义 简单 System Prompt 复杂 Agent 角色 + 任务规划
工具调用 ❌ 无 ✅ MCP 工具(文件系统、CSDN、微信)
记忆能力 ❌ 无(每次独立) ✅ MessageWindowChatMemory(多轮对话)
知识检索 手动 RAG 自动 RAG(RagAnswerAdvisor)

模型就很简单,主要讲智能体
智能体可以配置MCP工具回调、角色、顾问
智能体就是模型+工具+角色+记忆+顾问

  • 模型是大脑
  • 工具就是mcp
  • 角色,定义人格设定和专业知识
  • 记忆,记住历史对话
  • 顾问,RAG自动知识检索等

模型token

token与字数相关,一次问答输入输出的token和就是总消耗

根据 Ai Agent 案例,设计库表

首先,整个代码构建的整个 Ai Agent 最小化单元服务,我们可以根据这样的服务信息设计出库表结构。

第一步,从上到下,OpenAiApi 是最基础单元结构,可以被多个 OpenAiChatModel 使用,它可以被拆分出第一张表。

第二步,构建 OpenAiChatModel,这个阶段,需要 openAiApi、model对话模型、tool mcp 工具。其中model对话模型时一种固定固定资源,可以直接放到 ai_client_model 模型中,而 openAiApi、mcp 工具,都属于复杂配置,则需要额外的外部关联来衔接。也就是后面的 ai_client_config 配置,用于配置衔接关系。

第三步,ChatClient 对话客户端,这部分的实例化过程都是和外部其他的资源关联,本身表设计只要有一个客户端的唯一id和客户端的描述介绍即可。

第四步,给 mcp 增加一个表,mcp 服务是非常重要的,有 mcp 才有 agent 服务。mcp 的启动有 stido、sse 两种方式,每种方式都有对应的配置文件 json 数据。

第五步,defaultSystem 系统提示词,需要单独拆分出来。提示词等于智能体的大脑,也有人说,其实 Ai Agent 就是 prompt 的堆叠,所以写提示词是很重要的。

第六步,advisor 顾问角色,在 Spring Ai 框架中,以顾问的方式,访问记忆上下文,知识库资源,所以这部分也要单独设计库表。

第七步,设计一个 ai_client_config,用于配置;api、model、client、prompt、mcp、advisor的衔接关系。

第八步,设计 ai_agent、ai_agent_flow_config,也就是一个 ai agent,是可以连续调用多个 ai client 客户端的。

第九步,设计 ai_agent_stask_schedule 任务,这是一种触达手段,可以把配置好的任务,让 task 定时执行,如自动发帖、系统异常巡检、舆情风险检测、系统配置变更、运营活动报表等。

第十步,ai_client_rag_order,是知识库表,用于上传知识库做一个记录,这样顾问角色就可以访问知识库内容了。

【表描述】

  1. api表,基本单元,包含不同url,key等,供多个模型使用
  2. ai_client_model聊天模型配置表,定义系统可用AI模型,通过关联API配置
  3. ai_client_tool_mcp表存放工具集成配置,mcp启动有两种方式,sse和stido对应两个JSON字段
  4. ai_client_config表,用来存上面模型和mcp、api的对应衔接关系,例如:源类型,源ID,目标类型,目标ID,扩展参数。类型都是多样的
  5. ai_client对话客户端,客户端是功能单元可以独立处理特定类型的AI任务,客户端通过关联配置与模型、提示词、工具等资源建立连接,多个客户端可以组成智能体工作流
  6. ai_client_system_prompt系统提示词表,描述智能体的角色,决定了AI的行为方式和回答风格,这是核心组成部分,是智能体的“大脑”
  7. ai_client_advisor顾问角色表,包含名称、顾问类型、扩展参数配置,json记录,在SpringAi框架中,以顾问的方式记忆上下文和访问知识库
  8. ai_agent表,记录智能体信息,可以关联多个客户端
  9. ai_agent_flow_config表,记录当前智能体的流程配置,通过sequence字段指定多个客户端执行顺序,形成链路
  10. ai_agent_stask_schedule任务表,这是一种触达手段,定义任务描述,参数,cron表达式等,让task自动执行
  11. ai_client_rag_order知识库表,用于给上传知识库做一个记录,这样顾问角色就可以访问知识库内容了

本系统是基于Spring AI框架构建的AI Agent智能体平台,通过数据库驱动的配置化方式实现AI模型、客户端、工具和智能体的解耦和自动实例化。系统支持多种AI模型接入、工具集成(MCP协议)、任务调度等功能。

多数据源和Mapper配置

配置pgvector(向量库)、mysql(业务库)的连接

  • 首先添加一个DataSourceConfig配置类,来自己实现数据源的加载,这部分会替代原本配置到yml中的配置,由Spring加载数据源
    • 要配置MySQL、MyBatis集成、PgVector向量数据源,@Bean("mysqlDataSource")写明数据源名称
    • MyBatis的集成sqlSessionTemplate中用@Qualifier("mysqlDataSource"):明确指定注入名为”mysqlDataSource”的Bean,确保MyBatis使用MySQL数据源,而不会误用PgVector数据源
    • PgVectorJdbcTemplate中用@Qualifier("pgVectorDataSource"),明确注入到PgVector数据源。使用JdbcTemplate而不是MyBatis,更适合向量操作的简单SQL
    • HikariCP是目前性能最优的java的连接池,Spring默认使用,配置参数类似线程池,MySQL和PgVector用的都是这个
    • vectorStore向量库配置,指定库表名称,也就是初始化的向量库表vector_store_openai
  • 之后根据不同类型的数据源,注入到AI向量库使用场景和MyBatis业务使用场景中,类似之前的DB-Router

【多数据源设计优势】

  1. 数据隔离性: 业务数据和向量数据完全分离,互不影响,提升系统稳定性
  2. 性能优化: 针对不同数据类型和访问模式优化连接池参数
  3. 技术栈适配: MySQL使用MyBatis ORM,PgVector使用JdbcTemplate,各取所长
  4. 扩展性强: 可以轻松添加更多类型的数据源,如Redis、MongoDB等
  5. 配置灵活: 支持外部配置文件,便于不同环境的参数调整
  6. 故障隔离: 一个数据源的问题不会影响另一个数据源的正常使用

【Mapper配置】

一个数据库表为了可以让程序使用,主要需要 PO 对象(持久化对象),DAO 文件(数据传输对象)以及 Mapper 配置数据库语句来映射 PO 和 DAO。
mapper里面定义数据库到映射po,然后dao层写一写简单的增删改查接口就可以测试了

数据加载模型设计

首先,整个 Ai Agent 的实例化过程,就是各项组件的创建和组装的过程。那么,为了让整体的实现代码更易于维护,我们可以把这样的创建过程,通过规则树的方式进行串联实现。

domain包下新建agent领域,该领域有adapter、model、service

  • adapter:适配器层(之前大营销是Repository层),也就是整个agent需要什么样的数据结构,就会定义对应的接口方法。之后由基础设施层,通过pom引入domain领域层来适配这些接口。好处就是适配和防腐,如果将来基础层数据有变动,也不会影响到领域层的服务方法。
  • model:领域对象,这些对象是为了满足service服务创建的对象,包括聚合对象,实体,值对象
  • service:具体的逻辑服务实现,这部分要完成agent实例化过程。因为实际使用中,用户可以通过只实例化 api,也可以实例化整个 client,这是不同的操作。所以这块要做一个数据加载策略。

service包下类关系:

  • AbstractArmorySupport:抽象装配支撑类,定义了日志,和几个protected属性(Spring上下文、线程池、仓储接口),还有多线程加载方法,然后他还继承了AbstractMultiThreadStrategyRouter<ArmoryCommandEntity, DefaultArmoryStrategyFactory.DynamicContext, String>,这个是一个外部导入的扳手工程,这个类实现了原来的规则树逻辑。还实现了两个接口一个StrategyHandler接口,接口定义了策略处理方法,一个StrategyMapper接口,接口定义获取下个流程的 StrategyHandler 实现类,详情在扳手工程的第二节
  • RootNode:根节点:
    • 首先是map注入的加载策略实现
    • 然后是重写的多线程数据处理方法,通过map中的加载策略实例获取加载策略,从入参获取命令类型,然后作为参数调用加载策略的加载方法
    • doAppley执行入口,调用父类的router方法,流转到下个节点
    • 获取策略处理器
  • 具体策略节点:注入仓储和线程池属性,和一个加载数据方法
    • 先从ArmoryCommandEntity 获取客户端 ID 列表
    • 然后用创建多个CompletableFuture异步任务 + 线程池并发执行多个数据库的查询,查询好的结果会放到入参的上下文对象中,都查询完了方法就返回。
    • 查询的对象根据加载策略决定,加载客户端对话模型就只需要api和model两个对象,加载一个client,要顺序的加载所有的资源,包括;ai_client_api、ai_client_model、ai_client_tool_mcp、ai_client_system_prompt、ai_client_advisor、ai_client。

RootNode运行时调用流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
外部调用 RootNode


RootNode.doApply(requestParameter, dynamicContext)


router(requestParameter, dynamicContext) ← 父类 AbstractMultiThreadStrategyRouter 的方法

├─ 创建 DynamicContext(上下文对象,工厂类创建)


├─ 提交任务到线程池 (threadPoolExecutor)
│ │
│ ▼
│ multiThread(requestParameter, dynamicContext) ← RootNode 重写
│ │
│ ├─ 根据 commandType 选择策略
│ │ ILoadDataStrategy strategy = loadDataStrategyMap.get(commandType)
│ │
│ └─ 执行策略
│ strategy.loadData(requestParameter, dynamicContext)
│ │
│ ├─ AiClientLoadDataStrategy.loadData()
│ │ ├─ 异步查询 ai_client_api
│ │ ├─ 异步查询 ai_client_model
│ │ ├─ 异步查询 ai_client_tool_mcp
│ │ ├─ 异步查询 ai_client_system_prompt
│ │ ├─ 异步查询 ai_client_advisor
│ │ └─ 异步查询 ai_client
│ │
│ └─ AiClientModelLoadDataStrategy.loadData()
│ ├─ 异步查询 ai_client_api
│ └─ 异步查询 ai_client_model

└─ 返回结果

动态实例化客户端API

service.armory包下新建一个AiClientApiNode类,内部构建Ai Agent
AiClientApiNode 的构建会使用到 RootNode 节点加载的数据
AiClientApiNode 的构建完成后,会使用到 AbstractArmorySupport 中提供的注册 Bean 到 Spring 容器的方法,这个地方使用到了Spring源码

【手动构建bean的方法】

  • 第一步:获取Bean工厂,DefaultListableBeanFactory 是Spring容器的核心实现类。通过它可以动态管理Bean的生命周期(注解主要在项目初始化的时候注册Bean)
  • 第二步:构建Bean定义,使用 BeanDefinitionBuilder 创建Bean定义。genericBeanDefinition(beanClass, () -> beanInstance) 指定Bean类型和实例供应商。设置作用域为单例模式( SCOPE_SINGLETON )
  • 第三步:处理Bean冲突,检查是否已存在同名Bean,如果存在,先移除旧的Bean定义,确保新Bean能够正确注册。
  • 第四步:注册新Bean,将新的Bean定义注册到Spring容器。

【AiClientApiNode类doApply方法构建Ai Agent】

  1. 首先获取上下文对象中的aiClientApiList,并验空
  2. 然后遍历这个list,获取VO对象,每个VO都构建OpenAiApi,并注册为Bean
  3. 完成后router到下一个节点继续处理其他节点实例化
  4. router之后后会走到get方法,目前设置的是 defaultStrategyHandler,也就是不执行下一个节点。后续会随着功能开发来修改。

【agentTest测试类调用流程】

  1. 先注入工厂类和Spring上下文,然后执行构建方法
  2. 构建方法先用工厂类获取一个armoryStrategyHandler,默认实现就是rootNode
  3. mock一个ArmoryCommandEntity(命令类型为client,客户端列表是一个列表元素只有3001),和一个上下问对象用作为入参
  4. 执行armoryStrategyHandler(rootNode)的apply方法进行装配
  5. 进入到apply方法后执行multThread进行多线程装配:先获取命令类型,然后获取策略,最后执行加载方法,就是加载策略类的方法
  6. loadData方法先获取客户端列表,用异步任务+线程池查询当前client所有所需配置,存到上下文对象中
  7. 然后再执行RootNode的doApply方法内部执行router方法,进行流转,进入下一个节点AiClientApiNode
  8. AiClientApiNode的doApply方法中开始构建Ai Agent,从上下文对象中获取data验空,然后遍历上下文列表,每一个VO对象都组装成一个OpenAiApi对象并注册为Bean,然后再进入router方法,此时get就为空,返回
  9. 装配后从Spring容器中取出装配的Bean来验证

【这个框架的方法】

  • AbstractMultiThreadStrategyRouter类实现两个接口,一个StrategyMapper,一个StrategyHandler
  • router方法:获取下一个strategyHandler(实例是下一个节点类),然后进入获取实例的apply方法,为null就调用默认策略处理器,返回null
  • apply方法:multiThread异步加载数据并存到上下文中,然后执行doApply方法,具体实现都交由子类(mutiThread方法在父类默认缺省,就是{},子类不实现就直接略过这个方法了)
  • get方法:获取下一个节点,抽象类的接口方法没实现,而是交由子类实现
  • 通过这样的处理,从 router -> apply -> multiThread -> doApply 就顺序的把各个流程节点都执行了,帮助我们定义了标准的执行链路结构。

【这个流转的逻辑】

步骤 节点 动作 返回值
1 外部调用 handler.apply() -
2 RootNode multiThread() → 查库 -
3 RootNode doApply()router() -
4 RootNode get() → 返回 aiClientApiNode (目前写死) -
5 AiClientApiNode doApply() → 注册 Bean -
6 AiClientApiNode router()get() 返回 defaultStrategyHandler -
7 DEFAULT doApply()return null null
8 返回 逐层返回 null null
9 验证 从 Spring 容器获取 Bean ✅ 成功

目前就两个节点
一个RootNode用来加载数据到上下文,然后流转到下一个节点
一个AiClientApiNode用来用上下文对象构建Ai Agent并注册为Bean

动态实例化对话模型

上一节新增了Api节点类,并完成了构建、实例化、注册为Bean的逻辑
这次再新建两个节点类,AiClientToolMcpNode(MCP工具节点)、AiClientModelNode(模型节点处理)

【当前要实现的流程】

  • RootNode负责数据加载,将构建节点元素的数据依次加载到内存中
  • 之后在上一节完成的API节点处理基础上,开始创建MCP服务的创建,之后是ChatModel对话模块的创建,ChatModel创建需要用到api、mcp两个元素

【改动】

  • 首先,在 AbstractArmorySupport 定义 beanName、dataName,让每个节点类都可以实现一个自己的 beanName、dataName 方法,这样更好维护。
  • 之后,在 dynamicContext.getValue(dataName()); 使用 dataName 方法,获取 aiClientApiList 数据。
  • 最后,AiClientApiNode 节点完成后,要路由到下一个节点,aiClientToolMcpNode。也就是 get 方法里新增加的配置。

【AiClientToolMcpNode】

  • 类似api节点,doApply构建MCP服务:上下文获取MCP服务列表验空,然后将每一个VO都用私有方法createMcpSyncClient实例化为MCP,并注册为Bean
  • createMcpSyncClient私有方法:根据transportType决定使用sse、stdio两种方式中的哪种创建客户端

【AiClientModelNode】

  • 也是类似api节点,看来只有RootNode节点需要统一加载,只有他重写了multiThread方法进行loadData
  • 从上下文中获取模型列表验空,然后获取当前模型关联的api Bean对象,还有关联的Tool MCP Bean对象(这个是一个List<McpSyncClient>
  • 然后实例化对话模型,openAiApi属性为获取的Bean,defaultOptions属性中的toolCallbacks为用mcpSyncClients创建的SyncMcpToolCallbackProvider

【测试注意点】

  • 这个是直接运用npx命令启动@modelcontextprotocol/server-filesystem包,这是 MCP 官方提供的标准文件系统服务器,ai用这个包就能创建文件了
  • mcp数据库表里面的配置在windos环境要改很多,npx还是必须改成npx.cmd,后面的路径也要改成windos桌面的路径,用双反斜杠
  • 测试类里面的命令也是和mcp表中配置的一致
1
2
3
4
5
6
7
8
9
10
11
{
"filesystem": {
"command": "npx.cmd",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"C:\\Users\\26466\\Desktop",
"C:\\Users\\26466\\Desktop"
]
}
}
1
{"metadata":{"empty":false,"id":"chatcmpl-DGL7qHGnE0GlH8lOupqJOhzx9O7FT","model":"gpt-4.1-mini-2025-04-14","rateLimit":{},"usage":{"promptTokens":2519,"completionTokens":150,"totalTokens":2669}},"result":{"metadata":{"contentFilters":[],"empty":true,"finishReason":"STOP"},"output":{"media":[],"messageType":"ASSISTANT","metadata":{"role":"ASSISTANT","messageType":"ASSISTANT","refusal":"","finishReason":"STOP","index":0,"annotations":[],"id":"chatcmpl-DGL7qHGnE0GlH8lOupqJOhzx9O7FT"},"text":"您当前可以使用的目录有:\n- C:\\Users\\26466\\Desktop\n\n在这些目录中的文件和子目录您可以访问和操作。您需要我帮您使用哪些具体的工具或执行什么操作吗?以下是我可以使用的一些功能示例:\n- 读取文件内容\n- 写入或编辑文件内容\n- 列出目录内容\n- 搜索文件\n- 获取文件详细信息\n- 创建目录\n- 移动或重命名文件\n- 读取媒体文件(图片或音频)\n\n请告诉我具体需求,我可以为您执行相应操作。"{"$ref":"$.metadata.rateLimit.usage.result.metadata.contentFilters.output.metadata.annotations"}}},"results":[{"$ref":"$.metadata.rateLimit.usage.result"}]}

这段 JSON 数据是 AI 模型返回的原始响应结构,其中包含了元数据(如 Token 用量、模型版本)和实际的回复内容。

提取其中的核心文本信息并格式化后,AI 的实际回复如下:

📂 当前可用目录
您目前授权我可以访问和操作的目录为:
C:\Users\26466\Desktop

在此目录及其子目录中,我可以帮您执行以下操作:

🛠️ 可用功能列表
📖 读取文件:查看文件的具体内容。
✍️ 写入/编辑:创建新文件或修改现有文件内容。
📋 列出目录:查看文件夹下有哪些文件和子文件夹。
🔍 搜索文件:在目录中查找特定名称或类型的文件。
ℹ️ 获取详情:查看文件大小、创建时间等属性。
📁 创建目录:新建文件夹。
🔄 移动/重命名:更改文件位置或文件名。
🖼️ 读取媒体:查看图片或播放音频文件(如果支持)。

💬 下一步
请告诉我您的具体需求,例如:
“帮我在桌面创建一个名为 notes.md 的文件”
“列出桌面上所有的 txt 文件”
“读取 report.pdf 的内容”

我将立即为您执行相应操作。

实例化对话客户端

在上面的基础上再加入两个节点:

  • AiClientAdvisorNode顾问角色节点处理
  • AiClientNode客户端节点处理:关联了其他各项元素的,所以在构建时,需要在 AiClientNode 节点,从 Spring 容器通过 getBean 的方式,检索到对应的各项元素。

【编码实现】

  1. 节点那一层新建这两个节点类
  2. service.armory.factory.element中新建RagAnswerAdvisor顾问类,用于访问知识库
  3. 查询出系统提示词,并转为map结构方便使用
  4. model.valobj包下新建一个AiClientAdvisorVO,负责接收服务器的查询
  5. model.valobj包下新建策略工厂类AiClientAdvisorTypeEnumVO,内部运用策略模式定义了两种顾问类型(匿名内部类,匿名内部类默认继承父类,也可以重写父类方法),map结构,还有创建顾问对象,根据code返回等方法
    • CHAT_MEMORY:创建上下文记忆顾问,维护对话历史(基于内存窗口)。内部重写createAdvisor方法创建记忆组件并注入到Advisor中,创建Advisor类实例(来自SpringAI框架,可以保存最近N轮对话,每次提问时自动附带历史消息,超出窗口大小时自动移除最早的对话)
    • RAG_ANSWER:创建知识库问答顾问,从向量数据库检索相关信息。也重写了创建方法,创建Advisor实例(从向量数据库(VectorStore)中搜索与用户提问相关的文档,将检索到的内容自动添加到Prompt,让AI基于检索到的事实信息回答,而不是仅靠训练数据)
    • createAdvisor:抽象方法,具体实现在上面两个匿名内部类中
    • Map:静态属性,存入枚举对象(就是当前类,用父类来代表两个匿名子类的引用)
    • 静态代码块:在类初始化时初始化Map,code作为key,枚举类作为value
    • getByCode:静态方法,根据code获取枚举对象

【顾问创建】

  • AiClientAdvisorNode节点类,doApply方法执行业务逻辑,同样先从上下文中获取list对象,遍历list获取VO对象,根据配置的数据 List<AiClientAdvisorVO> aiClientAdvisorList 循环构建顾问角色,并注册为Bean。而 createAdvisor 方法则通过枚举策略进行构建。
  • 如果将来还有其他顾问角色,则在 AiClientAdvisorTypeEnumVO 扩展实现方法即可。

【客户端创建】

  1. 先获取上下文对象:存aiClientVO的list,systemPromptMap
  2. 预设话术:设置系统提示词,从systemPromptMap获取所有id符合的,然后都拼接到defaultSystem
  3. 对话模型:直接根据VO的ModelBeanName属性getBean
  4. MCP服务:从VO获取McpBeanNameList,遍历获取所有MCP的Bean并添加到一个mcpSyncClients
  5. advisor顾问角色:和上面MCP类似,获取列表后添加进一个advisors列表,然后将列表转成Advisor[](这是一个大小不变的数组,类型[]就是java的语法)
  6. 构建对话客户端:用建造者模式传参
    • 传入defaultSystem系统提示词属性,入参是第2步
    • 传入defaultToolCallbacks属性,入参是将第4步的对象转换new SyncMcpToolCallbackProvider(mcpSyncClients.toArray(new McpSyncClient[]{}))将原来的对象转成Mcp数组后转成MCP工具回调提供者对象
    • 传入第5步的顾问列表
  7. 构建完了将这个ChatClient注册为Bean
  8. 然后这个客户端类的strategyHandler因为已经是最后了,就返回defaultStrategyHandler就行,返回空节点并返回

【使用构建好的客户端进行对话】

  1. 这个构建好的客户端BeanName是3001直接获取并转成ChatClient对象(当初构建时就是这个类型,这个是SpringAI官方的类)
  2. 然后调用chatClient的prompt(Prompt).call()传入一个构建好的Prompt对象就可以进行对话流程
  3. .call()开始执行,进入ChatClient内部执行链
  4. 【Advisor处理链】按顺序执行所有顾问
    1. 聊天记忆顾问:从Memory中加载历史对话,将历史对话添加到当前Prompt中,例如,输入:有哪些工具可以使用。输出:上次说要学SpringAI,有哪些工具可以使用
    2. RAG问答顾问:执行用户问题查询,搜索相关文档,例如,输入:有哪些工具可以使用。返回:向量数据库返回:文档1:“CSDN发布工具使用说明”。输出:增强提示词,你是AI… 参考知识库:文档1
    3. 简单日志顾问:记录用户输入,记录模型响应,打印日志到控制台
  5. 合并系统提示词:将构建时的defaultSystem与Advisor增强后的内容合并:最终的 System Prompt = defaultSystem (预设话术) + RAG 检索的知识库内容 + 其他 Advisor 添加的内容
  6. 发送请求到ChatModel:发送一个请求,内容是JSON形式
  7. OpenAI API处理请求;理解System Prompt,分析用户问题,检查可用的tools列表,如果有工具调用的需求,返回tool_calls,否则直接返回文本回复
  8. 服务端接受响应,也是JSON,如果有tool_calls,则被Spring AI拦截并调用对应的工具:解析tool_calls,查找对应的MCP客户端,调用MCP工具,MCP服务器执行实际操作,返回执行结果给SpringAI,SpringAI将工具执行结果包装并返回给OpenAI,告知其工具已执行,OpenAI根据结果生成最终回复
  9. Advisor后置处理:某些Advisor会在响应返回后进行处理,例如:保存记忆、记录日志
  10. 将结果返回给用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
┌─────────────────────────────────────────────────────────────┐
│ 步骤 1: chatClient.prompt(Prompt) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 创建 PromptSpec 对象 │
│ └─ 包含:UserMessage("有哪些工具可以使用") │
│ │
│ ⚠️ 注意:此时还没有发送任何请求 │
│ │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 步骤 2: .call() - 开始执行 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 进入 ChatClient 的内部执行链 │
│ │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 步骤 3: 【Advisor 处理链】- 按顺序执行所有顾问 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 遍历 defaultAdvisors 数组,每个 Advisor 都有机会: │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ① PromptChatMemoryAdvisor (聊天记忆顾问) │ │
│ ├──────────────────────────────────────────────────────┤ │
│ │ 作用时机:在发送请求前 │ │
│ │ │ │
│ │ 执行内容: │ │
│ │ - 从 Memory 中加载历史对话 │ │
│ │ - 将历史对话添加到当前 Prompt 中 │ │
│ │ │ │
│ │ 输入: │ │
│ │ UserMessage("有哪些工具可以使用") │ │
│ │ │ │
│ │ 输出: │ │
│ │ [ │ │
│ │ AssistantMessage("上次你说要学习 Spring AI"), │ │
│ │ UserMessage("有哪些工具可以使用") │ │
│ │ ] │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ② RagAnswerAdvisor (RAG 问答顾问) │ │
│ ├──────────────────────────────────────────────────────┤ │
│ │ 作用时机:在发送请求前 │ │
│ │ │ │
│ │ 执行内容: │ │
│ │ - 使用用户问题查询 VectorStore │ │
│ │ - 检索相关文档片段 │ │
│ │ - 将文档片段添加到 System Prompt 中 │ │
│ │ │ │
│ │ 输入: │ │
│ │ "有哪些工具可以使用" │ │
│ │ │ │
│ │ 查询向量库返回: │ │
│ │ [文档 1: "MCP 协议支持文件系统操作...", │ │
│ │ 文档 2: "CSDN 发布工具配置说明..."] │ │
│ │ │ │
│ │ 输出:增强后的 System Prompt │ │
│ │ """ │ │
│ │ Ai 智能体 │ │
│ │ 你擅长使用 Planning 模式... │ │
│ │ │ │
│ │ 参考知识库: │ │
│ │ [1] MCP 协议支持文件系统操作... │ │
│ │ [2] CSDN 发布工具配置说明... │ │
│ │ """ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ③ SimpleLoggerAdvisor (简单日志顾问) │ │
│ ├──────────────────────────────────────────────────────┤ │
│ │ 作用时机:请求前后都执行 │ │
│ │ │ │
│ │ 执行内容: │ │
│ │ - 记录用户输入 │ │
│ │ - 记录模型响应 │ │
│ │ - 打印日志到控制台 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 步骤 4: 【合并 System Prompt】 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 将构建时的 defaultSystem 与 Advisor 增强后的内容合并: │
│ │
│ 最终的 System Prompt = │
│ defaultSystem (预设话术) │
│ + RAG 检索的知识库内容 │
│ + 其他 Advisor 添加的内容 │
│ │
│ 示例: │
│ """ │
│ Ai 智能体 │
│ 你是一个 AI Agent 智能体,可以根据用户输入信息生成文章... │
│ 你擅长使用 Planning 模式... │
│ 你的规划应该包括以下几个方面: │
│ 1. 分析用户输入的内容,生成技术文章。 │
│ 2. 提取文章标题、内容、标签、简述,发布到 CSDN │
│ ... │
│ │
│ 参考知识库: │
│ [1] MCP 协议支持文件系统操作... │
│ [2] CSDN 发布工具配置说明... │
│ """ │
│ │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 步骤 5: 【发送请求到 ChatModel】 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 调用构建时传入的 chatModel.call(prompt) │
│ │
│ 请求内容: │
│ { │
│ "model": "gpt-4.1-mini", │
│ "messages": [ │
│ { │
│ "role": "system", │
│ "content": "Ai 智能体\n你是一个 AI Agent..." │
│ }, │
│ { │
│ "role": "user", │
│ "content": "有哪些工具可以使用" │
│ } │
│ ], │
│ "tools": [...] // 从 MCP 工具注册表获取 │
│ } │
│ │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 步骤 6: 【OpenAI API 处理】 │
├─────────────────────────────────────────────────────────────┤
│ │
│ OpenAI 服务器接收请求并处理: │
│ │
│ ① 理解 System Prompt 中的人设和规则 │
│ ② 分析用户问题:"有哪些工具可以使用" │
│ ③ 检查可用的 tools 列表 │
│ ④ 如果有工具调用需求,返回 tool_calls │
│ ⑤ 否则直接返回文本回复 │
│ │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 步骤 7: 【接收响应】 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ChatResponse response = chatModel.call(prompt); │
│ │
│ 响应示例: │
│ { │
│ "id": "chatcmpl-xxx", │
│ "choices": [ │
│ { │
│ "message": { │
│ "role": "assistant", │
│ "content": "您可以使用以下工具:\n\n" + │
│ "1. **文件系统工具**:可以创建、编辑...\n" +
│ "2. **CSDN 发布工具**:可以自动发布...\n" +
│ "3. **微信通知工具**:可以发送消息...",
│ "tool_calls": null // 如果没有工具调用
│ }
│ }
│ ]
│ }
│ │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 步骤 8: 【Advisor 后置处理】 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 某些 Advisor 会在响应返回前进行后处理: │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ PromptChatMemoryAdvisor (保存记忆) │ │
│ ├──────────────────────────────────────────────────────┤ │
│ │ 执行内容: │ │
│ │ - 将本次对话保存到 Memory │ │
│ │ User: "有哪些工具可以使用" │ │
│ │ Assistant: "您可以使用以下工具:..." │ │
│ │ │ │
│ │ 目的:下次对话时可以检索这段历史 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ SimpleLoggerAdvisor (记录响应) │ │
│ ├──────────────────────────────────────────────────────┤ │
│ │ 执行内容: │ │
│ │ - 打印响应内容到日志 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 步骤 9: 【返回结果给用户】 │
├─────────────────────────────────────────────────────────────┤
│ │
│ String content = response.getResult().getOutput() │
│ .getText(); │
│ │
│ log.info("测试结果 (call):{}", content); │
│ │
│ 控制台输出: │
│ "您可以使用以下工具: │
│ 1. 文件系统工具... │
│ 2. CSDN 发布工具... │
│ 3. 微信通知工具..." │
│ │
└─────────────────────────────────────────────────────────────┘

Agent执行链路分析

好像我已经在上面分析完了,看看有没有补充的点

【短期记忆和长期记忆】

  • 上下文窗口中存一些短期记忆就是上几次的回答,保证对话连续性
  • 长期记忆单独存,只保留重要的知识点,保证长时记忆,同时减少token消耗
  • 短期记忆通常存在“内存”或“会话状态”中,而长期记忆则存在“数据库”或“向量存储”中。

【Ai Agent处理过程分类】

  1. 固定N个步骤,这类的一般是配置工作流的,提高任务执行的准确性。如,一些检索资料、发送帖子、处理通知等。
  2. 顺序循环调用,配置 Agent 要执行的多个 Client 端,以此顺序执行。适合一些简单的任务关系,并已经分配好的动作,类似于1的方式。
  3. 智能动态决策,这类是目前市面提供给大家使用的 Agent 比较常见的实现方式,它会动态的规划执行动作,完成行动步骤,观察执行结果,判断完成状态和步骤。并最终给出结果。

【AiSearchMCPTest】

  • AiSearchMCPTest, 搜索 MCP 服务。
  • AutoAgentTest,学习 Agent 执行过程。

先增加一个百度搜索的MCP服务,这个是百度官方的MCP工具,提供搜索功能。
MCPWorld可以找到各种官方MCP工具,这里用百度AI搜索
要先申请百度的key,然后填进去才能建立sse连接,这个key直接走小傅哥的那个第二个网站就行百度智能云
记得还要改测试里面的模型key

这个测试类就是直接用HttpClientSseClientTransport和百度mcp服务的url和key构建了一个sse连接作为mcp工具,传入了chatModel对象中

【AutoAgentTest测试类】

实现了三种类型的AI Agent,功能主要取决初始提示词的内容:

  1. Planning Agent:负责任务规划,将用户需求拆解为可执行的任务列表
  2. Executor Agent:负责执行具体任务,使用工具完成子任务
  3. React Agent:负责快速响应,处理简单查询和即时交互
  • 测试完整的 AutoAgent 工作流程中就会先后使用这几个agent,先任务规划,然后任务执行,然后建一个总结用的提示词,快速响应用来获取总结结果

  • 测试多轮对话:用快速响应Agent,使用相同的conversationId保证记忆,进行多轮对话(PromptChatMemoryAdvisor会自动加载历史对话)

  • 动态多轮执行测试:模拟 PlanningAgent 和 ExecutorAgent 的完整动态执行流程:

    1. 先配置参数,最大执行步数等,初始化执行上下文,就是StringBuilder(执行历史)、String(当前任务状态描述)、boolean(任务是否完成标志位)
    2. 初始化三个角色,任务分析师、精准执行器、质量监督员
    3. 然后进入for循环,进行精准多轮执行
      1. 第一阶段:分析任务,然后进行解析(把 AI 返回的结构化分析结果,以美观的格式打印到控制),如果任务完成度评估为100%,则把标志位改为true,并打破循环
      2. 第二阶段:精准执行,先建一个执行提示词,然后执行解析并打印到控制台
      3. 第三阶段:质量监督,先建一个质量监督检查提示词,然后进行质量检查,然后解析并打印到控制台,根据结果判断打印是否需要重新执行
      4. 更新执行历史,就是新建一个String保存当前各阶段的记录,然后添加进执行历史中
      5. 提取下一步任务,更新任务状态描述,然后进入下一轮循环
      6. 循环结束后进行结果总结,输出结果,然后建个提示词,让快速响应生成总结,然后打印出来流程结束
    • 此案例的重点在于把 固定步骤 处理循环步骤,注意;for (int step = 1; step <= maxSteps && !isCompleted; step++) for 循环里会不断的判断任务是否执行完成,如果没有则会进行分析,自主规划,之后执行。这部分东西,最好要执行验证结果。
    • 此过程很耗费 token,也依赖于LLM,如果比较差的模型,那么可能最后的结果也就不太理想。

Agent执行链路设计

Service新增execute领域,用来控制agent的执行策略

  • armory 是装配 agent 所需的元素,execute 是执行 agent 所需的内核。这部分要想清楚。

  • 之后,本节我们先来实现 auto 自动型的 agent 步骤,也就是把 AutoAgentTest 拆成各个节点。节点包括;Root 根节点,用于数据加载,之后是操作步骤节点 Step1~4,分别执行各自的流程步骤。**1-任务分析、2-精准执行、3-质量监督、4-执行总结。**这块的步骤不要过于拘泥,如果市面上各大厂还有更好的步骤分享,也可以新增加一个套执行过程步骤。类似你看到的我们用过的一些 Agent 也有是有版本迭代升级的,付费后就更好用。

  • ai_agent_flow_config 表,增加 client_name、client_type,用于 AutoAgent 调度不同类型的对话客户端。同时修改对应程序里的 dao、po、mapper。

  • 在 IAgentRepository 增加一个 queryAiAgentClientFlowConfig(String aiAgentId) 方法,来查询 Agent 下,配置的 Client 节点。

  • 在 domain agent model 模型下,把 valobj 下的枚举,专门增加一个枚举的包 enums 存放,方便管理。

  • 增加 ExecuteCommandEntity 执行 agent 请求实体对象,包含,aiAgentId、message、sessionId、maxStep

  • 实现 execute 执行包,以规则树方式实现执行过程,包括;RootNode、Step1AnalyzerNode、Step2PrecisionExecutorNode、Step3QualitySupervisorNode、Step4LogExecutionSummaryNode,各执行节点。这部分可以查看工程代码。

  • 基础层新建一个查询类,根据智能体ID查询流程配置列表,将数据库实体转为VO对象,以 clientType 为 Key,构建Map返回

【节点流程】

  1. RootNode数据加载节点:加载数据,并把数据保存在上下文中,路由到下个节点(路由就是get获取下一个strategyHandler然后执行他的apply方法并返回(节点实现了父类实现的StrategyHandler接口,所以这里的这个就是下一个节点类))
  2. Step1AnalyzerNode - 任务分析节点:第一阶段,先结合上下文中的历史记录建立任务分析提示词(首次设为首次执行),然后获取对话客户端,进行分析并打印,将分析结果保存到动态上下文,检查任务是否完成,如果完成就将标志位设置为true,开始路由(执行本节点的get,get方法会先检验任务是否完成、当前是否已到达最大步数,满足了就进入总结阶段,否则执行下一步)
  3. Step2PrecisionExecutorNode - 精准执行节点:第二阶段,从上下文中获取分析结果,建立执行提示词,获取对话客户端后执行并打印,将结果保存在上下文中,更新历史记录,路由到下个节点,当前节点的get就没有别的逻辑直接返回第三步节点
  4. Step3QualitySupervisorNode - 质量监督节点:第三阶段,从上下文中获取执行结果,建立监督提示词,获取对话客户端执行监督并打印,将监督结果保存到动态上下文中,根据监督结果决定是否需要重新执行,更新执行历史,增加步骤计数,如果任务已经完成或达到最大步数就进入总结阶段,否则就执行下一阶段(回到节点1)
  5. Step4LogExecutionSummaryNode - 执行总结节点:获取客户端执行总结,如果任务未完成生成最终总结报告,完成就返回字符串"ai agent execution summary completed!"

【测试注意】

百度官方MCP的地址配置在数据库了,记得把key改成自己的
数据库模型api也改成自己的,就是sk那个
Navicat的JSON在行里面编辑不方便,可以右键在单元格编辑器中编辑

Agent服务接口和UI对接

开发接口,然后与Ai实现的前端UI界面对接

config包下新建类,实现自动装配
程序启动时,进行自动装配,装配的数据(客户端),配置到 application-dev.yml 中。
trigger层的接口,调用domain领域层的自动agent策略了。trigger接口是sse的异步流式响应接口,所以要渗透到domain领域层增加 ResponseBodyEmitter 发送各个阶段的数据。

【修改说明】

  • app 模块下增加 AiAgentAutoConfiguration 自动装配配置服务,通过拿到 yml 配置文件中的 agent 客户端服务列表进行自动装配。
  • api 模块下增加 IAiAgentService.autoAgent 服务接口,接口的返参为 ResponseBodyEmitter 异步流式响应。
  • trigger 模块下的 http 包,实现 IAiAgentService.autoAgent 服务接口,调用 autoAgentExecuteStrategy 进行结果响应。那么,这里对应的要给 autoAgentExecuteStrategy 入参增加上异步响应参数。
  • 定义自动执行参数响应对象 AutoAgentExecuteResultEntity,包括数据类型(type),动作细节(subType),执行步骤(step)、数据内容(content)、是否完成(completed)、时间戳(timestamp)。同时聚合一些创建对象的方法,包括;创建分析阶段结果、创建分析阶段细分结果、创建执行阶段结果、创建执行阶段细分结果、创建监督阶段结果、创建监督阶段细分结果、总结阶段的结果、创建总结阶段细分的结果、创建错误结果、创建完成标识。
  • 在 AbstractExecuteSupport 抽取通用方法 sendSseResult,通过这样的方式让 Step1~4步都可以调用进行发送过程中数据。

【自动装配实现】

  • dev.yml文件中添加要加载的client-ids
  • config包下新建AiAgentAutoConfiguration类实现自动装配功能
  • config包下新建AiAgentAutoConfigProperties类,用于保存配置文件属性

类注解:

  • @Configuration标记为配置类
  • @EnableConfigurationProperties(AiAgentAutoConfigProperties.class)将配置文件中的属性映射到括号里面的类
  • @ConditionalOnProperty(prefix = "spring.ai.agent.auto-config", name = "enabled", havingValue = "true")条件注解,仅当配置文件中的spring.ai.agent.auto-config.enabled=true 时,此配置才生效

自动装配流程:

  1. 注入AiAgentAutoConfigProperties配置属性类、DefaultArmoryStrategyFactory装配工厂类
  2. 类实现了ApplicationListener<ApplicationReadyEvent>重写onApplicationEvent方法,在Spring Boot 应用完全启动后自动触发,重写方法中就是自动配置逻辑
  3. 从注入的配置属性类中获取并解析client-ids列表
  4. 从装配工厂类中获取armoryStrategyHandler(RootNode)并调用apply方法开始装配

【异步通知】

  • 之前execute领域内的IExecuteStrategy接口,新增入参ResponseBodyEmitter流式响应参数,用于 AutoAgent 策略执行中的结果返回操作。
  • ResponseBodyEmitter 是 Spring MVC 提供的一个类,用于异步地向 HTTP 响应体写入数据。它实现了异步非阻塞的响应输出,允许服务器端在请求处理完成之前,分多次发送数据给客户端。
  • 定义一个AutoAgentExecuteResultEntity类,封装返回结果
  • 抽象方法中新增sendSseResult方法,内部从上下文中获取结果并用emitter发送给前端,然后step1~4节点中就可以调用这个方法,就在每个节点解析结果后就可以调用这个方法

【接口】

  • trigger模块下建立AiAgentController
  • 注入IExecuteStrategy提供execute方法
  • 创建流式输出对象:emitter
  • 构建执行命令体:填入请求里的参数
  • 异步执行AutoAgent:调用execute

【测试】

打开前端页面,数据库改改mcp的设置,模型api换成自己的就行

【库表回顾】

  • ai_client:执行链路里面每一个节点都是一个client,比如任务分析师,需要初始提示词、顾问、模型、MCP工具。这几个属性就分别是几个表,他们的对应关系存在ai_client_cofig中,装配时就用这个查出来
  • ai_agent:多个client组成一个链路就成了agent(智能体),agent有个渠道类型字段(目前就agent智能体,chat_stram对话流)
  • ai_agent_flow_config:存agent的配置信息包括,agent与client的对应关系,客户端名称,客户端类型,序列号(执行顺序)
  • 上面自动装配装的是client不是agent,agent就是装配好的client成链路处理
  • ai_client_api:存模型key,模型通用
  • ai_agent_task_schedule:智能体定时任务表,例如自动发帖任务,交给对应智能体执行
  • ai_client_rag_order:rag:表存知识库信息

Agent-ELK日志分析场景

增加 Agent-ELK 日志分析的实际应用场景,通过 Agent 根据用户诉求,自主分析、规划、执行和输出结果,来帮助我们对日志检索的提效。

ELK(Elasticsearch、Logstash、Kibanna或自研) 是各个互联网公司中都有的一套分布式日志设备,以便于研发在遇到线上系统报警和运营反馈事故问题时,快速检索日志。但往往这种检索的日志的方式都是非常耗时的,所以增加 Agent 方式来辅助提效是非常有必要的。

【ELK日志只能分析系统流程】

  • 阶段一:执行模拟脚本,生成模拟日志(创建日志索引的es索引,生成模拟限流日志并写入)
  • 阶段二:将日志存到Elasticsearch(日志存储),然后Logstash日志处理、Kibana可视化界面
  • 阶段三:Agent配置,配置Agent和MCP连接
  • 阶段四:自动智能分析:日志分析、精准执行(执行结果会调用MCP工具存到ES中)、质量监督、执行总结

【编码实现】

ai_client_system_prompt:增加提示词,定义动态限流查询分析师,智能限流查询执行器、监督员、总结师的角色
ai_client_tool_mcp:增加新MCP工具,调用npx工具使用@awesome-ai/elasticsearch-mcp包

【测试】

  • 执行模拟日志脚本,但是脚本有中文utf-8编码bug我懒得处理了,直接让ai根据错误修改sh文件了,要将中文全改成英文,然后时间戳生成改成兼容windos的。改好了就能在git bash中./执行了
  • 然后Stack Management索引管理那里就能看到这个新建的索引,然后下面索引模式新建这个索引的索引模式模式

es的Management下的Stack Management中可以管理索引
es的Analytics下的Discover中可以查看日志
观察日志http://127.0.0.1:5601/app/discover他给每个日志都进行了分词,然后就可以用词去找对应的日志,上面的那个搜索框都有提示,比如我要搜id等于什么的日志_id : _Or70ZwBYNWvAkmTxUOw

之后就可以在前端ui直接向智能体发信息,例如通过ES查询被限流的用户,输出被限流的用户列表,智能体就会调用elasticsearch-mcp-server工具去查询,这个还要配置es的key,我就先不试了


Ai Agent 智能体笔记
http://www.981928.xyz/2026/03/01/Ai-Agent-智能体笔记/
作者
981928
发布于
2026年3月1日
许可协议