一、核心框架
框架
从基础认知到项目集成形成完整闭环:①理解 RAG→②两条核心流水线→③知识库构建→④检索与生成→⑤关键参数调试→⑥可解释检索→⑦失败模式排错→⑧项目落地适配,另含参数调试与可解释检索的补充内容。
二、RAG 核心定义与价值
(一)本质定义
RAG(检索增强生成)并非单一函数,而是通过 "先检索知识库相关片段,再将片段与问题结合喂给大模型" 的技术方案,核心是解决大模型训练数据滞后、知识不可追溯的痛点,让生成结果既精准又有依据。
(二)核心优势
- 知识更新灵活:无需重新训练大模型,仅需更新知识库即可覆盖新内容;
- 结果可追溯:通过可解释检索功能,明确答案来源于知识库的具体片段,降低 "幻觉" 风险;
三、两条核心流水线实操解析
(一)离线流水线(Ingest):知识库构建
1. 核心流程
(1)文档初始化
# 核心功能:创建并写入原始知识库文本
import os
# 定义知识库文件路径(飞书文档推荐目录结构)
KNOWLEDGE_PATH = "./knowledge.txt"
# 示例:高数知识库内容(可替换为自身项目内容)
knowledge_content = """
高等数学-函数极限
1. 极限的定义:设函数f(x)在点x0的某一去心邻域内有定义,如果存在常数A,对于任意给定的正数ε(无论它多么小),总存在正数δ,使得当x满足0<|x-x0|<δ时,对应的函数值f(x)都满足|f(x)-A|<ε,那么常数A就叫做函数f(x)当x→x0时的极限。
2. 极限的运算法则:
- 两个函数的和的极限等于极限的和:lim[f(x)+g(x)] = limf(x) + limg(x)
- 两个函数的积的极限等于极限的积:lim[f(x)·g(x)] = limf(x)·limg(x)
3. 常见极限公式:
- lim(sinx/x) = 1 (x→0)
- lim(1+1/x)^x = e (x→∞)
"""
# 写入知识库文件(指定UTF-8编码避免中文乱码,飞书文档重点提示)
def create_knowledge_base():
if not os.path.exists(KNOWLEDGE_PATH):
with open(KNOWLEDGE_PATH, "w", encoding="utf-8") as f:
f.write(knowledge_content)
print("知识库创建成功!")
else:
print("知识库已存在,无需重复创建")
if __name__ == "__main__":
create_knowledge_base()
(2)文档切分
# 核心功能:中文友好的文档切分(段落→句子→字符分层策略)
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 加载知识库文本
def load_knowledge():
with open("./knowledge.txt", "r", encoding="utf-8") as f:
return f.read()
# 初始化切分器(飞书文档推荐参数)
def split_text():
text = load_knowledge()
# 核心参数:chunk_size(单块长度)、chunk_overlap(重叠长度)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 初始值,可调整范围300-800
chunk_overlap=50, # 重叠长度,建议为chunk_size的10%-20%
separators=["\n\n", "\n", "。", "!", "?", ";", ",", " "], # 中文切分分隔符优先级
length_function=len # 按字符长度计算
)
# 执行切分
chunks = text_splitter.split_text(text)
print(f"切分完成,共生成 {len(chunks)} 个文本块")
# 打印第一个块验证
print("第一个文本块内容:", chunks[0])
return chunks
if __name__ == "__main__":
split_text()
(3)向量化与存储
# 核心功能:文本向量化并存储到Chroma向量库
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
# 加载切分后的文本块(承接上一步)
def load_split_chunks():
from demo_02_ingest_split import split_text
return split_text()
# 初始化embedding模型(飞书文档推荐硅基流动模型)
def init_embedding():
return HuggingFaceEmbeddings(
model_name="siliconflow/text-embedding-ada-002", # 兼容OpenAI格式的中文embedding模型
model_kwargs={"device": "cpu"}, # 无GPU则设为cpu
encode_kwargs={"normalize_embeddings": True} # 归一化向量,提升检索精度
)
# 构建向量库
def build_vectorstore():
chunks = load_split_chunks()
embeddings = init_embedding()
# 存储到本地chroma_db目录
vector_db = Chroma.from_texts(
texts=chunks,
embedding=embeddings,
persist_directory="./chroma_db" # 向量库存储目录
)
vector_db.persist() # 持久化存储
print("向量库构建完成,存储路径:./chroma_db")
return vector_db
if __name__ == "__main__":
build_vectorstore()
2. 关键参数与实操注意
chunk size:单块文本长度,常用区间 300-800,需根据知识库体量调整(如高数教材可设为 800-1000);chunk overlap:块间重叠长度(相邻两个文本块之间重复的字符长度),建议 10%-20%,用于避免跨 chunk 关键信息断裂(如公式、长句拆分丢失逻辑);- 核心禁忌:入库参数(
chunk size、overlap)与查询参数(TOPK、soccer sold)需分开调试,不可混改导致流程混乱。
(二)在线流水线(Query):检索与生成
1. 核心流程
(1)问题检索与过滤
# 核心功能:问题向量化+相似检索+阈值过滤
from langchain.vectorstores import Chroma
from demo_03_build_vectorstore import init_embedding
# 加载向量库
def load_vectorstore():
embeddings = init_embedding()
return Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
# 检索与过滤
def retrieve_and_filter(query):
vector_db = load_vectorstore()
# 核心参数:k(TOPK)、score_threshold(soccer sold)
retriever = vector_db.as_retriever(
search_kwargs={
"k": 3, # 召回3个最相关文本块(飞书文档推荐3-5)
"score_threshold": 0.5 # 相似度阈值,0.3-0.6为合理区间
}
)
# 执行检索
relevant_docs = retriever.get_relevant_documents(query)
print(f"检索到 {len(relevant_docs)} 个相关文本块")
# 打印检索结果及相似度
for i, doc in enumerate(relevant_docs):
print(f"\n第{i+1}个相关块:")
print("内容:", doc.page_content)
print("相似度得分:", doc.metadata["score"] if "score" in doc.metadata else "未返回")
return relevant_docs
if __name__ == "__main__":
# 测试查询:高数极限的运算法则有哪些
retrieve_and_filter("高数极限的运算法则有哪些")
(2)提示词组装与生成
# 核心功能:组装上下文+提示词+调用大模型生成答案
from demo_04_query_retrieve_filter import retrieve_and_filter
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
# 初始化大模型(可替换为国产大模型)
def init_llm():
return ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0.1, # 低温度保证答案精准
api_key="your-api-key", # 替换为自己的API Key
base_url="your-base-url" # 飞书文档中验证的base URL
)
# 组装提示词并生成答案
def generate_answer(query):
# 1. 检索相关文本块
relevant_docs = retrieve_and_filter(query)
if not relevant_docs:
return "知识库中未找到相关内容,无法回答该问题"
# 2. 组装上下文
context = "\n\n".join([doc.page_content for doc in relevant_docs])
# 3. 构建提示词(飞书文档强调:约束大模型仅基于上下文回答)
prompt_template = """
请仅基于以下上下文内容回答用户问题,禁止使用上下文外的任何信息。
如果上下文没有相关内容,请直接回答"知识库中未找到相关内容"。
上下文:
{context}
用户问题:
{query}
回答要求:
1. 条理清晰,分点说明;
2. 语言简洁,准确对应上下文内容;
3. 仅回答问题本身,不额外扩展。
"""
prompt = PromptTemplate(
input_variables=["context", "query"],
template=prompt_template
)
# 4. 调用大模型生成答案
llm_chain = LLMChain(llm=init_llm(), prompt=prompt)
answer = llm_chain.run(context=context, query=query)
return answer
if __name__ == "__main__":
# 测试查询
query = "高数极限的运算法则有哪些"
print("用户问题:", query)
print("\n生成答案:", generate_answer(query))
2. 核心参数与效果影响
| 参数名 | 作用 | 文档推荐值 | 实操调整逻辑 |
|---|---|---|---|
TOPK |
召回相关 chunk 数量 | 3-5 个 | 过多易稀释注意力,过少易遗漏关键信息 |
soccer sold |
相似度过滤阈值 | 0.3-0.6 | 低于 0.3 引入无关信息,高于 0.6 可能漏检 |
四、关键参数调试技巧
(一)调试核心原则
- 单一变量原则:每次仅调整一个参数(如固定
overlap=50,仅测试chunk size),避免多变量干扰结果; - 先看后调原则:优先打印
raw result(原始检索结果),明确是 "检索不到" 还是 "检索到用不好",再针对性调参; - 二分法高效试错:以
chunk size为例,在 100-2000 区间内逐步缩小范围(如 100→1000→500→750),快速找到最优值。
(二)参数调试对比代码
# 核心功能:对比不同参数组合的检索效果
from demo_04_query_retrieve_filter import load_vectorstore
def tune_parameters(query):
vector_db = load_vectorstore()
# 定义待测试的参数组合(飞书文档推荐的对比思路)
param_combinations = [
{"k": 3, "score_threshold": 0.5, "chunk_size": 500}, # 基准参数
{"k": 5, "score_threshold": 0.5, "chunk_size": 500}, # 仅调整TOPK
{"k": 3, "score_threshold": 0.4, "chunk_size": 500}, # 仅调整阈值
{"k": 3, "score_threshold": 0.5, "chunk_size": 800} # 仅调整chunk_size
]
# 逐一测试参数组合
for i, params in enumerate(param_combinations):
print(f"\n=== 测试参数组合 {i+1} ===")
print(f"参数:TOPK={params['k']}, 阈值={params['score_threshold']}, chunk_size={params['chunk_size']}")
retriever = vector_db.as_retriever(search_kwargs={"k": params["k"], "score_threshold": params["score_threshold"]})
results = retriever.get_relevant_documents(query)
print(f"检索结果数量:{len(results)}")
if results:
print("第一个结果相似度:", results[0].metadata.get("score", "N/A"))
if __name__ == "__main__":
tune_parameters("高数极限的运算法则有哪些")
五、可解释检索与失败模式排错
(一)可解释检索代码
# 核心功能:返回答案溯源信息(来源、位置、相似度)
from demo_04_query_retrieve_filter import load_vectorstore
def explainable_retrieve(query):
vector_db = load_vectorstore()
# 执行带分数的检索
results = vector_db.similarity_search_with_score(query, k=3)
# 组装可解释结果
explain_results = []
for i, (doc, score) in enumerate(results):
explain_results.append({
"序号": i+1,
"内容片段": doc.page_content,
"相似度得分": round(score, 4),
"来源文件": "knowledge.txt",
"建议可信度": "高" if score >= 0.5 else "中" if score >= 0.3 else "低"
})
# 打印可解释结果
print("=== 检索结果溯源 ===")
for res in explain_results:
print(f"\n{res['序号']}. 可信度:{res['建议可信度']}(相似度:{res['相似度得分']})")
print(f"内容:{res['内容片段']}")
print(f"来源:{res['来源文件']}")
return explain_results
if __name__ == "__main__":
explainable_retrieve("lim(sinx/x)的结果是什么")
(二)常见失败模式与排错步骤
- 文档未入库 / 未持久化:检查
chroma_db/目录是否存在; - 查询过于口语化 / 过短:优化 query(如 "高数极限怎么求"→"高等数学中函数极限的求解方法");
- 模型 "幻觉"(瞎编内容):在提示词中明确 "禁止使用知识库外信息",并提高
soccer sold至 0.5; - 上下文过长导致截断:采用摘要式检索,先压缩 chunk 文本再喂给大模型。
六、项目集成与落地适配
(一)功能开关实现代码
# 核心功能:RAG功能开关控制,适配不同项目场景
from demo_05_assemble_generate import generate_answer
from langchain.chat_models import ChatOpenAI
# 全局开关:控制是否启用RAG
USE_RAG = True # True启用,False禁用
# 直接调用大模型(禁用RAG时使用)
def direct_answer(query):
llm = ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0.3,
api_key="your-api-key",
base_url="your-base-url"
)
return llm.predict(f"请回答以下问题:{query}")
# 统一问答入口
def qa_entry(query):
if USE_RAG:
print("【启用RAG模式】")
return generate_answer(query)
else:
print("【禁用RAG模式,直接调用大模型】")
return direct_answer(query)
if __name__ == "__main__":
# 测试不同场景
print("=== 数学项目(启用RAG)===")
USE_RAG = True
print(qa_entry("lim(sinx/x)的结果是什么"))
print("\n=== 情绪项目(禁用RAG)===")
USE_RAG = False
print(qa_entry("怎么安慰心情不好的人"))
(二)项目适配场景
1. 高度适配场景
- 专业知识问答(如数学解题):将高数教材 / 公式库存入
knowledge.txt,通过 RAG 精准答疑并溯源; - 小众信息检索(如小众景点、专业手册):补充大模型知识盲区,提高答案准确性;
- 企业内部文档查询(如规章制度、技术手册):支持快速更新,员工检索高效且可追溯。
2. 不推荐场景
- 情绪交互类产品:RAG 无法模仿人性化表达,建议用 "提示词约束 + 微调" 替代;
- 大众信息查询(如热门景点、通用常识):大模型原生知识足够,RAG 会增加召回耗时,降低响应速度。
七、总结
(一)核心
- RAG 的核心是 "两条流水线协同":离线构建高质量知识库(切分 + 向量化),在线精准检索 + 约束生成,二者缺一不可;
- 参数调试是落地关键:通过单一变量法 + 二分法试错,无固定最优值,需结合知识库体量和业务场景调整;
- 场景适配决定效果:RAG 并非 "万能",仅在 "高精度、需追溯" 场景中优势明显,需通过功能开关灵活适配。
(二)总结
- RAG 核心是 "离线构建知识库 + 在线检索生成" 两条流水线,代码层面需重点关注文档切分、向量存储、参数过滤三大核心环节;
- 关键参数(chunk_size、TOPK、score_threshold)需通过单一变量法调试;
- 项目落地时需通过功能开关适配不同场景,高精度 / 可追溯场景启用 RAG,通用交互场景直接调用大模型更高效。