☰
从API套壳到真RAG:手把手搭建企业知识库小程序的完整指南
2026/10/5 9:37:52 网站建设 项目流程

上个月有朋友兴冲冲给我看一个"AI知识库小程序",说是接了大模型API做的,把公司几十份制度文档传上去就"什么都能答"。我随手问了两个问题:"迟到三次算严重违纪还是普通违纪?""员工手册是哪一年修订的?"结果它要么绕圈子,要么一本正经编了个日期。这种产品太典型了:想做RAG知识库小程序的人很多,但不少项目只是给大模型套了个壳,根本没有检索增强,也就是俗称的API套壳伪RAG。

这篇文章我打算分两条线讲清楚:一条教你怎么从0到1搭一个真正的RAG知识库小程序,覆盖文档切分、向量检索、DeepSeek API接入、小程序端适配这些关键步骤;另一条教你怎么用实测题、抓包、细节逼问快速判断对面是不是套壳伪RAG。无论你是产品经理、独立开发者,还是只想给团队搞个私有知识库,看完应该能形成自己的判断标准。

1. 先厘清概念:RAG到底"增"了什么强,API套壳为何假装是RAG

1.1 大模型是"闭卷考生",RAG是给它一本开卷资料

不夸张地说,RAG解决的是大模型的"知识新鲜度和私有化"问题。你让一个行业专家回答公司内部制度,他再博学也拿不到你公司上个月刚更新的流程文档,只能靠常识猜,猜就会错。如果把文档全文直接喂给他,一是塞不下,二是每次提问都重读全文,成本高到离谱。

RAG的做法是让模型"开卷考试":先把文档切成小块,做embedding向量化,放到向量库里;用户来一个问题,先把问题向量化,去向量库里召回最相关的几段文本,再把这些片段连同问题一起交给大模型生成回答。答案是模型现场读资料后组织出来的,不是凭空背出来的,这就是"检索增强生成"的完整闭环。

判断一个产品是不是真RAG,就看它有没有这个闭环里的两个核心环节:向量索引和检索召回。如果连向量库都没有,只有一个对话接口,那不叫RAG。

1.2 API套壳的三种假把式

根据我接触过的项目,伪RAG大致有三个层级:

第一层是最粗暴的,只接了大模型厂商的ChatCompletion接口,上传的文档就丢在服务器某个目录里,压根不解析。用户提问时,请求体里只有一句"请根据我的知识库回答:xxx",模型能答全凭训练数据里碰巧有相似内容。

第二层稍微勤快一点,做了文档存储,但没做向量检索。每次提问都把所有文档内容拼成一大段塞进Prompt,靠模型的长上下文硬扛。文档少的时候看起来能用,文档一多就超token上限,报错信息里经常会出现"maximum context length is 1048576 tokens"这类提示。

第三层是"半套壳",确实做了切分和向量化,但召回逻辑做得极差,比如永远只取前几段,或者根本没有按相关性排序,效果比不检索还差。这种产品从架构上算是RAG,但使用体验和白送没有区别。

为什么很多人会信这种伪RAG?因为日常问题模型本来就能答,比如"什么是KPI""如何写周报",套壳产品答得头头是道。只有问到私有化、细节化的问题才会露馅,而大部分演示者根本不会用这种问题去测试。

特征真RAGAPI套壳伪RAG
文档入库切分→向量化→建索引原样上传或仅存文件
用户提问问题向量化→召回TopK→重排原样发给模型
模型上下文只注入相关片段,长度可控全文档或固定Prompt
回答依据可指向原文片段依赖预训练知识
实时更新重新索引即生效需改Prompt或重新上传
可调试有检索参数、命中测试一般没有

1.3 伪RAG在什么场景下立刻露馅

最容易暴露伪RAG的是三类问题:精确数字类、多条件组合类、来源时间类。比如"2024年秋季产品目录里哪些型号防水等级达到IP68?"或者"上一版差旅报销标准里,住宿上限是多少来着?"真RAG如果没检索到,会老实说"知识库中没有相关内容";伪RAG则会基于相近词开始编,神态极其自信。

所以判断真假的第一步,不是看它演示什么,而是看你怎么提问。

2. 亲手搭一个真RAG知识库小程序:从文档入库到小程序上线的关键步骤

2.1 先选一条能落地的路线:自建流水线还是Dify这类平台

对于独立开发者和小团队,我建议起步别自己从零写embedding和向量库,优先用Dify这类开源知识库平台。它本身提供了完整的"知识库流水线",文档解析、切分、索引、检索、生成都能可视化配置,还能一键暴露成API给小程序调用。

自建流水线的好处是完全可控,缺点是坑多:切分策略、向量模型选型、索引更新、检索排序、并发性能,每一个环节都要自己排雷。如果你的技术栈是Java,可以看看LangChain4j的easy rag模块,它把很多样板代码封装好了;如果数据敏感不能出内网,本地化部署Ollama加开源嵌入模型也完全可行。我的建议是:先用Dify快速验证需求,验证了业务闭环再决定要不要自研替换,而不是一开始就扎进代码里。

2.2 文档和图片入库:切分粒度决定命中的下限

入库是把知识变成可检索格式的关键一步。文本处理有三个常见切分方式:按Markdown标题层级切、按固定长度切、按语义边界切。对有目录结构的制度文件、产品手册,我强烈建议按标题切,并且把父标题作为前缀拼进去。比如某一段内容本来只有"迟到3次视为严重违纪",切分后应该保留"员工手册 > 考勤制度 > 迟到处理 > 迟到3次视为严重违纪"这个上下文。这样检索时更容易命中完整的信息单元。

很多人会问:RAG知识库能存储图片吗?答案是能,但一般不是把图片原样塞进向量库,因为embedding模型处理不了图片。常规做法有三条路:一是OCR,把截图和表格转成文本再入库,适合手册里的产品截图;二是用多模态模型生成图片描述,把描述文本入库,回答时再引用原图路径给用户看;三是如果大模型支持图像输入,把图片放到CDN,检索到文本片段后带着图片链接一起给模型。绝大多数情况下,文本化的方式实现成本最低、效果最稳。

2.3 检索到生成的完整链路:一个可复制的Python后端示例

不管用不用Dify,你都应该理解后端这条链路,否则出了问题很难排查。我用FastAPI加Chroma加DeepSeek API给你一个最小化示例。

先做文档切分和向量化入库:

from chromadb import PersistentClient from sentence_transformers import SentenceTransformer model = SentenceTransformer("BAAI/bge-small-zh-v1.5") client = PersistentClient(path="./kb_db") collection = client.get_or_create_collection("knowledge_base") doc_chunks = split_by_markdown(doc_text) # 按标题切分,保留父标题前缀 vectors = model.encode(doc_chunks).tolist() ids = [str(i) for i in range(len(doc_chunks))] collection.add(ids=ids, embeddings=vectors, documents=doc_chunks)

然后写一个检索函数,再调用DeepSeek生成:

from openai import OpenAI import os client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com" ) def rag_answer(question: str): q_vec = model.encode([question]).tolist() hits = collection.query(query_embeddings=q_vec, n_results=5) context = "\n---\n".join(hits["documents"][0]) system_prompt = "你是一个企业知识库助手,请严格根据提供的资料回答问题;资料中没有的内容,请明确回答不知道。" resp = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": f"资料:\n{context}\n\n问题:{question}"} ] ) return resp.choices[0].message.content

这里有两个经验要分享。第一,n_results不建议调得太大,5条左右够用,否则上下文膨胀,既费token又稀释重点。第二,DeepSeek API的401错误非常常见,报错里会出现unexpected status 401 unauthorized: incorrect api key provided: sk-svcac****,十有八九是环境变量值末尾带了空格或换行,或者复制Key时漏了几位字符。

2.4 小程序端:别让前端UI拖后腿

后端做得再漂亮,小程序端体验不好照样劝退用户。三个高频需求一定要处理好。

第一个是页面标题。微信小程序里的"动态设置标题"用wx.setNavigationBarTitle就能实现,但要注意页面栈里的每个页面都要各自设置,不能指望全局配置。

第二个是顶部导航栏高度适配。不同手机的胶囊按钮位置不一样,直接用固定navigationBarHeight会出问题。正确做法是获取胶囊按钮位置和状态栏高度,用windowWidth等参数做动态计算,不只是为了好看,也关系到布局会不会被刘海屏遮挡。

第三个是问答历史列表的"加载更多"。这个需求很容易做翻车,因为用户翻得快一点就会重复请求。我的做法是维护一个pageNo和pageSize,在页面到onReachBottom时先判断isLoading锁,如果正在请求就直接跳过,等数据返回后再解锁。还有列表渲染的时候用wx:key,千万别用数组下标,否则删除或插入数据时会出现渲染错乱。

如果你用uni-app开发再打包成微信小程序,注意条件编译和包体控制。微信小程序对主包有2MB大小限制,把后端SDK、图表库这种重模块都放分包或后端去,前端只保留请求和渲染,能省不少麻烦。

2.5 上线前的安全与鉴权

API Key绝对不能写在小程序代码里,小程序端任何代码都能被扒出来,开发者工具里一搜就暴露了。正确的做法是自建一个薄的BFF层,小程序先调你的后端,由后端持有API Key再去调大模型和向量库。后端至少要做三件事:接口鉴权、用户级访问控制、限流。不然有人写个for循环就能刷完你一个月的token预算。

3. 三个方法拆穿API套壳伪RAG:实测题、抓包、逼问细节

3.1 实测题设计:让伪RAG当场翻车

想让套壳伪RAG露馅,最有效的是"指鹿为马"测试法。你挑一条知识库里明确有、但公开网络上完全搜不到的内容,比如"公司内部'蓝鲸项目'的立项日期是哪一天?""客服部第七版话术文件里的开场白第三句是什么?"。

如果产品答得出来还能说出出处,说明检索链路真的存在。如果它开始绕圈子、编日期、甚至给出模棱两可的话,基本可以判定是伪RAG。另一个好用的方法叫"多跳问题",答案需要集合两个段落的信息才能给出来,伪RAG的检索模块如果只抽一段,就很容易抓瞎。

3.2 抓包看请求链路:它到底调用了什么

提问测试是黑盒,抓包就是直接看白盒。用Charles抓微信小程序HTTPS请求是常见的调试手段,核心步骤是:电脑和手机连同一个局域网,手机配置代理指向主机IP,安装Charles根证书,再在SSL Proxying里开启相关域名。完成后操作小程序里的知识库问答,观察请求列表。

重点看两个东西:请求发到哪个域名,请求体里有什么。如果你发现问答请求直接发给了大模型厂商的API地址,且请求体里只有用户问题,没有任何"检索到的资料片段",那基本就是套壳。真RAG的调用链一般是:先有一个检索接口返回了chunks,再有一个生成接口把这些chunks拼进请求体,最后生成答案。抓包看到两个接口串行调用,才是正常现象。

一定要清楚:抓包调试只适用于自己开发或已获授权的小程序。随意抓取他人商业产品属于灰色操作,这个底线要守住。技术判断可以,但别拿来做坏事。

3.3 逼问细节:让它自曝"身世"

抓包不方便的时候,就用"逼问细节法"连续问它几个元问题:你现在的知识库有几个文档?最近一次更新是什么时候?请把刚才回答里第一条依据的原文念一遍?每条依据分别来自哪个章节?

伪RAG到这里基本就崩了,因为它根本没有知识库的元信息,只会说"我的知识库实时更新,请放心"。真RAG哪怕权限不够,也会告诉你它无法访问这些元信息,而不是胡编一个数字。还有一个观察点:如果程序在运行中直接弹出API Key错误,或者上下文长度类报错,说明它内部直连大模型且没有做检索压缩,这类产品不用再测了,属于肉眼可见的套壳。

3.4 从产品功能角度看"隐藏面"

给产品做一次"后台体检"也能辨别真伪。真RAG通常有这些配置项:切分策略选择、TopK召回数量、重排开关、向量模型选择、命中测试工具、文档版本回滚。伪RAG绝大多数只有一个"上传文档"和"聊天"两个框,没有中间过程,也没有召回结果的展示。

如果对方声称自己是知识库产品,但连"文档管理"和"更新时间"都说不清,那它大概率就是一个大模型的网页套壳。判断标准可以很简单:能不能修改检索权重?能不能看到召回片段?能不能测试某个问题命中了几条文档?三个都没有,别犹豫,换。

4. 过了"能回答"这道坎,真正的RAG瓶颈在检索,不在模型

4.1 别用准确率一票否决,先看Hit Rate和MRR

很多团队做完RAG,第一反应是看大模型回答得对不对,这其实把顺序搞反了。回答的质量上限由召回决定,如果Top5里根本没有正确答案,再好的大模型也编不出来。所以在评估RAG时,第一个指标应该是检索命中率。

实践中我通常建一个二三十条问题的测试集,每个问题都标注好它应该在知识库的哪个段落。然后单独测试检索器,看Top5命中率(Hit Rate)和第一个正确答案的平均排名(MRR)。如果Top5命中率只有60%,后面再怎么调Prompt都是白搭。先把问题集中在"为什么检索不到",而不是急着换模型。

4.2 切分策略和混合检索的甜点

切分策略对命中率的影响比大多数人想象中大。同样一份员工手册,固定512字切分容易把完整的制度条款切成两半,但按Markdown标题切分并保留父标题前缀,能显著提升精确匹配能力。对于产品型号、编号这类专有名词,纯向量检索往往不如BM25的精确匹配,所以现在的主流做法是混合检索:向量召回加关键词召回,再把两边结果合并重排。

重排这件事值得单独提一下。前一轮召回的几十条结果里,用Cross-Encoder或者一个简单的LLM打分,把真正对应的几条放到最前面,Top3命中率经常能提升二三十个百分点。进阶一点可以引入本体RAG,先把实体和关系抽取成知识图谱,再辅助检索,适合问答场景有强领域术语的团队,但实现成本也更高,不建议一上来就上。

4.3 图片和多模态:RAG知识库能不能存图片?

更完整的做法是"多模态RAG":图片原图不参与向量检索,但会把图片的元数据、OCR文本、描述文本一起入库。用户问"给这张产品结构图里的零件编号"时,系统先检索到描述文本,再把图片URL传给多模态模型,模型看完图后给出答案。如果你的用户经常要问截图里的表格、流程图,这个路径值得认真做。成本上,OCR和描述生成是一次性投入,图片问答时多模态模型单次调用会更贵,所以要注意缓存常见图片的问答结果。

4.4 本地小模型也可以,但要接受它的边界

很多人问我,卡帕西说过知识库可以用小模型做,那是不是意味着大模型无关紧要?当年的讨论引出了一波"Ollama + 本地向量库"的热潮,结论是:只要检索足够准,7B级别的本地小模型确实能应付大部分内部知识问答。本地小模型最大的优势是私有化和零API费用,配合本地embedding模型,就能搭一个完全离线可复制的小型RAG。一个最简单的命令就能把基础模型跑起来:

ollama run qwen2.5:7b

但小模型的推理能力弱,容易断章取义,所以Prompt要写得更死板,系统提示词里必须强调"没找到就说不知道",而且TopN数量要更保守。小模型不是不能做知识库,而是需要你用更高质量的检索去补偿它的生成短板。

5. 验收清单和踩坑复盘:我不想再看你交"假作业"

5.1 一张表验收你的RAG知识库小程序

如果你正在做知识库小程序,或者正在给供应商做验收,可以用下面这份自查表逐项打钩:

验收项要求
文档切分有明确策略,保留标题上下文
向量库独立存储,支持增量更新
检索测试有Hit Rate / MRR测试,Top5命中不低于80%
回答引用能指向原文片段或章节
图片处理有OCR或描述入库方案
API Key安全只存在后端,小程序端无法读取
错误处理401、上下文超限等错误有兜底
并发控制问答接口有限流和鉴权
小程序体验标题动态设置、导航栏适配、加载更多不重复请求

如果这份清单里有一半不能打钩,说明你还没有交付一个完整的RAG知识库小程序。

5.2 我实际踩过的几个坑

第一个坑是把图片原图直接转成embedding向量。我试过一次,向量库里全是无意义的噪声,检索结果完全失控。后来改成OCR加多模态描述,效果立刻正常了。第二个坑是小程序"加载更多"没做锁,用户连续下滑时重复请求,列表闪跳严重,加上isLoading判断才稳下来。第三个坑是DeepSeek API的401,排查了一晚上,最后发现是环境变量末尾带了一个换行符。Key从.env复制出来常有这种问题,肉眼根本看不出来。

还有用Dify搭知识库时遇到"排队中"卡了很久,原因是文档拆得太碎,索引任务一次性提交了几千个小块。后来把切分粒度调大,并控制批次提交,队列瞬间就顺畅了。再有一个项目教训:一开始把TopK调到20,文档一大,每次问答都携带大量片段,费用高且回答发散,后来压到Top5并加重排,回答质量明显变好。

5.3 给还在纠结的人一句话

不是所有"AI助手"都必须做成RAG。如果答案不需要依据你的私域文档,直接调用模型API也没问题。但如果对外宣称是"知识库",就必须有检索、有索引、有可验证的出处。伪RAG短期内能骗过体验者,骗不过一个较真的人追着问三句细节。你要么诚实标注"通用对话助手",要么老老实实把检索增强做完整。这条路没有捷径,但走通了之后,用户每一次追问"你确定吗"的时候,你都有原文可以甩回去,那种底气,是套壳产品永远给不了的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询