RAG 案例全流程详解
简易 RAG 原型,没有向量数据库,直接用句子相似度做检索,分为 4 大模块:知识库、召回模块、生成模块、主程序入口。
RAG 全称 Retrieval‑Augmented‑Generation,检索增强生成。核心思想:不直接丢给大模型回答,先从自有知识库检索相关片段,把片段塞给大模型,让大模型基于检索出来的资料做回答,减少幻觉,可以接入私有业务文档。
1、知识库 knowledge_base
knowledge_base = [
"宁波银行理财产品到期后,资金自动回到活期账户,一般T+1工作日到账。",
"查询持有的理财产品,可以登录手机银行,在我的资产页面查看理财持仓。",
"银行卡预留手机号修改,需要本人带身份证到线下网点办理。",
"宁波银行智能语音助手波波之音可以查询余额、理财持仓,部分业务支持语音办理。",
"理财产品不承诺保本,过往收益不代表未来收益。"
]
- 模拟银行 FAQ 知识库,每一条代表文档切片 chunk。
- 真实业务场景:PDF、Word 文档,会调用文本分割器,把长篇文档切分成一段段短文本,存入知识库 / 向量库,不能直接传整篇大文档。
- 切片目的:片段短,检索匹配更精准,同时避免 Prompt 超长超限。
2、召回模块(Retrieval 检索阶段)
sim_pipe = pipeline(
"sentence-similarity",
model="damo/nlp_structbert_sentence-similarity_chinese-base"
)
def retrieve_top_k(query, docs, k=2):
score_list = []
for doc in docs:
out = sim_pipe({"text": query, "text_pair": doc})
score = out["scores"][0]
score_list.append((doc, score))
score_list.sort(key=lambda x:x[1], reverse=True)
return [item[0] for item in score_list[:k]]
步骤拆解
- 加载
structbert句子相似度模型:输入两段文本,输出 0‑1 相似度分数,越接近 1 代表语义越接近。 - 入参:
query:用户提问,例:怎么看我买的理财?docs:全部知识库k=2:召回分数最高的 2 条文档。
- 循环遍历知识库每一条,把用户问题和知识库片段两两计算相似度,保存(文档,分数)。
sort(reverse=True)按分数从高到低降序排序。- 返回 top‑2 最匹配的文档片段。
当前代码缺点
这是 Demo 写法,不能用于海量知识库
- 每次用户提问,循环遍历全部知识库做推理,知识库几千上万条的时候速度极慢。
- 生产环境做法:不实时循环算相似度;提前把所有知识库文本转为 embedding 向量存入 FAISS/Chroma 向量库;用户提问时,把问题转 embedding,向量库做向量相似度搜索,毫秒级拿到 topK 片段。
3、增强 + 生成模块(Augment+Generation)
def generate_answer(user_query, context_docs):
context_text = "\n".join(context_docs)
prompt = f"""请根据下面参考资料回答用户问题,只使用参考资料内容,不要编造。
参考资料:
{context_text}
用户问题:{user_query}
回答:"""
return f"【模拟LLM输出】根据参考文档:{context_docs[0]}"
- Augment 增强:把上一步召回拿到的
context_docs(top2 文档)拼接,拼到 Prompt 提示词里面。把私有知识塞给大模型上下文。 - Prompt 指令约束:
只使用参考资料内容,不要编造,金融业务用来抑制大模型幻觉,不能输出知识库没有的业务规则。 - 当前代码做了模拟返回,没有真正调用大模型,规避大模型下载、模型不存在报错。
真实场景:把这个模拟返回替换成 Qwen/ChatGLM 等大模型 pipeline,传入完整 prompt,返回大模型输出结果。
Prompt 结构(标准 RAG 提示词)
系统约束指令
参考资料:【召回回来的业务文档】
用户问题:【用户输入】
回答:
4、主程序入口,完整链路串联
if __name__ == "__main__":
user_question = "怎么看我买的理财?"
print(f"用户问题:{user_question}\n")
top_docs = retrieve_top_k(user_question, knowledge_base, k=2)
print("【召回得到的参考文档】")
for d in top_docs:
print("-", d)
print("\n【RAG最终回答】")
ans = generate_answer(user_question, top_docs)
print(ans)
链路顺序:
- 用户输入问题:
怎么看我买的理财? - 调用
retrieve_top_k检索知识库,拿到 top2 匹配片段
本例中最高匹配片段:
查询持有的理财产品,可以登录手机银行,在我的资产页面查看理财持仓。
- 将召回片段拼接进 prompt
- 送入大模型生成答案,输出给用户。
执行输出示例
用户问题:怎么看我买的理财?
【召回得到的参考文档】
- 查询持有的理财产品,可以登录手机银行,在我的资产页面查看理财持仓。
- 宁波银行智能语音助手波波之音可以查询余额、理财持仓,部分业务支持语音办理。
【RAG最终回答】
【模拟LLM输出】根据参考文档:查询持有的理财产品,可以登录手机银行,在我的资产页面查看理财持仓。
面试延伸知识点(这个 Demo 的短板,生产优化方向)
- Embedding 向量库替换循环检索:FAISS,预计算全部知识库向量,查询做向量检索,解决知识库大时性能差。
- 重排序 Reranker:召回出来 top50,再用 reranker 模型二次打分筛选 top3,提升召回准确率。
- 文档切分优化:RecursiveCharacterTextSplitter,设置 chunk_size、chunk_overlap 重叠,避免语义被切断。
- 幻觉处理:Prompt 约束、引用溯源,输出答案带上参考文档来源;如果召回片段和问题完全无关,输出 “知识库未查询到相关内容”。
- Query 改写:用户提问模糊,对 query 做扩展、改写,提升召回效果。
- 评估指标:召回率 Recall、精确率 Precision,评估 RAG 效果。

浙公网安备 33010602011771号