Initial commit: Fintec AI Framework with Agent, RAG, and MCP modules
This commit is contained in:
54
rag-demo/pom.xml
Normal file
54
rag-demo/pom.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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>rag-demo</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RAG Starter -->
|
||||
<dependency>
|
||||
<groupId>com.ccb.fintec</groupId>
|
||||
<artifactId>fintec-framework-rag-spring-boot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Agent Starter (提供 ChatClient) -->
|
||||
<dependency>
|
||||
<groupId>com.ccb.fintec</groupId>
|
||||
<artifactId>fintec-framework-agent-spring-boot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ccb.fintec.rag.demo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class RagDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(RagDemoApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.ccb.fintec.rag.demo.controller;
|
||||
|
||||
import com.ccb.fintec.core.dto.AiResponse;
|
||||
import com.ccb.fintec.rag.demo.service.RagService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* RAG 演示控制器 - 展示封装后的简化用法
|
||||
*
|
||||
* 核心优势:
|
||||
* 1. 一行代码完成文档入库(支持 PDF、文本、Document 列表)
|
||||
* 2. 自动使用配置的检索参数(topK、相似度阈值)
|
||||
* 3. 支持 RAG + 会话记忆组合
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/rag")
|
||||
public class RagController {
|
||||
|
||||
private final RagService ragService;
|
||||
|
||||
public RagController(RagService ragService) {
|
||||
this.ragService = ragService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. 初始化示例数据
|
||||
*
|
||||
* 示例:POST /api/rag/init
|
||||
*/
|
||||
@PostMapping("/init")
|
||||
public Map<String, Object> init() {
|
||||
ragService.initSampleData();
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "示例数据已初始化");
|
||||
result.put("note", "使用了 fintec.ai.rag.* 配置的检索参数");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. 添加文本文档(最简单的方式)
|
||||
*
|
||||
* 示例:
|
||||
* POST /api/rag/add-text
|
||||
* Body: ["这是第一个文档", "这是第二个文档"]
|
||||
*/
|
||||
@PostMapping("/add-text")
|
||||
public Map<String, Object> addTextDocuments(@RequestBody List<String> texts) {
|
||||
ragService.addTextDocuments(texts);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "已添加 " + texts.size() + " 个文本文档");
|
||||
result.put("note", "自动分块、向量化,无需手动处理");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. RAG 问答 - 自动检索相关文档
|
||||
*
|
||||
* 示例:GET /api/rag/ask?question=什么是Spring AI?
|
||||
*/
|
||||
@GetMapping("/ask")
|
||||
public Map<String, Object> ask(@RequestParam String question) {
|
||||
// ✅ 自动使用配置的 topK 和相似度阈值
|
||||
String answer = ragService.ask(question);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("question", question);
|
||||
result.put("answer", answer);
|
||||
result.put("note", "自动从向量数据库检索相关文档,注入 prompt");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 4. RAG 问答 + 会话记忆
|
||||
*
|
||||
* 示例:
|
||||
* GET /api/rag/ask-with-memory?conversationId=user123&question=什么是RAG?
|
||||
* GET /api/rag/ask-with-memory?conversationId=user123&question=它有什么优势?
|
||||
*/
|
||||
@GetMapping("/ask-with-memory")
|
||||
public Map<String, Object> askWithMemory(
|
||||
@RequestParam String conversationId,
|
||||
@RequestParam String question) {
|
||||
// ✅ RAG + 记忆组合,多轮对话保持上下文
|
||||
String answer = ragService.askWithMemory(conversationId, question);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("conversationId", conversationId);
|
||||
result.put("question", question);
|
||||
result.put("answer", answer);
|
||||
result.put("note", "RAG 检索 + 会话记忆,多轮对话保持上下文");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 5. RAG 问答(带元数据)
|
||||
*
|
||||
* 示例:GET /api/rag/ask-with-metadata?question=什么是向量数据库?
|
||||
*/
|
||||
@GetMapping("/ask-with-metadata")
|
||||
public AiResponse askWithMetadata(@RequestParam String question) {
|
||||
// ✅ 自动收集 Token 使用量、耗时、检索文档数等元数据
|
||||
return ragService.askWithMetadata(question);
|
||||
}
|
||||
|
||||
/**
|
||||
* 6. 健康检查
|
||||
*/
|
||||
@GetMapping("/health")
|
||||
public Map<String, Object> health() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("status", "UP");
|
||||
result.put("service", "RAG Demo");
|
||||
result.put("features", new String[]{
|
||||
"文本文档入库(一行代码)",
|
||||
"PDF 文档入库(一行代码)",
|
||||
"Document 列表入库",
|
||||
"RAG 问答(自动检索)",
|
||||
"RAG + 会话记忆",
|
||||
"元数据收集(Token、耗时、检索文档数)",
|
||||
"可配置检索参数(topK、相似度阈值)"
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.ccb.fintec.rag.demo.service;
|
||||
|
||||
import com.ccb.fintec.core.dto.AiResponse;
|
||||
import com.ccb.fintec.rag.autoconfigure.template.RagTemplate;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* RAG 服务示例 - 展示封装后的简化用法
|
||||
*
|
||||
* 核心优势:
|
||||
* 1. 一行代码完成文档入库(PDF、文本、Document 列表)
|
||||
* 2. 自动使用配置的检索参数(topK、相似度阈值)
|
||||
* 3. 支持 RAG + 会话记忆组合
|
||||
* 4. 元数据包含检索文档数,便于监控
|
||||
*/
|
||||
@Service
|
||||
public class RagService {
|
||||
|
||||
private final RagTemplate ragTemplate;
|
||||
|
||||
public RagService(RagTemplate ragTemplate) {
|
||||
this.ragTemplate = ragTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 方式1:直接传入纯文本入库(最简单)
|
||||
*/
|
||||
public void addTextDocuments(List<String> texts) {
|
||||
for (String text : texts) {
|
||||
// ✅ 一行代码完成文本入库,自动分块、向量化
|
||||
ragTemplate.ingest(text, "sample-source");
|
||||
}
|
||||
System.out.println("已添加 " + texts.size() + " 个文本文档");
|
||||
}
|
||||
|
||||
/**
|
||||
* 方式2:传入 Document 列表入库(支持自定义元数据)
|
||||
*/
|
||||
public void addDocumentList(List<Document> documents) {
|
||||
// ✅ 一行代码完成 Document 列表入库,自动分块
|
||||
ragTemplate.ingest(documents);
|
||||
System.out.println("已添加 " + documents.size() + " 个 Document");
|
||||
}
|
||||
|
||||
/**
|
||||
* 方式3:PDF 文档入库(需要引入 spring-ai-pdf-document-reader 依赖)
|
||||
*
|
||||
* 示例:ragTemplate.ingest(new ClassPathResource("docs/manual.pdf"));
|
||||
*/
|
||||
public void addPdfDocument(org.springframework.core.io.Resource pdfResource) {
|
||||
// ✅ 一行代码完成 PDF 入库,自动读取、分块、向量化
|
||||
ragTemplate.ingest(pdfResource);
|
||||
System.out.println("已添加 PDF 文档");
|
||||
}
|
||||
|
||||
/**
|
||||
* RAG 问答 - 自动使用配置的 topK 和相似度阈值
|
||||
*/
|
||||
public String ask(String question) {
|
||||
// ✅ 自动从向量数据库检索相关文档,注入 prompt
|
||||
return ragTemplate.ask(question);
|
||||
}
|
||||
|
||||
/**
|
||||
* RAG 问答 + 会话记忆
|
||||
*/
|
||||
public String askWithMemory(String conversationId, String question) {
|
||||
// ✅ RAG + 记忆组合,多轮对话保持上下文
|
||||
return ragTemplate.ask(question, conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* RAG 问答(带元数据)
|
||||
*/
|
||||
public AiResponse askWithMetadata(String question) {
|
||||
// ✅ 自动收集 Token 使用量、耗时、检索文档数等元数据
|
||||
return ragTemplate.askWithMetadata(question);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化示例数据
|
||||
*/
|
||||
public void initSampleData() {
|
||||
List<String> sampleTexts = List.of(
|
||||
"Spring AI 是一个用于构建 AI 应用的框架,它提供了统一的 API 来访问不同的 AI 模型。",
|
||||
"RAG (Retrieval-Augmented Generation) 是一种结合信息检索和文本生成的技术,可以提高 AI 回答的准确性。",
|
||||
"向量数据库用于存储和检索高维向量数据,常见的向量数据库有 Chroma、Milvus、Pinecone 等。",
|
||||
"Ollama 是一个本地运行大语言模型的工具,支持 Llama、Mistral 等多种模型。",
|
||||
"OpenRouter 是一个提供多种 AI 模型访问的 API 服务平台,支持按量付费。"
|
||||
);
|
||||
|
||||
// ✅ 使用新的 ingest 方法,更简洁
|
||||
for (String text : sampleTexts) {
|
||||
ragTemplate.ingest(text, "sample-source");
|
||||
}
|
||||
System.out.println("已初始化 " + sampleTexts.size() + " 条示例数据");
|
||||
}
|
||||
}
|
||||
42
rag-demo/src/main/resources/application.properties
Normal file
42
rag-demo/src/main/resources/application.properties
Normal file
@@ -0,0 +1,42 @@
|
||||
# ============================================
|
||||
# 服务器配置
|
||||
# ============================================
|
||||
server.port=8082
|
||||
|
||||
# ============================================
|
||||
# 模型配置(使用 OpenRouter,与 agent-demo 一致)
|
||||
# ============================================
|
||||
spring.ai.openai.api-key=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
|
||||
spring.ai.openai.base-url=https://openrouter.ai/api
|
||||
spring.ai.openai.chat.options.model=openrouter/free
|
||||
# Embedding 模型也使用 OpenRouter(通过 OpenAI 兼容接口)
|
||||
spring.ai.openai.embedding.options.model=text-embedding-3-small
|
||||
|
||||
# ============================================
|
||||
# RAG 模块配置
|
||||
# ============================================
|
||||
# 文档分块大小(字符数):影响检索精度和上下文长度
|
||||
fintec.ai.rag.chunk-size=800
|
||||
# 分块重叠大小:保证上下文连贯性
|
||||
fintec.ai.rag.chunk-overlap=100
|
||||
# 检索返回的最相关文档数量
|
||||
fintec.ai.rag.top-k=5
|
||||
# 相似度阈值:低于此值的文档不会被使用
|
||||
fintec.ai.rag.similarity-threshold=0.7
|
||||
|
||||
# ============================================
|
||||
# 重试策略配置(可选)
|
||||
# ============================================
|
||||
# fintec.ai.retry.enabled=true
|
||||
# fintec.ai.retry.max-attempts=3
|
||||
# fintec.ai.retry.backoff-ms=1000
|
||||
|
||||
# ============================================
|
||||
# 日志配置
|
||||
# ============================================
|
||||
logging.level.root=INFO
|
||||
logging.level.com.ccb.fintec=DEBUG
|
||||
# 启用 Spring Boot 自动配置的调试信息
|
||||
debug=true
|
||||
# 启用更详细的日志
|
||||
logging.level.org.springframework.boot.autoconfigure=DEBUG
|
||||
Reference in New Issue
Block a user