AI Agent白手起家55: RAG 知识库设计——从文档摄入到智能检索
纲要知识库工具在智能体中的作用系统架构概览读与写分离写入路径文档加载、切分、嵌入与存储FastAPI服务提供add_url接口使用WebBaseLoader加载网页语义切分SemanticChunkerChroma向量数据库持久化读取路径查询重写与多路召回基于历史记录的问题改写链多查询生成与并行检索MMR算法去重排序最终答案合成链完整可运行代码项目结构依赖安装向量数据库写入服务add_docs.py知识库检索工具knowledge_tool.py启动与测试总结与相关度说明知识库工具给智能体装上“外挂大脑”大模型的知识停留在训练截止日期而实际应用中往往需要接入私有文档、产品手册、内部规章等。RAG技术正是解决这一问题的标准范式将文档向量化后存入数据库检索时用相似度找到最相关的片段交给大模型生成最终答案。小浪助手的知识库模块实现了完整的读取与写入链路并加入了查询重写优化显著提升了检索准确度。架构总览读取路径写入路径POST /add_url管理员FastAPI 服务WebBaseLoader 加载网页SemanticChunker 语义切分OpenAIEmbeddings 向量化Chroma 向量数据库用户提问查询重写链生成多个查询变体MMR 检索 top-k去重合并LLM 合成答案读取和写入共享同一个向量数据库但通过不同的模块独立实现便于维护和扩展。写入路径让知识“入库”采用FastAPI搭建轻量后台服务接收 URL 列表自动完成加载、切分、嵌入和存储。项目结构rag_service/ ├── add_docs.py # 文档写入服务 ├── knowledge_tool.py # 检索工具 ├── config.py # 环境变量 ├── .env └── chroma_db/ # 向量数据库持久化目录环境准备pipinstallfastapi uvicorn langchain langchain-openai langchain-community chromadb python-dotenv配置文件config.pyimportosfromdotenvimportload_dotenv load_dotenv()classConfig:OPENAI_API_KEYos.getenv(OPENAI_API_KEY)OPENAI_BASE_URLos.getenv(OPENAI_BASE_URL,https://api.openai.com/v1)EMBEDDING_MODELos.getenv(EMBEDDING_MODEL,BAAI/bge-m3)CHROMA_PERSIST_DIRos.getenv(CHROMA_PERSIST_DIR,./chroma_db)COLLECTION_NAMEos.getenv(COLLECTION_NAME,xiaolang_docs)CHUNK_SIZEint(os.getenv(CHUNK_SIZE,500))CHUNK_OVERLAPint(os.getenv(CHUNK_OVERLAP,50))文档写入服务add_docs.py# add_docs.pyimportuuidfromtypingimportList,DictfromfastapiimportFastAPIfrompydanticimportBaseModelfromlangchain_community.document_loadersimportWebBaseLoaderfromlangchain_experimental.text_splitterimportSemanticChunkerfromlangchain_openaiimportOpenAIEmbeddingsfromlangchain_community.vectorstoresimportChromafromconfigimportConfig appFastAPI()classDocumentProcessor:def__init__(self):self.embeddingsOpenAIEmbeddings(modelConfig.EMBEDDING_MODEL,openai_api_keyConfig.OPENAI_API_KEY,base_urlConfig.OPENAI_BASE_URL,)self.splitterSemanticChunker(self.embeddings,breakpoint_threshold_typepercentile)self.vectorstoreChroma(collection_nameConfig.COLLECTION_NAME,embedding_functionself.embeddings,persist_directoryConfig.CHROMA_PERSIST_DIR,)defadd_from_urls(self,urls:List[str])-Dict:从URL列表加载文档并存入向量库results[]forurlinurls:try:loaderWebBaseLoader(url)docsloader.load()ifnotdocs:results.append({url:url,status:empty})continuechunksself.splitter.split_documents(docs)# 为每个块生成唯一IDids[str(uuid.uuid4())for_inchunks]self.vectorstore.add_documents(chunks,idsids)results.append({url:url,status:success,chunks:len(chunks)})exceptExceptionase:results.append({url:url,status:error,detail:str(e)})return{results:results}processorDocumentProcessor()classUrlPayload(BaseModel):urls:List[str]app.post(/add_url)asyncdefadd_url(payload:UrlPayload):returnprocessor.add_from_urls(payload.urls)if__name____main__:importuvicorn uvicorn.run(app,host0.0.0.0,port8000)启动服务后访问http://localhost:8000/docs即可通过界面测试添加 URL 文档。读取路径精准检索与答案生成直接从向量库用原始问题进行相似度搜索往往得不到最佳结果因为口语化的提问与文档中的书面表达差异很大。查询重写技术可以生成多个不同角度的查询变体大幅提升召回率。知识库检索工具knowledge_tool.py# knowledge_tool.pyfromtypingimportListfromlangchain.toolsimporttoolfromlangchain_openaiimportChatOpenAI,OpenAIEmbeddingsfromlangchain_community.vectorstoresimportChromafromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.runnablesimportRunnablePassthroughfromconfigimportConfig# 初始化向量库只读模式embeddingsOpenAIEmbeddings(modelConfig.EMBEDDING_MODEL,openai_api_keyConfig.OPENAI_API_KEY,base_urlConfig.OPENAI_BASE_URL,)vectorstoreChroma(collection_nameConfig.COLLECTION_NAME,embedding_functionembeddings,persist_directoryConfig.CHROMA_PERSIST_DIR,)defrewrite_query(original_query:str,chat_history:str)-List[str]:利用 LLM 将用户问题改写为多个检索变体llmChatOpenAI(modelgpt-3.5-turbo,temperature0.3)promptChatPromptTemplate.from_template(根据聊天记录和最新的用户问题生成3个独立的、语义相同但表达不同的查询语句 每个查询单独一行不要编号不要解释。 聊天记录: {chat_history} 用户问题: {query} 生成的查询:)chainprompt|llm|StrOutputParser()resultchain.invoke({query:original_query,chat_history:chat_history})queries[q.strip()forqinresult.split(\n)ifq.strip()]return[original_query]queriestooldefsearch_knowledge_base(query:str)-str:从内部知识库检索相关文档并合成答案。用于需要专业领域知识的场景。# 查询重写rewritten_queriesrewrite_query(query)# 多路检索并去重all_docs[]forqinrewritten_queries:docsvectorstore.max_marginal_relevance_search(q,k3,fetch_k10)all_docs.extend(docs)# 去重seenset()unique_docs[]fordocinall_docs:ifdoc.page_contentnotinseen:seen.add(doc.page_content)unique_docs.append(doc)# 选取最相关的前5个片段context\n\n.join([d.page_contentfordinunique_docs[:5]])ifnotcontext:return知识库中未找到相关信息。# 合成最终答案llmChatOpenAI(modelgpt-3.5-turbo,temperature0)answer_promptChatPromptTemplate.from_template(使用以下检索到的上下文回答用户问题。如果不知道答案就说不知道最多三句话。\n上下文: {context}\n问题: {query}\n答案:)chainanswer_prompt|llm|StrOutputParser()returnchain.invoke({context:context,query:query})# 本地测试if__name____main__:# 先确保 add_docs 服务已启动并添加过文档test_queryLangGraph 是如何更新图状态的print(search_knowledge_base.invoke(test_query))完整运行流程在.env文件中配置OPENAI_API_KEY等环境变量。启动文档写入服务python add_docs.py在 Swagger UI 中提交要学习的网页 URL。测试检索工具python knowledge_tool.py观察控制台输出确认向量检索与答案生成正常。查询重写的价值许多开发者会忽略查询重写直接将用户问题扔给向量数据库。但在多轮对话中用户可能会说“那个呢”“上次那个”这些指代如果不结合历史记录重写为独立查询向量搜索基本无效。该模块通过引入历史记录和改写链生成了多个聚焦于核心语义的查询显著提升了检索的相关性和鲁棒性。总结本博客从文档写入到智能检索完整实现了 RAG 知识库工具。向量数据库选用Chroma嵌入模型使用硅基流动的BAAI/bge-m3并结合了语义切分、查询重写、MMR 检索等技术提供了一个可直接集成到智能体中的知识增强方案。所有代码均可直接运行开发者只需补充.env配置和相关文档即可。