字节大模型应用岗实习两小时拷打:记忆机制 + RAG 全链路,13 道题逐个答透
今天分享一篇字节跳动·大模型应用算法(实习)的面经。整场聊了两个小时,从 Agent 的长短期记忆怎么设计,到 RAG 的每个环节为什么这么做,再到 asyncio、HyDE、SGLang vs vLLM,面试官一路追问到底,问得很细。这位同学把题目基本都还原出来了,我做了补充和校对,分享给正在准备大模型岗的你。
面试者画像
经验· 本科在读(27 届),做过带记忆的对话 Agent + RAG 项目
项目背景· 长短期记忆 Agent + 检索增强问答
目标岗位· 大模型应用 · 实习
准备时间· 约 1 个月
本场考点
Agent 长短期记忆:提取 / 压缩 / 冲突更新
记忆条目结构化 · 摘要合并 · 时间戳+置信度仲裁
极端情绪检测旁路 + 不打断对话流的软干预
稠密向量 vs 稀疏向量 · 混合检索 · RRF 融合
长文档切片(Chunking)· chunk_size 怎么定
切片重叠比例与结构感知切分
余弦相似度 vs 欧氏距离 · 归一化与内积
Top-K 过大与 lost in the middle
Rerank(Cross-Encoder)解决向量召回缺交互
增量索引:chunk 粒度 upsert / 删除残留 / 版本一致性
防幻觉:Prompt 边界 + 拒答阈值
asyncio 异步并发 · 限流 / 超时重试
HyDE 假设文档检索及其风险
SGLang RadixAttention vs vLLM PagedAttention
推理提速:量化 / 投机解码 / 前缀复用
第一轮 · 技术面(项目 + 记忆 + 向量基础) · 60 分钟
面试官:简历上这个带记忆的对话 Agent + RAG,先整体讲讲背景、要解决什么问题、最难的点在哪。
我先用三句话给了框架,再展开。
背景:我做的是一个面向企业内部资料(产品手册、合同条款、设备巡检报告)的智能问答 Agent。它要支持多轮对话,还要能记住用户在历史里交代过的偏好和约束。这些资料有两个特点:一是篇幅长、结构杂,二是关键信息常常藏在图、表、扫描件里。
要解决的问题:第一版只做了单轮、纯文本 RAG,上线后暴露两类问题。一类是多轮场景下"失忆":用户上一轮说过"我只看华东区的数据",下一轮换个问法,系统又把全国的混进来。另一类是图表相关的问题答错,因为纯文本管线把图直接跳过了。我这个项目主要啃前一类(记忆),RAG 侧做的是检索质量的持续优化。
最难的点:我归纳成三条。第一,记忆不是越多越好,全量塞进上下文既爆 token 又引噪声,得想清楚"记什么、怎么压、冲突了听谁的"。第二,多轮里的指代和省略(那它呢、还能退吗)要还原成可检索的完整问题。第三,检索质量:召回要准、又不能把无关内容堆给模型。
面试官说"那就顺着记忆往下问,这块我比较关心"。
面试官:你说的"多轮失忆"具体有多严重?怎么发现的?
我抽样了一批脱敏后的多轮会话做人工标注,发现追问类问题(第二轮及以后、依赖上文的)里有相当一部分答得不对或答非所问,根因基本都是丢了上文的约束或指代没还原。为了能量化、能对比,我攒了一个几百条真实多轮对话的小评测集,标好"理想答案",然后固定其他变量,对比"带记忆 + Query 改写"和"纯单轮"两种配置的答案正确率。有了这个评测集,后面每改一处(记忆策略、改写开关、检索参数)都能看到数字涨没涨,而不是凭感觉。这套"先有评测集再优化"的习惯,面试里被问到好几次。
面试官:展开讲讲,记忆的提取、压缩、冲突更新具体怎么设计的?
我把记忆拆成短期和长期两层,三个动作贯穿其中。
提取:不是把整段对话原文都存。每轮结束我用一个轻量抽取 prompt把对话里值得长期记的东西抽成结构化条目,字段是类型 / 内容 / 来源轮次 / 时间戳。类型分几种:用户画像(口味、角色)、硬约束(只看华东、预算上限)、关键事实、待办。抽完再过一道"值不值得存"的判断,把寒暄、一次性的内容丢掉。
压缩:短期记忆放最近几轮原文,保证对话连贯、指代能还原;长期记忆只存上面那些结构化条目,而且定期对同一主题的条目做摘要合并(比如用户三次提到口味偏好,合并成一条最新的画像),避免条目无限膨胀。检索记忆时,我按相关性 + 时近性取一小批拼进上下文,而不是把长期记忆全带上。
冲突更新:这是最容易被追问的。新信息和旧记忆矛盾时(用户改了偏好),我不直接物理删除旧条目,而是按时间戳 + 来源置信度决定(置信度这里指来源强弱:用户显式说的 > 模型从对话里推断的):能明确判定是更新的(用户显式说"改成只看华南"),就把旧条目标记失效、写入新条目;拿不准的(可能是临时需求),就两条都留,检索时优先取较新、置信度较高的那条。这样既不丢历史,也不会拿过期信息答题。
面试官:那"值不值得存"和"冲突时听谁的",这两个判断是规则还是模型?
我用的是规则 + 模型结合。"值不值得存"先用规则粗筛(太短、纯寒暄直接丢),剩下的让小模型打个"长期价值"标签。冲突判定也是:时间戳是硬规则(新的优先),但"是不是真冲突、是不是显式更新"这种语义判断交给模型。纯靠模型成本高、还不稳定;纯靠规则又覆盖不了语义,所以混着来。
面试官:摘要合并会不会把细节合丢了?
会有这个风险,所以我只合并同主题、且明显冗余的条目,合并时保留"最新值 + 关键历史变更",而不是粗暴地把多条揉成一句。对那些低频但重要的硬约束,我单独标记、不参与摘要合并,避免被平均掉。
面试官:检索记忆时,你说按"相关性 + 时近性"取一小批,这两个怎么平衡?注入多少?
我给长期记忆条目也做了向量化,检索时先按相关性召回和当前 query 相关的条目,再用时近性做加权重排(同样相关就优先取更新的)。注入量我卡得很死,通常只拼3 到 5 条最相关的进上下文,外加短期记忆里最近的两三轮原文。原因和后面讲 Top-K 一样:记忆塞太多一样会稀释当前问题、引噪声、还白烧 token。硬约束(只看华东这类)是例外,只要还生效就每轮都带,不参与"取一小批"。
面试官:为什么记忆用向量库存,而不是直接拼进每轮 prompt?
早期我确实是把历史全量拼进 prompt,但多轮一长就两个问题:token 爆(成本和延迟都上去),以及长上下文里关键约束被淹没。改成"结构化条目 + 向量化存储、按需检索注入"之后,每轮只带回真正相关的那几条,token 稳定、相关性也更高。本质上记忆检索和 RAG 是同一套思路:不是把所有东西都给模型,而是每次只给最相关的一小撮。
面试官:那记忆和你 RAG 的知识库,是同一个库吗?会不会混在一起?
不是,我分开存、分开检索。知识库是企业文档,相对静态、全局共享;记忆是这个用户、这段会话的状态,动态、per-user。混在一起有两个大问题:一是隔离,用户 A 的记忆绝不能被召回给用户 B(这是数据安全红线);二是互相干扰,两类内容性质不同,放一个库里检索会彼此污染相关性。所以我让两路独立检索,各取所需,再一起拼进上下文。它们的共性只是"检索范式一样"(都是向量召回),但生命周期、权限范围、更新频率完全不同,工程上必须分开治理。
面试官:多轮里用户经常省略,比如上一轮问某款保险,这一轮直接说"那它过了犹豫期还能退吗"。你怎么把这个"它"补全成可检索的问题?
这是多轮 RAG 绕不开的一关:直接拿"那它还能退吗"去检索,几乎召回不到东西,因为它缺主语、缺上下文。我的做法是在检索之前加一步Query 改写(query rewriting / 指代消解):把当前这句省略、有指代的问法,结合最近几轮对话 + 记忆里的实体,用 LLM 改写成一个自包含、可独立检索的问题,比如把"那它过了犹豫期还能退吗"改写成"XX 重疾险过了犹豫期之后还能退保吗"。改写后的 query 再去走向量化和检索。
面试官:改写错了怎么办?是不是每轮都要改写?
两个点。一是不是每轮都改:先判断这轮是不是真的依赖上文(有没有指代词、是不是接着上一轮的话题),不依赖的(用户起了个全新问题)就跳过改写,省一次调用也避免画蛇添足。二是防改偏:改写时我会约束模型只做指代补全、不许新增事实,并且保留原始 query 一起检索(原句 + 改写句两路召回取并集再 rerank),这样即使改写有偏差,原句那一路还能兜底。改写本身也吃记忆里的硬约束(比如自动带上"华东区"),这正是记忆和检索打通的地方。
面试官:如果检测到用户情绪很极端,Agent 怎么在不中断对话流的前提下干预?
核心是"旁路检测 + 软干预"。情绪识别是一条和主回答并行的旁路,它不卡主链路(主回答该生成还生成),只在后台给这一轮打一个情绪标签。
一旦命中极端情绪(尤其涉及自伤等高危),我不另起一段去打断用户,而是在当前这轮生成时,往 system 侧注入一段干预指令:先共情、稳住情绪、放缓节奏,附上可求助的资源,再回到用户原本的问题。对用户来说,对话是连续的,只是这一轮的语气和内容被悄悄调过。
面试官:旁路怎么保证不阻塞主回答?高危了又怎么分级?
旁路我用异步跑(和主回答的 LLM 调用并发,不互相等);情绪标签即使慢一点回来,也只影响"下一轮"的策略,不卡当前。分级上分三档:轻度负面只是软化语气;中度追加共情前缀;高危则触发更强的固定话术 + 引导专业渠道,并降低这轮的"任务性"(先稳人,再谈事)。高危这档我宁可误报也不漏报,阈值压得低一些。
面试官:误报多了会不会很烦?这块你怎么评估?
会,所以软干预本身就是按"误报代价"设计的:轻中度只是软化语气,用户基本无感,即便误报代价也很小;只有高危才强干预。高危这档我用单独标注的评测集看 precision / recall,偏向高 recall(少漏),再靠人工复核控误伤。思路是:与其追求情绪分类百分百准,不如通过分级把误报的代价降到最低,这样阈值可以放心压低、优先不漏高危。
长短期记忆:提取 → 压缩合并 → 按时间戳/置信度冲突更新;情绪检测走并行旁路、软干预不打断对话
面试官:稠密向量和稀疏向量有什么区别?分别适合什么搜索?
稠密向量是 embedding 模型把文本压成的固定维度实数向量(几百到上千维),每一维没有显式含义,比的是语义,擅长"换个说法也能召回"(同义、近义、意图相近)。稀疏向量是词项维度上的高维稀疏表示(如 TF-IDF、词袋向量),配合 BM25 这类打分,比的是词项命中,擅长专有名词、型号、罕见词、必须精确匹配的关键词。两者互补,所以实际系统常用混合检索:稀疏保证关键词不漏,稠密保证语义能扩。
面试官:混合检索两路的分数量纲不一样,怎么融合?
不能直接把 BM25 分和余弦分相加,量纲不一致。常见两种做法:一是分数归一化后加权求和(权重按评测调);二是RRF(Reciprocal Rank Fusion),不看绝对分、只看每路里的排名,用1/(k+rank)求和融合,对量纲不敏感、实现简单、效果稳,我项目里用的就是 RRF。
面试官:RRF 里那个 k 怎么取?两路要不要加权?
k是个平滑常数(常见取 60 左右),作用是控制高排名项的影响力,它不太敏感,多数时候用默认值就稳。要不要给两路不同权重看场景:关键词主导的场景(型号、编号、代码)可以给稀疏路高一点,语义主导给稠密路高一点。但 RRF 的好处恰恰是对权重不敏感,所以我大多数时候等权 + 默认 k,只有在评测里看到某一路明显更靠谱时才微调权重。
稠密(语义)vs 稀疏(词项)互补;混合检索用 RRF 按排名融合,避开分数量纲不一致
面试官:向量化之前为什么要对长文档切片?不切会怎样?
两个原因。一是 embedding 模型有最大输入长度,超了会被截断、后面内容直接丢。二是一个向量表达不了一整篇长文的细节,整篇压成一个向量等于把不同主题平均掉,检索粒度太粗、命中也定位不到具体段落。不切片的后果就是:要么截断丢信息,要么向量被稀释、召回又粗又不准。
面试官:切片时设重叠区域是干嘛的?比例怎么定?
重叠是为了不把跨边界的语义切断:一句话或一个论点正好被切在两块边界上时,重叠能让它在相邻块里都完整出现,避免检索时上下文断裂。比例我一般按 chunk 大小取10% 到 20%左右,但具体不是拍的:优先在句子 / 段落 / 标题边界切(结构感知切分),再拿评测集看召回,在"重叠太小丢上下文"和"重叠太大冗余、库变大、还会重复召回同一段"之间折中。
面试官:chunk_size 本身怎么定?
也是看任务和评测,不是拍一个数。问答型、信息密集的文档我倾向小一点的 chunk(检索更精准、上下文更聚焦);叙事型、需要长上下文的就大一点。定的方法是:固定其他变量,扫几档 chunk_size,看评测集上的召回率和答案准确率在哪档最优,再结合 embedding 模型的最佳输入长度收敛。
面试官:小 chunk 检索准,但喂给模型时上下文又太碎,这个矛盾怎么破?
我用的是“小块检索、大块喂”(parent-child / small-to-big)。索引时切小块保证检索精准,但每个小块记住它所属的父块(整段 / 整节);命中小块后,实际喂给模型的是它的父块(或前后扩展的窗口)。这样召回的精准度和上下文的完整度兼得:用小块定位,用大块给足上下文。对表格、带标题层级的结构化文档,我还会让 chunk 带上所属标题路径(比如"第 3 章 / 理赔 / 等待期"),既帮检索也帮模型理解这段在讲什么。
切片防截断/稀释;重叠 10-20% 防跨边界语义被切断;优先按句子/段落边界切
面试官:余弦相似度和欧氏距离衡量文本相似,各自优缺点?
余弦看的是两个向量的夹角,对向量模长不敏感,只关心方向。文本 embedding 一般归一化后用余弦(归一化后余弦相似度和点积数值相等),好处是不受文本长短带来的模长差异影响,这也是主流选择。欧氏距离看的是绝对距离,会受模长影响,在没归一化时两个语义相近但模长差很多的向量距离可能偏大。所以衡量文本语义相似,余弦更稳;欧氏更适合那些"绝对位置 / 尺度有意义"的向量空间。
面试官:既然归一化后余弦等于点积,工程上一般怎么存怎么算?
工程上常先把向量归一化入库,检索时直接算内积(等价于余弦但更快),很多向量库的索引(如基于内积的 HNSW)也按这个来。这样既拿到余弦的"只看方向"的好处,又省掉每次现算模长的开销。
余弦看方向(文本主流)、欧氏看距离;归一化后余弦=内积,工程上归一化入库算内积更快
面试官:回到 embedding 本身,选一个 embedding 模型你会看哪些因素?
我大致看五点。一是语种和领域匹配:中文场景就选中文或多语模型,垂直领域(法律、医疗、内部黑话)要评估要不要微调。二是检索效果:可以参考 MTEB 这类榜单的检索任务得分,但榜单不等于你的数据,一定要在自己的评测集上实测召回。三是维度与性能的权衡:维度高表达力可能强,但存储和检索成本也高,要按库规模和延迟预算选。四是最大输入长度,要和 chunk_size 对得上。五是检索范式:有些模型是非对称检索(query 和 doc 用不同的指令 / 前缀),用错会掉点。
面试官:什么时候该自己微调 embedding?
当通用模型在你的垂直领域召回明显不行时(专业术语、缩写、内部说法,通用模型分不开),才考虑用领域内的 query-doc 对做对比学习微调。前提是:有足够的标注对(或能从点击 / 反馈里挖正负样本),而且评测确认通用模型确实不够。不要一上来就微调,通用模型 + 好的切片和 rerank 往往已经够用,微调是最后再上的手段,成本和维护都更高。
面试官:向量库底层怎么把检索做快的?为什么常说"近似"最近邻?
库一大,精确地算 query 和每个向量的相似度(暴力扫)就太慢了。所以工程上用ANN(近似最近邻)索引,用可控的一点点召回损失换数量级的提速。主流两类:图索引 HNSW(建多层近邻图,检索时从上层粗定位、逐层贪心跳到最近邻,召回高、查询快,但内存占用大)和倒排 + 量化 IVF-PQ(先聚类分桶、再用乘积量化压缩向量,省内存、适合超大规模库,精度略低)。选型看库规模、延迟、内存预算:中小库 HNSW 香,亿级以上常用 IVF-PQ 系。说"近似"是因为精确 KNN 在亿级向量上不现实,ANN 用可接受的召回损失换上线可行的速度。
面试官:召回率和速度之间怎么调?
靠索引参数,而且还是扫评测来定。HNSW 主要调ef(检索时候选集大小)和M(每个点的连接数),调大召回更高但更慢、更占内存;IVF 调nprobe(查多少个桶),调大召回高但慢。我的做法一样:在自己的评测集上扫这些参数,看Recall 和延迟的曲线,挑一个满足延迟预算的最高召回点。
聊到这儿一面差不多两个小时,面试官说"基础和项目都聊得挺细,下一轮继续往工程和优化上问",让我先休息十分钟。
关联面试题(网站题库已收录,可搜题看「文字解析 + 口语讲法」):www.wsxdmx.com
第二轮 · 技术面(RAG 进阶 + 工程) · 60 分钟
面试官:向量库检索出的 Top-K,如果 K 设得过大,对后续生成有哪些负面影响?
K 越大,召回里混进来的不相关 chunk越多。坏处有几条:无关内容稀释了真正有用的证据,模型容易被带跑、反而更爱幻觉;上下文被撑长,触发"lost in the middle"(中间位置的关键内容被忽略);还有延迟和 token 成本都上去了。所以 K 不是越大越好,通常先用较大的召回 K拉一批候选,再靠rerank精排,取较小的前几条喂给模型。
面试官:那进上下文的"前几条"到底怎么定?还有别的省 token、降噪的招吗?
进上下文的条数我也用评测扫:从 3 条往上加,看答案准确率到哪儿不再涨(很多场景 3 到 5 条就饱和了),再往上只增噪声和成本。除了"取小 K",还有几招:一是按 rerank 分数设阈值,分太低的即便排在前几也不要(宁缺毋滥);二是上下文压缩(contextual compression),对选中的 chunk 再抽出和 query 相关的句子、把无关句删掉,既省 token 又降噪;三是去重,把召回里语义重复的 chunk 合并。核心还是那句话:给模型的不是越多越好,是越相关越好。
面试官:初筛召回之后,为什么还要加一个 Rerank?它解决了向量搜索的哪些局限?
初筛一般是双塔 / 向量检索:query 和 doc各自独立编码再比相似度,快、能扛大库,但 query 和 doc 之间没有交互,容易出现"语义相近但其实答非所问"。Rerank用的是cross-encoder:把 query 和候选 doc拼在一起送进模型,让它们充分交互,打出更准确的相关性分。它解决的就是向量召回粗、缺交互的局限。代价是慢(每个候选都要过一次模型),所以只对初筛的Top-N做精排,而不是全库跑。补一句:双塔和 cross-encoder 之间还有ColBERT这类"后期交互"的折中,精度比普通双塔高、又比 cross-encoder 快;但要论最准,cross-encoder 仍是最强的精排方案。
其实 Rerank 为什么必要这道,我在站里刷到过、也看了它的口语版怎么讲,所以现场能比较利索地说清它补的是双塔向量"缺交互"那块,没卡壳。认真把口语讲法过一遍,临场是真的更敢开口。
面试官:精排取多少条、reranker 怎么选?
召回我一般拉Top-50 到 Top-100,精排后取Top-3 到 Top-5进上下文(配合前面说的"K 别太大")。reranker 选型看延迟预算和效果:轻量的用开源 bge-reranker 这类小 cross-encoder,延迟敏感就上更小的或做蒸馏;离线评测用Recall@k 之外再看 MRR / NDCG,确认精排确实把对的排上来了。
面试官:rerank 慢,线上扛得住吗?要是想省掉它呢?
扛不扛得住,取决于精排候选数和模型大小。控制手段:召回 K 别开太大(精排只跑 Top-50 到 100)、用小 cross-encoder 或蒸馏版、必要时上GPU batch 推理把候选并起来算。能不能省掉 rerank?当库不大、召回本身已经很准时可以省;但多数 RAG 加上 rerank 后端到端提升很明显,这点延迟通常值得。判断"值不值"还是回到评测:对比加 / 不加 rerank 的MRR、NDCG 和最终答案准确率,用数字决定,而不是凭感觉留或砍。
RAG 检索栈:混合召回 → Rerank 精排 → 边界 Prompt 防幻觉;K 不宜过大,HyDE 处理模糊提问
面试官:文档发生了局部更新,如何通过增量索引避免全量重新向量化?
按chunk 粒度做增量。每个 chunk 带doc_id和chunk_id,文档更新时只diff 出变化的 chunk:新增 / 改动的重新 embedding 后upsert进向量库,删掉的按 id 从库里删除,没变的原封不动。这样只动变更部分,不用全量重建。
面试官:这里最容易踩的坑是什么?
两个。一是删除残留:旧 chunk 必须真删干净,否则会召回到过期内容,我会用doc_id做整篇的"先删后插"来兜底。二是版本一致性:更新过程要么有短暂的新旧并存,要么加版本号 / 时间戳,检索时优先取最新版,避免一篇文档的新旧 chunk 同时被召回、自相矛盾。向量库选型上,要选支持按 id upsert / delete的(主流的都支持),否则增量很难做。
面试官:那如果是 embedding 模型本身升级 / 换了呢?
那增量就救不了了,必须全量重灌。因为换了 embedding 模型,向量空间变了,新旧向量不可比,老向量整体失效。所以换 embedding 是个大动作:离线把全部 chunk 重新 embedding、灌进新索引、再灰度切流量。这也是为什么前面说embedding 选型要慎重、别频繁换:文档增量更新是高频小操作,换模型是低频大工程,两者代价完全不是一个量级。
面试官:在 RAG 的生成阶段,如何在 Prompt 里设边界,防止模型在没搜到内容时产生幻觉?
在 system 里把边界写死:只能依据"提供的资料"回答,资料里没有就明确说"没有找到相关信息"、不要凭自己的知识补;每个关键结论尽量标出处;拿不准就拒答而不是猜。再配合检索侧的相关性阈值:召回分都太低就直接走拒答,不进生成。这是 prompt 约束 + 检索阈值的双保险。
面试官:拒答阈值怎么定?定高了会不会该答的也不答?
会,所以阈值要用评测集校:准备一批"库里有答案"和"库里没答案"的问题,扫阈值看拒答的准确率和漏答率,找一个平衡点。我一般偏保守一点(宁可多拒一点也别瞎编),因为这类企业问答编错的代价远高于"没答上"。
面试官:你说"每个结论标出处",具体怎么让模型给引用、又怎么保证引用是真的?
做法是给检索到的每个 chunk编号([1] [2]),在 prompt 里要求每个关键结论后面标上来源编号,生成后再后处理校验:引用的编号是不是真的存在、对应的 chunk 是不是真的支持这句话。更严的做法是逐句对齐到证据,把没有任何证据支撑的句子标灰或删掉。引用不只是体验问题,它本身就是反幻觉的抓手:逼模型只说"有据可查"的话,而不是自由发挥。
面试官:你前面反复说"拿评测集看",那 RAG 到底怎么评?检索和生成分别看什么指标?
我分两层评。**检索层(retriever)**看:Recall@k(top-k 召回里有没有覆盖到真正含答案的 chunk,这是 RAG 的天花板,召回没覆盖到后面再强也白搭)、MRR / NDCG(正确的 chunk 排得够不够靠前,这俩也正好用来判断 rerank 到底有没有把对的排上来)。生成层(端到端)看:答案正确性(对照标准答案)、忠实度 / faithfulness(答案是不是都有检索到的证据支撑、有没有脱离资料的幻觉)、以及该拒答时有没有拒答。
面试官:评测集从哪来?没有真实标注数据怎么办?
最好是从脱敏后的真实问题里抽样,人工标注"理想答案 + 对应的出处 chunk"。真实数据不够时,我用反向生成:让 LLM 基于文档反推出"问题 + 答案 + 出处"三元组,造一批合成评测集,再人工抽检保证质量。核心观点我也跟面试官说了:没有评估集的优化都是自嗨,所以这套评测我是在动手优化之前就先搭起来的。
面试官:你提到用 LLM 当裁判,它可靠吗?
LLM-as-judge 省人力,但有偏:位置偏好(偏向靠前的)、长度偏好(偏向啰嗦的)、还可能和被评模型同源带来的偏袒。所以我会固定打分 rubric(把"忠实度、正确性、相关性"拆成明确标准)、控制长度等混淆变量、并抽样和人工标注对齐校准。它适合做大批量初筛和相对比较,但关键结论我还是会人工复核一遍。
面试官:调用大模型 API 时,为什么要用 asyncio 异步编程?处理高并发请求时有什么优势?
调 API 是典型的IO 密集:大部分时间在等网络返回。同步写法是发一个等一个,CPU 干等着浪费。asyncio用单线程事件循环,把多个请求的"等待"重叠起来:一个请求在等返回时就去发下一个,回来了再接着处理。高并发下它不用为每个请求开线程(省去线程切换和内存开销,也绕开 GIL 对 IO 等待的影响),同样的机器能扛多得多的并发、吞吐更高。
面试官:那和多线程比呢?还要注意什么?
对纯 IO,asyncio 比多线程更省(没有线程栈和切换开销);CPU 密集才轮到多进程。要注意的坑:别在协程里调同步阻塞的库(会卡住整个事件循环,得用异步客户端或丢到线程池);还要做并发上限 / 限流(信号量控制同时在飞的请求数,别把对方 API 打挂)和超时 + 重试(指数退避),否则高并发下很容易雪崩。我当时大致是这么写的:
⌨ python
import asyncio
sem = asyncio.Semaphore(20) # 并发上限:同时在飞的请求数,别打挂对方 API
async def call_one(client, prompt):
async with sem:
for attempt in range(3): # 超时 + 指数退避重试
try:
return await asyncio.wait_for(client.chat(prompt), timeout=30)
except Exception:
await asyncio.sleep(2 ** attempt)
return None # 重试耗尽,降级处理
async def call_many(client, prompts):
return await asyncio.gather(*(call_one(client, p) for p in prompts))
要点就三个:Semaphore压并发上限、wait_for给超时、gather把一批请求并发跑完。真正要小心的是别在协程里混进同步阻塞调用(比如某些只有同步版的 SDK 或重 CPU 计算),那会把整个事件循环卡死,得换异步客户端或丢进线程池。
面试官:了解 HyDE 吗?介绍一下原理,它在处理模糊提问时有哪些优势?
HyDE是 Hypothetical Document Embeddings。思路是:用户 query 往往很短很模糊,直接拿它的 embedding 去检索,语义不够丰富。HyDE 先让LLM 针对 query 生成一段"假设文档(hypothetical document)"(哪怕内容不一定全对),再用这段假设文档的 embedding去检索。因为假设文档在语义和篇幅上更接近真正的目标文档,所以对模糊、口语化的提问,召回会明显更准。
面试官:假设文档要是编错了,会不会把检索带偏?
会有风险,所以 HyDE 更适合**"问得模糊但领域明确"的场景。几个缓解:让假设文档只生成要点 / 关键词级而不是长篇大论,降低编造空间;和原始 query 的检索结果做融合(两路召回取并集再 rerank),不完全依赖假设文档;以及对事实性极强**的问法不开 HyDE。它的代价也要讲清楚:多一次 LLM 调用,延迟和成本会上去。
面试官:HyDE 和你前面说的 Query 改写、还有 multi-query,是一回事吗?
都属于"在检索前先加工 query"这一类,但解决的问题不同。Query 改写(指代 / 省略还原)是把多轮里残缺的问法补成自包含的,治的是"问得不完整";multi-query是把一个 query扩成多个不同角度的子问题各自检索再合并,治的是"单一表述召回不全";HyDE是生成假设文档、拿它的 embedding 去检索,治的是"query 太短、语义不足"。三者可以叠加用,但每多一步都多一次调用、多一截延迟,所以我按场景按需上:多轮场景必开改写,模糊提问才考虑 HyDE 或 multi-query。叠加时一般先做改写(把 query 补全成自包含的),再视情况上 HyDE 或 multi-query(二选一),别层层生成把噪声越放越大。
面试官:你了解哪些推理框架?SGLang 相比 vLLM 的 PagedAttention,在推理延迟上有什么优势?
主流的有vLLM、SGLang、TensorRT-LLM等。vLLM的核心是PagedAttention:把KV cache像操作系统内存分页一样管理,显存碎片少、利用率高,从而支持更大的 batch、更高吞吐。SGLang的一个关键特性是RadixAttention:它用基数树(radix tree)把不同请求共享的前缀对应的 KV cache缓存复用起来。
所以当请求之间有大量公共前缀时(同一套 few-shot、长 system prompt、多轮对话、结构化编排),SGLang不用为每个请求重算这部分前缀,首 token 延迟和整体延迟都更低;它前端还有结构化生成 / 编排的 DSL。一句话:vLLM 强在显存与吞吐,SGLang 在"前缀大量复用"的场景下延迟更优。要补一句:vLLM 现在也支持自动前缀缓存(APC),所以"前缀复用"已不是 SGLang 独有,只是 RadixAttention 的复用更细粒度、在结构化生成和大量共享前缀的场景下通常更激进,两者也在持续互相吸收对方的优化。
面试官:如果还想再压延迟和显存,你会怎么做?
正交的几招:量化(把权重 / KV cache 降到 INT8 / FP8,省显存又快);KV cache 量化或复用;投机解码(speculative decoding,用小模型先猜、大模型批量校验);以及把能复用的前缀固定下来好让 RadixAttention 命中。具体上不上,看延迟预算和精度能接受多少损失,得拿评测压一遍。
面试官:在线服务到底该盯吞吐还是延迟?
看场景。面向用户的交互式服务(对话)首要看延迟,尤其是TTFT(首 token 延迟)和每 token 的TPOT,用户等不了;离线批处理(批量生成、灌库、跑评测)首要看吞吐和成本。麻烦的是同一框架里这俩常此消彼长:加大 batch 吞吐上去了,但单个请求的延迟也变高。所以要按SLA定 batch 和调度策略。我们是对话场景、延迟优先,这也是为什么 SGLang 的前缀复用对我们价值大:把长 system prompt 和多轮前缀的重复计算省掉,首 token 更快。
vLLM PagedAttention 强在显存/吞吐;SGLang RadixAttention 复用共享前缀,前缀多则延迟更优
聊到这儿两轮技术面就结束了,面试官说"问得差不多了,你回答得挺有条理",让我等后续通知。
复盘要点
记忆机制别只说存哪里:要讲清提取(存什么)、压缩(怎么不爆 token)、冲突更新(新旧矛盾听谁的)三层,并能说出「规则+模型」各管哪段。
RAG 每个环节都要能答「为什么这么做、解决什么局限」:Rerank 补向量召回缺交互、重叠防语义切断、拒答阈值防幻觉,都是成对的「问题→对策」。
参数类问题(chunk_size、重叠比例、Top-K、拒答阈值)答「怎么定」比答「设多少」更值钱:讲清「扫几档 + 看评测」的方法论,别拍数字。
混合检索别忘了「分数量纲不一致」,能说出 RRF 按排名融合,是个加分细节。
工程深度看追问:asyncio 别在协程里调同步阻塞库、要限流/超时重试;SGLang 的 RadixAttention 前缀复用是延迟优势的关键词。
被追问时顺着「问题→机制→代价」讲,主动补上每个方案的代价(HyDE 多一次调用、Rerank 慢),比只讲优点更显成熟。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
