主题
全景图:一个离线知识库系统由哪些部分组成
本章要回答的问题:一个能回答"我们厂设备怎么保养"的系统,从模型到界面到底由哪几块拼起来?
从一句需求拆出六个部件
需求是这样的:车间主任对着一个内网页面问"ZJ-03 的液压油多久换一次",系统要给出答案,并指出这个答案来自哪份保养计划的哪一条。整套东西不能连外网。
示例里的设备和台账来自 corpus/ 的合成语料(化名工厂、合成单据),只用来演示链路,不代表任何真实企业。
把这句话拆开,会得到一条完整链路(图 0.2-1):用户问题从接入层进来,编排层决定要不要检索、把材料拼进提示词,知识层负责找对材料,模型层生成,最后再经接入层流式送回去。运行层和交付层不在这条路径上,它们是托底的——决定这套东西能不能稳住、能不能交出去。
这六层不是抽象的分类,而是故障归因的坐标系。一条错误回答可能来自任何一层,判断错层,后面的调优全是白费。
每一层在做什么
接入层负责"把话说清楚、把结果送出去"。包括身份认证、会话历史、流式输出(Server-Sent Events 或分块响应)、超时与重试。它不涉及模型,但决定了用户对"快"的第一印象:首字延迟(TTFT)主要由它后面的链路决定,而前端能不能边生成边显示,由它决定。
编排层是流程控制器:收到问题后决定走哪条路——直接答、先检索再答、还是反问澄清;把检索到的片段拼进提示词;控制上下文预算;解析模型的输出和引用标记。RAG(Retrieval-Augmented Generation,检索增强生成)的原始论文把这套做法定义为"生成前先检索相关文档"(引用 [1])。工程上,这一层是主要业务逻辑所在,也是后面第五、六部分反复修改的地方。
知识层决定"资料能不能被找到"。文档解析(PDF、Word、扫描件)、切分策略、Embedding 模型、向量索引、稀疏检索、重排,全部在这一层。它不产生任何一个字,但决定了模型看到的材料对不对。经验上,RAG 系统的失败大部分发生在这里,而不是模型本身。
模型层是"把 token 变成 token"的部分:权重文件、分词器、对话模板、推理引擎。同样的模型,用不同的量化格式、不同的引擎(例如 llama.cpp,引用 [2])、不同的服务框架(例如 vLLM,引用 [3]),表现可能差出几倍。这部分对应第二、三、四部分。
运行层关心的是"能不能稳住":显存够不够、并发上来会不会排队、GPU 利用率是多少、有没有 OOM、日志里能不能看出是哪一步慢。这部分对应第七、八部分。
交付层是这本书和"跑个 demo"的分界线:离线环境怎么装、断网怎么验证、验收标准是什么、出了问题谁来看。对应第八部分。
数据是怎么流动的
系统其实有两条流水线,一条离线,一条在线,只在索引处交汇(图 0.2-2)。
离线入库(文档进来时跑一次,或定期增量):PDF/Word 解析成文本,切分成块,每块算一个向量,连同元数据(文档名、页码、版本、权限)写进索引。
在线问答(每次提问跑一次):问题先做改写与扩展,再去查索引,召回的结果合并去重、重排,取前 N 块拼进提示词,最后由模型生成并附上引用。
两条线上各有一个容易出错的接缝:离线侧是"切分是否保留了完整语义",在线侧是"检索回来的块是否真的能回答问题"。第六部分的评估方法,就是分别给这两个接缝装仪表。
一条最小可用的链路
不需要一上来就把六层都做全。能回答一个真实问题的最小系统,只需要四样东西:
- 一个能跑的模型(第二、三部分选出来并部署好);
- 一个索引,哪怕只是把几十段文本算成向量存进文件(第五部分);
- 一个固定的提示词模板,要求模型"只根据材料回答,并给出处"(第五部分);
- 一个命令行或网页入口(第八部分)。
这条链路大概率答得不好,但它能被测量。之后每一次改进,都应该对应评估集上的一个数字变化,而不是"感觉更聪明了"。这是本书和大多数教程在方法上的区别。
常见的失败长什么样
把症状映射到层,是最实用的排查技能。下面这张表后面会被反复引用:
| 症状 | 最可能的层 | 先看哪里 |
|---|---|---|
| 答得很流畅,但答的是别的东西 | 知识层(检索没召回) | 检索结果原文,而不是模型输出 |
| 检索到了正确段落,回答却漏掉关键条件 | 编排层(上下文被截断或提示词冲突) | 实际送进模型的完整 prompt |
| 同样的问题,两次答案差很多 | 模型层(采样参数、思考模式) | 温度、top-p、是否开启思考 |
| 首字要等十几秒 | 运行层 / 模型层(prefill 太长、显存不足换入换出) | 首字延迟(TTFT)与显存占用 |
| 一个人用很快,五个人用就排队 | 运行层(并发与批处理) | 队列长度、每 token 时间 |
| 换台机器就跑不起来 | 交付层(依赖、路径、驱动) | 离线安装清单 |
接下来读什么
序章剩下的两节处理两个前置问题:硬件差异(0.3)和阅读方法(0.4)。然后进入第一部分,先回答"模型为什么这么吃内存"——这个问题不搞清楚,后面的选型和部署都只能靠试。
参考资料
- Lewis, P. et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. arXiv:2005.11401. https://arxiv.org/abs/2005.11401
- ggml-org. llama.cpp. https://github.com/ggml-org/llama.cpp
- vLLM Project. vLLM Documentation. https://docs.vllm.ai/