
工程师招聘里最耗时的环节是什么很多人会先想到简历筛选但真正让一个团队停下手中工作的往往是那场 live coding 面试。候选人坐在屏幕前写代码面试官在旁边看着半小时过去代码没有跑通两个人的时间已经搭进去了。更麻烦的是同一道题不同面试官打分可能相差很大。Prepin 这类工具的出现正好切中了这个痛点让 AI 通过 voice 和 live coding 直接面试工程师。它不再是一个简单的文档问答机器人而是一个能说话、能出题、能运行代码、能生成评估报告的面试 Agent。从技术层面看这类系统把“面试官”这个角色拆解成了标准化的工程环节语音采集、对话管理、代码执行、自动评估。本文会从技术原理出发带你理解这类 AI 面试系统的工作机制并用一个可运行的最小原型演示“AI 出题、候选人写代码、沙箱运行、生成报告”的完整链路。无论你是想把这套能力集成到团队招聘流程还是想做一个模拟面试工具来练习自己的 live coding 能力这篇文章都有参考价值。读完之后你会清楚这类系统由哪些模块组成、最容易在哪里踩坑、以及从原型到生产环境还需要补哪些工程能力。1. 为什么 AI 面试官值得被认真对待1.1 传统 live coding 面试的三个低效点第一个低效点是面试官的时间成本。一场 45 分钟的 live coding 面试通常需要一名面试官全程参与之后还要花时间整理反馈、和其他面试官同步信息。如果一天面三四个候选人面试官自己的开发任务基本就停滞了。第二个低效点是主观偏差。同样的代码有人看重解法是否正确有人看重沟通思路是否清晰还有人纠结变量命名和代码风格。没有一个统一的评分标准候选人最终的录用与否往往取决于碰到哪位面试官。这对应聘者不公平对团队的招聘质量也不是好事。第三个低效点是候选人体验不可控。人在被注视下写代码紧张感会被放大。部分候选人本来能力过关却在“有人盯着看”的实时编程环境中发挥失常。这种噪音会干扰面试结果的判断。1.2 Prepin 改变了哪个环节从产品形态看Prepin 的定位是“用 voice 和 live coding 面试工程师的 AI”。它的核心思路是把面试流程从“两个人在一个房间里互相试探”变成“一个标准化系统在运行”。这里有三个关键变化。第一面试官的时间被释放。AI 可以承担初筛环节的编程面试面试官只需要查看 AI 生成的评估报告把精力集中在更重要的终面环节。第二评分过程变得可复现。同一套提示词、同一个题目、同样的评分维度所有候选人面对的是相对一致的面试流程主观偏差被压缩。第三live coding 变成了可实时运行的代码。候选人写完代码系统直接在沙箱中运行测试用例返回真实输出。评估依据的是代码的实际运行结果而不是候选人“口头说我会写”。1.3 这类工具适合谁从使用场景看有三类用户最需要关注这类 AI 面试系统。技术团队招聘负责人用 AI 做海量简历初筛后的第一轮编程能力考核节省核心工程师的时间。求职者把它当作模拟面试工具在低压力环境下练习 live coding熟悉“边写代码边讲解思路”的节奏。培训机构和技术学院批量评估学生的编程能力生成统一的评估报告减少讲师重复劳动。不适合的场景也很明确需要考察团队协作能力、系统设计能力、沟通软技能的资深岗位终面以及需要结合公司内部业务上下文的技术评审目前还不适合完全交给 AI。AI 面试官更适合做初筛和标准化考核而不是替代人类面试官的最终判断。2. 核心概念AI 面试官本质上是 Agent不是聊天机器人2.1 Chatbot 到 Agent 的差别很多人会把 AI 面试官理解成“一个会说说话、聊聊天的大模型”。这是一种常见的误解。聊天机器人Chatbot的核心能力是对话。你问一句它答一句上下文有界任务单一。而 Agent 的核心能力是“自主完成任务”。它需要理解目标、拆解步骤、调用工具、获取结果、做决策并继续推进。拿面试场景举例。一个 Chatbot 只能做到“候选人问问题它回答”。而一个面试 Agent 需要做到的是根据候选人级别和技术方向生成合适的题目通过语音与候选人完成多轮对话追问解题思路接收候选人提交的代码调用代码执行环境运行测试根据运行结果给出评分报告。这个过程不是单次对话而是一个有状态、有工具调用、有决策判断的任务闭环。所以Prepin 这类产品背后真正运行的是一个 Agent 系统。2.2 语音链路的技术组成voice 是 Prepin 的一个重要交互入口。语音面试的技术链路可以拆成四段语音活动检测Voice Activity Detection, VAD判断候选人是否开始说话以及一句话是否已经说完。语音识别Automatic Speech Recognition, ASR把候选人的语音转成文本交给大模型理解。大模型推理LLM根据对话状态生成下一个问题或追问。语音合成Text-to-Speech, TTS把模型生成的文本回复转为语音播放给候选人。如果忽略语音链路整个系统就是一个纯文本面试工具加上语音候选人就不用一边写代码一边打字回复交互自然不少。2.3 live coding 的核心难点live coding 部分的技术难点不在“出题”而在“代码执行”。AI 生成题目容易但要安全、稳定地运行候选人提交的代码需要一套专门的执行环境。一个能用的 live coding 执行环境至少要考虑四个问题资源限制单次运行允许消耗多少 CPU 时间、内存、网络请求安全隔离候选人的代码可能是恶意的不能让它访问宿主机文件系统运行反馈需要返回 stdout、stderr、退出码、运行耗时供 Agent 评估语言支持至少支持目标岗位常用的语言比如 Python、Java、JavaScript。这就是“代码沙箱”要做的事情。没有沙箱AI 面试系统永远只能停留在“看代码文本”的阶段无法真正验证候选人的代码能不能跑。3. 技术拆解语音 live coding 面试系统的模块构成一个完整的 AI 面试系统从架构上可以拆成五个核心模块。模块职责关键技术对话管理模块保存面试状态控制流程流转生成问题与追问大模型 Agent、状态机、Prompt 工程语音输入输出模块采集候选人语音识别为文本将回复合成为语音VAD、ASR、TTS代码执行沙箱运行候选人提交的代码返回运行结果与耗时Docker、容器隔离、资源配额评估模块根据对话记录和代码运行结果生成结构化报告大模型评估 Prompt、JSON 输出会话存储模块保存面试过程的完整记录供回放和复核关系型数据库、对象存储五个模块之间是串行协作的关系语音输入 - ASR - 对话管理 Agent - 是否要求写代码 ├─ 是 - 代码沙箱执行 - 结果回传 └─ 否 - TTS 语音回复 - 候选人继续面试结束时评估模块把整个对话记录和代码运行结果打包输出一份包含分数、优点、缺点和建议的 JSON 报告。这里面有一个容易被忽视的设计点评估模块不应该只依赖代码运行结果还应该结合候选人在写代码之前的思考过程。一个候选人虽然最后没有跑通代码但思路清晰、一步一步向正确方向推进和另一个候选人直接乱写一通评估结果应该有明显差异。这正是对话记录需要进入评估模块的原因。4. 环境准备与设计约定以项目官方文档为准本文演示一个独立的最小原型。下面的环境都是通用配置不依赖特定版本重点是讲清整体思路。4.1 运行环境操作系统macOS / Linux / Windows 均可Python 版本建议 3.10 及以上依赖说明使用默认 mock 模式时不需要安装任何第三方库接入真实大模型时需要openaiSDK如果你要接入真实的语音识别和语音合成还需要额外引入 ASR/TTS 相关 SDK本文不展开核心逻辑可以复用。4.2 目录结构miniprepin/ ├── agent.py # 面试 Agent 主逻辑 ├── sandbox.py # Python 代码沙箱执行器 ├── prompts.py # 提示词模板 ├── llm.py # 大模型调用封装默认 mock └── main.py # 命令行模拟面试入口整个原型只依赖 Python 标准库默认运行在 mock 模式目的是把“AI 面试官”的核心链路跑通。你可以把 mock 替换成真实大模型它就是一个最小可用的面试 Agent 骨架。5. 实现一个最小可用的 AI 面试 Agent这一节我们逐个文件实现原型。先写大模型调用封装。5.1 大模型调用封装文件路径miniprepin/llm.py# 文件路径miniprepin/llm.py import os # 默认 mock 模式便于无 API Key 时演示 MOCK_MODE os.getenv(MINIPREPIN_MOCK, 1) 1 def _mock_chat(messages, temperature0.7): 演示用 mock 实现返回固定文本。 真实场景中替换为大模型调用即可。 last_content messages[-1][content] if 请根据以下对话记录 in last_content: return ( {score: 78, summary: 候选人能给出哈希表方案代码可运行 但对边界条件考虑不足。, strengths: [思路正确, 代码结构清晰], weaknesses: [未处理空数组, 缺少复杂度说明], recommendation: 建议进入下一轮人工面试} ) if 请开始面试 in last_content: return ( 你好我是 AI 面试官。请实现 twoSum(nums, target) 要求返回两个下标先讲思路再写代码。 ) if 思路 in last_content: return 好的请把 Python 代码提交过来我会在沙箱中自动运行测试。 return 收到继续。 def create_llm_call(): 根据环境变量选择 mock 或真实大模型调用。 设置 MINIPREPIN_MOCK0 并配置 OPENAI_API_KEY 可切换到真实模型。 if MOCK_MODE or not os.getenv(OPENAI_API_KEY): return _mock_chat from openai import OpenAI client OpenAI() def chat(messages, temperature0.7): response client.chat.completions.create( modelos.getenv(MINIPREPIN_MODEL, gpt-4o-mini), messagesmessages, temperaturetemperature, ) return response.choices[0].message.content return chat这里真正容易踩坑的地方是mock 模式可以跑通流程但它不具备任何“智能”。你问它什么它都按预设分支回答。所以从这个原型切换到真实效果时务必先配置好模型 API并验证一次完整面试流程而不是默认 mock 结果就是真实模型水平。5.2 代码执行沙箱文件路径miniprepin/sandbox.py# 文件路径miniprepin/sandbox.py import os import subprocess import sys import tempfile class PythonSandbox: 使用子进程执行候选人的 Python 代码限制运行时间。 def __init__(self, timeout: int 10): self.timeout timeout def run(self, code: str, stdin_data: str | None None) - dict: with tempfile.NamedTemporaryFile( modew, suffix.py, deleteFalse, encodingutf-8 ) as f: f.write(code) code_path f.name try: proc subprocess.run( [sys.executable, code_path], inputstdin_data, capture_outputTrue, textTrue, timeoutself.timeout, cwdtempfile.gettempdir(), ) return { ok: proc.returncode 0, stdout: proc.stdout[-2000:], stderr: proc.stderr[-2000:], exit_code: proc.returncode, } except subprocess.TimeoutExpired: return { ok: False, stdout: , stderr: Timeout: 代码运行超时, exit_code: -1, } finally: os.unlink(code_path)这个沙箱做了三件事把候选人代码写入临时文件用独立子进程执行超时自动终止返回 stdout、stderr、退出码。要特别说明这是一个教学原型不是生产级安全沙箱。候选人代码仍然运行在你的机器上可以访问临时目录和当前用户权限范围内的资源。真实面试系统必须把代码执行放到 Docker 容器或其他隔离环境中并做资源配额控制。5.3 提示词模板文件路径miniprepin/prompts.py# 文件路径miniprepin/prompts.py SYSTEM_PROMPT 你是一位资深技术面试官负责对候选人进行编程能力面试。 你的职责 1. 根据 candidate_level 和 topic 出一道适合的算法题。 2. 通过提问引导候选人给出思路不要直接给答案。 3. 候选人提交代码后调用 execute_code 工具运行。 4. 根据运行结果和代码质量在面试结束时给出评分。 评分维度思路正确性、代码风格、边界处理、沟通能力。 评分范围0 - 100 分。 EVALUATION_PROMPT 请根据以下对话记录和代码运行结果输出 JSON 格式的面试评估报告。 字段包括score, summary, strengths, weaknesses, recommendation。 不要输出 JSON 以外的内容。 对话记录 {chat_history} 这个提示词设计的核心是“角色明确、流程清晰、输出可解析”。评分维度固定输出格式固定。生产环境中这两段 Prompt 需要持续迭代因为 AI 面试官的行为边界完全由 Prompt 定义。5.4 面试 Agent 主逻辑文件路径miniprepin/agent.py# 文件路径miniprepin/agent.py from dataclasses import dataclass, field from enum import Enum from llm import create_llm_call from prompts import EVALUATION_PROMPT, SYSTEM_PROMPT from sandbox import PythonSandbox class InterviewState(Enum): GREETING greeting QUESTION question WAITING_CODE waiting_code RUNNING_CODE running_code EVALUATING evaluating DONE done dataclass class InterviewSession: candidate_name: str level: str junior topic: str two_sum state: InterviewState InterviewState.GREETING question: str chat_history: list field(default_factorylist) code_submitted: str report: object None class InterviewAgent: def __init__(self, llm_callNone, sandbox: PythonSandbox | None None): self.llm_call llm_call or create_llm_call() self.sandbox sandbox or PythonSandbox(timeout10) self.session None def start_interview(self, name: str, level: str junior, topic: str two_sum): self.session InterviewSession( candidate_namename, levellevel, topictopic, stateInterviewState.QUESTION, ) messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: f请开始面试 {name}水平 {level}主题 {topic}。先问候并出一道题。}, ] self.session.question self.llm_call(messages) self.session.chat_history.append( {role: assistant, content: self.session.question} ) return self.session def receive_message(self, text: str) - str: self.session.chat_history.append({role: user, content: text}) reply self.llm_call( [ {role: system, content: SYSTEM_PROMPT}, *self.session.chat_history[-6:], ] ) self.session.chat_history.append({role: assistant, content: reply}) return reply def submit_code(self, code: str) - dict: self.session.code_submitted code self.session.state InterviewState.RUNNING_CODE result self.sandbox.run(code, stdin_data[1, 2, 3, 4]\n7\n) self.session.chat_history.append( {role: user, content: f候选人提交代码\n{code}} ) self.session.chat_history.append( {role: assistant, content: f代码运行结果{result}} ) return result def generate_report(self) - str: if not self.session.code_submitted: return 还没有收到候选人代码无法生成有效报告。 messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: EVALUATION_PROMPT.format( chat_historyself.session.chat_history )}, ] raw self.llm_call(messages, temperature0) self.session.report raw return raw这段代码里的状态机设计是整个 Agent 的骨架。InterviewState枚举记录了面试进行到哪个阶段后面的每一步都可以根据状态决定行为。真实系统中状态还会更细比如“正在等待候选人粘贴代码”“代码在沙箱中运行中”“评估报告已生成”等。5.5 命令行模拟面试入口文件路径miniprepin/main.py# 文件路径miniprepin/main.py from agent import InterviewAgent def main(): agent InterviewAgent() name input(候选人姓名) agent.start_interview(name, leveljunior, topictwo_sum) print(AI 面试官已启动。) print(命令说明) print( 输入任意文本与 AI 对话) print( 输入 /code 进入写代码模式以 END 结束粘贴) print( 输入 /report 生成评估报告) print( 输入 /quit 退出。) while True: user_input input(你) if user_input /quit: break if user_input /code: print(请粘贴代码输入 END 结束) lines [] while True: line input() if line END: break lines.append(line) result agent.submit_code(\n.join(lines)) print(代码运行结果) print(result) elif user_input /report: print(agent.generate_report()) else: response agent.receive_message(user_input) print(AI, response) if __name__ __main__: main()到这里一个最简单的 AI 面试 Agent 原型已经成型。整个链路是启动面试AI 先问候并出题候选人先讲思路AI 追问候选人进入写代码模式粘贴代码沙箱自动运行代码返回 stdout/stderr生成评估报告。5.6 快速跑通在miniprepin/目录下执行python main.py按提示输入候选人姓名然后与 AI 面试官交互。默认是 mock 模式所以 AI 回答来自预设分支。如果想使用真实大模型export MINIPREPIN_MOCK0 export OPENAI_API_KEY你的_api_key export MINIPREPIN_MODELgpt-4o-mini python main.py6. 运行结果与效果验证6.1 一次完整的 mock 面试假设候选人叫“小明”交互过程如下候选人姓名小明 AI 面试官已启动。 命令说明 输入任意文本与 AI 对话 输入 /code 进入写代码模式以 END 结束粘贴 输入 /report 生成评估报告 输入 /quit 退出。 你这道题我要用哈希表解。 AI 好的请把 Python 代码提交过来我会在沙箱中自动运行测试。 你/code 请粘贴代码输入 END 结束 def two_sum(nums, target): seen {} for i, num in enumerate(nums): rest target - num if rest in seen: return [seen[rest], i] seen[num] i return [-1, -1] END 代码运行结果 {ok: True, stdout: [], stderr: , exit_code: 0} 你/report {score: 78, summary: 候选人能给出哈希表方案代码可运行但对边界条件考虑不足。, strengths: [思路正确, 代码结构清晰], weaknesses: [未处理空数组, 缺少复杂度说明], recommendation: 建议进入下一轮人工面试}这里有一个演示上的细节需要说明mock 面试官在统计两数之和时沙箱传入的 stdin 数据是[1, 2, 3, 4]和7但候选人代码没有读取 stdin所以 stdout 为空。真实面试场景中题目描述必须明确约定输入输出格式或由面试官 Agent 把测试用例预置到代码模板里。6.2 如何判断系统是否正常工作验证这个原型重点关注三点流程是否完整从出题到提交代码再到生成报告所有环节都能走通沙箱是否真实执行了代码给候选人代码人为制造一个错误比如除以零观察 stderr 是否返回异常信息评估报告结构是否可解析报告必须是合法的 JSON且包含 score、summary 等字段。如果你接入了真实大模型还要额外验证一点AI 是否会在没有收到代码时强行评估。从设计上generate_report方法已经加了拦截但真实模型可能主动“脑补”出一份报告。此时需要评估 Prompt 和校验逻辑双重把关。7. 常见问题与排查思路AI 面试系统从原型到可用会遇到的问题往往不在“大模型不够聪明”而在工程细节。下面列几个最常见的坑。问题现象可能原因排查方式解决方案候选人讲了思路AI 仍不断追问同一问题对话状态未切换Prompt 没有携带状态信息打印 chat_history 观察上下文在 Prompt 中明确“候选人已给出思路请追问实现细节”的状态代码提交后 stdout 为空候选人代码没有读取 stdin或题目约定不清晰手动在沙箱执行该代码题目描述中明确输入输出格式或预置测试用例沙箱运行超时候选人代码存在死循环检查 timeout 配置查看进程是否残留设置合理超时生产环境使用容器资源限制评估报告不是合法 JSON大模型输出多了解释性文字查看原始输出在 Prompt 中强制“只输出 JSON”并增加解析失败重试真实语音识别转写错误ASR 模型对专业术语识别不准查看 ASR 转写文本对比原语音增加领域词表或允许候选人重复表述除了表格里的问题还有一类问题需要特别留意AI 面试官必须能够处理“冷场”。真实面试中候选人可能沉默 30 秒、可能说“我不太确定”、也可能直接跑题。系统需要在对话管理中增加超时机制和降级策略。比如候选人 30 秒没有输入AI 可以主动说“你可以先从暴力解法入手再尝试优化”。这在工程上是一个独立的“面试引导模块”不是大模型天然具备的能力。8. 从原型到生产工程落地建议8.1 架构升级方向本文原型把代码执行直接放在本机子进程中生产环境不能这样用。推荐架构是引入容器化执行平台。候选人代码提交后由后端服务拆分为独立的容器任务设置 CPU、内存、网络、运行时间上限容器结束后回收全部资源。同时语音模块需要换成实时音频流。原型中的命令行交互是文本进、文本出真实产品需要 WebRTC 或其他实时音频通道采集候选人语音经过 VAD 切分后送入 ASR再把 Agent 的回复通过 TTS 播放回去。整个链路的延迟预算通常在 3 到 5 秒以内超过这个范围候选人体验会明显下降。8.2 规避 AI 评估幻觉AI 面试官最大的风险是“一本正经地给错误评分”。我的建议是两条腿走路第一评分必须有证据锚点。Prompt 里强制要求 AI 引用对话记录和代码运行结果中的具体内容。比如“你说到用哈希表代码第 5 行实现了 seen 查找”而不是笼统说“思路清晰”。第二评估结果必须人工复核兜底。AI 生成的报告可以作为初筛参考但最终录用决策必须由人类面试官完成。更稳妥的做法是让 AI 报告只给出建议不直接给出“通过/不通过”的结论把决策权留给人。8.3 数据合规与候选人隐私面试过程包含语音、代码、对话记录属于敏感个人数据。在系统上线前至少要解决三件事知情同意面试验证开始前明确告知候选人“本次面试过程将被 AI 记录和分析”数据隔离面试录音和数据只对参与评估的面试官可见存储加密保留期限设定数据保留策略面试结束后定期清理或脱敏。如果候选人明确拒绝 AI 面试应该提供人工面试的替代路径。技术可以提升效率但不能剥夺候选人的选择权。8.4 人机协同的面试流程设计从实践角度看AI 面试官最适合承担“标准化初筛”这一环而不是全流程替代。一个推荐的流程是简历初筛之后AI 面试官先进行一轮 30 分钟左右的语音 live coding 面试AI 输出结构化报告按分数和关键词标记候选人通过初筛的候选人进入人工面试面试官可以参考 AI 报告中的代码和语音记录重点考察报告中标记的弱点人工面试结束后团队再结合 AI 报告做最终决策。这种模式既提升了效率又保留了人类面试官对复杂场景的判断力。AI 面试官的价值不是让面试官失业而是把面试官从重复劳动中解放出来去做更高质量的判断。9. 总结与后续学习方向Prepin 这类“voice live coding 面试工程师”的 AI 系统真正改变的不是面试的形式而是面试这个环节从“经验驱动”走向“工程驱动”。它把出题、追问、代码运行、评估这些环节标准化让一场面试可以被记录、被回放、被度量。如果你打算自己动手实践建议按三步走第一步先跑通本文的最小原型把 Agent 状态机和沙箱执行链路搞清楚 第二步接入真实大模型换成你的 API Key把所有流程用真实模型验证一遍 第三步再逐步加上语音输入输出、容器化沙箱、数据存储和人工复核界面。这个原型只是一个起点但它的核心架构——状态机 对话管理 代码执行 结构化评估——已经覆盖了 AI 面试系统最重要的骨架。后面无论是接入更强大的模型、增加更多编程语言支持还是设计更复杂的评估维度都可以在这个骨架上扩展。