你的简历里有两块工作:一个是智能文档助手 RAG 链路,一个是模型服务化的成本优化。请用 2 分钟说明这份简历上你最想让面试官看到的一条主线能力是什么?哪些部分是你独立主导、哪些是配合完成的?
1) 智能文档助手(问答 + 摘要 + 结构化抽取) 2023.09–2024.06
考察点考察候选人对自己简历主线的认知与贡献边界的表达是否清晰、是否夸大
按真实面试顺序分七个阶段,每道技术题附多轮追问链;以下为完整内容,未做删减,无需登录即可查看。
AI 生成 本报告由 AI 生成,仅供参考。出题标准来自多位资深 AI 面试官与算法技术专家的多年一线面试经验, 考什么由大模型对题库(含各大厂高频面试题)做 RAG 检索决定,每题挂回简历原文。算法 + 后端 + 前端全链路:RAG 问答助手从解析到上线,含降本与线上排障。
先看题目从哪来。下面是简历原文,带底纹的段落被出题引用; 点击任一段落,模块三仅保留与该段相关的问题。
面试官看到这份简历时脑子里在想什么:哪些是加分项、哪些会让他犹豫、 哪些是写错了、哪些信息不足需要当面问清。
七个阶段,顺序就是真实面试的顺序:先确认简历疑点 → 问项目情况 → 用最多的问题挖技术实现 → 客观题 → 八股 → 开放题 → 方向与规划。 技术题都带一条追问链,轮数不固定,追到能分出候选人高低的地方为止; 有的落到底层概念,有的落到八股。
面试开场:先把你简历里没写清、对不上、需要当面确认的地方问出来
你的简历里有两块工作:一个是智能文档助手 RAG 链路,一个是模型服务化的成本优化。请用 2 分钟说明这份简历上你最想让面试官看到的一条主线能力是什么?哪些部分是你独立主导、哪些是配合完成的?
1) 智能文档助手(问答 + 摘要 + 结构化抽取) 2023.09–2024.06
考察点考察候选人对自己简历主线的认知与贡献边界的表达是否清晰、是否夸大
简历里"可采信率 71%→89%"和"单均处理时长 6 分钟→2 分10 秒"这两个数字,分别是多大规模样本上、用什么口径测出来的?谁做的抽检?
- 结果:上线后客服单均处理时长从 6 分钟降到 2 分10 秒;答案带出处比例 100%,人工抽检 200 条的
考察点考察量化指标的口径意识——数字是否真实、可复现、有统计意义
你写"推理成本月均 3.8 万降至 2.4 万(-37%)",但我看你备注过只做过一周分项观察。那这 37% 里语义缓存和小模型分流各自贡献多少?如果被要求给一个可信归因,你会怎么补实验?
- 背景:高峰期日调用 12 万次,月推理成本 3.8 万元,其中约 40% 是重复问题。
考察点考察候选人对自己结论的严谨度,以及能否设计消融实验做归因
技能列表里写了 React,但简历没有任何 React 项目。这块你真实的使用程度是什么?如果面试官要求你现场讲一个 React 的并发/状态管理实践,你能讲出来吗?
挂靠简历:技能:React(仅出现在技能列表,无项目佐证)
考察点验证简历技能项的真实性,考察是否诚实且有可迁移的技术理解
岗位是算法工程师,但你简历里看不到模型训练或微调的痕迹,主要是大模型应用与工程链路。如果面试官问"为什么不调模型而做 RAG",你的判断依据是什么?这个选择在什么情况下会失效?
挂靠简历:简历未见模型训练/微调类工作,主要是大模型应用与工程链路
考察点考察技术选型的判断力,以及是否清楚 RAG 与微调的适用边界
前端与联调这块,除了"单用户限流 10 QPS",你还能给面试官提供哪些数字或事实来证明这块工作的价值?当时页面和接口的实际表现是什么样的?
- 与运维一起把服务上到自建 K8s,写了健康检查与限流(单用户 10 QPS),处理过一次因连接池
考察点考察候选人能否对自己的工作做量化,也检验其是否真的关心上线后的效果
先把你写过的项目问清楚:做什么、你负责哪块、为什么做、结果如何
你在「智能文档助手(问答 + 摘要 + 结构化抽取)」项目里写「客服团队每天处理 2000+ 份工单附件与合同,人工翻找条款平均耗时 6 分钟/单」。请说明:这个项目解决的核心问题是什么,整条链路你负责哪一段,哪些不是你做的?
- 背景:客服团队每天处理 2000+ 份工单附件与合同,人工翻找条款平均耗时 6 分钟/单。
考察点考察能否把业务问题、端到端链路与个人贡献边界讲清楚,判断是真做过还是挂名。
在「模型服务化与成本优化」里你写「高峰期日调用 12 万次,月推理成本 3.8 万元,其中约 40% 是重复问题」。请说明:这 40% 是怎么统计出来的?你做的语义缓存、小模型分流、两段式摘要三项优化,分别打的是哪一块成本?
- 背景:高峰期日调用 12 万次,月推理成本 3.8 万元,其中约 40% 是重复问题。
考察点考察成本优化的问题定位方式(数据从哪来)以及措施与问题的对应关系,验证数字不是拍脑袋。
在「前端与联调」里你写「与运维把服务上到自建 K8s,写健康检查与限流,处理连接池未释放导致的雪崩」。请说明:当时的现象是什么、你怎么一步步定位到是连接池未释放、最后做了哪几处修改?
- 与运维一起把服务上到自建 K8s,写了健康检查与限流(单用户 10 QPS),处理过一次因连接池
考察点考察线上故障排查的路径与复盘能力,看是真正经历过雪崩还是只看过文章。
你写「向 Milvus 贡献中文文档示例脚本;个人维护 RAG 评测小工具」——请说明:Milvus 那个 PR 具体改了什么、评审过程是怎样的?你那个评测小工具评测的是什么指标、380 star 主要来自什么?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察开源贡献的真实性与个人工具的价值判断,防止用 star 数代替真实能力。
简历里两个核心数字是「单均处理时长 6 分钟降至 2 分10 秒」和「人工抽检可采信率 71%→89%」。请分别说明:这两个指标怎么定义、怎么测量的?抽检了多少条、由谁判定?
- 结果:上线后客服单均处理时长从 6 分钟降到 2 分10 秒;答案带出处比例 100%,人工抽检 200 条的
考察点考察量化结果的真实性与口径,是面试官验证简历含金量最直接的一问。
回到「智能文档助手」,面对 2000+ 份工单和合同,为什么最后选的是 RAG,而不是写规则/模板抽取条款,或者直接微调一个小模型来做抽取?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察技术选型是否有依据,还是默认“RAG 就是对的”。
本报告的重心:沿项目里真实用到的技术逐层追问「实现是怎么做的」
你在「智能文档助手」里做了向量+BM25 多路召回与重排,请说明两路结果具体是怎么融合的:加权、RRF,还是各自召回后统一丢给 reranker?各路 topK 和权重是怎么定下来的?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察混合检索的融合方案是真实实现还是名词堆砌,以及参数是否用评测集定过。
RRF 的 k、两路各自 topK、rerank 后的最终 topN 你分别设成多少?改动这几个参数时 recall 和端到端 P95 各有什么变化?
什么情况下 hybrid 反而不如纯向量?比如用户问的是口语化长问句,BM25 会表现成什么样?
那说说 BM25 的公式:IDF 和文档长度归一化分别起什么作用?k1、b 这两个参数控制什么?
「模型服务化与成本优化」里你用了语义缓存,条件是 embedding 相似度>0.95 且长度差<15 字。请说明这两个阈值是怎么定出来的,误命中(不同问题被判成同一个)你怎么发现和控制?
挂靠简历:模型服务化与成本优化:embedding 相似度>0.95 且长度差<15 字命中
考察点考察阈值是用数据标定的还是拍脑袋,以及是否有误命中风险的意识。
缓存具体怎么存?Redis 里用什么结构、TTL 设多久、容量上限到了怎么淘汰?
如果 embedding 模型换了一个版本,或者文档库整体换了领域,之前缓存的语义空间就变了,你会怎么处理?
Redis 常用的数据结构有哪些?过期删除策略和内存淘汰策略分别说说。
长文档摘要你改成了两段式,单次输入 token 从 11k 降到 3.2k。请说明两段式具体是哪两段、每段做什么,以及怎么确认摘要质量没有因为压缩而下降?
把长文档摘要改成"分段摘要 + 合并摘要"两段式,单次输入 token 从平均 11k 降到 3.2k。
考察点考察两段式拆分是否理解本质(分段摘要+合并),以及有没有配套的质量口径。
分段摘要往上合并的时候,怎么防止细节在中间层被丢掉或被模型编造?
摘要类任务你做了小模型分流,为什么这类任务可以分给更小的模型?什么情况下不能分?
为什么要控制上下文长度?长上下文下自注意力的计算复杂度是多少,position 外推又会带来什么问题?
你在「智能文档助手」里自研了 PDF 版式还原,并把表格按行列展开再切分。请说明版式还原相比直接抽文本解决了什么问题,表格展开后的 chunk 长什么样,chunk 大小和 overlap 又怎么定?
- 做法:搭 RAG 链路——PDF 解析(自研版式还原,处理表格与页眉页脚)、按语义切分、
考察点考察文档解析与切分这一环的实际处理细节,识别是否只是调库。
chunk 大小和 overlap 你实际用的值是多少?往大调、往小调分别观察到什么变化?
如果检索总是召回“看起来相关但答非所问”的片段,你怎么判断是切分问题还是排序问题?
Milvus 这类向量库常用 HNSW 做近似最近邻,它的分层结构和 M、ef 参数大概是什么作用?为什么不全用暴力检索?
「智能文档助手」里你选了 bge-base 做向量化、bge-reranker 做重排。请说明选型依据:为什么是 base 而不是 large?加上 cross-encoder 重排后延迟增加多少,你怎么权衡?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察 embedding/rerank 选型是否有实验依据,以及是否理解双塔与交叉编码器的代价差异。
重排候选数你设成多少?从 20 加到 100 时,nDCG 和 P95 各自怎么变,你选的点在哪?
双塔和交叉编码器在结构上差在哪?为什么交叉编码器不能拿来做全库检索?
那双塔检索模型一般怎么训练?对比学习里的 in-batch negative 和 hard negative 分别起什么作用?
你用 FastAPI 提供接口、Celery 异步解析文档。请把一次上传到解析完成的链路讲完整:任务在哪排队、状态怎么回传前端、任务失败或重复投递怎么处理?
后端用 FastAPI 提供 /ask /upload /feedback 三个接口,Celery 处理异步解析,Redis 做任务队列与结果缓存。
考察点考察异步任务链路的完整性与幂等/重试这类生产细节。
Redis 做 broker 时任务积压怎么处理?worker 并发数、prefetch 你根据什么定?
Celery 的 ack 是在任务开始时还是完成时?worker 崩溃后任务会丢还是会重复执行?
消息队列的投递语义 at most once / at least once / exactly once 分别是什么?要实现消费幂等通常怎么做?
「前端与联调」里你提到处理了连接池未释放导致的雪崩,加了池的 maxLifetime 与熔断。请还原这次故障:现象是什么、怎么定位到连接池没释放、最后改了哪些地方?
未释放导致的雪崩(重启后加了池的 maxLifetime 与熔断)。
考察点考察线上故障排查的真实经验与根因分析能力,而不是只报修复动作。
熔断的失败率阈值、统计窗口、半开策略你怎么定的?熔断期间新请求怎么处理?
为什么设 maxLifetime 能缓解这个问题?如果根因其实是慢查询,maxLifetime 设小会带来什么副作用?
K8s 里 liveness 和 readiness probe 有什么区别?把依赖健康检查错配到 liveness 会有什么后果?
「智能文档助手」里可采信率从 71% 提到 89%、答案带出处比例 100%。请说明这两个指标的口径:可采信率是谁判的、抽检多少样本、判定标准是什么?出处 100% 又是怎么强制的?
- 结果:上线后客服单均处理时长从 6 分钟降到 2 分10 秒;答案带出处比例 100%,人工抽检 200 条的
考察点考察指标定义是否严谨、是否知道“带出处”不等于“答案正确”。
抽检样本怎么抽的?抽多少条?抽样偏差你怎么避免?
从 71% 到 89%,具体是哪几类错误被修掉的?分别对应链路上的哪个改动?
大模型幻觉的常见成因有哪些?RAG 场景下有哪些通用缓解手段?
「智能文档助手」的第三个能力是结构化抽取。请说明抽取字段的 schema 怎么定,以及怎么保证大模型稳定输出合法 JSON(温度、格式约束、校验与重试分别怎么做)?
1) 智能文档助手(问答 + 摘要 + 结构化抽取) 2023.09–2024.06
考察点考察结构化输出的工程稳定性,而不只是会写提示词。
抽取结果你用什么指标评?字段级准确率和整条记录完全正确的比例,你更看哪个?
同一字段在文档多处出现不同数值(比如多个金额),你的归一化和冲突消解规则是什么?
temperature、top-p、top-k 分别控制什么?为什么结构化抽取通常要求低温?
针对项目里提到的技术出客观题,并给出较优参考答案
你在「智能文档助手」里做了向量+BM25 多路召回与重排。请说明:这两路召回的结果具体怎么融合成最终候选集(加权分数、RRF 还是统一重排),以及为什么选这种融合方式?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察混合检索的融合策略选择与其背后的排序原理,能否说清而不是只报『我用了混合检索』
RRF 里那个 k 你取多少?两路各召回多少条、最终进 prompt 几条?这个配比是怎么调出来的?
什么情况下混合检索反而比纯向量差?失效边界在哪?
你在 Milvus 里用的哪种索引和度量方式?HNSW 和 IVF_FLAT 的区别是什么?为什么文本 embedding 常配余弦相似度而不是欧氏距离?
你在「模型服务化与成本优化」里做了语义缓存,命中条件是 embedding 相似度>0.95 且长度差<15 字。请说明:0.95 这个阈值和『长度差』这个附加条件分别解决了什么问题?误命中会带来什么后果、你怎么防?
- 做法:加语义缓存(embedding 相似度 > 0.95 且问题长度差 < 15 字则直接命中缓存),
考察点考察语义缓存的阈值取舍与误命中风险意识——缓存命中率不是越高越好
缓存的 key 具体怎么设计?多租户、不同模型版本、prompt 改了以后缓存怎么失效?
什么场景下语义缓存会失效甚至帮倒忙?
文本 embedding 是怎么训出来的?对比学习里的 InfoNCE 在做什么?余弦相似度和点积的区别是什么、为什么用之前常要先归一化?
你在成本优化里把长文档摘要改成两段式,单次输入 token 从 11k 降到 3.2k。请说明这两段各自做什么、怎么切,以及为什么这样能降 token 而不是在丢信息?
把长文档摘要改成"分段摘要 + 合并摘要"两段式,单次输入 token 从平均 11k 降到 3.2k。
考察点考察是否理解 map-reduce 式摘要的省 token 来自何处,以及信息损失如何兜底
第一段的输出粒度怎么定?太长第二段还会超,太短关键信息没了,你怎么权衡?
什么类型的文档用两段式反而更差?
Transformer 自注意力的计算复杂度是多少?上下文窗口受什么限制,位置编码在这里起什么作用?
你在「智能文档助手」里提到『表格按行列展开再切分』。请说明:一张复杂表格具体展开成什么形式?表头、合并单元格、跨页表格怎么处理?为什么不能直接按字符长度切?
挂靠简历:表格按行列展开再切分
考察点考察对结构化文档切分的处理细节,判断是否真做过版式还原而不是调库了事
展开后的行你按什么粒度合 chunk?太碎和太大分别会怎样?
跨页大表、嵌套表这种极端情况你实际怎么兜底?
文档切分整体上,chunk 大小和重叠怎么定?为什么按语义边界切优于定长硬切?
对应题库题卡:RAG 的完整链路你会怎么搭?文档切分这一步,你会怎么定 chunk 大小和重叠?为什么?
你用 bge-base 做向量化、bge-reranker 做重排。请说明两者在结构上有什么差别,为什么召回阶段用双塔、重排阶段用交叉编码器?rerank 的候选数你是怎么定的?
- 做法:加语义缓存(embedding 相似度 > 0.95 且问题长度差 < 15 字则直接命中缓存),
考察点考察对召回-重排两阶段范式的理解,能否讲清效率与精度的取舍
rerank 的候选数你是取 20 还是 50?上线后实测延迟增加多少、精度提升多少?
什么情况下加 rerank 也救不回来?
双塔模型和交叉编码器在训练目标和推理复杂度上分别是什么?为什么交叉编码器不能直接用来建向量索引?
由上面的技术检索到的经典基础题(八股)
你的 RAG 链路把向量化结果存进了 Milvus。请说明:Milvus 里常用的索引类型有哪些、各自的取舍是什么?度量方式 COSINE / IP / L2 有什么差别,怎么选?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察向量数据库索引与度量的硬知识,判断是否只是调 API 建了个库
你用 bge-base 做向量化入库。请说明:这类 embedding 模型的输出向量怎么取(池化方式)、为什么通常要做 L2 归一化、中文检索时 query 侧和 doc 侧的处理是否要一样?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察 embedding 使用的实操细节,这些细节直接决定检索质量
你在智能文档助手里把 BM25 和向量检索并行使用。请说明:BM25 相比朴素 TF-IDF 改了什么?k1 和 b 两个参数分别控制什么?为什么 BM25 特別擅长召回专有名词和型号?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察关键词检索的经典原理,判断是否理解 BM25 而不是当作黑盒调包
你在前端与联调里提到『处理连接池未释放导致的雪崩』。请说明:这里说的服务雪崩是怎么形成的?它和缓存雪崩、缓存穿透、缓存击穿分别是什么区别?
挂靠简历:处理连接池未释放导致的雪崩
考察点考察故障归因能力,能否把线上事故与经典缓存/服务稳定性问题区分清楚
你把服务上到自建 K8s 并写了健康检查。请说明:liveness probe 和 readiness probe 的职责差别是什么?配错 probe 会引发什么后果?
挂靠简历:写健康检查与限流
考察点考察容器编排的基础认知,判断是否只是让运维代劳
你用 FastAPI 提供接口、Celery 做异步解析。请说明:FastAPI 里 async def 和 def 路由的执行方式有什么差别?在 async def 里调用同步阻塞的解析代码会发生什么?为什么这类任务要交给 Celery?
后端用 FastAPI 提供 /ask /upload /feedback 三个接口,Celery 处理异步解析,Redis 做任务队列与结果缓存。
考察点考察异步模型的理解,判断是否踩过『async 里跑同步代码』这个经典坑
基于项目或简历内容的开放性设计题,考方案取舍,没有唯一答案
你在「智能文档助手」里把人工抽检可采信率从 71% 提到 89%,但同时答案带出处比例已经是 100%。如果业务要求继续推到 95%,你打算先动链路的哪一环?怎么衡量每一次改动是否真的有效?
- 结果:上线后客服单均处理时长从 6 分钟降到 2 分10 秒;答案带出处比例 100%,人工抽检 200 条的
考察点考察能否对 RAG 的失败样本做归因分类,并给出带评测闭环的优化优先级,而不是拍脑袋换模型。
你在「模型服务化与成本优化」里用的语义缓存规则是 embedding 相似度 > 0.95 且长度差 < 15 字命。如果让你重做一版更鲁棒的缓存,你会怎么设计?哪些类型的问题你坚决不允许命中缓存?
- 做法:加语义缓存(embedding 相似度 > 0.95 且问题长度差 < 15 字则直接命中缓存),
考察点考察缓存策略的边界设计意识——相似不等于等价,以及能否识别高风险命中的问题类型。
你提到单均处理时长从 6 分钟降到 2 分 10 秒、接口 P95 从 4.1s 降到 2.3s。如果要再压一版把在线问答的 P95 做到 1s 以内,你会怎么拆这条链路的延迟预算?
- 结果:上线后客服单均处理时长从 6 分钟降到 2 分10 秒;答案带出处比例 100%,人工抽检 200 条的
考察点考察端到端性能拆解能力——能不能先测量分段耗时再分配目标,并区分在线请求与异步任务。
你在智能文档助手里把合同表格「按行列展开再切分」。如果业务要求从合同里稳定抽出金额、付款节点、违约条款这类字段,你怎么设计端到端的结构化抽取方案?字段准确率怎么证明?
挂靠简历:表格按行列展开再切分
考察点考察结构化抽取的工程完整度——schema 约束、校验回退、字段级评测是否成体系。
你个人维护的 RAG 评测小工具拿了 380 star。如果公司要把类似能力做成每天跑、能卡住上线门禁的评测平台,你会怎么设计?以及怎么防止评测集被优化到过拟合?
- 开源:向 Milvus 贡献过一个中文文档示例脚本(已合并);个人维护一个 RAG 评测小工具(GitHub 380 star)。
考察点考察评测体系的工程化设计,以及是否有防过拟合、防指标自嗨的意识。
技术方向的选择依据、职业规划与长期打算
你在智能文档助手里同时承担「算法与后端接口」两条线。往后一年你更想往哪条深挖?请结合你做过的语义缓存、检索与重排、成本优化这些实际产出,说说你的判断依据。
挂靠简历:角色:算法与后端接口
考察点考察是否有清晰的自我定位与取舍逻辑,而不是项目里有什么就做什么。
这个岗位是算法工程师,但你的简历集中在 RAG 全链路与工程落地,没有训练或微调类产出。如果给你三个月和一份领域语料,你的第一版微调计划是什么?以及什么情况下你会判断该微调、什么情况下继续走 RAG 和提示词?
- 做法:加语义缓存(embedding 相似度 > 0.95 且问题长度差 < 15 字则直接命中缓存),
考察点考察是否正视自己的能力边界,以及补短板的路径是否可执行、判断边界是否清楚。
你的技术栈横跨 RAG 全链路、FastAPI/Celery 服务化、Vue3 管理端和自建 K8s 部署。你怎么判断「AI 全栈」在你的长期规划里是过渡还是主线?未来三年你打算怎么分层推进?
后端用 FastAPI 提供 /ask /upload /feedback 三个接口,Celery 处理异步解析,Redis 做任务队列与结果缓存。
考察点考察方向判断是否具体、有阶段目标,而不是跟风谈趋势。
你在模型服务化里把推理成本从月均 3.8 万降到 2.4 万,但你自己也提到没有拆分语义缓存与模型分流各自的贡献。往后遇到成本和效果的冲突,你会怎么定优先级?归因怎么才能做扎实?
- 结果:重复问题命中率 37%,推理成本从月均 3.8 万降到 2.4 万(-37%);接口 P95 从 4.1s 降到 2.3s。
考察点考察实验设计与归因意识,以及能否把成本问题放进业务价值里判断。
你向 Milvus 贡献过中文文档示例脚本,个人维护的 RAG 评测小工具也有 380 star。开源在你接下来的职业规划里扮演什么角色?你打算怎么延续,而不只是刷 star?
bge-base 向量化入 Milvus、多路召回(向量 + BM25)+ bge-reranker 重排、大模型合成答案并给出出处。
考察点考察开源投入是否有明确目的与方法论沉淀,而不是简历装饰。
你的技能列表里同时写了 Vue3 和 React,但项目里只有 Vue3 + TypeScript 的管理端与对话界面。如果目标岗位要求前端框架选型能力,你会怎么补这一块?以后做 AI 产品的界面,你按什么标准选框架?
- 用 Vue3 + TypeScript 写管理端与对话界面,实现引用定位(点击答案跳转到原文段落)、
考察点考察技能自我认知是否诚实,以及技术选型是否有判断标准而非凭熟悉度。
前三个模块给你的是题目与依据;这一模块把它们组织成一场真的面试: 面试官最可能问的问题、按真实顺序推进,每问都带一份可以照着组织语言的 标准参考答案。顺序与上面七个阶段一致。
简历我看过了,两条线挺清楚:前面是「智能文档助手」的 RAG 全链路,后面是模型服务化和成本优化。今天大概 45 分钟,我们从前到后走一遍,中间我会随时打断问细节。有不确定的地方直接说不确定,我更想看你的判断过程,不用硬凑。
你简历上这两段其实是同一条链路的两头——「智能文档助手」是主干,「模型服务化与成本优化」是它的下游。如果只允许你挑一个细讲,你挑哪个?另一个里有什么结论是可以直接迁移过去的?
我挑「智能文档助手」。因为它是从解析、切分、召回、重排到合成和接口的完整链路,成本那块本质上是它的下游产物,脱离主干讲成本不好讲清楚。可迁移的结论有三条:一是路由思想,把任务按难度分流到不同模型,这个在检索、抽取、摘要上都能用;二是分段处理,长文档两段式摘要和表格按行列展开,都是把「一次处理不完的东西」拆成可控颗粒;三是口径意识,成本这类数字必须先说清怎么统计的,否则优化无从验证。
你在「可采信率 71%→89%」后面专门加了个括号说明口径是人工盲评、不是自动指标。这句话是你主动加的,还是被要求加的?为什么要在简历里写这种自我限制的话?
是我主动加的。原因很直接:这种提升幅度的数字如果不写口径,面试官第一反应一定会追问,而且很容易被默认成自动指标拿去和别家对比,那对我反而不利。写清楚「人工盲评、是否可直接采用」,等于提前把边界划出来,讨论就能直接进入方法和样本量,而不是先花五分钟澄清数字含义。另外我确实有个习惯,写周报和文档时会把口径和边界写清楚,不做看起来很准的指标,这句话算是这个习惯的延伸。
这里有个小矛盾:你写「团队 4 人,我负责算法与后端接口」,但下面又写了前端与联调、K8s 上线、连接池雪崩。到底哪些是你主导、哪些是你配合?我不想听到全是你的。
我分一下。算法侧是我的主责,包括解析后的切分策略、召回重排的选型、合成答案的约束,这些改动是我提的、我做的;后端接口也是我写,三个接口和 Celery 那条异步链路。前端那一块,管理端主体是前端同学写的,我负责对话界面和引用定位这部分,以及和接口的联调。K8s 是运维主导,我配合写了健康检查和限流规则。那次雪崩是我参与定位和善后,重启也是我做的,但加 maxLifetime 和熔断是运维和我一起定的。原则是配合的活我不说成主导,但联调和线上问题我愿意认。
从链路最前端开始。你这段链路第一个环节是 PDF 解析,还自研了版式还原。当时进来的 PDF 主要是什么来源、什么类型?拿到一份从没见过的文档,你怎么判断解析质量够不够用?
来源是客服的工单附件和合同,这两类版式差别挺大:工单附件格式杂、扫描件不少,合同相对规整但有大量表格和跨页。判断解析质量我一般不看好不好看,只看两件事:关键内容有没有丢,以及切出来的块是不是语义完整。做法是维护一个抽样文档集,每次改解析器就跑一遍,人工对照原始 PDF 看条款、金额这类关键位置有没有被吃掉。如果表格解析完行列错位,后面检索再好也救不回来,所以这一环我是按「召回条款是否完整」来验收,而不是按字符抽取率。
你写「把表格按行列展开成结构化文本再切分,命中率明显改善」。你怎么确认是这个改动起了作用,而不是同期还改了别的东西?
这里我得先坦白,我当时没有做严格对照。我的观察方式是:先把「表格条款答不上来」的 bad case 挑出来做成一个小集合,改动前后跑同一批问题看命中变化,同时不碰其他环节。这种方式能排除掉一部分干扰,但不能算严格的因果证明,因为 reranker 和切分粒度那段时间也有微调。如果现在重做,我会固定其他变量、留一个不改的对照组,并且把命中率拆成表格类和正文类分开看,这样收益归属才站得住。
客服每天 2000+ 份附件,但你只暴露了 /ask /upload /feedback 三个接口。这个量级下高峰期上传是什么样?你怎么保证解析不会把在线问答拖垮?
上传接口本身很轻,只做落盘、算幂等键、投递任务,然后立刻返回一个 task 标识,真正的解析不在这条路径上。解析走 Celery 异步,任务进 Redis 队列,用独立的 worker 消费,并且限制并发数,避免解析把 CPU 和内存吃干、影响在线问答。在线问答是另一条路径,只读向量库和缓存,不依赖解析 worker。所以这两件事在资源上是要分开的,容量和并发也得分开压。这也是当时选 Celery 而不是在 FastAPI 里直接解析的原因。
「约 40% 是重复问题」——这 40% 到底是按问题原文完全一样统计的,还是按语义相似统计的?这直接决定你后面缓存能拿到多少收益空间。
这个口径我得如实说,当时的统计是偏粗的,主要是问题文本层面的重复统计,加人工看了一批近似问法,没有做严格的语义聚类。所以我只能说 40% 是个量级参考,不能当成精确的重复率。这也解释了为什么语义缓存的命中率是 37%,和 40% 比较接近属于合理,但我不能因此说缓存把重复问题捞干净了。如果重做,我会先对问题做一次聚类,分出「字面重复」「同义改写」「只是同一个文档的不同问题」三类,再看缓存能覆盖哪几类——因为第三类其实不该命中缓存,答案很可能不同。
你把「采纳/不采纳」直接回流成评测集。这里的「采纳」到底代表什么?用户点了采纳但答案是错的,这种情况你怎么办?
采纳在我看来只是弱标签,它代表用户这次懒得改或者够用了,不代表答案正确。所以我的做法是把它当成复核队列的优先级信号,不是直接当正样本入集。真正进评测集之前要人工过一遍,判断是答案错、出处贴错、还是用户就是没用上。不采纳的同样要分类,有的确实是答错,有的只是用户想换个问法。标签如果不分类,评测集会越跑越偏,最后优化的方向也会跟着偏。
你前端做了「点击答案跳转到原文段落」这个引用定位。切分之后的 chunk 和原文位置是怎么对应上的?如果一个答案横跨多个 chunk,怎么处理?
关键是切分的时候就别丢位置。每个 chunk 入库时我带着 doc_id、页码和块序号一起存,检索命中后这些来源信息要一路带到合成阶段。合成时我不让模型自己编出处,而是把这些候选块编号给它,要求它按给定的 cite 标识引用,模型只能选我给的编号。前端拿到的就是这些编号对应的位置,直接定位到原文段落并高亮。跨 chunk 的答案就带多个编号,前端就依次跳。这样做的核心是——出处是检索层给的,不是模型说的,模型说的出处不可信。
你上了 cross-encoder 重排,这本身是加延迟的操作,但整体 P95 反而从 4.1s 降到 2.3s。这两件事怎么会同时成立?重排在整条链路里占多少延迟?
这两件事不是因果关系。重排确实增加延迟,它是在召回之后对候选做一次交叉编码打分,属于纯加法。P95 从 4.1s 降到 2.3s 是成本优化那一版的整体口径,主要来自两个地方:一是语义缓存命中后直接返回,压根不走后面的重排和生成;二是两段式摘要把输入 token 从 11k 压到 3.2k,生成阶段的时间显著下降。所以是缓存和 token 省下来的时间,盖过了重排加的那部分。我不会拿这两个数字说重排变快了,那是两回事。
把表格按行列展开成文本之后,会引入大量重复的表头词。这对 BM25 的词频统计、和对 embedding 的语义表示,分别有什么副作用?你怎么缓解?
这道题考的是结构化文本对两路召回的不同影响。对 BM25 来说,重复表头会让这些词的文档频率虚高,IDF 被拉低,结果是表头词变得没有区分度,甚至任何带这张表的 chunk 都能被召回。缓解办法是控制重复:只在块首或每若干行保留一次表名加列名,或者把表名单独作为一个字段参与召回。对 embedding 来说问题相反,展开后的文本是一堆「字段名:值」的短句堆叠,语义很稀薄,直接编码出来区分度差。更稳的做法是把表名、列名拼成一句自然语言描述再编码,让它落到语义空间里。
你自己维护的那个 RAG 评测小工具,评的是检索还是生成?如果只允许你留一个指标,你留哪个,为什么?
检索和生成我分开评。检索侧看 recall@k、MRR 这类,便宜、可复现,适合回归;生成侧看答案是否忠实于给定的上下文、出处对不对。如果只留一个指标,我会留「答案可采信率」,因为它最贴近用户价值,能同时反映检索和生成两端的问题。但它贵,必须抽样且要人判。所以我的实际建议是:日常回归用检索指标卡门禁,发布前用可采信率抽样兜底。一个便宜但只能测一半,一个贵但能测全链路,两个位置不同,不能互相替代。
那次雪崩,你最后加的是连接池的 maxLifetime 和熔断。我想听的是判断过程:你怎么一步步确定问题出在池上,而不是下游本身慢?加这两个东西分别解决什么,哪个是真正修问题?
判断逻辑是先分清「谁在等」。看池的活跃连接数、等待获取连接的时长,同时看下游自身耗时和错误率。如果下游耗时正常、错误率也正常,而本服务这边请求全卡在拿连接上,那问题就在池,不在下游。maxLifetime 是兜底——它让连接到期强制回收,把泄漏或异常路径没归还的连接捞回来,但它不修复泄漏本身,真正修的是把所有分支包括异常分支的归还都补齐。熔断处理的是另一件事,它防的是下游真的挂了之后请求继续堆积,把本服务也拖垮。两者一个是治存量一个是防扩散,性质不同。
你简历里写了解「JSON Schema 约束输出结构化」。具体到工程上,你怎么保证模型输出的一定是合法 JSON?
我会分三层。第一层是优先用模型服务商提供的结构化输出或 function calling,由它在解码层面约束,这比纯 prompt 靠谱得多。第二层是 prompt 侧配合,把 schema 写清楚、字段尽量浅、少用嵌套和自由文本长字段,再给一两个例子。第三层是兜底:收到结果先用 JSON Schema 校验器校验,不合法就把错误信息拼回去让它修一次,重试一次还不行就降级到规则抽取或转人工。要注意的是,校验不能只看能不能解析,字段类型、枚举值、必填都得校,否则会出现「能解析但字段是错的」这种更隐蔽的问题。
你写「按语义切分」。具体是怎么实现的?在中文合同这种长段落文本上,语义切分和固定长度加 overlap 相比,收益和风险分别在哪?
常见实现是先把文本切成句子,再算相邻句子的向量相似度,在相似度明显掉下来的地方断开;另一种是直接按结构切,比如条款编号、标题层级。合同这类文本结构非常强,有明确的条、款、项,我个人会优先按结构切,语义相似度只用来兜底——比如一段正文特别长又没编号,再用它切。好处是块和条款对齐,召回出来就是完整一条,引用定位也准。风险有两个:块长度方差大,超长的还得再切、太短的还得合并,否则会影响召回;另外语义切分对流水线依赖重,多一步模型推理,慢了也更容易出错。
缓存你加了 TTL 和人工纠错入口。TTL 按什么定?如果一条缓存被用户标错了,系统怎么把它清掉,并且保证下次不会再命中同一条?
TTL 我倾向于按内容的时效性分档,不能一刀切,但这里的主要作用是控制错误的暴露窗口,所以宁短勿长。纠错的处理要分两步:第一步是立刻失效,把这条 key 删掉或者标记成不可用;第二步是防止它再被命中,因为命中判断是走相似度的,删了 key 之后同义问法还是可能匹配上来,所以要把这条问题加进一个「禁止命中」的集合,命中的时候先过这个集合。同时这条要进 bad case 集,用来回头看阈值是不是定松了。只删缓存不做第二步,等于没修。
如果同一份文件被上传两次,你怎么保证不会解析两遍、也不会产生两份向量?幂等这一层你放在哪?
幂等键放在文件内容上,不是文件名,也不是请求 ID。上传时先算内容 hash,查这个 hash 有没有解析过,有就直接复用原来的文档标识,不再投递任务。任务侧再做一层,用文档标识加分布式锁防止重复投递和并发消费。最后是写入侧,向量库里用文档标识加块序号做主键,走 upsert 而不是 insert,这样即使任务重复跑,结果也是覆盖而不是翻倍。三层里最后一层最重要,因为前两层都可能因为异常重试被绕过,写入幂等才是最终防线。
你拿 Redis 当 Celery 的 broker。Redis 做消息队列在可靠性上有什么已知的短板?什么情况下你会换成 RabbitMQ 这类?
Redis 作为 broker 本质上是拿 list 或 zset 模拟队列,没有原生的确认、重投、死信和优先级机制,投递语义偏 at-most-once,实例挂了或者 worker 拿到消息就崩,消息就可能丢。缓解方式是配合 visibility timeout 加定期重推,但代价是会出现重复投递,所以消费侧必须自己幂等。什么情况该换?当任务不能丢、需要严格的 ack 和死信队列、需要优先级和延迟队列、或者需要按路由分发的时候,我会选 RabbitMQ 这类专业 broker。反过来,任务可重跑、量不大、团队又已经在用 Redis,就先用 Redis,别为了可靠性提前复杂化。
你的 Redis 既做任务队列又做结果缓存,如果共用一个实例,这里有什么风险?你会怎么把它们隔开?
风险主要有三个。第一是淘汰策略打架:缓存希望内存满了按 LRU 淘汰,但队列里的消息被淘汰就是丢任务,所以同一个实例上没法用一套策略满足两边。第二是互相拖累,大 key 或者热 key 的慢操作会顶住整个实例,队列的延迟和缓存的延迟互相影响。第三是内存打满时两边一起出问题,故障域没有隔离。做法是拆成两个实例,队列那个设 noeviction,缓存那个设 allkeys-lru,并且分开监控。只分 DB 号是不够的,因为内存和淘汰是实例级共享的,DB 隔离不了这个。
你的元数据在 PostgreSQL、向量在 Milvus,一份文档被删除时,两边怎么保持一致?不一致的时候怎么兜底?
这里我不追求强一致,走的是「元数据为准、向量可重建」。删除时先在 PostgreSQL 标记为已删,再按文档标识去删 Milvus 里的向量。如果 Milvus 删除失败,就落到一个补偿任务里按软删列表重试。更关键的是读路径兜底:检索出来的候选,在返回前按 PostgreSQL 里的有效文档集合再过一遍,删漏的也召不回来。这样即使两边短时间不一致,用户也看不到已删文档。为什么不做强一致?因为跨存储的分布式事务代价太高,而这个场景对一致性的要求其实是「最终一致且读路径不能漏」,用软删加补偿加过滤就够了。
重排之后有多个候选块,合成答案时你怎么控制塞进 prompt 的上下文?如果超了窗口怎么办?
我先按重排分数排序,按顺序取,同时卡一个总 token 预算,预算里要给系统提示和输出留出余量,不能把窗口占满。超预算的时候我不倾向直接截断尾部,因为对条款类问题来说,把一个条款从中间截断比少召几篇更危险——模型会拿着半句话编出完整结论。更稳的做法是少取几篇但保证每篇完整,或者对候选做一次预处理压缩,但压缩会丢细节,只适合摘要类任务,不适合抽取类。所以按任务类型分开定策略,而不是一套规则走到底。
合同是敏感数据。如果这套系统要从客服扩到法务、销售等多个部门,你怎么设计权限和数据隔离?尤其是检索这一层,怎么保证不串?
核心原则是过滤条件必须下推到检索入口,而不是召回之后再筛。每个块入库时打上部门、密级这些标签,向量检索和 BM25 两路都在查询时带上这些过滤条件,Milvus 用标量字段过滤,关系库用条件过滤。如果先召回全部再过滤,中间结果已经进过 rerank、进过日志,那就是泄漏。第二个要点是缓存 key 一定要带权限维度,否则同一个问题法务命中过、销售就直接拿到答案,这是很容易被忽略的一条。再往外是审计和脱敏:谁在什么时候问了什么、答案引用了哪些文档都要留痕,敏感字段在返回前做掩码,生成阶段也不引入无权限的片段。
假设下周让你接一个全新领域,比如医疗或者法律,你只有两周时间判断这套 RAG 架构能不能复用。你的计划是什么?
前两天先不碰代码,拿 50 到 100 个真实问题和期望答案做一个小评测集,把现有链路直接跑上去拿基线,这一步的目的不是做好,是看清失败长什么样。第三天开始分桶:是检索根本召不回、是切分把关键信息切碎了、还是术语对不上、还是文档里压根没有答案。然后针对最大的那一桶试一到两个改动,比如加领域词表、换或补一个领域 embedding、调整切分粒度。第二周做消融,确认这个收益稳定、不是运气。判断标准很清楚:如果失败集中在检索层,架构能复用,调检索就行;如果大量问题需要跨文档多跳推理、或者答案本身不存在,那就不是调优能解决的,得换方案。
如果明天大模型 API 涨价三倍,或者公司要求整套私有化部署,你的链路怎么改?
不管是涨价还是私有化,第一件事都是把模型调用抽象成一层网关,业务侧只调这层接口,后面换什么模型对上层透明,这是前提。在这个基础上走三条路:一是路由,把对生成质量要求低的任务——摘要、分类、重排——先切到本地小模型或便宜模型,只留复杂抽取和追问走大模型;二是省 token,靠更准的召回把上下文压到最小,把离线摘要改成批处理跑;三是预热,把高频问题的答案提前算好。私有化部署的坑主要在本地模型的中文长文能力和并发吞吐,这两个必须压测过再切量,不能看着 demo 能跑就上。
你工作两年左右。从简历看,你的强项是把链路跑通然后落地。你觉得现在最影响你往上走一步的短板是什么?
我最缺的是可复现、可归因的实验能力。成本那个 37% 我没把语义缓存和小模型分流拆开看,只做过一周的分项观察,这就是典型症状——做得出来,但说不清是哪一步起的作用。这样往上走会很吃亏,因为你没法判断下一个改动值不值得做。打算分两步补:第一步是流程上的,任何优化先写口径、留对照组,评测集和回归跑当成习惯而不是额外工作;第二步是补模型侧的动手能力,做一次完整的数据构造和微调,因为现在遇到检索解决不了的问题,我手里只有调检索这一张牌。
你自研了 PDF 版式还原。如果公司让你继续在这条线上投入,你会继续自研,还是换开源或商业方案?依据是什么?
我的依据是文档来源的分布和维护成本,不是技术难度。如果绝大部分文档来自几家固定模板的合同,那自研或者用开源底座加模板规则成本很低、也可控,继续自研是划算的。如果来源很散、版式五花八门,自研就会变成长期人力黑洞——每来一个新模板就要修一次,这类活我不建议团队自己扛,应该换成成熟的版面分析加表格结构识别方案,把人放在检索和合成上。所以我会先统计一份文档来源和版式的分布,再决定,而且这个统计要定期重跑,因为业务模板是会变的,一次判断不能管三年。
最后一个问题。如果三年后你还在做 AI 应用方向,你希望别人用一句话介绍你?为了让这句话成立,接下来 12 个月你具体做什么?
我希望是「把 RAG 这类系统从 demo 做到线上,并且能说清它为什么好、为什么不好的那个人」。后半句比前半句更难,也是我现在最缺的。接下来 12 个月我想做三件事,都尽量可衡量:第一是把评测做扎实,口径、对照组、回归集这一套跑顺,争取每季度产出一份带口径的评测结论;第二是补一次完整的领域模型适配,哪怕是个小模型,把数据构造到评估走通一遍;第三是在系统侧再完整吃一次故障,压测、容量规划、成本模型都自己做一遍,而不是等运维给结论。
今天就到这儿。整体印象是:你对自己做过的事边界感比较清楚,也会主动标口径,这是加分项;短板也很明显——凡是涉及「归因」和「对照实验」的地方,你的回答都偏软,成本那 37% 是典型例子。建议你回去把两件事补上:一是把语义缓存和小模型分流做一次真正的拆项对照,二是把评测集和回归跑变成习惯,每次改动先写口径再动手。最后留 5 分钟给你提问,你可以问团队现在的技术阶段、这个岗位前半年最想解决的问题、以及算法和工程两条线在团队里怎么分工。
完整模拟面试问答属于最高档「深度版」;基础版与标准版不包含这一模块。 上传你的简历即可按同样流程生成一场。
上面是虚构简历的效果。上传你的简历,题目会挂到你写过的项目、指标和技术栈上, 并先给出简历体检与题目目录供你确认。