满帮 AI 全栈开发一面面经
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 请做一下自我介绍。
- 实习那个需求交付平台,构建了 6 个阶段串行、把 PRD 转化成研发文档——这块完整的实现流程大概讲一下?
- 拿到一个 PRD 之后,PRD 的质量、每个人写 PRD 的风格都不太一样,你怎么保证 PRD 到研发文档转换的准确性和稳定性?
- 你新产生的给 AI 读的文档,跟以前写给人读的 PRD 差异应该相当大吧?那 Git diff 的作用是什么?
- 做分析的时候会通过 MCP 去查询代码,你怎么保证它找到的代码是准确的?不同系统的历史债务不一样,有的老系统命名和代码规范都不太行,每次全局扫既费 token、准确率也不一定高吧?
- 你后面还预留了一些扩展点——这些串行的主流程在什么场景下满足不了你的诉求、需要扩展分支?
- 这个扩展点其实主要是为测试用例预留的,是吗?
- 这里通过 SSE 推送模型的输出以及节点处理的进度,还支持断线重连——当时是怎么做的?
- SSE 也会存在断联的情况吧?断联了是重新再推一遍,还是按上次的位置继续推?这个你是怎么实现的?
- 第二点写的是 MySQL 加 RocketMQ 用来构建运行链路——这里 MQ 起到了什么作用?
- 那你用的是延迟队列吗?
- 整个状态机靠 MQ 来传,那你怎么保证消息事件的整体可靠性?
- 重复发送的问题,处理过程中可能是不同机器在消费,不同机器消费的时候,幂等并不一定正好能拦掉吧?
- 你说的「查」和「插」,是指生产的时候还是消费的时候?
- 如果在消费过程中还有一些其他的处理操作,比如更新 Redis,那通过数据库事务回滚不一定能完全保证吧?
- 你 Redis 用得也挺多的,Redis 的分布式锁有用过吗?
- 追问:那锁能解决这个问题吗?
- 这个项目里边有涉及到前端的开发吗?
- 你个人技能里写到了线程池——线程池的一些核心参数、还有流程执行顺序,能大概讲一下吗?
- 场景题:业务或运营提一个诉求,要在后台实现一个批量导入的功能,每次数据量比较大,一两千条,放在一个 Excel 里。要求导入性能尽可能高,同时要把导入过程中业务校验的异常数据展示给运营去重新确认——你会用哪些技术来实现?
- 追问:异常数据存表,如果再轻量、简单一点呢?如果不存表呢?
- 追问:如果需要同步给运营、给用户展示,这个可以怎么简单化实现?运营一直在等,我们怎么来实现这个「等」的功能?
- 代码考核:用 Java 实现一个升序的冒泡排序。
- 智力题:一个 5L 的桶和一个 4L 的桶,怎么量出 4L 的水?
- 智力题:10 箱苹果每箱 10 个,其中 9 箱每个 1 斤、另一箱每个 0.9 斤,只称一次怎么找出那个箱子?
- 后面的职业发展有过考虑吗?
- 你为什么会考虑南京的岗位?
- 你的经历和实习更多还是偏 Agent 相关的开发,但 Agent 开发和 AI 全栈开发可能还不太一样——我们这边 AI 全栈是前端加后端的融合,甚至后面会有前端加后端加测试的融合,还会涉及一些算法相关的东西,不是仅仅通过一些编排或者 Java 研发的东西能搞出来的。你对这个岗位未来的发展规划到底是什么?
《参考解析》
PRD 转研发文档:稳定性要靠约束前置,而不是靠模型自觉。 输入风格不统一、质量参差是这类系统最大的不稳定源,可用的手段是分层的:第一层是输入规范化——固定 PRD 模板与必填区块(背景、目标用户、功能清单、验收标准),缺失字段不进入流水线,直接回报「这条 PRD 缺什么」而不是让模型去猜。第二层是流程拆分——把「一次大生成」拆成结构抽取、方案设计、接口与数据模型、任务拆解、测试用例这些串行阶段,每个阶段只吃上一步的结构化产物,输出走 JSON Schema 或固定标题的 Markdown 校验,不合法就重试或回退,失败不会污染下一阶段。第三层是知识与引用约束——通过 MCP 查仓库时先定位模块边界(代码搜索加目录级筛选)再读文件,避免全库扫描既烧 token 又噪声大;老系统命名混乱时,用「读入口文件加调用链」代替「全库关键字匹配」,并把不确定的结果标成待人工确认而不是硬写进文档。第四层是留痕——每个阶段产出落 Git,评审靠 diff 看「这次到底改了什么」,这也是 Git diff 在这种场景里的真正价值:它服务的是人和流程的可审计性,而不是给模型读——给模型读的应该是结构化的中间产物,两者格式诉求本来就不同。
SSE 的断线重连要按事件序号续传,而不是重推一遍。 SSE 本质是一条长连接上的单向文本流,浏览器断网、切后台、网关超时都会断,重连是常态。正确做法是服务端为每个任务维护事件日志并编号(event id + 递增 seq),客户端重连时带上 Last-Event-ID(浏览器原生 EventSource 会自动带,自研 fetch 流则自己传 query 参数),服务端从该序号之后补发历史事件再接着推增量。要做到这点,任务的事件不能只存在于内存或直接推给当前连接——按任务 ID 落一份短期可重放的事件缓冲(Redis Stream、List 或本地队列加 TTL 都行),生成侧的进度写这个缓冲、推送侧只负责从缓冲读并投递,读写解耦后「重连补发」就是一次范围查询。同时要处理幂等:客户端按 seq 去重,前端渲染用「覆盖式状态」而不是「追加式输出」,避免补发后出现重复段落。另外要有心跳(注释行保活)和终态事件(完成/失败),否则客户端无法区分「还在生成」和「连接已死」。
MQ 在状态流转里做解耦与削峰,但幂等和顺序必须自己兜。 六阶段串行链路里,每个阶段的产出都可能耗时很长,用 MQ 把状态推进异步化,好处是请求线程不用挂住、某个阶段失败可以只重试那一段、上下游按主题解耦。但消息系统给的只是「至少一次」投递,重复和乱序是默认状态,几件事必须做:一是幂等要以状态机为准而不是以「消息有没有处理过」为准——消费时用乐观锁更新状态(update ... set status = next where id = ? and status = current),影响行数为 0 就说明这条已被处理或状态不对,直接确认消费,比单独维护一张去重表更省事且天然防并发;二是「先查再插」有竞态,两个消费者同时查不到再同时插就会双写,靠数据库唯一索引兜底(业务键建唯一约束,捕获冲突异常当作已存在处理);三是顺序不能靠单队列之外的假设,如果同一任务的阶段消息需要严格有序,就按任务 ID 做分区键投到同一队列、由同一个消费者串行处理,或者干脆再加一层「消费时校验前置状态是否已达成」;四是普通消息加延迟队列(或用时间轮/定时表)来实现超时未完成的补偿与卡死检测,不要指望单条消息永不过期。
MQ 消费里混了 Redis 写入和数据库事务,就没有原子性可谈。 这是面试官紧追的点:数据库事务只能回滚数据库内的变更,一旦消费过程中还改了 Redis、发了通知、调了外部接口,事务回滚无法把这些副作用撤掉。应对思路有三条:一是把不可回滚的副作用挪到事务提交之后(本地消息表或事务提交后发事件),保证「数据库是唯一真相来源」,Redis 只是可重建的缓存;二是让所有外部写入都幂等,失败后重放安全,靠业务键和版本号收敛;三是把关键状态落到数据库,Redis 里的数据允许丢失并设计好重建路径(比如从状态表回填),别把 Redis 当唯一存储。分布式锁在这里能解决的只是「同一资源同一时刻只有一个执行者」,解决不了「执行到一半失败」——锁只提供互斥,不提供事务性;而且锁本身还有超时释放后原持有者仍在执行、误删他人锁、以及 Redis 主从切换丢锁的问题,所以锁要和幂等、状态校验叠加使用,不能当成一致性保证。
线程池的核心参数与执行顺序。 七个核心参数里真正决定行为的是四个:核心线程数、最大线程数、队列容量、拒绝策略,另外三个是空闲存活时间、时间单位和线程工厂。任务提交后的顺序是固定的:核心线程未满就直接新建线程执行;核心线程满了先入队列排队;队列满了才创建非核心线程直至最大线程数;再满就触发拒绝策略(AbortPolicy 抛异常、CallerRunsPolicy 回退给提交线程执行、Discard 系列丢弃)。由此推出一个常见误区——用无界队列(LinkedBlockingQueue 默认容量很大)时最大线程数永远不会被触发,等于把并发度锁死在核心线程数上,还会把任务堆在内存里直到 OOM;所以有界队列加明确拒绝策略才是严谨配置。参数怎么定要看任务是 CPU 密集还是 IO 密集:CPU 密集一般取核数上下,IO 密集可以放大数倍(按等待时间与计算时间的比值估),但更稳的做法是先给一个保守值,再用监控(活跃线程数、队列深度、拒绝次数、任务耗时)压测调优,而不是套公式。线程池还要记得命名线程、用自定义拒绝策略打日志,否则线上出了问题无从定位。
批量导入:把「校验」和「等待」解耦,性能靠批处理与异步。 一两千行的 Excel 单机导入其实很快,真正的瓶颈往往在逐行调数据库(每次一条 SQL 的往返开销)和同步阻塞用户请求。落地方案分三段:解析段用流式读取(EasyExcel/POI 的 SAX 模式)避免整个文件进内存,解析出的行先做格式与业务校验并给每行打上行号;写入段用批量插入(JDBC batch,几百到一千条一批)、必要的唯一索引与事务边界按批提交,先校验后写、否则一次回滚代价很高;反馈段把失败行连同原因收集起来,要么落一张导入任务表加明细表,要么更轻量——不建表,直接把失败行和原因序列化成结果文件(CSV)存对象存储,返回一个下载链接,或用任务结果缓存(Redis,带 TTL)在会话里回传。至于「运营一直在等」的问题,标准答案是别让 HTTP 请求等:接口立即返回一个任务 ID,客户端轮询进度接口或订阅 SSE,服务端在后台线程池里跑导入并更新进度,用户关掉页面再回来还能按任务 ID 查结果。同步简单实现只适合几十行的场景,行数上千就要上异步,否则网关超时和重复提交都会来。