游乐游手机版
首页/AI热点日报/热点详情

RAG应用进阶指南 构建可分离可维护的动态AI知识库

类型:热点整理2026-07-20
RAG系统通过架构解耦实现知识注入端与应用端分离,利用FAISS和元数据存储实现知识库的增删查改及内容追溯,支持多租户隔离,具备动态管理和可维护性,提升系统灵活性与扩展性。

你是否已经使用 LangChain 搭建了第一个 RAG 应用?很棒!但当你真正把它推向实际场景时,一些“现实”问题会很快暴露出来:

  • 数据更新太麻烦:每次有新文档,难道都要重新运行整个程序,重建一次向量数据库?
  • 数据与应用耦合紧密:数据处理和模型调用的代码混在一起,维护起来像一团乱麻。
  • 无法追溯原文来源:我怎么知道向量数据库里的某个向量,对应的是哪篇文档的哪句话?想删掉都找不到入口。
  • 多用户场景如何应对:如果想给不同客户或部门使用,如何确保他们只能看到自己的知识,互不干扰?

如果你遇到了以上任何一个问题,那恭喜你——你正在从“玩具项目”向“真正可用的产品”迈进。

今天这篇文章,就是要帮你把这个烦恼连根拔起。我们将重新设计 RAG 架构,实现数据处理AI 应用的优雅分离,并赋予知识库完整的生命周期管理(CRUD)能力。

架构升级——从“一体机”到“插拔式”

告别把所有代码都写在一起的“一体机”模式吧。一个成熟的 RAG 系统应该拆分为两个核心部分:

  1. 知识注入与管理端(数据枢纽):这是一个独立的模块,专门负责接收文档、处理数据、更新向量数据库。你可以定期运行它,也可以在需要时手动触发。
  2. RAG 应用端(AI 应用):这是面向用户的部分。它只负责加载一个已经建好的知识库,并执行检索和生成任务。

好处不言而喻:解耦、可维护、可扩展

构建你的“知识管理中心”

核心思路是:我们不仅需要一个向量数据库(比如 FAISS),还需要一个元数据存储来帮助管理原文。这里用 FAISS 存储向量用于快速检索,同时用一个简单的 JSON 文件或 SQLite 数据库 作为“账本”,记录每个数据块的原文、来源和唯一ID。

安装基础依赖:

pip install langchain langchain_community pypdf langchain-DeepSeek faiss-cpu sentence_transformers

第一步:存储知识并建立“账本”

这个脚本的目标是:接收新文档,将其存入 FAISS,并更新“账本”。

import os
import json
import uuid
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS

# --- 配置区 ---
FAISS_DB_PATH = "faiss_index_prod"  # 指定 FAISS 索引的存储路径
METADATA_STORE_PATH = "metadata_store.json" # “账本”路径
NEW_DOCUMENT_PATH = "最新的产品功能介绍.pdf" # 要添加的新文档

# --- 初始化模型 ---
embeddings_model = OpenAIEmbeddings()

# --- 加载或创建元数据存储 ---
if os.path.exists(METADATA_STORE_PATH):
    with open(METADATA_STORE_PATH, 'r') as f:
        metadata_store = json.load(f)
else:
    metadata_store = {}

# --- 加载并处理新文档 ---
loader = PyPDFLoader(NEW_DOCUMENT_PATH)
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
docs = text_splitter.split_documents(documents)

# --- 为新文档块生成唯一ID并存入元数据 ---
texts_to_embed = []
ids_to_add = []
for doc in docs:
    # 1. 生成唯一ID
    doc_id = str(uuid.uuid4())
    ids_to_add.append(doc_id)
    # 2. 将原文和来源存入“账本”
    metadata_store[doc_id] = {
        "content": doc.page_content,
        "source": doc.metadata.get('source', 'Unknown')
    }
    texts_to_embed.append(doc.page_content)

# --- 加载现有FAISS索引或创建新索引 ---
if os.path.exists(FAISS_DB_PATH):
    vector_db = FAISS.load_local(FAISS_DB_PATH, embeddings_model, allow_dangerous_deserialization=True)
    vector_db.add_texts(texts=texts_to_embed, ids=ids_to_add)
else:
    vector_db = FAISS.from_texts(texts=texts_to_embed, embedding=embeddings_model, ids=ids_to_add)

# --- 保存更新后的索引和元数据 ---
vector_db.sa ve_local(FAISS_DB_PATH)
with open(METADATA_STORE_PATH, 'w') as f:
    json.dump(metadata_store, f, indent=4)

print(f"成功添加 {len(docs)} 个数据块到知识库!")

现在你有了一个可以反复运行的脚本。每次有新文档,只需修改 NEW_DOCUMENT_PATH 并运行即可。

第二步:实现对知识的“查”与“删”

有了“账本”(metadata_store.json),管理就变得轻而易举。

查看原文(查询):可以直接打开 metadata_store.json 文件,搜索关键词,找到对应的原文内容和它的 doc_id。

删除原文(删除操作)

  1. 在“账本”中找到想要删除内容的 doc_id。
  2. 使用 FAISS 提供的 delete 方法删除向量。
import json
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS

# --- 删除指定知识的示例 ---
FAISS_DB_PATH = "faiss_index_prod"
METADATA_STORE_PATH = "metadata_store.json"
ID_TO_DELETE = "efe975e5-77fb-4f3a-a447-7a36cb97606b"  # 从账本中找到的需要删除的ID

# --- 初始化模型 ---
embeddings_model = HuggingFaceEmbeddings(model_name="../weight/all-MiniLM-L6-v2")

# 加载数据库和元数据
vector_db = FAISS.load_local(FAISS_DB_PATH, embeddings_model, allow_dangerous_deserialization=True)
with open(METADATA_STORE_PATH, 'r') as f:
    metadata_store = json.load(f)

# 1. 从 FAISS 中删除向量
success = vector_db.delete([ID_TO_DELETE])
print(f"向量删除是否成功: {success}")

# 2. 从“账本”中删除记录
if ID_TO_DELETE in metadata_store:
    del metadata_store[ID_TO_DELETE]

# 3. 保存变更
vector_db.sa ve_local(FAISS_DB_PATH)
with open(METADATA_STORE_PATH, 'w') as f:
    json.dump(metadata_store, f, indent=4)

print(f"ID为 {ID_TO_DELETE} 的知识已彻底删除!")

至此,你拥有了一个功能完备、可动态维护的知识管理中心。

构建“AI 应用端”

应用端的任务变得非常纯粹和简单:加载指定知识库,然后提供问答服务。

from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser

# --- 配置区 ---
FAISS_DB_PATH = "faiss_index_prod"  # 指定要加载的知识库!

# --- 加载预先构建好的知识库 ---
embeddings_model = OpenAIEmbeddings()
vector_db = FAISS.load_local(FAISS_DB_PATH, embeddings_model, allow_dangerous_deserialization=True)
retriever = vector_db.as_retriever()

# --- 构建 RAG 链 (这部分和之前一样) ---
template = """
请基于以下检索到的内容来回答问题。如果内容不足以回答,就说你不知道。
内容: {context}
问题: {question}
回答:
"""
prompt = ChatPromptTemplate.from_template(template)
llm = ChatOpenAI(model_name="gpt-4o")

rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# --- 开始提问!---
query = "检测服务的状态有那些?"
response = rag_chain.invoke(query)
print(response)

运行结果如下

问题:检测服务的状态有那些?
回答:检测服务的状态有:检测,就绪,⾃检,故障,关闭五种状态。

看!应用端的代码多么清爽——它完全不关心数据是怎么来的,只负责加载和使用。

总结

今天,我们完成了一次巨大的架构升级。你现在拥有的,不再是一个脆弱的 Demo,而是一个具备以下特性的、强大的 RAG 系统框架:

  • 架构解耦:数据处理与 AI 应用分离,易于维护和协作。
  • 动态管理:可以随时向知识库中增添、查询和删除知识。
  • 可追溯性:通过“账本”轻松找到向量对应的原文。
  • 多租户支持:懂得如何为不同用户提供隔离的知识服务。

这才是 RAG 在真实世界中的正确打开方式。基于这个框架,你可以继续扩展,比如接入更多文档类型、优化切分策略、或是更换更强大的向量数据库。

通往 AI 产品化的大门已经为你敞开,快去构建你的专属知识帝国吧!

来源:https://www.53ai.com/news/RAG/2025080460745.html

相关热点

继续查看同栏目近期热点。

延伸阅读

补充最近整理过的热点入口。