想象你开了一家餐厅,生意蒸蒸日上。你请来一位厨艺精湛的主厨——它就是大语言模型(LLM)。主厨做的每一道菜,都来自后厨的食材仓库。顾客点菜时,主厨要先快速扫一眼仓库,找到最新鲜、最对味的食材,再动手烹制。这家食材仓库,就是 RAG 客服系统的知识库;仓库日常的采购、清洗、切配、码放、补货、下架,就是我们今天要讲的知识库构建与更新。 如果仓库乱糟糟:烂叶没摘、食材堆成一团、过期的罐头混进新鲜货里,主厨再厉害也做不出一盘好菜。RAG 系统的回答质量上限,恰恰由知识库决定——模型只是“厨师”,知识库才是“食材”。这篇文章会带你走完一条完整流水线:从文档接入清洗、智能切分、向量化索引,到质量校验、增量更新,再到监控迭代,像管理后厨一样,把知识库打理得井井有条。

岗位关联

对 AI 从业者而言,掌握知识库构建与更新,意味着你从“能跑通 Demo”升级为“能扛住生产环境”,这是 RAG 工程落地中最核心、最值钱的硬技能。

目录
  1. 可视化演示
  2. 文档接入清洗
  3. 智能切分策略
  4. 向量化与索引构建
  5. 知识质量校验
  6. 增量更新机制
  7. 监控与迭代
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

文档接入清洗

清晨六点,后厨采购的货车到了。一筐青菜里夹着烂叶泥土,一箱海鲜贴着模糊的旧标签。帮厨师傅的第一件事不是直接塞进冰箱,而是拆包验收:烂叶摘掉、泥土冲净、标签核对,再把不合格的一整筐退回供应商。这一步看似简单,却决定了今天所有菜品的安全底线。

RAG 客服系统的知识库也面临同样的“食材验收”。公司里的文档千奇百怪:有 PDF、Word、Excel 导出的对账单,有网页爬下来的帮助中心,还有同事随手写的 Markdown。它们就像带着泥土和烂叶的原始食材,不能直接进仓库。文档接入清洗是流水线第一站,负责把各种格式解析成干净的纯文本,再提取标题、作者、版本、生效日期等元数据——相当于给每份食材贴上“产地、保质期、品名”的标签。

真实的预处理流水线长这样:先按文件类型路由到不同解析器(PDF 用文字层或 OCR,Word 转纯文本,HTML 去标签),然后是噪声过滤——去掉页眉页脚、页码、水印、乱码字符;紧接着是内容规整,把表格转成行文、把压缩的列表展开;最后是元数据提取,用正则或小模型识别标题与章节结构。流水线末端设一道质量门槛:统计一批文档的合格率,低于阈值(比如 95%)就整批拦截并通知负责人,而不是让坏数据混进知识库。

帮厨师傅把一筐筐验收合格的食材送进后厨,垃圾桶里躺着被退回的烂叶和过期标签。主厨因此永远不需要担心食材的“原罪”——他的每一道菜,都站在干净的起点上。知识库的第一站做扎实了,后面的环节才谈得上效果。

Q=NpassNtotal≥θ\displaystyle Q = \frac{N_{\text{pass}}}{N_{\text{total}}} \ge \theta
这批能通过验收的文档占比必须达到设定的合格线,否则整批打回,防止坏食材污染仓库。
  1. 合格数量$N_{\text{pass}}$ 表示一批文档中通过校验的数量(文本非空、无乱码、元数据齐全)。
    Npass=通过校验的文档数N_{\text{pass}} = \text{通过校验的文档数}
  2. 总数量$N_{\text{total}}$ 表示这一批送入流水线的文档总数。
    Ntotal=本批文档总数N_{\text{total}} = \text{本批文档总数}
  3. 合格率$Q$ 是合格文档数除以总数,即这批食材的“验收合格率”。
    Q=NpassNtotalQ = \frac{N_{\text{pass}}}{N_{\text{total}}}
  4. 质量门槛当 $Q \ge \theta$(阈值,如 0.95)才放行入库,否则整批回退。
    Q≥θ⇒放行;否则拦截Q \ge \theta \Rightarrow \text{放行;否则拦截}

智能切分策略

食材洗净了,接下来是切配。整颗白菜丢进锅里煮不熟,切得碎成末又会煮烂成一锅糊。老师傅的刀工讲究“依纹理下刀”:顺着菜帮的纹理切,既入味又保留口感;而且每刀之间不切断,留一点连着的部分,下锅时前后两段味道才不断层。

知识库里的长文档也是一样。一篇 8000 字的售后政策直接整篇向量化,检索时像把整颗白菜扔进锅里——向量被“稀释”,召回不精准;但如果切得太碎,每块只有一两句话,又会丢失上下文,回答时像夹不起的碎末。智能切分策略就是那把刀:确定合理的块大小(一般 300~800 token),并沿着标题、段落等“纹理”(语义边界)下刀。

切分有两个关键设计。第一是重叠窗口:相邻两个块保留 10%~20% 的重叠内容,防止一句话被拦腰截断而丢失语义。第二是父子分块:一个“父块”包含完整章节上下文,检索时先命中更小的“子块”,再拿父块给模型做上下文——相当于把“信息碎片”和“完整菜谱”都留在手边。重叠切分的公式如下:

老师傅的案板上,每一刀都顺着纹理落下,刀口之间留着“藕断丝连”的量,装盘时每一块都入口刚好、形神不散。切分策略做得好,检索阶段才能“夹得起菜、不夹碎菜”。

chunki=text[i×(L−O)  :  i×(L−O)+L]\displaystyle \text{chunk}_i = \text{text}\left[i \times (L - O) \;:\; i \times (L - O) + L\right]
第 i 块文本从“总长度减去重叠量后的偏移位置”开始取长度为 L 的窗口,保证相邻块有 O 长度的重叠。
  1. 块大小$L$ 是每个知识块的目标长度,通常用 token 数衡量,取值在 300~800。
    L=目标块长度(token)L = \text{目标块长度(token)}
  2. 重叠量$O$ 是相邻两块之间重复保留的长度,一般取 $L$ 的 10%~20%。
    O=重叠长度≈0.1L∼0.2LO = \text{重叠长度} \approx 0.1L \sim 0.2L
  3. 块序号$i$ 是第几块,从 0 开始计数。
    i∈{0,1,2,⋯ }i \in \{0, 1, 2, \cdots\}
  4. 起始偏移$i \times (L - O)$ 是第 $i$ 块的起始位置,因为每前进一块只前进 $L - O$,所以窗口之间重叠 $O$。
    starti=i×(L−O)\text{start}_i = i \times (L - O)
  5. 取窗口$[\text{start}_i, \text{start}_i + L)$ 即为第 $i$ 块的文本区间,Python 切片记法。
    chunki=text[starti:starti+L]\text{chunk}_i = \text{text}[\text{start}_i : \text{start}_i + L]

向量化与索引构建

切好的食材不能堆在台面上,得分装进保鲜盒,贴上标签,按菜系分区码放进冰箱:蔬菜区、肉类区、冷冻区。主厨在高峰期接到订单,报一声“宫保鸡丁”,帮厨能在几秒内从正确的区格拿出花生米、鸡腿肉和干辣椒。

知识库的“保鲜盒”就是向量化:Embedding 模型把每个知识块映射成一个高维向量(几百到几千维的浮点数数组),语义相近的文本在向量空间里彼此靠近。而“分区码放”就是索引构建:将向量写入向量数据库(如 FAISS、Milvus、pgvector),并建立倒排索引用于关键词查找。顾客的提问同样被映射成向量,检索就是在仓库里找最近邻的块——度量“距离”的常用标准是余弦相似度。

余弦相似度只关心方向、不关心长度,适合衡量文本语义一致度:

但实际上单靠向量检索不够稳,比如“保修期 12 个月”这种精确条款,关键词命中的可信度更高。生产系统普遍采用混合检索:把向量召回分数和 BM25 关键词分数按权重融合,再由重排模型精排。向量负责“懂你”,关键词负责“扣字眼”,两者互补才稳。

冰箱分区合理、标签清晰,帮厨才能在高峰期手起“盒”落。索引构建正是把“找得到”变成常态:它不产生知识,却决定了知识在关键时刻能不能被及时端上桌。

sim(q⃗,d⃗)=q⃗⋅d⃗∥q⃗∥ ∥d⃗∥\displaystyle \text{sim}(\vec{q}, \vec{d}) = \frac{\vec{q} \cdot \vec{d}}{\|\vec{q}\| \, \|\vec{d}\|}
用户问题向量和知识块向量的余弦相似度越接近 1,表示方向越一致、语义越贴近。
  1. 点积$\vec{q} \cdot \vec{d}$ 是两个向量各维度乘积之和,衡量一个向量在另一个方向上的投影量。
    q⃗⋅d⃗=∑jqjdj\vec{q} \cdot \vec{d} = \sum_{j} q_j d_j
  2. 范数$\|\vec{q}\|$ 是问题向量的模长,即各维度平方和再开方,用于归一化。
    ∥q⃗∥=∑jqj2\|\vec{q}\| = \sqrt{\sum_{j} q_j^2}
  3. 求比值点积除以两个模长的乘积,得到夹角的余弦值,范围在 -1 到 1。
    sim=q⃗⋅d⃗∥q⃗∥ ∥d⃗∥∈[−1,1]\text{sim} = \frac{\vec{q} \cdot \vec{d}}{\|\vec{q}\| \, \|\vec{d}\|} \in [-1, 1]

知识质量校验

食材码进冰箱前,还有一道关键工序——品控。老主厨的铁律:标签模糊的不收,发蔫变色的一律退货,没打生产日期的直接丢进废料桶。他还会抽查腌制时间对不上的食材,避免一道菜因食材问题砸了招牌。

知识库同样需要“品控盖章”。向量化之后的块如果存在重复、矛盾或残缺,会让检索结果互相打架。知识质量校验就是质检流水线,包含三道检查:完整性检查——块内容非空、元数据字段齐全、向量为非零向量;语义去重——用文本相似度找出描述同一件事的块并合并;冲突检测——新文档和旧文档对同一政策的说法矛盾时,标记出来走人工复核。高风险变更还要走审批流程,相当于“质检员签字放行”。

语义去重常用 Jaccard 相似度判断“两块是否在说同一件事”:

有了品控环节,冰箱里每一盒食材都“身世清白、标签可溯”。当主厨手忙脚乱时,他不需要担心某盒肉其实是上周的存货——质量校验把“出问题”拦在了烹饪之前。

J(A,B)=∣A∩B∣∣A∪B∣\displaystyle J(A, B) = \frac{|A \cap B|}{|A \cup B|}
把两段文本看成两个词集合,交集比并集;比值越高,两段话越像是在重复同一件事,超过阈值就触发合并或删除。
  1. 词集合$A$、$B$ 分别代表两段文本去重后的词集合。
    A={w1,w2,… },B={v1,v2,… }A = \{w_1, w_2, \dots\},\quad B = \{v_1, v_2, \dots\}
  2. 交集$A \cap B$ 是两段文本共同拥有的词,交集越大重复嫌疑越高。
    A∩B={两边都出现的词}A \cap B = \{\text{两边都出现的词}\}
  3. 并集$A \cup B$ 是所有不重复词的汇总。
    ∣A∪B∣=∣A∣+∣B∣−∣A∩B∣|A \cup B| = |A| + |B| - |A \cap B|
  4. 比值交集除以并集,结果在 0~1,超过阈值(如 0.8)判定为语义重复。
    J(A,B)≥0.8⇒合并或删除J(A, B) \ge 0.8 \Rightarrow \text{合并或删除}

增量更新机制

餐厅不是每周把冰箱清空重买一次食材,那样又贵又慢。真正的做法是:每天凌晨供应商送货,早上补充卖完的蔬菜;客人反映某道菜不新鲜,立刻下架;每周盘点一次,把临期食品提前处理。餐厅的食材永远是“流动的”,而不是“一次定终身”。

知识库也绝不能每次更新就全量重建——重跑一遍全量清洗、切分、向量化,耗时几小时到几天,还容易宕机。增量更新机制的目标是:以文档为最小单位,只处理“新增、修改、删除”的部分。触发源有三类:定时任务扫描源系统、业务系统主动推送(如订单政策变更的 API 回调)、运营人员人工上传。更新请求先进入消息队列,由异步管道消费,避免高峰时打爆数据库。

消费端按“文档 ID + 版本号”判断处理方式——新 ID 走新增流程,旧 ID 高版本覆盖低版本(原子替换该文档的全部块),收到删除标记的走下架流程(把对应块标记为 stale,定期物理清理)。为了避免过期的知识永远占据检索位,可以给块加“新鲜度权重”:

每天凌晨的送货车准时抵达,卖完的、过期的食材悄悄退场,冰箱里始终是“鲜货当道”。增量更新让知识库保持呼吸感——既不劳民伤财地全量重建,也不让旧知识赖着不走。

w(t)=e−λ(t−t0)\displaystyle w(t) = e^{-\lambda (t - t_0)}
t₀ 是知识块的入库时间,λ 是衰减速率;越久未更新的块,检索时的得分权重越低,实现软淘汰。
  1. 入库时刻$t_0$ 是知识块最后更新的时间戳。
    t0=最后更新时间t_0 = \text{最后更新时间}
  2. 衰减速率$\lambda$ 是决定“多快过期”的系数,业务变化快的场景取大值。
    λ>0,  业务越敏捷取值越大\lambda > 0,\; \text{业务越敏捷取值越大}
  3. 时间间隔$t - t_0$ 是当前时刻距上次更新的时长。
    Δt=t−t0\Delta t = t - t_0
  4. 指数衰减取 $e$ 的负指数,间隔越大权重越趋近于 0,但永不为负。
    w(t)∈(0,1]w(t) \in (0, 1]

监控与迭代

再稳的餐厅也会根据顾客反馈调整:大众点评上有人说“宫保鸡丁偏甜”,主厨当晚就会调糖量;某个菜一周销量垫底,菜单下轮就换掉。餐厅不是开完就完事,而是一台不断自我修正的机器。

RAG 客服系统的“顾客评价”来自监控指标和用户反馈。线上指标里,最核心的是检索召回类指标,例如 Recall@k——考察前 k 个召回的块里,有多少真正相关;还有无答案率(知识库里明明有,模型却说不知道)和兜底转人工率。业务侧还有 badcase 率:用户点“回答不满意”的比例。这些指标挂在看板上,一旦异常,立即触发定位。

Recall@k 的定义是:

发现问题后进入反馈闭环:人工把 badcase 标记分派到工作台,标注人员判断是“知识缺失、知识过时还是检索失败”,再把修复后的文档推进增量更新管道。每次更新都打版本快照,出问题可以一键回滚。最终沉淀成每周复盘:哪类问题高频出现,就反向优化清洗规则、切分参数或索引权重。

主厨每晚看点评、翻菜品销量,把“顾客说偏甜”翻译成“下次减一成糖”。监控与迭代让知识库不是一座死仓库,而是一个跟着业务口味持续进化的活厨房——这也是 RAG 客服系统能长期服务用户的原因。

Recall@k=1∣Q∣∑q∈Q∣相关块∩前k块∣∣相关块∣\displaystyle \text{Recall@k} = \frac{1}{|Q|} \sum_{q \in Q} \frac{|\text{相关块} \cap \text{前}k\text{块}|}{|\text{相关块}|}
对每个测试问题,看它相关的知识块有多少落进了前 k 个召回结果;对所有问题取平均,就是召回率。
  1. 测试集$Q$ 是人工标注的测试问题集合,每个问题都预先标好了哪些块是“相关块”。
    Q={q1,q2,…,qn}Q = \{q_1, q_2, \dots, q_n\}
  2. 前 k 命中对问题 $q$,系统召回前 $k$ 块,统计其中有几个属于相关块。
    ∣相关块∩前k块∣|\text{相关块} \cap \text{前}k\text{块}|
  3. 单题召回率命中数除以总相关块数,若相关块全被召回则为 1。
    recallq=∣相关块∩前k块∣∣相关块∣\text{recall}_q = \frac{|\text{相关块} \cap \text{前}k\text{块}|}{|\text{相关块}|}
  4. 求平均所有问题召回率取平均,得到系统级 Recall@k。
    Recall@k=1∣Q∣∑q∈Qrecallq\text{Recall@k} = \frac{1}{|Q|} \sum_{q \in Q} \text{recall}_q

AI 应用场景

电商大促前的知识库“新鲜度冲刺”

阶段:增量更新与质量校验

解决的问题:传统做法里,客服资料散落在多个系统,大促前靠运维手动改 FAQ,改错或遗漏会导致客服答错、客诉飙升。AI 方案把运营后台与增量管道打通:政策变更自动触发文档级原子替换,质检拦截矛盾,版本快照支持随时回滚。

具体案例:某电商平台“618“前更新运费险政策,旧政策说”退货运费由买家承担“,新政策改为”平台补贴”。增量管道识别出 doc-2024-072 发生变化,原子替换相关块,冲突检测拦截了旧政策残留,24 小时内全部客服节点生效。

金融机构合规文档的强一致更新

阶段:文档清洗与审批流程

解决的问题:银行内部制度文档更新必须审计留痕,错误知识外泄到客户侧风险极高。AI 方案在预处理流水线加“审批闸门”,高风险改动必须双人复核;全量审计日志与版本快照支持任意时刻回滚。

具体案例:某银行更新《信用卡年费减免规则》,新文档与旧文档在“年费计算周期”表述冲突,冲突检测自动标记,合规经理复核后放行,避免了客户 400 电话大面积误答。

SaaS 帮助中心多语言知识滚动优化

阶段:监控评估与反馈闭环

解决的问题:帮助中心文档多语言、更新频繁,用户反馈“答非所问”难定位。AI 方案用 badcase 工作台聚合用户点踩对话,按“缺失/过时/检索失败”分类,驱动每周文档修正。

具体案例:某 SaaS 厂商发现“导出数据”问题的命中率两周内下滑 15%,监控定位到相关 FAQ 下架后未补新块;运营补充新文档进入增量管道,指标第三天回升。

Python 演示

python100 行
"""RAG 知识库增量更新演示:清洗切分 + 去重入库 + 下架 + 质量门禁"""import hashlibimport refrom dataclasses import dataclass  @dataclassclass KnowledgeChunk:    chunk_id: str    content: str    doc_id: str    version: int = 1    status: str = "active"  # active / deleted     def is_valid(self) -> bool:        return len(self.content.strip()) >= 20  class ChunkSplitter:    """按段落切分,块间保留重叠窗口"""     def __init__(self, chunk_size: int = 200, overlap: int = 40):        self.chunk_size = chunk_size        self.overlap = overlap     def split(self, text: str) -> list[str]:        paragraphs = [p.strip() for p in re.split(r"\n+", text) if p.strip()]        chunks: list[str] = []        buffer = ""        for para in paragraphs:            if len(buffer) + len(para) <= self.chunk_size:                buffer += para + "\n"            else:                if buffer:                    chunks.append(buffer.strip())                tail = buffer[-self.overlap:] if buffer else ""                buffer = tail + para + "\n"        if buffer.strip():            chunks.append(buffer.strip())        return chunks  class KnowledgeBase:    """以文档为单位做增量更新,支持去重、下架与质量门禁"""     def __init__(self):        self.chunks: dict[str, KnowledgeChunk] = {}        self.oplog: list[dict] = []     def ingest(self, doc_id: str, text: str, splitter: ChunkSplitter) -> int:        added = 0        for content in splitter.split(text):            cid = hashlib.md5((doc_id + content).encode("utf-8")).hexdigest()[:12]            if cid in self.chunks and self.chunks[cid].status == "active":                continue            self.chunks[cid] = KnowledgeChunk(cid, content, doc_id)            self.oplog.append({"op": "add", "cid": cid, "doc_id": doc_id})            added += 1        return added     def delete_doc(self, doc_id: str) -> int:        removed = 0        for cid, chunk in self.chunks.items():            if chunk.doc_id == doc_id and chunk.status == "active":                chunk.status = "deleted"                self.oplog.append({"op": "delete", "cid": cid, "doc_id": doc_id})                removed += 1        return removed     def quality_gate(self) -> dict:        active = [c for c in self.chunks.values() if c.status == "active"]        valid = sum(1 for c in active if c.is_valid())        total = max(len(active), 1)        return {"pass_rate": round(valid / total, 3), "valid": valid, "total": total}  def main() -> None:    splitter = ChunkSplitter(chunk_size=200, overlap=40)    kb = KnowledgeBase()     policy = "\n".join([        "退换货政策:",        "七天内支持无理由退货,商品需保持完好。",        "退货运费默认由买家承担,质量问题除外。",        "换货流程:联系客服提交申请,审核通过后寄回。",    ])    print("首次接入分块数:", kb.ingest("doc-001", policy, splitter))     shipping = "\n".join([        "运费说明:",        "订单满99元包邮,偏远地区每单加收10元。",        "大促期间包邮门槛以活动页面为准。",    ])    print("增量新增分块数:", kb.ingest("doc-002", shipping, splitter))    print("下架分块数:", kb.delete_doc("doc-001"))    print("质量校验结果:", kb.quality_gate())  if __name__ == "__main__":    main()
目的:用一份最小可运行的增量管道演示 RAG 知识库的接入、去重、下架与质量门禁——像餐厅每日补货一样只处理变化的部分。
  1. splitter = ChunkSplitter(chunk_size=200, overlap=40)固定块大小与重叠量,对应正文的切分公式 $L$ 和 $O$。
  2. tail = buffer[-self.overlap:] if buffer else ""取上一段结尾作重叠衔接,防止跨块语义断裂。
  3. cid = hashlib.md5((doc_id + content).encode("utf-8")).hexdigest()[:12]用内容指纹做轻量去重,相同的块不重复入库。
  4. if cid in self.chunks and self.chunks[cid].status == "active": continue已激活块的重复提交直接跳过,对应“增量去重”。
  5. kb.ingest("doc-002", shipping, splitter)只处理新增文档,而不是全量重建,对应增量更新触发。
  6. kb.delete_doc("doc-001")旧文档下架,把对应块标记为 deleted,对应过期清理。
  7. kb.quality_gate()统计激活块的合格率,模拟质量门槛 $Q \ge \theta$。

常见误区

误区有些同学认为每次业务变更都该把所有文档重新跑一遍,这样知识库才“绝对新鲜”,反正线上数据量也不大。

正解全量重建的代价极高:重嵌全部文档要几小时到几天,期间服务风险陡增。生产系统用增量更新,以文档为单位做原子替换,配合新鲜度权重软淘汰,既保持时效又可控制。就像餐厅每天补货,而不是清空冰箱重买。

为何常见:原型阶段数据量小,全量重建几分钟就完事,把“全量很容易”的错觉带进了生产环境。

误区新手觉得块切得越小,检索命中越精确,于是把文档切成一两句话甚至一个词,结果回答却牛头不对马嘴。

正解块太小上下文断裂,模型只能靠猜;块太大又会稀释语义导致召回不准。正确做法是 300~800 token + 语义边界 + 重叠窗口 + 父子分块:小块负责精准召回,大块负责提供完整上下文。

为何常见:人们把“关键词精确匹配”的直觉错套在向量检索上,忽略了语义检索需要上下文完整性。

误区新手觉得 PDF 转纯文本是流水线小事,回答效果差就该调 prompt 或换大模型,跟清洗没关系。

正解水印、页眉、乱码、表格错乱都会污染 Embedding 向量,导致检索召回到垃圾块;元数据缺失还会让版本管理和增量更新失效。清洗是质量的第一杠杆,脏数据进场后再调模型也救不回来。

为何常见:效果问题最容易被归因到模型层,而脏数据的影响是隐性的——主厨背了锅,食材商却没人注意。

误区接触 Embedding 后觉得 BM25 等关键词检索是过时技术,在架构里干脆不保留。

正解向量懂语义但抠不精确条款,产品编号、政策条文这类内容要靠关键词命中才稳。生产系统普遍做混合检索:向量分和 BM25 分加权融合,再交给重排模型精排,两类互补才能扛住真实 query 的多样性。

为何常见:演示时向量检索效果惊艳,小规模数据样本掩盖了它在“精确匹配”上的短板。

误区认为去重就是拿哈希比对字符串,一样就删、不一样就留,简单粗暴。

正解同一政策的两种表述(同义改写、不同来源转载)字符未必相同,却会在向量空间里互相挤占位置、干扰排序。要用 Jaccard 或向量相似度做语义去重,再用冲突检测处理“说法矛盾”而非“说法相同”。

为何常见:把去重想成字符串匹配,忽略了业务文档天然存在多来源、多表述的特点。

面试题

Q1. RAG 客服系统构建知识库的第一步通常是?

单选题 · easy

  • A.训练客服大模型
  • B.文档接入清洗← 正确答案
  • C.部署推理服务
  • D.设计前端页面

答案:B。知识库是 RAG 的“食材仓库”,构建起点是把各种来源的文档接入并清洗成干净文本,就像后厨先验收处理食材。训练大模型对应“换厨师”,部署与前端属于交付环节,都不是知识库构建的第一步。

Q2. 清洗阶段提取“标题、作者、生效日期”属于哪类操作?

单选题 · easy

  • A.页眉页脚清理
  • B.元数据提取← 正确答案
  • C.文本向量化
  • D.重复内容合并

答案:B。标题、作者、生效日期描述文档身份信息,类比给食材贴“产地、保质期”标签,属于元数据提取。页眉页脚清理属于噪声过滤,向量化在清洗之后,重复内容合并属于去重环节。

Q3. 切分知识块时保留 10%~20% 重叠窗口,主要目的是?

单选题 · easy

  • A.减少存储占用
  • B.保持跨块语义连续← 正确答案
  • C.提升向量维度
  • D.替代人工审核

答案:B。像切菜时“藕断丝连”,重叠窗口让被拦腰截断的句子在相邻块里依然完整,避免语义断层。它不减少存储、不提维度,也替代不了人工审核。

Q4. 关于余弦相似度,下列说法正确的是?

单选题 · mid

  • A.只衡量向量方向一致度← 正确答案
  • B.只衡量向量长度大小
  • C.统计文本词频高低
  • D.计算文本字数多少

答案:A。余弦相似度 = 点积 ÷ 模长乘积,结果是夹角余弦,只看方向不看长度,适合衡量语义贴近程度。B 说反了,C 对应词频统计,D 是字面长度,与余弦无关。

Q5. 语义去重中 Jaccard 相似度的正确算法是?

单选题 · mid

  • A.交集大小除以并集大小← 正确答案
  • B.并集大小除以交集大小
  • C.向量点积再除以模长
  • D.文本长度相减取绝对值

答案:A。J(A,B)=|A∩B|/|A∪B|,即两段文字共同词数占全部不重复词数的比例,越接近 1 越像在说同一件事。B 把分子分母颠倒,C 是余弦相似度,D 是长度差,都不对。

Q6. 知识库质量校验中,“新政策与旧政策说法矛盾”应触发?

单选题 · mid

  • A.完整性检查
  • B.冲突检测← 正确答案
  • C.语义去重
  • D.重叠窗口

答案:B。新旧文档对同一规则表述相反属于“知识打架”,对应冲突检测,需标记并走人工复核。完整性检查看字段是否齐全,去重解决“说同一件事”而非“说法矛盾”,重叠窗口属于切分环节。

Q7. 知识质量校验通常包含哪些环节?

多选题 · mid

  • A.完整性检查← 正确答案
  • B.语义去重← 正确答案
  • C.冲突检测← 正确答案
  • D.模型微调

答案:ABC。质量校验像后厨品控:完整性查“食材是不是完整”,语义去重查“两块是不是同一件事”,冲突检测查“有没有自相矛盾”,三者缺一不可。模型微调属于大模型训练范畴,与知识库校验无关。

Q8. 增量更新中“以文档为最小单位原子替换”的含义是?

单选题 · hard

  • A.每次重建整个知识库
  • B.单文档新旧完整切换← 正确答案
  • C.只新增内容不删除
  • D.只更新向量丢弃元数据

答案:B。原子替换指某个文档变更时,把该文档的旧块整体换为新块,避免“改了一半、新旧混用”的中间态。A 是全量重建,C 只增不删,D 丢弃元数据会导致版本与回滚失效。

Q9. 知识库版本回滚的前提条件是什么?

单选题 · hard

  • A.每次更新前保存快照← 正确答案
  • B.定期人工记录日志
  • C.全量重建向量索引
  • D.删除所有历史版本

答案:A。回滚像“恢复上次的食材盘点”,前提是每次增量更新前对索引、元数据与块映射打快照,出事才能一致性地切回。人工日志不可自动恢复,重建或删除历史版本都是破坏性操作。

Q10. 关于知识库构建与更新,以下说法正确的有?

多选题 · extreme

  • A.清洗能提升向量化质量← 正确答案
  • B.融合向量分与关键词分← 正确答案
  • C.冲突检测可拦截新旧矛盾← 正确答案
  • D.知识库更新无需监控反馈

答案:ABC。脏文本会污染 Embedding,清洗是质量的底座;混合检索融合向量与关键词分数,专有名词更稳;冲突检测正是为拦“新旧矛盾”而生。D 错误——监控与反馈闭环让知识库持续进化,是生产系统的必备环节。