让AI不再"一本正经胡说八道":Spring AI RAG与VectorStore源码全解

0 阅读14分钟

让AI不再"一本正经胡说八道":Spring AI RAG与VectorStore源码全解

你的私有文档、技术手册、产品知识库——如何变成AI的"外挂大脑"?一文拆解从PDF到向量到检索增强生成的完整链路


你有没有遇到过这些情况?

❶ 让AI帮你查公司的内部API文档,它回答得头头是道——但全是编的

❷ 想搭一个基于自己知识库的智能客服,但不知道怎么把文档喂给AI?

❸ 听说RAG(检索增强生成)能解决AI幻觉问题,但看了半天概念还是不知道代码怎么写?

如果你对其中任何一个点了头——这篇就是为你写的

RAG(Retrieval-Augmented Generation)是目前解决AI"幻觉"问题的最有效方案,也是Spring AI里数据流最复杂、组件最多的一块。今天我们从源码层面把这条管线从头到尾拆干净。


一张图看懂RAG在干什么

┌──────────────────────────────────────────────────────────────────────┐
│                    Spring AI RAG 全流程架构                           │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌──────────┐   ┌─────────────┐   ┌──────────────┐   ┌───────────┐ │
│  │ 原始文档  │ → │ 文档读取    │ → │ 文本分割     │ → │ 向量化    │ │
│  │ PDF/MD   │   │DocumentReader│  │TextSplitter  │  │EmbedModel │ │
│  └──────────┘   └─────────────┘   └──────────────┘   └─────┬─────┘ │
│                                                           │         │
│                                                    float[]向量       │
│                                                           ↓         │
│                                                   ┌─────────────┐  │
│                                                   │ VectorStore  │  │
│                                                   │ Redis/Pg/Milvus│ │
│                                                   └──────┬──────┘  │
│                                                          │          │
│  ═════════════════════ 用户提问时 ════════════════════════           │
│                                                          ↓          │
│  ┌──────────┐   ┌──────────────────┐   ┌──────────────┐             │
│  │ 用户提问  │ → │ 向量化+相似度搜索  │ → │ Top-K相关文档 │            │
│  └──────────┘   │ similaritySearch  │   └──────┬───────┘             │
│                 └──────────────────┘          │                       │
│                                               ↓                        │
│                                         ┌──────────┐                  │
│                                         │ 注入提示词 │                  │
│                                         │ SystemPrompt│                │
│                                         └─────┬─────┘                  │
│                                               ↓                        │
│                                         ┌──────────┐                  │
│                                         │ LLM生成  │                  ← 有据可依!
│                                         │ 最终回答  │                  │
│                                         └──────────┘                  │
│                                                                      │
│  核心公式:用户问题 → 检索相关知识 → 注入上下文 → AI有依据地回答      │
└──────────────────────────────────────────────────────────────────────┘

一句话总结:RAG = 给AI开卷考试,允许它翻书再答题。


一、VectorStore:AI的"图书馆"

1.1 核心接口设计

VectorStore是整个RAG系统的基石——它负责存储文档向量并提供相似度搜索能力:

// org.springframework.ai.vectorstore.VectorStore
public interface VectorStore {
    
    // 📥 入库:将文档列表写入向量数据库
    void add(List<Document> documents);
    
    // 🗑️ 出库:按ID批量删除文档
    void delete(List<String> documentIds);
    
    // 🔍 简单搜索:返回最相关的文档
    List<Document> similaritySearch(String query);
    
    // 🔍 高级搜索:支持TopK、阈值过滤、元数据过滤
    List<Document> similaritySearch(SearchRequest request);
}

1.2 SearchRequest:搜索的瑞士军刀

public class SearchRequest {
    private String query;                    // 查询文本
    private int topK = 4;                    // 返回前K个结果
    private double similarityThreshold = 0.0; // 相似度阈值(0~1)
    private Map<String, Object> filterExpression; // 元数据过滤条件
}

这四个参数组合起来可以覆盖绝大多数检索场景:

  • topK=3 + threshold=0.7 → 只要最相关的3条高质量结果
  • topK=10 + threshold=0.0 + 过滤条件 → 按分类精确筛选
  • topK=1 + threshold=0.9 → 只要高度匹配的唯一答案

1.3 Document:文档的数据模型

// org.springframework.ai.document.Document
public class Document {
    
    private final String id;                    // 唯一标识(UUID)
    private final String content;               // 文档正文内容
    private final Map<String, Object> metadata; // 元数据(来源、分类等)
    private final float[] embedding;            // 向量表示(1536维等)
    
    // 快捷构造器
    public Document(String content) {
        this(content, Map.of());  // 自动生成ID和空元数据
    }
}

metadata是隐藏的神器——你可以存文件名、章节号、作者、创建日期等任意信息,后面做过滤搜索全靠它。

1.4 五种VectorStore实现怎么选?

这是实际项目中最常遇到的问题——Spring AI内置支持多种向量数据库:

维度RedisPgVectorMilvusQdrantChroma
部署难度⭐ 最简单⭐⭐ 中等⭐⭐⭐ 较复杂⭐⭐⭐ 较复杂⭐⭐ 简单
性能(百万级)⭐⭐⭐ 够用⭐⭐⭐⭐ 强⭐⭐⭐⭐⭐ 最强⭐⭐⭐⭐ 很强⭐⭐ 一般
运维成本低(你可能本来就在用)低(PostgreSQL扩展)中(需独立部署)中(需独立部署)低(嵌入式)
适合场景已有Redis的项目已有PG的项目大规模生产环境需要高性能过滤开发测试/小规模
额外依赖redis-stack(带向量模块)pgvector扩展Milvus服务端Qdrant服务端无(内存模式)

💡 选型建议

  • 如果项目已经用了Redis或PostgreSQL→ 优先选对应的实现,零额外基础设施成本
  • 如果是从零开始做生产级RAG → MilvusPgVector
  • 本地开发验证 → Chroma(纯内存,pip install即用)

二、向量化:把文字变成数字

2.1 EmbeddingModel接口

public interface EmbeddingModel {
    
    // 对单个文本向量化
    EmbeddingResponse embed(EmbeddingRequest request);
    
    // 批量向量化(默认实现逐个调用)
    default List<float[]> embed(List<String> texts) {
        return texts.stream()
            .map(text -> embed(new EmbeddingRequest(text)))
            .map(response -> response.getResult().getOutput())
            .toList();
    }
}

2.2 一个文本的向量化之旅

原始文本: "Spring AI 是一个 AI 应用开发框架"
    ↓
EmbeddingModel.embed() 被调用
    ↓
HTTP POST 到 Ollama / OpenAI / 智谱AI 等
    ↓
模型内部处理(分词 → 编码 → 映射到高维空间)
    ↓
返回: float[] embedding
= [0.1234, -0.5678, 0.9012, ..., 0.3456]
     ↑                          ↑
     共 768/1536/4096 个浮点数(取决于模型)

关键认知:这个向量不是随便生成的数字——语义相近的文本在向量空间中距离更近。这就是相似度搜索的理论基础。

2.3 从文档到入库的完整代码

// 准备文档
List<Document> documents = List.of(
    new Document("Spring AI 是一个 AI 框架", 
        Map.of("source", "docs", "category", "framework")),
    new Document("Spring Boot 是一个 Web 框架",
        Map.of("source", "docs", "category", "web"))
);

// Step 1: 提取所有文本内容
List<String> texts = documents.stream()
    .map(Document::getContent)
    .toList();

// Step 2: 批量向量化
List<float[]> embeddings = embeddingModel.embed(texts);

// Step 3: 将向量写回每个Document对象
for (int i = 0; i < documents.size(); i++) {
    documents.get(i).setEmbedding(embeddings.get(i));
}

// Step 4: 存入VectorStore
vectorStore.add(documents);  // 完成!

三、文本分割:大文档怎么切成小块?

3.1 为什么需要分割?

LLM有上下文长度限制(比如4K/8K/128K tokens)。一份100页的PDF不可能一次性塞进去。解决方案:先切小块,每块单独向量化存储,查询时只检索最相关的几块。

3.2 TokenTextSplitter:按Token粒度切分

// org.springframework.ai.document.TokenTextSplitter
public class TokenTextSplitter implements TextSplitter {
    
    private final int chunkSize;      // 每块最大Token数(如256/512/1024)
    private final int chunkOverlap;   // 块间重叠Token数(如20~50)
    private final Tokenizer tokenizer;// Token计数器
    
    public List<String> split(String text) {
        List<String> chunks = new ArrayList<>();
        
        // 第一层:按段落分割(保留语义完整性)
        String[] paragraphs = text.split("\n\n");
        
        for (String paragraph : paragraphs) {
            int tokens = tokenizer.countTokens(paragraph);
            
            if (tokens <= this.chunkSize) {
                // 小段落直接作为一个chunk
                chunks.add(paragraph);
            } else {
                // 大段落继续细分
                chunks.addAll(splitLargeChunk(paragraph));
            }
        }
        return chunks;
    }
    
    // 第二层:按句子粒度细切
    private List<String> splitLargeChunk(String text) {
        List<String> chunks = new ArrayList<>();
        String[] sentences = text.split("。|!|?");
        
        StringBuilder currentChunk = new StringBuilder();
        for (String sentence : sentences) {
            int currentTokens = tokenizer.countTokens(currentChunk.toString());
            int sentenceTokens = tokenizer.countTokens(sentence);
            
            if (currentTokens + sentenceTokens <= this.chunkSize) {
                // 还装得下 → 追加
                currentChunk.append(sentence).append("。");
            } else {
                // 装不下了 → 当前块完成,开启新块
                if (currentChunk.length() > 0) {
                    chunks.add(currentChunk.toString());
                }
                currentChunk = new StringBuilder(sentence).append("。");
            }
        }
        
        if (currentChunk.length() > 0) {
            chunks.add(currentChunk.toString());
        }
        return chunks;
    }
}

3.3 切割效果示意

原文档(约1000 tokens):
"Spring AI 是一个 AI 应用开发框架...[中间500 tokens]...
它提供了 ChatModel、VectorStore...[结尾部分]..."

    ↓ TokenTextSplitter(chunkSize=256, chunkOverlap=20)

┌─────────────────────────────────────────────────┐
│ Chunk 1 [tokens 0 ~ 256]                        │
│ "Spring AI 是一个 AI 应用开发框架..."            │
├──────────────────┬──────────────────────────────┤
│ Chunk 2 [236~492]│ ← 与Chunk1重叠20 tokens       │
│ "...框架的核心特性包括 ChatModel..."              │
├──────────────────┬──────────────────────────────┤
│ Chunk 3 [472~728]│ ← 与Chunk2重叠20 tokens       │
│ "...ChatModel 支持 GPT 和 Ollama..."             │
├──────────────────┬──────────────────────────────┤
│ Chunk 4 [708~1000]│ ← 与Chunk3重叠20 tokens      │
│ "...Ollama 还支持本地部署..."                      │
└─────────────────────────────────────────────────┘

💡 为什么要有overlap?
→ 保证被切断的句子/概念不会丢失上下文信息

3.4 chunkSize怎么选?

场景推荐chunkSize原因
FAQ类短问答128~256问题本身很短,不需要大块
技术文档512~1024需要完整的代码示例/说明
法律合同256~512条款之间相对独立
小说/长文1024~2048需要叙事连贯性

四、相似度搜索:如何在向量空间中找到"最相近"的内容?

4.1 余弦相似度:衡量两段文字有多"像"

/**
 * 余弦相似度计算 — RAG检索的核心算法
 * 返回值范围:-1.0(完全相反)~ 1.0(完全相同),通常关注 0~1 区间
 */
public float cosineSimilarity(float[] a, float[] b) {
    float dotProduct = 0.0f;   // 点积
    float normA = 0.0f;        // A向量的模
    float normB = 0.0f;        // B向量的模
    
    for (int i = 0; i < a.length; i++) {
        dotProduct += a[i] * b[i];
        normA += a[i] * a[i];
        normB += b[i] * b[i];
    }
    
    return dotProduct / (float)(Math.sqrt(normA) * Math.sqrt(normB));
}

直观理解

  • 0.95 → 两段文字语义几乎相同
  • 0.75 → 相关但不完全一样
  • 0.50 → 有点关系但不太确定
  • 0.20 → 基本无关

4.2 以Redis为例看完整搜索过程

// RedisVectorStore 的 similaritySearch 实现
public class RedisVectorStore implements VectorStore {
    
    @Override
    public List<Document> similaritySearch(SearchRequest request) {
        
        // Step 1: 把用户的查询文本也变成向量
        float[] queryEmbedding = embeddingModel.embed(request.getQuery());
        
        // Step 2: 从Redis取出所有已存储的文档向量
        List<Document> allDocuments = getAllDocuments();
        
        // Step 3: 逐一计算余弦相似度
        List<DocumentWithScore> results = allDocuments.stream()
            .map(doc -> new DocumentWithScore(
                doc,
                cosineSimilarity(queryEmbedding, doc.getEmbedding())  // 核心计算
            ))
            // Step 4: 过滤掉相似度不够的结果
            .filter(dws -> dws.score >= request.getSimilarityThreshold())
            // Step 5: 按相似度从高到低排序
            .sorted((a, b) -> Float.compare(b.score, a.score))
            // Step 6: 只取前K个
            .limit(request.getTopK())
            .map(dws -> dws.document)
            .toList();
        
        return results;
    }
}

4.3 一次真实搜索的全过程

用户提问: "Spring 框架有什么特点?"Step 1: 向量化问题
  "Spring 框架有什么特点?" → [0.234, 0.567, 0.891, ...]

Step 2: 计算与所有文档的相似度
  ├─ "Spring AI 是一个 AI 框架"     → 相似度 0.85 ✅ 高度匹配
  ├─ "Spring Boot 是一个 Web 框架"  → 相似度 0.78 ✅ 匹配
  ├─ "Java 是一门编程语言"           → 相似度 0.32 ❌ 太低
  └─ "Python 的Django框架很流行"    → 相似度 0.18 ❌ 不相关

Step 3: 设 threshold=0.5 → 过滤后剩2Step 4: 取 topK=2 → 返回这两条给AI作为参考素材

五、RAG Pipeline:让AI"开卷答题"

前面的内容都是准备工作。真正的魔法发生在这里——QuestionAnswerAdvisor 把检索到的文档注入到AI的提示词中。

5.1 QuestionAnswerAdvisor源码解析

/**
 * QuestionAnswerAdvisor — RAG的核心拦截器
 * 
 * 工作原理:
 * 在请求发送给LLM之前,自动执行以下步骤:
 * 1. 从用户问题提取查询词
 * 2. 在VectorStore中做相似度搜索
 * 3. 将搜索结果拼接到系统提示词
 * 4. LLM基于"参考资料"生成回答
 */
public class QuestionAnswerAdvisor implements CallAroundAdvisor {
    
    private final VectorStore vectorStore;
    private final EmbeddingModel embeddingModel;
    private final int topK;  // 默认检索前4条
    
    @Override
    public String getName() {
        return "QuestionAnswerAdvisor";
    }
    
    @Override
    public int getOrder() {
        return 2;  // 执行顺序:MemoryAdvisor之后,其他Advisor之前
    }
    
    /**
     * ★★★ 核心方法:在请求发出前注入检索结果 ★★★
     */
    @Override
    public AdvisedRequest before(AdvisedRequest request) {
        
        // Step 1: 提取用户的问题文本
        String userQuestion = extractUserQuestion(request);
        
        // Step 2: 🔑 在VectorStore中搜索相关文档
        List<Document> relevantDocs = vectorStore.similaritySearch(
            SearchRequest.builder()
                .query(userQuestion)
                .topK(this.topK)
                .similarityThreshold(0.5)
                .build()
        );
        
        // Step 3: 将文档列表格式化为可读文本
        String ragContext = buildRagContext(relevantDocs);
        
        // Step 4: 🔑 将检索结果追加到系统提示词
        String enhancedSystemPrompt = request.getSystemText() + "\n\n" +
            "【参考文档】\n" + ragContext + "\n" +
            "请基于以上参考文档回答用户问题。如果文档中没有相关信息,请如实告知。";
        
        // Step 5: 返回增强后的请求(对上层透明)
        return AdvisedRequest.from(request)
            .systemText(enhancedSystemPrompt)
            .build();
    }
    
    // 格式化检索结果
    private String buildRagContext(List<Document> documents) {
        return documents.stream()
            .map(doc -> "- " + doc.getContent())
            .collect(Collectors.joining("\n"));
    }
    
    @Override
    public ChatResponse after(AdvisedRequest request,
                               AdvisedResponse<ChatResponse> response) {
        return response.getChatResponse();  // 后置处理无特殊逻辑
    }
}

5.2 RAG完整数据流

用户提问: "Spring AI 有哪些核心模块?"
    │
    ▼
╔══════════════════════════════════════════════════╗
║  QuestionAnswerAdvisor.before() 开始工作          ║
╠══════════════════════════════════════════════════╣
║                                                  ║
║  ① 向量化问题                                    ║
║     "Spring AI有哪些核心模块?" → [0.12, 0.78...] ║
║                                                  ║
║  ② VectorStore.similaritySearch(topK=3)         ║
║     检索结果:                                     ║
║     • "Spring AI包含ChatModel、EmbeddingModel..." ║
║     • "Spring AI支持OpenAI、Ollama等多种模型..."  ║
║     • "VectorStore提供统一的向量存储抽象..."      ║
║                                                  ║
║  ③ 构建增强的系统提示词                           ║
║     原始系统提示词:                               ║
║     "你是一个AI助手"                              ║
║          +                                       ║
║     【参考文档】                                  ║
║     • Spring AI包含ChatModel、EmbeddingModel...   ║
║     • Spring AI支持OpenAI、Ollama...              ║
║     • VectorStore提供统一向量存储抽象...          ║
║          +                                       ║
║     请基于以上参考文档回答用户问题。               ║
║                                                  ║
╚══════════════════════════════════════════════════╝
    │
    ▼
发送给 LLM(此时LLM已经拿到了"参考资料")
    │
    ▼
LLM 生成回答:
"根据参考文档,Spring AI 的核心模块包括:
 ├── ChatModel:对话模型抽象层
 ├── EmbeddingModel:文本向量化
 ├── VectorStore:统一向量存储接口
 ├── AdvisorChain:请求处理管道
 └── Tool Calling:工具调用机制"

↑ 这次的回答有根有据,不再是瞎编的了!

六、ETL管道:从PDF到VectorStore的自动化流水线

6.1 DocumentReader:什么格式的文档都能读

// org.springframework.ai.document.DocumentReader
public interface DocumentReader {
    List<Document> read(Resource resource);  // 统一入口
}

// Spring AI 内置了多种实现:
Reader支持格式典型场景
PdfDocumentReaderPDF技术手册、论文、合同
MarkdownDocumentReaderMarkdown (.md)技术博客、README、Wiki
WebDocumentReaderHTML网页在线文档、新闻文章
JsonDocumentReaderJSON结构化数据、API响应

6.2 DocumentTransformer:文档加工流水线

// org.springframework.ai.document.DocumentTransformer
public interface DocumentTransformer {
    List<Document> transform(List<Document> documents);
}

// 最常用的转换器:文本分割
public class TextSplitterDocumentTransformer implements DocumentTransformer {
    
    private final TextSplitter textSplitter;
    
    @Override
    public List<Document> transform(List<Document> documents) {
        return documents.stream()
            .flatMap(doc -> {
                // 1. 分割文档内容为多个chunk
                List<String> chunks = textSplitter.split(doc.getContent());
                
                // 2. 为每个chunk创建新的Document(继承原元数据)
                return chunks.stream()
                    .map(chunk -> new Document(
                        chunk,
                        doc.getMetadata()  // 元数据透传!
                    ));
            })
            .toList();
    }
}

6.3 完整的ETL Pipeline代码

从一份PDF到可检索的向量存储,全部代码如下:

// ========== ETL Pipeline 完整流程 ==========

// Step 1: 读取原始文档
DocumentReader reader = new PdfDocumentReader();
List<Document> documents = reader.read(
    new FileSystemResource("company-docs/product-manual.pdf")
);

// Step 2: 文本分割(大文档 → 小chunks)
DocumentTransformer splitter = new TextSplitterDocumentTransformer(
    new TokenTextSplitter(
        512,    // 每个chunk最大512 tokens
        50      // chunk之间重叠50 tokens
    )
);
List<Document> chunks = splitter.transform(documents);
// 假设原来2个文档 → 切割后变成了15个chunk

// Step 3: 向量化
List<float[]> embeddings = embeddingModel.embed(
    chunks.stream().map(Document::getContent).toList()
);
for (int i = 0; i < chunks.size(); i++) {
    chunks.get(i).setEmbedding(embeddings.get(i));
}

// Step 4: 写入VectorStore
vectorStore.add(chunks);  // ✅ 入库完成!

// ===== 之后就可以检索了 =====
List<Document> results = vectorStore.similaritySearch(
    SearchRequest.builder()
        .query("产品的定价策略是什么?")
        .topK(3)
        .similarityThreshold(0.7)
        .build()
);

七、在ChatClient中使用RAG:一行注册搞定

说了这么多底层原理,实际用起来有多简单?

// 构建带有RAG能力的ChatClient
ChatClient chatClient = ChatClient.builder(chatModel)
    // 注册RAG Advisor —— 一行代码让AI具备知识检索能力
    .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, embeddingModel, 3))
    // 还可以叠加记忆能力
    .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
    .build();

// 使用 —— 和普通对话完全一样的API
String answer = chatClient.prompt()
    .user("Spring AI 有哪些核心模块?")  // 用户不需要知道RAG的存在
    .call()
    .content();

/*
 * 幕后自动完成的操作:
 *
 * ① QuestionAnswerAdvisor 拦截请求
 * ② 向量化用户问题
 * ③ 在VectorStore中搜索最相关的3篇文档
 * ④ 将文档内容注入系统提示词
 * ⑤ 发送给LLM(此时LLM看到了参考资料)
 * ⑥ LLM基于文档生成有据可依的回答
 * ⑦ 返回给用户 —— 全程透明!
 */

八、实战踩坑指南

8.1 chunkSize调不好会怎样?

问题现象解决方法
太大(如2048)检索到的内容太泛泛,噪声多,回答质量下降缩小到256~512
太小(如64)丢失上下文,AI看到的信息支离破碎放大到256以上
没有overlap关键信息恰好在切割边界处丢失设置20~50的重叠

8.2 相似度阈值设多少合适?

推荐阈值范围(基于实践经验):

0.7 ~ 0.9  → 严格要求匹配(适合FAQ、事实查询)
0.5 ~ 0.7  → 适中(适合大多数知识库场景)✅ 推荐
0.3 ~ 0.5  → 宽松(适合探索性搜索、创意类场景)

🚨 注意:首次上线建议先不加阈值(设为0),
   观察实际的相似度分布后再调整。

8.3 检索不到结果怎么办?

常见排查顺序:

  1. 确认文档是否成功入库vectorStore.similaritySearch("")看看有没有数据
  2. 检查chunkSize是否合理 → 可能文档被切得太碎了
  3. 检查EmbeddingModel是否一致 → 入库和查询必须用同一个模型!
  4. 降低阈值试试 → 可能是阈值设太高了

本篇小结

主题一句话总结重要度
RAG核心思想给AI开卷考试——检索相关知识后再回答⭐⭐⭐⭐⭐
VectorStore接口统一的向量存储抽象,屏蔽底层差异⭐⭐⭐⭐⭐
五种实现选型Redis/PgVector/Milvus/Qdrant/Chroma各有适用场景⭐⭐⭐⭐
Document数据模型内容+元数据+向量三合一⭐⭐⭐⭐
EmbeddingModel文本→向量的转换器,RAG的基础设施⭐⭐⭐⭐⭐
TokenTextSplitter按Token粒度切分大文档,chunkSize+overlap是关键参数⭐⭐⭐⭐
余弦相似度向量空间中的"距离度量",决定检索相关性⭐⭐⭐⭐
QuestionAnswerAdvisorRAG的核心——自动检索+注入提示词⭐⭐⭐⭐⭐
ETL PipelineRead→Split→Embed→Store 四步标准流程⭐⭐⭐⭐

关键类速查

类 / 接口所在包职责
VectorStorespring-ai-vectorstore向量存储核心接口
Documentspring-ai-common文档数据模型
EmbeddingModelspring-ai-model向量化模型接口
TextSplitterspring-ai-document文本分割策略接口
TokenTextSplitterspring-ai-document按Token切分的具体实现
SearchRequestspring-ai-vectorstore搜索请求封装
QuestionAnswerAdvisorspring-ai-client★ RAG核心拦截器
DocumentReaderspring-ai-document多格式文档读取
DocumentTransformerspring-ai-document文档转换处理链

📚 系列导航

本篇文章属于「Spring AI 系列」源码篇,建议按顺序阅读:

主题关键字
1入门:环境搭建与第一个 AI 对话Quick Start
2一次对话的完整生命线prompt→call→response
3Message 体系:User/System/Assistant消息格式
4RAG 基础:检索增强生成入门VectorStore
5Advisor 拦截器链责任链 / 自定义扩展
6Tool Calling 工具调用Function Callback
★ 7VectorStore与RAG Pipeline检索增强生成

👋 我是亦暖筑序,专注 Java 后端开发者的 AI 应用落地指南。

如果这篇文章对你有帮助,欢迎 点赞 + 收藏, 我们下期见!🚀