推进框架中的模块注册机制; 完善启动逻辑流程。

- 调整 InteractionFlowContext 为抽象类,实现者的模块上下文应当继承自该类。
- 完善 Agent 启动类具体逻辑
- 取消 Agent 类中 launchRunners 中关闭虚拟线程池的行为,交由JVM等待线程关闭,而非直接停止整个进程。
- 调整原hook注解分别为 BeforeExecute 、 AfterExecute,该注解用于模块抽象类的子类时可抽取重复逻辑
- 新增 Init 注解,用于执行初始化逻辑,可通过 order 属性指定顺序,默认为 0
- ModuleRegisterFactory 中新增‘加载 Init 注解标注的方法’的相关逻辑
- ModuleCheckFactory 添加对于 Init 注解的校验
- 完善了 ModuleProxyFactory 中设置代理实例的内容,可同时添加pre、post hook逻辑
This commit is contained in:
2025-08-06 00:17:10 +08:00
parent 507917157d
commit b1ed79ae9d
21 changed files with 232 additions and 60 deletions

View File

@@ -1,8 +1,10 @@
package work.slhaf.partner.api;
import work.slhaf.partner.api.entity.AgentContext;
import work.slhaf.partner.api.exception.AgentLaunchFailedException;
import work.slhaf.partner.api.factory.AgentRegisterFactory;
import work.slhaf.partner.api.factory.module.pojo.MetaModule;
import work.slhaf.partner.api.flow.AgentInteraction;
import work.slhaf.partner.api.flow.entity.InteractionFlowContext;
import java.util.ArrayList;
import java.util.List;
@@ -14,23 +16,38 @@ import java.util.concurrent.Executors;
*/
public class Agent {
private static final List<Runnable> runners = new ArrayList<>();
private final List<Runnable> runners = new ArrayList<>();
private final Class<?> applicationClass;
private final InteractionFlowContext interactionContext;
public static void run(Class<?> clazz) {
AgentContext context = AgentRegisterFactory.launch(clazz.getPackage().getName());
AgentInteraction.launch(context);
private Agent(Class<?> clazz, InteractionFlowContext interactionContext) {
this.applicationClass = clazz;
this.interactionContext = interactionContext;
}
public static Agent newAgent(Class<?> clazz, InteractionFlowContext interactionContext) {
if (clazz == null || interactionContext == null) {
throw new AgentLaunchFailedException("Agent class 和 interaction flow context 不能为 null");
}
return new Agent(clazz, interactionContext);
}
public void run() {
List<MetaModule> moduleList = AgentRegisterFactory.launch(applicationClass.getPackage().getName());
AgentInteraction.launch(moduleList, interactionContext);
launchRunners();
}
private static void launchRunners() {
private void launchRunners() {
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
for (Runnable runner : runners) {
executorService.execute(runner);
}
executorService.close();
}
public static void addRunner(Runnable runnable) {
runners.add(runnable);
public Agent addRunners(Runnable... runnable) {
runners.addAll(List.of(runnable));
return this;
}
}

View File

@@ -1,8 +1,10 @@
package work.slhaf.partner.api.entity;
import lombok.Data;
import work.slhaf.partner.api.factory.module.pojo.MetaModule;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
@@ -14,4 +16,5 @@ public class AgentContext {
private HashMap<Class<?>, Object> capabilityHolderInstances;
private Set<Class<?>> cores;
private Set<Class<?>> capabilities;
private List<MetaModule> moduleList;
}

View File

@@ -0,0 +1,11 @@
package work.slhaf.partner.api.exception;
public class AgentLaunchFailedException extends RuntimeException {
public AgentLaunchFailedException(String message, Throwable cause) {
super("Agent 启动失败: " + message, cause);
}
public AgentLaunchFailedException(String message) {
super("Agent 启动失败: " + message);
}
}

View File

@@ -1,8 +1,6 @@
package work.slhaf.partner.api.factory;
import cn.hutool.core.bean.BeanUtil;
import org.reflections.util.ClasspathHelper;
import work.slhaf.partner.api.entity.AgentContext;
import work.slhaf.partner.api.factory.capability.CapabilityCheckFactory;
import work.slhaf.partner.api.factory.capability.CapabilityInjectFactory;
import work.slhaf.partner.api.factory.capability.CapabilityRegisterFactory;
@@ -14,6 +12,7 @@ import work.slhaf.partner.api.factory.module.ModuleCheckFactory;
import work.slhaf.partner.api.factory.module.ModulePreHookExecuteFactory;
import work.slhaf.partner.api.factory.module.ModuleProxyFactory;
import work.slhaf.partner.api.factory.module.ModuleRegisterFactory;
import work.slhaf.partner.api.factory.module.pojo.MetaModule;
import java.io.File;
import java.net.URL;
@@ -27,7 +26,7 @@ public class AgentRegisterFactory {
private AgentRegisterFactory() {
}
public static AgentContext launch(String packageName) {
public static List<MetaModule> launch(String packageName) {
urls.addAll(packageNameToURL(packageName));
AgentRegisterContext registerContext = new AgentRegisterContext(urls);
//流程
@@ -46,11 +45,10 @@ public class AgentRegisterFactory {
//. 执行模块PreHook逻辑
new ModulePreHookExecuteFactory().execute(registerContext);
AgentContext agentContext = new AgentContext();
BeanUtil.copyProperties(registerContext, agentContext);
return agentContext;
return registerContext.getModuleFactoryContext().getModuleList();
}
/**
* 添加可扫描包
* @param packageName 指定的包名

View File

@@ -14,4 +14,5 @@ public class ModuleFactoryContext {
private List<MetaModule> moduleList = new ArrayList<>();
private HashMap<Class<?>,Set<MetaMethod>> preHookMethods = new HashMap<>();
private HashMap<Class<?>,Set<MetaMethod>> postHookMethods = new HashMap<>();
private HashMap<Class<?>,Set<MetaMethod>> initHookMethods = new HashMap<>();
}

View File

@@ -1,12 +1,14 @@
package work.slhaf.partner.api.factory.module;
import cn.hutool.core.util.ClassUtil;
import org.reflections.Reflections;
import work.slhaf.partner.api.factory.AgentBaseFactory;
import work.slhaf.partner.api.factory.config.ModelConfigManager;
import work.slhaf.partner.api.factory.context.AgentRegisterContext;
import work.slhaf.partner.api.factory.module.annotation.After;
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.Before;
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.exception.ModuleCheckException;
import work.slhaf.partner.api.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.flow.abstracts.AgentInteractionModule;
@@ -68,14 +70,35 @@ public class ModuleCheckFactory extends AgentBaseFactory {
}
private void hookLocationCheck() {
//检查@After注解
//检查@AfterExecute注解
postHookLocationCheck();
//检查@Before注解
//检查@BeforeExecute注解
preHookLocationCheck();
//检查@Init注解
initHookLocationCheck();
//检查@AgentModule注解是否只位于普通类上
agentModuleLocationCheck();
}
private void agentModuleLocationCheck() {
Set<Class<?>> types = reflections.getTypesAnnotatedWith(AgentModule.class);
for (Class<?> type : types) {
if (!ClassUtil.isNormalClass(type)) {
throw new ModuleCheckException("AgentModule 注解仅能位于普通类上! 异常类信息: " + type.getSimpleName());
}
}
}
private void initHookLocationCheck() {
Set<Method> methods = reflections.getMethodsAnnotatedWith(Init.class);
Set<Class<?>> types = methods.stream()
.map(Method::getDeclaringClass)
.collect(Collectors.toSet());
checkLocation(types);
}
private void preHookLocationCheck() {
Set<Method> methods = reflections.getMethodsAnnotatedWith(Before.class);
Set<Method> methods = reflections.getMethodsAnnotatedWith(BeforeExecute.class);
Set<Class<?>> types = methods.stream()
.map(Method::getDeclaringClass)
.collect(Collectors.toSet());
@@ -84,7 +107,7 @@ public class ModuleCheckFactory extends AgentBaseFactory {
private void postHookLocationCheck() {
Set<Method> methods = reflections.getMethodsAnnotatedWith(After.class);
Set<Method> methods = reflections.getMethodsAnnotatedWith(AfterExecute.class);
Set<Class<?>> types = methods.stream()
.map(Method::getDeclaringClass)
.collect(Collectors.toSet());

View File

@@ -1,9 +1,6 @@
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;
@@ -11,59 +8,81 @@ import work.slhaf.partner.api.factory.module.exception.ModuleInstanceGenerateFai
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.Comparator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* 通过扫描注解<code>@Before</code>获取到各个模块的后hook逻辑并通过动态代理添加到执行逻辑之后
* 通过扫描注解<code>@BeforeExecute</code>获取到各个模块的后hook逻辑并通过动态代理添加到执行逻辑之后
*/
public class ModuleProxyFactory extends AgentBaseFactory {
private List<MetaModule> moduleList;
private HashMap<Class<?>, Set<MetaMethod>> postHookMethods;
private HashMap<Class<?>, Set<MetaMethod>> 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 生成实例、并通过动态代理添加PostHook逻辑
//TODO 填充具体逻辑
generateInstances();
setPostHookProxy();
setHookProxy();
}
private void setPostHookProxy() {
private void setHookProxy() {
for (MetaModule module : moduleList) {
Class<?> clazz = module.getClazz();
try {
Class<?> proxyClass = new ByteBuddy()
.subclass(clazz)
.method(ElementMatchers.isOverriddenFrom(AgentInteractionModule.class))
.intercept(MethodDelegation.to(new ModuleProxyInterceptor(postHookMethods.get(clazz).stream().sorted(Comparator.comparing(MetaMethod::getOrder)).toList())))
.make()
.load(ModuleProxyFactory.class.getClassLoader())
.getLoaded();
AgentInteractionModule interactionModule = (AgentInteractionModule) proxyClass.getConstructor().newInstance();
//TODO 检测代理写法是否正确
//TODO 添加ModuleManager,负责统一管理Module的加载、卸载
MethodsListRecord record = getHookMethodsList(clazz);
//生成实例,
generateProxiedInstances(record);
} catch (Exception e) {
throw new ModuleProxyGenerateFailedException("创建代理对象失败: " + clazz.getSimpleName(), e);
}
}
}
private void generateProxiedInstances(MethodsListRecord record) {
}
private MethodsListRecord getHookMethodsList(Class<?> clazz) {
List<MetaMethod> post = new ArrayList<>();
List<MetaMethod> pre = new ArrayList<>();
//获取该类本身的hook逻辑
getHookMethodsList(post, pre, clazz);
//获取它所继承、实现的抽象类或接口, 以AgentInteractionModule、ActiveModel为终点
List<Class<?>> classes = getExtendedClasses(clazz);
//获取这些类中的hook逻辑
getHookMethodsList(post, pre, classes);
return new MethodsListRecord(post, pre);
}
private void getHookMethodsList(List<MetaMethod> post, List<MetaMethod> pre, List<Class<?>> classes) {
}
private List<Class<?>> getExtendedClasses(Class<?> clazz) {
return null;
}
private void getHookMethodsList(List<MetaMethod> post, List<MetaMethod> pre, Class<?> clazz) {
}
private void generateInstances() {
for (MetaModule metaModule : moduleList) {
try {
@@ -78,19 +97,27 @@ public class ModuleProxyFactory extends AgentBaseFactory {
private static class ModuleProxyInterceptor {
private List<MetaMethod> postHookMethods;
private final List<MetaMethod> postHookMethods;
private final List<MetaMethod> preHookMethods;
private ModuleProxyInterceptor(List<MetaMethod> postHookMethods) {
private ModuleProxyInterceptor(List<MetaMethod> postHookMethods, List<MetaMethod> preHookMethods) {
this.postHookMethods = postHookMethods;
this.preHookMethods = preHookMethods;
}
@RuntimeType
public Object intercept(@Origin Method method, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper, @This Object proxy) throws Exception {
for (MetaMethod metaMethod : preHookMethods) {
metaMethod.getMethod().invoke(proxy);
}
Object res = zuper.call();
for (MetaMethod metaMethod : postHookMethods) {
metaMethod.getMethod().invoke(proxy, allArguments);
metaMethod.getMethod().invoke(proxy);
}
return res;
}
}
record MethodsListRecord(List<MetaMethod> post, List<MetaMethod> pre) {
}
}

View File

@@ -4,9 +4,10 @@ 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.After;
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.Before;
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;
@@ -22,6 +23,7 @@ public class ModuleRegisterFactory extends AgentBaseFactory {
private List<MetaModule> moduleList;
private HashMap<Class<?>, Set<MetaMethod>> postHookMethods;
private HashMap<Class<?>, Set<MetaMethod>> preHookMethods;
private HashMap<Class<?>, Set<MetaMethod>> initHookMethods;
@Override
protected void setVariables(AgentRegisterContext context) {
@@ -30,6 +32,7 @@ public class ModuleRegisterFactory extends AgentBaseFactory {
moduleList = factoryContext.getModuleList();
postHookMethods = factoryContext.getPostHookMethods();
preHookMethods = factoryContext.getPreHookMethods();
initHookMethods = factoryContext.getInitHookMethods();
}
@Override
@@ -41,25 +44,37 @@ public class ModuleRegisterFactory extends AgentBaseFactory {
private void registerHookSet() {
setPostHookMethods();
setPreHookMethods();
setInitMethods();
}
private void setPreHookMethods() {
Set<Method> methods = reflections.getMethodsAnnotatedWith(After.class);
private void setInitMethods() {
Set<Method> methods = reflections.getMethodsAnnotatedWith(Init.class);
for (Method method : methods) {
MetaMethod metaMethod = new MetaMethod();
metaMethod.setMethod(method);
metaMethod.setOrder(method.getAnnotation(After.class).order());
metaMethod.setOrder(method.getAnnotation(Init.class).order());
addMetaMethod(method, metaMethod, initHookMethods);
}
}
private void setPreHookMethods() {
Set<Method> 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<Method> methods = reflections.getMethodsAnnotatedWith(Before.class);
Set<Method> methods = reflections.getMethodsAnnotatedWith(BeforeExecute.class);
for (Method method : methods) {
MetaMethod metaMethod = new MetaMethod();
metaMethod.setMethod(method);
metaMethod.setOrder(method.getAnnotation(Before.class).order());
metaMethod.setOrder(method.getAnnotation(BeforeExecute.class).order());
addMetaMethod(method, metaMethod, preHookMethods);
}
@@ -69,7 +84,7 @@ public class ModuleRegisterFactory extends AgentBaseFactory {
Class<?> clazz = method.getDeclaringClass();
if (preHookMethods.containsKey(clazz)) {
preHookMethods.get(clazz).add(metaMethod);
}else {
} else {
HashSet<MetaMethod> metaMethods = new HashSet<>();
metaMethods.add(metaMethod);
preHookMethods.put(clazz, metaMethods);

View File

@@ -15,6 +15,6 @@ import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface After {
public @interface AfterExecute {
int order() default 0;
}

View File

@@ -13,6 +13,6 @@ import java.lang.annotation.Target;
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
public @interface BeforeExecute {
int order() default 0;
}

View File

@@ -0,0 +1,12 @@
package work.slhaf.partner.api.factory.module.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Init {
int order() default 0;
}

View File

@@ -1,6 +1,9 @@
package work.slhaf.partner.api.flow;
import work.slhaf.partner.api.entity.AgentContext;
import work.slhaf.partner.api.factory.module.pojo.MetaModule;
import work.slhaf.partner.api.flow.entity.InteractionFlowContext;
import java.util.List;
/**
* Agent执行流程
@@ -9,7 +12,7 @@ public class AgentInteraction {
private AgentInteraction(){}
public static void launch(AgentContext context){
public static void launch(List<MetaModule> moduleList, InteractionFlowContext interactionContext){
//流程执行启动需考虑模块热插拔可结合http调整模块启用情况并序列化至本地或数据库中
}
}

View File

@@ -7,14 +7,14 @@ import work.slhaf.partner.api.common.chat.pojo.ChatResponse;
import work.slhaf.partner.api.common.chat.pojo.Message;
import work.slhaf.partner.api.factory.config.ModelConfigManager;
import work.slhaf.partner.api.factory.config.pojo.ModelConfig;
import work.slhaf.partner.api.factory.module.annotation.Before;
import work.slhaf.partner.api.factory.module.annotation.BeforeExecute;
import java.util.ArrayList;
import java.util.List;
public interface ActivateModel {
@Before
@BeforeExecute
default void modelSettings() {
Model model = new Model();
ModelConfig modelConfig = ModelConfigManager.INSTANCE.loadModelConfig(modelKey());

View File

@@ -6,5 +6,6 @@ import lombok.Data;
* 流程上下文
*/
@Data
public class InteractionFlowContext {
public abstract class InteractionFlowContext {
}

View File

@@ -0,0 +1,7 @@
import work.slhaf.partner.api.Agent;
public class TestApplication {
public static void main(String[] args) {
Agent.newAgent(TestApplication.class,null).run();
}
}

View File

@@ -0,0 +1,24 @@
package module;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import org.junit.jupiter.api.Test;
import work.slhaf.partner.api.flow.abstracts.AgentInteractionModule;
import java.lang.reflect.InvocationTargetException;
public class ModuleProxyTest {
@Test
public void test() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<? extends AgentInteractionModule> clazz = new ByteBuddy().subclass(MyAgentInteractionModule.class)
.method(ElementMatchers.isOverriddenFrom(AgentInteractionModule.class))
.intercept(MethodDelegation.to(
new MyModuleProxyInterceptor()
))
.make()
.load(ModuleProxyTest.class.getClassLoader())
.getLoaded();
clazz.getConstructor().newInstance().execute(null);
}
}

View File

@@ -0,0 +1,11 @@
package module;
import work.slhaf.partner.api.flow.abstracts.AgentInteractionModule;
import work.slhaf.partner.api.flow.entity.InteractionFlowContext;
public class MyAgentInteractionModule extends AgentInteractionModule {
@Override
public void execute(InteractionFlowContext context) {
System.out.println("MyAgentInteractionModule");
}
}

View File

@@ -0,0 +1,18 @@
package module;
import net.bytebuddy.implementation.bind.annotation.*;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class MyModuleProxyInterceptor {
public MyModuleProxyInterceptor() {}
@RuntimeType
public Object intercept(@Origin Method method, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper, @This Object proxy) throws Exception {
System.out.println("22222");
Object res = zuper.call();
System.out.println("11111");
return res;
}
}