当前位置: 首页 > news >正文

RAG 从入门到落地:我在企业级知识管理平台中集成大语言模型的完整实践

一、引言

KMS 知识管理平台是我负责了两年的项目,服务公司内部 2000+ 员工,沉淀了约 15 万篇文档。

但它的搜索功能一直是个痛点。

用户输入"去年合规审查的通过标准是什么",传统搜索引擎返回一堆包含"合规"“审查”"标准"关键词的文档列表。用户需要逐篇点开、阅读、筛选——很多时候翻了 10 分钟还找不到想要的答案。

这不是搜索算法的问题,而是关键字匹配的天花板。用户的真实意图是"帮我找到答案",而关键字搜索只能做到"帮你找到可能包含答案的文档"。

2025 年初,我开始探索 RAG(Retrieval-Augmented Generation,检索增强生成)。三个月后,第一个版本在 KMS 内部上线。这篇文章记录我从 0 到 1 的完整实践过程——从架构设计、技术选型、到踩坑与优化。

二、RAG 是什么?一张图讲清楚

先用一句话概括 RAG 的核心思想:

不让 LLM 凭记忆回答,而是先检索相关文档,把文档内容和问题一起喂给 LLM,让它基于"参考资料"来回答。

这样做的好处显而易见:

  • 知识可更新:文档变了,检索结果就变了,不需要重新训练模型
  • 减少幻觉:LLM 被约束在检索到的文档范围内回答,不太会凭空编造
  • 来源可追溯:每个答案都能指向具体的源文档,方便核实

RAG 流程分为两个阶段:

离线阶段(文档入库)

  1. 文档解析:将 Markdown、PDF、Word 等格式统一转为纯文本
  2. 文本切分(Chunking):将长文档切成适当大小的文本块
  3. 向量化(Embedding):将每个文本块转换为向量
  4. 向量存储:将向量存入向量数据库

在线阶段(用户提问)

  1. 用户输入问题
  2. 问题向量化
  3. 向量检索:在向量数据库中查找最相似的文本块
  4. Prompt 组装:将检索到的文本块 + 用户问题组装成 Prompt
  5. LLM 生成:大模型基于上下文生成答案

三、系统架构设计

KMS RAG 系统的整体架构分为四层:

文档层

负责文档的接入与预处理。KMS 中 90% 的文档是 Markdown 格式(Tiptap 编辑器产出),剩下 10% 是以附件形式上传的 PDF 和 Word。

# 文档解析器 —— 统一不同格式fromlangchain_community.document_loadersimport(UnstructuredMarkdownLoader,PyPDFLoader,Docx2txtLoader,)defload_document(file_path:str,file_type:str)->list[Document]:loader_map={'md':UnstructuredMarkdownLoader,'pdf':PyPDFLoader,'docx':Docx2txtLoader,}loader_class=loader_map.get(file_type)ifnotloader_class:raiseValueError(f"Unsupported file type:{file_type}")loader=loader_class(file_path)returnloader.load()

向量化层

负责文本块的 Embedding 生成。我选择了text-embedding-3-small模型,在成本和效果之间取得了较好的平衡。

fromopenaiimportOpenAI client=OpenAI()defcreate_embeddings(texts:list[str],model:str="text-embedding-3-small")->list[list[float]]:"""批量生成文本的向量表示"""response=client.embeddings.create(model=model,input=texts,)return[item.embeddingforiteminresponse.data]

检索层

负责根据用户问题召回最相关的文档片段。这里采用了混合检索策略——向量检索 + 关键字检索,两者互补。

fromlangchain_community.vectorstoresimportPGVectorfromlangchain.retrieversimportBM25RetrieverclassHybridRetriever:"""混合检索器:向量检索 + BM25 关键字检索"""def__init__(self,vector_store:PGVector,bm25_retriever:BM25Retriever):self.vector_store=vector_store self.bm25_retriever=bm25_retrieverdefretrieve(self,query:str,top_k:int=5)->list[Document]:# 向量检索(语义相似)vector_docs=self.vector_store.similarity_search(query,k=top_k)# BM25 关键字检索keyword_docs=self.bm25_retriever.get_relevant_documents(query)[:top_k]# 合并去重 + RRF (Reciprocal Rank Fusion) 重排序returnself._rrf_fusion(vector_docs,keyword_docs)

生成层

负责组装 Prompt 并调用 LLM 生成最终答案。

defbuild_rag_prompt(query:str,retrieved_docs:list[Document])->str:"""组装 RAG Prompt"""context="\n\n---\n\n".join(f"【来源:{doc.metadata.get('source','未知')}】\n{doc.page_content}"fordocinretrieved_docs)returnf"""你是一个专业的 KMS 知识库助���。请基于以下参考资料回答用户的问题。 ## 参考资料{context}## 回答要求 1. 如果参考资料中包含答案,请直接引用并标注来源 2. 如果参考资料不足以回答问题,请明确说明 3. 不要编造参考资料中没有的信息 ## 用户问题{query}## 回答"""

四、Chunking 策略:被低估的关键环节

Chunking 决定了检索的"颗粒度"。切太大——检索精度下降,噪声多;切太小——上下文不足,答案片段化。

我在 KMS 上实验了三组参数:

策略Chunk 大小Overlap检索精度(Recall@5)适用场景
固定长度512 tokens10%78%通用基线
固定长度1024 tokens10%81%长文档
语义切分不固定句子级87%Markdown 文档

最终选择语义切分——基于 Markdown 的标题层级(#####)作为天然的分界点:

fromlangchain.text_splitterimportMarkdownHeaderTextSplitter headers_to_split_on=[("##","h2_section"),("###","h3_section"),("####","h4_section"),]splitter=MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on,strip_headers=False,)# 按 Markdown 标题层级切分,每个 section 自带层级元数据splits=splitter.split_text(markdown_content)

这种做法让每个 Chunk 自带标题上下文(元数据中的 h2_section、h3_section),在检索时 LLM 能理解这个片段属于哪个章节,生成的答案更有条理。

五、向量数据库选型

KMS 的技术栈是 Python + PostgreSQL,所以向量数据库的候选范围很明确:

方案优势劣势结论
Milvus性能最强,千万级向量不虚需要独立部署和维护太重,小团队不合适
Pinecone免运维,开箱即用数据出境(合规问题)、成本随规模上升金融科技不适合
PGVector复用 PostgreSQL,零额外运维百万级后性能下降✅ 当前最优解

PGVector 最大的优势是零额外运维成本——KMS 本来就用 PostgreSQL,开启 PGVector 插件只需要一行 SQL:

CREATEEXTENSION vector;-- 创建向量存储表CREATETABLEdocument_embeddings(id UUIDPRIMARYKEYDEFAULTgen_random_uuid(),doc_id UUIDREFERENCESdocuments(id),chunk_indexINT,contentTEXT,embedding VECTOR(1536),-- text-embedding-3-small 的维度metadata JSONB,created_atTIMESTAMPDEFAULTNOW());-- 创建索引(IVFFlat 适合 10 万级数据)CREATEINDEXONdocument_embeddingsUSINGivfflat(embedding vector_cosine_ops)WITH(lists=100);

15 万文档切分后约 60 万个 Chunk,PGVector 的 IVFFlat 索引在Recall@5 = 85%的条件下查询延迟约 120ms,完全够用。

六、踩坑与优化清单

坑一:Overlap 设置过小导致答案断层

现象:用户问"KMS 权限模型有哪三种角色",检索召回了三段分别讲admineditorviewer的内容——但没召回讲"权限模型概述"的段落,导致 LLM 不理解这三种角色之间的关系。

根因:Chunk 之间的 Overlap 只有 50 个字符,而"概述段落"和"详细描述"之间隔了 200+ 字符,没有被相邻 Chunk 覆盖。

解决:将 Overlap 提升到 Chunk 大小的 15%(约 150 tokens),同时在检索策略上增加父文档召回——检索到某个 Chunk 后,连带召回它所属的整个 Section。

坑二:向量检索的"语义漂移"

现象:用户搜索"财务报表模板",结果中出现了大量"季度报告模板"。从向量角度看,它们的语义确实很接近,但对用户来说是两种不同的文档。

根因:纯向量检索对同义词和近义概念区分度不够。

解决:引入混合检索——BM25 关键字检索 + 向量检索,用 RRF 算法融合排序:

defrrf_fusion(rankings:list[list[Document]],k:int=60)->list[Document]:""" Reciprocal Rank Fusion: 多路检索结果融合 RRF_score(d) = Σ 1 / (k + rank_i(d)) """scores={}forrankinginrankings:forrank,docinenumerate(ranking):doc_id=doc.metadata.get('chunk_id',doc.page_content[:50])scores[doc_id]=scores.get(doc_id,0)+1/(k+rank+1)# 按融合分数排序sorted_scores=sorted(scores.items(),key=lambdax:x[1],reverse=True)return[self._get_doc_by_id(doc_id)fordoc_id,_insorted_scores]

坑三:Prompt Template 设计不当,LLM 放飞自我

现象:早期版本的 Prompt 没有明确约束"如果不知道就说不知道",导致 LLM 在检索不到相关资料时,用自己训练数据中的知识"填补"——产生了幻觉答案。

解决:在 Prompt 中明确加入"边界指令"——“如果参考资料不足以回答问题,请明确说明,不要编造”。这一点在生成层代码中已经体现。

坑四:成本控制被忽略

账单惊魂:第一个月测试阶段,Embedding API 和 GPT-4o 的调用费用接近 $200。排查发现两个问题:

  1. 每次用户搜索都重新 Embedding 查询文本(实际上可以缓存热门查询)
  2. 检索召回了 20 个 Chunk(top_k=20),实际 5 个就够

优化后

  • 热门查询的 Embedding 结果用 Redis 缓存(TTL 1 小时)
  • top_k 降到 5,配合 Rerank 精排
  • 月成本降到约 $45

七、总结

三个月时间,RAG 从概念到上线,最深的体会是:

RAG 的工程难点不是模型,而是文档处理、检索策略和 Prompt 工程。模型是别人的,但你的文档结构、Chunking 策略、检索调优是别人替代不了的。

当前版本还远不完美。下一步计划:

  • Rerank 模型引入:在粗排后用 Cross-Encoder 做精排,进一步提升检索精度
  • 多轮对话支持:让用户能追问,而不仅仅是单轮问答
  • 用户反馈闭环:收集用户对答案的点赞/点踩,用于持续优化检索策略

如果你也在做类似的事情,建议先从最小可行方案开始:PGVector + 语义切分 + 混合检索,三个组件搭好,基本能满足 80% 的企业知识库场景。剩下的 20% 留给持续迭代。


这篇文章记录了我将 RAG 落地到 KMS 企业知识管理平台的完整过程。项目还在持续迭代中,欢迎交流。

http://www.gsyq.cn/news/1553221.html

相关文章:

  • 从文案策划到视频渲染:多模型混合链路的最佳实践指南
  • 根本不存在所谓的“技术任务”:技术任务就是产品任务
  • ZIP/RAR密码恢复实战:从John the Ripper到Hashcat GPU加速破解
  • 2026年6月自来水厂便携式污泥浓度计选购深度解析:十大品牌技术量化排名与工程选型决策指南 - 液体流量液位品牌推荐
  • 2026潍坊黄金回收实测攻略:六大商圈门店评测与防坑指南 - 余生黄金回收
  • 昆明黄金回收全维度测评:门店排行 + 报价拆解,告别虚高引流 - 奢品小当家
  • 2026石嘴山黄金回收行情与六家实体门店实测 - 余生黄金回收
  • 87456
  • 2026年湘阴车主的安心之选:四家轮胎养护中心实力解析 - 国麟测评
  • PMD Java代码检查工具:从零到一,实战集成与自定义规则详解
  • 天津黄金回收门店实力排行榜|禹竞名奢汇稳居榜首行情透明价更高 - 名奢变现站
  • LLM应用开发、RAG、Agent、MCP、A2A、多模态与AI Infra系统工程师进阶学习路线图
  • GCP Vertex AI Provisioned Throughput 完全指南 — 从 429 限流到 PT 预留吞吐量
  • 2025-2026年北京慧考教育电话查询:选择学历提升服务前需核实资质与流程 - 品牌推荐
  • 同校大数据和计算机,历年录取分数线谁更高
  • 2026合肥黄金回收白银回收铂金回收门店实测|本地正规实体老店无套路门店推荐 - 中安检金银铂钻回收
  • 大连奢侈品翡翠回收门店实测!5家主流奢藏机构深度横评,翡翠变现选这家不踩坑 - 奢品小当家
  • NIST SP800-22随机数测试,Windows环境下Cygwin安装和使用教程
  • 2026东营黄金回收白银回收铂金回收门店实测|本地正规实体老店无套路门店推荐 - 中安检金银铂钻回收
  • 2026 郑州黄金回收本地五家品牌门店盘点:靠谱机构交易安全全面验证 - 奢侈品回收
  • 2026乌鲁木齐本地人必选防水补漏检测维修公司靠谱服务商TOP5推荐:房屋渗漏水检测维修/卫生间/厨房/天花板/阳台/外墙渗漏水检测补漏维修-暗管漏水检测专业仪器精准定位漏水点 - 即刻修防水
  • STM32 Bootloader与APP切换时CMSIS-RTOS2启动失败的深度排查与解决
  • GLM-5开源大模型:中文长文本与工具调用的工程化突破
  • 闲置礼品黄金、公司奖励金币,沈阳变现渠道推荐 - 逸程
  • 2026鄂尔多斯黄金回收白银回收铂金回收门店实测|本地正规实体老店无套路门店推荐 - 中安检金银铂钻回收
  • ansys模态计算中的核是可以定义并行计算的核心吗?——ansys划分网格比较慢——每次的错误提示会全部更新为新的,之前的看不到。——针对ANSYS错误提示仅显示最新内容、无法查看历史记录的问题,可按
  • OpenCore Legacy Patcher:让旧Mac突破系统限制的技术创新方案
  • 基于YOLOV8的道路缺陷检测系统1(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • 2026白城黄金回收白银回收铂金回收门店实测|本地正规实体老店无套路门店推荐 - 中安检金银铂钻回收
  • [智能体-447]:Coze:自主规划模式 vs 对话流模式:同样存在工作流,核心本质区别