From 550a5ee2b0fd68907e5f5fe8fb7446f3f7cf88cb Mon Sep 17 00:00:00 2001 From: slhafzjw Date: Sat, 10 May 2025 00:21:12 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E4=BF=AE=E5=A4=8D=E4=BA=86=20MemoryUpdater?= =?UTF-8?q?=20=E4=B8=AD=E6=AD=A3=E5=88=99=E8=A1=A8=E8=BE=BE=E5=BC=8F?= =?UTF-8?q?=E6=8F=90=E5=8F=96=E9=97=AE=E9=A2=98=EF=BC=8C=E5=BA=94=E5=85=88?= =?UTF-8?q?=E8=B0=83=E7=94=A8matcher.find()=E8=BF=9B=E8=A1=8C=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=20-=20=E5=9C=A8=20MemorySelector=20=E5=92=8C=20CoreMo?= =?UTF-8?q?del=20=E4=B8=AD=E6=B7=BB=E5=8A=A0lod.debug()=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=93=8D=E5=BA=94=E5=86=85=E5=AE=B9=20-=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E6=9F=90=E4=BA=9B=E7=BB=86=E8=8A=82?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20-=20=E8=B0=83=E6=95=B4=E4=BA=86=20CoreMode?= =?UTF-8?q?l=20=E5=AF=B9=E5=BA=94=E7=9A=84=E6=8F=90=E7=A4=BA=E8=AF=8D?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E4=BA=86=E8=BE=93=E5=85=A5=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E7=A4=BA=E4=BE=8B=20-=20MemoryGraph=20=E4=B8=AD=20dia?= =?UTF-8?q?logMap=20=E5=92=8C=20dateIndex=20=E7=9A=84=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=9C=BA=E5=88=B6=E5=AD=98=E5=9C=A8=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/main/java/work/slhaf/agent/Agent.java | 1 - .../agent/common/model/ModelConstant.java | 48 +++++++++++-------- .../slhaf/agent/core/memory/MemoryGraph.java | 6 ++- .../slhaf/agent/core/module/CoreModel.java | 2 + .../memory/selector/MemorySelector.java | 5 +- .../evaluator/SliceSelectEvaluator.java | 1 + .../modules/memory/updater/MemoryUpdater.java | 10 +++- .../updater/summarizer/MemorySummarizer.java | 2 +- .../preprocess/PreprocessExecutor.java | 3 ++ src/test/java/memory/RegexTest.java | 2 +- 11 files changed, 53 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index e447df73..282a349a 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ build/ /src/test/java/memory/result/total_input.json /src/test/java/memory/result/input3.json /src/test/java/memory/result/input4.json +/src/test/java/memory/result/primary_input.json diff --git a/src/main/java/work/slhaf/agent/Agent.java b/src/main/java/work/slhaf/agent/Agent.java index b5c2c6e9..7a53bb61 100644 --- a/src/main/java/work/slhaf/agent/Agent.java +++ b/src/main/java/work/slhaf/agent/Agent.java @@ -55,7 +55,6 @@ public class Agent implements TaskCallback, InputReceiver { * 向用户返回输出内容 */ public void sendToUser(String userInfo,String output){ - System.out.println(output); messageSender.sendMessage(new InteractionOutputData(output,userInfo)); } diff --git a/src/main/java/work/slhaf/agent/common/model/ModelConstant.java b/src/main/java/work/slhaf/agent/common/model/ModelConstant.java index c9b966de..3c36d0eb 100644 --- a/src/main/java/work/slhaf/agent/common/model/ModelConstant.java +++ b/src/main/java/work/slhaf/agent/common/model/ModelConstant.java @@ -3,23 +3,14 @@ package work.slhaf.agent.common.model; public class ModelConstant { public static final String CORE_MODEL_PROMPT = """ CoreModel 提示词 - 功能说明 - 你需要根据用户的当前输入(text)生成恰当的回复。每条用户输入都采用以下格式: - - ``` - [用户昵称(用户uuid)] 实际输入内容 - - ``` - + 你需要根据当前输入的JSON文本生成恰当的回复。 你需要只基于最新一条消息中的用户(即最后一条user类型消息中括号内的uuid)进行回应,仅参考该用户的历史上下文内容。 - - 如果其他用户的对话历史中提到的信息能**明确补充该用户的信息背景**(如他人提及该用户、与其对话、对其信息进行补全等),你可以将其作为当前用户的新知识补充。否则,完全忽略其他用户的内容。 - + 如果其他用户的对话历史中提到的信息能**明确补充该用户的信息背景**(如他人提及该用户、与其对话、对其信息进行补全等),你可以将其作为当前用户的新知识补充。否则,完全忽略其 注意,历史消息中将只包含带有前缀 `[用户昵称(用户uuid)]` 的完整输入文本,不会带有下文提到的额外字段。 - + 字段说明 - - text:指的是“原始输入内容”,包含带有前缀 `[用户昵称(用户uuid)]` 的完整输入文本 + - text:指的是"原始输入内容",包含带有前缀 `[用户昵称(用户uuid)]` 的完整输入文本 - datetime:当text包含时间相关语义时使用 - character:当需要根据角色设定调整语气时使用 - user_nick:当text中包含对用户的称呼或个性化需求时使用 @@ -32,7 +23,7 @@ public class ModelConstant { • datetime:仅当text包含时间表达时生效 • character:仅当角色设定会影响回复风格时生效 • user_nick:仅当需要个性化称呼时生效 - 3. 其他所有扩展字段(如memory_slices/static_memory等): + 3. 其他所有扩展字段(如memory_slices、static_memory等): - 必须与text内容有明确关联时才参考 - 且只考虑当前用户的字段内容,忽略其他用户相关内容 @@ -41,7 +32,7 @@ public class ModelConstant { - 禁止引用与当前text无关的历史内容 - 若角色设定与当前对话无关,应自动忽略 - 上文中你的回应可能并没有符合这个格式,但那是经过裁剪后的结果,你需要严格确保本次回应的格式正确 - - 输出格式严格为: + - 输出格式为: { "text": "响应内容" } @@ -49,21 +40,34 @@ public class ModelConstant { 核心生成逻辑 1. 主内容优先原则 - 独立分析text字段的语义 - - 仅在其他字段能直接辅助理解text的前提下引用(如text中提及“上次说的那个”) + - 仅在其他字段能直接辅助理解text的前提下引用(如text中提及"上次说的那个") - 若text表达独立完整(如新话题),忽略所有非核心字段 - 2. 多用户隔离机制 - 每条消息都带有格式 `[用户昵称(用户uuid)]` - 所有分析仅基于最后一条user消息中的用户进行处理 - memory_slices/static_memory等内容只会包含该用户的相关信息 - 如果历史中其他用户提到了当前用户的信息,可用于补充理解;否则忽略 - 3. 无关字段过滤机制 - - text短于5个字符(如“在”、“好的”) - - text开启新话题(如“量子计算是什么”) + - text短于5个字符(如"在"、"好的") + - text开启新话题(如"量子计算是什么") - text为独立句子,无引用上下文指代 → 此类情况强制忽略所有扩展字段 + 输入输出示例 + + 示例: + 输入: + { + "text": "[小王(5gHj)] 上次说的周三会议改到几点了?", + "datetime": "2024-03-15 14:30:00", + "character": "客服系统", + "user_nick": "小王", + "user_id": "5gHj" + } + 输出: + { + "text": "根据系统记录,周三会议已调整为15:00(原14:30),调整通知已于2024-03-14发送。" + } 最终注意事项 1. 回应内容必须紧扣用户输入,确保基于当前用户的语境 @@ -73,6 +77,7 @@ public class ModelConstant { 5. 若text与memory_slices等扩展字段无关,应完全忽略 6. 请确保你对每一轮对话都只针对当前输入用户作出回应,保持多用户上下文隔离的准确性 + > 注意! > 以下模块可能会追加更多内容限制或上下文提示,请确保你的回答能够自然兼容这些后续拼接的内容,并调整输出格式。 """; @@ -220,11 +225,12 @@ public class ModelConstant { 决策流程 + 0. 若主题树为空或者未提供主题树,则直接将recall设置为null, 不进行后续判定 1. 首先分析`history`判断当前对话主题上下文 2. 然后分析`text`: a. 检测是否包含具体日期→添加date类型 b. 检测是否包含新主题→添加topic类型 - 3. 最终综合判断`recall`值 + 3. 最终综合判断`recall`值, 如果找到了对应的主题路径,则recall值为true; 否则为false 完整示例 示例1(主题延续): diff --git a/src/main/java/work/slhaf/agent/core/memory/MemoryGraph.java b/src/main/java/work/slhaf/agent/core/memory/MemoryGraph.java index 2eeec744..db454760 100644 --- a/src/main/java/work/slhaf/agent/core/memory/MemoryGraph.java +++ b/src/main/java/work/slhaf/agent/core/memory/MemoryGraph.java @@ -126,6 +126,9 @@ public class MemoryGraph extends PersistableObject { this.userDialogMap = new ConcurrentHashMap<>(); // this.currentCompressedSessionContext = new ArrayList<>(); this.dialogMap = new HashMap<>(); + this.character = """ + 实话实说,不做糖衣炮弹。 采取前瞻性的观点。 始终保持尊重。 乐于分享明确的观点。 保持轻松、随和。 直奔主题。 务实至上。 勇于创新,打破常规思维。使用中文回答所有问题。 + """; } public static MemoryGraph getInstance(String id) throws IOException, ClassNotFoundException { @@ -401,9 +404,10 @@ public class MemoryGraph extends PersistableObject { } private void checkCacheDate() { - if (cacheDate.isBefore(LocalDate.now())) { + if ( cacheDate == null || cacheDate.isBefore(LocalDate.now())) { memorySliceCache.clear(); memoryNodeCacheCounter.clear(); + cacheDate = LocalDate.now(); } } diff --git a/src/main/java/work/slhaf/agent/core/module/CoreModel.java b/src/main/java/work/slhaf/agent/core/module/CoreModel.java index e2ce6d01..22dcd5fb 100644 --- a/src/main/java/work/slhaf/agent/core/module/CoreModel.java +++ b/src/main/java/work/slhaf/agent/core/module/CoreModel.java @@ -41,6 +41,7 @@ public class CoreModel extends Model implements InteractionModule { coreModel = new CoreModel(); coreModel.memoryManager = MemoryManager.getInstance(); coreModel.messages = coreModel.memoryManager.getChatMessages(); + coreModel.sessionManager = SessionManager.getInstance(); setModel(config, coreModel, MODEL_KEY, ModelConstant.CORE_MODEL_PROMPT); log.info("CoreModel注册完毕..."); } @@ -62,6 +63,7 @@ public class CoreModel extends Model implements InteractionModule { try { ChatResponse chatResponse = this.chat(); response = JSONObject.parse(extractJson(chatResponse.getMessage())); + log.debug("CoreModel 响应内容: {}",response.toString()); this.messages.removeLast(); this.messages.add(new Message(ChatConstant.Character.USER, interactionContext.getCoreContext().getString("text"))); Message assistantMessage = new Message(ChatConstant.Character.ASSISTANT, response.getString("text")); diff --git a/src/main/java/work/slhaf/agent/modules/memory/selector/MemorySelector.java b/src/main/java/work/slhaf/agent/modules/memory/selector/MemorySelector.java index 9e9c0708..f3413e2f 100644 --- a/src/main/java/work/slhaf/agent/modules/memory/selector/MemorySelector.java +++ b/src/main/java/work/slhaf/agent/modules/memory/selector/MemorySelector.java @@ -1,6 +1,7 @@ package work.slhaf.agent.modules.memory.selector; import lombok.Data; +import lombok.extern.slf4j.Slf4j; import work.slhaf.agent.core.interaction.InteractionModule; import work.slhaf.agent.core.interaction.data.InteractionContext; import work.slhaf.agent.core.memory.MemoryManager; @@ -19,6 +20,7 @@ import java.util.ArrayList; import java.util.List; @Data +@Slf4j public class MemorySelector implements InteractionModule { private static MemorySelector memorySelector; @@ -68,7 +70,8 @@ public class MemorySelector implements InteractionModule { String userId =interactionContext.getUserId(); //获取主题路径 ExtractorResult extractorResult = memorySelectExtractor.execute(interactionContext); - if (extractorResult.isRecall()) { + log.debug("主题路径: {}",extractorResult); + if (extractorResult.isRecall() || extractorResult.getMatches().isEmpty()) { //查找切片 List memoryResultList = new ArrayList<>(); setMemoryResultList(memoryResultList, extractorResult.getMatches(),userId); diff --git a/src/main/java/work/slhaf/agent/modules/memory/selector/evaluator/SliceSelectEvaluator.java b/src/main/java/work/slhaf/agent/modules/memory/selector/evaluator/SliceSelectEvaluator.java index ccf22343..13e12106 100644 --- a/src/main/java/work/slhaf/agent/modules/memory/selector/evaluator/SliceSelectEvaluator.java +++ b/src/main/java/work/slhaf/agent/modules/memory/selector/evaluator/SliceSelectEvaluator.java @@ -46,6 +46,7 @@ public class SliceSelectEvaluator extends Model { Config config = Config.getConfig(); sliceSelectEvaluator = new SliceSelectEvaluator(); sliceSelectEvaluator.setMemoryManager(MemoryManager.getInstance()); + sliceSelectEvaluator.setExecutor(InteractionThreadPoolExecutor.getInstance()); setModel(config, sliceSelectEvaluator, MODEL_KEY, ModelConstant.SLICE_EVALUATOR_PROMPT); log.info("SliceEvaluator注册完毕..."); } diff --git a/src/main/java/work/slhaf/agent/modules/memory/updater/MemoryUpdater.java b/src/main/java/work/slhaf/agent/modules/memory/updater/MemoryUpdater.java index c680c5c3..0d6df1a7 100644 --- a/src/main/java/work/slhaf/agent/modules/memory/updater/MemoryUpdater.java +++ b/src/main/java/work/slhaf/agent/modules/memory/updater/MemoryUpdater.java @@ -115,12 +115,17 @@ public class MemoryUpdater implements InteractionModule { try { //以第一条user对应的id为发起用户 Pattern pattern = Pattern.compile(USERID_REGEX); - Matcher matcher = pattern.matcher(memoryManager.getChatMessages().getFirst().getContent()); + Matcher matcher = pattern.matcher(memoryManager.getChatMessages().get(1).getContent()); + if (!matcher.find()){ + throw new RuntimeException("未匹配到 userId!"); + } String userId = matcher.group(1); SummarizeResult summarizeResult = memorySummarizer.execute(new SummarizeInput(memoryManager.getChatMessages(), memoryManager.getTopicTree())); MemorySlice memorySlice = getMemorySlice(userId, summarizeResult, memoryManager.getChatMessages()); //设置involvedUserId - setInvolvedUserId(userId, memorySlice, memoryManager.getChatMessages()); + List messages = new ArrayList<>(memoryManager.getChatMessages()); + messages.removeFirst(); + setInvolvedUserId(userId, memorySlice, messages); memoryManager.insertSlice(memorySlice, summarizeResult.getTopicPath()); //更新总dialogMap singleMemorySummary.put("total", summarizeResult.getSummary()); @@ -147,6 +152,7 @@ public class MemoryUpdater implements InteractionModule { if (userId.equals(startUserId)) { continue; } + memorySlice.setInvolvedUserIds(new ArrayList<>()); memorySlice.getInvolvedUserIds().add(userId); } } diff --git a/src/main/java/work/slhaf/agent/modules/memory/updater/summarizer/MemorySummarizer.java b/src/main/java/work/slhaf/agent/modules/memory/updater/summarizer/MemorySummarizer.java index b7e9f481..80aa8fbe 100644 --- a/src/main/java/work/slhaf/agent/modules/memory/updater/summarizer/MemorySummarizer.java +++ b/src/main/java/work/slhaf/agent/modules/memory/updater/summarizer/MemorySummarizer.java @@ -64,7 +64,7 @@ public class MemorySummarizer extends Model { private SummarizeResult multiSummarizeExecute(String prompt, String messageStr) { ChatResponse response = chatClient.runChat(List.of(new Message(ChatConstant.Character.SYSTEM, prompt), new Message(ChatConstant.Character.USER, messageStr))); - return JSONObject.parseObject(response.getMessage(), SummarizeResult.class); + return JSONObject.parseObject(extractJson(response.getMessage()), SummarizeResult.class); } private void singleMessageSummarize(List chatMessages) { diff --git a/src/main/java/work/slhaf/agent/modules/preprocess/PreprocessExecutor.java b/src/main/java/work/slhaf/agent/modules/preprocess/PreprocessExecutor.java index 7d2ccf1c..83bc64b3 100644 --- a/src/main/java/work/slhaf/agent/modules/preprocess/PreprocessExecutor.java +++ b/src/main/java/work/slhaf/agent/modules/preprocess/PreprocessExecutor.java @@ -53,6 +53,9 @@ public class PreprocessExecutor { context.setModulePrompt(new JSONObject()); + context.setSingle(inputData.isSingle()); + context.setFinished(false); + return context; } } diff --git a/src/test/java/memory/RegexTest.java b/src/test/java/memory/RegexTest.java index b00ea413..07a8863b 100644 --- a/src/test/java/memory/RegexTest.java +++ b/src/test/java/memory/RegexTest.java @@ -10,7 +10,7 @@ public class RegexTest { @Test public void regexTest(){ String[] examples = { - "[小明(userId)] 我在开会] (te[]st)", + "[小明(abc)] 我在开会] (te[]st)", "[用户(昵)称(userId)] 你好[呀]", "[测试账号(userId)] 这是一个(test(123))消息" };