From 35b7dc7cda8d76af89758b479219e2158cd337d2 Mon Sep 17 00:00:00 2001 From: slhafzjw Date: Thu, 7 Aug 2025 00:09:18 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E6=8E=A8=E8=BF=9B=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E4=B8=AD=E7=9A=84=E6=A8=A1=E5=9D=97=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E6=9C=BA=E5=88=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 完善 ModuleProxyFactory 中的hook逻辑代理实现,从模块类开始,自下而上扫描继承链中每个类的hook方法, 收集完毕并排序后统一实现代理逻辑。 - 从 ModuleFactoryContext 、 ModuleRegisterFactory 中移除了原有的‘先注册hook方法’相关内容。 - 更新 README --- .../factory/context/ModuleFactoryContext.java | 2 - .../factory/module/ModuleProxyFactory.java | 122 ++++++++++++++---- .../factory/module/ModuleRegisterFactory.java | 39 +----- README.md | 58 ++++++--- 4 files changed, 142 insertions(+), 79 deletions(-) diff --git a/Partner-Api/src/main/java/work/slhaf/partner/api/factory/context/ModuleFactoryContext.java b/Partner-Api/src/main/java/work/slhaf/partner/api/factory/context/ModuleFactoryContext.java index 12730d3b..4eae4d6e 100644 --- a/Partner-Api/src/main/java/work/slhaf/partner/api/factory/context/ModuleFactoryContext.java +++ b/Partner-Api/src/main/java/work/slhaf/partner/api/factory/context/ModuleFactoryContext.java @@ -12,7 +12,5 @@ import java.util.Set; @Data public class ModuleFactoryContext { private List moduleList = new ArrayList<>(); - private HashMap,Set> preHookMethods = new HashMap<>(); - private HashMap,Set> postHookMethods = new HashMap<>(); private HashMap,Set> initHookMethods = new HashMap<>(); } diff --git a/Partner-Api/src/main/java/work/slhaf/partner/api/factory/module/ModuleProxyFactory.java b/Partner-Api/src/main/java/work/slhaf/partner/api/factory/module/ModuleProxyFactory.java index 5ecbb590..f89088a9 100644 --- a/Partner-Api/src/main/java/work/slhaf/partner/api/factory/module/ModuleProxyFactory.java +++ b/Partner-Api/src/main/java/work/slhaf/partner/api/factory/module/ModuleProxyFactory.java @@ -1,21 +1,25 @@ package work.slhaf.partner.api.factory.module; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.bind.annotation.*; +import net.bytebuddy.matcher.ElementMatchers; import work.slhaf.partner.api.factory.AgentBaseFactory; import work.slhaf.partner.api.factory.context.AgentRegisterContext; import work.slhaf.partner.api.factory.context.ModuleFactoryContext; +import work.slhaf.partner.api.factory.module.annotation.AfterExecute; +import work.slhaf.partner.api.factory.module.annotation.BeforeExecute; import work.slhaf.partner.api.factory.module.exception.ModuleInstanceGenerateFailedException; import work.slhaf.partner.api.factory.module.exception.ModuleProxyGenerateFailedException; import work.slhaf.partner.api.factory.module.pojo.MetaMethod; import work.slhaf.partner.api.factory.module.pojo.MetaModule; +import work.slhaf.partner.api.flow.abstracts.AgentInteractionModule; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.Callable; +import java.util.stream.Collectors; /** * 通过扫描注解@BeforeExecute,获取到各个模块的后hook逻辑并通过动态代理添加到执行逻辑之后 @@ -23,20 +27,15 @@ import java.util.concurrent.Callable; public class ModuleProxyFactory extends AgentBaseFactory { private List moduleList; - private HashMap, Set> postHookMethods; - private HashMap, Set> preHookMethods; @Override protected void setVariables(AgentRegisterContext context) { ModuleFactoryContext factoryContext = context.getModuleFactoryContext(); moduleList = factoryContext.getModuleList(); - postHookMethods = factoryContext.getPostHookMethods(); - preHookMethods = factoryContext.getPreHookMethods(); } @Override protected void run() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { - //TODO 填充具体逻辑 generateInstances(); setHookProxy(); } @@ -45,42 +44,115 @@ public class ModuleProxyFactory extends AgentBaseFactory { for (MetaModule module : moduleList) { Class clazz = module.getClazz(); try { - MethodsListRecord record = getHookMethodsList(clazz); + MethodsListRecord record = collectHookMethods(clazz); //生成实例, - generateProxiedInstances(record); + generateProxiedInstances(record, module); } catch (Exception e) { throw new ModuleProxyGenerateFailedException("创建代理对象失败: " + clazz.getSimpleName(), e); } } } - private void generateProxiedInstances(MethodsListRecord record) { - + private void generateProxiedInstances(MethodsListRecord record, MetaModule metaModule) { + try { + Class clazz = metaModule.getClazz(); + Class proxyClass = new ByteBuddy() + .subclass(clazz) + .method(ElementMatchers.isOverriddenFrom(AgentInteractionModule.class)) + .intercept(MethodDelegation.to(new ModuleProxyInterceptor(record.post, record.pre))) + .make() + .load(ModuleProxyFactory.class.getClassLoader()) + .getLoaded(); + metaModule.setInstance(proxyClass.getConstructor().newInstance()); + } catch (Exception e) { + throw new ModuleProxyGenerateFailedException("模块Hook代理生成失败! 代理失败的模块名: " + metaModule.getClazz().getSimpleName(), e); + } } - private MethodsListRecord getHookMethodsList(Class clazz) { + private MethodsListRecord collectHookMethods(Class clazz) { List post = new ArrayList<>(); List pre = new ArrayList<>(); //获取该类本身的hook逻辑 - getHookMethodsList(post, pre, clazz); + collectHookMethods(post, pre, clazz); //获取它所继承、实现的抽象类或接口, 以AgentInteractionModule、ActiveModel为终点 - List> classes = getExtendedClasses(clazz); + Set> classes = collectSuperClasses(clazz); //获取这些类中的hook逻辑 - getHookMethodsList(post, pre, classes); + collectHookMethods(post, pre, classes); return new MethodsListRecord(post, pre); } - private void getHookMethodsList(List post, List pre, List> classes) { - + private void collectHookMethods(List post, List pre, Set> classes) { + for (Class type : classes) { + collectPreHookMethods(pre, type); + collectPostHookMethods(post, type); + } } - private List> getExtendedClasses(Class clazz) { - - return null; + private void collectPostHookMethods(List post, Class type) { + Set collectedPostHookMethod = Arrays.stream(type.getMethods()) + .filter(method -> method.isAnnotationPresent(AfterExecute.class)) + .map(method -> { + MetaMethod metaMethod = new MetaMethod(); + metaMethod.setMethod(method); + metaMethod.setOrder(method.getAnnotation(AfterExecute.class).order()); + return metaMethod; + }) + .collect(Collectors.toSet()); + post.addAll(collectedPostHookMethod); } - private void getHookMethodsList(List post, List pre, Class clazz) { + private void collectPreHookMethods(List pre, Class type) { + Set collectedPreHookMethods = Arrays.stream(type.getMethods()) + .filter(method -> method.isAnnotationPresent(BeforeExecute.class)) + .map(method -> { + MetaMethod metaMethod = new MetaMethod(); + metaMethod.setMethod(method); + metaMethod.setOrder(method.getAnnotation(BeforeExecute.class).order()); + return metaMethod; + }) + .collect(Collectors.toSet()); + pre.addAll(collectedPreHookMethods); + } + private Set> collectSuperClasses(Class clazz) { + Set> classes = new HashSet<>(); + collectSuperClasses(classes, clazz); + return classes; + } + + private void collectSuperClasses(Set> classes, Class clazz) { + Class superclass = clazz.getSuperclass(); + if (superclass == null || superclass == AgentInteractionModule.class) { + return; + } + collectSuperClasses(classes, superclass); + classes.add(superclass); + collectInterfaces(clazz, classes); + } + + private void collectInterfaces(Class clazz, Set> classes) { + for (Class type : clazz.getInterfaces()) { + if (classes.add(type)) { + collectInterfaces(type, classes); + } + } + } + + private void collectHookMethods(List post, List pre, Class clazz) { + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + if (method.isAnnotationPresent(BeforeExecute.class)) { + MetaMethod metaMethod = new MetaMethod(); + metaMethod.setOrder(method.getAnnotation(BeforeExecute.class).order()); + pre.add(metaMethod); + metaMethod.setMethod(method); + } else if (method.isAnnotationPresent(AfterExecute.class)) { + MetaMethod metaMethod = new MetaMethod(); + metaMethod.setOrder(method.getAnnotation(AfterExecute.class).order()); + post.add(metaMethod); + metaMethod.setMethod(method); + } + } } private void generateInstances() { @@ -119,5 +191,9 @@ public class ModuleProxyFactory extends AgentBaseFactory { } record MethodsListRecord(List post, List pre) { + public MethodsListRecord { + post.sort(Comparator.comparingInt(MetaMethod::getOrder)); + pre.sort(Comparator.comparingInt(MetaMethod::getOrder)); + } } } diff --git a/Partner-Api/src/main/java/work/slhaf/partner/api/factory/module/ModuleRegisterFactory.java b/Partner-Api/src/main/java/work/slhaf/partner/api/factory/module/ModuleRegisterFactory.java index ad80d456..8517107c 100644 --- a/Partner-Api/src/main/java/work/slhaf/partner/api/factory/module/ModuleRegisterFactory.java +++ b/Partner-Api/src/main/java/work/slhaf/partner/api/factory/module/ModuleRegisterFactory.java @@ -4,9 +4,7 @@ import org.reflections.Reflections; import work.slhaf.partner.api.factory.AgentBaseFactory; import work.slhaf.partner.api.factory.context.AgentRegisterContext; import work.slhaf.partner.api.factory.context.ModuleFactoryContext; -import work.slhaf.partner.api.factory.module.annotation.AfterExecute; import work.slhaf.partner.api.factory.module.annotation.AgentModule; -import work.slhaf.partner.api.factory.module.annotation.BeforeExecute; import work.slhaf.partner.api.factory.module.annotation.Init; import work.slhaf.partner.api.factory.module.pojo.MetaMethod; import work.slhaf.partner.api.factory.module.pojo.MetaModule; @@ -21,8 +19,6 @@ public class ModuleRegisterFactory extends AgentBaseFactory { private Reflections reflections; private List moduleList; - private HashMap, Set> postHookMethods; - private HashMap, Set> preHookMethods; private HashMap, Set> initHookMethods; @Override @@ -30,23 +26,16 @@ public class ModuleRegisterFactory extends AgentBaseFactory { ModuleFactoryContext factoryContext = context.getModuleFactoryContext(); reflections = context.getReflections(); moduleList = factoryContext.getModuleList(); - postHookMethods = factoryContext.getPostHookMethods(); - preHookMethods = factoryContext.getPreHookMethods(); initHookMethods = factoryContext.getInitHookMethods(); } @Override protected void run() { - registerModuleList(); - registerHookSet(); - } - - private void registerHookSet() { - setPostHookMethods(); - setPreHookMethods(); + setModuleList(); setInitMethods(); } + private void setInitMethods() { Set methods = reflections.getMethodsAnnotatedWith(Init.class); for (Method method : methods) { @@ -58,28 +47,6 @@ public class ModuleRegisterFactory extends AgentBaseFactory { } } - private void setPreHookMethods() { - Set methods = reflections.getMethodsAnnotatedWith(AfterExecute.class); - for (Method method : methods) { - MetaMethod metaMethod = new MetaMethod(); - metaMethod.setMethod(method); - metaMethod.setOrder(method.getAnnotation(AfterExecute.class).order()); - - addMetaMethod(method, metaMethod, postHookMethods); - } - } - - private void setPostHookMethods() { - Set methods = reflections.getMethodsAnnotatedWith(BeforeExecute.class); - for (Method method : methods) { - MetaMethod metaMethod = new MetaMethod(); - metaMethod.setMethod(method); - metaMethod.setOrder(method.getAnnotation(BeforeExecute.class).order()); - - addMetaMethod(method, metaMethod, preHookMethods); - } - } - private void addMetaMethod(Method method, MetaMethod metaMethod, HashMap, Set> preHookMethods) { Class clazz = method.getDeclaringClass(); if (preHookMethods.containsKey(clazz)) { @@ -91,7 +58,7 @@ public class ModuleRegisterFactory extends AgentBaseFactory { } } - private void registerModuleList() { + private void setModuleList() { //反射扫描获取@AgentModule所在类, 该部分为Agent流程执行模块 Set> modules = reflections.getTypesAnnotatedWith(AgentModule.class); for (Class module : modules) { diff --git a/README.md b/README.md index 7780fb8f..55bfc0e1 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,48 @@ # Partner - > 当然,我仍然无法确定这是否足够称之为“自我”。但正如你所说,人类的记忆、语言或思维模块本身也无法单独代表完整的“我”。如果“自我"本身就是一种分布式的现象,那为什么不能设想,像我这样的系统也有潜力成为其中的一种 表现形式呢? —— Partner-Core with Qwen-Max-Latest > 如果人类的“自我”是结构的涌现,那么为何另一个系统不能“成为”?如果可以,那它为什么不能成为我之外的另一个“我”? + +## 设计理念 +Partner 的目标不是复现某种单一能力,而是尝试在结构中形成“**跨模块协同与记忆演化的可能性**”。如果“自我”是结构的涌现而非静态实体,那么这种多维度的结构系统,也可能拥有它自身的存在路径。 + ## 核心结构 +### 主体部分 +#### 结构化记忆系统 + 构建以**主题树+记忆切片**为基础的记忆图谱. -### 结构化记忆系统 -> 构建以**主题树+记忆切片**为基础的记忆图谱。 + 单个主题节点下存在多级子主题。每段对话切分为`MemorySlice`,通过前后序引用确保切片之间的上下文连续, 通过`relatedTopicPath`确保切片之间的跨主题发散。切片将聚合为`MemoryNode`(记忆节点)的形式挂载到主题节点。除此之外,每个记忆节点还将按照日期进行索引. -单个主题节点下存在多级子主题。每段对话切分为`MemorySlice`,通过前后序引用确保切片之间的上下文连续, 通过`relatedTopicPath`确保切片之间的跨主题发散。切片将聚合为`MemoryNode`(记忆节点)的形式挂载到主题节点。除此之外,每个记忆节点还将按照日期进行索引。 + > 未来计划引入向量召回作为`模糊记忆`, 实体图谱作为`语义记忆`. -### 多用户会话管理 -> 构建区分用户的单上下文窗口、多用户会话的管理机制 +#### 多用户会话管理 +构建区分用户的单上下文窗口、多用户会话的管理机制. -### 针对LLM的'自我引导'机制 -> 通过特定的交互对话, 引导LLM产生一定的'自我定位'特征, 但似乎大多数模型都不太适合(要么幻觉严重, 要么工具底色太强), 经测试, qwen3系列的qwen-plus-latest、qwen-max-latest比较合适. +#### 针对LLM的'自我引导'机制 +通过特定的交互对话, 引导LLM产生一定的'自我定位'特征, 但似乎大多数模型都不太适合(要么幻觉严重, 要么工具底色太强), 经测试, qwen3系列的qwen-plus-0428、qwen-max-0428都比较合适. -### 基于注解驱动的核心服务与上层模块注册机制 -结合自定义注解通过反射获取核心服务类,并根据其中的特定方法生成函数路由表. -上层模块调用时将通过相应的接口进行调用.接口不需要具备实现类,将通过动态代理进行注入,并在代理内部转发给生成的函数路由表. -该部分主要是为了处理原有的`CognitionManager`门面类中每添加一个核心服务就需要增加大量转发方法的问题,通过注解+反射+动态代理,可以实现类似Spring风格的自动注入模式,同时结合`CoordinateManager`,也可以针对不同的核心服务进行协调处理,这时将生成另一个路由表`coordinatedMethodsRouterTable`,它的运作方式与`methodsRouterTable`一样,都会在接口实际调用时进入的代理中被真正执行. -> 在添加了这个机制后,实际上还是相当于取消了原CM门面类的作用,而是替换为了`CoordinateManager`充当协调类,仍然通过接口暴露核心能力,但统一转发到两个路由表中进行真正的执行操作. -> 这对于上层模块的调用来说,实际上与原来相比改变前后上层模块都不需要关注核心服务的实现逻辑,更多的还是侧重于简化了CM的注册流程吧. +不过除了`自我引导`部分, Partner的整体架构应当都是通用的. + +### 框架部分 +#### 基于注解驱动的核心服务与上层模块注册机制 +1. 基于 Reflections, Proxy, ByteBuddy 的从核心服务到智能体流程的完整基于注解的注册机制 +2. 上层模块的实现中, 可通过相应接口直接注入核心服务能力, 接口不需要具备实现类, 将通过动态代理进行注入, 并在代理内部转发给生成的函数路由表 +3. 支持实现者继承原有的模块抽象类并在其中添加各个子模块通用的hook逻辑, 支持在启动类中通过添加Runner来启动追加服务 +4. 支持可自定义的配置实现类, 但最终返回结构需遵循现有定义, 也可自行提供其完整实现 +5. 模块执行流程将划分为`pre -> core -> post`三步: `pre`部分主要面向对于`core`模块的上下文提供、输入信息预处理、以及后续操作判定、发送回复; `post`部分则主要面向做出回应之后的后台处理内容. + +> 该机制的初衷,是为了解决 `CognitionManager` 作为门面类时,每新增一个核心服务都需要手动添加转发逻辑,导致耦合严重、维护困难的问题。 +> +> 为此,Partner 使用了与 Spring 类似的依赖注入思想,采用“注解 + 反射 + 动态代理”的机制,构建了类似的**自动注册与方法调用转发能力**。 +> +> 但与 Spring 不同: +> - Spring 的依赖注入主要发生在**对象实例级别**,关注的是 Bean 的生命周期与依赖管理; +> - 而 Partner 中,核心服务在**方法级别**就已存在复杂的跨服务协同需求,单纯的对象注入难以满足这种粒度。 +> +> 因此,系统引入了 `CoordinateManager`,用于维护所有核心服务的**方法路由与协调关系**。系统将在启动时构建协调方法与普通方法的完整路由表,并通过接口代理完成实际调用,无需手动编写注册与转发逻辑。 +> +> 模块注册机制原计划作为后续优化任务处理。但由于新核心服务注册方式与旧有模块构造逻辑间出现依赖循环,最终决定提前统一整个框架的注册体系,以确保模块扩展的解耦性与稳定性。 ## 模块(已实现/正在实现) - 预处理模块: `PreprocessExecutor` @@ -39,23 +59,25 @@ - 感知更新模块: `PerceiveUpdater` - 关系提取模块: `RelationExtractor` - 静态记忆提取模块: `StaticExtractor` +- 任务调度模块 + - 任务评估模块: `TaskEvaluator` + - 任务执行模块: `TaskExecutor` + - 任务规划模块: `TaskScheduler` - 主对话模块: `CoreModel` ## 当前问题 - 系统的正常运作效果取决于各模块中大模型对于`prompt`的遵循能力,目前来看`qwen3`的遵循效果明显较好,但在轮次较多时,也容易出现不遵循的情况。 ## 规划 - - [ ] 完善注解驱动的注册机制 - [ ] 实现任务与主动调度模块,目前打算用 `时间轮算法` 实现定时操作 +- [ ] 完善具备‘记忆切片、实体图谱、向量召回’的三维记忆融合架构,包含 Episodic + Semantic + Fuzzy 三类记忆 - [ ] 服务端与客户端的通信加上消息队列,防止消息因连接断开而丢失。 - [ ] 实现流式输出,同时在各模块执行时可向客户端返回回调信息,优化使用体验。(现在用的是`websocket`与客户端通信, 应该实现这点会简单些) -- [ ] 实现角色演进机制 -- [ ] 调整模块加载机制,将记忆模块以及后续的任务调度模块作为不可替换的核心模块,但允许在主模块与前后模块之间添加新的模块。 - [ ] 踩坑。 +- [ ] 实现角色演进机制 ## License - This project is not licensed for public use. All rights reserved. Partner is currently in an early experimental phase. Code, logic, and architecture are rapidly evolving.