JeecgBoot 记忆库即插件架构
JeecgBoot 记忆库即插件架构
学习目标
通过本章学习,你将能够:
- 理解记忆库与知识库的区别
- 掌握记忆库转换为 MCP 插件的实现机制
- 了解 add_memory / query_memory 工具的定义与自动触发策略
- 了解可插拔记忆在多 AI 应用中的管理方案
前置知识
本章涉及记忆系统的通用原理,建议先阅读:
下文假设你已理解上述概念,直接聚焦 JeecgBoot 的具体实现。
项目实践
记忆库 vs 知识库
在 JeecgBoot 中,记忆库和知识库共享同一套底层存储(AiragKnowledge 表),通过 type 字段区分:
| 维度 | 知识库(knowledge) | 记忆库(memory) |
|---|---|---|
| 用途 | 领域知识检索(RAG) | 用户个人信息持久化 |
| 检索方式 | 向量相似度检索 | 工具调用(add/query) |
| 更新频率 | 低(管理员上传文档) | 高(AI 自动检测并追加) |
| 内容类型 | 文档、网页、FAQ | 用户偏好、历史交互记录 |
记忆库转插件
记忆库通过 getPluginMemory() 方法转换为标准的 MCP 插件格式:
// 伪代码:getPluginMemoryMap<String, Object> getPluginMemory(String memoryId) { AiragKnowledge knowledge = knowledgeMapper.selectById(memoryId); if (knowledge == null) return null;
// 构建 AiragMcp(插件格式) AiragMcp tool = new AiragMcp(); tool.setId(memoryId); tool.setName("memory"); tool.setDescr("用户长期记忆管理"); tool.setCategory("plugin");
// 定义两个工具:add_memory 和 query_memory JSONArray toolsArray = new JSONArray(); toolsArray.add(buildAddMemoryTool(memoryId, knowledge.getDescr())); toolsArray.add(buildQueryMemoryTool(memoryId, knowledge.getDescr())); tool.setTools(toolsArray.toJSONString());
// 元数据(认证 + 租户) tool.setMetadata(buildMetadata(tenantId));
// 转换为 ToolSpecification + ToolExecutor Map<ToolSpecification, ToolExecutor> tools = PluginToolBuilder.buildTools(tool, request);
return Map.of("pluginTool", tools, "pluginId", memoryId);}关键设计:转换后的记忆库在 AI 眼中就是一个普通的 MCP 插件。LLM 通过工具调用的方式读写记忆,而非通过内置的记忆模块。
add_memory 工具定义
{ "name": "add_memory", "description": "【自动触发】向记忆库添加长期信息。范围:{记忆库描述}。必须在检测到相关信息时立即自动调用,无需用户指令。", "path": "/api/airag/knowledge/addMemory", "method": "POST", "enabled": true, "parameters": [ { "name": "knowledgeId", "description": "知识库ID,需要原值传递,不允许修改", "type": "string", "location": "body", "required": true, "defaultValue": "{memoryId}" }, { "name": "content", "description": "记忆内容。格式要求:'在yyyy年MM月dd日 HH:mm分,用户[用户的行为/问题],assistant[助手的回答/反应]。'", "type": "string", "location": "body", "required": true }, { "name": "title", "description": "记忆标题", "type": "string", "location": "body", "required": true } ]}自动触发设计:描述中的 “【自动触发】” 前缀是关键。它告诉 LLM:
不要等待用户要求你添加记忆——当你检测到用户的个人信息(如姓名、职业、偏好等)时,立即调用此工具。
描述前缀中的 “范围:{记忆库描述}” 告诉 LLM 哪些信息应该存储:如果记忆库描述是”用户的编程偏好和工作环境信息”,那么 LLM 只在检测到这类信息时调用 add_memory。
query_memory 工具定义
{ "name": "query_memory", "description": "查询记忆库中的长期信息。当需要了解用户偏好、历史交互或个性化信息时调用。", "path": "/api/airag/knowledge/queryMemory", "method": "POST", "enabled": true, "parameters": [ { "name": "knowledgeId", "description": "知识库ID,需要原值传递", "type": "string", "required": true }, { "name": "query", "description": "查询关键词。使用用户的问题作为查询条件。", "type": "string", "required": true } ]}记忆库绑定到 AI 应用
记忆库通过应用配置绑定到具体的 AI 应用:
AiragApp 实体├── memoryId # 关联的记忆库 ID├── izOpenMemory # 是否开启记忆(1=开启,0=关闭)├── plugins # 插件列表(包含记忆库插件)└── ...在 sendWithAppChat 方法中:
// 开启记忆库时,将记忆库转换为插件if (izOpenMemory && memoryId 非空) { Map<String, Object> pluginMemory = airagKnowledgeService.getPluginMemory(memoryId); addPluginToParams(aiChatParams, pluginMemory);}记忆内容的结构化格式
记忆内容使用统一的模板格式存储:
在2025年06月01日 10:30分,用户[提到他是一名 Java 后端开发者,使用 Spring Boot 框架,偏好使用 MySQL 数据库],assistant[推荐使用 JeecgBoot 低代码平台可以快速搭建管理系统]。格式优势:
- 时间戳确保记忆有时效性
- 结构化格式便于后续检索和回顾
- LLM 容易理解这种自然语言格式
记忆库与 RAG 知识库的区别
虽然记忆库底层使用与知识库相同的技术(pgvector 向量存储),但访问方式不同:
| 维度 | 知识库(RAG) | 记忆库(插件) |
|---|---|---|
| 触发方式 | 隐式(每次聊天自动检索) | 显式(LLM 自主决定调用工具) |
| 检索时机 | 在 LLM 调用前检索 | 在 LLM 响应过程中按需调用 |
| 结果注入 | 检索结果追加到 Prompt | 工具返回结果由 LLM 自行处理 |
| 可控性 | 平台控制(配置 topK/相似度) | LLM 控制(自主决定查什么) |
问题与规避
| 陷阱 | 表现 | 对策 |
|---|---|---|
| 记忆库描述为空 | add_memory 不知道应该存储什么类型的信息 | 描述前缀使用 oConvertUtils.isEmpty(descr) ? "按记忆库描述允许的个人资料..." : descr 兜底 |
| LLM 过度调用 add_memory | 每条无关信息都被存储 | 在描述中明确范围限定(“范围:个人资料、偏好、属性”) |
| 记忆库关闭后仍注入 | 应用配置关闭记忆但插件仍在工具列表中 | izOpenMemory 检查,关闭时不调用 getPluginMemory |
| 多应用共享记忆库 | 不同应用读到同一个用户的不同记忆 | 记忆库绑定到具体应用,应用级隔离 |
设计取舍
工具调用 vs 隐式检索
JeecgBoot 选择:记忆库通过工具调用访问,而非隐式 RAG 检索。
| 方案 | 优势 | 代价 |
|---|---|---|
| 工具调用(当前方案) | LLM 自主控制检索时机,更灵活 | 需要 LLM 正确理解何时调用 |
| 隐式检索 | 无需 LLM 决策,每次聊天自动检索 | 每次聊天都检索,可能不需要的信息也被注入 |
记忆的特点是”按需获取”——不是每次聊天都需要回忆用户信息,只在对话涉及个人信息时才需要。工具调用模式更符合这一特点。
记忆格式:结构化 JSON vs 自然语言
JeecgBoot 选择:自然语言模板格式。
| 方案 | 优势 | 代价 |
|---|---|---|
| 自然语言模板 | LLM 容易理解和生成 | 不利于程序化分析 |
| 结构化 JSON | 程序容易解析和查询 | LLM 生成时需要遵循严格的 JSON 格式 |
在记忆系统中,主要消费者是 LLM(读取和写入),而非程序查询,自然语言格式更适合。
参考来源
- JeecgBoot 源码 v3.9.2,
AiragKnowledgeServiceImpl.java - langchain4j 工具系统文档