news 2026/10/2 7:45:16

Spring AI实现RAG完整链路:从分块调优到生产落地避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AI实现RAG完整链路:从分块调优到生产落地避坑指南

做企业内部运维知识库问答系统时,我一开始想走捷径:把文档全部塞进Prompt让模型直接回答。结果长文档直接超出上下文窗口,短文档又经常张冠李戴,问“发布回滚步骤”它能答出隔壁部门的离职流程。换到RAG(检索增强生成)之后,整个思路才顺过来。Java生态里能落地的RAG方案其实不多,Spring AI算是一个官方化、且迭代速度很快的选择。这篇文章我把Spring AI实现RAG的完整链路、核心API、最小可运行代码从头过一遍,重点讲分块、TopK、相似度阈值这些参数到底怎么调,以及上生产前必须处理的权限隔离、数据更新和版本兼容问题。适合刚开始接触RAG的Java开发者,也适合已经跑通Demo但检索质量不稳定、正急着定位问题的人。

1. 先从选型说起:Java服务里搭RAG,为什么最终留下Spring AI

1.1 市面上的路线其实就三类

很多Java团队第一次做RAG,第一反应是搜教程,一搜发现全是Python的FastAPI+LangChain+LangGraph+pgvector方案。方案本身没毛病,但对一个技术栈以Java为主的后端团队来说,代价是维护两套服务:一个Java业务系统,一个Python AI服务,中间还得自己封装API、处理鉴权、搞日志链路。我当时权衡过三条路线。

第一条是Python全家桶。生态最强,文档多,遇到问题基本搜得到答案。缺点是要额外养一套Python服务,团队里不是每个人都愿意碰。第二条是LangChain4j,Java移植版,理念和Spring AI很像,社区也活跃,但它跟Spring生态的整合没有官方背书,很多starter要自己封装。第三条是纯手写:自己调Embedding接口、自己算余弦相似度、自己拼接Prompt。代码量确实可控,小Demo还能跑,但很快你会发现重复造轮子的地方太多,尤其当你需要切换模型、换向量库、加过滤条件的时候。

最终我选了Spring AI,核心原因不是它功能最全,而是它的抽象层设计刚好卡在Java后端开发者的使用习惯上:你面对的是ChatModel、EmbeddingModel、VectorStore这些接口,替换实现只需要改配置,业务代码几乎不动。

1.2 Spring AI给了什么,没给什么

Spring AI给到的是标准化的RAG链路抽象,它把从文档解析、切分、向量化到检索、生成的通用环节都定义成了接口。更实际的一点是,它对模型供应商做了大量适配,OpenAI、通义千问、Ollama、智谱这些都有对应的starter,切换成本很低。这点如果自己写,光是各家SDK的API差异就能耗掉几天时间。

但我要强调一句:Spring AI没有帮你解决的问题更多。数据质量清洗、权限隔离、检索效果评估、文档增量更新,这些都不在框架范围内。RAG项目的效果瓶颈往往出在数据处理环节,而不是模型能力或者框架功能。所以选型时别指望框架帮你兜底,它只是把工程化链路给你铺好了。

我选型时列过一个对比表,直接贴出来供参考:

方案与Spring生态契合度维护成本可观测性适合场景
Python LangChain全家桶低,需跨服务高一般AI专项团队
LangChain4j中中一般已有LangChain经验的Java团队
纯手写Embedding+PG检索高低,但重复代码多自己控制小规模快速验证
Spring AI高低与Spring Boot日志体系天然集成Java后端团队,长期迭代

2. 把RAG拆开看:Spring AI里一条检索增强链路由哪些组件组成

2.1 一条标准RAG链路的四个环节

先用一句大白话解释RAG:模型回答问题之前,先从一个资料库里把可能相关的段落捞出来,然后让模型只根据这些捞出来的段落作答。相当于考试时给你一本可以翻阅的参考书,但只允许你看划了重点的几页。

四个环节分别是:加载、切分、向量化、检索与生成。

加载就是把PDF、Word、Markdown、TXT这类原始文件解析成纯文本。切分是把长文本切成固定大小的块,因为模型上下文窗口是有限的,而且整篇塞进去,检索精度会大幅下降。向量化是把每个文本块通过Embedding模型转成一个高维向量,这个向量在空间里的位置反映了文本的语义。最后,用户提问时同样把问题转成向量,从向量库里找出最相近的若干个文本块,拼进Prompt交给大模型。

这里面有个很容易忽略的点:向量检索是“相似度匹配”,不是“精确匹配”。也就是说,它捞出来的内容只能保证“看着像有关”,不能保证“一定正确”。所以后面所有调优工作,本质都是在提高这个“看着像”的准确率。

2.2 Spring AI核心API与环节对应关系

Spring AI把上面四个环节抽象得比较规整,每个环节都有对应的接口或类:

RAG环节Spring AI组件说明
文档加载DocumentReader接口,常见TikaDocumentReader、PagePdfDocumentReader、JsonReader负责把各种格式转成Document对象
文本切分DocumentSplitter接口,常见TokenTextSplitter按Token数切分,支持重叠
向量化EmbeddingModel接口,实现如OpenAI、DashScope、Ollama将文本变成向量
向量存储VectorStore接口,实现如PgVectorStore、RedisVectorStore、MilvusVectorStore保存向量并提供相似度检索
检索与生成QuestionAnswerAdvisor,或自行调用VectorStore + ChatModel检索排序、拼Prompt、调模型生成回答

这套分层设计最直接的好处是,每个环节都可以替换。你今天用OpenAI的Embedding模型写好了代码,明天要换成通义千问或者本地Ollama的bge模型,需要改的只是配置文件,链路代码完全不用动。我实际切换过一次,感受确实方便。

另外Document对象值得多说一句。它内部包含文本内容、元数据(Metadata)和文档ID。元数据非常关键,后面讲权限隔离、分类过滤全靠它,比如你可以给每一片文档打上category、department、owner这类标签,检索的时候按标签过滤。

2.3 两种落地写法:自动Advisor与手动检索

Spring AI里实现RAG有两种典型写法。

第一种是用QuestionAnswerAdvisor,它会自动完成“检索→拼Prompt→生成回复”这一串动作,代码量最少。

第二种是手动检索,自己调用VectorStore的similaritySearch拿到候选文档,再自己拼Prompt调用ChatModel。代码多一点,但调试、日志、过滤条件、多路召回这些全部由你自己控制。

两种写法没有绝对优劣。项目早期验证效果时用QuestionAnswerAdvisor最省事;进入生产后如果发现需要精细控制检索过程,手动写会更好用。我在第三章里会把两种方式的代码都贴出来。

我个人的经验是:先用Advisor快速跑通,然后看检索结果再做取舍。因为RAG的优化必须建立在能看到中间过程的基础上,如果你一开始就用黑盒式调用,连检索到什么内容都不知道,出了问题很难定位。

3. 跑通最小可用版本:依赖、入库、问答三件事

3.1 工程依赖与基础配置

我用的版本组合是Spring Boot 3.4.x加Spring AI 1.0.0。这里强烈建议通过BOM引入Spring AI的依赖,避免各个模块版本不一致。

<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>1.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

核心依赖两个:一个是模型starter,我示例里以DashScope(通义千问)为例,一个是VectorStore的pgvector starter。

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

注意,如果你用的是OpenAI官方或者本地部署的兼容OpenAI接口的服务,把模型依赖换成spring-ai-starter-model-openai即可,然后在application.yml里把base-url指向对应服务地址就可以了。Spring AI对OpenAI兼容接口的支持很通用,这也是很多人用它对接国内模型商或本地模型的原因。

application.yml配置如下:

spring: datasource: url: jdbc:postgresql://localhost:5432/rag_demo username: postgres password: postgres ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus embedding: options: model: text-embedding-v3 vectorstore: pgvector: index-type: HNSW distance-type: COSINE_DISTANCE dimensions: 1024 initialize-schema: true

这里有个参数容易踩坑:dimensions必须和Embedding模型输出的维度一致。text-embedding-v3输出1024维,所以配置里写1024。如果你换了模型,比如OpenAI的text-embedding-3-large,默认维度是3072,配置不改的话检索结果就是乱的。initialize-schema设为true可以让Spring AI启动时自动建表,开发环境方便,生产环境建议关掉,自己管理表结构。

3.2 知识入库:读取、切分、向量化、写入

知识入库是RAG链路上我最看重的环节,因为它直接决定后续检索质量。下面这段代码把上传文件解析、切分、加元数据、写入向量库一次完成。

@Service public class DocumentIngestService { private final VectorStore vectorStore; public DocumentIngestService(VectorStore vectorStore) { this.vectorStore = vectorStore; } public void ingestFile(MultipartFile file, String category) throws IOException { Resource resource = new InputStreamResource(file.getInputStream()); TikaDocumentReader reader = new TikaDocumentReader(resource); List<Document> documents = reader.get(); documents.forEach(doc -> { doc.getMetadata().put("source", file.getOriginalFilename()); doc.getMetadata().put("category", category); }); TokenTextSplitter splitter = new TokenTextSplitter(500, 100, 5, 500, true); List<Document> chunks = splitter.apply(documents); vectorStore.add(chunks); } }

关于TikaDocumentReader,我多说一句。它内部调用了Apache Tika,对PDF、Word、HTML等常见格式都能直接抽文本。相比之下,PagePdfDocumentReader只处理PDF,而且如果PDF是扫描件,没有OCR能力,抽出来全是空文本。所以我一般默认用Tika。

TokenTextSplitter的五个参数分别是:chunkSizeTokens=500,每块约500个Token;chunkOverlapTokens=100,前后块有100个Token的重叠;minChunkSizeChars=5,过滤掉太小的残留;maxNumChunks=500,单文档最多分500块;keepSeparator=true,切分时保留分隔符。重叠的作用很关键,它保证被切在边界上的完整语义不会丢失。

这里还想强调一个点:写入之前最好看一眼切分结果。我刚开始做的时候直接入库几千个文件,后来检索效果差才发现文本块里全是页眉页脚和空行。开发阶段可以在入库时把chunk内容打到日志里,抽样确认。

3.3 问答链路:Advisor自动版与手动版

先看最简写法,用QuestionAnswerAdvisor:

@Service public class RagQueryService { private final ChatClient chatClient; public RagQueryService(ChatModel chatModel, VectorStore vectorStore) { this.chatClient = ChatClient.builder(chatModel) .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.builder() .topK(5) .similarityThreshold(0.5) .build())) .build(); } public String ask(String question) { return chatClient.prompt() .user(question) .call() .content(); } }

这段代码的含义是:每次调用都会拿用户问题去向量库检索TopK=5个相关文档,过滤掉相似度低于0.5的结果,然后把命中文档和问题一起交给模型生成回答。QuestionAnswerAdvisor会控制Prompt模板,不需要你手写上下文拼接逻辑。

再看手动版本,适合需要控制中间过程或加日志的场景:

public String askWithManualRetrieval(String question) { List<Document> documents = vectorStore.similaritySearch(SearchRequest.builder() .query(question) .topK(5) .similarityThreshold(0.5) .build()); String context = documents.stream() .map(Document::getContent) .collect(Collectors.joining("\n\n---\n\n")); String prompt = """ 你是一个企业内部知识库助手。 请严格基于下面给出的资料回答用户问题,不要编造资料中不存在的内容。 如果资料里没有相关内容,请直接回答“资料中没有找到”。 资料: %s 用户问题: %s """.formatted(context, question); return chatModel.call(prompt); }

两个版本对比,手动版的好处是你能拿到检索到的documents,排查问题非常方便。我之前遇到答非所问,靠打印检索结果一眼就发现是切分太碎,正确答案被切成两半只捞回来一半。用Advisor的话这种问题很难定位。

3.4 用真实问题验证效果

我拿一个内部《服务发布规范》的文档做过测试,里面有一段内容:

如果发布后发现错误率超过1%或P95延迟超过300ms,立即执行回滚。回滚操作包括:将镜像回退到上一个稳定版本,同时回滚数据库迁移脚本对应的版本号,然后再观察15分钟。

我提问“什么情况下需要回滚”,检索命中的正好是包含错误率、延迟条件的那一段,模型给出的回答是:

“发布后发现错误率超过1%或P95延迟超过300ms时需要立即回滚,具体操作包括镜像回退、数据库迁移脚本版本回滚,并观察15分钟。”

这算是一个正常的RAG闭环。如果检索没命中,那大概率要回头检查分块参数或Embedding模型选择,而不是怀疑模型笨。

4. 检索质量调优:分块、TopK、相似度阈值这几个参数怎么摸

4.1 分块策略:太大容易串味,太小容易切碎

分块是RAG里最影响效果、也最需要实验的参数。

块太大,一个块里包含多个主题的内容。检索的时候模型把不相关的内容也一起拿进来了,回答容易出现“串味”。块太小,语义被切断。比如一句“该策略不适用于生产环境”可能独立成块,检索时只看到“适用于生产环境”,意思完全反了。块太小还容易检索不到,因为向量是从块级文本生成的,信息不足时向量的位置会偏移。

我日常使用的参考区间是chunkSizeTokens在400到800之间,overlap在50到100之间。如果文档本身是Markdown这类有标题结构的,我更推荐先按标题切段,再对超长的段按Token切分。这个做法实现起来也不复杂,就是先用正则按标题把文档拆开,再对每个子块判断长度是否超限。

另外还要说一个中文场景的细节:TokenTextSplitter按Token数切分,但中文一个Token不一定等于一个字。不同模型的分词器切出来的Token数量不一样。同一个中文文本,在OpenAI分词器下可能500个Token,在别的模型下可能是700个。所以不要想当然地用字符数换Token数,自己打印几个切分结果看看比较靠谱。

4.2 TopK和相似度阈值:一对配合使用的旋钮

TopK是指检索返回多少条候选文档。这个参数直接决定两件事:召回率和上下文开销。

TopK太小,比如2,相关文档很容易被漏掉,尤其当答案分散在多段文本时。TopK太大,比如20,噪声文档会把模型带偏,而且Token消耗也翻倍。一般从5开始调,如果你发现很多问题是因为答案根本不在检索结果里,试试加大到8或10。

相似度阈值则是判断“这条结果算不算相关”的门槛。不同Embedding模型的评分分布差异极大。text-embedding-v3的相似度普遍偏高,经常在0.6到0.8波动;有些本地模型的分值则在0.3到0.6之间徘徊。所以照抄别人的0.5阈值不一定有效,必须看你自己模型的实际分布。

我的建议是0.5作为起点,然后专门用一批已知正确答案的问题去打印检索相似度,观察“正确答案对应的文档”和“错误结果”的分数区间,把阈值放在两类结果分布有区分度的位置。

4.3 我调参时用的一套笨办法

说一套我实测下来比较好用的调参流程,虽然土,但有效。

第一步,准备20到30条标准问答对。这些问题必须覆盖你真实业务里最常被问的那类问题,答案一定要可以从知识库原文里找到。第二步,跑检索,记录每条问题在某个参数组合下的TopK结果里是否包含“答案来源所在的那个文本块”。这一步不要看模型回答,只看检索命中情况。第三步,调整分块大小、TopK、阈值,反复跑,记录命中率。第四步,检索命中率满意后,再批量跑问答,看回答质量。

我当时记录过一组数据:

chunkSizeTokensTopK阈值检索命中率回答可用率
50050.516/2014/20
80050.518/2017/20
80080.519/2015/20
80080.420/2014/20

很直观地看到:检索命中率和回答可用率不是一回事。加大TopK之后命中率上去了,但噪声多了,模型回答质量反而下降。最后我选了800块大小、TopK=5、阈值0.5的组合。这说明调参不能只看单一指标,一定要把“检索命中”和“最终回答”分开看。

提示:如果你发现调了很久检索命中率还是上不去,先怀疑数据质量而不是参数。极有可能是文档切分前抽取出来的文本本身就丢了关键内容,比如表格里的数据没被抽出来。

5. 从Demo到生产:权限隔离、数据更新、脏数据和版本迁移这些坑要提前填

5.1 多租户和权限隔离:检索阶段就要过滤

这是企业知识库最容易出事故的地方。向量检索本身是不知道权限的,它只计算文本向量的距离,谁的问题和哪份文档更接近,它就返回什么。如果HR的离职流程文档被检索到,并且被你拼进Prompt交给模型回答,那这算一次严重的数据暴露事故。

解决方案是在入库阶段就把权限维度写进元数据,检索时用过滤条件限制范围。

SearchRequest.builder() .query(question) .topK(5) .filterExpression("category == 'hr'") .build();

Spring AI的pgvector实现支持通过filterExpression对元数据做过滤。如果你的场景是按部门隔离,就维护一个部门到category的映射,检索前先算出当前用户能看到哪些类目,再把过滤条件拼进SearchRequest。

这里有个细节容易被忽略:过滤条件匹配的是元数据字段,不是向量里的语义。所以入库时元数据的值一定要规范化,比如统一用部门编码而不是部门名称。我之前遇到过一个线上问题,就是同一个部门在文档里有时叫“技术部”有时叫“技术中心”,导致按部门过滤时漏数据。后来统一改成编码字段,问题才解决。

5.2 数据更新:向量库里躺着的旧文档怎么处理

VectorStore提供了add和delete接口,但真实业务里“更新”不是一个简单的覆盖动作。你上传一份新版本的《考勤制度》,旧版本还在向量库里,如果不删掉,检索时新老条款同时被捞出来,模型根据哪条回答就看随机性了。

我维护了一套映射:业务文档ID对应一批向量文档ID。入库时把业务ID写在元数据里,同时记录写入后返回的Document ID列表。更新时先按业务ID找到旧向量ID列表,执行delete,再入库新版本。这套逻辑自己写也不复杂,但必须做。

对中小规模知识库,比如几万块以内,偷懒的办法是每次更新时全量重建。把知识库文档全部重新读取、切分、向量化、写入,中间短暂停机或者用两套集合切换。这个方法看着笨,但逻辑简单,不容易出现新旧数据共存的问题。数据量大了以后再换成增量更新方案。

5.3 脏数据与格式解析

RAG项目里脏数据是常态。PDF的分页页眉、页脚、目录、页码,Word里的批注、修订,Markdown里的HTML嵌套标签,这些都会干扰切分质量。

我遇到过最典型的一个坑:某份PDF每一页都重复打印了公司名称和页脚,Tika抽出来的文本里这些内容穿插在正文之间。TokenTextSplitter切分后,很多块的第一句都是页眉,向量化之后这些块的特征被严重稀释,检索效果非常差。处理办法是在入库前做文本清洗,把连续重复出现的行去除,或者把页眉页脚对应的文本区域在拆分阶段排除掉。

另外,Excel文件直接让Tika抽文本的话,表格结构会丢失,行与行的对应关系会乱。建议针对表格类文档做定制处理:每一行转成一条文本,并加上表头信息。这种细节对检索质量影响很大,因为很多企业知识库查的就是表里的数据。

5.4 索引与性能:HNSW、Embedding耗时、批量入库

pgvector提供了两种常见的索引类型:IVFFlat和HNSW。对于中小规模数据量,我推荐直接上HNSW,它的查询召回率更稳定,对参数不敏感。IVFFlat需要训练过程,而且如果你的数据分布和训练数据不一致,召回率会明显下降。

指标IVFFlatHNSW
建索引速度快较慢
查询速度快快
召回率与训练数据分布强相关稳定
内存占用低较高

性能上,一次RAG请求的耗时分布大概是这样:用户问题Embedding一次网络往返,可能几十到上百毫秒;向量检索在索引正常的情况下是毫秒级;LLM生成占据大头,几百毫秒到几秒。所以你要优化体验,首先盯的是LLM生成耗时和搜索结果的方案选择,而不是向量检索本身。

入库阶段要注意Embedding调用频繁。大批量文档入库时如果逐条循环调用API,速度很慢而且容易触发限流。建议用批量接口,一次传一批文本,既省时间又降低费用。Spring AI的EmbeddingModel接口也提供了批量方法,值得用起来。

5.5 版本迁移:从0.8到1.0再到2.0,API改了什么

Spring AI的版本迭代速度很快,API也远没有达到稳定。我从0.8.x时代用起,到1.0.0 GA期间踩过不少兼容性的坑。

0.8到1.0有一批明显的改名:EmbeddingClient变成了EmbeddingModel,OpenAiChatClient变成了OpenAiChatModel,依赖坐标也从spring-ai-openai-spring-boot-starter改成了spring-ai-starter-model-openai。不少网上教程用的还是旧写法,照抄前先确认版本。

到了2.0,又有一次较大调整,具体以官方迁移文档为准。这里我给一个通用建议:升级版本时不要直接改版本号就完事,先用官方example仓库里的最小示例跑通,再回头改自己的业务代码。Spring AI的example仓库里覆盖了各个版本的标准用法,这是排查API问题的第一手资料。

版本问题无法完全避免,但可以通过锁版本和集中管理依赖来减少意外。项目里Spring AI版本必须由BOM统一管理,严禁各模块自己写死不同版本号。

6. 进阶路线:重排序、GraphRAG和Agentic RAG值不值得上

6.1 粗排到精排:用重排序模型抢救TopK

基础RAG的检索依赖向量相似度,本质上是一个“粗排”过程。向量模型处理不了太复杂的语义关系,比如同义词、长尾表达、需要多条件匹配的问题,排名靠前的可能并不是真正相关的内容。

重排序的思路是用一个专门的Rerank模型,把向量检索出来的TopK(比如20条)重新打分排序,只取前5条进入Prompt。Rerank模型的计算代价比向量检索高,但它只处理少量候选,整体延迟仍可接受。

部署方式一般独立一个Rerank服务,Java代码通过HTTP调用。

public List<String> rerank(String question, List<String> candidates) { // 调用本地/远程 rerank 服务的HTTP接口 // 请求体里带 question 和 candidates // 返回按相关性分数排序后的候选列表 List<ScoreItem> scored = rerankHttpClient.rerank(question, candidates); return scored.stream() .filter(item -> item.score() >= 0.1) .limit(5) .map(ScoreItem::text) .toList(); }

我自己实测下来的感受是:Rerank对“症状描述型”问题提升最明显。比如问“登录页面一直转圈怎么排查”,向量粗排可能把“登录报错信息”排在前面,Rerank能把“页面加载卡顿排查”排上来。这类效果的提升,单纯调向量参数很难达到。

6.2 GraphRAG解决什么问题

基础RAG处理不了多跳问题。比如“A系统依赖B服务的哪个接口,B服务挂了会影响哪些上游”,这个答案分散在多篇文档里,而且彼此之间的关联关系比文本相似度更关键。GraphRAG的思路是把文档里的实体和关系抽取出来,构建成知识图谱,回答问题时沿着图结构去检索,而不是只看文本向量距离。

Spring AI也在往这个方向扩展,但生态还比较早期。如果团队真的有强关系型问答需求,更成熟的做法是自建链路:用LLM抽取实体关系,存入Neo4j这类图数据库,问答时先做实体识别再查图。

GraphRAG的问题是建设成本高。实体抽取的准确性需要反复调试,图Schema设计要懂业务,维护成本比普通RAG高一个量级。我建议普通知识库先用基础RAG加Rerank,等确实遇到多跳检索瓶颈再考虑。

6.3 Agentic RAG:多轮检索与工具调用

Agentic RAG是近期的热门方向,核心区别是:基础RAG一次检索就回答,Agentic RAG可以拆解问题、多次检索、调用工具、反思修正。

举个实际场景:用户问“对比一下测试环境和预发环境的Nginx配置差异”。理想流程是先识别出这是对比型问题,然后分别检索两个环境的配置文档,最后汇总差异。如果只做一次向量检索,很可能只返回其中一份文档,回答就不完整。

用Spring AI落地,可以利用ChatClient配合工具调用或Agent模块,把“检索测试环境文档”“检索预发环境文档”设计成两个工具,让模型自主决定调用哪个,再汇总结果。

但对大多数内部知识库来说,Agentic RAG的稳定性仍然是挑战。多轮调用意味着Token消耗翻倍、失败节点增多、耗时变长。一定要事先做好路由,简单问题直接一次检索回答,复杂问题才走多轮链路。不要盲目把所有流量都导向Agent,成本和体验都会失控。


做完整套链路后,我最大的体会是:RAG的代码只占三成,剩下七成在数据清洗、质量评估和权限设计上。Spring AI把框架层的链路封装得很顺手,但它不会告诉你哪些文本块是脏的、哪些用户没有权限看到这段内容、分块参数应该定多少。最后分享一个我一直保留的土办法:维护一份二三十条的标准问答对,每次改分块参数或者升级版本后,用同一份问题集跑一遍回归,把检索命中的片段和最终回答一起存档。这套办法看着笨,但对检索质量下降的感知比什么监控都直观。你一旦发现某次版本升级后命中率掉了,回头对照存档就能快速定位问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 7:44:31

帝国CMS发布Word文档实操指南:机械行业网站运营避坑手册

做机械行业的网站运营&#xff0c;最频繁也最头疼的一件事&#xff0c;就是把Word文档里的内容发布到帝国CMS后台。为什么这么说&#xff1f;因为机械行业的产品手册、技术方案、招标文件、参数表&#xff0c;动辄几十页&#xff0c;里面全是表格、图纸、特殊符号、多级标题&am…

作者头像 李华
网站建设 2026/10/2 7:43:49

小区团购系统毕设全攻略:Spring Boot+Vue从选题到答辩

每年到毕业设计选题的时候&#xff0c;“小区团购系统的设计与实现”这个题目基本都会出现在热门列表里。这个题火有火的道理&#xff1a;场景贴近生活&#xff0c;导师一听就知道你要做什么&#xff1b;业务链路完整&#xff0c;设计、开发、测试每个环节都有东西可写&#xf…

作者头像 李华
网站建设 2026/10/2 7:42:03

Levenshtein距离原理与Python生产级实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 7:42:03

从u-boot切入嵌入式:告别单片机思维,迈向系统级开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 7:40:55

SpringSecurity + JWT 权限认证实战:从过滤器链原理到落地

SpringSecurity JWT 实现权限认证功能&#xff1a;从原理到落地&#xff0c;一套能直接用的方案接了个前后端分离的新项目&#xff0c;用户体系、角色权限都要从零搭。说实话&#xff0c;权限这块我第一反应就是 SpringSecurity&#xff0c;理由很简单&#xff1a;这玩意是 Ja…

作者头像 李华
网站建设 2026/10/2 7:40:50

用Materials Studio片段库高效构建有机金属配合物的实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华