还在用脚本一次性跑完 RAG 流程?太 Low 了!本教程带你将数据处理与 AI 应用彻底解耦。你将学会:1) 建立一个独立的“数据中心”,随时增删改查你的知识。2) 让 RAG 应用加载指定知识库。
Github: https://github.com/langchain-ai/langchain
嘿,各位 AI 架构师和探索者!
你们是否成功用 LangChain 搭建了你的第一个 RAG 应用?很棒!但你可能很快就发现了一些“现实”问题:
如果你遇到了以上任何一个问题,那么恭喜你,你正在从“玩具项目”向“真正可用的产品”迈进!
今天,这篇进阶指南将彻底解决你的烦恼。我们将重新设计 RAG 架构,实现数据处理与AI 应用的优雅分离,并赋予你的知识库完整的生命周期管理(CRUD)能力。
告别把所有代码都写在一起的“一体机”模式吧!一个成熟的 RAG 系统应该分为两个核心部分:
这样做的好处是显而易见的:解耦、可维护、可扩展。
这里的核心思想是:我们不仅需要一个向量数据库 (如 FAISS),还需要一个元数据存储来帮助我们管理原文。
我们将使用FAISS存储向量用于快速检索,同时用一个简单的JSON 文件或 SQLite 数据库作为“账本”,记录每个数据块的原文、来源和唯一ID。
安装基础依赖:
pip install langchain langchain_community pypdf langchain-DeepSeek faiss-cpu sentence_transformers
这个脚本的目标是:接收新文档,将其存入 FAISS,并更新我们的“账本”。
importos
importjson
importuuid
fromlangchain_community.document_loadersimportPyPDFLoader
fromlangchain.text_splitterimportRecursiveCharacterTextSplitter
fromlangchain_openaiimportOpenAIEmbeddings
fromlangchain_community.vectorstoresimportFAISS
# --- 配置区 ---
FAISS_DB_PATH ="faiss_index_prod"# 指定 FAISS 索引的存储路径
METADATA_STORE_PATH ="metadata_store.json"# “账本”路径
NEW_DOCUMENT_PATH ="最新的产品功能介绍.pdf"# 要添加的新文档
# --- 初始化模型 ---
embeddings_model = OpenAIEmbeddings()
# --- 加载或创建元数据存储 ---
ifos.path.exists(METADATA_STORE_PATH):
withopen(METADATA_STORE_PATH,'r')asf:
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 = []
fordocindocs:
# 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索引或创建新索引 ---
ifos.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.save_local(FAISS_DB_PATH)
withopen(METADATA_STORE_PATH,'w')asf:
json.dump(metadata_store, f, indent=4)
print(f"成功添加{len(docs)}个数据块到知识库!")
现在,你有了一个可以反复运行的脚本。每次有新文档,只需修改 NEW_DOCUMENT_PATH 并运行即可!
有了“账本” (metadata_store.json),管理就变得轻而易举。
查看原文 (Read):你可以直接打开 metadata_store.json 文件,搜索关键词,找到对应的原文内容和它的 doc_id。
删除原文 (Delete):
importjson
fromlangchain_community.document_loadersimportPyPDFLoader
fromlangchain_community.embeddingsimportHuggingFaceEmbeddings
fromlangchain.text_splitterimportRecursiveCharacterTextSplitter
fromlangchain_community.vectorstoresimportFAISS
# --- 删除指定知识的示例 ---
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)
withopen(METADATA_STORE_PATH,'r')asf:
metadata_store = json.load(f)
# 1. 从 FAISS 中删除向量
success = vector_db.delete([ID_TO_DELETE])
print(f"向量删除是否成功:{success}")
# 2. 从“账本”中删除记录
ifID_TO_DELETEinmetadata_store:
delmetadata_store[ID_TO_DELETE]
# 3. 保存变更
vector_db.save_local(FAISS_DB_PATH)
withopen(METADATA_STORE_PATH,'w')asf:
json.dump(metadata_store, f, indent=4)
print(f"ID为{ID_TO_DELETE}的知识已彻底删除!")
至此,你拥有了一个功能完备、可动态维护的知识管理中心!
应用端的任务就变得非常纯粹和简单了:加载指定知识库,然后提供问答服务。
fromlangchain_openaiimportChatOpenAI, OpenAIEmbeddings
fromlangchain_community.vectorstoresimportFAISS
fromlangchain.promptsimportChatPromptTemplate
fromlangchain.schema.runnableimportRunnablePassthrough
fromlangchain.schema.output_parserimportStrOutputParser
# --- 配置区 ---
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 系统框架:
这才是 RAG 在真实世界中的正确打开方式。基于这个框架,你可以继续扩展,比如接入更多的文档类型、优化切分策略、或是更换更强大的向量数据库。
通往 AI 产品化的大门已经为你敞开,快去构建你的专属知识帝国吧!
| 欢迎光临 链载Ai (https://www.lianzai.com/) | Powered by Discuz! X3.5 |