最小 RAG 项目落地

从 MVP 边界、数据入库、检索问答到 Java Spring Boot / Spring AI + pgvector 实践方案

Posted by Ekko on June 8, 2026

这篇笔记的目标,不是继续解释 RAG 的概念,而是把问题直接落到“怎么起一个最小可行项目”上:如果现在要做一个能接文档、能入库、能检索、能回答、还能继续迭代的 RAG 服务,第一版到底应该长什么样。

这篇内容会有明确的技术偏向:优先给出 Java Spring Boot / Spring AI 的落地方案,向量库侧默认从 pgvector 起步,因为它对很多已有 Java 后端团队来说心智成本最低。内容重点放在 MVP 方案、工程拆解和升级路线,不把重点放在复杂的 agent 编排上。

参考资料:

官方文档:Spring AI - Retrieval Augmented Generation 、 Spring AI - Vector Databases 、 Spring AI - PGvector 、 Spring AI - ETL Pipeline

官方仓库:pgvector/pgvector

实践参考:Microsoft Learn - Develop a RAG application with Spring AI and Azure OpenAI 、 Datawhale - All-in-RAG 第一节 RAG 简介

[TOC]


一、先定边界:什么叫“最小 RAG 项目”

这里说的“最小”,不是指只完成一次性演示,而是指:

  • 已经有基本的数据接入能力
  • 已经有向量化和检索能力
  • 已经能通过接口问答
  • 已经能定位问题出在入库、检索还是生成
  • 但还没有把系统做复杂

因此,一个最小可行的 RAG 项目至少应具备下面 7 个能力:

  1. 能导入一批文档
  2. 能把文档切块并写入向量库
  3. 能按问题做相似度检索
  4. 能把检索结果拼到 prompt 里生成答案
  5. 能返回来源信息
  6. 能按元数据过滤
  7. 能单独验证入库和检索是否正常

如果缺少第 5 到第 7 项,这个系统通常还停留在可演示阶段,很难视为可落地方案。

二、第一版为什么建议走 Spring Boot + Spring AI + pgvector

对于 Java 团队,第一版更适合从下面这条技术线起步:

1
2
3
4
5
Spring Boot
  + Spring AI
  + PostgreSQL / pgvector
  + 一个 Embedding 模型
  + 一个 Chat 模型

选择这条技术线,并不是因为它在所有场景下都最优,而是因为:

  • Java 团队已有 Spring Boot 经验
  • Spring AI 已经把模型调用、向量存储、RAG Advisor 这层胶水补上了
  • pgvector 可以复用现有 PostgreSQL 体系
  • 第一版不需要额外引入专用向量库运维成本
  • 后续要升级到 Qdrant、Milvus 也不是推倒重来

更务实的启动姿势可以概括为:

先用 Spring AI 把 RAG 主链跑通,向量存储先用 pgvector 验证价值。

三、第一版架构保持最小链路即可

一个最小 RAG 项目,建议先拆成两条链路:

graph LR
    A[文档来源] --> B[文本提取]
    B --> C[切块]
    C --> D[Embedding]
    D --> E[pgvector]
    Q[用户问题] --> R[Query Embedding]
    R --> E
    E --> S[Top K 检索]
    S --> T[Prompt 组装]
    T --> U[LLM 生成]
    U --> V[答案与来源]

这张图对应的就是第一版最小链路:离线侧负责把文档变成可检索的向量数据,在线侧负责把用户问题转成检索请求,再把召回结果拼进 prompt 生成答案。

离线链路:文档入库

1
2
3
4
5
文档来源
  -> 文本提取
  -> 切块
  -> Embedding
  -> 写入 pgvector

在线链路:检索问答

1
2
3
4
5
6
7
用户问题
  -> Query Embedding
  -> pgvector 相似度检索
  -> 取回 top k 文档片段
  -> 拼进 Prompt
  -> LLM 生成答案
  -> 返回答案和来源

这就是最常见的 Naive RAG 形态,对于项目第一版通常已经足够。

第一版通常不必优先引入:

  • 多路召回
  • rerank
  • query rewrite
  • Agentic Workflow
  • GraphRAG

如果最小链路本身还没有稳定,这些升级件只会进一步增加排障难度。

四、项目目录的拆分原则:先把“入库”和“问答”分开

一个比较务实的 Spring Boot 项目结构,可以先长这样:

1
2
3
4
5
6
7
8
9
10
src/main/java
  /config
  /controller
  /service
  /rag
    /ingest
    /retrieve
    /prompt
  /repository
  /model

这里最关键的是职责不要混:

  • ingest 负责读文档、切块、入库
  • retrieve 负责搜索和过滤
  • prompt 负责组装上下文和回答约束
  • controller 只负责暴露接口

如果一开始就把这些职责全部集中在一个 Service 里,后面很难判断到底是哪一层出了问题。

五、最小数据模型怎么设计:不要只存 embedding

第一版最容易犯的错,就是只想着“把向量存进去”。

但真正可用的文档片段,至少应该带下面这些信息:

  • id
  • documentId
  • chunkId
  • content
  • metadata
  • embedding

其中 metadata 建议至少包含:

  • 文档名
  • 来源路径或 URL
  • 标题
  • 页码
  • 文档类型
  • 租户 / 业务域
  • 更新时间

为什么元数据重要?

因为后面常见需求通常会落到这些方向:

  • 只查某个知识库
  • 只查某类文档
  • 只看某个租户
  • 回答里要标出处
  • 某份文档更新后需要删除旧版本

如果一开始不留元数据,后面几乎一定要返工。

六、Spring AI 这条线最核心的几个对象

如果准备用 Spring AI 做第一版 RAG,建议先把下面几个对象关系看清楚。

ChatClient

它负责和聊天模型交互,是最终发起 prompt 调用的入口。

EmbeddingModel

它负责把文本转成向量。

无论是文档切块,还是用户 query,向量都要由它生成。

VectorStore

它负责把文档和向量存进去,并支持相似度检索。

在 pgvector 场景里,Spring AI 已经提供了 PgVectorStore 对应支持。

QuestionAnswerAdvisor

这是 Spring AI 官方提供的最直接 RAG 入口。

它会在用户提问时:

  • 先去向量库查相关文档
  • 把检索结果拼到用户问题里
  • 再交给模型生成答案

如果只是第一版最小项目,QuestionAnswerAdvisor 已经足够。

RetrievalAugmentationAdvisor

这是比 QuestionAnswerAdvisor 更模块化的一层。

这一层允许把流程拆成:

  • pre-retrieval
  • retrieval
  • post-retrieval
  • generation

如果只是最小项目,先不用它也没问题;但如果后面要加改写、重排、空上下文策略,它会更好扩展。

七、依赖如何配置:先用官方 starter,不要自己拼太多胶水

如果按 Spring AI 官方思路走,第一版通常至少需要这几类依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-openai</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-advisors-vector-store</artifactId>
    </dependency>

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
    </dependency>
</dependencies>

如果后面想从简单问答升级到模块化 RAG,再补:

1
2
3
4
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-rag</artifactId>
</dependency>

这里有个现实建议:

  • 第一版不宜同时兼容太多模型厂商
  • 先固定一个 embedding 模型
  • 再固定一个 chat 模型
  • 先把链路稳定性打出来

八、数据库如何起步:pgvector 是第一版很合适的起点

为什么第一版更推荐 pgvector

它更适合回答一个很常见的问题:

已有 Java + Spring Boot + PostgreSQL 团队,是否可以先不引入独立向量库?

在很多 MVP 场景下,答案是可以。

它的优势很直接:

  • 部署和运维成本低
  • 可复用现有 Postgres 账号、权限、备份
  • 业务表和向量表在同一数据库体系内
  • 元数据过滤和业务字段联动更自然

当然,它也不是没边界:

  • 超大规模检索不一定是最优
  • 专用向量库在分布式、复杂检索上可能更强

但这些问题对第一版 MVP 往往不是核心矛盾。

本地起库可以先用 Docker

比如:

1
2
3
4
5
6
7
8
9
services:
  postgres:
    image: pgvector/pgvector:pg16
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: rag
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres

手工建表大致示意

Spring AI 文档里给出了 pgvector 典型 schema,大意如下:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE IF NOT EXISTS vector_store (
    id uuid DEFAULT uuid_generate_v4() PRIMARY KEY,
    content text,
    metadata json,
    embedding vector(1536)
);

CREATE INDEX ON vector_store USING HNSW (embedding vector_cosine_ops);

如果使用 Spring AI 的自动 schema 初始化,也可以让框架完成这部分工作,但要注意:

  • 新版本需要显式开启 initialize-schema
  • embedding 维度要和所选模型一致

除此之外,还要注意 pgvector 的两个工程边界:

  • 一旦使用 HNSW 或 IVFFlat 这类近似索引,检索结果就是速度与召回率的权衡,不再等同于精确最近邻
  • 当近似索引与强元数据过滤叠加时,过滤通常发生在索引扫描之后,top k 结果可能不足,尤其在多租户共享同一索引时更明显

九、配置如何编写:先保证“能接模型、能连数据库、能自动建表”

一个最小可行配置,大致可以写成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/rag
    username: postgres
    password: postgres

  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4o-mini
      embedding:
        options:
          model: text-embedding-3-small

    vectorstore:
      pgvector:
        initialize-schema: true
        index-type: HNSW
        distance-type: COSINE_DISTANCE
        dimensions: 1536

如果使用的是 Azure OpenAI 或其他兼容实现,主要替换的是模型配置,不影响整体思路。

这里还需要额外注意两件事:

  • dimensions 可以显式声明,也可以由 EmbeddingModel 推导;如果后续切换到不同维度的 embedding 模型,通常需要重建 vector_store 表并重新向量化历史数据
  • 批量导入量较大时,可以结合 max-document-batch-size 控制单批写入规模,避免一次性写入过大批次

十、离线入库如何实现:最小实现先把文档读、切、存跑通

文档来源先收敛

第一版强烈建议先只支持 1 到 2 种输入格式,比如:

  • Markdown
  • PDF

不建议在第一阶段同时支持:

  • Word
  • 网页抓取
  • 图片 OCR
  • Excel
  • 飞书 / Confluence / 钉钉

因为 RAG 的第一版痛点往往不在“格式不够多”,而在“已有格式能不能稳定进来”。

切块策略先保持稳定

第一版可以先采用:

  • 按段落 / 标题切
  • 配合固定 token 上限
  • 保留少量 overlap

最重要的是:

  • 不要把整页一次性塞成大 chunk
  • 不要切得太碎导致上下文断掉

切块参数一旦确定,应尽量保持稳定。否则同一批测试问题在不同切块方案下会得到完全不同的召回结果,后续评估很难判断问题到底来自切块、检索还是生成。

Spring AI 的 ETL Pipeline 可以作为默认入库骨架

如果希望尽量贴近 Spring AI 官方提供的抽象,离线入库可以先按下面这条链路理解:

1
2
3
DocumentReader
  -> DocumentTransformer(如 TokenTextSplitter)
  -> DocumentWriter(如 VectorStore)

这条链路的价值在于职责非常明确:

  • DocumentReader 负责把外部文件转成 Document
  • DocumentTransformer 负责切块、格式整理和内容变换
  • DocumentWriter 负责把处理后的文档写入目标存储

在 Spring AI 的官方 ETL Pipeline 中,MarkdownDocumentReader、PDF Reader、TokenTextSplitter 与 VectorStore 这些对象已经形成了比较自然的最小组合。第一版即使不把这条链路完全框架化,也建议按这个职责模型拆实现。

入库服务示意

下面这个示意代码,不追求 API 100% 完整,而是把职责边界摆清楚:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
public class RagIngestService {

    private final VectorStore vectorStore;

    public RagIngestService(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    public void ingest(List<RagChunk> chunks) {
        List<Document> documents = chunks.stream()
                .map(chunk -> new Document(
                        chunk.content(),
                        Map.of(
                                "documentId", chunk.documentId(),
                                "title", chunk.title(),
                                "page", String.valueOf(chunk.page()),
                                "source", chunk.source()
                        )))
                .toList();

        vectorStore.add(documents);
    }
}

这里的重点不是 vectorStore.add() 这一行本身,而是入库前已经把:

  • chunk 内容
  • 文档身份
  • 来源信息
  • 位置元数据

都准备好了。

入库接口建议单独做

比如至少提供一个:

  • POST /api/rag/ingest

或者一个后台任务入口:

  • POST /api/rag/rebuild

原因很简单:

  • 入库是批处理动作
  • 问答是在线请求
  • 两者生命周期和故障处理方式完全不同

不建议把它们合并成同一个接口。

文档更新、删除与重建策略要提前确定

第一版即使不做完整的文档生命周期系统,也至少要先回答下面几个问题:

  • 同一份文档更新后,是先按 documentId 删除旧 chunk,再重建,还是通过版本号过滤只保留当前版本
  • 误导入、重复导入或脏数据写入后,是否有单文档级别的回滚与重建入口
  • embedding 模型切换后,旧向量是否继续保留,还是整库重建

如果这些问题没有提前约束,系统很容易出现“新旧版本混检”“历史脏数据长期残留”“换模型后召回结果失真”这类问题。

十一、在线问答如何实现:Spring AI 最小方案可以直接使用 QuestionAnswerAdvisor

这是 Spring AI 官方给出的最直接 RAG 方式,也是第一版最容易落地的方式。

最小问答链路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Service
public class RagChatService {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    public RagChatService(ChatModel chatModel, VectorStore vectorStore) {
        this.chatClient = ChatClient.builder(chatModel).build();
        this.vectorStore = vectorStore;
    }

    public String ask(String question) {
        var advisor = QuestionAnswerAdvisor.builder(vectorStore)
                .searchRequest(SearchRequest.builder()
                        .topK(5)
                        .similarityThreshold(0.75d)
                        .build())
                .build();

        return chatClient.prompt()
                .advisors(advisor)
                .user(question)
                .call()
                .content();
    }
}

这段代码背后的链路是:

  1. 用户输入问题
  2. QuestionAnswerAdvisor 去向量库做相似度搜索
  3. 把召回内容拼到 prompt
  4. 模型基于上下文生成回答

这已经是一条完整的 RAG 问答主链。

如果需要按知识库过滤

Spring AI 支持在 SearchRequest 里加过滤表达式。

例如:

1
2
3
4
5
6
7
var advisor = QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder()
                .topK(5)
                .similarityThreshold(0.75d)
                .filterExpression("kb == 'employee-handbook'")
                .build())
        .build();

这样就可以按:

  • 知识库
  • 文档类型
  • 租户
  • 业务域

做基础隔离。

如果需要运行时动态过滤

Spring AI 也支持在请求时动态传 filter。

这在多知识库、多租户场景里非常实用,因为不需要为每个知识库都创建一套固定 client。

例如,可以把静态 QuestionAnswerAdvisor 和运行时过滤参数组合起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder()
                .topK(5)
                .similarityThreshold(0.75d)
                .build())
        .build();

return chatClient.prompt()
        .advisors(qaAdvisor)
        .advisors(a -> a.param(
                QuestionAnswerAdvisor.FILTER_EXPRESSION,
                "kb == 'employee-handbook'"))
        .user(question)
        .call()
        .content();

十二、如果希望更像一个接口服务,建议至少暴露这 3 类 API

文档入库

1
POST /api/rag/documents

负责:

  • 上传文档
  • 解析
  • 切块
  • 入库

相似度检索调试

1
GET /api/rag/search?q=...

负责:

  • 只返回检索结果
  • 不调用 LLM

这是线上排问题非常重要的接口。

因为当线上反馈“答得不对”时,必须先知道:

  • 是没召回到
  • 还是召回到了但没用好

问答接口

1
POST /api/rag/ask

输入:

  • 用户问题
  • 可选知识库
  • 可选过滤参数

输出:

  • 回答文本
  • 命中的来源
  • 可选调试信息

十三、Prompt 如何约束:第一版一定要强约束

最小 RAG 项目里,prompt 不求花哨,但一定要清楚。

这类 prompt 至少要表达 4 件事:

  1. 只能基于检索到的内容回答
  2. 证据不足时明确说不知道
  3. 尽量引用来源
  4. 不要把外部常识直接混入

例如:

1
2
3
4
你是一个知识库问答助手。
请严格基于提供的上下文回答问题。
如果上下文不足以支持回答,请直接说明“不知道”或“资料不足”。
回答尽量简洁,并在最后列出使用到的来源标题。

如果没有这层约束,很容易出现:

  • 检索证据明显不足时,模型仍然继续生成看似完整的回答
  • 资料和常识冲突时,模型自行引入上下文之外的补充信息

十四、第一版最值得做的,不是优化回答,而是先把可观测性补上

真正落地时,最有价值的往往不是“再换个大模型”,而是能不能看到这几个东西:

  • 本次 query 是什么
  • top k 召回了哪些 chunk
  • 每个 chunk 的分数是多少
  • 最终拼给模型的上下文是什么
  • 回答引用了哪些来源
  • 使用的 embedding 模型和 chat 模型版本是什么
  • 检索过滤条件与链路总耗时是什么

如果没有这些信息,后面一旦出现“答错了”的反馈,排查基本只能靠猜。

所以第一版就建议加上最基础的日志和调试开关。

十五、如何判断第一版是不是合格:先看这 5 个问题

文档是否真的入库成功

不要只看接口 200。

至少确认:

  • 向量表里是否真的有记录
  • metadata 是否正确
  • chunk 数量是否符合预期

检索接口是否能召回对的片段

这一步必须脱离 LLM 单独验证。

回答是否真的使用了检索结果

不要只看“像不像对”,而要看:

  • 回答能否被检索片段支撑

资料不足时是否会拒答

如果资料不足仍持续生成无依据内容,第一版就不能算合格。

来源是否能回显

哪怕第一版只返回:

  • 文档标题
  • 来源地址

都比完全不给出处强很多。

十六、Java Spring Boot / Spring AI 的推荐落地路线如何选择

如果把“现在真的要做一个 Java 版 RAG 服务,最推荐哪条路”落成具体方案,可以先从下面这个组合开始。

先把三个阶段放到一张表里看,会更容易判断当前项目所处的位置:

阶段 核心组合 主要目标 关键能力 适用场景
第一阶段:最小可行方案 Spring Boot + Spring AI + pgvector + QuestionAnswerAdvisor 先把 RAG 主链跑通 文档入库、向量检索、基础问答、来源返回 快速验证知识库问答价值,团队主体是 Java,且已有 PostgreSQL
第二阶段:增强方案 Spring Boot + Spring AI + pgvector/专用向量库 + RetrievalAugmentationAdvisor 提升检索可控性和扩展性 元数据过滤、检索调试接口、模块化 retrieval、为 query rewrite / rerank 预留扩展点 需要多知识库、多租户和更细粒度检索策略
第三阶段:生产增强 在前两阶段基础上补齐工程能力 把可用系统推进到可持续运营 异步入库、版本控制、删除重建索引、多路召回、缓存、评估集、链路观测 已进入生产迭代,需要稳定性、可观测性和持续优化能力

第一阶段:最小可行方案

1
2
3
4
5
Spring Boot
  + Spring AI
  + OpenAI / Azure OpenAI 兼容模型
  + pgvector
  + QuestionAnswerAdvisor

适用场景:

  • 想快速验证知识库问答价值
  • 团队主体是 Java
  • 已有 PostgreSQL
  • 文档量仍处在中小规模

第二阶段:增强方案

1
2
3
4
5
6
Spring Boot
  + Spring AI
  + pgvector 或专用向量库
  + RetrievalAugmentationAdvisor
  + 元数据过滤
  + 检索调试接口

适用场景:

  • 需要更精细的检索控制
  • 需要模块化扩展 query rewrite / rerank
  • 开始进入多知识库、多租户、多场景

第三阶段:生产增强

再逐步补:

  • 异步入库
  • 文档版本控制
  • 删除 / 重建索引
  • 多路召回
  • rerank
  • 缓存
  • 评估集
  • 观测与链路追踪

这才是比较现实的演进顺序。

十七、什么时候该从 QuestionAnswerAdvisor 升级到模块化 RAG

如果已经遇到下面这些问题,就说明该升级了:

  • 不同问题需要不同检索策略
  • 只靠 top k 效果波动很大
  • 需要加 query rewrite
  • 需要空上下文兜底策略
  • 需要后处理或重排

这时就可以考虑把简单模式:

  • QuestionAnswerAdvisor

升级成更可编排的:

  • RetrievalAugmentationAdvisor

但顺序一定要对:

  • 先把最小链路稳定下来
  • 再增加模块化能力

不要反过来。

十八、第一版最常见的坑,往往都很朴素

文档没切好

导致定义、结论、限制条件被拆开,最后召回内容不完整。

只返回答案,不返回来源

一旦答错,几乎没法排查。

把检索和问答绑死

导致无法单独验证检索质量。

元数据设计太弱

后面想做知识库隔离和过滤时直接卡住。

直接上专用向量库,结果项目复杂度先爆炸

第一版最怕的不是技术不够高级,而是链路太长,问题定位不了。

embedding 模型切换后仍沿用旧向量

如果更换了 embedding 模型,尤其是维度、分布或语义空间发生变化之后,继续沿用旧向量数据通常会让召回结果出现明显漂移。此时需要关注的不是“配置是否已经改完”,而是历史数据是否已经完成重新向量化。

近似索引叠加强过滤,导致 top k 不足

在 pgvector 的近似索引模式下,元数据过滤通常不会在索引扫描前完全生效。过滤条件越强,越容易出现召回数不够、结果波动增大或不同租户之间互相影响召回效果的问题。

十九、如果从 0 开一个 Java RAG 小项目,可以按这个顺序做

  1. 起一个 Spring Boot 工程
  2. 接入 Spring AI 和一个稳定模型
  3. 本地拉起 PostgreSQL + pgvector
  4. 打通 VectorStore.add() 入库
  5. 先确定 documentId、版本号与删除 / 重建策略
  6. 单独做一个 /search 接口验证检索
  7. 再接 QuestionAnswerAdvisor 做 /ask
  8. 补来源返回和基础日志
  9. 准备 20 到 50 条真实问答样本做人工验证

如果这 9 步都稳定了,再谈:

  • 重排
  • 混合检索
  • 复杂编排
  • 多模型切换

二十、最后总结:最小项目真正要证明的,不是“会调 AI API”

全文可以概括为:

最小 RAG 项目要证明的,不是“模型能回答”,而是“文档能被稳定入库、稳定召回、稳定约束生成”。

而在 Java Spring Boot 生态里,更务实的起步方案可以概括为:

Spring Boot + Spring AI + pgvector + QuestionAnswerAdvisor。

这条路线的价值不在于一步到位,而在于它特别适合第一版:

  • 容易启动
  • 容易理解
  • 容易排查
  • 容易往上升级

顺着这条线继续展开,比较值得单独深入的两个专题是:

  • Spring AI 里的 QuestionAnswerAdvisor / RetrievalAugmentationAdvisor 执行链与扩展点
  • Java RAG 项目里的文档入库、切块、版本管理和评估体系