LLM 面试备考
首页/系统设计

设计题 01:企业知识库问答(RAG)

权重:★★★ 形式:45 分钟系统设计轮
素材来源:Own — HKGAI 1823 香港政府聊天机器人、HKJC Race Bot 的知识库检索、wiener_project 的混合检索
相关知识点:03-rag/(全 6 篇)、05-agent-evaluation/rag-eval.md、06-agent-infra/latency-cost.md


1. 题干#

「我们有个客户,内部有几万份文档 —— 制度、流程、产品手册、会议纪要都有。员工现在靠搜索和问同事找信息,效率很低。他们想做一个内部问答系统,员工用自然语言提问,系统从文档里找答案。你来做,怎么设计?」

注意这个题干有多模糊:没说文档格式、没说多少人用、没说能不能出错、没说更新频率、没说要不要权限。这不是题干写得差,这是刻意的。 面试官在看你什么时候发现信息不足。


2. 第一步:澄清(这一轮最重要的动作)#

绝对不要听完就开始画架构。开场第一句应该是:

「在动手之前,我想先确认几件事 —— 因为这几个答案会显著改变方案。」

必问清单(按对方案的影响排序)#

# 问题 为什么它会改变方案
1 答错的代价是什么? 制度类问答答错可能导致合规事故 → 需要强制引用来源 + 不确定时拒答;而「找会议纪要」答错只是不方便 → 可以更激进地生成
2 答案必须可溯源吗? 必须溯源 → 每个 chunk 要带文档 ID 和定位信息,且生成时强制引用;不必须 → 可以省掉一堆工程
3 文档权限分级吗? 有权限 → 检索层必须做权限过滤(这是在向量库之前的一层,很多人会漏),且不能只靠 prompt 约束
4 文档更新频率? 每天更新 → 需要增量索引和失效机制;一年一版 → 全量重建即可
5 文档格式分布? PDF 扫描件占比高 → OCR 是主要工作量,不是 RAG;纯 markdown → 基本可以跳过解析层
6 多少用户、多少 QPS? 决定要不要自建推理、要不要缓存、成本量级
7 用户问的是「事实查询」还是「综合判断」? 前者 → 扁平 chunk 检索够用;后者(「我们的报销政策在几个文件里说法不一致,以哪个为准」)→ 需要 GraphRAG 或重排序以上的手段
8 能不能接受「我不知道」? 决定拒答阈值。企业场景通常必须能拒答,否则用户会信任错误的答案

面试技巧:不要机械地把 8 个问题全问完 —— 那像在念清单。挑影响最大的 2–3 个先问,拿到答案后再问下一层。追问本身也是考点。


3. 参考架构#

假设澄清后得到:有权限分级、必须溯源、PDF 为主、日更、事实查询为主、允许拒答。

┌─ 离线索引 ────────────────────────────────────────────┐
│  文档源 → 解析(OCR/PDF/MD) → 清洗 → 分块             │
│         → 嵌入 → 向量库 + 全文索引(BM25)             │
│         → 元数据(权限标签/文档ID/版本/时间)          │
└───────────────────────────────────────────────────────┘
                        ↓
┌─ 在线查询 ────────────────────────────────────────────┐
│  用户问题                                            │
│    ↓ ① 权限过滤(在检索前,不是检索后)              │
│    ↓ ② 查询改写(口语 → 检索友好)                   │
│    ↓ ③ 混合检索:BM25 ∥ 向量                         │
│    ↓ ④ 融合(RRF)                                   │
│    ↓ ⑤ 重排序(cross-encoder,取 top-n)             │
│    ↓ ⑥ 上下文组装(带来源标记 + token 预算截断)     │
│    ↓ ⑦ 生成(强制引用 + 不确定则拒答)               │
│    ↓ ⑧ 引用校验                                     │
│  答案 + 来源链接                                      │
└───────────────────────────────────────────────────────┘

画图时要讲的三个点(这是把架构和「背组件」区分开的地方):

  • ① 权限过滤必须在检索前。如果先检索再过滤,一是会泄漏「存在这份文档」的信息(通过答案数量和相关性),二是 top-k 被无权限文档占满导致有效结果变少。正确做法是把权限作为向量库的元数据过滤器(pre-filter)。
  • ③ 为什么混合检索而不是纯向量。企业文档里全是专有名词和编号(「GB/T 1234」「第三章第二节」「流程 A-07」)。这类精确 token 稠密向量召回很差,而 BM25 恰好擅长。代价是多一路检索和融合逻辑。
  • ⑦ 拒答不是一个 prompt 技巧,是架构的一部分。要有明确的「证据不足」判定 —— 比如重排后的最高分低于阈值、或引用校验不通过 —— 并且这个阈值要可调、可评测。

4. 关键决策与权衡#

4.1 分块:按什么切#

  • 起步方案:按文档结构切(标题层级 + 递归字符切分,chunk 256–1024 token,overlap 10–20%)。
    ⚠️ 口径统一:此处原写「~500–1000 / 10–15%」,与 03-rag/chunking.md 的原口径(200–512)不一致,已一并对齐到 256–1024 / 10–20%。强调这是「起步区间」而非答案 —— 面试里正确的答法是「先落在这个区间,再用带 gold chunk 标注的小评测集扫 chunk size × overlap,以 Recall@k 选优」。
  • 为什么不用固定长度:会切断表格和条款,而企业文档的答案常在一个完整条款里。
  • 代价:结构解析要针对格式做适配(PDF 的标题层级不一定能解析出来),工程量大。
  • 进阶(先不做):父子分块 —— 用小 chunk 检索、把父 chunk 给模型,缓解「检索粒度 vs 生成上下文」的矛盾。但这是第二阶段优化,不是起点。

4.2 要不要用 GraphRAG#

主动说不做,并且给理由 —— 这是资深的信号:

「GraphRAG 擅长的是跨文档的全局性问题(『这几个文件在哪些点上有冲突』)。但这个场景 90% 是事实查询,扁平检索够用。GraphRAG 的索引成本很高、更新很难(改一份文档可能要重建社区摘要),在这个场景是过度设计。我建议先不做,等发现某一类问题检索确实解决不了再评估。」

同样,多智能体也不要上。单轮问答不需要 agent 编排 —— 加进去只是增加延迟和失败面。

4.3 拒答阈值怎么定#

  • 不能凭感觉。要构造一批「知识库里没有答案」的问题当评测集,测「该拒答时拒答的比例」和「不该拒答却拒答的比例」,在两条曲线之间选点。
  • 这正好呼应 05-agent-evaluation/rag-eval.md 里的 abstention correctness 指标。

4.4 更新与失效#

  • 增量索引:文档指纹(hash)判断变化,只重建变化的文档。
  • 失效比新增难:文档删了,其 chunk 要从索引里彻底移除;文档改了,旧 chunk 要清掉。只做新增不做删除,会长期产生「幽灵答案」。
  • 版本意识:答案要能回答「这是哪个版本的制度」。

4.5 明确不做什么#

这一节要主动说出来,不要等面试官问。 把「我判断这些在这个场景下不值得做」讲出口,是资深和初级最大的分界 —— 初级候选人倾向于把知道的组件全堆上去。

不做 理由
不上 GraphRAG 见 4.2。这个场景 90% 是事实查询,扁平检索够用;GraphRAG 索引成本高、文档更新时要重建社区摘要,是过度设计
不上多智能体 单轮问答没有多角色协作需求。加编排只增加延迟和失败面 —— 这是 04-agent-architecture/multi-agent.md 里「上下文隔离是唯一真实收益」的直接推论
不做微调 这个场景要的是「检索到正确的私有知识」,不是「改变模型行为」。微调擅长学格式与风格,不擅长注入易变的事实 —— 知识更新一次就要重训,成本结构不成立
第一阶段不做父子分块 有效但要先证明基础方案不够。先上简单方案、用评测暴露瓶颈,再针对性加复杂度
不自建推理 内部工具的并发量通常撑不起自建 vLLM 的回本线。除非澄清后发现 QPS 很高,否则直接用 API 更划算

表述要点:说「不做」时必须带理由和条件 —— 「不做 X,因为 Y;如果发现 Z,我会重新评估」。只说「不做 X」听起来像不会。说「不做 X,因为 Y,除非 Z」才是工程判断。


5. 容量与成本直觉#

面试官常问「这大概多少钱」。不要求精算,但要有量级感觉:

  • 索引:1 万份文档 × 平均 20 页 ≈ 20 万页 ≈ 数千万 token。一次性嵌入成本很低(嵌入比生成便宜一两个数量级),通常不是瓶颈。
  • 存储:向量 + 原文 + 元数据,量级在几十 GB,普通机器能扛。
  • 在线:真正的成本决定项是每次查询注入多少上下文。这也是为什么 ⑥ 要给上下文设 token 预算、⑤ 要做重排序(把 50 个候选压到 5 个高质量 chunk)。
  • 要不要自建推理:取决于 QPS。内部工具(几百人)多半直接用 API 更划算 —— 自建 vLLM 要有足够并发才回本,这是 06-agent-infra/latency-cost.md 里的核心判断。

说出「这个规模下自建推理不划算」比说「我用 vLLM」更能体现工程判断。


6. 面试官会怎么往下挖#

第一层:澄清与动机#

  • Q:你会先问我什么? ← 见第 2 节,这是开场题
  • Q:为什么不直接上长上下文模型,把文档全塞进去? ← 成本、延迟、且「塞得进」不等于「找得到」(lost in the middle)

第二层:技术细节#

  • Q:检索错了和生成错了,你怎么区分? ← 用 oracle context 反事实实验,见 03-rag/pipeline.md
  • Q:chunk size 你怎么定? ← 必须有依据,不是「拍一个」
  • Q:权限过滤放在检索前还是后?为什么? ← 见 3 节的 ①
  • Q:用户问了一个库里没有的问题,系统怎么表现? ← 拒答机制 + 怎么评测
  • Q:文档里同一件事有两个说法,系统怎么办? ← 这题很难。诚实答法:当前方案会随机给一个并对一个来源;要正确处理需要冲突检测,属于已知局限

第三层:工程与生产#

  • Q:怎么知道系统上线后有没有变差? ← 需要固定的回归评测集 + 线上失败回流
  • Q:文档更新了,怎么保证答案跟着更新? ← 增量索引 + 失效机制
  • Q:成本怎么控制? ← 上下文预算、重排序压缩、缓存高频问题
  • Q:怎么排查线上某个具体错误回答? ← 链路追踪,每步输入输出都要留

第四层:反思与边界#

  • Q:这个方案什么时候会不够用? ← 综合判断型问题、跨文档冲突、多跳推理
  • Q:如果客户说「答错一次就不能接受」,你怎么回? ← 诚实说做不到 100%,然后把方案改成「强制引用 + 高置信才作答 + 低置信转人工」,并说明这会让可用性下降
  • Q:这个项目里最容易被低估的工作量是什么? ← 文档解析和权限体系,不是模型

7. 自测#

1. 为什么权限过滤必须在检索之前,而不是生成之后?说出两个具体后果。

答案要点

① 泄漏信息 —— 用户能通过答案的成败或相关性推断出「存在一份自己无权看的文档」;② 挤占 top-k —— 无权限文档占据召回名额,导致有效结果变少、召回率下降。正确做法是把它作为向量库的元数据 pre-filter。另外要注意:不能只靠 prompt 约束「不要引用无权文档」,那是不可靠的软约束。

2. 这个场景为什么不该上 GraphRAG 和多智能体?请从「方案必要性」角度说明。

答案要点

GraphRAG 的价值在跨文档全局性问题,而该场景 90% 是事实查询,扁平检索够用;其索引成本高、文档更新时可能要重建社区摘要,属于过度设计。多智能体价值在上下文隔离与多角色协作,而单轮问答没有多角色需求,加进去只增加延迟和失败面。主动判断「不需要」并说出理由,比堆组件更能体现工程判断。

3. 客户要求「答错一次都不能接受」,你怎么回应?

答案要点

先诚实说明 LLM 系统无法做到 100% 正确,不能承诺不可能的事。然后把方案改为保守策略:强制引用来源、仅在证据充分时作答、低置信一律转人工,并明确告知客户这会让「能自动回答的比例」下降 —— 把选择权交回客户,让他用可用性换准确性。这体现了 FDE 的诚实与边界意识。

4. 文档被删除后,如果只做增量「新增」不做删除处理,会发生什么?

答案要点

产生「幽灵答案」:已删除文档的 chunk 仍在索引中,系统会继续基于不存在的内容作答,而且引用链接可能已失效。这类错误很难被用户发现,因为答案看起来有来源。因此索引维护必须处理删除与更新,通常用文档指纹比对 + 物理删除旧 chunk。

5. 换成「这个知识库要支持 5000 人同时用,QPS 500」,你的架构哪些部分要改?

答案要点

主要压力在生成侧而非检索侧:需要考虑缓存高频问题、控制单次上下文 token、评估自建推理(vLLM 连续批处理)是否比 API 更划算、以及限流与降级策略。检索层通常可加副本横向扩展;嵌入不需要在线跑。要点是识别出瓶颈从「检索」转移到「生成吞吐与成本」,而不是把所有组件都改一遍。