Initial commit: Fintec AI Framework with Agent, RAG, and MCP modules

This commit is contained in:
limqsh
2026-04-27 17:23:58 +08:00
parent a9a1441537
commit 69c5aacdc8
85 changed files with 7143 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
package com.ccb.fintec.mcp.server.autoconfigure.aspect;
import com.ccb.fintec.mcp.server.autoconfigure.metrics.McpToolMetrics;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;
/**
* MCP 工具监控切面
*
* 自动为所有带 @Tool 注解的方法添加监控指标
*
* @author fintec-framework
*/
@Aspect
@Component
public class McpToolMonitoringAspect {
private static final Logger logger = LoggerFactory.getLogger(McpToolMonitoringAspect.class);
private final McpToolMetrics metrics;
public McpToolMonitoringAspect(McpToolMetrics metrics) {
this.metrics = metrics;
}
/**
* 环绕通知:拦截所有带 @Tool 注解的方法
*/
@Around("@annotation(tool)")
public Object monitorToolExecution(ProceedingJoinPoint joinPoint, Tool tool) throws Throwable {
// 获取工具名称(使用方法名或自定义名称)
String toolName = tool.name().isEmpty()
? joinPoint.getSignature().getName()
: tool.name();
logger.debug("🔧 调用工具: {}", toolName);
// 开始计时
var timerSample = metrics.startTimer(toolName);
try {
// 执行方法
Object result = joinPoint.proceed();
// 记录成功
metrics.recordSuccess(toolName, timerSample);
logger.debug("✅ 工具调用成功: {}", toolName);
return result;
} catch (Throwable e) {
// 记录失败
metrics.recordFailure(toolName, timerSample, e);
logger.error("❌ 工具调用失败: {}", toolName, e);
throw e;
}
}
}

View File

@@ -0,0 +1,51 @@
package com.ccb.fintec.mcp.server.autoconfigure.config;
import com.ccb.fintec.core.properties.McpProperties;
import com.ccb.fintec.core.properties.ObservabilityProperties;
import com.ccb.fintec.core.properties.RateLimitProperties;
import com.ccb.fintec.core.properties.SafetyProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* AI 核心配置属性绑定类MCP 模块)
*/
@ConfigurationProperties(prefix = "fintec.ai")
public class AiCoreConfigProperties {
private McpProperties mcp = new McpProperties();
private SafetyProperties safety = new SafetyProperties();
private RateLimitProperties rateLimit = new RateLimitProperties();
private ObservabilityProperties observability = new ObservabilityProperties();
public McpProperties getMcp() {
return mcp;
}
public void setMcp(McpProperties mcp) {
this.mcp = mcp;
}
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 ObservabilityProperties getObservability() {
return observability;
}
public void setObservability(ObservabilityProperties observability) {
this.observability = observability;
}
}

View File

@@ -0,0 +1,76 @@
package com.ccb.fintec.mcp.server.autoconfigure.config;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* MCP Server 自动配置类
*
* 提供简化的 MCP Server 开发体验:
* 1. 自动扫描所有带 @Tool 注解方法的 Bean注册为 MCP 工具
* 2. 业务系统只需要在方法上加 @Tool无需任何配置
* 3. 支持 Spring AI 标准的 @Tool 注解
*
* 使用方式:
* <pre>{@code
* @Service
* public class MyTools {
* @Tool(description = "获取当前时间")
* public String getCurrentTime() {
* return LocalDateTime.now().toString();
* }
* }
* }</pre>
*/
@AutoConfiguration
@ConditionalOnClass(ToolCallbackProvider.class)
@EnableConfigurationProperties(AiCoreConfigProperties.class)
public class McpAutoConfiguration {
/**
* 自动扫描所有带 @Tool 注解方法的 Bean注册为 MCP 工具
* 业务系统只需要在方法上加 @Tool无需任何配置
*/
@Bean
@ConditionalOnMissingBean(ToolCallbackProvider.class)
public ToolCallbackProvider mcpToolCallbackProvider(ApplicationContext context) {
List<Object> toolBeans = Arrays.stream(context.getBeanDefinitionNames())
.map(name -> {
try {
return context.getBean(name);
} catch (Exception e) {
return null;
}
})
.filter(bean -> bean != null && hasToolAnnotation(bean.getClass()))
.collect(Collectors.toList());
if (toolBeans.isEmpty()) {
// 没有工具也能正常启动,不报错
return MethodToolCallbackProvider.builder()
.toolObjects()
.build();
}
return MethodToolCallbackProvider.builder()
.toolObjects(toolBeans.toArray())
.build();
}
private boolean hasToolAnnotation(Class<?> clazz) {
return Arrays.stream(clazz.getMethods())
.anyMatch(m -> m.isAnnotationPresent(Tool.class));
}
}

View File

@@ -0,0 +1,140 @@
package com.ccb.fintec.mcp.server.autoconfigure.metrics;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* MCP 工具监控指标
*
* 收集和管理 MCP 工具的调用指标,包括:
* - 调用次数
* - 调用耗时
* - 成功/失败次数
*
* @author fintec-framework
*/
@Component
public class McpToolMetrics {
private static final String METRIC_PREFIX = "mcp.tool";
private static final String CALLS_COUNTER = METRIC_PREFIX + ".calls";
private static final String SUCCESS_COUNTER = METRIC_PREFIX + ".success";
private static final String FAILURE_COUNTER = METRIC_PREFIX + ".failure";
private static final String EXECUTION_TIMER = METRIC_PREFIX + ".execution.time";
private final MeterRegistry meterRegistry;
private final Map<String, Counter> callsCounters;
private final Map<String, Counter> successCounters;
private final Map<String, Counter> failureCounters;
private final Map<String, Timer> executionTimers;
public McpToolMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.callsCounters = new ConcurrentHashMap<>();
this.successCounters = new ConcurrentHashMap<>();
this.failureCounters = new ConcurrentHashMap<>();
this.executionTimers = new ConcurrentHashMap<>();
}
/**
* 记录工具调用开始
*
* @param toolName 工具名称
* @return Timer.Sample 用于记录执行时间
*/
public Timer.Sample startTimer(String toolName) {
return Timer.start(meterRegistry);
}
/**
* 记录工具调用成功
*
* @param toolName 工具名称
* @param sample Timer.Sample
*/
public void recordSuccess(String toolName, Timer.Sample sample) {
// 增加调用次数
getOrCreateCallsCounter(toolName).increment();
// 增加成功次数
getOrCreateSuccessCounter(toolName).increment();
// 记录执行时间
if (sample != null) {
sample.stop(getOrCreateExecutionTimer(toolName));
}
}
/**
* 记录工具调用失败
*
* @param toolName 工具名称
* @param sample Timer.Sample
* @param error 异常信息
*/
public void recordFailure(String toolName, Timer.Sample sample, Throwable error) {
// 增加调用次数
getOrCreateCallsCounter(toolName).increment();
// 增加失败次数
getOrCreateFailureCounter(toolName).increment();
// 记录执行时间(即使失败也记录)
if (sample != null) {
sample.stop(getOrCreateExecutionTimer(toolName));
}
}
/**
* 获取或创建调用次数计数器
*/
private Counter getOrCreateCallsCounter(String toolName) {
return callsCounters.computeIfAbsent(toolName, name ->
Counter.builder(CALLS_COUNTER)
.tag("tool", name)
.description("MCP 工具调用次数")
.register(meterRegistry)
);
}
/**
* 获取或创建成功次数计数器
*/
private Counter getOrCreateSuccessCounter(String toolName) {
return successCounters.computeIfAbsent(toolName, name ->
Counter.builder(SUCCESS_COUNTER)
.tag("tool", name)
.description("MCP 工具调用成功次数")
.register(meterRegistry)
);
}
/**
* 获取或创建失败次数计数器
*/
private Counter getOrCreateFailureCounter(String toolName) {
return failureCounters.computeIfAbsent(toolName, name ->
Counter.builder(FAILURE_COUNTER)
.tag("tool", name)
.description("MCP 工具调用失败次数")
.register(meterRegistry)
);
}
/**
* 获取或创建执行时间计时器
*/
private Timer getOrCreateExecutionTimer(String toolName) {
return executionTimers.computeIfAbsent(toolName, name ->
Timer.builder(EXECUTION_TIMER)
.tag("tool", name)
.description("MCP 工具执行时间")
.register(meterRegistry)
);
}
}

View File

@@ -0,0 +1 @@
com.ccb.fintec.mcp.server.autoconfigure.config.McpAutoConfiguration