- 修复了 MemoryUpdater 中正则表达式提取问题,应先调用matcher.find()进行匹配

- 在 MemorySelector 和 CoreModel 中添加lod.debug()输出模块响应内容
- 修复了某些细节问题
- 调整了 CoreModel 对应的提示词,添加了输入输出示例
- MemoryGraph 中 dialogMap 和 dateIndex 的更新机制存在问题,需要修复
This commit is contained in:
2025-05-10 00:21:12 +08:00
parent 4f6a0a8b2a
commit 550a5ee2b0
11 changed files with 53 additions and 28 deletions

1
.gitignore vendored
View File

@@ -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

View File

@@ -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));
}

View File

@@ -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_slicesstatic_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主题延续

View File

@@ -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();
}
}

View File

@@ -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"));

View File

@@ -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<MemoryResult> memoryResultList = new ArrayList<>();
setMemoryResultList(memoryResultList, extractorResult.getMatches(),userId);

View File

@@ -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注册完毕...");
}

View File

@@ -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<Message> 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);
}
}

View File

@@ -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<Message> chatMessages) {

View File

@@ -53,6 +53,9 @@ public class PreprocessExecutor {
context.setModulePrompt(new JSONObject());
context.setSingle(inputData.isSingle());
context.setFinished(false);
return context;
}
}

View File

@@ -10,7 +10,7 @@ public class RegexTest {
@Test
public void regexTest(){
String[] examples = {
"[小明(userId)] 我在开会] (te[]st)",
"[小明(abc)] 我在开会] (te[]st)",
"[用户(昵)称(userId)] 你好[呀]",
"[测试账号(userId)] 这是一个(test(123))消息"
};