用最通俗易懂的方式来理解RAG,那就是从数据库查资料,把查到的资料拿出来,然后写进Prompt里面,最后交给LLM。
一、RAG到底在做什么
RAG是Retrieval-Augmented Generation,通常译为“检索增强生成”。我的理解是:先从外部资料中检索与问题相关的内容,再把这些内容连同问题交给生成模型。外部资料是回答是提供的上下文,不需要把这几条重新训练进模型参数。
二、实现最简RAG项目:环境准备
##powershell## mkdir rag_practice cd rag_practice notepad rag.py创建文件夹并且进入,然后创建一个Python的记事本脚本,只要你本地电脑中已经有Python就足够了,不需要再设置虚拟环境,因为接下来这段代码不需要安装其他Python的包,只需要Python自带的内容就可以完成了。
三、项目代码
第一步:建库
人工构造 3 条文档片段,手动标注 id、关键词元数据,直接生成可用的文档列表。
documents = [ { "id": "D1", "keywords": ["RAG", "检索增强生成"], "text": "RAG 先检索相关资料,再让模型根据资料回答问题。", }, { "id": "D2", "keywords": ["切分", "分块"], "text": "文本切分是把长文档拆成小段,方便找到相关内容。", }, { "id": "D3", "keywords": ["Agent", "智能体"], "text": "Agent 可以根据任务决定调用哪个工具。", }, ] print(f"已准备 {len(documents)} 条资料")这是极简内容,在完整项目中这段内容模拟建库,建立企业级数据库需要下列流程:
1、文档加载(统一输出结构):文本可能来自不同类型的文件
2、文本清洗和结构化解析(去除水印、空行、乱码、统一编码等):真实文件存在大量噪声
3、文本切分(解决检索匹配问题):块太大→检索匹配不准;块太小→上下文语义断裂
4、元数据增强(记录重要数据):检索时可做元数据过滤(比如只搜某份文件、某类主题),回答时可溯源到具体页码
建库时候Embedding模型
建库时: D1 的 text → Embedding 模型 → 向量 v1 D2 的 text → Embedding 模型 → 向量 v2 D3 的 text → Embedding 模型 → 向量 v3 提问时: “怎样让模型先查资料再回答?” → Embedding 模型 → 问题向量 q → 比较 q 与 v1、v2、v3 的相似度 → 取回最相关资料的原文第二步:接受用户问题
这段代码接收用户输入的问题,去除空格,并将内容转变为小写。
question = input("请输入问题:").strip() normalized_question = question.lower()在真实项目中,用户内容可能来自网页聊天框或者接口请求,可能还需要将问题处理为向量。
第三步:检索相关内容
这里实现的是手工标签的词面匹配,没有计算相关性分数。
retrieved_docs = [] for document in documents: for keyword in document["keywords"]: if keyword.lower() in normalized_question: retrieved_docs.append(document) break在真实项目中用 Embedding 模型把每个片段转换成向量,并让向量与原文、来源保持关联。
所以Embedding模型的职责是帮助找经过处理向量数据库中的关联向量。
第四步:查看检索结果,处理空结果
打印命中了哪些资料,便于检查检索是否符合预期。若一条也没找到,就结束程序,避免把空资料交给模型。
print(f"检索到 {len(retrieved_docs)} 条资料") for document in retrieved_docs: print(f"[{document['id']}] {document['text']}") if not retrieved_docs: print("没有检索到相关资料,暂不生成回答。") raise SystemExit第五步:把资料整理成上下文
把每条命中资料的编号和原文放在一起,再用换行符连接。最终的context就是准备提供给模型的参考资料。
context_lines = [] for document in retrieved_docs: context_lines.append(f"[{document['id']}] {document['text']}") context = "\n".join(context_lines)系统可能取回多个片段,连同文件名、页码等来源一起整理。同时需要控制资料长度、减少重复内容,避免把大量无关片段放进模型输入。
第六步:构造Prompt
把回答要求、检索资料和用户问题放在同一个 Prompt 中。原本模型只看到问题;现在还能看到我们检索出的资料。
prompt = f"""请只根据下面的资料回答问题。 如果资料不足,请回答“资料不足,无法回答”。 回答后标出所用资料编号,例如 [D1]。 资料: {context} 问题:{question} 回答:"""所以我们总结一下,用最通俗易懂的方式来理解RAG,那就是从数据库查资料,把查到的资料拿出来,然后写进Prompt里面,最后交给LLM。
写进Prompt、写进Prompt、写进Prompt(重要的东西说三遍)
最重要的地方就是把内容写进Prompt也可以说是写进提示词工程中。