初步完善感知相关模块,提示词待设计:

- 修复了`Config`中生成的配置文件的模块链未加入`PostprocessExecutor`的问题
- 发现`InteractionHub`中还留有未使用的`coreModel`、`taskScheduler`,已删除
- 将`PerceiveUpdater`感知更新模块的提取逻辑下放到感知子模块`RelationExtractor`和`StaticMemoryExtractor`,感知更新主模块只负责将两个子模块的执行进行并发以及整合结果,最终提交给`PerceiveCapability`进行更新
-
This commit is contained in:
2025-07-07 16:26:04 +08:00
parent 9302417e58
commit a1d3c1e295
13 changed files with 201 additions and 116 deletions

View File

@@ -17,8 +17,9 @@
### 针对LLM的'自我引导'机制 ### 针对LLM的'自我引导'机制
> 通过特定的交互对话, 引导LLM产生一定的'自我定位'特征, 但似乎大多数模型都不太适合(要么幻觉严重, 要么工具底色太强), 经测试, qwen3系列的qwen-plus-latest、qwen-max-latest比较合适. > 通过特定的交互对话, 引导LLM产生一定的'自我定位'特征, 但似乎大多数模型都不太适合(要么幻觉严重, 要么工具底色太强), 经测试, qwen3系列的qwen-plus-latest、qwen-max-latest比较合适.
## 模块实现 ## 模块(已实现/正在实现)
- 预处理模块: `Preprocessor` - 预处理模块: `PreprocessExecutor`
- 后处理模块: `PostprocessExecutor`
- 记忆模块 - 记忆模块
- 记忆选择模块: `MemorySelector` - 记忆选择模块: `MemorySelector`
- 主题提取模块: `MemorySelectExtractor` - 主题提取模块: `MemorySelectExtractor`
@@ -26,6 +27,11 @@
- 记忆更新模块: `MemoryUpdater` - 记忆更新模块: `MemoryUpdater`
- 记忆总结模块: `MemorySummarizer` - 记忆总结模块: `MemorySummarizer`
- 静态记忆提取模块: `StaticMemoryExtractor` - 静态记忆提取模块: `StaticMemoryExtractor`
- 感知模块
- 感知选择模块: `PerceiveSelector`
- 感知更新模块: `PerceiveUpdater`
- 关系提取模块: `RelationExtractor`
- 静态记忆提取模块: `StaticExtractor`
- 主对话模块: `CoreModel` - 主对话模块: `CoreModel`
## 当前问题 ## 当前问题
@@ -37,11 +43,10 @@
- [ ] 调整模块加载机制,将记忆模块以及后续的任务调度模块作为不可替换的核心模块,但允许在主模块与前后模块之间添加新的模块。 - [ ] 调整模块加载机制,将记忆模块以及后续的任务调度模块作为不可替换的核心模块,但允许在主模块与前后模块之间添加新的模块。
- [ ] 实现流式输出,同时在各模块执行时可向客户端返回回调信息,优化使用体验。(现在用的是`websocket`与客户端通信, 应该实现这点会简单些) - [ ] 实现流式输出,同时在各模块执行时可向客户端返回回调信息,优化使用体验。(现在用的是`websocket`与客户端通信, 应该实现这点会简单些)
- [ ] 服务端与客户端的通信加上消息队列,防止消息因连接断开而丢失。 - [ ] 服务端与客户端的通信加上消息队列,防止消息因连接断开而丢失。
- [ ] 踩坑。
### 长期规划
- [ ] 实现角色演进机制 - [ ] 实现角色演进机制
- [ ] 实现任务调度模块(主动调度、意图推断、定时调度) - [ ] 实现任务调度模块(主动调度、意图推断、定时调度)
- [ ] 实现任务与主动调度模块,目前打算用 `时间轮算法` 实现定时操作
- [ ] 踩坑。
## License ## License

View File

@@ -8,6 +8,7 @@ import org.apache.commons.io.FileUtils;
import work.slhaf.agent.module.modules.core.CoreModel; import work.slhaf.agent.module.modules.core.CoreModel;
import work.slhaf.agent.module.modules.memory.selector.MemorySelector; import work.slhaf.agent.module.modules.memory.selector.MemorySelector;
import work.slhaf.agent.module.modules.memory.updater.MemoryUpdater; import work.slhaf.agent.module.modules.memory.updater.MemoryUpdater;
import work.slhaf.agent.module.modules.process.PostprocessExecutor;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -98,6 +99,7 @@ public class Config {
List<ModuleConfig> moduleConfigList = List.of( List<ModuleConfig> moduleConfigList = List.of(
new ModuleConfig(MemorySelector.class.getName(), ModuleConfig.Constant.INTERNAL, null), new ModuleConfig(MemorySelector.class.getName(), ModuleConfig.Constant.INTERNAL, null),
new ModuleConfig(CoreModel.class.getName(), ModuleConfig.Constant.INTERNAL, null), new ModuleConfig(CoreModel.class.getName(), ModuleConfig.Constant.INTERNAL, null),
new ModuleConfig(PostprocessExecutor.class.getName(),ModuleConfig.Constant.INTERNAL,null),
new ModuleConfig(MemoryUpdater.class.getName(), ModuleConfig.Constant.INTERNAL, null) new ModuleConfig(MemoryUpdater.class.getName(), ModuleConfig.Constant.INTERNAL, null)
// new ModuleConfig(TaskScheduler.class.getName(), ModuleConfig.Constant.INTERNAL, null) // new ModuleConfig(TaskScheduler.class.getName(), ModuleConfig.Constant.INTERNAL, null)
); );

View File

@@ -25,8 +25,6 @@ public class InteractionHub {
@ToString.Exclude @ToString.Exclude
private TaskCallback callback; private TaskCallback callback;
private CoreModel coreModel;
private TaskScheduler taskScheduler;
private List<InteractionModule> interactionModules; private List<InteractionModule> interactionModules;
public static InteractionHub initialize() throws IOException { public static InteractionHub initialize() throws IOException {

View File

@@ -82,6 +82,7 @@ public class PerceiveCore extends PersistableObject {
user.setImpressions(temp.getImpressions()); user.setImpressions(temp.getImpressions());
user.setAttitude(temp.getAttitude()); user.setAttitude(temp.getAttitude());
user.setStaticMemory(temp.getStaticMemory()); user.setStaticMemory(temp.getStaticMemory());
user.updateRelationChange(user.getRelationChange());
usersLock.unlock(); usersLock.unlock();
} }
} }

View File

@@ -5,8 +5,10 @@ import lombok.EqualsAndHashCode;
import work.slhaf.agent.common.serialize.PersistableObject; import work.slhaf.agent.common.serialize.PersistableObject;
import java.io.Serial; import java.io.Serial;
import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@@ -24,12 +26,23 @@ public class User extends PersistableObject {
// private HashMap<LocalDate, String> events = new HashMap<>(); // private HashMap<LocalDate, String> events = new HashMap<>();
private List<String> impressions = new ArrayList<>(); private List<String> impressions = new ArrayList<>();
private List<String> attitude = new ArrayList<>(); private List<String> attitude = new ArrayList<>();
private List<String> staticMemory = new ArrayList<>(); private LinkedHashMap<LocalDate,String> relationChange = new LinkedHashMap<>();
private HashMap<String,String> staticMemory = new HashMap<>();
public void addInfo(String platform, String userInfo) { public void addInfo(String platform, String userInfo) {
this.info.put(platform, userInfo); this.info.put(platform, userInfo);
} }
public void updateRelationChange(String changeReason){
relationChange.put(LocalDate.now(),changeReason);
}
public void updateRelationChange(LocalDate date, String changeReason){
relationChange.put(date,changeReason);
}
public void updateRelationChange(LinkedHashMap<LocalDate,String> tempRelationChange){
relationChange.putAll(tempRelationChange);
}
public static class Constant { public static class Constant {
public static class Relation { public static class Relation {
public static final String STRANGER = "陌生"; public static final String STRANGER = "陌生";

View File

@@ -1,11 +1,8 @@
package work.slhaf.agent.module.modules.perceive.updater; package work.slhaf.agent.module.modules.perceive.updater;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import work.slhaf.agent.common.chat.pojo.ChatResponse;
import work.slhaf.agent.common.chat.pojo.Message;
import work.slhaf.agent.common.thread.InteractionThreadPoolExecutor; import work.slhaf.agent.common.thread.InteractionThreadPoolExecutor;
import work.slhaf.agent.core.cognation.CognationCapability; import work.slhaf.agent.core.cognation.CognationCapability;
import work.slhaf.agent.core.cognation.CognationManager; import work.slhaf.agent.core.cognation.CognationManager;
@@ -15,15 +12,14 @@ import work.slhaf.agent.core.interaction.data.context.InteractionContext;
import work.slhaf.agent.core.interaction.module.InteractionModule; import work.slhaf.agent.core.interaction.module.InteractionModule;
import work.slhaf.agent.module.common.Model; import work.slhaf.agent.module.common.Model;
import work.slhaf.agent.module.common.ModelConstant; import work.slhaf.agent.module.common.ModelConstant;
import work.slhaf.agent.module.modules.perceive.updater.pojo.PerceiveChatInput; import work.slhaf.agent.module.modules.perceive.updater.relation_extractor.pojo.RelationExtractResult;
import work.slhaf.agent.module.modules.perceive.updater.pojo.PerceiveChatResult;
import work.slhaf.agent.module.modules.perceive.updater.relation_extractor.RelationExtractor; import work.slhaf.agent.module.modules.perceive.updater.relation_extractor.RelationExtractor;
import work.slhaf.agent.module.modules.perceive.updater.static_extractor.StaticExtractor; import work.slhaf.agent.module.modules.perceive.updater.static_extractor.StaticMemoryExtractor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap; import java.util.concurrent.Callable;
import java.util.List; import java.util.concurrent.locks.ReentrantLock;
/** /**
* 感知更新,异步 * 感知更新,异步
@@ -39,9 +35,8 @@ public class PerceiveUpdater extends Model implements InteractionModule {
private CognationCapability cognationCapability; private CognationCapability cognationCapability;
private InteractionThreadPoolExecutor executor; private InteractionThreadPoolExecutor executor;
private RelationExtractor relationExtractor; private RelationExtractor relationExtractor;
private StaticExtractor staticExtractor; private StaticMemoryExtractor staticMemoryExtractor;
private List<Message> tempMessages;
public static PerceiveUpdater getInstance() throws IOException, ClassNotFoundException { public static PerceiveUpdater getInstance() throws IOException, ClassNotFoundException {
if (perceiveUpdater == null) { if (perceiveUpdater == null) {
@@ -52,7 +47,7 @@ public class PerceiveUpdater extends Model implements InteractionModule {
perceiveUpdater.setCognationCapability(CognationManager.getInstance()); perceiveUpdater.setCognationCapability(CognationManager.getInstance());
perceiveUpdater.setExecutor(InteractionThreadPoolExecutor.getInstance()); perceiveUpdater.setExecutor(InteractionThreadPoolExecutor.getInstance());
perceiveUpdater.setRelationExtractor(RelationExtractor.getInstance()); perceiveUpdater.setRelationExtractor(RelationExtractor.getInstance());
perceiveUpdater.setStaticExtractor(StaticExtractor.getInstance()); perceiveUpdater.setStaticMemoryExtractor(StaticMemoryExtractor.getInstance());
setModel(perceiveUpdater, ModelConstant.Prompt.PERCEIVE, true); setModel(perceiveUpdater, ModelConstant.Prompt.PERCEIVE, true);
} }
} }
@@ -67,42 +62,38 @@ public class PerceiveUpdater extends Model implements InteractionModule {
if (!trigger){ if (!trigger){
return; return;
} }
tempMessages = new ArrayList<>(cognationCapability.getChatMessages()); ReentrantLock userLock = new ReentrantLock();
String userId = context.getUserId(); User user = new User();
PerceiveChatInput input = getPerceiveInput(userId); user.setUuid(context.getUserId());
PerceiveChatResult perceiveChatResult = getPerceiveResult(input); List<Callable<Void>> tasks = new ArrayList<>();
User user = getTempUser(context, perceiveChatResult); tasks.add(() -> {
user.setStaticMemory(perceiveChatResult.getStaticMemory()); runStaticExtractorAction(context, userLock, user);
return null;
});
tasks.add(() -> {
runRelationExtractorAction(context, userLock, user);
return null;
});
executor.invokeAll(tasks);
perceiveCapability.updateUser(user); perceiveCapability.updateUser(user);
}); });
} }
private static User getTempUser(InteractionContext context, PerceiveChatResult perceiveChatResult) { private void runRelationExtractorAction(InteractionContext context, ReentrantLock userLock, User user) {
User user = new User(); RelationExtractResult relationExtractResult = relationExtractor.execute(context);
user.setUuid(context.getUserId()); userLock.lock();
user.setRelation(perceiveChatResult.getRelation()); user.setRelation(relationExtractResult.getRelation());
user.setImpressions(perceiveChatResult.getImpressions()); user.setImpressions(relationExtractResult.getImpressions());
user.setAttitude(perceiveChatResult.getAttitude()); user.setAttitude(relationExtractResult.getAttitude());
return user; user.updateRelationChange(relationExtractResult.getRelationChangeHistory());
userLock.unlock();
} }
private PerceiveChatResult getPerceiveResult(PerceiveChatInput input) { private void runStaticExtractorAction(InteractionContext context, ReentrantLock userLock, User user) {
ChatResponse response = singleChat(JSONObject.toJSONString(input)); HashMap<String, String> newStaticMemory = staticMemoryExtractor.execute(context);
return JSONObject.parseObject(response.getMessage(), PerceiveChatResult.class); userLock.lock();
} user.setStaticMemory(newStaticMemory);
userLock.unlock();
private PerceiveChatInput getPerceiveInput(String userId) {
HashMap<String,String> map = new HashMap<>();
User user = perceiveCapability.getUser(userId);
map.put("[用户昵称] <用户的昵称信息>",user.getNickName());
map.put("[关系] <你与用户的关系>", user.getRelation());
map.put("[态度] <你对于用户的态度>", user.getAttitude().toString());
map.put("[印象] <你对于用户的印象>", user.getImpressions().toString());
map.put("[静态记忆] <你关于用户的静态记忆>", user.getStaticMemory().toString());
PerceiveChatInput input = new PerceiveChatInput();
input.setPrimaryUserPerceive(map);
input.setChatMessages(tempMessages);
return input;
} }
@Override @Override

View File

@@ -1,13 +0,0 @@
package work.slhaf.agent.module.modules.perceive.updater.pojo;
import lombok.Data;
import java.util.List;
@Data
public class PerceiveChatResult {
private String relation;
private List<String> impressions;
private List<String> attitude;
private List<String> staticMemory;
}

View File

@@ -1,17 +1,43 @@
package work.slhaf.agent.module.modules.perceive.updater.relation_extractor; package work.slhaf.agent.module.modules.perceive.updater.relation_extractor;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
import work.slhaf.agent.common.chat.pojo.ChatResponse;
import work.slhaf.agent.common.chat.pojo.Message;
import work.slhaf.agent.core.cognation.CognationCapability;
import work.slhaf.agent.core.cognation.CognationManager;
import work.slhaf.agent.core.cognation.submodule.perceive.PerceiveCapability;
import work.slhaf.agent.core.cognation.submodule.perceive.pojo.User;
import work.slhaf.agent.core.interaction.data.context.InteractionContext;
import work.slhaf.agent.module.common.Model; import work.slhaf.agent.module.common.Model;
import work.slhaf.agent.module.modules.perceive.updater.pojo.PerceiveChatResult; import work.slhaf.agent.module.common.ModelConstant;
import work.slhaf.agent.module.modules.perceive.updater.relation_extractor.pojo.RelationExtractInput;
import work.slhaf.agent.module.modules.perceive.updater.relation_extractor.pojo.RelationExtractResult;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Data
public class RelationExtractor extends Model { public class RelationExtractor extends Model {
private static volatile RelationExtractor relationExtractor; private static volatile RelationExtractor relationExtractor;
public static RelationExtractor getInstance() { private CognationCapability cognationCapability;
private PerceiveCapability perceiveCapability;
private List<Message> tempMessages;
public static RelationExtractor getInstance() throws IOException, ClassNotFoundException {
if (relationExtractor == null) { if (relationExtractor == null) {
synchronized (RelationExtractor.class) { synchronized (RelationExtractor.class) {
if (relationExtractor == null) { if (relationExtractor == null) {
relationExtractor = new RelationExtractor(); relationExtractor = new RelationExtractor();
relationExtractor.setCognationCapability(CognationManager.getInstance());
relationExtractor.setPerceiveCapability(CognationManager.getInstance());
setModel(relationExtractor, ModelConstant.Prompt.PERCEIVE, true);
} }
} }
} }
@@ -19,8 +45,43 @@ public class RelationExtractor extends Model {
} }
//TODO 完善关系提取与相应提示词 //TODO 完善关系提取与相应提示词
public PerceiveChatResult execute(){ public RelationExtractResult execute(InteractionContext context){
return null; tempMessages = new ArrayList<>(cognationCapability.getChatMessages());
String userId = context.getUserId();
RelationExtractInput input = getRelationInput(userId);
RelationExtractResult relationExtractResult = getRelationResult(input);
User user = getTempUser(context, relationExtractResult);
perceiveCapability.updateUser(user);
return relationExtractResult;
}
private User getTempUser(InteractionContext context, RelationExtractResult relationExtractResult) {
User user = new User();
user.setUuid(context.getUserId());
user.setRelation(relationExtractResult.getRelation());
user.setImpressions(relationExtractResult.getImpressions());
user.setAttitude(relationExtractResult.getAttitude());
return user;
}
private RelationExtractResult getRelationResult(RelationExtractInput input) {
ChatResponse response = singleChat(JSONObject.toJSONString(input));
return JSONObject.parseObject(response.getMessage(), RelationExtractResult.class);
}
private RelationExtractInput getRelationInput(String userId) {
HashMap<String,String> map = new HashMap<>();
User user = perceiveCapability.getUser(userId);
map.put("[用户昵称] <用户的昵称信息>",user.getNickName());
map.put("[关系] <你与用户的关系>", user.getRelation());
map.put("[态度] <你对于用户的态度>", user.getAttitude().toString());
map.put("[印象] <你对于用户的印象>", user.getImpressions().toString());
map.put("[静态记忆] <你对该用户的静态记忆>", user.getStaticMemory().toString());
RelationExtractInput input = new RelationExtractInput();
input.setPrimaryUserPerceive(map);
input.setChatMessages(tempMessages);
return input;
} }
@Override @Override

View File

@@ -1,4 +1,4 @@
package work.slhaf.agent.module.modules.perceive.updater.pojo; package work.slhaf.agent.module.modules.perceive.updater.relation_extractor.pojo;
import lombok.Data; import lombok.Data;
import work.slhaf.agent.common.chat.pojo.Message; import work.slhaf.agent.common.chat.pojo.Message;
@@ -7,7 +7,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
@Data @Data
public class PerceiveChatInput { public class RelationExtractInput {
private HashMap<String,String> primaryUserPerceive; private HashMap<String,String> primaryUserPerceive;
private List<Message> chatMessages; private List<Message> chatMessages;
} }

View File

@@ -0,0 +1,13 @@
package work.slhaf.agent.module.modules.perceive.updater.relation_extractor.pojo;
import lombok.Data;
import java.util.List;
@Data
public class RelationExtractResult {
private String relation;
private List<String> impressions;
private List<String> attitude;
private String relationChangeHistory;
}

View File

@@ -1,46 +0,0 @@
package work.slhaf.agent.module.modules.perceive.updater.static_extractor;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import work.slhaf.agent.common.chat.pojo.ChatResponse;
import work.slhaf.agent.module.common.Model;
import work.slhaf.agent.module.common.ModelConstant;
import work.slhaf.agent.module.modules.perceive.updater.static_extractor.data.StaticExtractInput;
import java.util.HashMap;
import java.util.Map;
@EqualsAndHashCode(callSuper = true)
@Data
public class StaticExtractor extends Model {
private static volatile StaticExtractor staticExtractor;
public static StaticExtractor getInstance() {
if (staticExtractor == null) {
synchronized (StaticExtractor.class) {
if (staticExtractor == null) {
staticExtractor = new StaticExtractor();
setModel(staticExtractor, ModelConstant.Prompt.MEMORY, true);
}
}
}
return staticExtractor;
}
public Map<String, String> execute(StaticExtractInput input) {
ChatResponse response = singleChat(JSONUtil.toJsonPrettyStr(input));
JSONObject jsonObject = JSONObject.parseObject(response.getMessage());
Map<String, String> result = new HashMap<>();
jsonObject.forEach((k, v) -> result.put(k, (String) v));
return result;
}
@Override
protected String modelKey() {
return "static_extractor";
}
}

View File

@@ -0,0 +1,60 @@
package work.slhaf.agent.module.modules.perceive.updater.static_extractor;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import work.slhaf.agent.common.chat.pojo.ChatResponse;
import work.slhaf.agent.core.cognation.CognationCapability;
import work.slhaf.agent.core.cognation.CognationManager;
import work.slhaf.agent.core.cognation.submodule.perceive.PerceiveCapability;
import work.slhaf.agent.core.interaction.data.context.InteractionContext;
import work.slhaf.agent.module.common.Model;
import work.slhaf.agent.module.common.ModelConstant;
import work.slhaf.agent.module.modules.perceive.updater.static_extractor.data.StaticMemoryExtractInput;
import java.io.IOException;
import java.util.HashMap;
@EqualsAndHashCode(callSuper = true)
@Data
public class StaticMemoryExtractor extends Model {
private static volatile StaticMemoryExtractor staticMemoryExtractor;
private CognationCapability cognationCapability;
private PerceiveCapability perceiveCapability;
public static StaticMemoryExtractor getInstance() throws IOException, ClassNotFoundException {
if (staticMemoryExtractor == null) {
synchronized (StaticMemoryExtractor.class) {
if (staticMemoryExtractor == null) {
staticMemoryExtractor = new StaticMemoryExtractor();
staticMemoryExtractor.setCognationCapability(CognationManager.getInstance());
staticMemoryExtractor.setPerceiveCapability(CognationManager.getInstance());
setModel(staticMemoryExtractor, ModelConstant.Prompt.MEMORY, true);
}
}
}
return staticMemoryExtractor;
}
public HashMap<String, String> execute(InteractionContext context) {
StaticMemoryExtractInput input = StaticMemoryExtractInput.builder()
.userId(context.getUserId())
.messages(cognationCapability.getChatMessages())
.existedStaticMap(perceiveCapability.getUser(context.getUserId()).getStaticMemory())
.build();
ChatResponse response = singleChat(JSONUtil.toJsonPrettyStr(input));
JSONObject jsonObject = JSONObject.parseObject(response.getMessage());
HashMap<String, String> result = new HashMap<>();
jsonObject.forEach((k, v) -> result.put(k, (String) v));
return result;
}
@Override
protected String modelKey() {
return "static_extractor";
}
}

View File

@@ -9,7 +9,7 @@ import java.util.Map;
@Data @Data
@Builder @Builder
public class StaticExtractInput { public class StaticMemoryExtractInput {
private String userId; private String userId;
private List<Message> messages; private List<Message> messages;
private Map<String,String> existedStaticMap; private Map<String,String> existedStaticMap;