项目:RAG+微调综合系统
本节定位
前面你已经分别学过:
- RAG:让模型先查资料再回答
- 微调:让模型更适应某类任务或风格
这一节要解决的问题是:
如果一个领域系统既需要外部知识,又需要特定表达风格和任务能力,该怎么办?
这时,RAG 和微调往往不是替代关系,而是组合关系。
学习目标
- 理解为什么“只做 RAG”或“只做微调”有时都不够
- 学会把领域问答系统拆成 RAG 层和微调层
- 设计一个可解释的 RAG+微调项目方案
- 跑通一个最小的组合式项目骨架
一、为什么要把 RAG 和微调组合起来?
1.1 单独 RAG 的优点和局限
RAG 的优点:
- 知识可更新
- 可引用来源
- 不必重新训练模型
但它也有局限:
- 模型未必懂你的领域表达
- 检索到了也未必会答得符合业务格式
- 复杂任务时,模型的“回答习惯”未必够稳
1.2 单独微调的优点和局限
微调的优点:
- 能让模型更懂特定任务形式
- 输出风格更稳定
- 指令跟随更贴合业务
但它也有局限:
- 新知识更新没那么灵活
- 很难靠微调记住所有细节文档
- 成本更高
1.3 所以它们经常是互补关系
可以先用一句话记住:
RAG 负责补知识,微调负责补行为。
这正是组合式系统的核心逻辑。
二、这个项目到底在做什么?
我们把目标定成一个领域问答助手,比如:
- 面向企业内部政策文档
- 回答时要稳定引用来源
- 输出格式必须规范
- 某些问题需要用固定业务口径回答
也就是 说,这个系统既要:
- 查得到知识
- 又要答得像该领域系统
三、先画出系统结构
3.2 这张图真正重要的地方
不是“组件多”,而是职责清楚:
- 检索器负责找资料
- 微调模型负责按业务方式组织答案
这能让系统更可解释,也更容易迭代。
四、一个最小知识库和检索器
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
kb = [
{"id": "doc1", "text": "退款政策:购买后 7 天内且学习进度低于 20% 可退款。"},
{"id": "doc2", "text": "证书政策:完成项目并通过测试后可获得证书。"},
{"id": "doc3", "text": "客服处理规范:回答时需要先说明政策依据,再给出结论。"}
]
vectorizer = TfidfVectorizer(token_pattern=r"(?u)\\b\\w+\\b")
doc_vectors = vectorizer.fit_transform([item["text"] for item in kb])
def retrieve(query, top_k=2):
query_vec = vectorizer.transform([query])
scores = cosine_similarity(query_vec, doc_vectors)[0]
top_idx = scores.argsort()[::-1][:top_k]
return [kb[i] for i in top_idx]
print(retrieve("退款条件是什么"))
这个检索器本身不复杂,但它已经是组合系统的第一半。
五、再模拟一个“微调后的回答风格”
在真实项目里,这一步可能来自:
- 指令微调
- LoRA / QLoRA
- 监督数据集训练
为了让代码能直接运行,这里我们先用规则模拟“已经被训练过的业务输出风格”。
def domain_answer_style(question, retrieved_docs):
evidence = " ".join(doc["text"] for doc in retrieved_docs)
if "退款" in question:
return {
"answer": "根据现行退款政策,购买后 7 天内且学习进度低于 20% 的用户可申请退款。",
"reasoning_style": "先政策后结论",
"evidence": evidence
}
if "证书" in question:
return {
"answer": "根据证书政策,完成项目并通过测试后可以获得证书。",
"reasoning_style": "先政策后结论",
"evidence": evidence
}
return {
"answer": "当前没有找到足够匹配的业务规则。",
"reasoning_style": "谨慎拒答",
"evidence": evidence
}
5.2 为什么这个模拟是有意义的?
因为它在帮你理解:
- RAG 解决的是“知道什么”
- 微调解决的是“怎么答”
六、把两部分真正串起来
def rag_plus_finetune_system(question):
docs = retrieve(question, top_k=2)
result = domain_answer_style(question, docs)
return {
"question": question,
"retrieved_docs": docs,
**result
}
result = rag_plus_finetune_system("退款条件是什么?")
print(result["question"])
print(result["answer"])
print("evidence:", result["evidence"])