最小 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 GenerationSpring AI - Vector DatabasesSpring AI - PGvectorSpring AI - ETL Pipeline

官方仓库:pgvector/pgvector

实践参考:Microsoft Learn - Develop a RAG application with Spring AI and Azure OpenAIDatawhale - 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 体系
  • 第一版不需要额外引入专用向量库运维成本
  • 后续要升级到 QdrantMilvus 也不是推倒重来

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

先用 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 的两个工程边界:

  • 一旦使用 HNSWIVFFlat 这类近似索引,检索结果就是速度与召回率的权衡,不再等同于精确最近邻
  • 当近似索引与强元数据过滤叠加时,过滤通常发生在索引扫描之后,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、TokenTextSplitterVectorStore 这些对象已经形成了比较自然的最小组合。第一版即使不把这条链路完全框架化,也建议按这个职责模型拆实现。

入库服务示意

下面这个示意代码,不追求 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 项目里的文档入库、切块、版本管理和评估体系