diff --git a/README.md b/README.md index ef6a5391..8ee486b0 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ ### 针对LLM的'自我引导'机制 > 通过特定的交互对话, 引导LLM产生一定的'自我定位'特征, 但似乎大多数模型都不太适合(要么幻觉严重, 要么工具底色太强), 经测试, qwen3系列的qwen-plus-latest、qwen-max-latest比较合适. -## 模块实现 -- 预处理模块: `Preprocessor` +## 模块(已实现/正在实现) +- 预处理模块: `PreprocessExecutor` +- 后处理模块: `PostprocessExecutor` - 记忆模块 - 记忆选择模块: `MemorySelector` - 主题提取模块: `MemorySelectExtractor` @@ -26,6 +27,11 @@ - 记忆更新模块: `MemoryUpdater` - 记忆总结模块: `MemorySummarizer` - 静态记忆提取模块: `StaticMemoryExtractor` +- 感知模块 + - 感知选择模块: `PerceiveSelector` + - 感知更新模块: `PerceiveUpdater` + - 关系提取模块: `RelationExtractor` + - 静态记忆提取模块: `StaticExtractor` - 主对话模块: `CoreModel` ## 当前问题 @@ -37,11 +43,10 @@ - [ ] 调整模块加载机制,将记忆模块以及后续的任务调度模块作为不可替换的核心模块,但允许在主模块与前后模块之间添加新的模块。 - [ ] 实现流式输出,同时在各模块执行时可向客户端返回回调信息,优化使用体验。(现在用的是`websocket`与客户端通信, 应该实现这点会简单些) - [ ] 服务端与客户端的通信加上消息队列,防止消息因连接断开而丢失。 -- [ ] 踩坑。 - -### 长期规划 - [ ] 实现角色演进机制 - [ ] 实现任务调度模块(主动调度、意图推断、定时调度) +- [ ] 实现任务与主动调度模块,目前打算用 `时间轮算法` 实现定时操作 +- [ ] 踩坑。 ## License diff --git a/src/main/java/work/slhaf/agent/common/config/Config.java b/src/main/java/work/slhaf/agent/common/config/Config.java index ca8ebb85..598f72a0 100644 --- a/src/main/java/work/slhaf/agent/common/config/Config.java +++ b/src/main/java/work/slhaf/agent/common/config/Config.java @@ -8,6 +8,7 @@ import org.apache.commons.io.FileUtils; import work.slhaf.agent.module.modules.core.CoreModel; import work.slhaf.agent.module.modules.memory.selector.MemorySelector; import work.slhaf.agent.module.modules.memory.updater.MemoryUpdater; +import work.slhaf.agent.module.modules.process.PostprocessExecutor; import java.io.File; import java.io.IOException; @@ -98,6 +99,7 @@ public class Config { List moduleConfigList = List.of( new ModuleConfig(MemorySelector.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(TaskScheduler.class.getName(), ModuleConfig.Constant.INTERNAL, null) ); diff --git a/src/main/java/work/slhaf/agent/core/InteractionHub.java b/src/main/java/work/slhaf/agent/core/InteractionHub.java index c99819a9..7a0d9424 100644 --- a/src/main/java/work/slhaf/agent/core/InteractionHub.java +++ b/src/main/java/work/slhaf/agent/core/InteractionHub.java @@ -25,8 +25,6 @@ public class InteractionHub { @ToString.Exclude private TaskCallback callback; - private CoreModel coreModel; - private TaskScheduler taskScheduler; private List interactionModules; public static InteractionHub initialize() throws IOException { diff --git a/src/main/java/work/slhaf/agent/core/cognation/submodule/perceive/PerceiveCore.java b/src/main/java/work/slhaf/agent/core/cognation/submodule/perceive/PerceiveCore.java index 78117088..ea91d825 100644 --- a/src/main/java/work/slhaf/agent/core/cognation/submodule/perceive/PerceiveCore.java +++ b/src/main/java/work/slhaf/agent/core/cognation/submodule/perceive/PerceiveCore.java @@ -82,6 +82,7 @@ public class PerceiveCore extends PersistableObject { user.setImpressions(temp.getImpressions()); user.setAttitude(temp.getAttitude()); user.setStaticMemory(temp.getStaticMemory()); + user.updateRelationChange(user.getRelationChange()); usersLock.unlock(); } } diff --git a/src/main/java/work/slhaf/agent/core/cognation/submodule/perceive/pojo/User.java b/src/main/java/work/slhaf/agent/core/cognation/submodule/perceive/pojo/User.java index aa60ce14..d575af0f 100644 --- a/src/main/java/work/slhaf/agent/core/cognation/submodule/perceive/pojo/User.java +++ b/src/main/java/work/slhaf/agent/core/cognation/submodule/perceive/pojo/User.java @@ -5,8 +5,10 @@ import lombok.EqualsAndHashCode; import work.slhaf.agent.common.serialize.PersistableObject; import java.io.Serial; +import java.time.LocalDate; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; @EqualsAndHashCode(callSuper = true) @@ -24,12 +26,23 @@ public class User extends PersistableObject { // private HashMap events = new HashMap<>(); private List impressions = new ArrayList<>(); private List attitude = new ArrayList<>(); - private List staticMemory = new ArrayList<>(); + private LinkedHashMap relationChange = new LinkedHashMap<>(); + private HashMap staticMemory = new HashMap<>(); public void addInfo(String platform, String 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 tempRelationChange){ + relationChange.putAll(tempRelationChange); + } + public static class Constant { public static class Relation { public static final String STRANGER = "陌生"; diff --git a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/PerceiveUpdater.java b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/PerceiveUpdater.java index 5d69ebcc..13d8722e 100644 --- a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/PerceiveUpdater.java +++ b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/PerceiveUpdater.java @@ -1,11 +1,8 @@ package work.slhaf.agent.module.modules.perceive.updater; -import com.alibaba.fastjson2.JSONObject; import lombok.Data; import lombok.EqualsAndHashCode; 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.core.cognation.CognationCapability; 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.module.common.Model; 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.pojo.PerceiveChatResult; +import work.slhaf.agent.module.modules.perceive.updater.relation_extractor.pojo.RelationExtractResult; 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.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.locks.ReentrantLock; /** * 感知更新,异步 @@ -39,9 +35,8 @@ public class PerceiveUpdater extends Model implements InteractionModule { private CognationCapability cognationCapability; private InteractionThreadPoolExecutor executor; private RelationExtractor relationExtractor; - private StaticExtractor staticExtractor; + private StaticMemoryExtractor staticMemoryExtractor; - private List tempMessages; public static PerceiveUpdater getInstance() throws IOException, ClassNotFoundException { if (perceiveUpdater == null) { @@ -52,7 +47,7 @@ public class PerceiveUpdater extends Model implements InteractionModule { perceiveUpdater.setCognationCapability(CognationManager.getInstance()); perceiveUpdater.setExecutor(InteractionThreadPoolExecutor.getInstance()); perceiveUpdater.setRelationExtractor(RelationExtractor.getInstance()); - perceiveUpdater.setStaticExtractor(StaticExtractor.getInstance()); + perceiveUpdater.setStaticMemoryExtractor(StaticMemoryExtractor.getInstance()); setModel(perceiveUpdater, ModelConstant.Prompt.PERCEIVE, true); } } @@ -67,42 +62,38 @@ public class PerceiveUpdater extends Model implements InteractionModule { if (!trigger){ return; } - tempMessages = new ArrayList<>(cognationCapability.getChatMessages()); - String userId = context.getUserId(); - PerceiveChatInput input = getPerceiveInput(userId); - PerceiveChatResult perceiveChatResult = getPerceiveResult(input); - User user = getTempUser(context, perceiveChatResult); - user.setStaticMemory(perceiveChatResult.getStaticMemory()); + ReentrantLock userLock = new ReentrantLock(); + User user = new User(); + user.setUuid(context.getUserId()); + List> tasks = new ArrayList<>(); + tasks.add(() -> { + runStaticExtractorAction(context, userLock, user); + return null; + }); + tasks.add(() -> { + runRelationExtractorAction(context, userLock, user); + return null; + }); + executor.invokeAll(tasks); perceiveCapability.updateUser(user); }); } - private static User getTempUser(InteractionContext context, PerceiveChatResult perceiveChatResult) { - User user = new User(); - user.setUuid(context.getUserId()); - user.setRelation(perceiveChatResult.getRelation()); - user.setImpressions(perceiveChatResult.getImpressions()); - user.setAttitude(perceiveChatResult.getAttitude()); - return user; + private void runRelationExtractorAction(InteractionContext context, ReentrantLock userLock, User user) { + RelationExtractResult relationExtractResult = relationExtractor.execute(context); + userLock.lock(); + user.setRelation(relationExtractResult.getRelation()); + user.setImpressions(relationExtractResult.getImpressions()); + user.setAttitude(relationExtractResult.getAttitude()); + user.updateRelationChange(relationExtractResult.getRelationChangeHistory()); + userLock.unlock(); } - private PerceiveChatResult getPerceiveResult(PerceiveChatInput input) { - ChatResponse response = singleChat(JSONObject.toJSONString(input)); - return JSONObject.parseObject(response.getMessage(), PerceiveChatResult.class); - } - - private PerceiveChatInput getPerceiveInput(String userId) { - HashMap 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; + private void runStaticExtractorAction(InteractionContext context, ReentrantLock userLock, User user) { + HashMap newStaticMemory = staticMemoryExtractor.execute(context); + userLock.lock(); + user.setStaticMemory(newStaticMemory); + userLock.unlock(); } @Override diff --git a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/pojo/PerceiveChatResult.java b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/pojo/PerceiveChatResult.java deleted file mode 100644 index 919ebc6e..00000000 --- a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/pojo/PerceiveChatResult.java +++ /dev/null @@ -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 impressions; - private List attitude; - private List staticMemory; -} diff --git a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/RelationExtractor.java b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/RelationExtractor.java index c479c3b5..4af6beee 100644 --- a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/RelationExtractor.java +++ b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/RelationExtractor.java @@ -1,17 +1,43 @@ 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.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 { private static volatile RelationExtractor relationExtractor; - public static RelationExtractor getInstance() { + private CognationCapability cognationCapability; + private PerceiveCapability perceiveCapability; + + private List tempMessages; + + + public static RelationExtractor getInstance() throws IOException, ClassNotFoundException { if (relationExtractor == null) { synchronized (RelationExtractor.class) { if (relationExtractor == null) { 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 完善关系提取与相应提示词 - public PerceiveChatResult execute(){ - return null; + public RelationExtractResult execute(InteractionContext context){ + 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 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 diff --git a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/pojo/PerceiveChatInput.java b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/pojo/RelationExtractInput.java similarity index 65% rename from src/main/java/work/slhaf/agent/module/modules/perceive/updater/pojo/PerceiveChatInput.java rename to src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/pojo/RelationExtractInput.java index fc8faa7b..184e86b9 100644 --- a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/pojo/PerceiveChatInput.java +++ b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/pojo/RelationExtractInput.java @@ -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 work.slhaf.agent.common.chat.pojo.Message; @@ -7,7 +7,7 @@ import java.util.HashMap; import java.util.List; @Data -public class PerceiveChatInput { +public class RelationExtractInput { private HashMap primaryUserPerceive; private List chatMessages; } diff --git a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/pojo/RelationExtractResult.java b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/pojo/RelationExtractResult.java new file mode 100644 index 00000000..2d7b3d08 --- /dev/null +++ b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/relation_extractor/pojo/RelationExtractResult.java @@ -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 impressions; + private List attitude; + private String relationChangeHistory; +} diff --git a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/StaticExtractor.java b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/StaticExtractor.java deleted file mode 100644 index 55f08afc..00000000 --- a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/StaticExtractor.java +++ /dev/null @@ -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 execute(StaticExtractInput input) { - ChatResponse response = singleChat(JSONUtil.toJsonPrettyStr(input)); - JSONObject jsonObject = JSONObject.parseObject(response.getMessage()); - Map result = new HashMap<>(); - jsonObject.forEach((k, v) -> result.put(k, (String) v)); - return result; - } - - @Override - protected String modelKey() { - return "static_extractor"; - } -} diff --git a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/StaticMemoryExtractor.java b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/StaticMemoryExtractor.java new file mode 100644 index 00000000..fd29647c --- /dev/null +++ b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/StaticMemoryExtractor.java @@ -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 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 result = new HashMap<>(); + jsonObject.forEach((k, v) -> result.put(k, (String) v)); + return result; + } + + @Override + protected String modelKey() { + return "static_extractor"; + } +} diff --git a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/data/StaticExtractInput.java b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/data/StaticMemoryExtractInput.java similarity index 89% rename from src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/data/StaticExtractInput.java rename to src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/data/StaticMemoryExtractInput.java index 438ed6d2..364554d6 100644 --- a/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/data/StaticExtractInput.java +++ b/src/main/java/work/slhaf/agent/module/modules/perceive/updater/static_extractor/data/StaticMemoryExtractInput.java @@ -9,7 +9,7 @@ import java.util.Map; @Data @Builder -public class StaticExtractInput { +public class StaticMemoryExtractInput { private String userId; private List messages; private Map existedStaticMap;