本文深入解析RAG问答闭环的核心流程,指出常见误区并非Prompt撰写,而是Prompt前的关键步骤。文章详细阐述了从鉴权到记录日志的六大步骤,强调检索与上下文构建的重要性,并针对模型调用超时降级、答案引用与日志记录等工程问题提供解决方案。通过知Hub实例,展示如何实现完整的问答链路,帮助读者理解并构建实用的RAG系统。
很多人做 RAG,最容易误判一个问题:
答案不准,是不是 Prompt 没写好?
Prompt 当然重要。
但在真实项目里,更多问题不是发生在 Prompt 那一行,而是发生在 Prompt 之前。
比如:
用户身份没校验,查到了不该查的知识库。topK 太小,正确片段排在后面没进上下文。相似度阈值太高,相关内容被过滤掉。引用片段太乱,模型拿到的资料本身就不可靠。模型超时没有降级,接口直接卡死。问答日志没记录,出了问题也不知道慢在哪里。
所以 RAG 不是“把问题直接丢给大模型”。
一个能上线的 RAG 问答接口,至少要跑完这 6 步:
鉴权-> 检索-> 组上下文-> 构造 Prompt-> 调模型-> 返回引用并记录日志
这篇就用 KnowHub 的第 12 章,把这条链路拆开讲清楚。

01 RAG 问答到底多了什么
如果直接问大模型,链路很短:
用户问题 -> 大模型 -> 答案
但企业知识库不能这么做。
用户问:
我们公司报销发票最晚什么时候提交?
通用大模型不知道你的内部制度。
它可能会猜一个“30 天”“90 天”之类的答案,听起来合理,但不一定对。
RAG 的核心价值,就是在模型回答前先补资料:
用户问题-> 从知识库检索相关片段-> 把片段拼进 Prompt-> 要求模型只能基于资料回答-> 返回答案和引用来源
所以 RAG 的重点不是“模型能不能说话”,而是:
正确资料有没有被找出来?正确资料有没有被送进上下文?模型有没有被约束只能基于资料回答?用户能不能看到答案依据?系统能不能追踪这次问答?
这些问题解决了,才算完成真正的 RAG 问答闭环。
02 第一步:入口必须先确认用户是谁
KnowHub 的问答接口是:
POST /kb/{kbId}/chat
kbId 表示用户要在哪个知识库里提问。
请求体可以很简单:
{ ”question”: ”这份文档主要讲了什么?”, ”topK”: 5, ”similarityThreshold”: 0.3}
这里有一个非常容易踩的坑:
userId 不能相信前端传入。
正确做法是后端从登录态里取用户 ID:
Long userId = UserContext.requireUserId();request.setUserId(userId);
原因很简单。
如果让前端传 userId,恶意用户就可能把请求里的 ID 改成别人。
RAG 系统一旦权限边界破了,后果不是“回答错了”这么简单,而是可能把别人的知识库内容检索出来。
所以问答入口第一步不是调用模型,而是确认:
当前用户是谁?他有没有权限访问这个知识库?这次检索只能限制在哪个 kbId 下?
这一步做错,后面所有技术优化都没有意义。

03 第二步:先检索,不要急着调模型
进入 KnowledgeChatService 后,第一件事不是拼 Prompt,而是检索。
核心调用可以理解为:
KnowledgeSearchResponse searchResponse = documentVectorService.search( request.getUserId(), kbId, question, request.getTopK(), request.getSimilarityThreshold());
这一步复用了第 11 章讲过的 pgvector 向量召回能力。
职责划分很清楚:
DocumentVectorService:负责找相关 chunkKnowledgeChatService:负责把 chunk 变成答案
这里最值得关注的是两个参数。
第一个是 topK。
它决定最多拿多少个候选片段。
如果 topK 太小,正确答案可能排在第 8 条、第 12 条,根本进不了上下文。
第二个是 similarityThreshold。
它决定相似度低于多少的片段直接不要。
阈值太低,会把很多噪声塞给模型。
阈值太高,可能把真正答案提前过滤掉。
所以排查 RAG 答案不准时,不要只看最后回答。
先把召回结果打印出来:
命中了哪些 chunk?每个 chunk 的 similarity 是多少?来源文档是不是正确?内容是否真的能回答用户问题?
如果召回结果本身不对,后面 Prompt 再好也救不回来。
04 第三步:没有资料时,不要让模型硬答
如果检索结果为空,系统应该直接返回:
知识库中未检索到相关内容,无法确定。
并且不要调用大模型。
这点很重要。
因为没有引用资料时继续调用模型,本质上就从 RAG 退化成普通聊天了。
用户以为模型是在根据知识库回答,实际模型是在凭常识补全。
这就是很多企业知识库最危险的幻觉来源。
KnowHub 里可以用几个状态字段把它说清楚:
modelCalled = falsemodelSuccess = falsemodelFallback = falsereferenceCount = 0
这表示:
不是模型失败。不是接口异常。而是知识库没有召回到可用依据。
前端也可以根据这个状态给用户更准确的提示。
比如:
没有找到相关资料,请换个问法,或确认文档是否已完成索引。
这种设计比让模型编一个答案更可靠。
05 第四步:把引用片段整理成上下文
有了检索结果后,下一步是构造上下文。
不要把数据库查出来的内容原样乱塞给模型。
更好的方式是把每个命中的 chunk 整理成有编号的引用块:
[引用1]来源文档: 员工报销制度.pdfchunkId: 88chunkIndex: 6similarity: 0.823456内容: 发票应在开具后 30 天内提交。
这样做有几个好处。
第一,模型知道每段资料的边界。
第二,回答时可以标注“引用1、引用2”。
第三,用户能看到答案来自哪份文档。
第四,后端排查时能定位到具体 chunk。
在 KnowHub 中,这部分可以交给:
KnowledgeChatPromptBuilder.buildContext(references)
注意,上下文不是越多越好。
如果 topK 太大,或者 chunk 太长,会出现三个问题:
Prompt 变长,模型调用变慢。无关资料变多,回答更容易跑偏。上下文超过模型限制,关键内容反而被截断。
实际项目里,建议先用一个保守配置:
topK = 5similarityThreshold = 0.3
如果发现正确答案经常排在后面,再考虑提高 topK,并在后续加 rerank。
06 第五步:Prompt 要约束模型,不是求模型发挥
RAG 的 Prompt 不需要写得花哨。
它最重要的任务是给模型划边界。
核心规则可以这样写:
你是知识库问答助手。请严格基于给定资料回答问题。如果资料中没有答案,请回复:知识库中未检索到相关内容,无法确定。不要编造资料中不存在的信息。回答要简洁、直接。如果能够回答,请在答案末尾用“引用:引用1、引用2”标明依据。
一个稳定的 RAG Prompt 通常由四段组成:
角色说明回答规则引用资料用户问题
也就是:
你是谁你能做什么你不能做什么你要依据哪些资料回答用户到底问了什么
很多人一遇到回答不准,就不断往 Prompt 里加要求。
但更有效的排查顺序应该是:
先看召回结果是否正确。再看上下文是否清晰。最后再调 Prompt 约束。
否则你只是在让模型基于错误资料“更认真地回答”。

07 第六步:调用模型时必须考虑超时和降级
KnowHub 使用 Spring AI 的 ChatClient 调用聊天模型。
配置可以简化理解为:
@Beanpublic ChatClient chatClient(ChatModel chatModel) { return ChatClient.builder(chatModel).build();}
真正调用时,核心代码很短:
return chatClient.prompt().user(prompt).call().content();
但线上系统不能只写这一行。
因为聊天模型是外部依赖,外部依赖就可能出现:
响应慢超时网络异常服务限流模型服务返回错误
如果没有保护,用户的一次问答可能一直卡住。
所以 KnowHub 在模型调用外层加了 fallback:
timeout 内返回成功答案:modelSuccess=truetimeout 或异常:返回降级答案,modelFallback=true
降级答案可以是:
当前 AI 服务暂时不可用,请稍后重试。
这里要区分两个概念。
Sentinel 是入口限流:
请求太多,先别进来。
fallback 是模型降级:
请求已经进来了,但模型暂时不可用。
它们解决的问题不一样。
前者保护系统入口,后者保护外部模型依赖。
08 答案必须带引用,也必须记日志
RAG 系统如果只返回一段答案,问题很大。
因为你无法判断:
答案来自哪里?用了哪些 chunk?检索耗时多久?模型耗时多久?有没有触发降级?为什么这次回答不准?
所以 KnowHub 会保存两类数据。
第一类是问答日志:
qa_log
它记录这次问答的基础信息和状态:
user_idkb_idquestionanswermodel_nametop_ksimilarity_thresholdretrieval_cost_time_msmodel_cost_time_mscost_time_msmodel_successmodel_fallbackmodel_error_message
第二类是引用来源:
qa_reference
它记录这次问答用到了哪些文档片段:
qa_log_iddocument_idchunk_idchunk_indexdocument_namesource_textsimilarity_score
这两个表要配合使用。
qa_log 解决“这次问答发生了什么”。
qa_reference 解决“这次答案依据是什么”。
以后排查问题时,不能只看用户截图。
应该直接看:
这次 question 是什么?references 是哪些?similarity 分数是多少?answer 有没有引用资料外的信息?retrieval 和 model 分别耗时多少?
这才是工程化 RAG 和玩具 Demo 的区别。

09 一次完整问答怎么跑
现在把完整链路串起来。
用户请求:
POST /kb/2/chatContent-Type: application/json { ”question”: ”这份文档主要验证什么?”, ”topK”: 5, ”similarityThreshold”: 0.3}
后端执行流程:
Gateway 校验 JWT-> 透传 X-User-Id-> Controller 从 UserContext 读取 userId-> 覆盖 request.userId-> Sentinel 判断是否限流-> KnowledgeChatService.chat-> DocumentVectorService.search-> 问题向量化-> pgvector TopK 检索-> 返回 references-> references 为空:直接返回无法确定-> references 不为空:buildContext-> buildPrompt-> ChatClient 调用聊天模型-> 超时或异常:fallback-> 保存 qa_log-> 保存 qa_reference-> 返回 answer + references + 耗时 + 模型状态
最终响应可以包含:
qaLogIdkbIdquestionanswertopKsimilarityThresholdreferenceCountreferencesretrievalCostTimeMsmodelCostTimeMscostTimeMsmodelCalledmodelSuccessmodelFallback
这不是为了让接口看起来复杂。
而是为了让前端、后端和运维都能判断:
这次回答有没有依据?模型有没有成功?慢在哪里?出了问题该查哪一层?
10 最常见的 5 类问题
1. 返回“未检索到相关内容”
优先排查:
文档是否上传成功索引任务是否 SUCCESSdocument_chunk 是否有切片document_chunk_vector 是否有向量kbId 是否正确userId 是否正确similarityThreshold 是否过高
这个问题通常不是模型问题,而是检索阶段没有拿到可用资料。
2. 回答看起来像编的
优先看 references。
如果 references 本身不相关,应该调整检索。
如果 references 是对的,但回答仍然扩展了资料外内容,就加强 Prompt 约束:
如果资料中没有明确依据,不得使用常识补全。
3. 回答太慢
看三个耗时:
retrievalCostTimeMsmodelCostTimeMscostTimeMs
检索慢,就查 pgvector 查询、索引和过滤条件。
模型慢,就查 Prompt 长度、网络、模型服务和 timeout。
4. 经常触发降级
如果看到:
modelFallback = true
重点查:
模型服务是否可访问API key 是否有效是否被限流timeout 是否太短Prompt 是否过长
5. 引用内容太多太乱
常见原因是:
topK 太大similarityThreshold 太低chunk 太长缺少 rerank
短期可以调参数。
长期更建议增加 rerank,把“粗召回”和“精排序”分开。
本章小结
这一章讲的是 RAG 问答闭环。
RAG 不是直接问大模型。
它至少要完成:
鉴权检索组上下文构造 Prompt调用模型返回引用并记录日志
其中最关键的一点是:
没有检索到资料,就不要让模型硬答。
KnowHub 的 POST /kb/{kbId}/chat 接口会先从 UserContext 读取当前用户 ID,再调用 DocumentVectorService.search(…) 获取 TopK 片段。
如果没有 references,直接返回“知识库中未检索到相关内容,无法确定”。
如果有 references,就构造上下文和 Prompt,再通过 Spring AI ChatClient 调用聊天模型。
模型调用失败或超时时,系统返回 fallback 答案,并用 modelFallback=true 明确标记。
最后,系统把问答写入 qa_log,把引用写入 qa_reference。
这样每一次回答都能追踪来源、定位耗时、判断状态。
下一章会进入 Redis。
主功能跑通之后,还要解决三个更工程化的问题:
高频 owner 校验怎么减轻数据库压力?索引任务状态怎么让前端稳定查询?RabbitMQ 重复消费时怎么避免同一个任务并发执行?
最后
2026 年一晃已经过半,AI 大模型的热潮不仅没有降温,反而持续升温!
金融行业用大模型做风控、医疗依靠 AI 解析影像,电商、制造、教育各行各业,都在把 AI 融入日常业务。曾经热闹的 “百模大战”,早就告别单纯比拼模型参数,正式进入落地应用时代。
现在企业疯狂紧缺一类人才:懂业务、懂 AI、能做出可上线项目的大模型开发工程师,岗位缺口大,薪资待遇十分可观。

风口再好,不如手握高薪 offer 实在。行情火热,普通人、程序员该怎样从零入门大模型,抓住这波机会?
今天整理好【2026 最新版】AI 大模型全套免费学习资源,覆盖零基础入门、项目实战、理论知识、大厂面试,从基础一路进阶。所有资料分类归档,没有多余杂料,无套路免费分享给想要入局 AI 赛道的程序员与零基础小白!
👇👇扫码免费领取全部内容👇👇

1、大模型系统化完整学习路线

2、大模型经典书籍&文档

3、AI 大模型最新行业研究报告

4、企业级实战项目 + 完整配套源码

5、大厂大模型面试真题汇总

6、这些资料真的有用吗?
这份资料由我和鲁为民博士(北京清华大学学士和美国加州理工学院博士)共同整理,现任上海殷泊信息科技CEO,其创立的MoPaaS云平台获Forrester全球’强劲表现者’认证,服务航天科工、国家电网等1000+企业,以第一作者在IEEE Transactions发表论文50+篇,获NASA JPL火星探测系统强化学习专利等35项中美专利。本套AI大模型课程由清华大学-加州理工双料博士、吴文俊人工智能奖得主鲁为民教授领衔研发。
资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的技术人员,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。


这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】


349

被折叠的 条评论
为什么被折叠?



