企业级RAG知识库搭建实战:从架构设计到部署上线

📅 2026/8/27 8:32:26
企业级RAG知识库搭建实战:从架构设计到部署上线
1. RAG 企业级知识库搭建核心能力速览RAGRetrieval-Augmented Generation检索增强生成这两年已经从一个学术概念变成了企业内部知识库落地的标准方案。它的核心思路并不复杂先让模型“去查资料”再基于查到的资料“组织回答”。对比直接让大模型靠训练记忆硬答RAG 的优势非常明显答案可以溯源到具体的文档片段知识更新只需要重新索引资料不需要反复重训模型幻觉问题也能被明显压制。这次我们来看一条完整的 RAG 企业级项目实战路线从架构设计、技术选型到环境准备、文档解析与切块、向量化入库再到 API 服务、批量任务和引用溯源。目标不是停留在概念上而是能照着走通一套真正可用于企业场景的知识库问答系统。能力项说明项目定位RAG 知识库问答系统可私有化部署文档上传后自动入库并可检索问答核心模块文档解析、文本切块、向量化、向量存储、召回排序、LLM 生成回答、引用溯源典型技术栈Embedding 模型 向量数据库Qdrant / Milvus / Chroma LLMQwen / llama.cpp 本地模型或云端 API部署方式Docker Compose 编排、Python 脚本启动、也可基于 Dify 等平台化工具快速搭建是否支持 API支持服务端可暴露 HTTP 接口供已有业务系统调用是否支持批量任务支持文档目录批量导入、定时增量索引均可实现硬件门槛Embedding 与文本切块可 CPU 运行生成问答环节按模型规模不同本地模型建议中端以上显卡云端 API 则无显著硬件要求同源扩展方案Spring AI 2.0 Qdrant、LlamaIndex / LangChain、Agentic RAG、Ontology RAG 等适合场景企业内部文档问答、知识库助手、产品手册客服、论文阅读、合同与制度检索这篇文章的实操部分会依次覆盖环境准备、Docker Compose 启动向量数据库、文档导入与切块、向量化入库、本地问答接口联调、引用溯源验证、批量导入任务设计以及一套完整的常见问题排查清单。2. RAG 知识库搭建的适用场景与使用边界2.1 适合谁用企业内部知识管理团队想把散落在 Wiki、飞书文档、Confluence、PDF 里的大量制度、规范、FAQ 变成一个统一问答入口。开发者个人想给本地笔记、论文库、技术文档建一个私有的“ChatPDF”数据不出本机。大模型应用开发者不想每次回答都依赖模型训练数据希望回答能带上实时、可验证的上下文。Java / Python 后端团队需要把知识库能力嵌入到已有 OA、客服系统、IM 机器人中的场景。2.2 能解决什么问题回答内容可以引用文档原文降低大模型“一本正经胡说八道”的风险。知识更新成本低新增资料只需重新切块、向量化几秒钟到几分钟内即可生效。数据留在本地满足隐私要求较高的企业场景。2.3 不适合什么场景需要高度多轮对话记忆、复杂推理判断且答案无法从文档中直接找到的场景RAG 并不擅长。文档质量差、扫描件未 OCR、表格结构混乱、语义密集的 PDF会明显影响召回效果。希望“零维护”的团队不适合自建 RAG因为切块参数、Embedding 模型、召回策略都需要持续调优。2.4 边界与合规提醒这里必须明确RAG 系统本质上是“企业资料的二次加工”。无论使用什么框架都要确保上传文档的版权与授权链条清晰不得把未授权的商业文档、个人隐私数据、内部机密文件直接投入未经安全评估的系统。涉及人脸、个人信息、客服对话记录等敏感数据时需要脱敏、权限管控和审计。生产环境必须验证模型输出的引用是否真实对应原文不建议对高风险场景直接开放自动化决策。3. RAG 应用架构与主流技术选型一套标准的 RAG 系统可以拆成五个环节文档加载 - 文本切块 - Embedding 向量化 - 向量检索 - LLM 生成回答每个环节都有对应选型。不同团队的技术栈不同选型也不同。3.1 文档加载与解析输入可能是 PDF、Word、Markdown、HTML、TXT。企业场景里PDF 的解析质量直接决定后续召回上限。目前主流思路文本型 PDF直接用 pypdf、PyMuPDF 提取。扫描版 PDF先接 OCR如 PaddleOCR、Tesseract再做版面分析。复杂表格和图文混排使用 MinerU、Marker 这类深度文档解析引擎输出 Markdown 结构后切块。从实际项目角度看先输出带结构的 Markdown再做切块比直接按原始文本切块效果好很多。原因是 Markdown 保留了标题层级、表格结构让后续切块更容易对齐语义边界。3.2 文本切块策略切块是 RAG 项目里最容易被低估的环节。切得太大向量化后语义模糊召回精度下降切得太小上下文碎片化生成阶段缺乏足够信息。推荐一组可用作起点的参数{ chunk_size: 512, chunk_overlap: 64, separators: [\n\n, \n, 。, , , , ] }这组参数的意思是每个块最多 512 个 token块之间重叠 64 个 token优先按照段落、换行、句号这样的天然边界切分。实际项目中还需要按文档类型做调整制度类、文档类适合 512 左右。代码类、日志类要按代码块边界切不能硬切句子。FAQ 类问答对建议一行一问一答单独成一个块不要混切。表格型内容保留 Markdown 表格结构优先整体切缺失会损失列含义。3.3 Embedding 模型选型Embedding 负责把文本变成向量。这一步的质量决定了“语义召回”的上限。主流的开源选项包括BGE 系列BAAI/bge-large-zh-v1.5中文场景效果好本地可跑。M3E 系列中文语义匹配场景友好。Qwen3-Embedding有商用协议约束需要按自身场景确认授权。云端 API 系列OpenAI 的 text-embedding-3-small 等适合不自建 Embedding 服务的团队。本地部署 Embedding 模型时CPU 也能推理但批量建库时 GPU 会明显更快。后续文章会给出一套不分模型品牌的调用抽象方便你后续替换模型。3.4 向量数据库选型向量数据库负责存储向量并提供 Top-K 相似度检索。QdrantRust 编写性能强支持 Docker 一键启动有完善的 HTTP API。是目前 RAG 项目里最容易上手的选项。Milvus特性丰富适合数亿条级别的向量部署相对重。Chroma轻量适合个人原型和本地小规模场景。Elasticsearch 8.x如果团队已有 ES 体系可以直接复用自带 dense_vector 字段支持。Pinecone / Weaviate 云服务适合不想自建运维的团队。企业级项目里比较稳妥的起步姿势是先上 Qdrant跑通全链路再按数据规模评估是否需要迁移 Milvus。3.5 生成模型选型回答生成阶段可以直接调云端大模型 API也可以用本地模型。云端 API适合不承担推理硬件运维、数据合规要求相对宽松的团队。本地 llama.cpp Qwen2-7B / Qwen2.5-7B适合数据不出内网、隐私要求高的企业。vLLM 部署适合需要高并发、多路访问的生产环境。如果只是在个人电脑上验证先跑通云端 API 或 4-bit 量化的本地小模型即可。生产规模再考虑 vLLM 多卡推理。3.6 扩展方案Agentic RAG 与 Ontology RAG近期热词里频繁出现 Agentic RAG 和 Ontology RAG这里简单展开一下区别。传统 RAG一次检索一次生成流程固定。Agentic RAG模型具备路由和判断能力先判断问题是否需要检索、需要检索哪类知识库再决定调用哪个检索工具甚至可以多轮迭代检索。适合复杂问题拆解。Ontology RAG在向量检索之外引入实体关系和知识图谱。先构建领域本体和实体链接再结合图查询与向量召回。适合高度依赖实体关系的领域比如医疗、法律、金融。这些方案不是互相排斥的。成熟的企业级架构通常会把这几种能力叠加先用 Agent 控制流程再用 Ontology 做实体约束最后用向量召回兜底。4. RAG 本地部署环境准备与前置条件4.1 硬件与基础环境清单按常见本地部署实践建议准备以下环境项目最低要求推荐要求操作系统Windows 10 / Ubuntu 20.04Ubuntu 22.04 及以上CPU4 核 8 线程8 核以上内存16GB32GB 以上显卡可选Embedding 可纯 CPUNVIDIA GPU 8GB 显存以上磁盘10GB 空闲50GB 以上预留向量库与模型文件Docker20.10最新稳定版Python3.10 或 3.113.11 以上本文以 Ubuntu Docker Compose Python 3.11 为例Windows 用户可以通过 WSL2 或 Docker Desktop 执行同样命令。4.2 检查本机端口与 Docker 状态Qdrant 默认占用 6333 端口HTTP和 6334 端口gRPCWeb 管理面板走 6333。启动前先检查端口是否被占用# 检查端口占用 sudo lsof -i :6333 sudo lsof -i :6334 # 查看 Docker 服务状态 systemctl status docker如果端口被占用可以通过 Docker 端口映射换掉宿主机端口后面会给出具体配置。4.3 创建项目目录结构建议按下面结构组织项目文件rag-project/ ├── docker-compose.yml ├── app/ │ ├── ingest.py # 文档导入与切块脚本 │ ├── api.py # FastAPI 问答服务 │ └── config.py # 配置项 ├── data/ │ ├── docs/ # 原始文档 │ ├── chunks/ # 切块结果 │ └── logs/ # 运行日志 └── requirements.txt目录拆分清晰后面做批量任务和问题排查会省很多时间。5. 企业级 RAG 知识库搭建环境部署与一键启动这里提供一个可复制的起步方案Docker 启动 QdrantPython 环境负责文档导入和 API 服务。5.1 docker-compose 启动 Qdrant新建docker-compose.ymlversion: 3.8 services: qdrant: image: qdrant/qdrant:latest container_name: rag-qdrant ports: - 6333:6333 - 6334:6334 volumes: - ./qdrant_storage:/qdrant/storage restart: unless-stopped启动docker compose up -d查看日志docker logs -f rag-qdrant启动成功后访问http://127.0.0.1:6333/dashboard可以看到 Qdrant 的管理面板。这一步相当于把向量数据库服务跑起来了。如果端口调整过记得把宿主机端口改成实际映射端口。5.2 安装 Python 依赖新建requirements.txtfastapi uvicorn pypdf pymupdf openai qdrant-client sentence-transformers安装pip install -r requirements.txt这里需要说明sentence-transformers负责本地 Embedding 模型openai库可以兼容 OpenAI、Qwen、Ollama 等多种兼容接口。如果只用本地 llama.cpp 服务也可以直接调用/v1/embeddings和/v1/chat/completions兼容端点。5.3 编写文档导入与向量化脚本下面这个脚本用 Qdrant 官方客户端从指定目录读取文档文本按固定参数切块再调用本地 Embedding 服务向量化并写入集合。import os from typing import List from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct def split_text(text: str, chunk_size: int 512, overlap: int 64) - List[str]: 简单的按长度切块示例生产环境建议按段落边界切分。 chunks [] start 0 text_len len(text) while start text_len: end min(start chunk_size, text_len) chunks.append(text[start:end]) if end text_len: break start max(0, end - overlap) return chunks def embed_text(text: str) - List[float]: 调用本地 OpenAI 兼容接口 /v1/embeddings。 import requests response requests.post( http://127.0.0.1:8000/v1/embeddings, json{model: local-embedding, input: text}, timeout60, ) response.raise_for_status() return response.json()[data][0][embedding] def ingest_directory(qdrant_client: QdrantClient, docs_dir: str, collection_name: str): if not qdrant_client.collection_exists(collection_name): qdrant_client.create_collection( collection_namecollection_name, vectors_configVectorParams(size1024, distanceDistance.COSINE), ) points [] point_id 0 for filename in os.listdir(docs_dir): filepath os.path.join(docs_dir, filename) if not filename.endswith(.txt): continue with open(filepath, r, encodingutf-8) as f: content f.read() chunks split_text(content) for chunk in chunks: vector embed_text(chunk) points.append(PointStruct(idpoint_id, vectorvector, payload{text: chunk, source: filename})) point_id 1 if len(points) 100: qdrant_client.upsert(collection_namecollection_name, pointspoints) print(finserted {len(points)} points) points [] if points: qdrant_client.upsert(collection_namecollection_name, pointspoints) print(finserted {len(points)} points) if __name__ __main__: client QdrantClient(host127.0.0.1, port6333) ingest_directory(client, docs_dirdata/docs, collection_namecompany_docs)这个脚本是可运行的最小骨架。实际项目中文档需要先做 PDF 提取、Markdown 结构化、敏感词过滤再进入切块环节。向量维度需要根据所选 Embedding 模型调整本地 bge-large-zh 通常是 1024 维云端 OpenAI 模型是 1536 维不能写死必须与模型输出保持一致。5.4 启动本地问答 API 服务新建api.pyfrom fastapi import FastAPI from pydantic import BaseModel from typing import List, Optional from qdrant_client import QdrantClient import requests app FastAPI(titleRAG Knowledge Base API, version1.0.0) qdrant_client QdrantClient(host127.0.0.1, port6333) COLLECTION_NAME company_docs LLM_ENDPOINT http://127.0.0.1:8000/v1/chat/completions class AskRequest(BaseModel): question: str top_k: int 5 class AskResponse(BaseModel): answer: str sources: List[str] def retrieve(question: str, top_k: int): 获取问题向量再到 Qdrant 检索相似片段。 embedding_response requests.post( http://127.0.0.1:8000/v1/embeddings, json{model: local-embedding, input: question}, timeout60, ) embedding_response.raise_for_status() question_vector embedding_response.json()[data][0][embedding] search_result qdrant_client.search( collection_nameCOLLECTION_NAME, query_vectorquestion_vector, limittop_k, ) return search_result app.post(/ask, response_modelAskResponse) def ask(request: AskRequest): results retrieve(request.question, request.top_k) context \n\n.join([r.payload[text] for r in results]) sources [r.payload[source] for r in results] llm_response requests.post( LLM_ENDPOINT, json{ model: local-llm, messages: [ {role: system, content: 请基于提供的文档片段回答用户问题。如果文档中没有相关内容请明确说明。}, {role: user, content: f文档片段\n{context}\n\n问题{request.question}} ], temperature: 0.2, }, timeout120, ) llm_response.raise_for_status() answer llm_response.json()[choices][0][message][content] return AskResponse(answeranswer, sourcessources) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8080)启动python api.py启动后访问http://127.0.0.1:8080/docs可以打开 Swagger 文档。到这里一条“文档导入 - 向量化入库 - 检索 - 生成回答 - 返回引用来源”的基础链路已经打通。5.5 通过 Dify 快速搭建知识库的可选路线如果不打算从头写代码热词里频繁出现的 Dify 是一条更快的路线。Dify 自带知识库功能提供文档上传、切块设置、Embedding 模型管理、检索测试等完整界面还支持工作流编排和 API 发布。Dify 部署通常使用 Docker Composegit clone https://github.com/langgenius/dify.git cd dify/docker cp .env.example .env docker compose up -d启动后进入 Web 界面创建知识库、上传文档、设置切块参数、选好 Embedding 模型就能直接进入问答调试。这套方案适合想快速产出原型或者不会大量写代码的团队。但要注意Dify 的知识库 API 和底层向量库抽象程度高遇到深层调优问题时需要回到底层配置和日志中排查。6. 功能测试与效果验证部署完成后建议按照下面几个维度逐项测试而不是直接丢大批文档进去。6.1 测试文档导入与向量化准备 3 到 5 份不同格式的小文件如一份 Markdown 格式的《人事请假制度》、一份 PDF 格式的《员工手册摘要》、一份纯 TXT 格式的《FAQ 列表》。调用脚本导入python ingest.py判断标准日志中打印出插入的向量数量。Qdrant 管理面板中能看到 collection 和 points 数量。随机抽查 Qdrant 中存储的 payload能看到原始文本片段和来源文件名。常见失败原因Embedding 服务未启动、向量维度不匹配、文档编码不是 UTF-8。6.2 测试单条问答与引用溯源用 curl 发起一个测试请求curl -X POST http://127.0.0.1:8080/ask \ -H Content-Type: application/json \ -d {question: 公司的年假规定是什么, top_k: 3}预期结果返回 answer 和 sources 两个字段。sources 应该包含匹配的文档文件名。判断标准回答内容和文档原文一致没有明显编造。sources 中返回了包含该规定的文档。如果回答胡编乱造优先检查切块大小和检索 Top-K 设置。6.3 测试语义召回与关键词召回差异准备一组相似的测试问题“请假流程需要提交什么材料”“工伤休假怎么处理”“年假能跨年休吗”好的知识库应该能区分这三类问题并分别召回正确片段。如果总是指向同一段文本说明切块粒度过大或检索策略过于单一。可以考虑调小 chunk_size。增加重排模型Rerank环节。调整 Top-K 数量。6.4 测试长上下文回答稳定性把一个长问题的内容拆成多个子问题连续追问。观察模型是否会出现“上下文遗忘”或者引用错乱。一个常见的坑是多轮对话中知识库检索永远只基于当前这一轮问题不携带历史。生产环境如果要支持多轮问答需要把历史对话摘要也作为检索条件或者直接用 Agentic RAG 方案做多轮规划。6.5 验证输出质量与 groundednessGroundedness 指的是“回答内容是否扎根于检索到的文档”。可以做一些对抗性测试把问题问成一个文档里完全不存在的细节。故意问一个文档中语义相似但结论相反的内容。判断标准系统应该明确回答“根据现有文档无法确认”而不是强行生成。写 system prompt 时应该强调“没有依据不要编造”并在代码层面判断检索结果的相关得分阈值低于阈值的片段不送入 LLM。7. 接口 API 调用示例与批量任务设计7.1 使用 Python 调用问答接口下面的示例代码可以直接集成到已有业务系统import requests url http://127.0.0.1:8080/ask payload { question: 新员工入职需要准备哪些材料, top_k: 5 } response requests.post(url, jsonpayload, timeout180) response.raise_for_status() data response.json() print(回答, data[answer]) print(引用来源) for source in data[sources]: print(-, source)7.2 批量文档导入任务设计企业场景中不可能每次手动运行ingest.py。建议设计成定时或任务触发模式。一个简单的批量处理流程设置data/docs为待导入目录。每次启动任务前先计算目录文件的 hash只处理新增和变更文件。对每个文件单独做切块并记录切块数量。入库成功后写入处理日志到data/logs。处理失败的文件单独放入data/error并记录失败原因。批量任务需要考虑重试策略。向量化接口偶发超时是正常现象建议对单个文件最多重试 3 次每次间隔 5 秒。如果超过重试次数不要跳过文件而是写入失败队列人工复核。7.3 增量更新方案知识库不是一次性建设。文档更新后需要更新对应向量。常见做法在 payload 中保存文档的文件名和内容 hash。更新前先按文件名删除旧的向量。再执行切块、向量化、写入。Qdrant 支持按 payload 字段过滤删除from qdrant_client import QdrantClient client QdrantClient(host127.0.0.1, port6333) client.delete( collection_namecompany_docs, points_selector{ filter: { must: [ {key: source, match: {value: employee_handbook.pdf}} ] } } )这个操作保证了同一文档反复更新的情况下知识库里不会出现重复的旧内容。8. 资源占用与性能观察8.1 观察维度实际项目中最值得监控的四个指标是文档解析峰值内存大 PDF 一次性读入容易占用好几 GB 内存。Embedding 推理耗时CPU 推理和 GPU 推理差距极大。向量检索耗时小规模数据通常是毫秒级数据量增大后需要关注索引类型。生成问答耗时LLM 推理速度和并发数直接相关。以常见的本地部署配置为例说明观察方法。假设在一台 16GB 内存、无独显的服务器上运行Embedding 模型用 bge-small-zh512 维左右生成侧用云端 API那么整条链路的资源压力主要在文档解析和切块阶段如果生成侧也走本地 llama.cpp 加载 7B 模型内存压力会明显上升。具体数值因硬件差异较大实际占用要以本机测试为准。8.2 如何降低资源占用文档解析阶段使用按页加载或流式读取不要一次性读入超大文件。Embedding 模型选择小参数量版本例如 bge-small 而非 bge-large。向量召回阶段如果数据量不大优先使用 HNSW 索引默认参数不要追求过大索引数量。生成模型使用 4-bit 量化配合 llama.cpp 的--ctx-size限制上下文长度。部署 API 服务时设置 uvicorn 的进程数为 1 到 2避免多进程重复加载模型导致显存翻倍。8.3 显存与内存观察命令Linux 下观察显存和内存# 查看 GPU 显存占用 nvidia-smi # 查看 Python 进程内存占用 ps aux --sort-%mem | grep python如果使用 Ollama 或 llama.cpp 服务模型加载后显存占用会相对稳定。回答越长的内容上下文 token 越多显存占用会小幅增长。如果系统出现进程残留先 kill 掉旧进程再重启避免端口和显存同时被占用。8.4 性能优化思路当数据量变大、并发变高时按以下顺序排查优化Embedding 是否改成批量接口一次传多段文本而不是逐段请求。向量数据库是否需要增加副本或升级到 Milvus。生成模型是否需要切换 vLLM 部署启用 continuous batching。是否引入 Rerank 模型用更少但更准的 Top-K 结果喂给 LLM。检索逻辑是否先用稀疏关键词召回过滤再走向量精确排序。9. RAG 知识库搭建常见问题与排查方法问题现象可能原因排查方式解决方案Qdrant 容器启动失败端口被占用或目录权限问题docker logs rag-qdrant查看日志换端口映射或给存储目录加写权限导入文档后检索不到内容切块为空、向量维度不匹配、batch 未刷入检查切块数量、向量维度和 Qdrant points 数调整切块逻辑确认向量维度与 collection 配置一致回答内容与文档无关检索召回结果差或 Top-K 过小打印检索结果比较召回文本与问题相关性缩小 chunk_size增加重叠或引入 Rerank 模型回答出现编造内容system prompt 约束不足检索结果未被有效限制检查 context 拼接是否完整增加“未找到相关内容时不要编造”的系统指令并设置相似度阈值过滤本地 Embedding 接口超时模型加载慢或并发过高查看服务日志和 GPU 占用批量向量化调整超时时间或更换更小的模型多文档导入后重复内容很多同一文档反复入库检查导入脚本是否有去重逻辑payload 中记录文件 hash按 hash 或文件名先清理旧向量PDF 内容读取乱码扫描版 PDF 未 OCR 或编码异常打开 PDF 查看是否为图片型接入 OCR 引擎或先转成可检索 PDFAPI 调用返回 503下游 LLM 服务未启动或过载检查 LLM 服务端口和日志重启下游服务或者增加请求重试批量任务跑到一半卡住单文件解析异常导致进程阻塞查看日志定位卡住文件给解析函数加单文件超时失败文件进入错误队列多进程部署后显存翻倍多个进程各自加载了一份模型查看进程数和显存大小限制进程数或使用模型共享方案10. 最佳实践与使用建议10.1 小参数起步第一次跑通时不要上来就导几百份文档。用 5 份小文件、3 个测试问题把“文档导入 - 检索 - 生成 - 引用”这条链路验证没问题再逐步加量。10.2 保留一套最小可运行配置把能跑通的 docker-compose.yml、Embedding 接口地址、切块参数、prompt 内容记录下来保存成项目里的config.example.yaml防止重新部署时忘了关键配置。10.3 文件和日志目录分清楚原始文档、切块结果、向量库、日志必须分目录。批量任务处理时新增、变更、失败文件用独立目录或者数据库状态字段区分。否则排错时根本不知道哪些文件成功、哪些失败。10.4 接口服务限制访问范围API 服务默认监听0.0.0.0:8080。生产环境建议前置 Nginx 反向代理。开启 API 密钥认证。设置请求频率限制。只允许内网或指定网段访问。否则知识库接口会变成内部资料泄露入口。10.5 回答前做相似度阈值过滤检索结果的相似度分数如果低于 0.5不同 Embedding 模型阈值不同大概率是不相关片段。建议在送入 LLM 前过滤掉低分片段或者直接告知用户“未找到足够相关内容”。10.6 商用与发布前复核确认 Embedding 模型和 LLM 的商用授权范围。确认上传文档的版权归属和使用授权是否清晰。人脸、声音、个人信息等敏感数据必须脱敏后才能入库。任何对外发布的回答都要支持“点击溯源到原文”的能力。11. 总结与下一步RAG 企业级知识库搭建的学习路径用一句话总结就是先切块再向量化最后在生成前把检索这件事做扎实。最容易踩的坑不是大模型选择而是文档解析和切块质量。切块切不好后续用再强的模型都救不回来。因此建议第一次上手时重点验证“文档切块 - 检索召回”这一半链路先把召回准确率提上去再接 LLM 生成。下一步可以做的扩展方向引入 Rerank 模型提升 Top-K 结果的排序质量。用 Agentic RAG 实现多知识库路由和复杂问题拆解。用 Ontology RAG 补充实体关系检索能力。用 Spring AI 2.0 Qdrant 把知识库能力接入 Java 微服务。用 Dify 的工作流蓝图把普通 RAG 升级为可编排的企业级应用。对了把这篇收藏起来下次搭 RAG 知识库时直接照着清单来。