逐日AI
第 2 周 · D14约 5 小时

综合项目与复盘:多租户企业知识库问答,一张 RAG 决策地图与面试专题

把十三天的东西收成一个可以放进作品集的项目:多租户、带引用、有评估面板的企业知识库问答。然后把整门课压缩成一张决策地图,回答面试里最常被追问的那些问题。

今日目标 0/3

登录后可以勾选并保存进度。

今日目标

  1. 能把前十三天的模块组装成一个多租户、带引用与评估面板的完整项目并说清架构
  2. 能画出一张 RAG 决策地图,面对一个新需求时按图给出该用哪套配置及其理由
  3. 能面对面试里的追问,用本课的数据和取舍说清自己每一个技术选择

今天不讲任何新原理。前十三天每一天都留下了一个零件和一组数字,今天只做三件事:把零件装起来、把数字收成一张地图、把这张地图变成你在面试里能讲出口的东西。读完回到页面顶部把三条目标勾掉。

小白版讲解

一、作品集项目的三个判据

先说一件不太好听的事:「我做过一个 RAG 项目」这句话在面试里的信息量接近于零。 现在人人都做过。真正把人分开的是接下来那三个追问——你怎么知道它好用?哪个决定你现在会改?如果只能再做三件事,你做哪三件?

所以一个能进作品集的项目,判据不是「功能多」,而是这三条:

第一,能演示。 五分钟之内,你要能投喂一份文档、问一个问题、点开引用看到原文、再打开面板看到指标。做不到这一串,你的项目在别人眼里就只是一段 README。这也是为什么今天的实验最后要求你写一段演示脚本——演示路径本身就是一次设计评审,走不顺的地方通常就是设计有问题的地方。

第二,能讲清取舍。 每一个配置项后面都要有一句「因为什么,所以选它」,而且那个「因为」最好是你自己量出来的。原料前十三天已经替你攒够了:为什么先做 BM25 再上向量、为什么重排只作用于前二十条、为什么门槛卡在原始分而不是融合分——每一条背后都有一次实测。

第三,有可复现的数据。 不是「效果不错」,而是「在这份三十篇语料、这二十道题、六百 token 预算下,召回率 93.8%、多跳档 75.0%、无答案题拒答率 0.0%」。最后那个 0.0% 尤其要写出来——一份只报好数字的项目说明,比不报数字更可疑

这三条里最难的是第二条。取舍的原料你已经有了,但十三天散落的几十个结论怎么组织成一件能随身带走、遇到新需求就能翻开的东西?那需要一张地图。

二、多租户:一件事,三处落点

先把项目从单租户改成多租户,因为它是「企业知识库」这四个字的最低门槛,也是最容易做错的地方。

做错的形态高度一致:把租户当成查询时的一个筛子。 检索照常在全库跑,拿回结果再筛掉不属于这个客户的。D13 已经论证过为什么这是错的——越权的块已经被读进内存、参与过排序、大概率还进了日志;更现实的后果是结果被稀释,用户看到的是「查不到」,你在日志里看到的是一次正常检索。

正确的做法是把这一件事拆到三个地方各做一遍,缺一处后面两处就失效:

落点一 摄取时打标 谓词在打分之前 落点三 评估时分租户统计 原始文档 带租户标签的块 索引 用户提问 落点二 检索时过滤 候选与回答 面板
Mermaid 源码
mermaidmermaid
flowchart LR
  A[原始文档] -->|落点一 摄取时打标| B[带租户标签的块]
  B --> C[索引]
  D[用户提问] --> E{落点二 检索时过滤}
  C --> E
  E -->|谓词在打分之前| F[候选与回答]
  F -->|落点三 评估时分租户统计| G[面板]

落点一,摄取时打标。 块 id 从 doc-001#c01 变成 t-yunti/doc-001#c01:两个工作区的块在库里是物理上不同的行。实验里同一份三十篇语料投影成两个工作区——总部订阅四个部门,得到 134 块(与第七天那套系统逐字相同);试用客户只导入了产品手册,得到 44 块。打标发生在摄取时,不是查询时,否则后面两处根本没有可用的字段。

落点二,检索时过滤。 谓词和排序、LIMIT 写进同一条语句,关键词那一路同理——不在范围里的块一次都没参与打分。差别是可以量的:限定到客服范围(23 块)时,前置过滤的向量一路老老实实返回 23 条;换成「先全库排序取前 50 再筛」,返回的只剩个位数,而你不会收到任何报错。

还有个容易忽略的细节:关键词索引按租户建,不按部门建。 逆文档频率是语料统计量,共用一张索引意味着 A 租户导入一万篇就改变了 B 租户看到的词权重;反过来按部门建,词权重又会因为「这次是客服在问」而变。权限收窄发生在候选过滤那一步,不在统计量那一层。

ingest.js
// 落点一:摄取时投影。一篇文档只有落在这个租户的订阅范围里,才被切成它的块
export function ingest(docs, tenants) {
  const chunks = []
  for (const tenant of tenants) {
    for (const doc of docs) {
      if (!tenant.subscribes.includes(doc.department)) continue
      chunks.push(...chunkDoc(doc, tenant.id))
    }
  }
  return chunks
}
 
// 落点二:范围判定只写一次,内存实现、SQL 实现和评估侧共用,
// 避免「三个地方各判各的」——那是权限事故最常见的来源
export function inScope(chunk, scope) {
  if (chunk.tenantId !== scope.tenantId) return false
  return scope.departments ? scope.departments.includes(chunk.department) : true
}

三、评估面板:把秤变成仪表盘

落点三是评估时分租户统计,它值得单独一节,因为实验里最扎眼的一个现象就出在这儿。

第八天造的是一把秤:跑一次、看一眼、完事。面板要比它多做三件事,否则三周之后没人会再跑——分租户、把失败分类、跟一条存档基线比并且能让流水线失败。

先看分租户这一件。同一份二十道题的标准答案集,在两个工作区下不是同一份题:试用客户只有产品文档,那么「灰度分几批放量」在它那里根本没有答案,正确行为是拒答,跟语料里压根没有的那四道题是同一类。按这个口径重新投影之后,实验跑出来的面板长这样:

工作区索引块数可答题应拒答召回率multinDCG@10拒答率
总部13416493.8%75.0%0.64450.0%
试用44515100.0%无此档0.764920.0%
全局平均211995.2%75.0%0.673215.8%

第三行就是这一节的全部意义。它没有一个数字是错的,而它每一个数字都在骗人:召回率 95.2% 看着比总部还高,拒答率 15.8% 看着像系统会闭嘴——实际上总部的拒答率是 0.0%,试用工作区在十五道该拒答的题里答了十二道。全局平均把一个租户的塌方按人头摊薄了,而线上真正会投诉的恰恰是那个租户。

顺带两条实现纪律。一是空集合的平均值是 0,但「0.0%」和「这一档没有题」是两件事——试用工作区一道多跳题都没有,印成 0.0% 会让人以为多跳全崩了,面板里必须用别的符号区分。二是报告的时间戳要固定,每跑一次就 diff 一行,两周后没人愿意再看这份 diff。

至于总部那 0.0% 的拒答率,它是第八天基线里那条最大的债,十四天下来一分没还。今天把它写进面板的主表,就是为了让它没法再被忽略——这本身也是面板存在的理由。

四、一张 RAG 决策地图

现在做今天真正的产出。地图的输入只有四个:数据规模、更新频率、延迟预算、准确度要求。输出是十个旋钮,每一条都长成「因为哪一天量到了什么,所以选它」的形状。

旋钮由谁决定判据(括号里是它出自哪一天)
切块方式文档形态有标题层级就按结构切,零成本、与语义切分打平;没有结构的长文本才值那一次全量 embedding(D4)
向量索引数据规模五万块以内顺序扫比近似索引还快还准;判据不是行数,是索引塞不塞得进内存(D5)
量化数据规模半精度的召回损失落在重跑噪声里、索引小四成,几乎白捡;二值量化必须配原始向量精排(D5)
检索路数延迟预算向量一路要多一次 embedding 往返;它换来的是「换个说法也能捞到」(D9)
重排延迟预算买的是排序质量,买不到召回率,也救不了拒答——准入卡在原始分上(D9)
查询改写一律要单轮几乎不涨指标,多轮是「有没有」的分界线(D10)
Agentic准确度要求收益集中在多跳,代价摊给全部问题;先分流再进循环,不要当默认值(D12)
增量同步更新频率全量重建等于每天把知识库重买一遍(D13)
拒答准确度要求提示词兜不住,只有代码回查能保证编造的进不了返回结果(D6)
评估节奏准确度要求检索侧指标零成本、每次提交都跑;模型裁判花钱,留给合并前与每晚(D8)

写成代码就是一个纯函数。注意它返回的不只是取值,还有每条取值的依据——没有依据的建议跟网上那些「最佳实践清单」没有区别。

decide.js
export function decide(req) {
  const chunks = Math.round(req.docs * CHUNKS_PER_DOC) // 一篇约 4.5 块,本课语料实测
  const decisions = []
  const put = (knob, value, driver, because) => {
    decisions.push({ knob, value, driver, because })
    return value
  }
 
  // 判据不是「我有多少行」,是「索引还塞不塞得进内存」
  const index = put(
    'index',
    chunks < 50_000 ? 'seqscan' : chunks < 5_000_000 ? 'hnsw' : 'dedicated',
    'docs',
    'D5:五万块以内顺序扫比 HNSW 还快还准',
  )
  // 重排是一次同步往返,卡在检索之后、生成之前,用户全程在等
  const rerank = put(
    'rerank',
    req.accuracy !== 'best-effort' && req.latencyBudgetMs >= 800,
    'latencyBudgetMs',
    'D9:建模 180 ms,买到 nDCG(0.6438 到 0.7218),买不到召回率',
  )
  return { config: { index, rerank }, decisions }
}

但地图更值钱的用法是第二种:拿它复核一份已经在跑的配置。实验里把今天这个项目的十个旋钮跟地图对了一遍,报出两处不一致——没开查询改写、评估只在合并时跑。作业不是去改成一致,而是把这两处逐条解释掉:本项目只跑单轮问答,改写按第十天的实测确实不涨指标;模型裁判要花钱,二十道题的教学项目还不值得每晚跑。解释得通的留着,解释不通的才是真该改的。

这个动作就是面试里那句「哪个决定你现在会改」的预演。 你答不上来,通常不是因为项目做得差,而是因为从来没有人逼你把配置和理由并排列出来过。

五、常见失败模式速查

用户说「答得不准」的时候,这四个字里其实塞了完全不同的四种病。速查表的价值就是把它们分开,因为修法互不通用

现象先查哪一步判据
答非所问检索侧答案文档在不在候选池里。不在就是检索的债,放宽门槛一点用没有
只答得出片段门槛与预算在候选池但没过门槛,是门槛的债;过了门槛没装进预算,是块太大或预算太小
引用错位生成侧引用校验的通过率。编号是闭集,对不上一行代码就能判
更新不生效同步与缓存先看对账认没认出这篇改了,再看缓存 key 里有没有索引版本(D13)

前三种在面板里是自动分类的,而分类的关键细节很容易写错:多跳题的诊断对象是缺的那几篇,不是「有没有捞到任意一篇」。 实验里 q06 要同时命中两篇,第一篇稳稳排第一、第二篇一次都没进候选池;用「任意一篇」去判会把它归成预算问题,然后你去调预算,调一整天也没用。

classify.js
// 只诊断「缺的那几篇」。顺序就是排查顺序:从最上游查起,因为上游的错会被下游放大
function classify(hit, rankedDocIds, contextDocIds, answerDocIds, candidates, result) {
  if (!hit) {
    const missing = answerDocIds.filter((id) => !contextDocIds.includes(id))
    if (missing.some((id) => !rankedDocIds.includes(id))) return 'not-retrieved'
    const admitted = candidates.filter((c) => c.admitted).map((c) => c.chunk.docId)
    if (missing.some((id) => !admitted.includes(id))) return 'gated-out'
    return 'budget-squeezed'
  }
  return result.rejected.length > 0 ? 'citation-misaligned' : undefined
}

第四种在问答之外——同步没跑、对账没认出改动、缓存 key 里没有索引版本号,三种都表现为「我明明改了文档」。第十三天那条纪律在这里就是速查表:「什么时候必须失效」等价于「key 里有没有把那样东西算进去」。

六、十四天攒下来的,和接下来学什么

最后收束。这门课真正给你的不是十四个手法,是下面这几条——它们全都是被自己的实验打过脸之后才写下来的

  1. 指标会互相打架,先说清你在优化什么。 每路取 5 条时召回率 93.8%、nDCG 0.6281;取 50 条时 87.5%、0.7186,方向相反(D9)。
  2. 指标会饱和,一列全是满分说明尺子坏了。 平均倒数排名恒为 1.0000,因为题目从语料反向出(D8、D10)。
  3. 端到端指标会把两个相反的效应搅在一起。 q07 是关键词命中、混合检索弄坏、重排修回——不是「混合检索让指标涨了」,是「重排修好了融合弄坏的那道题」(D9)。
  4. 两种失败要用两种药。 候选池里根本没有要多跳;捞到了被自己的过滤器扔了要放宽。分类判错,容错就用反了(D12)。
  5. 不是所有手法都该默认开。 「全开」与「默认配置」召回率完全相同,模型调用却是 2.5 倍、检索次数 4.3 倍(D10)。
  6. 结论有适用边界。 「块头对 BM25 有害」在按结构切块时没复现——真正的表述是「块头在块边界与结构不对齐时才有害」(D4、D11)。
  7. 先有失败案例,再有索引结构。 五种高级索引结构没有一种跑赢基线(D11)。
  8. 离线测不出的要承认测不出。 假设文档嵌入那笔效果账被写成「我们没资格填」,而不是给一个带免责声明的数字(D10)。

还有一条是用血换来的。第十一天第一版写了「向量侧确实是正收益」,看起来有实测支撑;后来量了一遍才发现,融合前二十条里向量路独有的只占 5.8%,其中含答案文档的题数是 0/20——向量路一条新的答案文档都没带进来,只是重洗了名次。那个「涨」是字面重合度检索器的涨幅,不是语义检索器的涨幅。于是它推翻了自己的结论。

教训是:MOCK 的边界要量出来,不能靠猜。 判据只有一句——

这个结论是「这次实验测出来的」,还是「这个结构必然导致的」?

前者必须标出边界(换真模型可能反过来),后者可以放心讲。「重排只改顺序、不改准入,所以救不了拒答」属于后者,换成真的交叉编码器结论也不变。读你自己的实验报告时也这么分一遍,这一条比任何一个百分比都值钱。

接下来往哪走?两条最近的路都在站内:把检索结果塞进有限的上下文、决定留什么删什么,是上下文工程课的第三天;把这套检索包成一个别的 Agent 也能调用的标准工具,是 MCP 课的第二天。再往后,值得自己找资料补的是两块:评估与可观测(今天这份面板只是最小版,线上还要处理采样、分位数与告警)和 Agent 安全(今天的租户谓词只挡住了检索,提示词注入与工具越权是另一套问题)。

源码导读

动手实验

🧪 D14 实验:一个多租户、带引用与评估报告的企业知识库问答项目,以及一页 RAG 决策地图

代码位置:labs/rag-14days/day-14-capstone-knowledge-base

验收标准:

  1. 离线跑通后参考答案九项全绿、退出码为 0,原样的起始代码有六项失败。
  2. 摄取那一项打印「总部 134 块 / 试用 44 块」,块编号带租户前缀;拿试用工作区问一个工程问题,走的是按租户范围的拒答而不是「资料中没有找到」。
  3. 面板主表里两个工作区不是同一行:召回率 93.8% 对 100.0%、拒答率 0.0% 对 20.0%,而全局平均把它们抹成 95.2% 与 15.8% 一行。
  4. 失败模式分成三类各自列出题号,其中一道被判成「答案文档没进候选池」,一道被判成「引用与句子对不上」。
  5. 决策地图对着本项目配置报出两处不一致,每处都带依据;把输入换成五百万篇、秒级、准确度优先,索引与同步两个旋钮跟着变。

动手之前先确认一件事:今天不要改任何检索参数。 语料、二十道题、六百 token 预算、每路取五十、融合留二十、两条门槛全部沿用前面各天——改掉任何一个,你今天的数字就没法跟第八天到第十二天并排看,而这个项目的说服力恰恰在于「可以并排看」。起始代码挖了五个练习点,都落在本天新增的三处落点和两笔账上。

  1. 补完摄取时打标,看两个工作区的索引块数分开成 134 与 44,块编号带上租户前缀。
  2. 把范围谓词挪到打分之前,看客服范围下向量一路从「返回个位数」变成老实返回 23 条。
  3. 补完倒数排名融合,看融合结果里出现向量一路独有的候选,而不再是关键词那一路的原样。
  4. 把面板的分组键改成租户,看主表从一行变成三行,并把全局平均那一行是怎么骗人的讲给自己听。
  5. 补完费用折算,跑一遍决策地图的复核,把两处不一致逐条解释掉——解释不通的那条才是真该改的。

面试题

今天 5 道题在下方题库区,是全课唯一一天出五道,侧重端到端方案设计、按约束做取舍,以及把十四天的知识点串起来。展开后先看「分析过程」再看要点——照着推导练,比背要点管用。标注「国内高频 / 海外高频」方便按目标市场取舍。

检查清单与明日预告

  • 能把前十三天的模块组装成一个多租户、带引用与评估面板的完整项目并说清架构
  • 能画出一张 RAG 决策地图,面对一个新需求时按图给出该用哪套配置及其理由
  • 能面对面试里的追问,用本课的数据和取舍说清自己每一个技术选择
  • 能说出多租户的三处落点,并解释为什么只做检索时过滤那一处是不够的
  • 能用「这个结论是测出来的、还是结构必然的」这句话,把自己报告里的每个数字分一遍
  • 实验的 5 条验收标准全部通过,手里有一份能贴进项目说明的面板
  • 5 道面试题不看要点也能答出至少 4 道

十四天到这里结束。回头看第 1 天那个只有 BM25 的最小系统——它今天仍然活在项目里,是关键词那一路,也是所有对照实验的基线。这十四天真正加进去的不是向量、重排或者循环,而是一套要求每个决定都拿出证据的习惯。把今天这个项目放进你的仓库,把面板贴进 README,把决策地图打印出来贴在显示器边上——下一次有人问「这个方案行不行」,你的第一反应会不再是「应该行」,而是「我去跑一遍看看」。

面试题库

  • 给你一个五百万文档、要求秒级响应、准确率优先的知识库场景,你会怎么设计这套系统?You are handed a knowledge base of five million documents that must answer in about a second, with accuracy as the top priority. How would you design it?
    国内高频海外高频深入#system-design#scaling#latency-budget

    分析过程 · 先想清楚再作答

    1. 这题的题眼不在「你会用什么技术」,而在「你有没有一套从约束推配置的方法」。开口就报架构图和技术栈的答案会被判成背方案;拿到分的答法是先把约束翻译成数字,再让每个选择被某个数字逼出来。
    2. 先把三个约束量化:五百万文档按一篇四五块估,是两千多万块,单精度 1536 维就是上百 GB,**索引塞不进单机内存**,这一条直接决定了存储选型;秒级响应意味着从收到问题到第一个字的预算大约一秒,而生成本身通常就吃掉七八百毫秒,检索侧只剩两三百毫秒;准确率优先意味着可以拿延迟和钱换指标,但只能换到那两三百毫秒为止。
    3. 然后逐项落地,每一项都挂在上面某个数字上:存储上专用向量库或分区加半精度量化(半精度的召回损失通常落在重跑噪声里,索引却小四成,这是白捡的);检索保留关键词与向量两路加倒数排名融合,因为精确匹配的文档号、错误码、人名是向量的固定盲区;重排只作用于融合后的前二十条——它买的是排序质量,一次同步往返,秒级预算里放得下一次,放不下两次。
    4. 接着讲两个「不上」的决定,这一段比上面更能显出做过工程:**Agentic 检索不作为默认路径**,它的收益集中在多跳题上而代价摊给全部问题,秒级预算下更是直接超支——正确做法是先用一次便宜的分类把多跳分流出来,只让那一小部分进循环;**上下文块头之类的手法先不上**,因为它对关键词一路是稀释、对向量一路才是补位,方向相反,得在自己的真实 embedding 上测过再说。
    5. 准确率优先必须落成可验收的东西,否则是空话:一份不少于一百题的标准答案集(其中多跳与无答案各占一成以上)、召回率与排序质量分开看、无答案题的拒答率单独一栏、引用由代码回查而不是靠提示词自觉。**报数字时把最难看的那一栏也报出来**,比只报总分可信得多。
    6. 可预期的追问:五百万文档怎么建第一版索引?答案是这笔钱是一次性大额支出,要按批做、可断点续跑,并且从第一天就上基于内容指纹的增量同步——否则每次改配置都等于把整个知识库重买一遍。再追问就谈灰度:新旧两套向量双写在两列上,用同一份标准答案集在两列上各跑一遍再切流量,回滚只是改一个配置项。

    How to reason about it · think before answering

    1. The real subject here is not which technologies you know, it is whether you have a repeatable way to derive a configuration from constraints. Opening with an architecture diagram reads as a memorized answer; the way to score is to turn each constraint into a number first, then let every choice be forced by one of those numbers.
    2. Quantify the three constraints. Five million documents at roughly four or five chunks each is over twenty million chunks; at 1536 float dimensions that is hundreds of gigabytes, so the index does not fit in one machine's memory — that alone settles storage. A one-second budget to first token, with generation typically eating seven or eight hundred milliseconds, leaves only two or three hundred for retrieval. Accuracy first means you may trade latency and money for metrics, but only within that remaining budget.
    3. Now derive each knob from one of those numbers: a dedicated vector store or partitioning, plus half precision (its recall loss usually sits inside run-to-run noise while the index shrinks by about forty percent — essentially free); keep both keyword and vector routes with reciprocal rank fusion, because exact matches on document ids, error codes and names are a permanent blind spot for embeddings; rerank only the top twenty after fusion, since it buys ranking quality at the cost of one synchronous round trip, and a one-second budget affords exactly one.
    4. Then state two things you deliberately do not build, which is the part that reads as field experience. Agentic retrieval is not the default path: its gains concentrate on multi-hop questions while its cost is spread over every question, and it blows a one-second budget outright — the right move is a cheap classifier that routes only the multi-hop minority into the loop. Contextual chunk headers and similar tricks also wait, because they dilute the keyword route while helping the vector route; the directions are opposite, so measure on your own embeddings before committing.
    5. Accuracy first has to become something you can sign off on. That means a golden set of at least a hundred questions with multi-hop and unanswerable each above ten percent, recall and ranking quality read separately, abstention rate on unanswerable questions as its own column, and citations verified by code rather than trusted from the model. Reporting the ugliest column alongside the headline number is far more credible than reporting a single score.
    6. Expected follow-up: how do you build the first index over five million documents? It is a one-off large expense, so batch it, make it resumable, and put content-hash incremental sync in from day one, or every config change means buying the whole corpus again. Push further and you get to rollout: dual-write the new embeddings into a second column, evaluate both columns on the same golden set, then shift traffic, so rollback is a config flip rather than an eight-hour rebuild.

    答题要点

    • 先把约束翻译成数字:两千多万块决定索引塞不进单机内存,一秒预算里检索侧只剩两三百毫秒。
    • 存储用专用库或分区加半精度;检索保留关键词与向量两路加倒数排名融合,重排只作用于前二十条。
    • 明确说出「不上」的两项:Agentic 只对分流出来的多跳开,块头这类方向相反的手法先测再说。
    • 准确率优先要落成一百题以上的标准答案集、拒答率单独一栏、引用由代码回查。
    • 第一版建索引是一次性大额支出:分批可续跑,并从第一天就上增量同步;换模型走双写切列。

    Key points

    • Translate constraints into numbers first: twenty million chunks means the index will not fit one machine, and a one-second budget leaves retrieval two to three hundred milliseconds.
    • Dedicated store or partitions plus half precision; keep keyword and vector routes with RRF, and rerank only the top twenty after fusion.
    • Name the two things you will not ship: agentic only for a routed multi-hop minority, and chunk headers only after measuring on your own embeddings.
    • Turn accuracy-first into a hundred-plus question golden set, abstention rate as its own column, and code-verified citations.
    • First index build is a one-off large expense: batch it, make it resumable, add incremental sync on day one, and dual-write columns for model swaps.
  • 你做过的这个 RAG 项目里,哪个决定你现在会改?为什么?Looking back at the RAG project you built, which decision would you change now, and why?
    国内高频海外高频深入#retrospective#evidence#chunking

    分析过程 · 先想清楚再作答

    1. 这题看着是软性问题,其实区分度极高。答「暂时没有」等于承认没做过复盘;答成一长串自我批评又会显得没有判断力。面试官真正在听的是:你能不能把一个决定、它当时的依据、后来的证据、以及新的判断,四样东西串成一条链子说清楚。
    2. 怎么拆:挑一个**当时有理由、后来被数据推翻**的决定,而不是一个「当时就知道是凑合」的决定。前者证明你有量化的习惯,后者只证明你赶过工期。所以答案的骨架固定是四段——当时选了什么、依据是什么、后来量到了什么、现在的判断是什么。
    3. 本课里有一个现成的样本:某一天先量到「给每个块拼上标题块头之后,命中率不变、索引 token 涨一成、答案文档平均名次从 2.88 退到 3.25」,据此写下「块头对关键词检索是负收益」。后来换成按文档结构切块再复核,这条名次退化**没有复现**。原因是切法变了:固定长度硬切时块边界跟小节边界不对齐,块头会把不属于这一块的标题词塞进来;按结构切时块本身就落在一个小节里,块头补的信息跟块里已有的高度重合。所以正确的表述不是「块头有害」,而是「**块头在块边界与结构不对齐时才有害**」。
    4. 这条链子的价值在于它演示了一个可复用的动作:**给每个结论标出它绑定的前提**。前提变了就要重跑,不能拿新配置去配旧结论。顺着这个思路还能给出第二个例子:曾经写过「向量侧确实是正收益」,后来把融合前的两路拆开数了一遍才发现,向量路独有的候选只占很小一部分,其中含答案文档的次数是零——那个「涨」根本不是语义检索带来的,于是这条结论被自己推翻。
    5. 可预期的追问:那你以后怎么避免这类错误?答两条具体的:一是每个数字旁边写清它绑定了哪几个前提(语料、题集、预算、切法),二是报结论前先问自己一句「这是这次实验测出来的,还是这个结构必然导致的」——前者要标边界,后者才能直接讲。

    How to reason about it · think before answering

    1. This looks like a soft question but it separates people sharply. Saying 'nothing yet' admits you never ran a retrospective; a long list of self-criticism reads as poor judgement. What the interviewer is listening for is whether you can chain four things together: the decision, the evidence you had then, the evidence you got later, and your current call.
    2. How to pick: choose a decision that was justified at the time and later overturned by data, not one you always knew was a shortcut. The first proves you measure; the second only proves you were behind schedule. So the answer has a fixed four-part shape — what you chose, on what basis, what you measured later, and what you now believe.
    3. This course supplies a ready example. One day measured that prepending a heading-path header to every chunk left hit rate unchanged, grew index tokens by about ten percent, and pushed the answer document's mean rank from 2.88 to 3.25 — hence 'headers hurt keyword retrieval'. A later day re-ran the same comparison under structure-aware chunking and the rank regression did not reproduce. The reason was the chunker: with fixed-length cuts, chunk boundaries do not line up with section boundaries, so the header injects heading terms that do not belong to that chunk; with structure-aware cuts, each chunk already sits inside one section and the header largely restates what is already there. The correct statement is therefore not 'headers hurt' but 'headers hurt when chunk boundaries are misaligned with document structure'.
    4. The value of the chain is that it demonstrates a reusable habit: attach the premises to every conclusion. Change a premise and you owe a re-run; you may not pair new settings with an old conclusion. The same reasoning yields a second example: an earlier claim that 'the vector route is clearly a net gain' collapsed once the two routes were counted separately before fusion — the vector-only candidates were a small share and contained the answer document zero times, so the improvement was never semantic at all.
    5. Expected follow-up: how will you avoid this class of error in future? Give two concrete practices. Write the bound premises next to every number — corpus, question set, budget, chunker. And before publishing any conclusion, ask whether it was measured by this experiment or forced by the structure of the implementation; the first needs its boundaries stated, only the second can be asserted flatly.

    答题要点

    • 答案要串成四段:当时选了什么、依据是什么、后来量到了什么、现在的判断是什么。
    • 挑一个当时有理由、后来被数据推翻的决定,而不是一个当时就知道在凑合的决定。
    • 样本:块头从「对关键词检索有害」修正成「块边界与结构不对齐时才有害」,因为切法这个前提变了。
    • 每个数字旁边写清它绑定的前提;前提变了就必须重跑,不能新配置配旧结论。
    • 报结论前先分类:这是实验测出来的,还是实现结构必然导致的——前者要标边界。

    Key points

    • Structure the answer in four beats: the choice, the evidence then, the evidence later, the call now.
    • Pick a decision that was defensible at the time and later overturned by data, not one you knew was a shortcut.
    • Worked example: 'headers hurt keyword retrieval' was corrected to 'headers hurt when chunk boundaries misalign with structure', because the chunker premise changed.
    • Record the premises bound to every number; when a premise changes you owe a re-run rather than a reinterpretation.
    • Classify before asserting: measured by this experiment, or forced by the implementation's structure — the former needs its boundaries stated.
  • 怎么向不懂技术的业务方证明你的检索系统真的变好了?How do you convince a non-technical stakeholder that your retrieval system actually got better?
    国内高频海外高频基础#evaluation#stakeholder-communication#abstention

    分析过程 · 先想清楚再作答

    1. 这题在考沟通,但拿分点在技术判断上:你选哪几个数字给业务方看,暴露了你自己有没有看懂这些指标。把召回率、nDCG、MRR 一股脑摊出去的答法会被判成不懂受众;只说「用户反馈变好了」又会被判成没有度量。
    2. 先立一条原则:**给业务方看的必须是他们能自己判断对错的东西**。归一化折损累计增益他们没法判断,而「这一百个真实问题里,系统答对了多少、答错了多少、老老实实说查不到了多少」他们一眼就能判断。所以对外的口径应该是三个数:答对率、答错率、拒答率,而且三个加起来是一百。
    3. 关键是把**答错和拒答分开**。这一条最能建立信任:查不到就说查不到不是故障,是正确输出;真正的故障是查不到还编一段。很多团队只报「准确率」,结果一个学会了一直拒答的系统能刷出满分——所以这三个数必须并排出现,缺一个都能被骗。
    4. 然后给可核对的证据,而不是只给数字:**挑十条真实问题做前后对照**,各贴出改动前和改动后的回答,每句结论后面挂着可点开的引用。业务方点开原文核对一遍,比看任何百分比都有说服力,而且这个动作顺带完成了一次人工抽检——你自己也需要它来校准模型裁判靠不靠谱。
    5. 本课里有一条要主动说的教训:**一列全是满分说明尺子坏了**。我们的题目是从语料反向出的,字面重合度过高,平均倒数排名恒为 1.0000。这个数字拿给业务方看,只会换来一次「那你们已经完美了」的误会,而它其实是指标饱和。指标撞天花板时该做的是把题目出难一点。
    6. 可预期的追问:那怎么让业务方参与进来?答一条很实用的:让他们提供题目。把线上答错的问题一条条补进标准答案集,评估集是长出来的,而不是一次性造好的;这样每一次改进都能指着「你上次提的那个问题现在答对了」,比任何汇报都直接。

    How to reason about it · think before answering

    1. This is a communication question whose scoring hinges on technical judgement: which numbers you choose to show reveals whether you understand the metrics yourself. Dumping recall, nDCG and MRR on a business stakeholder reads as tone-deaf; saying 'user feedback improved' reads as unmeasured.
    2. Start from a principle: show them something they can adjudicate themselves. They cannot judge normalized discounted cumulative gain, but they can absolutely judge 'out of these hundred real questions, how many did it answer correctly, how many wrongly, and how many did it honestly decline'. So the external framing is three numbers — correct, wrong, declined — and they sum to one hundred.
    3. The crucial move is separating wrong from declined, and it is the fastest way to earn trust: saying 'not found' is a correct output, not a failure; the failure is inventing an answer when nothing was found. Teams that report a single 'accuracy' number can be gamed by a system that learns to decline everything, which is why all three must appear side by side.
    4. Then supply checkable evidence rather than only numbers: take ten real questions and show before-and-after answers with clickable citations on every claim. A stakeholder who opens the source and verifies one claim is more convinced than by any percentage, and the exercise doubles as the human spot-check you need anyway to calibrate whether your model judge is trustworthy.
    5. There is a lesson from this course worth volunteering: a column of perfect scores means the ruler is broken. Our questions were written backwards from the corpus, lexical overlap is unusually high, and mean reciprocal rank sits at exactly 1.0000. Showing that to a stakeholder only invites the misreading that you are already perfect, when in fact the metric has saturated. When a metric hits the ceiling, the response is to make the questions harder.
    6. Expected follow-up: how do you get the business side involved? One very practical answer: let them supply questions. Every production miss gets appended to the golden set, so the evaluation set grows rather than being built once. Then each release can point at 'the question you raised last month now answers correctly', which lands better than any status report.

    答题要点

    • 对外只用三个他们能自己判断的数:答对率、答错率、拒答率,三者相加为一百。
    • 答错和拒答必须分开——查不到就说查不到是正确输出,只报一个准确率会被「一直拒答」刷满分。
    • 配十条真实问题的前后对照,每句结论挂可点开的引用,让他们自己核对原文。
    • 主动说明指标饱和:某一列恒为满分是尺子坏了,不是系统完美,该做的是把题目出难一点。
    • 让业务方提供题目,把线上答错的问题补进标准答案集——评估集是长出来的。

    Key points

    • Externally report three numbers they can adjudicate: correct, wrong, declined — summing to one hundred.
    • Keep wrong and declined separate; a single accuracy number is gamed by a system that learns to decline everything.
    • Pair it with ten before-and-after real questions, every claim carrying a citation they can open and verify.
    • Volunteer the saturation caveat: a column of perfect scores means a broken ruler, and the fix is harder questions.
    • Let stakeholders contribute questions; append every production miss to the golden set so it grows over time.
  • RAG 系统上线后用户反馈「答得不准」,你的排查顺序是什么?Users report that your live RAG system 'answers inaccurately'. What is your triage order?
    国内高频海外高频进阶#debugging#failure-modes#observability

    分析过程 · 先想清楚再作答

    1. 这题几乎是必考题,而绝大多数人答成一堆并列的可能性:可能是切块问题、可能是提示词问题、可能是模型不行。并列不是排查,排查的意思是**有顺序、有判据、每一步能把可能性砍掉一半**。
    2. 先把「答得不准」这四个字拆开——它至少塞了四种病,而且修法互不通用:答非所问、只答得出片段、引用错位、更新不生效。所以第一个动作不是改配置,是**拿到具体的问题和回答,把它归到这四类里的一类**。
    3. 然后给顺序,而且要说清顺序的理由:**排查从右往左看、修复从左往右修**。从右往左是因为你最先看到的是生成结果;从左往右是因为上游的错会被下游放大——检索没捞到的东西,再好的提示词也救不回来。具体走法是:打印这一问的候选池和最终上下文,先看答案文档在不在候选池里。不在,是检索的债;在候选池但没过准入门槛,是门槛的债;过了门槛却没装进上下文预算,是块太大或预算太小;都进了而模型没用上,才轮到生成侧。
    4. 这里有一个容易写错的细节值得主动讲:**多跳题的诊断对象是缺的那几篇,不是「有没有捞到任意一篇」**。我们实验里有一道题要同时命中两篇,第一篇稳稳排第一、第二篇一次都没进候选池;用「任意一篇」去判会把它归成预算问题,然后你去调预算,调一整天也没用。这一条区分度很高,因为它只有真的按题排查过才想得到。
    5. 第四类「更新不生效」发生在问答之外,判据是另一条:先看对账认没认出这篇改了(内容指纹算之前有没有做换行归一化),再看缓存的 key 里有没有把索引版本和权限范围算进去。「什么时候必须失效」等价于「key 里有没有把那样东西算进去」,key 少放一样,那样东西变了缓存就不会失效。
    6. 可预期的追问:怎么让这套排查不靠人肉?答案是把分类做进评估面板——每一道没中的题自动标出它属于四类中的哪一类,并按租户分开统计。全局平均会把单个客户的塌方按人头摊薄,而线上会投诉的恰恰是那个客户。

    How to reason about it · think before answering

    1. This one is almost guaranteed to be asked, and most people answer with a flat list of possibilities: maybe chunking, maybe the prompt, maybe the model. A list is not triage. Triage means an order, a decision rule at each step, and each step eliminating half the search space.
    2. First decompose the complaint. 'Inaccurate' hides at least four distinct failures whose fixes do not transfer: off-topic answers, partial answers, misaligned citations, and stale content. So the first action is not to change a setting, it is to obtain the specific question and answer and classify it into one of those four.
    3. Then give the order along with its justification: read the pipeline right to left, fix it left to right. Right to left because the generated answer is what you see first; left to right because upstream errors are amplified downstream — no prompt can recover a document retrieval never fetched. Concretely: dump the candidate pool and the final context for that question, and check whether the answer document is in the pool at all. Absent means a retrieval debt; present but below the admission gate means a gate debt; admitted but never packed into the context budget means chunks too large or budget too small; all present and still unused means it is finally a generation problem.
    4. One detail worth volunteering because it is easy to get wrong: for multi-hop questions, diagnose the documents that are missing, not whether any one of them was retrieved. In our experiment one question needed two documents; the first ranked first every time and the second never entered the candidate pool at all. Judging by 'any of them' labels it a budget problem, and you can spend a full day tuning budgets to no effect. This distinction only occurs to someone who has actually triaged question by question.
    5. The fourth class, stale content, happens outside the question path and has its own rule: first check whether reconciliation even noticed the edit (was the content hash computed after line-ending normalization?), then check whether the cache key includes the index version and the permission scope. 'When must this expire' is equivalent to 'is that thing part of the key' — leave something out of the key and changes to it will never invalidate the entry.
    6. Expected follow-up: how do you stop relying on manual triage? Build the classification into the evaluation panel so every missed question is automatically labelled with one of the four classes, and report it per tenant. A global average dilutes one customer's collapse across the whole population, and that customer is exactly the one who will file the complaint.

    答题要点

    • 先把「答得不准」归类成四种病:答非所问、只答得出片段、引用错位、更新不生效——修法互不通用。
    • 排查从右往左看、修复从左往右修:先打印候选池与最终上下文,看答案文档卡在哪一层。
    • 四层判据依次是:没进候选池、进了没过门槛、过了没装进预算、都进了模型没用上。
    • 多跳题只诊断缺的那几篇;用「有没有捞到任意一篇」会把「根本没捞到」误判成预算问题。
    • 「更新不生效」查对账与缓存 key:什么时候必须失效,等价于 key 里有没有算进那样东西。

    Key points

    • Classify the complaint into four failures first — off-topic, partial, misaligned citation, stale — because their fixes do not transfer.
    • Read right to left, fix left to right: dump the candidate pool and final context and find which layer the answer document stalls at.
    • The four rules in order: never retrieved, retrieved but below the gate, admitted but squeezed out of the budget, packed but unused by the model.
    • For multi-hop, diagnose only the missing documents; judging by 'any one retrieved' mislabels a never-retrieved case as a budget problem.
    • For stale content, check reconciliation and the cache key: what must expire is exactly what the key must contain.
  • 如果预算只够做三件事来提升一个已有 RAG 系统的效果,你选哪三件?为什么是这三件?If you could only fund three changes to improve an existing RAG system, which three would you pick and why those three?
    国内高频海外高频进阶#prioritization#evaluation#abstention

    分析过程 · 先想清楚再作答

    1. 这题在考优先级判断,而不是知识面。答成「上重排、上混合检索、上查询改写」这类手法清单几乎必然掉分——因为它跳过了一个前提:**你凭什么知道这三件对你的系统有用?** 面试官等的就是这句话。
    2. 所以第一件必须是**建评估**,而且理由要具体到不可反驳:没有秤,剩下两件做完你也说不清是变好还是变坏;有了秤,后面每一笔钱都能算回报。而且它便宜——检索侧三个指标是纯本地计算、几秒钟、零成本,能挂进每次提交;花时间的只是给题目标答案文档那一次。顺带说清评估集的配比:多跳与无答案各占一成以上,缺了无答案那一类,一个只会硬答的系统在报表上就是满分。
    3. 第二件是**把拒答从提示词搬进代码**,这一件的性价比通常最高而最容易被跳过。提示词里写十遍「找不到就说找不到」增益接近于零;而引用编号是一个闭集,判它存不存在只要一行代码,再加一道「这句话与被引块的实质重合度」就能拦住「编号是真的、内容是假的」那一类。我们实验里的基线拒答率是 0.0%——四道语料里根本没有答案的题一道都没闭嘴,这类缺陷在只报召回率的报表上完全不可见。
    4. 第三件要**先看失败案例再决定**,这才是这道题真正的答案。看完面板你会落到其中之一:多跳题占比高就补桥接检索或改索引结构;换个说法就捞不到,说明该上向量那一路或混合检索;答案捞到了却排不进上下文,那是重排或者预算的活。**先有失败案例,再有手法**——我们试过五种高级索引结构,没有一种跑赢基线,因为我们的系统压根没有那些结构要补的短板。
    5. 为什么不选那些看起来更亮的:Agentic 检索的收益集中在多跳题上而代价摊给全部问题;「全开」所有查询侧手法在我们的实测里召回率和默认配置一模一样,模型调用却是 2.5 倍、检索次数 4.3 倍。**堆手法很容易,说清楚为什么关掉某几项才是本事。**
    6. 可预期的追问:三件做完怎么证明钱花对了?答:每一项单独开关各跑一遍,报三笔账——指标涨了多少、延迟涨了多少、钱涨了多少。只报第一笔的提案不该被批准,包括你自己的。

    How to reason about it · think before answering

    1. This tests prioritisation, not breadth. Answering with a list of techniques — add reranking, add hybrid retrieval, add query rewriting — almost always loses points, because it skips a prerequisite: how do you know those three help your system? That is precisely the sentence the interviewer is waiting for.
    2. So the first item has to be building evaluation, with a reason specific enough to be unarguable: without a scale, you cannot tell whether the other two helped or hurt; with one, every subsequent spend has a measurable return. It is also cheap — the three retrieval metrics are pure local computation, run in seconds, cost nothing, and can gate every commit; the only real effort is labelling answer documents once. Include the composition rule: multi-hop and unanswerable each above ten percent, because without the unanswerable class a system that only ever guesses scores perfectly on your report.
    3. Second, move abstention out of the prompt and into code — usually the best return per unit of effort, and the item most often skipped. Writing 'say you don't know' ten times in a prompt buys almost nothing. Citation numbers are a closed set, so checking existence is one line, and adding a substantive-overlap check catches the harder forgery where the number is real but the content is not. Our baseline abstention rate was 0.0 percent: four questions with no answer in the corpus, zero of them declined — a defect that is completely invisible on a report that only shows recall.
    4. Third, look at the failure cases before deciding, which is the actual answer to this question. After reading the panel you land on one of a few branches: a high share of multi-hop means bridging retrieval or a different index structure; queries that miss when phrased differently mean you need the vector route or hybrid retrieval; answers retrieved but never packed into context means reranking or budget. Failure cases first, technique second — we tried five advanced index structures and not one beat the baseline, because our system simply did not have the weakness they address.
    5. Why not the flashier options: agentic retrieval concentrates its gains on multi-hop while spreading cost across every question, and in our measurements turning on every query-side technique produced exactly the same recall as the default configuration while using 2.5 times the model calls and 4.3 times the retrievals. Stacking techniques is easy; explaining why you switched several off is the skill.
    6. Expected follow-up: once the three are done, how do you prove the money was well spent? Toggle each one individually and report three ledgers — how much the metric moved, how much latency moved, how much cost moved. A proposal that reports only the first should not be approved, including your own.

    答题要点

    • 第一件是建评估:没有秤,另外两件做完也说不清变好还是变坏;检索侧指标零成本可挂进每次提交。
    • 评估集必须含无答案那一类,否则一个只会硬答的系统在报表上就是满分。
    • 第二件是把拒答从提示词搬进代码:编号是闭集,再加实质重合度就能拦住「编号真、内容假」。
    • 第三件由失败案例决定,不由手法清单决定——先有失败案例,再有索引结构或检索手法。
    • 每一项单独开关跑一遍并报三笔账:指标、延迟、钱。只报第一笔的提案不该被批准。

    Key points

    • First, build evaluation: without a scale the other two changes are unverifiable, and the retrieval metrics are cheap enough to gate every commit.
    • The golden set must include unanswerable questions, or a system that only ever guesses scores perfectly on your report.
    • Second, move abstention from the prompt into code: citation numbers are a closed set, and a substantive-overlap check catches real-number-fake-content forgeries.
    • Third is chosen by the failure cases, not by a list of techniques — failure cases first, index structure or retrieval trick second.
    • Toggle each change individually and report three ledgers: metric, latency, cost. A proposal reporting only the first should not be approved.

评论

登录后即可参与讨论

还没有评论,来说第一句。