news 2026/4/18 15:23:36

【程序员必备】告别“人工智障“:用Python搭建RAG知识库,让大模型不再“胡说八道“!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【程序员必备】告别“人工智障“:用Python搭建RAG知识库,让大模型不再“胡说八道“!

上次我微调 0.5B 模型的时候,也发现了,一些“事实”,例如“免费额度 15GB”大模型学习不进去,总是回答“10GB”,要反复微调才能够答到“15GB”。而这种问题,除了从微调入手,最好的做法是用 RAG(检索增强生成)技术。

两种技术分工情况:

  • 微调(Fine-Tuning):改变模型的表达方式或推理方式
  • RAG:给模型提供一个可以参考的资料库

之前本人曾经用 Ragflow(类似 Dify) 实践过 RAG 知识库,这次我想更侧重开发流程以及调优,这次用 python 原生实现。

一、RAG 文档准备阶段(Knowledge Base / Knowledge Ingestion)

先准备知识文件knowledge_base.txt,内容只有一条(暂时这样,先校验功能的完整性):

怎么安装 SDK?`pip install feiyue-sdk` 即可完成安装。

生成知识向量库的 python 代码:

import osfrom langchain_text_splitters import RecursiveCharacterTextSplitterfrom langchain_huggingface import HuggingFaceEmbeddingsfrom langchain_chroma import Chromafrom langchain_community.document_loaders import DirectoryLoader, TextLoader# 配置向量存储目录VECTOR_DB_PATH = "./vector_store"DATA_PATH = "./data"def ingest_local_data(vector_db_path=VECTOR_DB_PATH, data_path=DATA_PATH): ifnot os.path.exists(data_path): print(f"❌ Error: {data_path} not found.") return # 1️⃣ 初始化 Embeddings embeddings = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", model_kwargs={'device': 'cpu'} # 可改为 'cuda' 或 'mps' ) # 2️⃣ 初始化向量数据库 vector_db = Chroma( persist_directory=vector_db_path, embedding_function=embeddings ) # 3️⃣ 加载文档 loader = DirectoryLoader(data_path, glob="**/*.txt", loader_cls=TextLoader) docs = loader.load() # 4️⃣ 文档切分 splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=100) final_docs = splitter.split_documents(docs) # 5️⃣ 入库 vector_db.add_documents(final_docs) print(f"✅ Ingested {len(final_docs)} chunks from {data_path}")if __name__ == "__main__": ingest_local_data()

这是 RAG 的文档准备阶段(Knowledge Base / Knowledge Ingestion):

  1. 读取./data/*.txt文件
  2. 切分成 chunk,最大块大小 600(字符),相邻 chunk 重叠字符 100(保持上下文连续性)
  3. 使用 embedding 模型(这里用BAAI/bge-small-zh-v1.5)将文本块转换为向量,向量计算设备我使用 cpu(M1 Mac 上跑小模型,先这样跑着吧)
  4. 向量库持久化到./vector_store

你可能认为我没有定义切分的字符,但是看看源码里,其实默认是有一定的默认符号的。

# langchain_text_splitters.character.RecursiveCharacterTextSplitter.__init__def __init__( self, separators: list[str] | None = None, keep_separator: bool | Literal["start", "end"] = True, # noqa: FBT001,FBT002 is_separator_regex: bool = False, # noqa: FBT001,FBT002 **kwargs: Any,) -> None: """Create a new TextSplitter.""" super().__init__(keep_separator=keep_separator, **kwargs) self._separators = separators or ["\n\n", "\n", " ", ""] self._is_separator_regex = is_separator_regex

运行结果图:

生成一个chroma.sqlite3文件,以及一部分的 bin 二进制文件,用数据库连接工具,可以看到这个数据库的内容。这些其实就是存放向量,以及各种元数据的地方了。

🚩 到这里,我们 RAG 的前期“知识库准备”就 OK 了。后续我们可以对其进行 QA 问答,看看其是否能够回答知识库的内容。

二、RAG 知识检索阶段(Retrieval / Vector Search)

import osimport torchfrom modelscope import snapshot_downloadfrom transformers import AutoModelForCausalLM, AutoTokenizer, pipelinefrom langchain_core.prompts import ChatPromptTemplatefrom langchain_huggingface import HuggingFacePipelinefrom langchain_huggingface import HuggingFaceEmbeddingsfrom langchain_chroma import Chromafrom langchain_classic.chains.combine_documents import create_stuff_documents_chainfrom langchain_classic.chains import create_retrieval_chainVECTOR_DB_PATH = "./vector_store"BASE_MODEL_ID = "Qwen/Qwen2.5-0.5B-Instruct"class RAGService: def __init__(self, vector_db_path=VECTOR_DB_PATH): self.vector_db_path = vector_db_path # 设备选择 if torch.cuda.is_available(): self.device = "cuda" elif torch.backends.mps.is_available(): self.device = "mps" else: self.device = "cpu" print(f"🖥️ Using device: {self.device}") # Embeddings self.embeddings = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", model_kwargs={'device': self.device} ) # LLM self.llm = self._load_model() # 向量库 self.vector_db = Chroma( persist_directory=self.vector_db_path, embedding_function=self.embeddings ) def _load_model(self): print("📥 Downloading/Loading Model...") model_dir = snapshot_download(BASE_MODEL_ID) tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_dir, torch_dtype="auto", device_map="auto", trust_remote_code=True ) pipe = pipeline( "text-generation", model=model, tokenizer=tokenizer, max_new_tokens=512, temperature=0.1, return_full_text=False ) return HuggingFacePipeline(pipeline=pipe) def get_chain(self): system_prompt = ( "你是一个专业的助手。请仅根据提供的上下文(Context)回答问题。" "如果你在上下文中找不到答案,请诚实告知。回答请简明扼要。" "\n\n上下文: {context}" ) prompt = ChatPromptTemplate.from_messages([ ("system", system_prompt), ("human", "{input}"), ]) question_answer_chain = create_stuff_documents_chain(self.llm, prompt) return create_retrieval_chain( self.vector_db.as_retriever(search_kwargs={"k": 3}), question_answer_chain )if __name__ == "__main__": service = RAGService() rag_chain = service.get_chain() user_input = "安装飞跃云SDK的指令是什么?" response = rag_chain.invoke({"input": user_input}) print("\n🤖 AI Answer:\n", response["answer"]) print("\n📄 Sources used:", [d.metadata.get('source') for d in response["context"]])

这里涉及到 RAG 检索的步骤,主要步骤如下:

  1. 问题向量化:用户输入问题 → embedding 模型(这里是BAAI/bge-small-zh-v1.5,也就是原来的 embedding 模型) → 得到query 向量(用于相似度检索)。
  2. 相似度搜索:在向量数据库中搜索与query向量最相似(相似度匹配)的文档块。常用相似度匹配方法有余弦相似度(cosine similarity)、内积(dot product),通常检索top-k(返回相似度最高的 k 个文档)文档块。

检索成功,成功输出pip install feiyue-sdk,以及溯源到data/input/knowledge_base.txt

🚩 到这里,我们成功跑通了 RAG 知识库的检索(或者说召回),LLM 用知识库的内容回答了我们的问题。

三、用 RAG 构建个人知识库 - 从 0 到 1 跑通

上面我们将 RAG 的文档准备、检索的步骤都跑通了,这次我们要进行实践,并且解决其中遇到的难题。

这次,我们对上次的 RAG 程序,改动model_kwargs={'device': 'mps'}用 Mac 显卡 mps 计算。我们将我本地的.md文档切分为 chunk,然后向量化。

🚩 切分与向量化成功!发现执行后一共有 5433 个 chunk。接着,我们要执行检索。

🚩 检索成功,回答基本正确,并且可以看到 RAG 成功溯源到原文件,显示了原文件名。

虽然原文秩后续从 8 改为了 4,这里没有答好,但是用BAAI/bge-small-zh-v1.5这个小 embedding 模型,加上小模型Qwen/Qwen2.5-0.5B-Instruct,这个组合能够达到这个效果已经相当不错。

四、RAG 检索信息偏移的修复 - chunk 探秘 & 模型理解修复

接下来,我们要探究如何修复刚刚回答信息偏移的问题,明明我们文章说的“r = 4”,但是召回的信息是“r = 8”。

我们进入向量数据库一探究竟,看看 chunk 的切分情况,可以查看embedding_metadata这个表,它存放着 chunk 的元数据信息(向量化就不是人看的了,所以只能看看元数据),这里面有 chunk 切分后的内容。

create table main.embedding_metadata( id INTEGER references main.embeddings, -- 向量(embedding)的唯一 ID,外键,指向 embeddings 表中的一条向量记录 key TEXTnotnull, -- 元数据的键(metadata key),目前只有 chroma:document 、source 两种 string_value TEXT, -- 当元数据值是字符串类型时使用 int_value INTEGER, -- 当元数据值是整数类型时使用 float_value REAL, -- 当元数据值是浮点数类型时使用 bool_value INTEGER, -- 当元数据值是布尔类型时使用(通常 0=false, 1=true) primary key (id, key) );

这里的设计要注意,同一个 id 属于同一个向量(embeddings表的一条记录),而不是同一个元数据(embedding_metadata),也就是说这个表 id 不是唯一的,primary key (id, key)限定的是 id + key 唯一,这里我们就明白了这个表设计的含义了,就是向量的元数据(废话,但有助于理解)。

我们现在去看看我们溯源的那个文件,也就是上篇文章的地方,sql 如下:

select count(*) from embedding_metadata;select *from embedding_metadatawhere id in (select id from embedding_metadata where embedding_metadata.string_value like '%低成本 LLM 微调实录:M1 Mac 上跑通 0.5B 小模型 LoRA 微调全流程.md');

还记得我们之前拿到 5433 个 chunk 吗,这里元数据有 10868,刚好是两倍,还有 2 是上篇文章的一个文件切分的 1 个 chunk 生成的 2 个元数据信息。一个 chunk 的两个元数据,分别是sourcechroma:document,分别代表源文件名称,以及 chunk 的文本内容。我们甚至可以观察到,我们设置的splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=100),确实在这里可以看到chunk_size块大小 600 字符,chunk_overlap重叠文本为 100 字符的证据。

所以,我们要看到底检索到了哪些 chunk,以及根据这些 chunk,LLM 是否有可能得到 r 为 4 而不是 8。

改动以下代码,让其能够打印出d.id这些embedding_id

if __name__ == "__main__": service = RAGService() rag_chain = service.get_chain() user_input = "我的低成本 LLM 微调用了哪个模型?秩用了多少?" response = rag_chain.invoke({"input": user_input}) print("\n🤖 AI Answer:\n", response["answer"]) print("\n📄 Sources used:", [d.id for d in response["context"]])

根据响应里的embedding_id,可以查询到 chunk 元数据。

select *from embedding_metadatawhere id in (select id from embeddings where embedding_id in ('db3167c3-8607-40b2-b415-fcfb3f38e6fc', 'cb1f9a64-abfe-4469-bc1e-a92708190fa5', '85571f4a-68cc-4654-a491-4b413aa7c149'));

查看之后,发现,这几条(id 为 768、780、795)中只有一条(795)有关于秩的值的描述:

而且,r (秩)也可能有关系,秩从 8 改为 4 可能会比较容易训练,毕竟 LoRA 的论文中也支持用小 r 也能产生很好的效果。

这里的描述的语义不是很明确,需要语文理解能力,那个小模型得不到正确的信息也情有可原(可以考虑换大一点的模型),而这里的向量模型反而是没有问题的。

尝试调整温度temperature,这个值目前是 0.1,可能模型太过“严肃”,所以没有理解到我这句话的“艺术性”。

🚩 竟然成功了!我们通过调整温度,让模型理解了得到了正确的回答。

不过存在问题:

  • 幻觉:回答中夹带着一些“幻觉”,有噪声。
  • 过拟合的风险:这次的测试的参数,仅针对我问的这个问题,有可能产生“过拟合”问题,对其他问题的效果可能没有那么好。(过拟合可以理解为函数完美穿过了当前问题点,导致偏离了大部分点)

先调到 0.3 温度,让 RAG 回答我当前问题到达一个还不错的水平,然后再多问几个问题测试下。

这里有可能的隐藏坑点:

  1. 这个 k(Amount of documents to return)我设置为了 3,比较小,可能会导致检索出来的内容较少,有可能会难以形成“正确的答案”。但是 k 太大,我的小模型可能承受不住庞大的上下文。
  2. RAG 的反向能力很弱,问“我不会什么”比“我会什么”要难以真正的答到点上,因为是基于检索出 topK 条目然后再整合成自然语言的。

你在搭建个人知识库时遇到了哪些‘人工智障’时刻?欢迎评论区交流。

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 8:33:40

《企业AI落地实战白皮书:从培训到获客的全链路解决方案》

—— 基于九尾狐AI 20年实战经验的方法论拆解第一章:行业困局与趋势一、企业AI转型的“冰火两重天”当前,AI技术已成为驱动企业增长的核心引擎,但企业AI培训市场却呈现“冰火两重天”的尴尬局面:一方面,87%的企业决策者…

作者头像 李华