diff --git a/README.md b/README.md
index ea3c947..9ab3963 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Spring AI Demo
-> 🤖 一个简洁优雅的 Spring AI 对话演示项目,基于 Spring Boot 3.2.0 与 Spring AI 1.0.0-M3 构建,支持流式响应与 Markdown 渲染。
+> 🤖 一个简洁优雅的 Spring AI 对话演示项目,基于 Spring Boot 3.2.0 与 Spring AI 1.0.0-M3 构建,支持流式响应、Markdown 渲染与 RAG 文档问答。
[](https://spring.io/projects/spring-boot)
[](https://spring.io/projects/spring-ai)
@@ -13,7 +13,9 @@
- 📡 **流式响应** - 实时逐字输出,体验流畅
- 📝 **Markdown 支持** - 完整渲染代码块、表格、列表等格式
- 👨💻 **代码高亮** - Highlight.js 自动语言检测与语法着色
-- 🖼️ **向量数据库** - Milvus 集成(预留 RAG 扩展能力)
+- 📚 **RAG 文档问答** - 文档向量化存储,智能语义检索
+- 🗃️ **向量数据库** - Milvus 分布式向量数据库集成
+- 🔍 **Embedding 服务** - SiliconFlow BAAI/bge-large-zh-v1.5
- 📔 **PDF 解析** - Apache PDFBox 文档处理支持
- 🎨 **精美界面** - 深色主题响应式设计
@@ -24,6 +26,7 @@
- JDK 17+
- Maven 3.8+
- [Ollama](https://ollama.ai/) 本地服务
+- Milvus 向量数据库(可选,用于 RAG)
### 1. 安装 Ollama
@@ -58,8 +61,9 @@ mvn spring-boot:run
| 基础框架 | Spring Boot | 3.2.0 |
| AI 框架 | Spring AI | 1.0.0-M3 |
| AI 模型 | OpenAI/Ollama | - |
+| Embedding | SiliconFlow BAAI/bge-large-zh-v1.5 | - |
+| 向量数据库 | Milvus | 2.3.4 |
| 响应式编程 | Spring WebFlux | 3.2.0 |
-| 向量数据库 | Milvus Store | 1.0.0-M3 |
| 文档处理 | Apache PDFBox | 2.0.29 |
### 前端
@@ -77,22 +81,29 @@ springAiDemo/
├── src/
│ └── main/
│ ├── java/com/demo/
-│ │ ├── MyApplication.java # Spring Boot 启动入口
-│ │ └── controller/
-│ │ └── ChatController.java # AI 聊天 REST API
+│ │ ├── MyApplication.java # Spring Boot 启动入口
+│ │ ├── config/
+│ │ │ └── RagConfig.java # RAG 文本分割配置
+│ │ ├── controller/
+│ │ │ ├── ChatController.java # AI 聊天 REST API
+│ │ │ └── DocumentController.java # 文档导入 REST API
+│ │ └── service/
+│ │ └── DocumentService.java # 文档处理服务
│ └── resources/
-│ ├── application.yaml # 应用配置
-│ └── static/ # 前端静态资源
-│ ├── index.html # 主页面
-│ ├── css/style.css # 深色主题样式
-│ └── js/app.js # 流式响应逻辑
+│ ├── application.yaml # 应用配置
+│ └── static/ # 前端静态资源
+│ ├── index.html # 主页面
+│ ├── css/style.css # 深色主题样式
+│ └── js/app.js # 流式响应逻辑
+├── data/
+│ └── doris_intro.md # RAG 示例文档
├── pom.xml
└── README.md
```
## 💬 API 文档
-### 端点列表
+### 聊天接口
| 方法 | 端点 | 描述 | 参数 |
|:---|:---|:---|:---|
@@ -100,6 +111,12 @@ springAiDemo/
| GET | `/api/chat/ai` | 同步 AI 对话 | `msg` |
| GET | `/api/chat/ai/stream` | 流式 AI 对话 | `msg` |
+### 文档接口
+
+| 方法 | 端点 | 描述 | 参数 |
+|:---|:---|:---|:---|
+| GET | `/api/documents/import` | 导入文档到向量库 | - |
+
### 请求示例
**同步对话:**
@@ -112,10 +129,36 @@ curl "http://localhost:8080/api/chat/ai?msg=什么是Spring AI"
curl "http://localhost:8080/api/chat/ai/stream?msg=讲一个故事"
```
-### 响应格式
+**导入 RAG 文档:**
+```bash
+curl http://localhost:8080/api/documents/import
+```
-- **同步**: `text/plain` - 完整回复文本
-- **流式**: `text/html;charset=UTF-8` - Server-Sent Events 分块传输
+## 📚 RAG 文档问答
+
+### 功能说明
+
+将本地文档导入 Milvus 向量数据库,实现基于语义理解的智能问答。
+
+### 使用步骤
+
+1. 将 `.md` 或 `.txt` 文档放入 `data/` 目录
+2. 启动应用
+3. 调用导入接口:
+ ```bash
+ curl http://localhost:8080/api/documents/import
+ ```
+4. 通过聊天界面提问相关问题
+
+### 文档处理配置
+
+```yaml
+document:
+ data-path: data # 文档目录
+ chunk-size: 400 # 分割块大小 (tokens)
+ min-chunk-size: 200 # 最小块大小
+ max-num-chunk: 10000 # 最大块数量
+```
## 🎨 界面预览
@@ -133,7 +176,7 @@ curl "http://localhost:8080/api/chat/ai/stream?msg=讲一个故事"
## 🛠️ 配置说明
-### 模型配置 (`application.yaml`)
+### AI 对话配置
```yaml
spring:
@@ -142,61 +185,87 @@ spring:
base-url: http://localhost:11434 # Ollama 服务地址
chat:
options:
- model: gpt-oss:120b-cloud # 当前模型
- temperature: 0.7 # 生成温度
+ model: gpt-oss:120b-cloud # 对话模型
+ temperature: 0.7
```
-**支持模型**(需在 Ollama 中预先拉取):
-- `gpt-oss:120b-cloud` - 默认模型
-- `kimi-k2.5:cloud` - Kimi 模型
-- `gemma4:e2b` - Google Gemma 模型
+### Embedding 配置
+
+```yaml
+spring:
+ ai:
+ openai:
+ embedding:
+ api-key: your-siliconflow-api-key
+ base-url: https://api.siliconflow.cn
+ model: BAAI/bge-large-zh-v1.5
+ dimensions: 1024
+ enabled: true
+```
+
+### 向量数据库配置
+
+```yaml
+spring:
+ ai:
+ vectorstore:
+ milvus:
+ host: 192.168.50.103
+ port: 19530
+ database-name: doris_docs
+ collection-name: vector_store
+ embedding-dimension: 1024
+ index-type: IVF_FLAT
+ metric-type: COSINE
+```
## 🎬 架构图
```
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
-│ Client │────▶│ ChatController │────▶│ ChatClient │
+│ Client │────▶│ ChatController │────▶│ ChatClient │
│ (HTTP) │ │ (Spring MVC) │ │ (Spring AI) │
└─────────────┘ └──────────────────┘ └────────┬────────┘
- │
- ▼
- ┌─────────────────┐
- │ Ollama/OpenAI │
- │ (LLM Provider) │
- └─────────────────┘
+ │
+ ┌──────────────────┐ │
+ │ DocumentService │ │
+ │ (文档处理) │ ▼
+ └────────┬─────────┘ ┌─────────────────┐
+ │ │ Ollama/OpenAI │
+ ▼ │ (LLM Provider) │
+ ┌──────────────────┐ └─────────────────┘
+ │ Milvus Store │
+ │ (向量存储) │
+ └──────────────────┘
```
## 📄 预留功能
以下依赖已集成,可按需启用:
-- 🔍 **Milvus 向量数据库** - RAG 语义搜索
-- 📔 **PDF 文档解析** - 文档上传与处理
-- 🗨️ **Embedding 服务** - 文本向量化
+- 📔 **PDF 文档解析** - Apache PDFBox 文档处理
## 👷 已知限制
1. 暂无对话历史持久化
2. 暂未实现请求频率限制
-3. JUnit 版本较旧(3.8.1),建议升级至 JUnit 5
+3. Milvus 连接信息硬编码在配置中
-## 📖 待办事项
+## 📖 更新日志
-- [ ] 添加全局异常处理
-- [ ] 实现 RAG 文档问答
-- [ ] 对话历史存储
-- [ ] 动态模型切换 API
-- [ ] Docker 部署支持
-- [ ] 单元测试完善
-
-## 📤 更新日志
-
-### v1.0.0 (2026-04-17)
+### v1.0.0 (2026-04-19)
- 🎉 初始版本发布
- 支持流式 AI 对话
- Markdown 渲染与代码高亮
- 深色主题 Web 界面
+### v1.1.0 (2026-04-19)
+- ✨ 新增 RAG 文档问答功能
+- ✨ 新增 DocumentController 文档导入 API
+- ✨ 新增 DocumentService 文档处理服务
+- ✨ 配置 SiliconFlow Embedding 服务
+- ✨ 集成 Milvus 向量数据库
+
## 📗 License
MIT © 2026 Spring AI Demo
diff --git a/data/doris_intro.md b/data/doris_intro.md
new file mode 100644
index 0000000..0a1cd3f
--- /dev/null
+++ b/data/doris_intro.md
@@ -0,0 +1,98 @@
+# Apache Doris 简介
+
+Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库,以极速易用的特点被人们所熟知,仅需亚秒级响应时间即可返回海量数据下的查询结果,不仅可以支持高并发的点查询场景,也能支持高吞吐的复杂分析场景。
+
+## 核心特性
+
+### 1. 极速易用
+
+Apache Doris 采用了列式存储引擎,通过向量化执行引擎和查询优化器,能够在海量数据下实现亚秒级的查询响应。同时,Doris 提供了标准 SQL 支持,用户无需学习新的查询语言,即可快速上手。
+
+### 2. 高可用性
+
+Doris 采用了多副本机制,数据自动备份,节点故障时可以自动切换,保证服务的高可用性。支持在线扩缩容,不影响业务运行。
+
+### 3. 实时分析
+
+Doris 支持实时数据导入和查询,数据导入后即可被查询,无需等待数据预处理。支持多种数据导入方式,包括 Stream Load、Broker Load、Routine Load 等。
+
+### 4. 联邦查询
+
+Doris 支持通过外部表的方式查询其他数据源,如 MySQL、Hive、Iceberg、Hudi 等,实现数据联邦查询,无需数据搬迁。
+
+## 技术架构
+
+Apache Doris 采用 MySQL 协议,高度兼容 MySQL 语法,支持标准 SQL。整体架构分为两层:
+
+### Frontend (FE)
+
+FE 主要负责:
+- 接收和解析用户请求
+- 查询规划和优化
+- 元数据管理
+- 集群管理
+
+FE 节点分为 Follower 和 Observer 两种角色,Follower 参与选举和写入,Observer 只用于扩展查询能力。
+
+### Backend (BE)
+
+BE 主要负责:
+- 数据存储
+- 查询执行
+- 数据导入
+
+BE 节点负责数据的存储和计算,采用列式存储和向量化执行引擎,提供高性能的查询能力。
+
+## 使用场景
+
+### 1. 报表分析
+
+适用于各类报表场景,支持复杂的多维分析,亚秒级响应,支持高并发查询。
+
+### 2. 即席查询
+
+支持灵活的即席查询,用户可以快速探索数据,发现业务洞察。
+
+### 3. 实时数仓
+
+构建实时数据仓库,支持实时数据导入和查询,满足实时分析需求。
+
+### 4. 日志分析
+
+适用于日志分析场景,支持海量日志数据的快速检索和分析。
+
+## 数据导入
+
+Apache Doris 支持多种数据导入方式:
+
+### Stream Load
+
+通过 HTTP 协议导入数据,适用于实时数据导入场景。支持 CSV、JSON 等格式。
+
+```bash
+curl --location-trusted -u user:passwd \
+ -H "column_separator:," \
+ -T data.csv \
+ http://fe_host:http_port/api/database/table/_stream_load
+```
+
+### Broker Load
+
+通过 Broker 进程访问外部存储系统(如 HDFS、S3)导入数据,适用于大批量数据导入。
+
+### Routine Load
+
+从 Kafka 等消息队列持续消费数据并导入,适用于实时数据流场景。
+
+## 查询优化
+
+Apache Doris 提供了多种查询优化手段:
+
+1. **智能索引**:支持前缀索引、Bitmap 索引、Bloom Filter 索引等
+2. **分区分桶**:通过合理的分区分桶策略,减少数据扫描量
+3. **物化视图**:预计算常用查询结果,加速查询响应
+4. **查询缓存**:缓存查询结果,避免重复计算
+
+## 总结
+
+Apache Doris 是一款高性能、易用的分析型数据库,适用于多种实时分析场景。通过 MPP 架构、列式存储、向量化执行等技术,提供亚秒级的查询响应能力。
diff --git a/pom.xml b/pom.xml
index 2af0b0b..5f8448b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,12 +48,12 @@
spring-ai-milvus-store-spring-boot-starter
-
-
-
-
-
-
+
+
+ io.milvus
+ milvus-sdk-java
+ 2.3.4
+
diff --git a/src/main/java/com/demo/config/RagConfig.java b/src/main/java/com/demo/config/RagConfig.java
new file mode 100644
index 0000000..c3a4228
--- /dev/null
+++ b/src/main/java/com/demo/config/RagConfig.java
@@ -0,0 +1,27 @@
+package com.demo.config;
+
+import org.springframework.ai.transformer.splitter.TokenTextSplitter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+@Configuration
+public class RagConfig {
+ @Value("${document.chunk-size}")
+ private int chunkSize;
+
+ @Value("${document.min-chunk-size}")
+ private int minChunkSize;
+
+ @Value("${document.max-num-chunk}")
+ private int maxNumChunk;
+ @Bean
+ public TokenTextSplitter tokenTextSplitter() {
+ return new TokenTextSplitter(
+ chunkSize,
+ minChunkSize,
+ 5,
+ maxNumChunk,
+ true
+ );
+ }
+}
diff --git a/src/main/java/com/demo/controller/DocumentController.java b/src/main/java/com/demo/controller/DocumentController.java
new file mode 100644
index 0000000..ea883e9
--- /dev/null
+++ b/src/main/java/com/demo/controller/DocumentController.java
@@ -0,0 +1,31 @@
+package com.demo.controller;
+
+import com.demo.service.DocumentService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+
+
+@RestController
+@RequestMapping("api/documents")
+@RequiredArgsConstructor
+public class DocumentController {
+
+ @Autowired
+ public final DocumentService documentService;
+
+ @GetMapping("import")
+ public String importDocument() {
+ try {
+ documentService.importDocument();
+ } catch (IOException e) {
+ return "not ok" + e.getMessage();
+ }
+ return "ok";
+ }
+}
+
diff --git a/src/main/java/com/demo/service/DocumentService.java b/src/main/java/com/demo/service/DocumentService.java
new file mode 100644
index 0000000..e2850bb
--- /dev/null
+++ b/src/main/java/com/demo/service/DocumentService.java
@@ -0,0 +1,82 @@
+package com.demo.service;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.reader.TextReader;
+import org.springframework.ai.transformer.splitter.TokenTextSplitter;
+import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+
+public class DocumentService {
+
+ @Value("${document.data-path}")
+ private String dataPath;
+
+ @Autowired
+ private final VectorStore vectorStore;
+
+ @Autowired
+ private final TokenTextSplitter textSplitter;
+
+ public void importDocument() throws IOException {
+ log.info("开始导入文档到向量数据库");
+
+ Path path = Paths.get(dataPath);
+ if (Files.notExists(path)) {
+ log.error("数据不存在:{}",dataPath);
+ throw new IOException("数据不存在:" + dataPath);
+ }
+ List allDocuments = new ArrayList<>();
+ // 读取文件
+
+ try (Stream paths = Files.walk(path)){
+ paths.filter(Files::isRegularFile)
+ .filter(p -> p.getFileName().toString().endsWith(".md")
+ || p.getFileName().toString().endsWith(".txt"))
+ .forEach(filePath -> {
+ try {
+ log.info("开始处理 {} 文件",filePath);
+ TextReader reader = new TextReader(new FileSystemResource(filePath.toFile()));
+ List documents = reader.get();
+
+ // 切割文件。生成documents
+ List splitDocuments = textSplitter.apply(documents);
+
+ // 添加文件元信息
+ splitDocuments.forEach(document -> {
+ document.getMetadata().put("source",filePath.getFileName().toString());
+ document.getMetadata().put("file_path",filePath.toString());
+ });
+ allDocuments.addAll(splitDocuments);
+
+ log.info("文件{}分割为{}个文档片段",filePath.getFileName().toString(),splitDocuments.size());
+ }catch (Exception e) {
+ log.error("处理失败",e);
+ e.printStackTrace();
+ }
+ });
+ }
+
+ if (allDocuments.size() > 0) {
+ log.info("开始往向量数据库中插入数据(通过embedding模型进行向量化),总共有{}个文档片源",allDocuments.size());
+ // 插入到向量数据库中
+ vectorStore.add(allDocuments);
+ }
+ }
+}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 249c1ff..35f1bf9 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -15,15 +15,28 @@ spring:
# model: gemma4:e2b
# max-tokens: 10000
temperature: 0.7
-# embedding:
-# api-key: ollama
-# base-url: http://localhost:11434
+ embedding:
+ api-key: key
+ base-url: https://api.siliconflow.cn
+ options:
+ model: BAAI/bge-large-zh-v1.5
+ dimensions: 1024
+ enable: true
+ vectorstore:
+ milvus:
+ client:
+ host: "192.168.50.103"
+ port: 19530
+ databaseName: "doris_docs"
+ collectionName: "vector_store"
+ embeddingDimension: 1024
+ indexType: IVF_FLAT
+ metricType: COSINE
+ # 增加这个配置,可以让springai帮我们创建vector_store
+ initialize-schema: true
-#spring:
-# ai:
-# ollama:
-# base-url: http://localhost:11434
-# chat:
-# options:
-# model: gemma4:e2b
-# temperature: 0.7
\ No newline at end of file
+document:
+ data-path: data
+ chunk-size: 400
+ min-chunk-size: 200
+ max-num-chunk: 10000
\ No newline at end of file