Initial commit: Fintec AI Framework with Agent, RAG, and MCP modules
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.ccb.fintec.mcp.server.autoconfigure.config.McpAutoConfiguration
|
||||
Reference in New Issue
Block a user