一句话概括:先检索,再生成。
用户提问 → 从知识库里找出最相关的几段文字 → 把这些文字和问题一起交给大模型 → 模型基于"给定材料"作答。
它把"记忆"这件事从模型参数里拆了出来,交给一个可随时更新的外部知识库。好处是显而易见的:
代价是:检索质量决定了上限。检索不到,再强的模型也答不对。
一个最小可用的 RAG 系统只有四个环节:
听起来很唬人,实际上核心代码不到一百行。下面我们逐一实现。
切分的目标是"每块语义完整、长度适中"。太小会丢失上下文,太大会稀释相关性。实践中 300~800 字符是比较稳妥的区间。
def split_text(text, chunk_size=500, overlap=50):
"""按字符切分,overlap 让相邻块有重叠,避免语义被切断"""
chunks, start = [], 0
while start < len(text):
end = start + chunk_size
chunks.append(text[start:end])
start = end - overlap # 回退一段,保证连续性
return [c for c in chunks if c.strip()]一个细节:中文按字符切、英文按词切,效果差异很大。如果文档结构清晰(有标题、段落),优先按段落切,再用长度做兜底。
这里为了不引入重型依赖,我们用最朴素的方式演示——真实项目里把 embed() 换成 OpenAI、BGE 或任意 embedding 模型的调用即可,逻辑完全一致。
import numpy as np
def embed(text: str) -> np.ndarray:
"""示意:真实场景替换为 embedding API 调用"""
vec = np.array([hash(text[i:i+3]) % 1000 for i in range(0, min(len(text), 300), 3)],
dtype=float)
return vec / (np.linalg.norm(vec) + 1e-9)
def retrieve(query, chunks, k=3):
q = embed(query)
scored = []
for c in chunks:
v = embed(c)
n = min(len(q), len(v))
scored.append((float(np.dot(q[:n], v[:n])), c)) # 余弦相似度
scored.sort(reverse=True, key=lambda x: x[0])
return [c for _, c in scored[:k]]生产环境中,向量库(FAISS、Chroma、Milvus 等)负责管理索引和近似最近邻搜索,这里用列表循环是为了让流程一眼看清。
这一步的关键是提示词设计:明确告诉模型"只依据材料回答,找不到就说不知道"。这一句话能挡掉大部分幻觉。
def ask(query, chunks, llm):
context = "\n---\n".join(retrieve(query, chunks))
prompt = f"""请仅根据下面的资料回答问题,资料中没有的信息请回答"资料中未提及"。
资料:
{context}
问题:{query}
"""
return llm(prompt)至此,一个最小 RAG 就闭环了。llm 可以是任何模型接口,换成官方 SDK 的 chat.completions.create 调用即可。
跑通之后,真正的挑战才开始。以下几个问题几乎每个项目都会遇到:
1. 检索不准,答非所问。 最常见的原因是切分不合理。标题和正文被切开、表格被打散、上下段落失去关联,都会让检索失效。解决办法是给每个片段加上来源标题作为"上下文前缀",检索命中的概率会明显提升。
2. 相似度高分,但内容无关。 纯向量检索对"关键词精确匹配"不敏感,比如产品型号、人名。实践中常用混合检索:向量召回 + BM25 关键词召回,再融合排序,效果比单一路径稳定得多。
3. 上下文塞太多,反而变差。 不是召回越多越好。无关片段会干扰模型判断,还挤占上下文窗口。一般 3~5 个片段足够,宁可精不要多。
4. 多轮对话丢失上下文。 用户追问"那第二步呢",单独拿这句话去检索必然失败。需要先让模型把追问改写成完整问题,再进入检索。
5. 没有评估,全靠感觉。 建议准备 20~30 条真实问题作为测试集,记录"检索命中率"和"答案正确率"。没有评估,优化就是在盲猜。
RAG 不是万能钥匙。如果你的任务是改变模型的表达风格、语气或固定格式,那更适合微调;如果知识总量很小(几页纸),直接塞进提示词反而更简单可靠;如果需求是复杂的多步推理、跨文档计算,单轮检索就不够了,需要考虑 Agent 或工作流编排。
判断标准其实很简单:问题是"缺知识"还是"缺能力"。缺知识用 RAG,缺能力用微调或更强的模型。
RAG 的门槛比想象中低——四个环节、不到百行代码就能跑通一个 demo。但它真正的难度在于后半程:切分策略、混合检索、重排序、评估体系,每一项都影响最终体验。
如果你准备动手,建议的路径是:先用几十行代码跑通最小闭环,拿真实文档试一遍,感受一下"哪里答得不对",然后再针对性地优化某一个环节。比起一上来就搭复杂架构,这种"先跑起来再打磨"的方式,往往走得更远。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。