2026-08-30 秋招XX AI全栈开发一面复盘
XX AI 全栈工程师(上海)的一面,形式是牛客 AI 面试。8月30日面完后记下了这些,结果没有留下记录。
整体流程是自我介绍、八股、Agent、项目追问,少了一个手撕环节。我当时觉得它挺公式化,但转念一想,真人面试官的追问就不公式了吗?
题目与我的作答
1. 前端与 Redis 八股
八股选了 TypeScript 方向,记得四道:
- JavaScript 在浏览器里的事件是怎么执行的?
- TypeScript 泛型有什么作用?
- 缓存穿透、缓存击穿、缓存雪崩有什么区别?
- Redis 的两种持久化机制有什么区别?当时记成一个 A 开头、一个 R 开头。
这部分留下了题目,没有留下我逐题回答的原话。下面把它们按复习时能讲清楚的程度补上。
2. Function Calling → 私有 API → Agent 设计模式
后面这组是递进追问,不是几个互不相关的八股。
先问 Function Calling。我当时说,本地内置了 Python 解释器,把 Tools 写好、规约好,模型自己调用工具去跑。但我又提到,本地环境不能覆盖所有场景,还会涉及一些 API。
于是它继续问:私有 API、网络服务这种工具,具体怎么实现?我回答了 MCP。
再往后问 Agent 有哪些设计模式。我当时只知道 ReAct,不清楚还有哪些更通用的模式。这两块是我自己觉得答得不好的地方。
3. 最自豪的 AI 项目,以及和 AI 迭代中发现的问题
Agent 部分结束后,开始问项目:
- 讲一个自己最自豪、最想讲的 AI / Vibe Coding 项目,解决了什么问题,怎么做的。
- 有没有一开始没发现,后来和 AI 反复迭代才发现的问题?最后怎么定位、怎么解决?
- 再围绕这个问题继续追问一些细节。
我记住了这条追问线,但没有留下具体讲的是哪个项目、举了哪个问题,后面几次追问也记不清了。这一段先保留到这里。
复盘与重做
以下是事后整理的知识解释和重答思路,不是把它们补写成我当时已经答出来的内容。
1. 浏览器事件执行:同步代码、微任务、任务
可以先从一段最短的代码说起:
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);输出是 1、4、3、2。
1 和 4 是同步代码,先执行。Promise.then 的回调进入微任务队列,setTimeout 的回调是后续任务。当前这段代码执行完、调用栈清空后,先清空微任务,再有机会处理下一个任务,所以 3 在 2 前面。计时器写0也不表示回调会立刻打断当前代码。
讲事件循环时,把这条顺序说清楚,再按追问补浏览器提供的计时器、网络等异步能力,以及渲染时机,就比较容易展开。这里讲的是同一个页面 JavaScript 执行环境里的顺序,不是说整个浏览器只有一个线程。
2. TypeScript 泛型:保留输入和输出的类型关系
泛型可以把类型当成参数。同一份函数能处理不同类型,同时保留类型检查。
function identity<T>(x: T): T {
return x;
}
const a = identity(123); // number
const b = identity("hello"); // string如果写成 any,函数确实也能接收这些值,但返回值和输入之间的类型关系就丢了。这里的 T 表示:传进来是什么类型,返回的仍然是那个类型。
需要限制参数能力时,就加约束:
function printLength<T extends { length: number }>(x: T) {
console.log(x.length);
}这时 T 可以变化,但必须有 length。接口也一样,例如 ApiResponse<T> 可以复用响应结构,让 data 分别是 User、Product,不用每次重写一遍。Python 的 TypeVar、list[int] 也是类型参数化的例子,只是 Python 的类型提示本身不会把运行时变成静态类型语言。
3. 缓存三件套:先分清故障发生在哪里
| 情况 | 发生了什么 | 常见处理 |
|---|---|---|
| 穿透 | 查的数据根本不存在,缓存和数据库都没有,请求反复打到数据库 | 参数校验、缓存空值、布隆过滤器 |
| 击穿 | 某个热点 Key 失效,大量请求同时回源 | 合并回源请求、互斥更新、提前刷新或逻辑过期 |
| 雪崩 | 大量 Key 同时失效,或者缓存服务整体不可用 | TTL 加随机值,配合限流、降级、多级缓存和高可用 |
先用“不存在、一个热点、大面积失效”把三种情况分开,再针对场景展开。TTL 加随机值能缓解同时过期,解决不了 Redis 整体宕机,不能把所有方案混在一起背。
4. RDB 和 AOF:快照与写操作日志
RDB 保存某个时间点的数据快照,文件通常比较紧凑,适合备份和恢复。代价是宕机时可能丢掉最近一次快照之后的修改。
AOF 记录写操作,通过重放恢复数据。具体能丢多少数据取决于刷盘策略:always 尽量及时刷盘;everysec 每秒刷盘,通常可能丢最近约一秒的写入;no 交给操作系统决定。它也会做重写,不能理解成日志永远只增不减。
两种可以结合使用,重点是根据可接受的数据丢失窗口、性能和恢复需求选择,不能简单说一种在所有场景都更好。
5. Function Calling:把模型和执行程序分开
我当时说“模型自己调用 Python 解释器去跑”,容易把两层混在一起。
模型看到工具说明和参数格式后,生成的是“调用哪个工具、传什么参数”的结构化请求。真正执行函数、操作解释器、访问网络或数据库的是外面的程序。执行完,再把结果交回模型,让它继续判断。
用户问题
→ 模型返回工具名、参数
→ 程序检查参数与权限,执行工具
→ 工具结果放回上下文
→ 模型继续回答,或再调用工具所以“内置 Python 解释器”可以是工具的一种实现,但不是 Function Calling 本身的定义。先讲清楚这两层,后面的远程工具问题也就容易接了。
6. 私有 API:先解释调用怎么发生,再说 MCP
模型不需要自己持有私有服务的密钥,也不直接替我完成网络请求。可以在后端封装一个工具函数,由它带着服务需要的身份和权限,通过 HTTP、SDK 或 RPC 调用接口,把结果整理好再返回。
我当时直接回答 MCP,跳到了工具接入方式这一层。MCP 可以用来标准化工具发现和调用,但不用 MCP,也能把一个私有 API 包装成工具。
把这条路径说完整会更清楚:
模型提出工具调用
→ 后端工具函数
→ 带认证访问私有服务
→ 得到业务结果
→ 返回模型如果工具通过 MCP 接入,宿主里的 Client 先向 Server 查询可用工具及参数说明,再把这些能力提供给模型;执行时由 Client 把调用交给 Server。常见的方法名是 tools/list 和 tools/call。
MCP Server 可以在本机,也可以在远端。本机常见 STDIO,远端可以使用 HTTP;它不等于“远程 API”,也不是所有工具调用的必经步骤。除 Tools 外,它还可以提供 Resources 和 Prompts。
7. Agent 设计模式:从 ReAct 往外展开
我当时只知道 ReAct。复习时可以按任务怎么组织来记几种思路:
- ReAct:推理、行动、观察结果,再继续推理,边做边决定下一步。
- Plan-and-Execute:先列计划,再逐步执行,必要时调整计划。
- Reflection:先得到一版结果,再检查、修正。
- Router / Supervisor:按任务类型分发,或由一个协调者安排其他 Agent。
这不是一个互斥、穷尽的分类表。实际系统往往会混合使用,再配合重试、超时、最大步数、检查点、人工确认等控制。
8. 项目追问:需要补一个具体故事
这一部分还缺当时的具体回答,不能拿现在的项目经历直接回填。后续回忆时,只需要把一件事说清楚:
做的是哪个项目 → 最初漏掉了什么问题 → 怎么发现 → 和 AI 怎么迭代 → 最后怎么确认解决了。
如果某次追问已经记不起来,就留在这里,不把通用答题建议写成当时的对话。
顺带整理几个容易混的概念
这些来自后续复习,不另外算成面试官问过的题:
- Multi-Agent 是多个 Agent 分工协作;职责、工具和上下文可以不同,但会增加通信、成本和调试难度。
- Trace 记录一次任务里的模型调用、工具调用、耗时和错误,是排查执行过程的线索,不是一种 Agent 架构。
- Swarm 在不同系统里的具体含义不同,常用来描述多个 Agent 之间动态协作或交接,不能把某个框架的做法当成唯一标准。
- Memory 可以存放在外部;只有被取出并放进当前请求的部分,才是模型这一次真正看到的上下文。
- RAG 主要解决取什么资料来辅助生成;Agent 主要讨论怎样决定和执行后续行动,两者可以结合。
- Redis 的过期删除处理到期的 Key,内存淘汰处理内存不足时留谁;LRU 看最近使用,LFU 看频率。主从复制复制数据,Sentinel负责监控与故障切换,Cluster通过16384个槽分片并提供相应故障转移能力。
当时想写的帖子叫“秋招第一面和第一挂”,但“第一挂”说的是另一家公司的结果,不能因此把这场也记成挂了。