我会按这几个层次来讲:
-
面试官怎么出题
-
你第一反应该怎么说
-
你每一步怎么要求大模型
-
大模型可能怎么回答
-
你怎么人工纠偏
-
最后怎么把这个过程讲得让面试官满意
我用一个很典型、很适合 AI coding 的题:
实现一个工单处理系统
要求先按"面试现场 + 你用 AI 协作开发"的场景来模拟。
题目设定
面试官说:
> 现在有一个工单处理系统,支持创建工单、分配工单、更新工单状态、查询工单列表。
> 你可以用 AI 辅助你完成,但我更关注你的过程,不太关注你最后写得多完整。
> 你边做边讲你的思路。
这时候,面试官其实在看的是:
-
你会不会先收需求
-
你会不会把问题结构化
-
你是不是让 AI 受控地工作
-
你有没有验证意识
-
你会不会纠偏
一、你第一句话怎么说
这个很重要,别一上来就开写。
你应该这样说:
> 我先不急着直接生成代码,我会先把需求和边界确认一下,再让模型先产出一个轻量设
> 计,再拆分任务逐步实现。这样更可控,也方便我边做边校验。
这句话很加分,因为它说明你有流程感。
然后你开始澄清需求。
二、第一步:需求澄清
你应该问什么
你可以问面试官:
> 我先确认几个边界:
>
> 1. 这是做成一个内存版核心类即可,还是要模拟完整后端接口?
> 2. 工单状态是否有固定流转,比如 CREATED -> ASSIGNED -> PROCESSING -> DONE ->
> CLOSED?
> 3. 分配工单时是否允许重复分配?
> 4. 查询列表是否只需要全部查询,还是需要按状态/处理人过滤?
> 5. 现场实现我先按单机内存版写,是否可以?
假设面试官回答:
> 可以按 Java 内存版写,不需要 controller 和数据库。
> 需要状态流转校验。
> 查询先支持全部列表和按状态过滤。
> 重点看你的抽象和流程。
这时候你就可以进入 AI 协作了。
三、第二步:你怎么给大模型下第一条指令
这里最关键的是:
先让 AI 出设计,不要先写代码
你对模型的 prompt
你可以这样写:
你现在是一个资深 Java 后端工程师。请先不要写代码,先帮我输出一个轻量设计文档
题目:
实现一个工单处理系统,支持:
-
创建工单
-
分配工单给处理人
-
更新工单状态
-
查询工单列表
-
支持按状态过滤查询
约束:
-
使用 Java
-
面试现场实现,先做内存版,不接数据库,不写 controller
-
重点是核心领域建模和 service 设计
-
状态流转需要校验
-
代码要尽量简洁,避免过度设计
请输出:
-
核心类设计
-
数据结构
-
状态流转规则
-
核心方法设计
-
异常处理策略
-
测试点
不要输出代码。
四、大模型第一轮可能怎么回答
它大概率会给你这样的内容。
模型回答示例:design.md
design.md
1. 核心类设计
Ticket
表示工单实体,包含:
-
id: Long
-
title: String
-
description: String
-
assignee: String
-
status: TicketStatus
-
createdAt: LocalDateTime
TicketStatus
工单状态枚举:
-
CREATED
-
ASSIGNED
-
PROCESSING
-
DONE
-
CLOSED
TicketService
负责核心业务操作:
-
createTicket
-
assignTicket
-
updateStatus
-
listTickets
-
listTicketsByStatus
2. 数据结构
使用内存 Map<Long, Ticket> 存储工单:
-
key: 工单 id
-
value: Ticket 对象
使用一个自增 long 作为工单 id 生成器。
3. 状态流转规则
允许的状态流转:
-
CREATED -> ASSIGNED
-
ASSIGNED -> PROCESSING
-
ASSIGNED -> CLOSED
-
PROCESSING -> DONE
-
PROCESSING -> CLOSED
-
DONE -> CLOSED
不允许:
-
CLOSED -> 任意状态
-
DONE -> PROCESSING
-
CREATED -> DONE
4. 核心方法设计
createTicket(String title, String description)
创建工单,初始状态为 CREATED。
assignTicket(Long ticketId, String assignee)
给工单分配处理人,若当前状态不是 CREATED,则可根据规则决定是否允许重新分配。
updateStatus(Long ticketId, TicketStatus newStatus)
更新工单状态,需要校验状态流转是否合法。
listTickets()
返回所有工单。
listTicketsByStatus(TicketStatus status)
按状态过滤工单。
5. 异常处理策略
-
参数非法:IllegalArgumentException
-
工单不存在:NoSuchElementException
-
状态流转非法:IllegalStateException
6. 测试点
-
正常创建工单
-
正常分配工单
-
合法状态流转
-
非法状态流转
-
查询全部工单
-
按状态过滤查询
-
查询不存在工单时异常
五、这时候你不能直接信,你要怎么 review
这一步是面试里最体现水平的地方。
你不能说"好,继续生成代码"。
你要先人工点评。
你应该怎么讲
你可以这样边看边说:
> 这个设计整体方向是对的,实体、枚举、service 的划分也比较适合现场题。
> 但我会做两个调整。
> 第一,description 不是题目强需求,如果时间有限我会先去掉,避免非核心字段分散注
> 意力。
> 第二,分配逻辑里提到"是否允许重新分配",这里需求没明确,我会先约束成只有
> CREATED 状态可以分配,这样规则更清晰。
这段非常像真实工程师。
然后你对模型补充约束。
六、第二轮:你怎么修设计
你给模型的第二条 prompt
请根据以下修正更新 design.md:
-
为了适配面试现场,先去掉 description 字段,保留最小必要字段
-
assignTicket 只允许在 CREATED 状态下执行
-
保持实现简洁,不引入 repository、controller 等额外层
请输出修订后的精简设计,不要输出代码。
模型可能返回
design.md(修订版)
核心类
Ticket
-
id: Long
-
title: String
-
assignee: String
-
status: TicketStatus
-
createdAt: LocalDateTime
TicketStatus
-
CREATED
-
ASSIGNED
-
PROCESSING
-
DONE
-
CLOSED
TicketService
-
createTicket(String title)
-
assignTicket(Long ticketId, String assignee)
-
updateStatus(Long ticketId, TicketStatus newStatus)
-
listTickets()
-
listTicketsByStatus(TicketStatus status)
状态规则
-
创建后初始状态为 CREATED
-
仅 CREATED 状态允许 assignTicket,分配后变为 ASSIGNED
-
ASSIGNED -> PROCESSING / CLOSED
-
PROCESSING -> DONE / CLOSED
-
DONE -> CLOSED
-
CLOSED 不允许再变更
存储
-
Map<Long, Ticket> tickets
-
long nextId 自增
异常
-
IllegalArgumentException
-
NoSuchElementException
-
IllegalStateException
测试点
-
createTicket
-
assignTicket
-
updateStatus
-
invalid transition
-
listTickets
-
listTicketsByStatus
这时设计就比较干净了。
七、第三步:让模型拆任务,不要一口气写代码
这是控制 AI 的核心手段。
你的 prompt
要求:
-
将实现拆分成最小可执行任务
-
每个任务应尽量独立,便于逐步实现和验证
-
按面试现场 coding 场景安排顺序
-
不要输出代码
模型可能返回:task.md
task.md
-
定义 TicketStatus 枚举
-
定义 Ticket 类及其字段、构造函数、getter
-
在 Ticket 中实现 assignTo 方法
-
在 Ticket 中实现 updateStatus 方法及状态流转校验
-
定义 TicketService,初始化内存存储和 id 生成器
-
实现 createTicket
-
实现 assignTicket
-
实现 updateStatus
-
实现 listTickets
-
实现 listTicketsByStatus
-
编写简单测试样例或 main 方法验证流程
八、这时候你怎么对面试官解释
你别沉默。
你可以说:
> 我现在先让模型把实现拆成小任务,因为如果一次生成完整代码,面试中很难控制质量,
> 也不方便定位问题。拆小以后,我可以逐步验收。
这句话很加分。
九、第四步:开始让模型按模块生成代码
第一小步:先生成最基础的数据结构
你的 prompt
请只完成 task 1 和 task 2:
-
定义 TicketStatus 枚举
-
定义 Ticket 类及其字段、构造函数、getter
要求:
-
Java
-
不要实现 service
-
不要实现业务方法
-
保持代码简洁,适合面试现场
模型可能返回
import java.time.LocalDateTime;
enum TicketStatus {
CREATED,
ASSIGNED,
PROCESSING,
DONE,
CLOSED
}
class Ticket {
private final long id;
private final String title;
private String assignee;
private TicketStatus status;
private final LocalDateTime createdAt;
public Ticket(long id, String title) {
this.id = id;
this.title = title;
this.status = TicketStatus.CREATED;
this.createdAt = LocalDateTime.now();
}
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getAssignee() {
return assignee;
}
public TicketStatus getStatus() {
return status;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
}
十、这时你要怎么 review
你要说出检查点。
你可以这样讲:
> 这一步我主要看领域对象是不是最小闭环。
> 现在字段基本够了,初始状态也设成了 CREATED。
> 但我会注意 title 是否需要判空,这里我可能会放到 service 层统一做输入校验,避免
> 实体构造器和 service 重复校验。
这就显得你不是被动接收。
十一、第二小步:生成业务方法
你的 prompt
请只完成 task 3 和 task 4:
-
在 Ticket 中实现 assignTo(String assignee)
-
在 Ticket 中实现 updateStatus(TicketStatus newStatus)
-
需要校验状态流转是否合法
-
assignTo 只允许在 CREATED 状态下执行
要求:
-
直接在现有 Ticket 类基础上补充
-
使用 IllegalArgumentException 和 IllegalStateException
-
保持实现清晰,不要过度抽象
模型可能返回
public void assignTo(String assignee) {
if (assignee == null || assignee.isBlank()) {
throw new IllegalArgumentException("assignee cannot be empty");
}
if (this.status != TicketStatus.CREATED) {
throw new IllegalStateException("ticket can only be assigned in CREATED
status");
}
this.assignee = assignee;
this.status = TicketStatus.ASSIGNED;
}
public void updateStatus(TicketStatus newStatus) {
if (newStatus == null) {
throw new IllegalArgumentException("newStatus cannot be null");
}
if (!isValidTransition(this.status, newStatus)) {
throw new IllegalStateException(
"invalid status transition: " + this.status + " -> " + newStatus
);
}
this.status = newStatus;
}
private boolean isValidTransition(TicketStatus from, TicketStatus to) {
return switch (from) {
case CREATED -> false;
case ASSIGNED -> to == TicketStatus.PROCESSING || to ==
TicketStatus.CLOSED;
case PROCESSING -> to == TicketStatus.DONE || to == TicketStatus.CLOSED;
case DONE -> to == TicketStatus.CLOSED;
case CLOSED -> false;
};
}
十二、这里出现了一个真实面试里很重要的点:AI 出错了
你看,CREATED -> false 这个实现其实没问题,因为分配不是走 updateStatus,而是走
assignTo。
但面试官可能会追问:
> 那 CREATED -> ASSIGNED 为什么不在状态流转函数里?
这时候你要能讲清楚。
你的回答方式
你可以说:
> 我这里是把"分配"当作一个显式业务动作,而不是纯状态修改,所以 CREATED ->
> ASSIGNED 不通过 updateStatus,而通过 assignTo 触发。
> 这样做的好处是把"赋值 assignee + 更新状态"绑定在一个业务动作里,避免直接调状态
> 更新时漏掉 assignee。
> 如果想做得更统一,也可以把 CREATED -> ASSIGNED 纳入状态机,但那样还要额外约
> 束"进入 ASSIGNED 必须有 assignee"。
这个解释非常好,面试官会觉得你对建模有理解。
十三、第三小步:生成 service
你的 prompt
请只完成 task 5 到 task 10,实现 TicketService。
要求:
-
使用 Map<Long, Ticket> 做内存存储
-
使用 long nextId 做自增 id
-
提供方法:
-
createTicket(String title)
-
assignTicket(Long ticketId, String assignee)
-
updateStatus(Long ticketId, TicketStatus newStatus)
-
listTickets()
-
listTicketsByStatus(TicketStatus status)
-
输入非法时抛 IllegalArgumentException
-
工单不存在时抛 NoSuchElementException
-
返回列表时避免暴露内部可变集合
-
不要写 controller,不要写数据库代码
模型可能返回
import java.util.*;
import java.util.stream.Collectors;
class TicketService {
private final Map<Long, Ticket> tickets = new HashMap<>();
private long nextId = 1L;
public Ticket createTicket(String title) {
if (title == null || title.isBlank()) {
throw new IllegalArgumentException("title cannot be empty");
}
Ticket ticket = new Ticket(nextId++, title);
tickets.put(ticket.getId(), ticket);
return ticket;
}
public void assignTicket(Long ticketId, String assignee) {
Ticket ticket = getRequiredTicket(ticketId);
ticket.assignTo(assignee);
}
public void updateStatus(Long ticketId, TicketStatus newStatus) {
Ticket ticket = getRequiredTicket(ticketId);
ticket.updateStatus(newStatus);
}
public List<Ticket> listTickets() {
return new ArrayList<>(tickets.values());
}
public List<Ticket> listTicketsByStatus(TicketStatus status) {
if (status == null) {
throw new IllegalArgumentException("status cannot be null");
}
return tickets.values()
.stream()
.filter(ticket -> ticket.getStatus() == status)
.collect(Collectors.toList());
}
private Ticket getRequiredTicket(Long ticketId) {
if (ticketId == null) {
throw new IllegalArgumentException("ticketId cannot be null");
}
Ticket ticket = tickets.get(ticketId);
if (ticket == null) {
throw new NoSuchElementException("ticket not found: " + ticketId);
}
return ticket;
}
}
十四、这里你还要继续 review,而不是直接结束
你要说:
> 这一步 service 已经形成主链路了。
> 我会再看两个点。
> 第一,listTicketsByStatus 返回的是新的列表,没直接暴露内部集合,这个是好的。
> 第二,目前返回的还是 Ticket 对象本身,如果是严格封装场景,可能还要考虑 DTO 或只
> 读视图,但面试现场这个程度足够了。
这个评价会显得你知道生产和面试之间的差别。
十五、第五步:补测试或演示代码
你的 prompt
请基于现有代码,补充一个简单的 main 方法演示以下流程:
-
创建两个工单
-
给其中一个工单分配处理人
-
将其状态更新为 PROCESSING 和 DONE
-
查询全部工单
-
查询 DONE 状态工单
-
演示一次非法状态流转并捕获异常
要求:
-
代码尽量简洁
-
便于面试现场展示功能正确性
模型可能返回
public class Main {
public static void main(String[] args) {
TicketService service = new TicketService();
Ticket t1 = service.createTicket("Fix login bug");
Ticket t2 = service.createTicket("Optimize search performance");
service.assignTicket(t1.getId(), "Alice");
service.updateStatus(t1.getId(), TicketStatus.PROCESSING);
service.updateStatus(t1.getId(), TicketStatus.DONE);
System.out.println("All tickets:");
for (Ticket ticket : service.listTickets()) {
System.out.println(ticket.getId() + " - " + ticket.getTitle() + " - "
- ticket.getStatus());
}
System.out.println("DONE tickets:");
for (Ticket ticket : service.listTicketsByStatus(TicketStatus.DONE)) {
System.out.println(ticket.getId() + " - " + ticket.getTitle());
}
try {
service.updateStatus(t2.getId(), TicketStatus.DONE);
} catch (Exception e) {
System.out.println("Expected error: " + e.getMessage());
}
}
}
十六、这时候面试官可能怎么追问
这是最像真实场景的部分。
追问 1:如果 AI 生成错了你怎么办?
你可以答:
> 我会先判断问题在哪一层。
> 如果是实现偏差,我会直接约束模型重写局部。
> 如果是设计不合理,我会回到 design.md 修设计,而不是在代码层硬补丁。
> 我不会带着错误设计一路往下生成。
这句很好。
追问 2:如果题目变复杂,要接数据库怎么办?
你可以答:
> 我会保留当前的领域对象和 service 接口,把存储抽象成 repository。
> 面试现场我先做内存版,是为了优先把业务规则讲清楚。
> 如果扩展到生产,再把 repository、事务、索引、并发控制接进去。
这说明你有分层扩展意识。
追问 3:为什么不是一次让 AI 全写完?
你可以答:
> 一次性全写完的问题是不可控。
> 模型可能在设计、状态机、异常处理上某一步走偏,但如果代码量太大,我很难快速定
> 位。
> 分阶段做的好处是每一步都能小闭环验收,返工成本低。
追问 4:你怎么保证 AI 写的代码质量?
你可以答:
> 我一般做四层控制。
> 第一层是 prompt 约束,明确边界、不要过度设计。
> 第二层是设计先行,先看方案再写代码。
> 第三层是分步生成,每一步人工 review。
> 第四层是运行验证和边界测试。
> 本质上不是"信任模型",而是"管理模型输出"。
这句很强。
十七、这个场景里,真正高分的不是代码,是你体现出的 6 个能力
面试官满意的点通常是这些:
-
你会先问需求
-
你知道先设计再编码
-
你会让 AI 分步做,而不是一把梭
-
你会人工 review
-
你能发现模型输出里的问题
-
你能解释自己的建模取舍
所以你要记住:
AI coding 的高分,不是 prompt 花哨,而是流程可控。
十八、如果你在面试现场,完整表达可以怎么说
你可以这样讲,几乎可以直接背:
> 如果让我在面试现场做 AI coding,我一般不会直接把题目丢给模型生成最终代码。
> 我会先和面试官确认需求边界,比如实现范围、状态规则、是否需要数据库和接口层。
> 确认后,我会先让模型输出一个简短设计文档,包含领域对象、核心方法、状态流转和异
> 常处理策略。
> 设计确认没问题后,我再让模型拆成小任务,比如先写数据结构,再写核心 service,再
> 补测试。
> 每一步我都会人工 review,检查是否符合设计、是否漏边界、是否有过度设计。
> 如果发现问题,我会回到对应阶段修正,而不是继续带着错误往下生成。
> 对我来说,AI coding 不是让模型替我写代码,而是把模型纳入一个受控的软件开发流
> 程。
这版很适合阿里这类会看工程感的面试。
十九、我再给你一个更真实的"你-模型-面试官"三方短剧版
面试官: 你可以开始了。
你: 我先确认边界,这题我先按 Java 内存版实现,不写 controller 和数据库,重点放在
工单建模和状态流转,可以吗?
面试官: 可以。
你: 那我先让模型出一个设计,不直接写代码。
你对模型: "请先输出 design.md,不要写代码......"
模型: 输出 Ticket / TicketStatus / TicketService 等设计。
你: 这个设计基本合理,但我会去掉非核心字段,只保留最小闭环,assign 只允许在
CREATED 状态。
面试官: 为什么这么改?
你: 因为现场时间有限,我优先保证主链路和领域规则清晰,避免样板代码和非核心字段分
散注意力。
面试官: 如果后来要接数据库呢?
你: 我会保留当前领域模型和 service 接口,把内存存储替换成 repository,现场先聚焦
核心规则,生产再补持久化和事务。
这就是一个很完整的面试过程了。
二十、你现在最该练什么
建议你练 3 件事:
- 练"第一句话"
要先说流程,不要直接 code。
- 练"设计 review"
模型出设计后,你要能讲出:
-
哪合理
-
哪要删
-
为什么这么改
- 练"解释取舍"
比如:
-
为什么先做内存版
-
为什么不一次全写
-
为什么状态规则放实体里
-
为什么分配是业务动作不是纯状态更新
这三件事,比你会不会写一个复杂 prompt 更重要。