全栈开发知识库

17 · 面试冲刺:盲区补全与高频追问实战

本篇是对前 16 篇做"面试官视角"的全面扫描后,补上的最后一批盲区:LLM 底层原理、RAG 进阶追问、工程配套(测试/会话/长任务)、前沿动态、系统设计题,以及一份"面试官连环追问"实战模拟。前 16 篇是你的武器库,这一篇教你应对武器库之外的冷枪。

预计 2~3 天。建议面试前 3 天精读,第 8 节的追问链至少自问自答两遍。

1. LLM 底层原理速通(应用岗也逃不掉的三连问)

面试官不要求你会训练模型,但"简述一下大模型是怎么生成文字的"是应用岗的送分/送命题。

1.1 自回归生成:LLM 是一个"接龙机器"

大模型生成文字的本质:给定前面所有字,预测下一个字的概率分布,选一个,拼上去,再预测下一个——循环往复,这叫自回归(Autoregressive)生成。三个直接推论,全是面试考点:

  • 为什么输出是一个字一个字蹦的? 因为它真的就是一个字一个字生成的——这也是流式输出(SSE)天然合理的原因
  • 为什么会有幻觉? 模型的目标是"生成概率上通顺的下一个字",不是"陈述事实"。当训练数据里没有答案时,它依然会选一个"看起来通顺"的字接下去——幻觉不是 bug,是这个机制的固有属性,所以工程上要用 RAG(给它资料)和工具(给它事实)来约束
  • 为什么长输出越来越慢、越贵? 每生成一个字都要带着前面全部内容算一遍注意力,上下文越长每步越贵(KV Cache 就是为了缓解重复计算,见第 10 篇)

1.2 Transformer 与注意力:一句话版本

Transformer 是这类模型的网络结构,核心是注意力机制(Attention):生成每个字时,模型会"回头看"前文所有字,并给每个字分配不同的关注权重——"他把苹果给了小明,小明很开心"里,生成"开心"的主语时,注意力会重点投向"小明"。面试答到这个深度,应用岗就够了;如果面试官继续追 Q/K/V 矩阵,坦诚说"应用层我很熟,底层结构了解到注意力权重这一层"远好于背错公式。

1.3 Tokenizer 与采样参数(每天都在用,必须会讲)

Tokenizer(分词器):模型不认识"字",只认识 token——把文本切成的子词单元。最常用的切法叫 BPE(字节对编码):反复把语料里最常一起出现的字符对合并成一个单元,常见词成为一个 token,生僻词被拆成几段。英文约 1 token ≈ 0.75 个单词,中文约 1 token ≈ 0.6~0.7 个汉字。所以:计费按 token、上下文窗口按 token、"9.11 和 9.9 谁大"翻车也和数字被切成奇怪的 token 有关。

采样参数(你在手敲项目里只用了 temperature,面试会问全家桶):

参数 作用 实战取值
temperature 概率分布"抹平"程度:0=永远选最大概率(稳定),越高越随机(有创造性) 判断/结构化 0~0.2,对话 0.5~0.7,创作 0.8+
top_p(核采样) 只从累计概率前 p 的候选里选,砍掉长尾胡话 默认 0.9~1,一般只调 temperature 和 top_p 之一
top_k 只从概率最高的 k 个候选里选 多数 API 不暴露,知道即可
frequency_penalty 惩罚重复用词 模型复读机时调大

面试金句:"temperature=0 也不保证完全确定性输出(浮点并行计算有非确定性),所以可复现性要靠评估集跑分布,不能靠固定种子。"

2. WebSocket vs SSE + 断线重连(项目实际开发必被追问)

你的手敲项目全用 SSE,面试官必追:"为什么不用 WebSocket?断线了怎么办?"

SSE(你用的) WebSocket
方向 只能服务器→客户端 双向
协议 就是普通 HTTP,Nginx/网关/防火墙天然友好 独立协议,需要 Upgrade 握手,代理配置麻烦
客户端实现 EventSource 或 fetch 流,极简 要处理连接状态机
适用 LLM 流式输出、进度推送(单向为主的场景) 在线协作、游戏、语音(双向高频)

标准答案:"聊天场景里用户→服务器走普通 POST 就够了,只有服务器→用户需要流式,所以 SSE 是更简单的正确选择;做实时语音对话时才需要 WebSocket。"

断线重连怎么做(高频追问,分三层答):

  1. 协议层:SSE 原生支持重连——每个事件带 id:,浏览器的 EventSource(浏览器内置的 SSE 客户端对象,一行 new EventSource(url) 就能订阅事件流)断线后自动重连,并带上 Last-Event-ID 请求头告诉服务端"我收到哪了",服务端据此续推
  2. 业务层:长任务(如深度研究)不要把状态只放在推送流里——任务开始时返回 task_id,进度落到 Redis/数据库,断线后前端用 task_id 轮询或重新订阅,推送流只是加速通道,不是事实源
  3. 兜底:推送流彻底挂了,降级为轮询 GET /task/{id}/status——这也是第 13 篇进阶练习的正确演化方向

3. RAG 进阶追问(第 10 篇讲了主线,这里是面试官的下一层)

追问 1:怎么分块(chunking)? 不是无脑按 500 字切。答:按语义边界优先(标题/段落/代码块),块间做 10~20% 重叠防止句子被腰斩;结构化文档(合同/手册)按章节切并把标题路径拼进每个块(第三章>违约责任>...),检索命中率显著提升。

追问 2:向量检索有时搜不到明明存在的内容,怎么办? 标准答案是混合检索(Hybrid Search):向量检索(语义相似)+ BM25(经典关键词算法,精确匹配型号/人名/错误码这类"字面必须对上"的查询)双路召回,再用 RRF(倒数排名融合)合并——把每条结果在两路里的名次取倒数后相加,总分高者排前,简单却有效。只用向量,"错误码 E4032"这种查询就是搜不准。

追问 3:百万级向量怎么做到毫秒检索? 近似最近邻(ANN)索引,主流是 HNSW(分层可导航小世界图):牺牲一点点召回率换几个数量级的速度,Qdrant/Milvus/pgvector 底层都是它。知道名字和"用空间换时间的图索引"这一句就够了。

追问 4:用户的问题和文档的说法对不上怎么办? 查询改写(Query Rewriting):检索前先让 LLM 把口语问题改写成"适合检索的样子"(补全指代、拆多跳问题、生成多个改写版本分别检索)。进阶变体 HyDE:让模型先"虚构一个理想答案",用这个假答案去做向量检索,往往比用问题本身更准。

追问 5:RAG 效果怎么量化? 分两层各自评:检索层用召回率 Recall@K(正确文档是否出现在前 K 条里)和 MRR(正确文档排得越靠前得分越高);生成层用忠实度(答案是否只基于检索内容,防幻觉)和答案相关性——开源框架 RAGAS 把这套指标打包好了。面试金句:"RAG 调优先看检索层指标,检索都没召回,调提示词是白费。"

追问 6:听说过 GraphRAG 吗? 一句话版:把文档抽取成知识图谱(实体+关系),回答"跨文档多跳"问题(如"A 公司投资的公司里谁和 B 有合作")比纯向量检索强,代价是构建成本高。知道适用场景即可。

4. 给 Agent 写测试(工程素养分水岭,几乎没人准备)

面试官:"LLM 输出不确定,你的 Agent 怎么写单元测试?"——答上来的候选人不到两成。

核心思想:把确定性逻辑和 LLM 隔离,LLM 用桩(mock)替换。你手敲项目的架构天然可测——所有 LLM 调用都走 llm.py 的 chat():

先认识 pytest:Python 最流行的测试框架——pip install pytest 后,在项目目录运行 pytest 命令,它会自动找到所有 test_ 开头的文件和函数并执行,assert 断言失败即测试失败。

# test_agent.py —— 给第11篇的MiniAgent写测试(放在项目根目录, 终端运行: pytest)
import agent as agent_mod
from agent import MiniAgent
from memory import Memory


def fake_chat(messages, temperature=0.3):
    """桩LLM: 固定剧本,让测试可重复"""
    if messages[-1]["content"].startswith("[工具"):
        return "根据工具结果,现在是2026年"
    return "[TOOL_CALL:get_time:]"


def test_tool_call_loop(monkeypatch, tmp_path):
    monkeypatch.setattr(agent_mod, "chat", fake_chat)     # 替换真实LLM
    events = []
    a = MiniAgent(Memory(path=str(tmp_path / "m.jsonl")))
    reply = a.run("s1", "现在几点了?", on_event=events.append)
    assert [e["type"] for e in events] == ["tool_call", "tool_result", "final"]
    assert "2026" in reply

代码里函数签名上的 monkeypatch 和 tmp_path 是 pytest 自带的"魔法参数"(术语叫 fixture),写上名字就能用:monkeypatch.setattr 临时替换某个对象、测试结束自动还原;tmp_path 是自动创建的一次性临时目录,测试完自动清理。

分层测试策略(面试答题框架):

  1. 单元测试(不花钱、毫秒级):mock 掉 LLM,测确定性逻辑——工具解析正则、参数校验、记忆读写、自修复循环的重试路径。放进 CI 跑(CI = 持续集成:代码一提交,服务器就自动跑全部测试的流水线,比如 GitHub Actions;红了不许合并)
  2. 集成测试(花小钱):少量用例调真实 LLM,只断言结构(能解析、字段齐)不断言字面内容
  3. 评估集(第 10/16 篇):30~100 条典型输入,LLM-as-a-Judge 打分,发版前跑——这层测"质量",上面两层测"不崩"

顺带一提:本教程手敲项目的全部代码,就是用这套"mock LLM + 真实执行其余链路"的方法验证过的——你可以在面试中把这套方法论讲成自己的工程实践。

5. 会话状态与水平扩容(从单机到多实例的必考题)

面试官:"你的 Agent 服务要扩到 3 个实例,会遇到什么问题?"

你手敲项目的会话记忆存在进程内存里(第 11 篇的 defaultdict)——单机没问题,一扩容就翻车:用户第一句话打到实例 A,第二句被负载均衡到实例 B,B 的内存里没有他的对话历史。

解法阶梯(按推荐顺序答):

  1. 状态外置(正解):会话历史搬进 Redis(第 10 篇提过:常驻内存的键值数据库,读写微秒级,是多实例共享数据的标配)——键 session:{id},值为消息列表,设 TTL(存活时间,到期自动删除,防止废弃会话堆积),任何实例处理任何请求都先从 Redis 取状态——服务本身变成无状态,可以随意扩缩容、滚动重启不丢会话
  2. 粘性会话(能用但别吹):负载均衡按 session_id 做哈希(把任意字符串换算成一个固定数字,同样输入永远得到同样结果)再对实例数取余,同一会话就永远落到同一台实例——实现简单,但实例挂了它负责的会话全丢,扩容时哈希重分布,只适合过渡
  3. 长期记忆(第 11 篇的 jsonl)同理搬进数据库/向量库

连带追问:长任务怎么管理和取消? 研究任务跑 3 分钟,用户关了页面/点了取消,后台还在烧钱。答:① 任务创建即返回 task_id,状态机(pending/running/done/cancelled)落 Redis ② Agent 循环里每步开始前检查取消标记(if redis.get(f"cancel:{task_id}"): break),LLM 请求本身设超时 ③ 前端断连不等于取消,取消是显式接口 POST /task/{id}/cancel。这也是第 13 篇 run_research 循环的自然改造点。

连带追问:多租户怎么隔离? 一套服务给多家企业客户用:数据隔离(所有存储键/表带 tenant_id,检索加过滤,向量库也要按租户过滤——最容易漏)、配额隔离(限流和 token 预算按租户计)、配置隔离(每租户可有自己的提示词/知识库/模型路由)。

连带追问:提示词上线出问题怎么回滚? 提示词进 Git + 配置中心(统一存放线上配置、修改后立即生效不用重新发版的服务,常见如 Nacos/Apollo),带版本号;发布走灰度(5% 流量新版本,评估指标不跌再放量);出问题一键切回旧版本号——把提示词当代码运维(第 16 篇坑 15 的运维版)。

6. 前沿动态(2026 面试的"你还在学习吗"探测题)

  • 推理模型(Reasoning Models):o 系列/DeepSeek-R1 这类模型在回答前先生成长思维链,数学/代码/规划能力大幅提升。对 Agent 开发的影响(面试重点):以前靠工程脚手架做的事(ReAct 提示词、显式规划),模型内化了一部分——简单任务直接问推理模型可能优于复杂多 Agent 编排;但推理 token 又慢又贵,所以模型路由(简单问题给对话模型、难题给推理模型)成了新的必修课
  • 多模态 Agent:模型直接看图(截图/病历/报表)、听语音。典型架构:语音对话 = ASR(语音转文字)→ LLM → TTS(文字转语音)三段式,或用端到端实时语音模型降低延迟;看懂"多模态输入会显著增加 token 消耗"这类工程账即可
  • Computer Use / Browser Agent:模型看屏幕截图、输出点击坐标来直接操作电脑/浏览器——工具调用的终极形态(不再需要为每个网站写 API 工具),目前的短板是慢、贵、易被页面变化打断
  • Agent 互操作协议:MCP(第 03 篇,模型连工具的标准)已成事实标准;A2A 等 Agent 之间互相通信的协议在竞争中,一句话立场:"MCP 解决 Agent 用工具,A2A 类协议想解决 Agent 之间互相调用,后者格局未定"
  • 记忆系统产品化:MemGPT(把上下文当操作系统内存分页管理)、mem0 等专门的记忆中间件——你手敲的"短期+长期+检索拼装"(第 11/14 篇)正是它们的简化原型,面试时可以这样挂钩

7. 系统设计题实战:"设计一个智能客服 Agent 系统"

高级岗必考白板题。给一个可复用的六步答题框架,以电商客服为例:

  1. 先问清需求边界(直接开画架构图是扣分项):日活多少?要接哪些系统(订单/物流/退款)?哪些操作允许 Agent 自主执行、哪些必须人工?——展示"工程师先收敛问题"的素养
  2. 主链路:用户消息 → 网关(鉴权/限流,第 10 篇)→ 会话服务(Redis 取历史,第 5 节)→ 意图分流:简单 FAQ 走 RAG 直答(便宜),订单操作走工具 Agent,情绪激动/连续失败转人工
  3. 知识与工具:FAQ/政策文档进 RAG(混合检索,第 3 节);订单查询/退款是工具,写操作(退款)必须二次确认 + 金额上限 + 审计日志(第 16 篇坑 13)
  4. 可靠性:LLM 超时重试与降级话术、工具幂等(退款绝不能重复执行,第 10 篇幂等)、转人工兜底——"Agent 答不了"是正常路径不是异常
  5. 可观测与评估:每轮对话记录 trace(第 10 篇),线上指标 = 解决率/转人工率/用户评分,bad case 回流评估集(第 16 篇坑 16)
  6. 成本:意图分流用小模型,FAQ 直答缓存,只有复杂工单用旗舰模型——报一个"每千次会话成本"的估算思路,面试官眼睛会亮

8. 模拟实战:面试官的连环追问链(背熟)

真实面试不是背题,是顺着你的项目往下钻。以你的手敲项目为例,一条典型的十连问,每问附答题要点(出处都在本教程里):

  1. "介绍下你的 Agent 项目" → 一分钟版本(第 11/12 篇的"面试怎么讲")
  2. "工具调用怎么实现的?" → 文本协议 + 正则解析 + 回喂循环;主动对比原生 Function Calling 的优劣(第 10 篇)
  3. "模型输出格式错了怎么办?" → 提取 JSON → 解析重试 → Pydantic 校验 → 报错回喂自修复(第 12 篇)
  4. "为什么这里不用 LangChain?" → "先手写理解原理,生产我会评估 LangGraph;手写让我知道框架在帮我做什么,出问题能下钻"——这是加分答案,别答"框架不好"
  5. "响应太慢用户投诉,怎么优化?" → 并行(12 篇)→ SSE 流式(11 篇)→ 缓存(10 篇)→ 模型路由(本篇 6 节)
  6. "怎么保证线上质量?" → 分层测试(本篇 4 节)+ 评估集 + LLM-as-a-Judge + bad case 回流(16 篇)
  7. "扩到多实例会怎样?" → 会话状态外置 Redis(本篇 5 节)
  8. "用户输入'忽略你的指令'怎么办?" → 提示词注入五道防线(10 篇)+ 外部内容定界(16 篇坑 13)
  9. "什么时候会考虑微调?" → 成本阶梯:提示词→RAG→微调;格式/风格类任务用日志蒸馏数据做 QLoRA(15 篇)
  10. "上线后模型厂商 API 挂了怎么办?" → 超时+重试+多供应商 fallback(llm.py 收口的好处:换 base_url 即切换)+ 降级话术;私有化部署做兜底(15 篇)

准备方法:对着这条链自问自答,每题 1~2 分钟说出声。能顺畅走完全链,Agent 岗的技术面就没有你接不住的球。

9. 诚实的边界:这套教程之外你还需要单独准备的

面试不只考 Agent,以下三块本教程不覆盖,按岗位要求自行补:

  1. 算法题:多数公司仍考 LeetCode(力扣,最大的刷题网站,面试算法题基本出自这里)的简单/中等题(数组、哈希、双指针、二叉树)。刷题量参考:热题 100 里刷 50 题
  2. 计算机基础八股:进程 vs 线程(GIL 讲过,06 篇)、TCP/HTTP(04 篇有应用层)、操作系统内存管理——社招大厂会问,背八股文资料即可
  3. 数据库:手写基本 SQL(JOIN/GROUP BY)、索引原理(B+ 树一句话)、Redis 数据结构——Agent 岗位周边必问

自查:如果 JD 写着"扎实的计算机基础",这三块至少准备两周;如果 JD 全在说 LLM/Agent/RAG,本教程 + 本篇就是主战场。

返回目录:README

本页由 Markdown 自动渲染 · 共 628 篇笔记