blockKeywords;
+
+ public SafetyAspect(AiCoreConfigProperties properties) {
+ this.blockKeywords = properties.getSafety().getBlockKeywords();
+ log.info("🔒 安全过滤已启用,拦截关键词: {}", blockKeywords);
+ }
+
+ /**
+ * 拦截 ChatModel 的 call 方法
+ */
+ @Around("execution(* org.springframework.ai.chat.model.ChatModel.call(..))")
+ public Object checkSafety(ProceedingJoinPoint joinPoint) throws Throwable {
+ // 1. 检查请求参数中的用户输入
+ Object[] args = joinPoint.getArgs();
+ for (Object arg : args) {
+ if (arg != null) {
+ String argStr = arg.toString();
+ if (containsSensitiveWord(argStr)) {
+ throw new AiException("SAFETY_VIOLATION", "❌ 请求包含敏感内容,已被拦截");
+ }
+ }
+ }
+
+ // 2. 执行 AI 调用
+ Object result = joinPoint.proceed();
+
+ // 3. 检查 AI 响应
+ if (result instanceof ChatResponse chatResponse) {
+ String responseText = chatResponse.getResult().getOutput().getText();
+ if (containsSensitiveWord(responseText)) {
+ log.warn("⚠️ AI 响应包含敏感内容,已拦截");
+ throw new AiException("SAFETY_VIOLATION", "❌ 响应包含敏感内容,已被拦截");
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * 检查文本是否包含敏感词
+ */
+ private boolean containsSensitiveWord(String text) {
+ if (text == null || text.isEmpty()) {
+ return false;
+ }
+
+ String lowerText = text.toLowerCase();
+ for (String keyword : blockKeywords) {
+ if (lowerText.contains(keyword.toLowerCase())) {
+ log.warn("🚫 检测到敏感词: {}", keyword);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/AgentAutoConfiguration.java b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/AgentAutoConfiguration.java
new file mode 100644
index 0000000..9c19900
--- /dev/null
+++ b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/AgentAutoConfiguration.java
@@ -0,0 +1,98 @@
+package com.ccb.fintec.agent.autoconfigure.config;
+
+import com.ccb.fintec.agent.autoconfigure.multimodal.MultimodalTemplate;
+import com.ccb.fintec.agent.autoconfigure.template.AgentTemplate;
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
+import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
+import org.springframework.ai.chat.memory.MessageWindowChatMemory;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.retry.support.RetryTemplate;
+
+/**
+ * Agent 自动配置类
+ *
+ * 提供简化的 Agent 开发体验:
+ * 1. 自动创建 ChatClient,内置对话记忆和日志 Advisor
+ * 2. 提供 AgentTemplate,封装常用调用模式
+ * 3. 默认使用内存记忆,业务系统可自行配置 JDBC 或其他持久化方案
+ */
+@AutoConfiguration
+@ConditionalOnClass(ChatModel.class)
+@EnableConfigurationProperties(AiCoreConfigProperties.class)
+public class AgentAutoConfiguration {
+
+ /**
+ * 默认对话记忆:内存实现
+ * 如果业务系统需要持久化记忆,可以声明自己的 ChatMemory Bean 覆盖此配置
+ *
+ * 示例:JDBC 持久化记忆
+ * {@code
+ * @Bean
+ * public ChatMemory jdbcChatMemory(DataSource dataSource) {
+ * return MessageWindowChatMemory.builder()
+ * .chatMemoryRepository(new JdbcChatMemoryRepository(dataSource))
+ * .maxMessages(20)
+ * .build();
+ * }
+ * }
+ */
+ @Bean
+ @ConditionalOnMissingBean(ChatMemory.class)
+ public ChatMemory chatMemory(AiCoreConfigProperties properties) {
+ return MessageWindowChatMemory.builder()
+ .chatMemoryRepository(new InMemoryChatMemoryRepository())
+ .maxMessages(properties.getAgent().getMemoryWindowSize())
+ .build();
+ }
+
+ /**
+ * 核心:ChatClient 统一构造
+ * 内置:记忆 Advisor + 日志 Advisor
+ * 安全过滤通过 SafetyAspect AOP 切面实现
+ * 业务系统可以通过声明自己的 ChatClient Bean 完全覆盖
+ */
+ @Bean
+ @ConditionalOnMissingBean(ChatClient.class)
+ public ChatClient chatClient(ChatModel chatModel,
+ ChatMemory chatMemory,
+ AiCoreConfigProperties properties) {
+ return ChatClient.builder(chatModel)
+ .defaultAdvisors(
+ // 对话记忆(自动管理上下文)
+ MessageChatMemoryAdvisor.builder(chatMemory).build(),
+ // 请求/响应日志,方便调试
+ new SimpleLoggerAdvisor()
+ )
+ .build();
+ }
+
+ /**
+ * AgentTemplate:业务系统的唯一入口
+ * 封装了所有常用 AI 调用模式,内置自动重试能力
+ */
+ @Bean
+ @ConditionalOnMissingBean(AgentTemplate.class)
+ public AgentTemplate agentTemplate(ChatClient chatClient,
+ ChatModel chatModel,
+ AiCoreConfigProperties properties,
+ RetryTemplate retryTemplate) {
+ return new AgentTemplate(chatClient, chatModel, properties, retryTemplate);
+ }
+
+ /**
+ * MultimodalTemplate:多模态能力(图片/文档理解)
+ */
+ @Bean
+ @ConditionalOnMissingBean(MultimodalTemplate.class)
+ public MultimodalTemplate multimodalTemplate(ChatClient chatClient) {
+ return new MultimodalTemplate(chatClient);
+ }
+}
diff --git a/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/AiCoreConfigProperties.java b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/AiCoreConfigProperties.java
new file mode 100644
index 0000000..ffe06be
--- /dev/null
+++ b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/AiCoreConfigProperties.java
@@ -0,0 +1,62 @@
+package com.ccb.fintec.agent.autoconfigure.config;
+
+import com.ccb.fintec.core.properties.AgentProperties;
+import com.ccb.fintec.core.properties.ObservabilityProperties;
+import com.ccb.fintec.core.properties.RateLimitProperties;
+import com.ccb.fintec.core.properties.RetryProperties;
+import com.ccb.fintec.core.properties.SafetyProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+/**
+ * AI 核心配置属性绑定类(Agent 模块)
+ */
+@ConfigurationProperties(prefix = "fintec.ai")
+public class AiCoreConfigProperties {
+
+ private AgentProperties agent = new AgentProperties();
+ private SafetyProperties safety = new SafetyProperties();
+ private RateLimitProperties rateLimit = new RateLimitProperties();
+ private RetryProperties retry = new RetryProperties();
+ private ObservabilityProperties observability = new ObservabilityProperties();
+
+ public AgentProperties getAgent() {
+ return agent;
+ }
+
+ public void setAgent(AgentProperties agent) {
+ this.agent = agent;
+ }
+
+ public SafetyProperties getSafety() {
+ return safety;
+ }
+
+ public void setSafety(SafetyProperties safety) {
+ this.safety = safety;
+ }
+
+ public RateLimitProperties getRateLimit() {
+ return rateLimit;
+ }
+
+ public void setRateLimit(RateLimitProperties rateLimit) {
+ this.rateLimit = rateLimit;
+ }
+
+ public RetryProperties getRetry() {
+ return retry;
+ }
+
+ public void setRetry(RetryProperties retry) {
+ this.retry = retry;
+ }
+
+ public ObservabilityProperties getObservability() {
+ return observability;
+ }
+
+ public void setObservability(ObservabilityProperties observability) {
+ this.observability = observability;
+ }
+}
diff --git a/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/RetryAutoConfiguration.java b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/RetryAutoConfiguration.java
new file mode 100644
index 0000000..178b5e7
--- /dev/null
+++ b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/config/RetryAutoConfiguration.java
@@ -0,0 +1,57 @@
+package com.ccb.fintec.agent.autoconfigure.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.retry.RetryCallback;
+import org.springframework.retry.RetryContext;
+import org.springframework.retry.RetryListener;
+import org.springframework.retry.support.RetryTemplate;
+
+/**
+ * Retry 重试配置
+ *
+ * 为 AI 调用提供自动重试能力,处理网络波动、临时故障等场景
+ */
+@Configuration
+@ConditionalOnProperty(prefix = "fintec.ai.retry", name = "enabled", havingValue = "true", matchIfMissing = false)
+public class RetryAutoConfiguration {
+
+ private static final Logger log = LoggerFactory.getLogger(RetryAutoConfiguration.class);
+
+ @Bean
+ public RetryTemplate retryTemplate(AiCoreConfigProperties properties) {
+ RetryTemplate template = RetryTemplate.builder()
+ .maxAttempts(properties.getRetry().getMaxAttempts())
+ .fixedBackoff(properties.getRetry().getBackoffMs())
+ .build();
+
+ // 注册监听器,记录重试日志
+ template.registerListener(new RetryListener() {
+ @Override
+ public void close(RetryContext context, RetryCallback callback, Throwable throwable) {
+ if (context.getRetryCount() > 0) {
+ log.info("AI 调用重试完成,总重试次数: {}, 最终结果: {}",
+ context.getRetryCount(),
+ throwable == null ? "成功" : "失败");
+ }
+ }
+
+ @Override
+ public void onError(RetryContext context, RetryCallback callback, Throwable throwable) {
+ log.warn("AI 调用第 {} 次重试,原因: {}",
+ context.getRetryCount(),
+ throwable.getMessage());
+ }
+
+ @Override
+ public boolean open(RetryContext context, RetryCallback callback) {
+ return true;
+ }
+ });
+
+ return template;
+ }
+}
diff --git a/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/config/GraphAutoConfiguration.java b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/config/GraphAutoConfiguration.java
new file mode 100644
index 0000000..c101167
--- /dev/null
+++ b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/config/GraphAutoConfiguration.java
@@ -0,0 +1,19 @@
+package com.ccb.fintec.agent.autoconfigure.graph.config;
+
+import com.ccb.fintec.agent.autoconfigure.graph.template.GraphTemplate;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * Graph 工作流自动配置
+ */
+@AutoConfiguration
+public class GraphAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public GraphTemplate graphTemplate() {
+ return new GraphTemplate();
+ }
+}
diff --git a/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/example/GraphWorkflowExample.java b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/example/GraphWorkflowExample.java
new file mode 100644
index 0000000..e733da2
--- /dev/null
+++ b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/example/GraphWorkflowExample.java
@@ -0,0 +1,131 @@
+package com.ccb.fintec.agent.autoconfigure.graph.example;
+
+import com.ccb.fintec.core.graph.Condition;
+import com.ccb.fintec.core.graph.Node;
+import com.ccb.fintec.agent.autoconfigure.graph.template.GraphTemplate;
+import com.ccb.fintec.agent.autoconfigure.graph.workflow.*;
+
+/**
+ * Graph 工作流使用示例
+ */
+public class GraphWorkflowExample {
+
+ public static void main(String[] args) {
+
+ // 示例1: 顺序工作流 - 数据处理流水线
+ sequentialExample();
+
+ // 示例2: 并行工作流 - 同时调用多个AI模型
+ parallelExample();
+
+ // 示例3: 路由工作流 - 根据问题类型选择不同处理逻辑
+ routingExample();
+
+ // 示例4: 循环工作流 - 迭代优化直到满足条件
+ loopExample();
+ }
+
+ /**
+ * 顺序工作流示例: 文本处理流水线
+ */
+ private static void sequentialExample() {
+ System.out.println("=== 顺序工作流示例 ===");
+
+ Node cleanNode = GraphTemplate.node("清理文本",
+ text -> text.trim().replaceAll("\\s+", " "));
+
+ Node translateNode = GraphTemplate.node("翻译",
+ text -> "[翻译] " + text);
+
+ Node summarizeNode = GraphTemplate.node("总结",
+ text -> "[总结] " + text.substring(0, Math.min(50, text.length())));
+
+ SequentialWorkflow workflow = GraphTemplate.sequential(
+ cleanNode, translateNode, summarizeNode
+ );
+
+ String result = workflow.execute(" 这是一段需要处理的 文本内容 ");
+ System.out.println("结果: " + result);
+ }
+
+ /**
+ * 并行工作流示例: 同时分析文本的多个维度
+ */
+ private static void parallelExample() {
+ System.out.println("\n=== 并行工作流示例 ===");
+
+ Node sentimentNode = GraphTemplate.node("情感分析",
+ text -> "情感: 积极");
+
+ Node keywordNode = GraphTemplate.node("关键词提取",
+ text -> "关键词: AI, Spring, 工作流");
+
+ Node categoryNode = GraphTemplate.node("分类",
+ text -> "分类: 技术文档");
+
+ ParallelWorkflow workflow = GraphTemplate.parallel(
+ sentimentNode, keywordNode, categoryNode
+ );
+
+ var results = workflow.execute("Spring AI 是一个强大的框架");
+ results.forEach(r -> System.out.println("结果: " + r));
+
+ workflow.shutdown();
+ }
+
+ /**
+ * 路由工作流示例: 根据问题类型选择处理逻辑
+ */
+ private static void routingExample() {
+ System.out.println("\n=== 路由工作流示例 ===");
+
+ Node techNode = GraphTemplate.node("技术问题处理",
+ q -> "技术回答: " + q);
+
+ Node businessNode = GraphTemplate.node("业务问题处理",
+ q -> "业务回答: " + q);
+
+ Node defaultNode = GraphTemplate.node("通用回答",
+ q -> "通用回答: " + q);
+
+ RoutingWorkflow workflow = GraphTemplate.routing()
+ .addBranch("技术", ctx -> ((String) ctx).contains("技术"), techNode)
+ .addBranch("业务", ctx -> ((String) ctx).contains("业务"), businessNode)
+ .setDefault(defaultNode);
+
+ String result1 = workflow.execute("这是一个技术问题");
+ System.out.println("结果1: " + result1);
+
+ String result2 = workflow.execute("这是一个业务问题");
+ System.out.println("结果2: " + result2);
+
+ String result3 = workflow.execute("这是一个普通问题");
+ System.out.println("结果3: " + result3);
+ }
+
+ /**
+ * 循环工作流示例: 迭代优化文本直到满意
+ */
+ private static void loopExample() {
+ System.out.println("\n=== 循环工作流示例 ===");
+
+ int[] iteration = {0};
+
+ Node optimizeNode = GraphTemplate.node("优化文本",
+ text -> {
+ iteration[0]++;
+ return text + " [优化版本" + iteration[0] + "]";
+ });
+
+ // 最多迭代3次,或者当文本长度超过50时停止
+ LoopWorkflow workflow = GraphTemplate.loop(
+ optimizeNode,
+ ctx -> ((String) ctx).length() < 50,
+ 3
+ );
+
+ String result = workflow.execute("初始文本");
+ System.out.println("结果: " + result);
+ System.out.println("迭代次数: " + iteration[0]);
+ }
+}
diff --git a/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/node/SimpleNode.java b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/node/SimpleNode.java
new file mode 100644
index 0000000..49b46b0
--- /dev/null
+++ b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/node/SimpleNode.java
@@ -0,0 +1,27 @@
+package com.ccb.fintec.agent.autoconfigure.graph.node;
+
+import java.util.function.Function;
+
+/**
+ * 简单节点实现
+ */
+public class SimpleNode implements com.ccb.fintec.core.graph.Node {
+
+ private final String name;
+ private final Function function;
+
+ public SimpleNode(String name, Function function) {
+ this.name = name;
+ this.function = function;
+ }
+
+ @Override
+ public O execute(I input) {
+ return function.apply(input);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+}
diff --git a/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/template/GraphTemplate.java b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/template/GraphTemplate.java
new file mode 100644
index 0000000..9fd9366
--- /dev/null
+++ b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/template/GraphTemplate.java
@@ -0,0 +1,54 @@
+package com.ccb.fintec.agent.autoconfigure.graph.template;
+
+import com.ccb.fintec.core.graph.Condition;
+import com.ccb.fintec.core.graph.Node;
+import com.ccb.fintec.agent.autoconfigure.graph.node.SimpleNode;
+import com.ccb.fintec.agent.autoconfigure.graph.workflow.*;
+
+import java.util.List;
+
+/**
+ * Graph 工作流模板 - 提供便捷的工作流创建方法
+ */
+public class GraphTemplate {
+
+ /**
+ * 创建顺序工作流
+ */
+ @SafeVarargs
+ public static SequentialWorkflow sequential(Node, ?>... nodes) {
+ return new SequentialWorkflow(nodes);
+ }
+
+ /**
+ * 创建并行工作流
+ */
+ @SafeVarargs
+ public static ParallelWorkflow parallel(Node, ?>... nodes) {
+ return new ParallelWorkflow(nodes);
+ }
+
+ /**
+ * 创建路由工作流
+ */
+ public static RoutingWorkflow routing() {
+ return new RoutingWorkflow();
+ }
+
+ /**
+ * 创建循环工作流
+ * @param node 要循环执行的节点
+ * @param condition 继续循环的条件
+ * @param maxIterations 最大迭代次数
+ */
+ public static LoopWorkflow loop(Node, ?> node, Condition condition, int maxIterations) {
+ return new LoopWorkflow(node, condition, maxIterations);
+ }
+
+ /**
+ * 创建简单节点
+ */
+ public static Node node(String name, java.util.function.Function function) {
+ return new SimpleNode<>(name, function);
+ }
+}
diff --git a/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/workflow/LoopWorkflow.java b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/workflow/LoopWorkflow.java
new file mode 100644
index 0000000..d152014
--- /dev/null
+++ b/fintec-framework-agent-spring-boot-autoconfigure/src/main/java/com/ccb/fintec/agent/autoconfigure/graph/workflow/LoopWorkflow.java
@@ -0,0 +1,45 @@
+package com.ccb.fintec.agent.autoconfigure.graph.workflow;
+
+import com.ccb.fintec.core.graph.Condition;
+import com.ccb.fintec.core.graph.Node;
+
+/**
+ * 循环工作流 - 根据条件重复执行节点
+ */
+public class LoopWorkflow {
+
+ private final Node, ?> node;
+ private final Condition continueCondition;
+ private final int maxIterations;
+
+ /**
+ * @param node 要循环执行的节点
+ * @param continueCondition 继续循环的条件(返回true则继续)
+ * @param maxIterations 最大迭代次数(防止无限循环)
+ */
+ public LoopWorkflow(Node, ?> node, Condition continueCondition, int maxIterations) {
+ this.node = node;
+ this.continueCondition = continueCondition;
+ this.maxIterations = maxIterations;
+ }
+
+ /**
+ * 执行循环工作流
+ */
+ @SuppressWarnings("unchecked")
+ public O execute(I input) {
+ Object result = input;
+ int iteration = 0;
+
+ while (iteration < maxIterations && continueCondition.evaluate(result)) {
+ result = ((Node