推进框架中的模块注册机制,完善了模块校验与加载,接下来应当进行对于PostHook的动态代理以及模块的实例化逻辑。

- 移除了 ActivateModel 中的 promptModule 方法,不再需要
- 添加了必要的注释
- 为 AgentRegisterFactory 添加了用于指定扫描包的方法
- 新增了几个异常类
- 新增 MetaModule 类,包含Agent执行模块的必要信息,在工厂流程中作为执行模块的上下文
- 完善了 ModuleCheckFactory 中的检查逻辑
This commit is contained in:
2025-08-03 23:48:20 +08:00
parent 3c41abbba8
commit ca3ffca4ea
23 changed files with 256 additions and 68 deletions

View File

@@ -3,38 +3,58 @@ package work.slhaf.partner.api.factory;
import cn.hutool.core.bean.BeanUtil;
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;
import work.slhaf.partner.api.factory.config.ConfigLoaderFactory;
import work.slhaf.partner.api.factory.entity.AgentRegisterContext;
import work.slhaf.partner.api.factory.exception.ExternalModulePathNotExistException;
import work.slhaf.partner.api.factory.module.ModuleCheckFactory;
import work.slhaf.partner.api.factory.module.ModuleProxyFactory;
import work.slhaf.partner.api.factory.module.ModulePreHookExecuteFactory;
import work.slhaf.partner.api.factory.module.ModuleRegisterFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class AgentRegisterFactory {
private static List<String> paths = new ArrayList<>();
private AgentRegisterFactory() {
}
public static AgentContext launch(String path) {
AgentRegisterContext registerContext = new AgentRegisterContext(path);
paths.add(path);
AgentRegisterContext registerContext = new AgentRegisterContext(paths);
//流程
//0. 加载配置
new ConfigLoaderFactory().execute(registerContext);
//1. 执行register和check逻辑
//1. 注册并检查Capability
new CapabilityRegisterFactory().execute(registerContext);
new CapabilityCheckFactory().execute(registerContext);
new ModuleRegisterFactory().execute(registerContext);
//2. 注册并检查Module
new ModuleCheckFactory().execute(registerContext);
//2. 为module通过动态代理添加后hook逻辑并进行实例化
//3. 先一步注入Capability,避免因前hook逻辑存在针对能力的引用而报错
//4. 执行前hook逻辑
new ModuleRegisterFactory().execute(registerContext);
//3. 为module通过动态代理添加PostHook逻辑并进行实例化
new ModuleProxyFactory().execute(registerContext);
//. 先一步注入Capability,避免因前hook逻辑存在针对能力的引用而报错
new CapabilityInjectFactory().execute(registerContext);
//. 执行模块PreHook逻辑
new ModulePreHookExecuteFactory().execute(registerContext);
AgentContext agentContext = new AgentContext();
BeanUtil.copyProperties(registerContext,agentContext);
return agentContext;
}
//TODO 也需要可指定路径,当前只是新增了可扫描包
public static void addScanPath(String path) {
File file = new File(path);
if (!file.exists() || !file.isDirectory()) {
throw new ExternalModulePathNotExistException("不存在的外部模块目录: "+path);
}
paths.add(path);
}
}

View File

@@ -2,9 +2,13 @@ package work.slhaf.partner.api.factory.entity;
import lombok.Data;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ConfigurationBuilder;
import work.slhaf.partner.api.common.chat.pojo.Message;
import work.slhaf.partner.api.factory.config.pojo.ModelConfig;
import work.slhaf.partner.api.factory.module.pojo.MetaModule;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
@@ -21,8 +25,16 @@ public class AgentRegisterContext {
private Set<Class<?>> capabilities;
private HashMap<String, List<Message>> modelPromptMap = new HashMap<>();
private HashMap<String, ModelConfig> modelConfigMap = new HashMap<>();
private List<MetaModule> moduleList = new ArrayList<>();
public AgentRegisterContext(String path) {
reflections = new Reflections(path);
public AgentRegisterContext(List<String> paths) {
reflections = new Reflections(new ConfigurationBuilder().setScanners(
Scanners.FieldsAnnotated,
Scanners.SubTypes,
Scanners.MethodsAnnotated,
Scanners.TypesAnnotated
)
.forPackages(paths.toArray(paths.toArray(new String[0])))
);
}
}

View File

@@ -0,0 +1,7 @@
package work.slhaf.partner.api.factory.exception;
public class ExternalModulePathNotExistException extends RuntimeException {
public ExternalModulePathNotExistException(String message) {
super("AgentRegisterFactory 执行失败: " + message);
}
}

View File

@@ -1,18 +1,108 @@
package work.slhaf.partner.api.factory.module;
import org.reflections.Reflections;
import work.slhaf.partner.api.factory.config.ModelConfigManager;
import work.slhaf.partner.api.factory.entity.AgentBaseFactory;
import work.slhaf.partner.api.factory.entity.AgentRegisterContext;
import work.slhaf.partner.api.factory.module.annotation.After;
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.exception.ModuleCheckException;
import work.slhaf.partner.api.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.flow.abstracts.AgentInteractionModule;
import work.slhaf.partner.api.flow.abstracts.AgentInteractionSubModule;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class ModuleCheckFactory extends AgentBaseFactory {
private Reflections reflections;
@Override
protected void setVariables(AgentRegisterContext context) {
reflections = context.getReflections();
}
@Override
protected void run() {
//检查注解AgentModule所在类是否继承了AgentInteractionModule
agentModuleAnnotationCheck();
//检查hook注解所在方法是否位于AgentInteractionModule子类/AgentInteractionSubModule子类/ActivateModel子类
//检查当前加载的模块数量、名称与prompt是否一致
hookLocationCheck();
//检查实现了ActivateModel的模块数量、名称与prompt是否一致
activateModelImplCheck();
}
private void activateModelImplCheck() {
try {
Set<Class<? extends ActivateModel>> types = reflections.getSubTypesOf(ActivateModel.class);
Set<String> modelKeySet = new HashSet<>();
for (Class<? extends ActivateModel> type : types) {
ActivateModel instance = type.getConstructor().newInstance();
modelKeySet.add(instance.modelKey());
}
Set<String> promptKeySet = ModelConfigManager.INSTANCE.getModelPromptMap().keySet();
if (!promptKeySet.containsAll(modelKeySet)) {
modelKeySet.removeAll(promptKeySet);
throw new ModuleCheckException("存在未配置Prompt的ActivateModel实现! 缺少Prompt的ModelKey列表: "+ modelKeySet);
}
}catch (Exception e) {
throw new ModuleCheckException("ActivateModel 检测出错",e);
}
}
private void hookLocationCheck() {
//检查@After注解
postHookLocationCheck();
//检查@Before注解
preHookLocationCheck();
}
private void preHookLocationCheck() {
Set<Method> methods = reflections.getMethodsAnnotatedWith(Before.class);
Set<Class<?>> types = methods.stream()
.map(Method::getDeclaringClass)
.collect(Collectors.toSet());
checkLocation(types);
}
private void postHookLocationCheck() {
Set<Method> methods = reflections.getMethodsAnnotatedWith(After.class);
Set<Class<?>> types = methods.stream()
.map(Method::getDeclaringClass)
.collect(Collectors.toSet());
checkLocation(types);
}
private void checkLocation(Set<Class<?>> types) {
for (Class<?> type : types) {
if (AgentInteractionModule.class.isAssignableFrom(type)) {
continue;
}
if (AgentInteractionSubModule.class.isAssignableFrom(type)) {
continue;
}
if (ActivateModel.class.isAssignableFrom(type)) {
continue;
}
throw new ModuleCheckException("在不支持的类中使用了hook注解: "+type.getSimpleName());
}
}
private void agentModuleAnnotationCheck() {
Set<Class<?>> types = reflections.getTypesAnnotatedWith(AgentModule.class);
for (Class<?> type : types) {
if (type.isAnnotation()) {
continue;
}
if (type.isAssignableFrom(AgentInteractionModule.class)) {
continue;
}
throw new ModuleCheckException("存在未继承AgentInteractionModule.class的AgentModule实现: " + type.getSimpleName());
}
}
}

View File

@@ -1,4 +0,0 @@
package work.slhaf.partner.api.factory.module;
public class ModuleHookExecutor {
}

View File

@@ -0,0 +1,21 @@
package work.slhaf.partner.api.factory.module;
import work.slhaf.partner.api.factory.entity.AgentBaseFactory;
import work.slhaf.partner.api.factory.entity.AgentRegisterContext;
import java.lang.reflect.InvocationTargetException;
/**
* 负责执行前hook逻辑
*/
public class ModulePreHookExecuteFactory extends AgentBaseFactory {
@Override
protected void setVariables(AgentRegisterContext context) {
}
@Override
protected void run() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
}
}

View File

@@ -0,0 +1,21 @@
package work.slhaf.partner.api.factory.module;
import work.slhaf.partner.api.factory.entity.AgentBaseFactory;
import work.slhaf.partner.api.factory.entity.AgentRegisterContext;
import java.lang.reflect.InvocationTargetException;
/**
* 通过扫描注解<code>@Before</code>获取到各个模块的后hook逻辑并通过动态代理添加到执行逻辑之后
*/
public class ModuleProxyFactory extends AgentBaseFactory {
@Override
protected void setVariables(AgentRegisterContext context) {
}
@Override
protected void run() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//TODO 通过动态代理生成实例、添加PostHook逻辑
}
}

View File

@@ -3,6 +3,13 @@ package work.slhaf.partner.api.factory.module;
import org.reflections.Reflections;
import work.slhaf.partner.api.factory.entity.AgentBaseFactory;
import work.slhaf.partner.api.factory.entity.AgentRegisterContext;
import work.slhaf.partner.api.factory.module.annotation.AgentModule;
import work.slhaf.partner.api.factory.module.pojo.MetaModule;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
/**
* 负责扫描<code>@Module</code>注解获取模块实例
@@ -10,14 +17,26 @@ import work.slhaf.partner.api.factory.entity.AgentRegisterContext;
public class ModuleRegisterFactory extends AgentBaseFactory {
private Reflections reflections;
private List<MetaModule> moduleList;
@Override
protected void setVariables(AgentRegisterContext context) {
reflections = context.getReflections();
moduleList = context.getModuleList();
}
@Override
protected void run() {
//反射扫描获取InteractionModule所在类与hook注解所在方法
//反射扫描获取@AgentModule所在类, 该部分为Agent流程执行模块
Set<Class<?>> modules = reflections.getTypesAnnotatedWith(AgentModule.class);
for (Class<?> module : modules) {
AgentModule agentModule = module.getAnnotation(AgentModule.class);
MetaModule metaModule = new MetaModule();
metaModule.setName(agentModule.name());
metaModule.setOrder(agentModule.order());
metaModule.setClazz(module);
moduleList.add(metaModule);
}
moduleList.sort(Comparator.comparing(MetaModule::getOrder));
}
}

View File

@@ -6,6 +6,13 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 仅适用于以下类中的方法:
* 1. <code>@AgentModule</code>注解所在类
* 2. <code>ActivateModel</code>子类
* 3. <code>AgentInteractionModule</code>或者<code>AgentInteractionSubModule</code>子类
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface After {

View File

@@ -1,14 +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;
import java.lang.annotation.*;
/**
* 用于注解执行模块
*/
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AgentModule {

View File

@@ -5,6 +5,12 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 仅适用于以下类中的方法:
* 1. <code>@AgentModule</code>注解所在类
* 2. <code>ActivateModel</code>子类
* 3. <code>AgentInteractionModule</code>或者<code>AgentInteractionSubModule</code>子类
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {

View File

@@ -0,0 +1,11 @@
package work.slhaf.partner.api.factory.module.exception;
public class ModuleCheckException extends ModuleFactoryFailedException{
public ModuleCheckException(String message) {
super(message);
}
public ModuleCheckException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,11 @@
package work.slhaf.partner.api.factory.module.exception;
public class ModuleFactoryFailedException extends RuntimeException {
public ModuleFactoryFailedException(String message) {
super("ModuleFactory 执行失败: "+message);
}
public ModuleFactoryFailedException(String message, Throwable cause) {
super("ModuleFactory 执行失败: "+message, cause);
}
}

View File

@@ -0,0 +1,11 @@
package work.slhaf.partner.api.factory.module.pojo;
import lombok.Data;
@Data
public class MetaModule {
private String name;
private int order;
private Class<?> clazz;
private Object instance;
}

View File

@@ -18,16 +18,16 @@ public interface ActivateModel {
default void modelSettings() {
Model model = new Model();
ModelConfig modelConfig = ModelConfigManager.INSTANCE.loadModelConfig(modelKey());
model.setBaseMessages(withBasicPrompt() ? loadSpecificPromptAndBasicPrompt(modelKey(), promptModule()) : loadSpecificPrompt(modelKey(), promptModule()));
model.setBaseMessages(withBasicPrompt() ? loadSpecificPromptAndBasicPrompt(modelKey()) : loadSpecificPrompt(modelKey()));
model.setChatClient(new ChatClient(modelConfig.getBaseUrl(), modelConfig.getApikey(), modelConfig.getModel()));
}
private List<Message> loadSpecificPrompt(String modelKey, String specificModule) {
private List<Message> loadSpecificPrompt(String modelKey) {
return null;
}
private List<Message> loadSpecificPromptAndBasicPrompt(String modelKey, String specificModule) {
private List<Message> loadSpecificPromptAndBasicPrompt(String modelKey) {
return null;
}
@@ -82,5 +82,4 @@ public interface ActivateModel {
boolean withBasicPrompt();
String promptModule();
}