mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 16:53:04 +08:00
适配框架时发现工厂注册链上存在一些执行顺序上的错误,于是尝试修复问题,为Agent启动链添加了完整的注释,并做出了必要的修复与调整
This commit is contained in:
@@ -12,6 +12,10 @@ import java.util.List;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>Agent 启动入口</h2>
|
||||||
|
* 详细启动流程请参阅{@link AgentRegisterFactory}
|
||||||
|
*/
|
||||||
public final class Agent {
|
public final class Agent {
|
||||||
|
|
||||||
public static AgentGatewayStep newAgent(Class<?> clazz) {
|
public static AgentGatewayStep newAgent(Class<?> clazz) {
|
||||||
|
|||||||
@@ -16,12 +16,20 @@ import work.slhaf.partner.api.agent.factory.module.ModuleRegisterFactory;
|
|||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||||
import work.slhaf.partner.api.agent.runtime.data.AgentContext;
|
import work.slhaf.partner.api.agent.runtime.data.AgentContext;
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||||
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.AgentRunningFlow;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>Agent 注册工厂</h2>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 具体流程依次按照 {@link AgentRegisterFactory#launch(String)} 方法顺序执行,最终将执行模块列表对应实例交给 {@link AgentConfigManager} ,传递给 {@link AgentRunningFlow} 针对交互做出调用
|
||||||
|
* <p/>
|
||||||
|
*/
|
||||||
public class AgentRegisterFactory {
|
public class AgentRegisterFactory {
|
||||||
|
|
||||||
private static final List<URL> urls = new ArrayList<>();
|
private static final List<URL> urls = new ArrayList<>();
|
||||||
@@ -35,14 +43,14 @@ public class AgentRegisterFactory {
|
|||||||
//流程
|
//流程
|
||||||
//0. 加载配置
|
//0. 加载配置
|
||||||
new ConfigLoaderFactory().execute(registerContext);
|
new ConfigLoaderFactory().execute(registerContext);
|
||||||
//1. 注册并检查Capability
|
//1. 注册并检查Module
|
||||||
new CapabilityRegisterFactory().execute(registerContext);
|
|
||||||
new CapabilityCheckFactory().execute(registerContext);
|
|
||||||
//2. 注册并检查Module
|
|
||||||
new ModuleCheckFactory().execute(registerContext);
|
new ModuleCheckFactory().execute(registerContext);
|
||||||
new ModuleRegisterFactory().execute(registerContext);
|
new ModuleRegisterFactory().execute(registerContext);
|
||||||
//3. 为module通过动态代理添加PostHook逻辑并进行实例化
|
//2. 为module通过动态代理添加PostHook逻辑并进行实例化
|
||||||
new ModuleProxyFactory().execute(registerContext);
|
new ModuleProxyFactory().execute(registerContext);
|
||||||
|
//3. 加载检查Capability层内容后进行能力层的内容注册
|
||||||
|
new CapabilityCheckFactory().execute(registerContext);
|
||||||
|
new CapabilityRegisterFactory().execute(registerContext);
|
||||||
//. 先一步注入Capability,避免因前hook逻辑存在针对能力的引用而报错
|
//. 先一步注入Capability,避免因前hook逻辑存在针对能力的引用而报错
|
||||||
new CapabilityInjectFactory().execute(registerContext);
|
new CapabilityInjectFactory().execute(registerContext);
|
||||||
//. 执行模块PreHook逻辑
|
//. 执行模块PreHook逻辑
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import work.slhaf.partner.api.agent.factory.capability.exception.UnMatchedCapabi
|
|||||||
import work.slhaf.partner.api.agent.factory.capability.exception.UnMatchedCoordinatedMethodException;
|
import work.slhaf.partner.api.agent.factory.capability.exception.UnMatchedCoordinatedMethodException;
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.ModuleCheckFactory;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
|
||||||
import work.slhaf.partner.api.agent.util.AgentUtil;
|
import work.slhaf.partner.api.agent.util.AgentUtil;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -18,7 +21,34 @@ import java.util.stream.Collectors;
|
|||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行<code>Capability</code>相关检查
|
* <h2>Agent启动流程 4</h2>
|
||||||
|
*
|
||||||
|
* <p>负责通过反射收集 {@link Capability} 和 {@link CapabilityCore} 注解所在类,并判断是否存在被错误忽略的方法</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityCheckFactory#loadCoresAndCapabilities()}</p>
|
||||||
|
* 通过反射收集 {@link Capability} 和 {@link CapabilityCore} 注解所在类为对应集合
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityCheckFactory#checkCountAndCapabilities()}</p>
|
||||||
|
* 检测 {@link Capability} 与 {@link CapabilityCore} 的数量、对应的能力是否相等。每一个core都将对应一个capability,并通过value属性进行匹配
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityCheckFactory#checkCapabilityMethods()}</p>
|
||||||
|
* 检测在 {@link Capability} 与 {@link CapabilityCore} 中是否存在对方尚未实现/注册的方法
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityCheckFactory#checkCoordinatedMethods()}</p>
|
||||||
|
* 检查是否包含协调方法({@link ToCoordinated}),如果存在,则进一步检查在 {@link CoordinateManager} 所注类中是否有提供对应的实现
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityCheckFactory#checkInjectCapability()}</p>
|
||||||
|
* 检查 {@link InjectCapability} 注解是否只用在 {@link CapabilityHolder} 所标识类的字段上。{@link AgentModule} 与 {@link AgentSubModule} 已经被 {@link CapabilityHolder} 标注
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>下一步流程请参阅{@link CapabilityRegisterFactory}</p>
|
||||||
*/
|
*/
|
||||||
public class CapabilityCheckFactory extends AgentBaseFactory {
|
public class CapabilityCheckFactory extends AgentBaseFactory {
|
||||||
|
|
||||||
@@ -37,12 +67,18 @@ public class CapabilityCheckFactory extends AgentBaseFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() {
|
protected void run() {
|
||||||
|
loadCoresAndCapabilities();
|
||||||
checkCountAndCapabilities();
|
checkCountAndCapabilities();
|
||||||
checkCapabilityMethods();
|
checkCapabilityMethods();
|
||||||
checkCoordinatedMethods();
|
checkCoordinatedMethods();
|
||||||
checkInjectCapability();
|
checkInjectCapability();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadCoresAndCapabilities() {
|
||||||
|
cores.addAll(reflections.getTypesAnnotatedWith(CapabilityCore.class));
|
||||||
|
capabilities.addAll(reflections.getTypesAnnotatedWith(Capability.class));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查<code>@InjectCapability</code>注解是否只用在<code>@CapabilityHolder</code>所标识类的字段上
|
* 检查<code>@InjectCapability</code>注解是否只用在<code>@CapabilityHolder</code>所标识类的字段上
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import work.slhaf.partner.api.agent.factory.capability.annotation.ToCoordinated;
|
|||||||
import work.slhaf.partner.api.agent.factory.capability.exception.ProxySetFailedExceptionCapability;
|
import work.slhaf.partner.api.agent.factory.capability.exception.ProxySetFailedExceptionCapability;
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.ModuleInitHookExecuteFactory;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
@@ -18,9 +21,23 @@ import java.util.function.Function;
|
|||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 负责执行<code>Capability</code>的注入逻辑
|
* <h2>Agent启动流程 6</h2>
|
||||||
*/
|
*
|
||||||
public class CapabilityInjectFactory extends AgentBaseFactory {
|
* <p>负责执行 {@link Capability} 的注入逻辑。</p>
|
||||||
|
*
|
||||||
|
* <p>实现方式:</p>
|
||||||
|
* <ol>
|
||||||
|
* <li>通过动态代理,为 {@link AgentModule} 与 {@link AgentSubModule} 中待注入的
|
||||||
|
* <b>能力接口</b> 类型(即 {@link Capability} 标注的接口类)生成代理对象。
|
||||||
|
* </li>
|
||||||
|
* <li>在代理对象内部,根据调用方法的签名确定路由,将调用转发至对应的具体函数。
|
||||||
|
* </li>
|
||||||
|
* <li>通过此机制,实现了 {@link Capability} 单一语义层面上普通方法与协调方法的统一入口。
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>下一步流程请参阅 {@link ModuleInitHookExecuteFactory}</p>
|
||||||
|
*/public class CapabilityInjectFactory extends AgentBaseFactory {
|
||||||
|
|
||||||
private Reflections reflections;
|
private Reflections reflections;
|
||||||
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import work.slhaf.partner.api.agent.factory.capability.exception.CoreInstancesCr
|
|||||||
import work.slhaf.partner.api.agent.factory.capability.exception.DuplicateMethodException;
|
import work.slhaf.partner.api.agent.factory.capability.exception.DuplicateMethodException;
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@@ -17,19 +19,48 @@ import java.util.HashMap;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static cn.hutool.core.util.ClassUtil.isNormalClass;
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 负责获取<code>@Capability</code>和<code>@CapabilityCore</code>标识的类,并生成函数路由表、设置<code>Core</code>实例用于后续注入
|
* <h2>Agent启动流程 5</h2>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 负责收集注解 {@link Capability} 和 {@link CapabilityCore} 标识的类,并生成函数路由表、创建core、capability实例,以及放入instanceMap供后续进行注入操作
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityRegisterFactory#setCoreInstances()}</p>
|
||||||
|
* 通过反射调用无参构造函数创建core实例,并将实例放入instanceMap供后续使用
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityRegisterFactory#generateRouterTable()}</p>
|
||||||
|
* 生成函数路由表:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityRegisterFactory#generateMethodsRouterTable()}</p>
|
||||||
|
* 生成普通方法对应的函数路由表
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link CapabilityRegisterFactory#generateCoordinatedMethodsRouterTable()}</p>
|
||||||
|
* 生成协调方法对应的函数路由表
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* 函数路由表生成完毕、core实例创建完毕之后,将交由下一工厂完成能力(Capability)注入操作,注入到 {@link AgentModule} 与 {@link AgentSubModule} 对应的实例中
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>下一步流程请参阅{@link CapabilityInjectFactory}</p>
|
||||||
*/
|
*/
|
||||||
public final class CapabilityRegisterFactory extends AgentBaseFactory {
|
public class CapabilityRegisterFactory extends AgentBaseFactory {
|
||||||
|
|
||||||
private Reflections reflections;
|
private Reflections reflections;
|
||||||
private HashMap<String, Function<Object[], Object>> methodsRouterTable;
|
private HashMap<String, Function<Object[], Object>> methodsRouterTable;
|
||||||
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
||||||
private HashMap<Class<?>, Object> capabilityCoreInstances;
|
private HashMap<Class<?>, Object> coreInstances;
|
||||||
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
||||||
private Set<Class<?>> cores;
|
private Set<Class<?>> cores;
|
||||||
private Set<Class<?>> capabilities;
|
private Set<Class<?>> capabilities;
|
||||||
@@ -40,38 +71,18 @@ public final class CapabilityRegisterFactory extends AgentBaseFactory {
|
|||||||
reflections = context.getReflections();
|
reflections = context.getReflections();
|
||||||
methodsRouterTable = factoryContext.getMethodsRouterTable();
|
methodsRouterTable = factoryContext.getMethodsRouterTable();
|
||||||
coordinatedMethodsRouterTable = factoryContext.getCoordinatedMethodsRouterTable();
|
coordinatedMethodsRouterTable = factoryContext.getCoordinatedMethodsRouterTable();
|
||||||
capabilityCoreInstances = factoryContext.getCapabilityCoreInstances();
|
coreInstances = factoryContext.getCapabilityCoreInstances();
|
||||||
cores = factoryContext.getCores();
|
cores = factoryContext.getCores();
|
||||||
capabilities = factoryContext.getCapabilities();
|
capabilities = factoryContext.getCapabilities();
|
||||||
capabilityHolderInstances = factoryContext.getCapabilityHolderInstances();
|
capabilityHolderInstances = factoryContext.getCapabilityHolderInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
|
protected void run() {
|
||||||
setCapabilityCoreInstances();
|
setCoreInstances();
|
||||||
setAnnotatedClasses();
|
|
||||||
generateRouterTable();
|
generateRouterTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置<code>CapabilityCore</code>、<code>Capability</code>注解标识类
|
|
||||||
*/
|
|
||||||
private void setAnnotatedClasses() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
|
|
||||||
cores.addAll(reflections.getTypesAnnotatedWith(CapabilityCore.class));
|
|
||||||
capabilities.addAll(reflections.getTypesAnnotatedWith(Capability.class));
|
|
||||||
setCapabilityHolderInstances();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCapabilityHolderInstances() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
|
||||||
for (Class<?> clazz : reflections.getTypesAnnotatedWith(CapabilityHolder.class)) {
|
|
||||||
if (!isNormalClass(clazz)){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Object o = clazz.getDeclaredConstructor().newInstance();
|
|
||||||
capabilityHolderInstances.put(clazz, o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成函数路由表
|
* 生成函数路由表
|
||||||
*/
|
*/
|
||||||
@@ -128,17 +139,16 @@ public final class CapabilityRegisterFactory extends AgentBaseFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成普通方法对应的函数路由表
|
* 扫描`@Capability`与`@CapabilityMethod`注解的类与方法
|
||||||
|
* 将`capabilityValue.methodSignature`作为key,函数对象为通过反射拿到的core实例对应的方法
|
||||||
*/
|
*/
|
||||||
private void generateMethodsRouterTable() {
|
private void generateMethodsRouterTable() {
|
||||||
//扫描`@Capability`与`@CapabilityMethod`注解的类与方法
|
|
||||||
//将`capabilityValue.methodSignature`作为key,函数对象为通过反射拿到的core实例对应的方法
|
|
||||||
cores.forEach(core -> Arrays.stream(core.getMethods())
|
cores.forEach(core -> Arrays.stream(core.getMethods())
|
||||||
.filter(method -> method.isAnnotationPresent(CapabilityMethod.class))
|
.filter(method -> method.isAnnotationPresent(CapabilityMethod.class))
|
||||||
.forEach(method -> {
|
.forEach(method -> {
|
||||||
Function<Object[], Object> function = args -> {
|
Function<Object[], Object> function = args -> {
|
||||||
try {
|
try {
|
||||||
return method.invoke(capabilityCoreInstances.get(core), args);
|
return method.invoke(coreInstances.get(core), args);
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -154,12 +164,12 @@ public final class CapabilityRegisterFactory extends AgentBaseFactory {
|
|||||||
/**
|
/**
|
||||||
* 反射获取<code>CapabilityCore</code>实例
|
* 反射获取<code>CapabilityCore</code>实例
|
||||||
*/
|
*/
|
||||||
private void setCapabilityCoreInstances() {
|
private void setCoreInstances() {
|
||||||
try {
|
try {
|
||||||
for (Class<?> core : cores) {
|
for (Class<?> core : cores) {
|
||||||
Constructor<?> constructor = core.getDeclaredConstructor();
|
Constructor<?> constructor = core.getDeclaredConstructor();
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
capabilityCoreInstances.put(core, constructor.newInstance());
|
coreInstances.put(core, constructor.newInstance());
|
||||||
}
|
}
|
||||||
} catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
|
} catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
|
||||||
IllegalAccessException e) {
|
IllegalAccessException e) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|||||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||||
import work.slhaf.partner.api.agent.factory.context.ConfigFactoryContext;
|
import work.slhaf.partner.api.agent.factory.context.ConfigFactoryContext;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.ModuleCheckFactory;
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||||
import work.slhaf.partner.api.agent.runtime.config.FileAgentConfigManager;
|
import work.slhaf.partner.api.agent.runtime.config.FileAgentConfigManager;
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
import work.slhaf.partner.api.chat.pojo.Message;
|
||||||
@@ -11,6 +12,14 @@ import work.slhaf.partner.api.chat.pojo.Message;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>Agent启动流程 0</h2>
|
||||||
|
* <p>
|
||||||
|
* 通过指定的 {@link AgentConfigManager} 或者默认的 {@link FileAgentConfigManager} 加载配置文件
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* <p>下一步流程请参阅{@link ModuleCheckFactory}</p>
|
||||||
|
*/
|
||||||
public class ConfigLoaderFactory extends AgentBaseFactory {
|
public class ConfigLoaderFactory extends AgentBaseFactory {
|
||||||
|
|
||||||
private AgentConfigManager agentConfigManager;
|
private AgentConfigManager agentConfigManager;
|
||||||
@@ -23,7 +32,7 @@ public class ConfigLoaderFactory extends AgentBaseFactory {
|
|||||||
modelConfigMap = factoryContext.getModelConfigMap();
|
modelConfigMap = factoryContext.getModelConfigMap();
|
||||||
modelPromptMap = factoryContext.getModelPromptMap();
|
modelPromptMap = factoryContext.getModelPromptMap();
|
||||||
|
|
||||||
if (AgentConfigManager.INSTANCE == null){
|
if (AgentConfigManager.INSTANCE == null) {
|
||||||
AgentConfigManager.setINSTANCE(new FileAgentConfigManager());
|
AgentConfigManager.setINSTANCE(new FileAgentConfigManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import cn.hutool.core.util.ClassUtil;
|
|||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
import work.slhaf.partner.api.agent.factory.module.annotation.*;
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleCheckException;
|
import work.slhaf.partner.api.agent.factory.module.exception.ModuleCheckException;
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
|
||||||
@@ -21,6 +18,30 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.getMethodAnnotationTypeSet;
|
import static work.slhaf.partner.api.agent.util.AgentUtil.getMethodAnnotationTypeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>Agent启动流程 1</h2>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 检查模块部分抽象类与注解、接口的使用方式
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleCheckFactory#annotationAbstractCheck(Set, Class)}</p>
|
||||||
|
* 所有添加了 {@link AgentModule} 注解的类都将作为Agent的执行模块,为规范模块入口,都必须实现抽象类: {@link AgentRunningModule}; {@link AgentSubModule} 注解所在类则必须实现 {@link AgentRunningSubModule}
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleCheckFactory#moduleConstructorsCheck(Set)}</p>
|
||||||
|
* 所有 {@link AgentModule} 与 {@link AgentSubModule} 注解所在类都必须具备空参构造方法,初始化逻辑可放在 @Init 注解所处方法中,将在 Capability 与 subModules 注入后才会执行
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleCheckFactory#activateModelImplCheck()}</p>
|
||||||
|
* 检查实现了 {@link ActivateModel} 的模块数量、名称与prompt是否一致
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>下一步流程请参阅{@link ModuleRegisterFactory}</p>
|
||||||
|
*/
|
||||||
public class ModuleCheckFactory extends AgentBaseFactory {
|
public class ModuleCheckFactory extends AgentBaseFactory {
|
||||||
|
|
||||||
private Reflections reflections;
|
private Reflections reflections;
|
||||||
@@ -32,13 +53,14 @@ public class ModuleCheckFactory extends AgentBaseFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() {
|
protected void run() {
|
||||||
Set<Class<?>> types = reflections.getTypesAnnotatedWith(AgentModule.class);
|
Set<Class<?>> moduleTypes = reflections.getTypesAnnotatedWith(AgentModule.class);
|
||||||
//检查注解AgentModule所在类是否继承了AgentInteractionModule
|
Set<Class<?>> subModuleTypes = reflections.getTypesAnnotatedWith(AgentSubModule.class);
|
||||||
agentModuleAnnotationCheck(types);
|
//检查注解AgentModule或AgentSubModule所在类是否继承了对应的抽象类
|
||||||
|
annotationAbstractCheck(moduleTypes, AgentRunningModule.class);
|
||||||
|
annotationAbstractCheck(subModuleTypes, AgentRunningSubModule.class);
|
||||||
//检查AgentModule是否具备无参构造方法
|
//检查AgentModule是否具备无参构造方法
|
||||||
moduleConstructorsCheck(types);
|
moduleConstructorsCheck(moduleTypes);
|
||||||
//检查hook注解所在方法是否位于AgentInteractionModule子类/AgentInteractionSubModule子类/ActivateModel子类
|
moduleConstructorsCheck(subModuleTypes);
|
||||||
hookLocationCheck();
|
|
||||||
//检查实现了ActivateModel的模块数量、名称与prompt是否一致
|
//检查实现了ActivateModel的模块数量、名称与prompt是否一致
|
||||||
activateModelImplCheck();
|
activateModelImplCheck();
|
||||||
}
|
}
|
||||||
@@ -92,7 +114,7 @@ public class ModuleCheckFactory extends AgentBaseFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initHookLocationCheck() {
|
private void initHookLocationCheck() {
|
||||||
Set<Class<?>> types = getMethodAnnotationTypeSet(AgentModule.class,reflections);
|
Set<Class<?>> types = getMethodAnnotationTypeSet(AgentModule.class, reflections);
|
||||||
checkLocation(types);
|
checkLocation(types);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,12 +150,12 @@ public class ModuleCheckFactory extends AgentBaseFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void agentModuleAnnotationCheck(Set<Class<?>> types) {
|
private void annotationAbstractCheck(Set<Class<?>> types, Class<?> clazz) {
|
||||||
for (Class<?> type : types) {
|
for (Class<?> type : types) {
|
||||||
if (type.isAnnotation()) {
|
if (type.isAnnotation()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (AgentRunningModule.class.isAssignableFrom(type) && ClassUtil.isNormalClass(type)) {
|
if (clazz.isAssignableFrom(type) && ClassUtil.isNormalClass(type)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw new ModuleCheckException("存在未继承AgentInteractionModule.class的AgentModule实现: " + type.getSimpleName());
|
throw new ModuleCheckException("存在未继承AgentInteractionModule.class的AgentModule实现: " + type.getSimpleName());
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module;
|
package work.slhaf.partner.api.agent.factory.module;
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||||
|
import work.slhaf.partner.api.agent.factory.AgentRegisterFactory;
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||||
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
||||||
@@ -10,6 +11,7 @@ import work.slhaf.partner.api.agent.factory.module.pojo.MetaMethod;
|
|||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaSubModule;
|
import work.slhaf.partner.api.agent.factory.module.pojo.MetaSubModule;
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||||
|
import work.slhaf.partner.api.agent.util.AgentUtil;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -19,7 +21,22 @@ import static work.slhaf.partner.api.agent.util.AgentUtil.collectExtendedClasses
|
|||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 负责执行前hook逻辑
|
* <h2>Agent启动流程 7</h2>
|
||||||
|
*
|
||||||
|
* <p>负责执行初始化hook逻辑,即 {@link Init} 注解所在方法</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleInitHookExecuteFactory#collectInitHookMethods(Class)}</p>
|
||||||
|
* 分别遍历前置模块拿到的模块列表({@link ModuleInitHookExecuteFactory#moduleList}, {@link ModuleInitHookExecuteFactory#subModuleList}),通过 {@link AgentUtil#collectExtendedClasses(Class, Class)} 收集到当前模块类的继承链上的所有类后,收集其所有带有 {@link Init} 注解的方法
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleInitHookExecuteFactory#proceedInitMethods(BaseMetaModule, List)}</p>
|
||||||
|
* 收集好初始化方法后,将通过反射执行该方法,所用实例即为前置模块中收集到的执行模块与子模块的 {@link MetaModule} 与 {@link MetaSubModule} 内容
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>Agent启动流程到此进行完毕。整个工厂执行链中均为针对 {@link AgentRegisterContext} 进行的操作,在 {@link AgentRegisterFactory} 中,将进行最终处理以及将必要内容进行传递。</p>
|
||||||
*/
|
*/
|
||||||
public class ModuleInitHookExecuteFactory extends AgentBaseFactory {
|
public class ModuleInitHookExecuteFactory extends AgentBaseFactory {
|
||||||
|
|
||||||
@@ -36,15 +53,15 @@ public class ModuleInitHookExecuteFactory extends AgentBaseFactory {
|
|||||||
@Override
|
@Override
|
||||||
protected void run() {
|
protected void run() {
|
||||||
//遍历模块列表,并向上查找@Init注解
|
//遍历模块列表,并向上查找@Init注解
|
||||||
for (MetaModule metaModule : moduleList) {
|
|
||||||
List<MetaMethod> initHookMethods = collectInitHookMethods(metaModule.getClazz());
|
|
||||||
proceedInitMethods(metaModule, initHookMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MetaSubModule metaSubModule : subModuleList) {
|
for (MetaSubModule metaSubModule : subModuleList) {
|
||||||
List<MetaMethod> initHookMethods = collectInitHookMethods(metaSubModule.getClazz());
|
List<MetaMethod> initHookMethods = collectInitHookMethods(metaSubModule.getClazz());
|
||||||
proceedInitMethods(metaSubModule, initHookMethods);
|
proceedInitMethods(metaSubModule, initHookMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (MetaModule metaModule : moduleList) {
|
||||||
|
List<MetaMethod> initHookMethods = collectInitHookMethods(metaModule.getClazz());
|
||||||
|
proceedInitMethods(metaModule, initHookMethods);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void proceedInitMethods(BaseMetaModule metaModule, List<MetaMethod> initHookMethods) {
|
private void proceedInitMethods(BaseMetaModule metaModule, List<MetaMethod> initHookMethods) {
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import net.bytebuddy.ByteBuddy;
|
|||||||
import net.bytebuddy.implementation.MethodDelegation;
|
import net.bytebuddy.implementation.MethodDelegation;
|
||||||
import net.bytebuddy.implementation.bind.annotation.*;
|
import net.bytebuddy.implementation.bind.annotation.*;
|
||||||
import net.bytebuddy.matcher.ElementMatchers;
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
import org.reflections.Reflections;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||||
|
import work.slhaf.partner.api.agent.factory.capability.CapabilityCheckFactory;
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||||
|
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
||||||
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
||||||
@@ -18,10 +19,9 @@ import work.slhaf.partner.api.agent.factory.module.pojo.MetaMethod;
|
|||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaSubModule;
|
import work.slhaf.partner.api.agent.factory.module.pojo.MetaSubModule;
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.Module;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
@@ -30,46 +30,85 @@ import java.util.stream.Collectors;
|
|||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.collectExtendedClasses;
|
import static work.slhaf.partner.api.agent.util.AgentUtil.collectExtendedClasses;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过扫描注解<code>@BeforeExecute</code>,获取到各个模块的后hook逻辑并通过动态代理添加到执行逻辑之后
|
* <h2>Agent启动流程 3</h2>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 扫描前置模块各个hook注解生成代理对象,放入对应的list中并按照类型为键放入 {@link ModuleProxyFactory#capabilityHolderInstances} 中供后续完成能力(capability)注入
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
*
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleProxyFactory#createProxiedInstances()}</p>
|
||||||
|
* 根据moduleList中的类型信息,向上查找继承链获取所有hook方法收集为{@link MethodsListRecord},然后通过ByteBuddy根据收集到的preHook与postHook生成代理对象,放入对应的 {@link MetaModule} 对象以及 instanceMap 中
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleProxyFactory#injectSubModule()}</p>
|
||||||
|
* 通过反射将子模块实例注入到执行模块中带有注解 {@link InjectModule} 的字段
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>下一步流程请参阅{@link CapabilityCheckFactory}</p>
|
||||||
*/
|
*/
|
||||||
public class ModuleProxyFactory extends AgentBaseFactory {
|
public class ModuleProxyFactory extends AgentBaseFactory {
|
||||||
|
|
||||||
private List<MetaModule> moduleList;
|
private List<MetaModule> moduleList;
|
||||||
private List<MetaSubModule> subModuleList;
|
private List<MetaSubModule> subModuleList;
|
||||||
private Reflections reflections;
|
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
||||||
private final HashMap<Class<?>, Object> subModuleInstances = new HashMap<>();
|
private final HashMap<Class<?>, Object> subModuleInstances = new HashMap<>();
|
||||||
private final HashMap<Class<?>, Object> moduleInstances = new HashMap<>();
|
private final HashMap<Class<?>, Object> moduleInstances = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
protected void setVariables(AgentRegisterContext context) {
|
||||||
ModuleFactoryContext factoryContext = context.getModuleFactoryContext();
|
ModuleFactoryContext factoryContext = context.getModuleFactoryContext();
|
||||||
|
CapabilityFactoryContext capabilityFactoryContext = context.getCapabilityFactoryContext();
|
||||||
moduleList = factoryContext.getAgentModuleList();
|
moduleList = factoryContext.getAgentModuleList();
|
||||||
subModuleList = factoryContext.getAgentSubModuleList();
|
subModuleList = factoryContext.getAgentSubModuleList();
|
||||||
reflections = context.getReflections();
|
capabilityHolderInstances = capabilityFactoryContext.getCapabilityHolderInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() {
|
protected void run() {
|
||||||
generateInstances();
|
createProxiedInstances();
|
||||||
createProxy();
|
|
||||||
injectSubModule();
|
injectSubModule();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void injectSubModule() {
|
private void injectSubModule() {
|
||||||
Set<Field> fields = reflections.getFieldsAnnotatedWith(InjectModule.class);
|
for (MetaModule module : moduleList) {
|
||||||
try {
|
Arrays.stream(module.getClazz().getFields())
|
||||||
for (Field field : fields) {
|
.filter(field -> field.isAnnotationPresent(InjectModule.class))
|
||||||
field.setAccessible(true);
|
.forEach(field -> {
|
||||||
field.set(moduleInstances.get(field.getDeclaringClass()), subModuleInstances.get(field.getType()));
|
try {
|
||||||
}
|
field.setAccessible(true);
|
||||||
} catch (Exception e) {
|
field.set(
|
||||||
throw new ModuleInstanceGenerateFailedException("模块实例注入失败", e);
|
moduleInstances.get(module.getClazz()),
|
||||||
|
subModuleInstances.get(field.getType())
|
||||||
|
);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new ModuleInstanceGenerateFailedException("模块实例注入失败", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createProxy() {
|
private void createProxiedInstances() {
|
||||||
generateModuleProxy(moduleList);
|
generateModuleProxy(moduleList);
|
||||||
generateModuleProxy(subModuleList);
|
generateModuleProxy(subModuleList);
|
||||||
|
updateInstanceMap(moduleInstances, moduleList);
|
||||||
|
updateInstanceMap(subModuleInstances, subModuleList);
|
||||||
|
updateCapabilityHolderInstances();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCapabilityHolderInstances() {
|
||||||
|
capabilityHolderInstances.putAll(moduleInstances);
|
||||||
|
capabilityHolderInstances.putAll(subModuleInstances);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInstanceMap(HashMap<Class<?>, Object> instanceMap, List<? extends BaseMetaModule> list) {
|
||||||
|
for (BaseMetaModule baseMetaModule : list) {
|
||||||
|
instanceMap.put(baseMetaModule.getClazz(), baseMetaModule.getInstance());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -88,8 +127,8 @@ public class ModuleProxyFactory extends AgentBaseFactory {
|
|||||||
|
|
||||||
private void generateProxiedInstances(MethodsListRecord record, BaseMetaModule module) {
|
private void generateProxiedInstances(MethodsListRecord record, BaseMetaModule module) {
|
||||||
try {
|
try {
|
||||||
Class<? extends AgentRunningModule> clazz = module.getClazz();
|
Class<? extends Module> clazz = module.getClazz();
|
||||||
Class<? extends AgentRunningModule> proxyClass = new ByteBuddy()
|
Class<? extends Module> proxyClass = new ByteBuddy()
|
||||||
.subclass(clazz)
|
.subclass(clazz)
|
||||||
.method(ElementMatchers.isOverriddenFrom(AgentRunningModule.class))
|
.method(ElementMatchers.isOverriddenFrom(AgentRunningModule.class))
|
||||||
.intercept(MethodDelegation.to(new ModuleProxyInterceptor(record.post, record.pre)))
|
.intercept(MethodDelegation.to(new ModuleProxyInterceptor(record.post, record.pre)))
|
||||||
@@ -165,30 +204,6 @@ public class ModuleProxyFactory extends AgentBaseFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateInstances() {
|
|
||||||
for (MetaModule module : moduleList) {
|
|
||||||
try {
|
|
||||||
Class<? extends AgentRunningModule> clazz = module.getClazz();
|
|
||||||
AgentRunningModule instance = clazz.getConstructor().newInstance();
|
|
||||||
module.setInstance(instance);
|
|
||||||
moduleInstances.put(module.getClazz(), instance);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ModuleInstanceGenerateFailedException("模块实例构造失败:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MetaSubModule module : subModuleList) {
|
|
||||||
try {
|
|
||||||
Class<? extends AgentRunningSubModule> clazz = module.getClazz();
|
|
||||||
AgentRunningSubModule instance = clazz.getConstructor().newInstance();
|
|
||||||
module.setInstance(instance);
|
|
||||||
subModuleInstances.put(module.getClazz(), instance);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ModuleInstanceGenerateFailedException("模块实例构造失败:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private record ModuleProxyInterceptor(List<MetaMethod> postHookMethods, List<MetaMethod> preHookMethods) {
|
private record ModuleProxyInterceptor(List<MetaMethod> postHookMethods, List<MetaMethod> preHookMethods) {
|
||||||
@RuntimeType
|
@RuntimeType
|
||||||
public Object intercept(@Origin Method method, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper, @This Object proxy) throws Exception {
|
public Object intercept(@Origin Method method, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper, @This Object proxy) throws Exception {
|
||||||
|
|||||||
@@ -17,7 +17,27 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 负责扫描<code>@Module</code>注解获取模块实例
|
* <h2>Agent启动流程 2</h2>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 负责收集 {@link AgentModule} 与 {@link AgentSubModule} 注解所在类的信息,供后续工厂完成动态代理、模块与能力注入
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleRegisterFactory#setModuleList()}</p>
|
||||||
|
* 扫描 {@link AgentModule} 注解,获取执行模块信息: 类型、模块名称({@link AgentModule#name()}),执行顺序。并按照注解的 {@link AgentModule#order()} 字段进行排序
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <p>{@link ModuleRegisterFactory#setSubModuleList()}</p>
|
||||||
|
* 扫描 {@link AgentSubModule} 注册,获取子模块类型信息
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* 两种模块都将存入各自的list中,供后续模块完成注册与注入
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>下一步流程请参阅{@link ModuleProxyFactory}</p>
|
||||||
*/
|
*/
|
||||||
public class ModuleRegisterFactory extends AgentBaseFactory {
|
public class ModuleRegisterFactory extends AgentBaseFactory {
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package work.slhaf.partner.common.util;
|
package work.slhaf.partner.common.util;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONArray;
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
import work.slhaf.partner.Agent;
|
import work.slhaf.partner.api.agent.Agent;
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
import work.slhaf.partner.api.chat.pojo.Message;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
|
||||||
import work.slhaf.partner.core.cognation.CognationCapability;
|
import work.slhaf.partner.core.cognation.CognationCapability;
|
||||||
import work.slhaf.partner.core.common.pojo.MemoryResult;
|
import work.slhaf.partner.core.common.pojo.MemoryResult;
|
||||||
import work.slhaf.partner.core.submodule.cache.CacheCapability;
|
import work.slhaf.partner.core.submodule.cache.CacheCapability;
|
||||||
@@ -36,8 +37,6 @@ import java.util.List;
|
|||||||
@AgentModule(name="memory_selector",order=1)
|
@AgentModule(name="memory_selector",order=1)
|
||||||
public class MemorySelector extends PreRunningModule {
|
public class MemorySelector extends PreRunningModule {
|
||||||
|
|
||||||
private static volatile MemorySelector memorySelector;
|
|
||||||
|
|
||||||
@InjectCapability
|
@InjectCapability
|
||||||
private CacheCapability cacheCapability;
|
private CacheCapability cacheCapability;
|
||||||
@InjectCapability
|
@InjectCapability
|
||||||
@@ -45,26 +44,10 @@ public class MemorySelector extends PreRunningModule {
|
|||||||
@InjectCapability
|
@InjectCapability
|
||||||
private CognationCapability cognationCapability;
|
private CognationCapability cognationCapability;
|
||||||
|
|
||||||
|
@InjectModule
|
||||||
private SliceSelectEvaluator sliceSelectEvaluator;
|
private SliceSelectEvaluator sliceSelectEvaluator;
|
||||||
|
@InjectModule
|
||||||
private MemorySelectExtractor memorySelectExtractor;
|
private MemorySelectExtractor memorySelectExtractor;
|
||||||
private SessionManager sessionManager;
|
|
||||||
|
|
||||||
private MemorySelector() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MemorySelector getInstance() throws IOException, ClassNotFoundException {
|
|
||||||
if (memorySelector == null) {
|
|
||||||
synchronized (MemorySelector.class) {
|
|
||||||
if (memorySelector == null) {
|
|
||||||
memorySelector = new MemorySelector();
|
|
||||||
memorySelector.setSliceSelectEvaluator(SliceSelectEvaluator.getInstance());
|
|
||||||
memorySelector.setMemorySelectExtractor(MemorySelectExtractor.getInstance());
|
|
||||||
memorySelector.setSessionManager(SessionManager.getInstance());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return memorySelector;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(PartnerRunningFlowContext runningFlowContext) throws IOException, ClassNotFoundException {
|
public void execute(PartnerRunningFlowContext runningFlowContext) throws IOException, ClassNotFoundException {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ 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.partner.api.agent.factory.module.annotation.AgentSubModule;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
||||||
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
|
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
|
||||||
@@ -30,21 +32,14 @@ import static work.slhaf.partner.common.util.ExtractUtil.extractJson;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@AgentSubModule
|
||||||
public class SliceSelectEvaluator extends AgentRunningSubModule<EvaluatorInput, List<EvaluatedSlice>> implements ActivateModel {
|
public class SliceSelectEvaluator extends AgentRunningSubModule<EvaluatorInput, List<EvaluatedSlice>> implements ActivateModel {
|
||||||
private static volatile SliceSelectEvaluator sliceSelectEvaluator;
|
|
||||||
private InteractionThreadPoolExecutor executor;
|
private InteractionThreadPoolExecutor executor;
|
||||||
|
|
||||||
public static SliceSelectEvaluator getInstance() throws IOException, ClassNotFoundException {
|
@Init
|
||||||
if (sliceSelectEvaluator == null) {
|
public void init() {
|
||||||
synchronized (SliceSelectEvaluator.class) {
|
executor = InteractionThreadPoolExecutor.getInstance();
|
||||||
if (sliceSelectEvaluator == null) {
|
|
||||||
sliceSelectEvaluator = new SliceSelectEvaluator();
|
|
||||||
sliceSelectEvaluator.setExecutor(InteractionThreadPoolExecutor.getInstance());
|
|
||||||
log.info("SliceEvaluator注册完毕...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sliceSelectEvaluator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import lombok.Data;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
import work.slhaf.partner.api.chat.pojo.Message;
|
||||||
@@ -21,7 +22,6 @@ import work.slhaf.partner.module.modules.memory.selector.extractor.data.Extracto
|
|||||||
import work.slhaf.partner.module.modules.memory.selector.extractor.data.ExtractorMatchData;
|
import work.slhaf.partner.module.modules.memory.selector.extractor.data.ExtractorMatchData;
|
||||||
import work.slhaf.partner.module.modules.memory.selector.extractor.data.ExtractorResult;
|
import work.slhaf.partner.module.modules.memory.selector.extractor.data.ExtractorResult;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -31,36 +31,25 @@ import static work.slhaf.partner.common.util.ExtractUtil.fixTopicPath;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class MemorySelectExtractor extends AgentRunningSubModule<PartnerRunningFlowContext, ExtractorResult> implements ActivateModel {
|
public class MemorySelectExtractor extends AgentRunningSubModule<PartnerRunningFlowContext, ExtractorResult>
|
||||||
|
implements ActivateModel {
|
||||||
private static volatile MemorySelectExtractor memorySelectExtractor;
|
|
||||||
|
|
||||||
@InjectCapability
|
@InjectCapability
|
||||||
private MemoryCapability memoryCapability;
|
private MemoryCapability memoryCapability;
|
||||||
@InjectCapability
|
@InjectCapability
|
||||||
private CognationCapability cognationCapability;
|
private CognationCapability cognationCapability;
|
||||||
|
|
||||||
private SessionManager sessionManager;
|
private SessionManager sessionManager;
|
||||||
|
|
||||||
private MemorySelectExtractor() {
|
@Init
|
||||||
modelSettings();
|
public void init() {
|
||||||
}
|
sessionManager = SessionManager.getInstance();
|
||||||
|
|
||||||
public static MemorySelectExtractor getInstance() throws IOException, ClassNotFoundException {
|
|
||||||
if (memorySelectExtractor == null) {
|
|
||||||
synchronized (MemorySelectExtractor.class) {
|
|
||||||
if (memorySelectExtractor == null) {
|
|
||||||
memorySelectExtractor = new MemorySelectExtractor();
|
|
||||||
memorySelectExtractor.setSessionManager(SessionManager.getInstance());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return memorySelectExtractor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExtractorResult execute(PartnerRunningFlowContext context) {
|
public ExtractorResult execute(PartnerRunningFlowContext context) {
|
||||||
log.debug("[MemorySelectExtractor] 主题提取模块开始...");
|
log.debug("[MemorySelectExtractor] 主题提取模块开始...");
|
||||||
//结构化为指定格式
|
// 结构化为指定格式
|
||||||
List<Message> chatMessages = new ArrayList<>();
|
List<Message> chatMessages = new ArrayList<>();
|
||||||
List<MetaMessage> metaMessages = sessionManager.getSingleMetaMessageMap().get(context.getUserId());
|
List<MetaMessage> metaMessages = sessionManager.getSingleMetaMessageMap().get(context.getUserId());
|
||||||
if (metaMessages == null) {
|
if (metaMessages == null) {
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import lombok.Data;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
||||||
|
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
|
||||||
import work.slhaf.partner.api.chat.constant.ChatConstant;
|
import work.slhaf.partner.api.chat.constant.ChatConstant;
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
import work.slhaf.partner.api.chat.pojo.Message;
|
||||||
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
|
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
|
||||||
@@ -32,6 +35,7 @@ import static work.slhaf.partner.common.util.ExtractUtil.extractUserId;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@AgentModule(name="memory_updater",order=6)
|
||||||
public class MemoryUpdater extends PostRunningModule {
|
public class MemoryUpdater extends PostRunningModule {
|
||||||
|
|
||||||
private static volatile MemoryUpdater memoryUpdater;
|
private static volatile MemoryUpdater memoryUpdater;
|
||||||
@@ -47,25 +51,24 @@ public class MemoryUpdater extends PostRunningModule {
|
|||||||
private CacheCapability cacheCapability;
|
private CacheCapability cacheCapability;
|
||||||
@InjectCapability
|
@InjectCapability
|
||||||
private PerceiveCapability perceiveCapability;
|
private PerceiveCapability perceiveCapability;
|
||||||
private InteractionThreadPoolExecutor executor;
|
|
||||||
|
@InjectModule
|
||||||
private MemorySelectExtractor memorySelectExtractor;
|
private MemorySelectExtractor memorySelectExtractor;
|
||||||
|
@InjectModule
|
||||||
private MemorySummarizer memorySummarizer;
|
private MemorySummarizer memorySummarizer;
|
||||||
|
|
||||||
private SessionManager sessionManager;
|
private SessionManager sessionManager;
|
||||||
|
private InteractionThreadPoolExecutor executor;
|
||||||
/**
|
/**
|
||||||
* 用于临时存储完整对话记录,在MemoryManager的分离后
|
* 用于临时存储完整对话记录,在MemoryManager的分离后
|
||||||
*/
|
*/
|
||||||
private List<Message> tempMessage;
|
private List<Message> tempMessage;
|
||||||
|
|
||||||
private MemoryUpdater() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MemoryUpdater getInstance() throws IOException, ClassNotFoundException {
|
public static MemoryUpdater getInstance() throws IOException, ClassNotFoundException {
|
||||||
if (memoryUpdater == null) {
|
if (memoryUpdater == null) {
|
||||||
synchronized (MemoryUpdater.class) {
|
synchronized (MemoryUpdater.class) {
|
||||||
if (memoryUpdater == null) {
|
if (memoryUpdater == null) {
|
||||||
memoryUpdater = new MemoryUpdater();
|
memoryUpdater = new MemoryUpdater();
|
||||||
memoryUpdater.setMemorySelectExtractor(MemorySelectExtractor.getInstance());
|
|
||||||
memoryUpdater.setMemorySummarizer(MemorySummarizer.getInstance());
|
|
||||||
memoryUpdater.setSessionManager(SessionManager.getInstance());
|
memoryUpdater.setSessionManager(SessionManager.getInstance());
|
||||||
memoryUpdater.setExecutor(InteractionThreadPoolExecutor.getInstance());
|
memoryUpdater.setExecutor(InteractionThreadPoolExecutor.getInstance());
|
||||||
memoryUpdater.setScheduledUpdater();
|
memoryUpdater.setScheduledUpdater();
|
||||||
@@ -75,6 +78,13 @@ public class MemoryUpdater extends PostRunningModule {
|
|||||||
return memoryUpdater;
|
return memoryUpdater;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Init
|
||||||
|
public void init() {
|
||||||
|
executor = InteractionThreadPoolExecutor.getInstance();
|
||||||
|
sessionManager = SessionManager.getInstance();
|
||||||
|
setScheduledUpdater();
|
||||||
|
}
|
||||||
|
|
||||||
private void setScheduledUpdater() {
|
private void setScheduledUpdater() {
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
log.info("[MemoryUpdater] 记忆自动更新线程启动");
|
log.info("[MemoryUpdater] 记忆自动更新线程启动");
|
||||||
|
|||||||
@@ -2,27 +2,28 @@ package work.slhaf.partner.module.modules.task;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||||
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
|
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
|
||||||
import work.slhaf.partner.runtime.interaction.module.InteractionFlow;
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class TaskScheduler implements InteractionFlow {
|
public class TaskScheduler extends AgentRunningModule {
|
||||||
private static TaskScheduler taskScheduler;
|
private static TaskScheduler taskScheduler;
|
||||||
|
|
||||||
private TaskScheduler(){}
|
private TaskScheduler() {
|
||||||
|
}
|
||||||
|
|
||||||
public static TaskScheduler getInstance() {
|
public static TaskScheduler getInstance() {
|
||||||
if (taskScheduler == null) {
|
if (taskScheduler == null) {
|
||||||
taskScheduler = new TaskScheduler();
|
taskScheduler = new TaskScheduler();
|
||||||
log.info("TaskScheduler注册完毕...");
|
log.info("TaskScheduler注册完毕...");
|
||||||
}
|
|
||||||
|
|
||||||
return taskScheduler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return taskScheduler;
|
||||||
public void execute(PartnerRunningFlowContext runningFlowContext) {
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public void execute(PartnerRunningFlowContext runningFlowContext) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Partner 的目标不是复现某种单一能力,而是尝试在结构中形成
|
|||||||
- 系统的正常运作效果取决于各模块中大模型对于`prompt`的遵循能力,目前来看`qwen3`的遵循效果明显较好,但在轮次较多时,也容易出现不遵循的情况。
|
- 系统的正常运作效果取决于各模块中大模型对于`prompt`的遵循能力,目前来看`qwen3`的遵循效果明显较好,但在轮次较多时,也容易出现不遵循的情况。
|
||||||
|
|
||||||
## 规划
|
## 规划
|
||||||
- [ ] 完善注解驱动的注册机制
|
- [ ] 完成框架与本体的适配工作
|
||||||
- [ ] 实现任务与主动调度模块,目前打算用 `时间轮算法` 实现定时操作
|
- [ ] 实现任务与主动调度模块,目前打算用 `时间轮算法` 实现定时操作
|
||||||
- [ ] 完善具备‘记忆切片、实体图谱、向量召回’的三维记忆融合架构,包含 Episodic + Semantic + Fuzzy 三类记忆
|
- [ ] 完善具备‘记忆切片、实体图谱、向量召回’的三维记忆融合架构,包含 Episodic + Semantic + Fuzzy 三类记忆
|
||||||
- [ ] 服务端与客户端的通信加上消息队列,防止消息因连接断开而丢失。
|
- [ ] 服务端与客户端的通信加上消息队列,防止消息因连接断开而丢失。
|
||||||
|
|||||||
Reference in New Issue
Block a user