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

BIN
fintec-framework-ai-core/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ccb.fintec</groupId>
<artifactId>fintec-framework-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>fintec-framework-ai-core</artifactId>
<dependencies>
<!-- 只依赖 spring-ai 核心,不绑定具体模型 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-commons</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,84 @@
package com.ccb.fintec.core.dto;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* AI 统一响应对象 - 封装所有 AI 调用的返回结果
*/
public class AiResponse {
private String requestId;
private Object content;
private TokenUsage tokenUsage;
private long duration;
private LocalDateTime timestamp;
private Map<String, Object> metadata;
private ErrorInfo error;
public static class TokenUsage {
private int promptTokens;
private int completionTokens;
private int totalTokens;
public TokenUsage() {}
public TokenUsage(int promptTokens, int completionTokens, int totalTokens) {
this.promptTokens = promptTokens;
this.completionTokens = completionTokens;
this.totalTokens = totalTokens;
}
// Getters and Setters
public int getPromptTokens() { return promptTokens; }
public void setPromptTokens(int promptTokens) { this.promptTokens = promptTokens; }
public int getCompletionTokens() { return completionTokens; }
public void setCompletionTokens(int completionTokens) { this.completionTokens = completionTokens; }
public int getTotalTokens() { return totalTokens; }
public void setTotalTokens(int totalTokens) { this.totalTokens = totalTokens; }
}
public static class ErrorInfo {
private String code;
private String message;
private String detail;
public ErrorInfo() {}
public ErrorInfo(String code, String message, String detail) {
this.code = code;
this.message = message;
this.detail = detail;
}
// Getters and Setters
public String getCode() { return code; }
public void setCode(String code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getDetail() { return detail; }
public void setDetail(String detail) { this.detail = detail; }
}
public AiResponse() {
this.timestamp = LocalDateTime.now();
this.metadata = new HashMap<>();
}
// Getters and Setters
public String getRequestId() { return requestId; }
public void setRequestId(String requestId) { this.requestId = requestId; }
public Object getContent() { return content; }
public void setContent(Object content) { this.content = content; }
public TokenUsage getTokenUsage() { return tokenUsage; }
public void setTokenUsage(TokenUsage tokenUsage) { this.tokenUsage = tokenUsage; }
public long getDuration() { return duration; }
public void setDuration(long duration) { this.duration = duration; }
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
public ErrorInfo getError() { return error; }
public void setError(ErrorInfo error) { this.error = error; }
}

View File

@@ -0,0 +1,42 @@
package com.ccb.fintec.core.exception;
/**
* AI 框架统一异常
*/
public class AiException extends RuntimeException {
private final String errorCode;
private final String requestId;
public AiException(String message) {
super(message);
this.errorCode = "AI_ERROR";
this.requestId = null;
}
public AiException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
this.requestId = null;
}
public AiException(String errorCode, String message, String requestId) {
super(message);
this.errorCode = errorCode;
this.requestId = requestId;
}
public AiException(String message, Throwable cause) {
super(message, cause);
this.errorCode = "AI_ERROR";
this.requestId = null;
}
public String getErrorCode() {
return errorCode;
}
public String getRequestId() {
return requestId;
}
}

View File

@@ -0,0 +1,16 @@
package com.ccb.fintec.core.graph;
/**
* 条件边 - 用于路由决策
*/
@FunctionalInterface
public interface Condition {
/**
* 判断条件是否满足
*
* @param context 上下文数据
* @return 是否满足条件
*/
boolean evaluate(Object context);
}

View File

@@ -0,0 +1,26 @@
package com.ccb.fintec.core.graph;
/**
* 工作流节点 - 封装 AI 调用或业务逻辑
*
* @param <I> 输入类型
* @param <O> 输出类型
*/
@FunctionalInterface
public interface Node<I, O> {
/**
* 执行节点逻辑
*
* @param input 输入数据
* @return 输出结果
*/
O execute(I input);
/**
* 获取节点名称(用于调试和日志)
*/
default String getName() {
return this.getClass().getSimpleName();
}
}

View File

@@ -0,0 +1,47 @@
package com.ccb.fintec.core.metrics;
/**
* AI 观测指标常量 - 统一定义所有模块的监控指标前缀和名称
*/
public final class MetricConstants {
// 指标前缀
public static final String METRIC_PREFIX = "fintec.ai";
// Chat 相关指标
public static final String CHAT_REQUEST_TOTAL = METRIC_PREFIX + ".chat.request.total";
public static final String CHAT_REQUEST_DURATION = METRIC_PREFIX + ".chat.request.duration";
public static final String CHAT_TOKEN_USAGE = METRIC_PREFIX + ".chat.token.usage";
public static final String CHAT_ERROR_TOTAL = METRIC_PREFIX + ".chat.error.total";
// RAG 相关指标
public static final String RAG_REQUEST_TOTAL = METRIC_PREFIX + ".rag.request.total";
public static final String RAG_REQUEST_DURATION = METRIC_PREFIX + ".rag.request.duration";
public static final String RAG_RETRIEVAL_COUNT = METRIC_PREFIX + ".rag.retrieval.count";
public static final String RAG_EMBEDDING_DURATION = METRIC_PREFIX + ".rag.embedding.duration";
// MCP 相关指标
public static final String MCP_TOOL_CALL_TOTAL = METRIC_PREFIX + ".mcp.tool.call.total";
public static final String MCP_TOOL_CALL_DURATION = METRIC_PREFIX + ".mcp.tool.call.duration";
public static final String MCP_TOOL_ERROR_TOTAL = METRIC_PREFIX + ".mcp.tool.error.total";
// Graph 工作流指标
public static final String GRAPH_WORKFLOW_TOTAL = METRIC_PREFIX + ".graph.workflow.total";
public static final String GRAPH_WORKFLOW_DURATION = METRIC_PREFIX + ".graph.workflow.duration";
public static final String GRAPH_NODE_EXECUTION_TOTAL = METRIC_PREFIX + ".graph.node.execution.total";
// 安全相关指标
public static final String SECURITY_BLOCK_TOTAL = METRIC_PREFIX + ".security.block.total";
public static final String SECURITY_SCAN_DURATION = METRIC_PREFIX + ".security.scan.duration";
// 限流相关指标
public static final String RATE_LIMIT_REJECT_TOTAL = METRIC_PREFIX + ".rate.limit.reject.total";
// 重试相关指标
public static final String RETRY_TOTAL = METRIC_PREFIX + ".retry.total";
public static final String RETRY_SUCCESS_TOTAL = METRIC_PREFIX + ".retry.success.total";
private MetricConstants() {
// 防止实例化
}
}

View File

@@ -0,0 +1,37 @@
package com.ccb.fintec.core.properties;
/**
* Agent 相关配置属性
*/
public class AgentProperties {
/** 默认系统提示词,统一公司 AI 风格 */
private String defaultSystemPrompt = "你是一个专业的 AI 助手,请用中文简洁准确地回答问题。";
/** 对话记忆保留的最大消息条数 */
private int memoryWindowSize = 20;
/** 流式输出超时(秒) */
private int streamTimeoutSeconds = 60;
public String getDefaultSystemPrompt() {
return defaultSystemPrompt;
}
public void setDefaultSystemPrompt(String defaultSystemPrompt) {
this.defaultSystemPrompt = defaultSystemPrompt;
}
public int getMemoryWindowSize() {
return memoryWindowSize;
}
public void setMemoryWindowSize(int memoryWindowSize) {
this.memoryWindowSize = memoryWindowSize;
}
public int getStreamTimeoutSeconds() {
return streamTimeoutSeconds;
}
public void setStreamTimeoutSeconds(int streamTimeoutSeconds) {
this.streamTimeoutSeconds = streamTimeoutSeconds;
}
}

View File

@@ -0,0 +1,27 @@
package com.ccb.fintec.core.properties;
/**
* MCP 相关配置属性
*/
public class McpProperties {
/** MCP Server 名称 */
private String serverName = "fintec-mcp-server";
/** MCP Server 版本 */
private String serverVersion = "1.0.0";
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public String getServerVersion() {
return serverVersion;
}
public void setServerVersion(String serverVersion) {
this.serverVersion = serverVersion;
}
}

View File

@@ -0,0 +1,25 @@
package com.ccb.fintec.core.properties;
/**
* 观测配置属性
*/
public class ObservabilityProperties {
private boolean logTokenUsage = true;
private long slowRequestThresholdMs = 3000;
public boolean isLogTokenUsage() {
return logTokenUsage;
}
public void setLogTokenUsage(boolean logTokenUsage) {
this.logTokenUsage = logTokenUsage;
}
public long getSlowRequestThresholdMs() {
return slowRequestThresholdMs;
}
public void setSlowRequestThresholdMs(long slowRequestThresholdMs) {
this.slowRequestThresholdMs = slowRequestThresholdMs;
}
}

View File

@@ -0,0 +1,47 @@
package com.ccb.fintec.core.properties;
/**
* RAG 相关配置属性
*/
public class RagProperties {
/** 文档分块大小(字符数) */
private int chunkSize = 800;
/** 分块重叠大小 */
private int chunkOverlap = 100;
/** RAG 检索返回的最相关文档数量 */
private int topK = 5;
/** RAG 相似度阈值,低于此值不使用 */
private double similarityThreshold = 0.7;
public int getChunkSize() {
return chunkSize;
}
public void setChunkSize(int chunkSize) {
this.chunkSize = chunkSize;
}
public int getChunkOverlap() {
return chunkOverlap;
}
public void setChunkOverlap(int chunkOverlap) {
this.chunkOverlap = chunkOverlap;
}
public int getTopK() {
return topK;
}
public void setTopK(int topK) {
this.topK = topK;
}
public double getSimilarityThreshold() {
return similarityThreshold;
}
public void setSimilarityThreshold(double similarityThreshold) {
this.similarityThreshold = similarityThreshold;
}
}

View File

@@ -0,0 +1,25 @@
package com.ccb.fintec.core.properties;
/**
* 限流配置属性
*/
public class RateLimitProperties {
private int maxRequestsPerMinute = 60;
private int maxTokensPerRequest = 4096;
public int getMaxRequestsPerMinute() {
return maxRequestsPerMinute;
}
public void setMaxRequestsPerMinute(int maxRequestsPerMinute) {
this.maxRequestsPerMinute = maxRequestsPerMinute;
}
public int getMaxTokensPerRequest() {
return maxTokensPerRequest;
}
public void setMaxTokensPerRequest(int maxTokensPerRequest) {
this.maxTokensPerRequest = maxTokensPerRequest;
}
}

View File

@@ -0,0 +1,34 @@
package com.ccb.fintec.core.properties;
/**
* 重试策略配置属性
*/
public class RetryProperties {
private boolean enabled = true;
private int maxAttempts = 3;
private long backoffMs = 1000;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public int getMaxAttempts() {
return maxAttempts;
}
public void setMaxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
}
public long getBackoffMs() {
return backoffMs;
}
public void setBackoffMs(long backoffMs) {
this.backoffMs = backoffMs;
}
}

View File

@@ -0,0 +1,28 @@
package com.ccb.fintec.core.properties;
import java.util.ArrayList;
import java.util.List;
/**
* 安全防护配置属性
*/
public class SafetyProperties {
private boolean enabled = true;
private List<String> blockKeywords = new ArrayList<>();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<String> getBlockKeywords() {
return blockKeywords;
}
public void setBlockKeywords(List<String> blockKeywords) {
this.blockKeywords = blockKeywords;
}
}