当前位置: 首页 > news >正文

AI: 告别过时信息, 用RAG和一份PDF 为LLM打造一个随需更新的“外脑”

嘿,各位技术同学!今天,我们来聊一个大家在使用大语言模型(LLM)时都会遇到的痛点:知识过时

无论是像我一样,用 Gemini Pro 学习日新月异的以太坊,还是希望它能精确掌握某个特定工具的最新用法,我们都可能被它“记忆”里的旧知识所困扰。比如,我让它生成 PlantUML 图时,它偶尔会“穿越”回过去,使用一些已经被废弃的旧语法,这无疑增加了我们的学习和开发成本。

为了解决这个问题,我动手开发了一个小项目:一个基于**检索增强生成(RAG)**的智能问答系统。它能读取最新的官方文档(比如 PlantUML 的语言参考指南),并让 Gemini 模型在回答问题前,先查阅这些最新资料。今天,我就和大家分享这个项目的技术实现,希望能给大家带来一些启发。
在这里插入图片描述

项目核心理念:RAG如何解决问题?

在我们深入代码之前,先花一分钟理解一下 RAG 的工作原理。

想象一下,我们让一位非常聪明的实习生(LLM)写一份报告,但他对某个特定领域的最新进展不太了解。我们会怎么做?我们不会让他凭空想象,而是会把最新的行业报告、研究论文(外部知识库)丢给他,说:“看,根据这些资料来写。”

RAG 就是这个过程的自动化版本。它通过一个“检索”步骤,从我们的知识库(PDF、文档、网站等)中找到与用户问题最相关的信息,然后将这些信息连同原始问题一起打包,交给 LLM,让它基于这些“新鲜出炉”的材料来“生成”答案。

这个流程可以用下面的 PlantUML 图清晰地表示:
在这里插入图片描述

技术实现深度解析

现在,让我们深入 main.py 文件,看看这个 RAG 系统是如何一步步构建起来的。

第一步:知识库的建立与“保鲜”

万丈高楼平地起,我们的第一步是建立一个可靠的知识库。在这个项目中,知识来源于一份PDF文档。但我们做了一个非常实用的优化:自动检测文档更新

代码通过 get_pdf_hash 函数计算 PDF 文件的 SHA256 哈希值。

def get_pdf_hash(file_path):"""计算文件的 SHA256 哈希值"""sha256_hash = hashlib.sha256()with open(file_path, "rb") as f:for byte_block in iter(lambda: f.read(4096), b""):sha256_hash.update(byte_block)return sha256_hash.hexdigest()

load_or_create_vectorstore 函数中,系统启动时会检查持久化目录 chroma_db 中存储的旧哈希值与当前 PDF 文件的哈希值是否一致。

  • 如果一致:太棒了!说明文档没变,直接从磁盘加载已经处理好的向量数据库,秒级启动。
  • 如果不一致或首次运行:说明知识需要更新!系统会自动删除旧的数据库,然后使用 PyPDFLoader 加载新的 PDF,并用 RecursiveCharacterTextSplitter 将其分割成更小的、易于检索的文本块。

这个设计非常关键,它确保了我们的“外脑”永远存储的是最新信息,同时避免了每次运行都重复处理文档的昂贵开销。

第二步:将知识向量化 (Embedding)

计算机不理解文字,只理解数字。为了让机器能够“理解”并比较文本片段的相似度,我们需要将它们转换成向量,这个过程叫做嵌入(Embedding)

我们使用了 HuggingFace 的 sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 模型。这个模型能将文本块转换成一个包含几百个数字的向量,神奇的是,语义上相似的文本块,其对应的向量在多维空间中的距离也更近。

def setup_embeddings():"""初始化并返回嵌入模型"""print("正在初始化嵌入模型...")embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL_NAME,model_kwargs={'device': 'cpu'},encode_kwargs={'normalize_embeddings': False})return embeddings

所有从 PDF 中分割出来的文本块都会经过这个模型处理,然后连同它们的向量表示一起存入 Chroma 向量数据库中。

第三步:搭建检索与生成的核心链

现在,我们有了存储知识的数据库和负责回答问题的 LLM,是时候把它们“链接”起来了。这里我们借助了 LangChain 框架的 RetrievalQA 链。

def create_qa_chain(llm, vectorstore):"""基于LLM和向量数据库创建并返回问答链"""retriever = vectorstore.as_retriever(search_kwargs={"k": 2})qa_chain = RetrievalQA.from_chain_type(llm=llm,chain_type="stuff",retriever=retriever,return_source_documents=True)return qa_chain

这个链条的工作流程如下:

  1. 设置检索器(Retriever)vectorstore.as_retriever(search_kwargs={"k": 2}) 这行代码创建了一个检索器,当用户提问时,它会去 Chroma 数据库中检索出最相似的 k=2 个文档片段。
  2. 选择链类型(Chain Type)chain_type="stuff" 是一种简单直接的策略。它会把检索到的所有文档片段(stuff them)一股脑地塞进一个提示词模板中,和用户的原始问题一起发送给 LLM。
  3. 返回源文档return_source_documents=True 是一个非常有用的调试和验证工具。它让 LLM 在给出答案的同时,也告诉我们它参考了哪些原文片段。这大大增强了结果的透明度和可信度。

实践与效果

一切准备就绪后,main 函数将所有部分串联起来,并用一个具体问题来测试系统:

def main():"""主执行函数,编排整个RAG流程"""embeddings = setup_embeddings()vectorstore = load_or_create_vectorstore(PDF_FILE_PATH, PERSIST_DIRECTORY, embeddings)llm = setup_llm()qa_chain = create_qa_chain(llm, vectorstore)query = "如何绘制 JSON 数据图?"ask_question(qa_chain, query)

当运行这个脚本时,它会:

  1. 初始化模型和向量数据库。
  2. 接收问题 如何绘制 JSON 数据图?
  3. 在数据库中找到关于 JSON 数据可视化的相关片段。
  4. 将这些片段和问题一起发给 Gemini。
  5. Gemini 基于最新的 PlantUML 语法(来自PDF)生成答案,并附上参考来源。

这样,我们就得到了一个既有 Gemini 强大推理能力,又有最新、最准确领域知识的 AI 助手。

实用建议与未来展望

这个项目虽然简单,但扩展性很强:

  1. 更换知识源:我们可以轻易地将 PDF_FILE_PATH 换成任何我们想要的 PDF 文档,比如以太坊的白皮书、某个开源库的API文档,甚至是我们的学习笔记。
  2. 支持更多格式:通过使用 LangChain 提供的其他 DocumentLoader,我们可以让系统读取 .txt, .md 文件,甚至直接爬取网站内容。
  3. 部署为服务:使用 FastAPI 或 Flask 将这个问答系统包装成一个 API 服务,让其他人也能方便地使用。
  4. 优化检索:对于非常大的文档,可以探索更高级的检索策略,如 Parent Document Retriever,以获得更相关的上下文。

结论

通过 RAG,我们为大语言模型安装了一个可随时更新、可定制的“外脑”。这个方法不仅有效解决了 LLM 知识滞后的问题,还通过引入可验证的信源,大大提高了生成内容的可信度。最棒的是,实现这一切的技术门槛并不高。

希望这篇文章能激励大家动手尝试,为自己的学习和工作场景打造一个专属的、永远不会“过时”的AI伙伴。
https://github.com/xilu0/plantuml-agent/blob/main/main.py

http://www.dtcms.com/a/304621.html

相关文章:

  • go install报错: should be v0 or v1, not v2问题解决
  • React图标库推荐与选择建议
  • 【Spring-cloud-OpenFegin源码解析】
  • VitePress学习笔记
  • 编程算法在金融、医疗、教育、制造业的落地应用。
  • 云服务器上基于lora微调Qwen2.5-VL-7b-Instruct模型之Lora微调代码讲解
  • Netty中InternalThreadLocalMap的作用
  • Rust实现GPU驱动的2D渲染引擎
  • Vue3 学习教程,从入门到精通, Vue3 自定义指令语法知识点及案例(20)
  • c++ nlohmann/json读写json文件
  • JavaWeb学习打卡18(JDBC案例详解)
  • ansible 使用更高版本的python版本
  • Python中的决策树机器学习模型简要介绍和代码示例(基于sklearn)
  • 【牛客网C语言刷题合集】(五)——主要二进制、操作符部分
  • GO 开发环境安装及配置
  • Claude Code 使用教程(对接智谱模型)
  • 84、【OS】【Nuttx】【启动】栈溢出保护:asm 关键字(下)
  • SpringBoot集成Quzrtz实现定时任务
  • 【目标检测】小样本度量学习
  • 记录一个TI DSP编译器的Bug
  • CentOS安装ffmpeg并转码视频为mp4
  • 预过滤环境光贴图制作教程:第四阶段 - Lambert 无权重预过滤(Stage 3)
  • 预过滤环境光贴图制作教程:第一步 - HDR 转立方体贴图
  • Android Compose 自定义组件完全指南
  • 对College数据进行多模型预测(R语言)
  • 《React与Vue构建TODO应用的深层逻辑》
  • 【lucene】SegmentCoreReaders
  • linux_前台,后台进程
  • LeetCode热题100——155. 最小栈
  • (LeetCode 面试经典 150 题) 150. 逆波兰表达式求值 (栈)