LangChain RAG实战:用PostgreSQL把你的本地文档变成智能知识库(Python代码详解)
LangChain与PostgreSQL构建智能知识库从文档预处理到高效检索实战在信息爆炸的时代如何快速从海量文档中提取有价值的知识成为开发者面临的共同挑战。本文将带你用Python构建一个基于LangChain和PostgreSQL的智能知识库系统实现文档的自动化处理和精准检索。不同于简单的代码示例我们将深入每个环节的技术细节与实战经验让你真正掌握RAG检索增强生成技术的核心要点。1. 环境配置与数据库优化构建高效的知识库系统始于合理的基础设施配置。PostgreSQL作为关系型数据库与pgvector扩展的结合提供了向量存储与检索的完美平衡。1.1 系统依赖与pgvector调优安装核心组件前建议创建独立的Python虚拟环境python -m venv rag_env source rag_env/bin/activate # Linux/Mac rag_env\Scripts\activate # Windows关键依赖安装命令pip install langchain psycopg2-binary pgvector langchain-openai unstructured注意unstructured包用于处理PDF/Word等非结构化文档建议同时安装libmagic系统依赖pgvector性能优化配置PostgreSQL 16示例-- 创建专用表空间SSD存储推荐 CREATE TABLESPACE vector_ts LOCATION /ssd_mount/postgresql/vector_data; -- 创建优化配置的数据库 CREATE DATABASE rag_db WITH OWNER postgres ENCODING UTF8 TABLESPACE vector_ts CONNECTION LIMIT -1;1.2 高级向量索引配置标准的CREATE EXTENSION vector只是开始实际生产环境需要精细调整-- 调整work_mem提高构建性能 SET work_mem 256MB; -- 创建带HNSW索引的向量列 CREATE TABLE document_embeddings ( id SERIAL PRIMARY KEY, content TEXT, embedding vector(1536), -- OpenAI维度 metadata JSONB ); CREATE INDEX ON document_embeddings USING hnsw (embedding vector_cosine_ops) WITH (m 16, ef_construction 64);提示HNSW参数中m影响索引构建时的连接数值越大查询精度越高但构建速度越慢ef_construction控制搜索范围建议初始值设为m的4倍2. 文档预处理流水线设计原始文档的质量直接决定检索效果。我们构建多阶段处理流程确保文本标准化。2.1 多格式文档加载策略LangChain的文档加载器需要针对不同文件类型配置from langchain.document_loaders import ( PyPDFLoader, Docx2txtLoader, UnstructuredMarkdownLoader, UnstructuredFileLoader ) def load_document(file_path): if file_path.endswith(.pdf): loader PyPDFLoader(file_path) elif file_path.endswith(.docx): loader Docx2txtLoader(file_path) elif file_path.endswith(.md): loader UnstructuredMarkdownLoader(file_path) else: loader UnstructuredFileLoader(file_path) return loader.load()2.2 智能文本分割实践RecursiveCharacterTextSplitter的参数配置需要结合内容特性from langchain.text_splitter import ( RecursiveCharacterTextSplitter, MarkdownHeaderTextSplitter ) # 针对技术文档的优化分割器 tech_splitter RecursiveCharacterTextSplitter( chunk_size800, chunk_overlap200, separators[\n## , \n### , \n\n, \n, ], length_functionlen, keep_separatorTrue ) # 带Markdown标题保留的分割方案 headers_to_split_on [(#, Header 1), (##, Header 2)] markdown_splitter MarkdownHeaderTextSplitter( headers_to_split_onheaders_to_split_on, return_each_lineFalse )文档预处理效果对比表参数组合适合场景优点缺点chunk_size500, overlap100技术API文档保持接口描述完整可能分割代码示例chunk_size1200, overlap300研究论文保持论证连贯性检索效率略低标题感知分割结构化文档保留章节上下文需要规范格式3. 向量存储与检索优化3.1 混合检索策略实现结合语义搜索与关键词过滤提升准确率from langchain.retrievers import BM25Retriever, EnsembleRetriever from langchain.vectorstores import PGVector # 初始化混合检索器 def create_hybrid_retriever(documents): # 语义检索 vector_store PGVector.from_documents( documentsdocuments, embeddingOpenAIEmbeddings(), connection_stringCONNECTION_STRING, pre_delete_collectionTrue ) vector_retriever vector_store.as_retriever(search_kwargs{k: 5}) # 关键词检索 bm25_retriever BM25Retriever.from_documents(documents) bm25_retriever.k 3 # 组合检索 return EnsembleRetriever( retrievers[bm25_retriever, vector_retriever], weights[0.4, 0.6] )3.2 查询重写与扩展技术提升原始查询的表达能力from langchain.prompts import PromptTemplate from langchain.chains import LLMChain query_rewrite_prompt PromptTemplate( input_variables[question], template作为信息检索专家请将以下用户问题改写为3个更适合向量检索的版本 保持专业术语不变但增加相关上下文线索。原始问题{question} 输出格式 1. 改写版本1 2. 改写版本2 3. 改写版本3 ) def expand_query(question, llm): rewrite_chain LLMChain(llmllm, promptquery_rewrite_prompt) rewritten rewrite_chain.run(question) return [q.split(. )[1] for q in rewritten.split(\n) if q]4. 生产环境部署要点4.1 连接池与性能监控import psycopg2.pool from prometheus_client import start_http_server, Summary # 数据库连接池配置 postgresql_pool psycopg2.pool.ThreadedConnectionPool( minconn5, maxconn20, dsnCONNECTION_STRING ) # 性能监控指标 SEARCH_TIME Summary(rag_search_seconds, Time spent processing RAG search) SEARCH_TIME.time() def search_with_metrics(query): # 实际搜索逻辑 pass # 启动监控端点 start_http_server(8000)4.2 自动化更新管道实现文档变更的实时同步import hashlib from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class DocHandler(FileSystemEventHandler): def __init__(self, vector_store): self.vs vector_store def on_modified(self, event): if not event.is_directory: content load_document(event.src_path) doc_hash hashlib.md5(content.encode()).hexdigest() if self._check_update_needed(doc_hash): self._process_update(content, event.src_path) def _process_update(self, content, path): docs tech_splitter.create_documents([content]) self.vs.add_documents(docs) print(fUpdated: {path}) # 启动监听 observer Observer() observer.schedule(DocHandler(vector_store), path./docs/, recursiveTrue) observer.start()5. 高级应用场景扩展5.1 多租户知识隔离# 在元数据中增加租户字段 from langchain.schema import Document def create_tenant_docs(raw_text, tenant_id): docs tech_splitter.create_documents([raw_text]) for doc in docs: doc.metadata[tenant_id] tenant_id return docs # 检索时添加过滤器 retriever vector_store.as_retriever( search_kwargs{filter: {tenant_id: client_a}} )5.2 检索结果可视化分析import matplotlib.pyplot as plt from sklearn.manifold import TSNE def plot_embeddings(docs, embeddings): # 降维可视化 tsne TSNE(n_components2, random_state42) reduced tsne.fit_transform(embeddings) plt.figure(figsize(12,8)) plt.scatter(reduced[:,0], reduced[:,1], alpha0.5) for i, doc in enumerate(docs): plt.annotate(doc.metadata.get(source,), (reduced[i,0], reduced[i,1]), fontsize8) plt.title(Document Embedding Clustering) plt.show()在真实项目中我们发现技术文档的chunk_size设置在600-800之间配合200左右的overlap能在保持上下文完整性和检索效率间取得最佳平衡。对于包含大量代码示例的文档建议先提取代码块单独处理再对说明文本进行分割。