AI agent项目复盘(Day-1)+ 规则树模型
这是我第一次接触ai的项目,所以可能会有很多理解错误的点,大部分只是自己对ai的认识,所以如果有错误的地方,可以直接指出。
根据我对xfg这个项目的学习,首先是先通过API功能测试,使用 Spring AI、LangChain4J、Google ADK 框架对接 AI 服务,完成功能验证。先感受一下不同框架下的ai测试效果。

这里分了不同的包,进行不同场景下的测试。
agent包中,我们进行了Agent智能体编排,主要进行agent流程,执行逻辑的测试。
model包中,我们使用AI模型底层API层,通过调用大模型接口,是SDK原生能力的测试。
tool包下的mcp包中,进行能力的扩展,是对Agent调用工具,函数调用能力的测试。
感受完在不同条件下的ai测试后,我们正式开始搭建一个可以动态配置的Agent智能体。
我们定义使用工程 YML 文件方式,配置的通用的智能体配置表。允许用户在使用脚手架创建完成智能体后工程后,通过 YML 配置出自己需要的智能体,在结合业务场景做对应的衔接开发。

AiAgentAutoConfig类来加载YML配置。

public class AiAgentConfigTableVO {
/**
* 应用名称
*/
private String appName;
/**
* 智能体配置
*/
private Agent agent;
/**
* 智能体模块
*/
private Module module;
@Data
public static class Agent {
/**
* 智能体ID
*/
private String agentId;
/**
* 智能体名称
*/
private String agentName;
/**
* 智能体描述
*/
private String agentDesc;
}
@Data
public static class Module {
private AiApi aiApi;
private ChatModel chatModel;
private List<Agent> agents;
private List<AgentWorkflow> agentWorkflows;
private Runner runner;
@Data
public static class AiApi {
private String baseUrl;
private String apiKey;
private String completionsPath = "/v1/chat/completions";
private String embeddingsPath = "/v1/embeddings";
}
@Data
public static class ChatModel {
private String model;
private List<ToolMcp> toolMcpList;
private List<ToolSkills> toolSkillsList;
@Data
public static class ToolMcp {
private SSEServerParameters sse;
private StdioServerParameters stdio;
private LocalParameters local;
@Data
public static class SSEServerParameters {
private String name;
private String baseUri;
private String sseEndpoint;
private Integer requestTimeout = 3000;
}
@Data
public static class StdioServerParameters {
private String name;
private Integer requestTimeout = 3000;
private ServerParameters serverParameters;
@Data
public static class ServerParameters {
private String command;
private List<String> args;
private Map<String, String> env;
}
}
@Data
public static class LocalParameters{
private String name;
}
}
@Data
public static class ToolSkills{
/**
* 类型;directory(用户配置的,映射进来的)、resource(放到工程下的)
*/
private String type="directory";
/**
* 路径;
*/
private String path;
}
}
@Data
public static class Agent {
private String name;
private String instruction;
private String description;
private String outputKey;
}
@Data
public static class AgentWorkflow{
/**
* 类型;loop、parallel、sequential
*/
private String type;
private String name;
private List<String> subAgents;
private String description;
private Integer maxIterations = 3;
}
@Data
public static class Runner{
private String agentName;
private List<String> pluginNameList;
}
}
}
这两个类为配置表对象类,AiAgentConfigTableVO用来接收,存储,传递你在配置文件里所写的所有AI智能体配置。AiAgentAutoConfigProperties把 application.yml 配置文件里 ai.agent.config 开头的所有配置,自动映射成 Java 对象,是整个 AI Agent 自动装配体系的配置入口顶层类。
我们来梳理一下流程:
1. Spring Boot 启动初始化阶段
-
扫描并加载配置类
AiAgentAutoConfig被@Configuration标记,Spring 会在启动时扫描到它。- 同时
@EnableConfigurationProperties(AiAgentAutoConfigProperties.class)生效,告诉 Spring:把AiAgentAutoConfigProperties当成一个配置属性类,进行绑定。
-
绑定配置属性
- Spring 会读取
application.yml/application.properties中所有ai.agent.config.*开头的配置。 - 把配置值绑定到
AiAgentAutoConfigProperties的字段上:ai.agent.config.enabled→private boolean enabled(默认值 false)ai.agent.config.tables→private Map<String, AiAgentConfigTableVO> tables
- 绑定完成后,
AiAgentAutoConfigProperties会被注册为一个 Bean。
- Spring 会读取
-
依赖注入
- Spring 会把
AiAgentAutoConfigProperties和IArmoryService这两个 Bean,注入到AiAgentAutoConfig中被@Resource标记的字段里。 - 此时
AiAgentAutoConfig实例已经创建完成,但它的业务方法还没执行。
- Spring 会把
2. 应用就绪事件触发阶段(ApplicationReadyEvent)
Spring Boot 启动完成后,会发布 ApplicationReadyEvent 事件。因为 AiAgentAutoConfig 实现了 ApplicationListener<ApplicationReadyEvent>,所以它的 onApplicationEvent 方法会被触发。
事件触发后,开始装配agent节点,在装配agent所有节点中,通过引用xfg的扳手工程中的规则树(组合模式)来进行流转实现。
之后,定义了单一职责的 IArmoryService 装配服务接口,并通过工厂管理节点衔接服务,以及定义上下文对象。这个上下文对象,会在各个节点间记录数据并流转使用。
public void acceptArmoryAgents(List<AiAgentConfigTableVO> tables) throws Exception {
for (AiAgentConfigTableVO table : tables) {
StrategyHandler<ArmoryCommandEntity, DefaultArmoryFactory.DynamicContext, AiAgentRegisterVO> handler = defaultArmoryFactory.armoryStrategyHandler();
handler.apply(ArmoryCommandEntity.builder()
.aiAgentConfigTableVO(table).build(),
new DefaultArmoryFactory.DynamicContext());
}
}
接下来,我们介绍一下xfg扳手工程项目中的一个通用组件--规则树。
我们在做产品项目时,肯定会遇到某些场景,需要经过很多流程,这些流程牵扯着不同的业务属性,如:授信,账户,风控,配置,营销,推荐,之后又要到,不同的产品线上,如A产品功能,B产品功能。
那么,规则树的编排就很适合这样的场景使用。它的设计可以像组装乐高积木,改一个大楼一样,有非常多的延展,来满足我们的场景诉求。

-
左侧,是复杂的业务流程节点编排,可以通过【路由】,拿到 get 方法中的节点判断和处理,走到下一个节点。与责任链相比,单链路执行适合固定的流程,如一些规则过滤。而规则树则适合编排复杂业务流程。
-
右侧,是流程过滤提供的方法,包括了;多线程异步数据加载、业务受理、get负责路由执行时做的一些判断。而受理前、受理后、受理后异常,则在流程中进行穿插处理。
1.StrategyHandler 策略处理接口
StrategyHandler 是一个泛型接口,定义了策略处理的核心方法。它包含一个默认实现,当没有匹配的策略时使用默认处理。该接口的泛型参数包括请求参数类型(T)、动态上下文类型(D)和返回结果类型(R)。
/**
* @description 受理策略处理
* T 入参类型
* D 上下文参数
* R 返参类型
*/
public interface StrategyHandler<T, D, R> {
StrategyHandler DEFAULT = (T, D) -> null;
R apply(T requestParameter, D dynamicContext) throws Exception;
}
2.StrategyMapper 策略映射器
StrategyMapper 是一个泛型接口,用于根据请求参数和动态上下文获取待执行的策略处理器。它定义了一个方法,根据传入的参数返回对应的策略处理器。
/**
* @description 策略映射器
* T 入参类型
* D 上下文参数
* R 返参类型
*/
public interface StrategyMapper<T, D extends DynamicContext, R> {
/**
* 获取待执行策略
*
* @param requestParameter 入参
* @param dynamicContext 上下文
* @return 返参
* @throws Exception 异常
*/
StrategyHandler<T, D, R> get(T requestParameter, D dynamicContext) throws Exception;
}
3.AbstractStrategyRouter 策略路由抽象类
AbstractStrategyRouter 是一个抽象类,结合了策略处理器和策略映射器的功能,提供了灵活的策略路由机制。它实现了 StrategyMapper 和 StrategyHandler 接口,兼具映射和执行能力。该类还提供了一个默认策略处理器,当没有匹配的策略时使用默认处理。
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 策略路由抽象类
* @create 2024-12-14 13:25
*/
public abstract class AbstractStrategyRouter<T, D extends DynamicContext, R> implements StrategyMapper<T, D, R>, StrategyHandler<T, D, R> {
@Getter
@Setter
protected StrategyHandler<T, D, R> defaultStrategyHandler = StrategyHandler.DEFAULT;
public R router(T requestParameter, D dynamicContext) throws Exception {
StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);
if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);
return defaultStrategyHandler.apply(requestParameter, dynamicContext);
}
}
4 AbstractMultiThreadStrategyRouter 异步资源加载策略抽象类
AbstractMultiThreadStrategyRouter 扩展了基础策略路由,支持多线程异步数据加载和业务处理分离。它提供了异步数据加载框架,在执行业务逻辑前预先加载所需资源,从而提高系统性能和响应速度。
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 异步资源加载策略
* @create 2024-12-21 08:48
*/
public abstract class AbstractMultiThreadStrategyRouter<T, D extends DynamicContext, R> implements StrategyMapper<T, D, R>, StrategyHandler<T, D, R> {
@Getter
@Setter
protected StrategyHandler<T, D, R> defaultStrategyHandler = StrategyHandler.DEFAULT;
public R router(T requestParameter, D dynamicContext) throws Exception {
StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);
if (null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);
return defaultStrategyHandler.apply(requestParameter, dynamicContext);
}
@Override
public R apply(T requestParameter, D dynamicContext) throws Exception {
try {
// 前置处理
R applyBefore = applyBefore(requestParameter, dynamicContext);
if (null != applyBefore) return applyBefore;
// 异步加载数据
multiThread(requestParameter, dynamicContext);
// 业务流程受理
R apply = doApply(requestParameter, dynamicContext);
// 后置处理
applyAfter(requestParameter, dynamicContext, apply);
return apply;
} catch (Exception e) {
// 后置处理(异常)
applyAfterException(requestParameter, dynamicContext, e);
throw e;
}
}
/**
* 异步加载数据
*/
protected abstract void multiThread(T requestParameter, D dynamicContext) throws ExecutionException, InterruptedException, TimeoutException;
/**
* 业务流程受理
*/
protected abstract R doApply(T requestParameter, D dynamicContext) throws Exception;
protected R applyBefore(T requestParameter, D dynamicContext) {
return proceed(requestParameter, dynamicContext);
}
protected void applyAfter(T requestParameter, D dynamicContext, R r) {
}
protected void applyAfterException(T requestParameter, D dynamicContext, Exception e) {
}
}
在项目中我们使用在策略路由抽象类上进行优化后的AbstractMultiThreadStrategyRouter。
xfg的规则树模型设计的核心组件就是这些,我们只需要在项目中再加入一个默认工厂类(上文已提及),用它来负责创建和管理策略处理器,提供策略模式的统一入口。它维护策略执行的上下文信息,支持数据在不同策略节点间传递。
public class DefaultArmoryFactory {
@Resource
private RootNode rootNode;
@Resource
private ApplicationContext applicationContext;
public StrategyHandler<ArmoryCommandEntity,DynamicContext, AiAgentRegisterVO> armoryStrategyHandler(){
return rootNode;
}
public AiAgentRegisterVO getAiAgentRegisterVO(String agentId) {
return applicationContext.getBean(agentId, AiAgentRegisterVO.class);
}
/**
* 定义一个上下文对象,用于各个节点串联的时候,写入数据和使用数据
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class DynamicContext {
/**
* LLM API
*/
private OpenAiApi openAiApi;
/**
* LLM ChatModel
*/
private ChatModel chatModel;
/**
* 原子安全的递进步骤
*/
private Integer currentStepIndex=0;
/**
* 当前的智能体
*/
private AiAgentConfigTableVO.Module.AgentWorkflow currentAgentWorkflow;
/**
* 智能体配置组
*/
private Map<String, BaseAgent> agentGroup=new HashMap<>();
private Map<String, Object> dataObjects = new HashMap<>();
public <T> void setValue(String key, T value) {
dataObjects.put(key, value);
}
public <T> T getValue(String key) {
return (T) dataObjects.get(key);
}
public List<BaseAgent> queryAgentList(List<String> agentNames){
if(agentNames == null || agentNames.isEmpty() || agentGroup == null){
return Collections.emptyList();
}
List<BaseAgent> agents=new ArrayList<>();
for (String agentName : agentNames) {
BaseAgent agent = agentGroup.get(agentName);
if(agent != null){
agents.add(agent);
}
}
return agents;
}
}
}
然后在提供一个规则树抽象支持类,它继承了AbstractMultiThreadStrategyRouter,为具体业务实现提供了支持。
public abstract class AbstractArmorySupport extends AbstractMultiThreadStrategyRouter<ArmoryCommandEntity, DefaultArmoryFactory.DynamicContext, AiAgentRegisterVO> {
@Resource
protected ApplicationContext applicationContext;
@Override
protected void multiThread(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException {
}
/**
* 通用的Bean注册方法
*
* @param beanName Bean名称
* @param beanClass Bean类型
* @param <T> Bean类型
*/
protected synchronized <T> void registerBean(String beanName, Class<T> beanClass, T beanInstance) {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
// 注册Bean
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(beanClass, () -> beanInstance);
BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
// 如果Bean已存在,先移除
if (beanFactory.containsBeanDefinition(beanName)) {
beanFactory.removeBeanDefinition(beanName);
}
// 注册新的Bean
beanFactory.registerBeanDefinition(beanName, beanDefinition);
log.info("成功注册Bean: {}", beanName);
}
protected <T> T getBean(String beanName){
return (T) applicationContext.getBean(beanName);
}
}
通过规则树模型的设计,为之后的各个节点装配,流转,节点之间的通信和Bean对象的注册,获取Bean对象等等,提供了极大的便利。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)