LangChain搭建简单本地知识库
LangChain 是一个开源的大语言模型(LLM)应用开发框架,核心是把大模型与外部数据、工具、工作流连接起来,解决大模型 “知识过时、无法联网、不会用工具” 的问题,让你快速搭建出上下文感知、能推理、可调用工具的 AI 应用,LangChain = LLM + 外部数据 + 工具 + 工作流编排,是开发 LLM 应用的事实标准框架
定位:LLM 应用的 “乐高积木” 与 “操作系统”,不是替代大模型,而是增强与编排大模型。
解决三大痛点
- 知识边界:让模型能读你的文档、数据库、实时数据(RAG)。
- 工具缺失:让模型会用计算器、搜索引擎、API、代码解释器。
- 流程复杂:把多步骤任务(提问→检索→计算→回答)串成可复用的 “链”。
语言支持:Python(主流) + JavaScript/TypeScript
搭建流程
- 文档加载,并按一定条件切割成片段
- 将切割的文本片段灌入检索引擎
- 封装检索接口
- 构建调用流程:Query -> 检索 -> Prompt -> LLM -> 回复
环境准备
# 1. 处理PDF文件的核心库:用于读取PDF内容、提取文本(LangChain的PDF加载器底层常依赖它)
pip install pypdf2
# 2. 阿里云百炼大模型的Python SDK:用于调用阿里云的通义千问等大模型(替代OpenAI)
pip install dashscope
# 3. LangChain核心库:提供LLM应用开发的核心组件(链、代理、记忆、提示模板等)
pip install langchain
# 4. LangChain对接OpenAI生态的专用库:封装OpenAI/兼容OpenAI接口的模型调用逻辑(如通义千问也可适配)
pip install langchain-openai
# 5. LangChain社区生态库:包含第三方集成(如PDF加载器、向量数据库对接、本地模型适配等)
pip install langchain-community
# 6. FAISS向量数据库(CPU版):Facebook开源的高效向量检索库,用于RAG场景的本地向量存储与匹配
pip install faiss-cpu
创建知识库
# -*- coding: utf-8 -*-
import os
import logging
import dashscope
import pickle
import config
from PyPDF2 import PdfReader
from langchain.chains.question_answering import load_qa_chain
from langchain_openai import OpenAI, ChatOpenAI
from langchain_openai import OpenAIEmbeddings
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.callbacks.manager import get_openai_callback
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from typing import List, Tuple
# 提取pdf文件每页的内容和对应的页码
def extract_text_with_page_numbers(file) -> Tuple[str, List[int]]:
"""
:param file: pdf文件
:return: 提取的文本内容,每行文本对应的页面列表
"""
text = ""
page_numbers = []
for page_number, page in enumerate(file.pages, start=1):
extracted_text = page.extract_text()
if extracted_text:
text += extracted_text
page_numbers.extend([page_number] * len(extracted_text.split("\n")))
else:
logging.warning(f"Page {page_number} has no text")
return text,page_numbers
# 处理文本创建向量存储
def process_text_with_splitter(text:str,page_numbers:List[int],save_path:str = None) -> FAISS:
"""
:param text: 提取的文本内容
:param page_numbers: 每行文本对应的页面列表
:param save_path: 保存向量数据库的路径
:return: 基于FAISS的向量存储对象
"""
# 创建文本分割器材递归文本分割,长文本分割成小块
text_splitter = RecursiveCharacterTextSplitter(
# 分割器会优先用第一个分隔符(\n\n,空两行)拆分文本
# 如果拆分后的块仍超过chunk_size,再用下一个分隔符(\n,换行),依此类推,直到最后用空字符串("")强制拆分字符。
# 优先按段落(\n\n)切 → 再按行(\n)→ 再按句子(.)→ 再按空格()→ 最后按字符切,符合中文 / 英文文本的自然分割逻辑
separators=["\n\n","\n","."," ",""], # 分割符优先级(从高到低)
chunk_size=512, # 每个文本块的最大长度(按 length_function 计算,这里是字符数)
chunk_overlap=128, # 相邻文本块的重叠长度(避免切割后语义断裂),比如第一个块是 1-512 字符,第二个块是 385-896 字符(512-128=385),保证语义连贯
length_function=len # 计算文本长度的函数(默认就是 len,按字符数算),如果是处理嵌入模型(如 OpenAI Embedding),也可以换成按「token 数」计算(比如 tiktoken 库)
)
# 分割文本
chunks = text_splitter.split_text(text)
# 调用阿里百练炼型,用于后续分割的文本向量
embeddings = DashScopeEmbeddings(
model = "text-embedding-v4",
dashscope_api_key=config.API_KEY
)
# 从文本块创建知识库
knowledgeBase = FAISS.from_texts(chunks,embeddings)
# 存储每个文本块对应的页码信息
page_info = { chunk:page_numbers[i] for i,chunk in enumerate(chunks)}
knowledgeBase.page_info = page_info
if save_path:
os.makedirs(save_path,exist_ok=True)
# 保存FAISS向量数据库
knowledgeBase.save_local(save_path)
# 保存页码信息到同一目录
with open(os.path.join(save_path,"page_info.pkl"),"wb") as f:
pickle.dump(page_info, f)
return knowledgeBase
# 从磁盘加载向量数据库和页码信息
def load_knowledge_base(load_path: str, embeddings=None) -> FAISS:
# 如果没有提供嵌入模型,则创建一个新的
if embeddings is None:
embeddings = DashScopeEmbeddings(
model="text-embedding-v4",
dashscope_api_key=config.API_KEY
)
# 加载FAISS向量数据库,添加allow_dangerous_deserialization=True参数以允许反序列化
knowledgeBase = FAISS.load_local(load_path, embeddings, allow_dangerous_deserialization=True)
print(f"向量数据库已从 {load_path} 加载。")
# 加载页码信息
page_info_path = os.path.join(load_path, "page_info.pkl")
if os.path.exists(page_info_path):
with open(page_info_path, "rb") as f:
page_info = pickle.load(f)
knowledgeBase.page_info = page_info
print("页码信息已加载。")
else:
print("警告: 未找到页码信息文件。")
return knowledgeBase
if __name__ == '__main__':
pdf_reader = PdfReader("人工智能全景科普:从基础认知到未来展望(10页完整版).pdf")
text,page_numbers = extract_text_with_page_numbers(pdf_reader)
print(text)
print(page_numbers)
# 处理文本并创建知识库,同时保存到磁盘
save_dir = "./vector_db"
knowledgeBase = process_text_with_splitter(text, page_numbers, save_path=save_dir)
# 处理文本并创建知识库
knowledgeBase = process_text_with_splitter(text, page_numbers)
print( knowledgeBase)
查询知识库
# 从内存中加载查询
def query_chat(query,knowledgeBase):
"""
:param query: 提问的问题
:param knowledgeBase: 使用内存中已经存在的向量存储对象
:return:
"""
# 执行相似度搜索,找到与查询相关的文档
docs = knowledgeBase.similarity_search(query)
# 初始化对话大模型
chatLLM = ChatOpenAI(
api_key = config.API_KEY,
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1",
model = "deepseek-v3"
)
# 加载问答链
chain = load_qa_chain(chatLLM, chain_type="stuff")
# 准备输入数据
input_data = {"input_documents": docs, "question": query}
# 使用回调函数跟踪API调用成本
with get_openai_callback() as cost:
# 执行问答链
response = chain.invoke(input=input_data)
print(f"查询已处理。成本: {cost}")
print(response["output_text"])
print("来源:")
# 记录唯一的页码
unique_pages = set()
# 显示每个文档块的来源页码
for doc in docs:
text_content = getattr(doc, "page_content", "")
source_page = knowledgeBase.page_info.get(
text_content.strip(), "未知"
)
if source_page not in unique_pages:
unique_pages.add(source_page)
print(f"文本块页码: {source_page}")

# 从硬盘加载向量数据库(替换原内存直接使用逻辑)
knowledgeBase = FAISS.load_local(
vector_db_path,
embeddings,
allow_dangerous_deserialization=True
)
风月都好看,人间也浪漫.

浙公网安备 33010602011771号