夜雨聆风学习资料网

ARTICLE · 1066062

补文档补到怀疑人生?问题不在你手慢——手快不过上游出料的速度

补文档补到怀疑人生?问题不在你手慢——手快不过上游出料的速度

我是小兵,一个动手派AI架构师。“AI工程化实战”系列第 12 篇。

手再快,也快不过上游出料

第 11 篇《知识库防腐坏》结尾我写下一句判断:粮仓的地基,在第 12 篇。写完那天晚上,我亲手给这句话补了个注脚。巡检报出库里缺 X200 的保修页,我心想补一份文档能有多难:上官网扒 PDF、下载、转文本、肉眼比对有没有重复、手动切成段、跑一遍向量化脚本入库。等我抬头看表,半天没了;更糟的是我忘了记这是哪个版本、这些切片是从哪份料切出来的。第二天打开待补清单,上面又多了三条。

那一刻我盯着那张越补越长的单子认了:问题不在我手慢——手再快,也快不过上游出料的速度。我缺的从来不是这一批料,是一条从文档采集、清洗、切片、向量化,到版本管理、血缘、权限,再到离线批量更新和在线增量更新双链路隔离的数据工程底座

第 11 篇管“发现和处置”(体检、止血、换版纪律),本篇管“料怎么持续、干净、可追溯地运进来”——接的是第 11 篇那张待补清单。

六段流水线:把补粮从人等料变成流水线自己跑

一句话主线:第 11 篇交过来三样东西(待补清单、处置单、版本号)当入口 → ① 采集 → ② 清洗 → ③ 切片 → ④ 向量化 → ⑤ 版本/血缘/权限 → ⑥ 双链路 → 产出可被换版门接住的新版本快照 V_{k+1}。

知识库不是一座建好就没人喂的静态仓库,是一台需要“进料口”的机器。六段各回答一个问题、各拦一类脏东西:

  • ① 采集:料从哪来、谁准进?把待补清单变成采集任务,设来源白名单闸,sensitive 在入口就拦。这是整条流水线唯一能在源头拦“来源不可信/涉密”的闸——不可信的料比没有更糟,它会在末端以幻觉、越权可见的形式被召回。
  • ② 清洗:脏料不进城。归一、脱敏、校验(元数据过 schema 闸)、去重四道闸,默认全开,把“干净”当默认值。
  • ③ 切片:怎么切才不切坏语义。按文档类型分层切,每块挂元数据(chunk_id/父文档 raw_id/偏移/version)。
  • ④ 向量化:文本变成能检索的向量。真实 embedding 模型藏在接口后,桩换来换去流水线逻辑一行不改。
  • ⑤ 版本/血缘/权限:三张表撑起这一段——每份料有号、来路可溯、谁可见。
  • ⑥ 双链路:离线批量重建 + 在线增量追加,隔离、别打架。

三个取舍点,每个都真付过费

取舍一:清洗最贵,却也是唯一能在源头拦下脏料的那道闸。归一要处理编码残渣、脱敏要跑 PII 识别、校验要维护元数据规则、去重要比指纹,每道都是算力加规则维护。有个被反复引用的口径说数据准备和清洗要吃掉数据工作者 25%~80% 的时间——Anaconda 摸底约 38%~45%、CrowdFlower 称 60%~80%,口径宽、别拿一个数当铁律,但方向一致:清洗就是最费人的那一段。可它是源头过滤:一份脏料在源头拦掉只要几毫秒,漏进库之后要靠第 11 篇的巡检扫出来、软下架、再换版清,那是十倍的账。我认这道闸的贵,因为让脏料进城的账,巡检已经替我算过一遍了。

取舍二:切片按文档类型分层切,别迷信“固定 token 数一把梭”。最常见的懒办法是“按固定字符数硬切”,它会把“整机保修 12 个月”从中间切断——“整机保修”和“12 个月”落进两个块,检索只召回半句,模型答“保修”却答不出几个月。正确做法是按文档类型分层:政策/条款按语义段切、参数/表格按行按表切、长叙述按段落加滑窗重叠切。2026 年公开口径的基线是递归切约 512 token、10%~20% overlap;块大小跟着查询类型走——事实型 query 用 256~512 token、分析/多跳 512~1024、整段理解 1024~2048。还有一条:响应质量在约 2.5k token 上下文之后骤降,切块过大稀释检索信号、过小切断上下文,别走两头极端。分层切要维护多种切法,复杂度和调试成本比“一把梭”高,但切坏的账比多维护几种切法贵得多。这个取舍的代码落点是一对断言:chunk_by_structure 把“整机保修 12 个月”完整落在一块里,naive_fixed_chunk 专门在“整机保修”和“12 个月”中间下刀当反面教材。

取舍三:双链路是分工,不是二选一。很多人纠结“到底用全量重建还是增量更新”,这是个伪问题。在线增量解决“料要新鲜”(新 FAQ 当天能查到),离线批量解决“库要一致”(换切片策略、换 embedding,影响全库的必须整版重算)。正确的做法是两条都建:新料攒批走在线增量追加(只 append、不碰全量索引),结构性变更走离线批量重建(独立版本命名空间、过门才上)。增量不是“省掉全量重建”的偷懒——5 万文档语料、每天 500 条变更,只重 embedding 那 500 条,比全量重建便宜约 100 倍、秒级生效;反过来,1M 文档语料批量 embedding 约 24~48 小时,所以结构性变更才放低峰窗口走批量。代价是两条链路写同一份元数据表要防并发——重建期间到货的新料必须落隔离区,绝不能混进正在重建的那版。

踩过的坑:全量重建撞上在线增量

某次我跑全量重建(换了切片策略),重建跑了两小时,期间在线增量任务还在往索引里追加新料。重建结束时索引里混着“新策略的旧料 + 旧策略的新料”,检索命中忽新忽旧,比第 11 篇的“软下架不彻底”更阴——那个至少还能看出旧料没下架,这个是同一批料策略都混了,肉眼根本看不出来。

根因是两条链路共用一张索引、互相没隔离。当时我把它当成“两个任务各跑各的”,其实它们物理上就共用一张索引,根本没法各跑各的。修复很简单:重建时设一个 rebuilding 开关,在线追加落独立隔离区 pending,重建完成、过门、切换之后,增量再对齐到新版。双链路不是两条流水线的简单叠加,是“重建期间增量不污染”的隔离纪律——省掉隔离,重建反而把库搅得更乱。

料运进来了,问题还没完

同样一张待补清单,这次不用手补了:料从采集段进来、清洗段拦掉脏的、切片段按语义切、向量化段批量入库、版本/血缘/权限段给每份料上号上痕上权限、双链路各走各的,产出 V_{k+1} 交给第 11 篇换版门。清单还是那张清单,但它从“我一天补不动的体力活”,变成了“流水线自己会跑的进料口”。第 11 篇给的是“粮仓会烂、要体检要换血”的运营纪律,本篇给的是“补粮的流水线”——纪律解决“烂了怎么办”,地基解决“料怎么持续运进来”。

有一段取舍得单独提醒:换 embedding 模型或向量维度是最贵的一次变更。真实维度是 3072/1536/1024 三档(text-embedding-3-large、text-embedding-3-small、BGE-M3),新旧向量不可比,要么整版重 embedding、要么用对齐适配层映射,两条都走换版不原地改——维度是个旋钮,换维度 = 换版本。

工具面一句话收口:demo 里三张表生产化就是“DVC 3.67.1 管数据版本 + OpenLineage 2-0-2 / Marquez 0.50.0 管血缘 + 一张 ACL 表”,解析换 Unstructured 0.23.1/0.24.0、编排换 Airflow 3.3.1 / Dagster 1.13.21 / Prefect 3.8.5——六段逻辑一行不改。小团队别被“六段”吓住,它可以渐进搭:先跑通采集 + 清洗 + 切片(料能干净入库),版本/血缘和双链路随规模补。

下一篇《LLMOps 不完全指南》:数据底座是地基,LLMOps 是地基建起来之后,怎么把整栋楼管起来。


这篇是脱水版。完整版含可离线跑的 mini_data_pipeline.py + 7 个断言的测试、流水线六段阶段表、三个付费踩坑、选型对比和工具对接,已同步发布在 CSDN。点文末“阅读原文”看完整代码和可跑 Demo。

评论区聊聊:你们的知识库更新还靠人肉补吗?有没有“补一篇花半天、第二天又来三条”的时刻?

我是小兵,一个动手派AI架构师。这里只写自己跑过、摔过、复盘过的AI工程化案例。如果你想持续收到这类实战内容,点击关注,下篇见。

相关学习资料