Skip to content

09 口袋速背卡片(考前 10 分钟看这一页) ​

只背这一页。长文都在这页之外的 01–08 篇,考前不要再看长文。 顺序:① 主线口述 → ② 数字 → ③ 四条链路一句话 → ④ 十个必答 → ⑤ 六个缺陷 → ⑥ 三个反问。


① 主线口述(必须形成条件反射,约 40 秒) ​

"BinRag 是企业文档知识库的 RAG 问答系统,我用 Go 写的。 入库是异步管道:多格式解析 → 三种分块策略 → Embedding 存 Qdrant,同时灌一份我自己实现的 BM25 内存倒排索引,worker 池失败重试、重启续跑。 问答时向量和 BM25 双路并行召回,加权 RRF 融合(k=60,0.7/0.3),再过 cross-encoder 重排,按 2048 token 预算组装上下文,SSE 流式输出回答和可定位的引用来源。 部署上是一个 Go 二进制:Web 版 go:embed 前端,桌面版 Wails 内嵌同一套后端;对外另开一个 MCP Server,用 6 个只读 Tool 把 RAG 能力开放给外部 Agent。 开发方式我直说:AI 辅助编码,但我先写 spec 和可验证的 checklist,做了一次全量代码 review 修掉 6 个 P0 和十几项 P1——所以我清楚它哪里是坏的。"


② 数字卡(被追问就报,报完补一句来源) ​

项值
两路召回 / 最终引用各 Top-10 / 最多 5 条
RRFk=60,向量 0.7 / BM25 0.3(校验强制和为 1)
BM25k1=1.2、b=0.75(硬编码);IDF=log((N−df+0.5)/(df+0.5)+1)
中文分词bigram(生产额外开 unigram 补单字)
分块chunk_size 512(部署配置 500)、overlap 50
上下文预算2048 token、最多 5 条、历史 10 条
入库worker 5(代码默认 2)、轮询 500ms、重试 3 次、退避 2s/4s/8s(区别于外部调用的 1s/2s/4s)
外部依赖Embedding 批 100(部署 10)、重排 top_n 5 / 超时 30s / 重试 3 / QPS 10
安全扫描件阈值 20 字符、MCP 6 Tool、审计截断 2000 字符、JWT 1200 分钟
规模Go 63 文件 / 405 测试函数、前端 vitest 37、Python pytest 86、30+ 条 REST 路由

③ 四条链路一句话 ​

链路一句话
入库上传只做"校验+落盘+建任务"立刻返回 task_id;worker 用 UPDATE…WHERE id IN (SELECT…pending) RETURNING 原子认领;Load→可读性校验(≥20字符)→Chunk→Embed→Qdrant.Upsert + BM25.Add;失败退避重试写在 updated_at 里;重启把 processing 重置为 pending。
检索向量(Qdrant gRPC,Must+kb_id 下推过滤)∥ BM25(打分前按 kb 过滤)→ 加权 RRF → 截断 → cross-encoder 重排(失败降级原序)。
问答历史(≤10) → Query 改写(低温 LLM,失败降级原问题)→ 检索 → buildContext(2048 token / 5 条,[编号](来源)正文)→ 生成;SSE 顺序 sources → chunk×N → done / error。
MCP同进程 /mcp,streamable HTTP;Bearer API Key SHA-256 认证(失败 HTTP 401);越权与不存在统一返回 -32001(不泄露资源存在性);异步审计。

④ 十个必答题(一句话答案) ​

  1. 为什么混合检索? 向量对专有名词/编号/错误码会漏,BM25 补精确匹配;混合的目的是降方差,不是普涨。
  2. BM25 自己实现的? 是,200 行倒排索引;IDF 用非负变体(末尾 +1 保证不为负),k1 控制词频饱和、b 控制长度归一化。
  3. 中文怎么分词? bigram,无词典;代价是单字查询漏召回,所以生产额外开 unigram。
  4. 为什么用 RRF 不用加权求和? 余弦 0~1 与 BM25 无上界量纲不可比;RRF 只看排名,天然免疫量纲,且双路命中自动上位;k=60 起平滑作用(削弱头部)。
  5. cross-encoder 和向量检索区别? 双塔独立编码所以能预计算(负责召回);cross-encoder 把 query 和文档拼一起过模型、能建模交互,精度高但不能预计算(负责精排)。
  6. 重排挂了怎么办? 降级返回融合原序 + warn 日志,不阻塞主链路;副作用是 score 字段语义从相关性分变成 RRF 分。
  7. 为什么单集合不用一库一集合? 集合数随租户线性爆炸;单集合 + payload kb_id 过滤,过滤下推到检索里而不是检索完再筛。
  8. Token 预算怎么裁? 自研估算(中文 2 token/字、英文按词),按分数序累加,放不下就停;最多 5 条。
  9. 怎么防幻觉? 三层:prompt 强约束"资料未覆盖就说未覆盖"、引用编号与 sources 一一对应可溯源、检索为空直接返回"未找到相关资料"不调 LLM。(没做 NLI 校验,主动认)
  10. 你怎么知道效果变好了? cmd/eval:数据集驱动 Recall@K + LLM-as-Judge(准确性 0-10 / 忠实度二值,temperature=0);另有独立 Python RAGAS 服务(faithfulness/answer_relevancy/context_precision/context_recall)。基线还没跑完,我不报没有统计意义的数字。

⑤ 六个必须主动交代的缺陷(每题一句:缺陷 + 影响 + 修法) ​

#缺陷一句话说法
1BM25 无持久化、启动无回灌"重启后混合检索静默退化成纯向量(DocCount()>0 门控跳过,无告警,只有 trace 的 method 字段能看出来)。修法:启动从 payload/DB 回灌 + 就绪状态 + 告警;前置依赖是先把 Rebuild 改用 AddWithDocID。"
2BM25 独有命中正文为空"allDocs 只由向量结果构建,所以 BM25 独有的 chunk 只剩 ID,空内容进上下文。修法:BM25 索引存正文,或融合后批量 Get 补齐。"
3重排无 oversample"候选池 = topK,重排只能改排序、救不了召回。修法:召回放大 3~5 倍再重排取 topK。"
4score 语义不稳定"重排成功是 relevance_score,降级是 RRF 分,同字段两套量纲。修法:拆成 fusion_score / rerank_score。"
5安全类:MCP owner 解析失败 fail-open、REST 层把用户凭据当系统级 Key、无 metrics/trace"权限收敛应该从凭据 owner 派生而不是从凭据类型派生;检索范围解析失败必须 fail-closed。可观测性只有 slog 日志,没有 metrics 和 trace。"
6Embedding 不校验返回条数 + 无 payload 索引 / 无 score 阈值 / 无超时预算"少返回条目会产生 nil 向量并真的写进 Qdrant;kb_id 没建 payload 索引;检索层没有超时预算,靠上游 ctx 兜。"

说缺陷的姿势:一句缺陷 → 一句影响 → 一句修法 → 停住。不要道歉、不要过度自我否定。


⑥ 三个反问(挑 2–3 个问) ​

  1. "你们线上向量检索和关键词检索怎么融合的?RRF 还是加权?召回和重排的条数比例大概多少?"
  2. "团队现在有 RAG 效果的评估基线吗?人工标注还是模型判分?"
  3. "如果我进来做这块,第一件该做的是补评估基线,还是先解决某个具体线上问题?"

⑦ 最关键的三句"保命话术" ​

  1. 被问效果数字:"没有可信基线,我不编 Recall。唯一真实的一组是 RAGAS 在 7 条样本上的四个指标——faith 1.0 / relevancy 0.78 / precision 0.83 / recall 0.95,样本量我先讲清楚,它只证明链路通。另外 docs/15-rag优化计划.md 里那句'预期 Recall 提升 10%+'是计划里的预期,不是实测。"
  2. 被质疑 AI 写的:"是 AI 辅助。我先写 spec 和可验证 checklist,做过一次全量 review 修掉 6 个 P0 + 十几项 P1;有些模块我只做到行为验证,比如 ffmpeg 抽帧和 Wails 打包。"
  3. 被问不会的:"这一层我没做到源码级——我的理解是 X(说出你知道的边界),回去我会先看 Y(说出你会从哪入手)。" (永远不要硬编。会说"不知道边界在哪"比假装懂安全一百倍。)

⑧ 架构图(考前照着默画一遍) ​

上传 → 落盘 + task(pending) → 返回 task_id
                          ↓ 500ms 轮询 / 原子认领
 worker×5 → Load → 校验(≥20字符) → Chunk(3策略) → Embed(批) → Qdrant.Upsert ─┬→ BM25 索引 Add
                                                                              └→ PG 状态更新
 失败 → pending, retry++ (退避写 updated_at, 2s/4s/8s) → 超 3 次 failed

问答 → KB 范围校验(防越权) → 历史(≤10) → Query 改写(LLM)
     → 检索[向量 ∥ BM25 → 加权 RRF(k=60, 0.7/0.3) → 重排(/v1/rerank)]
     → buildContext(≤2048 token, ≤5 条) → 生成
     → SSE: thinking×N → sources → chunk×N → done / error
出口:REST / SSE / MCP(6 只读 Tool)     共享:internal/app 装配 + 认证 + 权限

⑨ 上场前 60 秒自检 ​

  • [ ] 我能不看稿说出 ② 里的 6 个数字吗?
  • [ ] 我能用一句话说清"RRF 为什么不用加权求和"吗?(关键词:量纲不可比)
  • [ ] 我能主动说出 ⑤ 里的第 1 条(BM25 静默失效)吗?
  • [ ] 我的"效果数字"答法是否已经背成肌肉记忆?
  • [ ] 反问准备好了吗?
  • [ ] 最后一个心理准备:被问到答不上来的细节是正常的,这个项目有 6 万行代码,我只需要把主干链路、每个取舍的理由、以及我知道的缺陷讲清楚——这三样我都有。

持续学习,持续构建。