Spring AI 手搓 RAG:文档上传 → 向量化 → 检索增强,全链路 Java 实现

上篇写了 Spring AI 多模型切换,这篇补上后半段------RAG 知识库 。不用 Python、不用 LangChain、不用 Milvus,纯 Spring AI + Java 一把梭。 代码已更新 👉 spring-ai-qa-service

整体流程

css 复制代码
上传 PDF/MD/TXT
    ↓
PagePdfDocumentReader / 纯文本读取
    ↓
TokenTextSplitter 分块(按 Token 数切分)
    ↓
DashScopeEmbeddingModel 向量化
    ↓
SimpleVectorStore 存储(本地 JSON 文件)
    ↓
用户提问 → QuestionAnswerAdvisor 检索 Top-K → 注入 Prompt → 大模型回答

1. 文档上传与解析

java 复制代码
public synchronized KnowledgeDocumentInfo upload(MultipartFile file) throws IOException {
    // 校验文件类型:只允许 txt / md / pdf
    String extension = extractExtension(originalFilename);
    if (!ALLOWED_EXTENSIONS.contains(extension)) {
        throw new IllegalArgumentException("不支持的文件类型");
    }

    // 保存到本地
    Path storedPath = storageDirectory.resolve(storedFilename);
    file.transferTo(storedPath);

    // 按类型解析文档
    List<Document> sourceDocuments = readDocuments(storedPath, extension);
    // 分块
    List<Document> chunks = textSplitter.apply(sourceDocuments);
    // 写入向量库
    vectorStore.add(chunks);
}

PDF 用 Spring AI 自带的 PagePdfDocumentReader,纯文本文档直接按段落切分。

2. 向量化(Embedding)

用了通义千问的 DashScope Embedding,和 Chat 共用同一个 API Key,不用额外注册:

java 复制代码
@Bean
@ConditionalOnNonEmptyApiKey(property = "spring.ai.dashscope.api-key", env = "AI_DASHSCOPE_API_KEY")
DashScopeEmbeddingModel ragEmbeddingModel(...) {
    DashScopeApi api = DashScopeApi.builder()
            .apiKey(resolved.apiKey())
            .baseUrl(resolved.baseUrl())
            .build();
    return DashScopeEmbeddingModel.builder()
            .dashScopeApi(api)
            .build();
}

3. 向量存储

选的是 SimpleVectorStore------纯内存 + JSON 文件持久化,零外部依赖:

java 复制代码
@Bean
SimpleVectorStore vectorStore(DashScopeEmbeddingModel embeddingModel, RagProperties props) {
    SimpleVectorStore store = SimpleVectorStore.builder(embeddingModel).build();
    // 启动时从文件恢复
    Path storePath = Path.of(props.storagePath()).resolve("vector-store.json");
    if (Files.exists(storePath)) {
        store.load(storePath.toFile());
    }
    return store;
}

生产环境可以换成 Elasticsearch / PostgreSQL pgvector / Milvus,接口不变,改个 Bean 就行。

4. 检索增强(最核心的一行)

java 复制代码
private ChatClient.CallResponseSpec buildPrompt(ChatClient chatClient, ChatRequest request) {
    var spec = chatClient.prompt()
            .system(isRagMode ? RAG_SYSTEM_PROMPT : DEFAULT_SYSTEM_PROMPT)
            .messages(toMessages(request.history(), request.message()));

    // 如果用户开启了知识库模式,注入 QuestionAnswerAdvisor
    if (isRagMode) {
        VectorStore store = vectorStore.getIfAvailable();
        if (store != null) {
            spec.advisors(new QuestionAnswerAdvisor(
                store,
                SearchRequest.builder().topK(4).build()
            ));
        }
    }
    return spec;
}

QuestionAnswerAdvisor 是 Spring AI 内置的 RAG Advisor------它自动做三件事:

  1. 把用户问题用同一个 Embedding 模型向量化
  2. 在 VectorStore 里检索 Top-K 相似文档块
  3. 把检索结果拼到 Prompt 里作为上下文

你完全不需要写检索逻辑。

5. System Prompt 区分

java 复制代码
// 普通模式
"你是一个有帮助的 AI 助手。用简洁、准确的中文回答。"

// RAG 模式
"你是企业规则问答助手。请优先依据检索到的知识库内容回答。
 若文档中没有相关信息,请明确说明「未在已导入的规则文档中找到依据」,不要编造。"

这是 RAG 落地最关键的一步------告诉模型「不知道就说不知道」,否则它会在检索不到的时候瞎编。

6. 前端知识库面板

Vue 3 写的侧边栏组件 KnowledgePanel.vue:

  • 启动时调 /api/rag/status 检查 RAG 是否可用(没配 API Key 就隐藏面板)
  • 支持拖拽/选择文件上传,实时显示文档列表和分块数
  • 聊天框新增「知识库模式」开关,开启后 AI 用 RAG System Prompt

踩坑

  • SimpleVectorStore 的 save/load :需要手动在启动时 load(),关闭时 save()。不 save 的话重启后向量库就没了
  • PDF 解析的依赖 :PagePdfDocumentReader 需要 pdfbox,pom.xml 里别忘了加
  • Embedding 和 Chat 共用一个 API Key:DashScope 的 Embedding 和 Chat 都走同一个 Key,但自动配置里它们是分开的------Embedding 的自动配置也要排除,自己手动装配

下一步

  • 支持更多文档格式(Word / HTML)
  • 向量库切换为 PostgreSQL pgvector(SimpleVectorStore 适合 Demo,生产量大了内存扛不住)
  • 对话中展示检索到的参考段落

GitHub: github.com/zgqstory/sp...

相关推荐
Mister Leon3 分钟前
Nvidia Orin AGX实战之从量化到蒸馏:在 Orin 上落地的大模型压缩实战
人工智能·算法·机器学习
Latchh6 分钟前
iLovePDF免费版能批量压缩吗?4款替代对比2026
图像处理·人工智能·计算机视觉·音视频
云和数据.ChenGuang11 分钟前
LangChain 实战指南:从入门到构建智能应用
开发语言·人工智能·python·langchain
IT_陈寒13 分钟前
Vite性能优化完全指南
前端·人工智能·后端
一条泥憨鱼15 分钟前
AI Agent 安全实践:从 MCP 工具调用到 Java 权限校验
java·人工智能·安全
东方佑16 分钟前
用先验重构线性注意力:为什么它至少比普通线性注意力更接近标准注意力
人工智能·机器学习·重构
lucas_AI19 分钟前
Read-Parse-Describe:文档解析终于把「图里画了什么」也写进结果了
人工智能
aneasystone本尊23 分钟前
学习大模型训练的目标函数:Next Token Prediction
人工智能
星核0penstarry23 分钟前
## 一、为什么 Spark UDF 越来越成为瓶颈
人工智能·api·ai编程·gpu·灰度发布·ai 智能体重写·等价性测试
中空板生产 线26 分钟前
现代中空板的发展趋势
人工智能