Skip to content
掌握度

时长约 60 分钟 · 自我介绍 + 7 问答 + 算法(LC 146 LRU,已完成)+ 反问。本轮重点验证前两场行动项:聊天助手模块三道验证题两道满分、一道过线,修复确认;稳定性四层仍靠查笔记(第三次),线程池流程顺序颠倒(4 分),两道转入睡前默写闭环。

一面 ​

1. 自我介绍 — 9.5 分 ​

问题:请先做个自我介绍,两分钟以内。

我的回答:

面试官您好,我叫 XX,杭州某高校计算机技术硕士,27 届。竞赛拿过 ACM、CCPC 省赛银奖,蓝桥杯全国三等奖。技术栈分两块:后端是 Java、Spring Boot、MySQL、Redis、ES、Kafka 这一套;AI 应用方向做过 MCP、Function Calling、ReAct 和 RAG 全链路。实习在某互联网大厂数据中台做了近半年后端研发,主要负责 MySQL 到 Hive 数据同步链路的落地,包含 Binlog 采集、消息队列和 Spark 计算。核心难点是全量和增量数据之间有一段天然的重叠窗口,我们靠批次锚点配合下游主键幂等去重做到不丢不重,数据入湖延迟 P95 稳定在 3 分钟以内。个人主要做了两个项目。一个是终端 CodeAgent CLI,这是基于 ReAct 和工具调用,做了分层记忆和任务编排;安全上做了 Git 快照回滚——快照放在一个独立的仓库里,回滚全程不碰用户自己的 Git 历史;长上下文超限则用动态阈值的压缩来解决。另一个是 RAG 知识库,大文件通过分片上传断点续传把 1GB 上传从 15 秒优化到 3 秒,向量加 BM25 混合检索让 recall@5 从 63% 提到 85%,回答支持流式和原文溯源。我应聘 AI 应用开发岗位,希望能把后端工程能力和大模型应用经验落地到业务中。谢谢。

复盘:

  • 连续第三场 9.5,定位句、钩子、措辞全部稳定——这段封版,不再投入
  • 关联:自我介绍

2. 引用溯源 — 9.5 分(行动项验证通过) ​

问题:怎么防止模型编造出处?模型输出不存在的编号会怎样?

我的回答:

检索结果在拼进 Prompt 前进行编号,而模型只能看到这个编号。编号背后真实的文件出处——文件指纹、页码、锚点——存在服务端的映射表里,模型从头到尾接触不到,它回答时只能引用这个编号,写不了出处内容。回答完成之后,完成通知把编号到出处的映射推给前端,用户点角标,后端查映射跳到原文预览。所以伪造这一层在架构上就消失了,模型根本没有编的材料。就算模型真的输出了一个不存在的编号引用,前端查不到映射关系,会降级成普通文本,最多幻觉成一个无效的角标,绝对不会指向错误来源,危害被封在显示层面。实测主流模型对编号引用的遵从性很高,因为编号就在上下文中,照抄是成本最低的动作。映射本身有三级容灾:当前会话内存、Redis 生成快照、历史行的 JSON 字段——在线、断线重连、查历史三个场景引用都能还原。代价是模型不能改写引用描述,灵活性低一点;但知识库场景可溯源远比灵活重要。

复盘:

  • 第一场的「忘记」已完全修复:口诀(编号进 Prompt / 真相在服务端 / 伪造只掉角标)+ 三级容灾 + 代价取舍,全部自发说出
  • 关联:聊天助手模块「引用机制」

3. ReAct 自主检索 vs 固定流水线 — 9.5 分(行动项验证通过) ​

问题:为什么不做成固定「先检索再回答」流水线?自主的风险怎么管?

我的回答:

固定的「先检索再回答」流水线主要有两个毛病:一是成本,每次对话都强制检索,就连寒暄也要打一次 embedding 加一次 ES;二是质量不可控,检索结果好坏都无脑塞给大模型,检索失败或者结果不搭时,回答直接被污染。

所以用 ReAct 把决定权交给模型:需要事实依据时它主动调检索工具,拿到结果觉得不够还能再查一轮,闲聊就直接回答。但自主性有代价,就是导致循环失控,模型可能无限调工具。笼子是两层预算加强制收尾:最多四轮、最多八次工具调用,预算耗尽还有一轮强制收尾——把工具列表清空,逼模型基于已有结果作答,防止它陷在工具循环里。检索原则本身是「知识库优先」的强制检索,配五类免检索白名单:企业内部知识模型其实没见过,是幻觉高发区,所以默认都查,纯寒暄这类才免检——分流交给模型判断,不写死代码分类器。

检索工具内部就是权限混合检索,模型自主调工具等于自动带上了用户的权限边界——自主性不会引入越权。

复盘:

  • 上一场只答出白名单、核心论证丢失——本轮成本 / 质量 / 循环失控 / 笼子 / 权限自动带上全部到位,修复确认
  • 关联:聊天助手模块「为什么用 ReAct」

4. CodeAgent 检索工具分工 — 忘记 (新题) ​

问题:CodeAgent 有哪几种代码检索?Agent 什么时候用哪种?为什么 grep 不建索引?

我的回答:不知道。

完整答案:

检索工具有三种,互补不替代。grep_code:封装 ripgrep 的精确检索,字面量或正则定位,结果直接是行号——零索引、零一致性维护;search_code:RAG 语义检索,用自然语言找「大概在哪」,依赖离线向量索引;glob_files:按文件名模式找文件。三把尺子是「精确 → 语义 → 文件名」:主路径是 grep 定位到行、再 read 读上下文,search_code 只在不知道关键词、按「意思相近」找时才用。

为什么 grep 不建索引:grep 的价值恰恰是零状态——即时、永远新鲜。代码刚改完、索引还没更新,grep 出旧结果比慢更致命。精确检索永远不建索引;要「按意思找」才值得上索引,那是 search_code 的职责。

深度点:rg 快是因为 Rust 正则引擎 NFA 无回溯爆炸、字面量前缀 SIMD 预过滤、多线程目录遍历、感知 .gitignore 跳过构建产物——自己重写等于把十年打磨重做一遍还做不对,所以封装它并留纯 Java 兜底(rg 未安装或超时降级),保证裸环境不失效。实测 10 万行级仓库单次检索 P95 约 22ms,离简历的 200ms 有近十倍余量。

记忆口诀:精确、语义、文件名,三把尺子各量各的;grep 零状态永远新鲜,要语义才配建索引。

5. 位点 vs 时间戳(快问)— 7 分(补强通过) ​

问题:全量增量切分锚点为什么用位点不用业务时间戳?

我的回答:

因为 Canal 可以通过 Binlog 文件 + position 位点确定采集到具体哪一条数据,而如果使用时间戳定位不到当前具体采集到那一条数据。

完整答案:

核心一句:位点在单库内是全序单调的,业务时间戳不是。

位点由「Binlog 文件名 + 偏移量」组成,每一条事件唯一且严格递增——所以既能当切分锚点(wholeLoadBatchTime 精确落在某条事件上,增量从位点之后消费,一条不丢),又能当排序依据(同一主键多次变更,按位点保留最大的那条,末态一定正确)。

业务时间戳撑不起这两个职责:一是碰撞——同一秒多条变更,排序分不出先后;二是回拨——NTP 校时、主从切换都会让时间戳倒退,按时间戳切分会出现「后发生的变更被当成旧的」;三是它不是 Binlog 原生属性,应用层填的时间戳不可控。所以位点管顺序和切分,时间戳只做展示。

复盘:

  • 核心理由(位点定位到具体一条、时间戳定位不到)说出来了,补强通过;满分差「重复、回拨」两个具体的失效模式词
  • 关联:Binlog2Hive链路设计 Q6

6. 大模型链路稳定性 — 查笔记通过,记忆未闭环 (第三次) ​

问题:做了哪几层可用性设计?Embedding 挂了怎样?和熔断器差距在哪?

我的回答:忘记了,从笔记中复制了原文(内容与笔记完全一致:四层设计 + 与熔断器差距 + Resilience4j 演进方向)。

记忆方案:这段背全文必挂,改背骨架口诀——

兜底管体面、降级管功能、限流管流量、热切管供应商。差距一句话:熔断器是自动的,我的是手动的。

四个短语各挂一层:兜底 → 友好提示;降级 → 纯 BM25 + 同权限过滤;限流 → IP / 账号 / 每日次数 / token 余额四道闸;热切 → 密钥加密落库不重启。展开内容已经会了,先用口诀默写四层,再逐层展开。

7. 线程池提交流程 — 4 分 (行动项未闭环) ​

问题:任务提交后 core、queue、max、拒绝策略怎么依次生效?CodeAgent 的 4 并发固定线程池为什么不会触发拒绝?

我的回答:

任务提交,首先看核心线程数是否已满,再看最大线程数,如果满了则放入队列中,队列中的线程按照 FIFO 算法执行。

完整答案:

四步,顺序是铁律:① 线程数 < corePoolSize:直接新建核心线程执行——哪怕有空闲核心线程也新建,不复用;② 线程数达到 core:任务进 workQueue 排队;③ 队列满且线程数 < maxPoolSize:新建非核心线程执行——是队列满了才扩非核心,不是先扩满再排队;④ 队列满且线程数到 max:触发拒绝策略。四种:AbortPolicy(默认,抛 RejectedExecutionException)、CallerRunsPolicy(提交线程自己跑,天然背压)、DiscardPolicy(静默丢)、DiscardOldestPolicy(丢队首最老的再试)。

记忆钩子:招人比喻——正式工没满先招正式工;满了排队;队列挤满招临时工;临时工也招满,来一个拒一个。

CodeAgent 的 4 并发固定线程池:core = max = 4,队列是无界 LinkedBlockingQueue——队列永远不满,③④步永远走不到,永不拒绝:超出的任务 FIFO 排队、跑完补位。安全的理由不是参数好,是场景好:拓扑上同层无依赖任务数天然有限,堆积有上限。外部流量驱动的服务,无界队列就是 OOM 隐患,必须换有界队列配拒绝策略。

复盘:

  • 顺序颠倒(核心满 → 看 max → 才入队),正是这道八股最经典的错误点
  • 后端岗行动项第一件,讲过一轮仍未闭环——和稳定性同病:讲过 ≠ 记住。用「正式工 → 排队 → 临时工 → 拒门外」睡前默写
  • 关联:面渣逆袭 Java 并发线程池页

8. 算法:LeetCode 146 LRU 缓存 — 完成 ​

问题:LeetCode 146 · LRU 缓存,要求 get / put 均 O(1)。

按新规则线上作答,已完成。核心考点自评:哈希表 + 双向链表,get/put 均 O(1);面试追问方向:「为什么单用哈希表或单用双向链表都不行」。

反问(备选,已备两条) ​

  1. 技术向:「团队现在 RAG / Agent 类业务落地到什么阶段了?线上有检索质量和回答质量的评测体系吗?」——可能把话题引回 recall@5 主场。
  2. 成长向:「校招生进来一般从哪类模块入手?导师制怎么运作,多久能独立负责一条业务线?」

总复盘 ​

得分曲线:9.5 / 9.5 / 9.5 / 忘 / 7 / 查 / 4 / 算法完成,问答均分 7.9。

本轮结论:

  • 聊天助手模块修复大半:引用溯源、ReAct 自主检索两道从「忘记 / 5.5」修复到满分——重读笔记的行动项有效
  • 仍有两个未闭环:稳定性四层(第三次靠查,改背口诀骨架 + 睡前默写)、线程池流程(顺序颠倒,用招人比喻默写)
  • 一道新题暴露:CodeAgent 检索分工(grep_code / search_code / glob_files)——新题首次作答,背口诀即可
  • 自我介绍三场稳定 9.5,封版

行动项:

  • [ ] 睡前默写:稳定性四层口诀 + 线程池「正式工 → 排队 → 临时工 → 拒门外」各一遍,次日晨再一遍
  • [ ] 背 CodeAgent 检索分工口诀:精确、语义、文件名
  • [ ] LRU 复盘:哈希 + 双向链表,为何缺一不可

本站仅供个人学习使用,请勿外传