AI 模拟面试平台 — 双视角演练 · RAG 知识库 · 实时辅助
简介 • 功能特性 • 快速开始 • 配置项 • 使用指南 • API • 注意事项
Interview Agent 是一个基于大语言模型的 AI 模拟面试平台。它把「准备面试」这件事拆成两个可自由切换的视角,并结合 RAG 向量知识库与联网搜索,让每一轮对话都带上岗位 JD、简历与知识库的上下文。
| 模式 | 说明 |
|---|---|
🎯 你是面试官(interviewer) |
你扮演面试官向 AI 求职者提问。AI 结合岗位 JD、简历、代码与知识库给出专业回答,适合练习提问技巧、快速验证候选人口径 |
🧑 你是求职者(candidate) |
AI 扮演面试官向你提问。系统按 JD 与简历出题,支持设置时长、推算题量、追踪进度,结束后生成多维度评估报告 |
除模拟演练外,本项目还提供了面向真实面试场景的实时辅助(均基于 Windows 平台能力):
| 能力 | 抓取对象 | 用途 |
|---|---|---|
| 📷 截图答题 | 屏幕画面(Windows Graphics Capture) | 一键截取主显示器,交给视觉模型提取题目并作答 |
| 🎧 系统音频转写 | 扬声器回环(WASAPI Loopback) | 抓取会议软件 / 视频里对方说的话,实时转文字 |
| 🎤 语音输入 / 朗读 | 麦克风(STT)与扬声器(TTS) | 按住说话转文字;AI 回复一键朗读 |
对话与面试
- 🤖 双模式 AI 对话,SSE 流式输出(思维链与正文分通道推送)
- ⏱️ 面试时长推算:按「时长 + 回答长度 + 候选人级别 + 面试轮次」计算题量与环节拆分,支持动态重规划
- 🎓 候选人分级:实习 / 校招 / 社招,自动调整问题难度与数量
- 🔄 面试轮次:一面 / 二面 / HR 面(HR 面强制 30 分钟上限并禁用编程题)
- 🧑💻 编程题模式:按岗位类型从题库选题(仅求职者模式 + 技术岗生效)
- 📝 面试报告:多维度评估(技术能力 / 沟通表达 / 综合素质),支持结构化 QA 逐题评估
- 🗂️ 题库管理:自建题目、从 LeetCode 题库导入、三种使用模式(严格 / 混合 / 灵活改编)
- 💬 会话历史:完整回放历史对话,含思维链与 token 计数
知识与检索
- 📚 RAG 向量知识库:上传 PDF / Word / 文本 / 代码 / 项目压缩包,FAISS 向量检索增强回答
- 🌐 联网搜索:集成 DuckDuckGo,实时补充最新技术资讯
- 🗂️ 岗位管理:一个岗位可挂多份 JD,可指定对话使用哪一份
平台与体验
- 🔐 用户系统:JWT 认证(access + refresh),数据按用户隔离;也可一键切换到单用户免登录模式
- 📊 分析仪表盘:概览统计、进步趋势、薄弱项分析、多次面试对比
- 🎛️ 模型选择:模型列表从官方
GET /models动态获取(不硬编码),支持思考开关与推理强度 - 🔒 安全机制:CORS 白名单、bcrypt 密码哈希、FAISS 索引 SHA-256 完整性校验
- 📐 上下文管理:tiktoken 精确计数 + 智能裁剪,适配超长上下文模型
- 🖥️ 界面定制:7 套配色预设 + 自定义背景 / 文字色,WCAG 对比度提示
- 🪟 独立窗口模式:置顶、透明度、捕获排除、全局 F8 热键,只留一个窗口
- 🐳 多种部署:本地脚本 / Docker Compose / 单容器
🎤 语音功能(STT/TTS)默认全部关闭,需在
.env中主动开启,未开启时对系统零影响。
| 项目 | 要求 |
|---|---|
| Python | 3.11+ |
| Node.js | 18+(构建前端需要;CI 使用 22) |
| DeepSeek API Key | platform.deepseek.com |
| 操作系统 | Windows / macOS / Linux 均可跑核心功能;截图答题、系统音频捕获、独立窗口为 Windows 专属 |
cp .env.example .env编辑 .env,至少填入:
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
JWT_SECRET=请换成一串足够长的随机字符串国内用户:
.env.example已默认配置 HuggingFace 镜像HF_ENDPOINT=https://hf-mirror.com,首次启动下载 Embedding 模型(约 90MB)无需额外操作。也可以完全不填
DEEPSEEK_API_KEY—— 前端支持在界面上直接填写 API Key(仅存本地浏览器),此时界面的 Key 优先级更高。
| 方式 | 命令 | 适用平台 | 说明 |
|---|---|---|---|
| 🪟 独立窗口(推荐 Windows) | start_app.bat 或 python desktop.py |
Windows | 后端 + 前端 + 语音微服务装进一个应用窗口 |
| 📜 本地脚本(开发推荐) | start.bat / ./start.sh |
全平台 | 后端 :8000 + 前端 :5173(热更新) |
| 🐳 Docker Compose | docker-compose up -d |
全平台 | Nginx :80 → 后端 :8000 |
| 📦 单容器 | docker run |
全平台 | 后端直接托管前端构建产物 |
Windows — 双击 start.bat
macOS / Linux
chmod +x start.sh
./start.sh脚本会自动完成:检查 Python / Node → 创建 .env → 安装依赖 → 初始化数据库 → (可选)配置语音 → 启动后端与前端 → 打开浏览器。启动后访问前端 **http://localhost:5173**,API 文档 **http://localhost:8000/docs**。
启动过程中会询问是否启用语音功能(默认 n);选 y 后会继续询问 STT 推理设备(CPU / GPU)。
python desktop.py
# 或经由 main.py 启动(参数自动转发)
python main.py --desktop
python main.py --desktop --no-topmost --width 1366 --height 768桌面版与网页版功能一致,区别只在「形态」:
网页版 start.bat |
桌面版 start_app.bat |
|
|---|---|---|
| 后端 | 独立 cmd 窗口 | 桌面进程内 |
| 前端 | Vite dev(5173,热更新) | 后端托管的 dist 构建产物 |
| 语音微服务 | 各自一个 cmd 窗口 | 桌面进程内,不弹窗 |
| 可见窗口 | 4 个控制台 + 1 个浏览器 | 只有 1 个独立窗口 |
桌面版默认免登录(单用户模式,数据归属本机账号
desktop@local)。想恢复登录页:.env中设AUTH_REQUIRED=true,或start_app.bat --auth-required true。桌面版默认不询问语音开关,是否启用完全由
.env中的VOICE_ENABLED/STT_ENABLED/TTS_ENABLED决定,保持双击即用。控制台启动后会自动隐藏,日志写入
logs/desktop.log。排查问题用start_app.bat --hide-console false保留黑窗口。独立窗口要求先构建前端:
cd frontend && npm run build(start_app.bat会在产物缺失或源码更新时自动重新构建)。
常用启动参数
| 参数 | 默认值 | 说明 |
|---|---|---|
--host / --port |
127.0.0.1 / 8000 |
监听地址与端口 |
--auto-port |
false |
端口被占用时自动向后查找可用端口 |
--reload |
false |
开启热重载(开发用) |
--shell {auto,native,browser} |
auto |
窗口实现方式,见下表 |
--window-title TEXT |
面试 Agent — AI 模拟面试助手 |
窗口标题 |
--width / --height |
1280 / 800 |
窗口尺寸 |
--topmost [BOOL] / --no-topmost |
true |
窗口置顶 |
--opacity N |
1.0 |
窗口透明度 0.2–1.0(也可写 20–100 百分数) |
--zoom N |
1.0 |
界面缩放 0.5–2.0(也可写百分数) |
--capture-exclude [BOOL] |
true |
从截屏 / 录屏中排除窗口(仅 native 真正生效) |
--hide-taskbar [BOOL] |
false |
隐藏任务栏图标 |
--fullscreen [BOOL] |
false |
全屏启动 |
--app PATH |
— | 启动后打开的页面,如 --app /login |
--browser PATH |
— | 【browser 模式】指定浏览器可执行文件 |
--user-data-dir PATH |
~/.interview-agent/desktop-profile |
【browser 模式】浏览器用户数据目录 |
--debug |
false |
【native 模式】开启 WebView 开发者工具 |
--hide-console [BOOL] |
true |
启动后隐藏启动器控制台 |
--auth-required [BOOL] |
false |
是否要求登录;true 恢复登录页 |
--no-console |
false |
不输出启动日志 |
--open-timeout SEC |
90 |
等待服务就绪的最长秒数 |
两种窗口实现
--shell |
内核 | 置顶 | 捕获排除 | 全局 F8 热键 | 依赖 |
|---|---|---|---|---|---|
native(auto 时优先) |
Edge WebView2 | ✅ | ✅ 真正生效 | ✅ 窗口失焦也能触发 | pywebview + WebView2 运行时 |
browser |
系统 Edge / Chrome --app |
✅ | ❌ 系统跨进程限制 | 无(零依赖) |
未安装
pywebview时自动回退到browser模式,功能不受影响(仅捕获排除无效)。为什么
browser做不到捕获排除? Windows 的SetWindowDisplayAffinity只能作用于本进程拥有的窗口;browser模式窗口属于浏览器进程,跨进程调用会被系统拒绝。native模式下窗口由本进程创建,因此截屏 / 录屏 / 直播中该窗口内容不可见。为什么
browser做不到全局热键? 网页 JS 的keydown只在窗口聚焦时才有事件,这是浏览器的安全边界。native模式改由 Python 侧调用 Win32RegisterHotKey注册,系统投递WM_HOTKEY,因此焦点在任何窗口上都能触发。
cp .env.example .env # 编辑填入 DEEPSEEK_API_KEY
docker-compose up -d
docker-compose ps访问 **http://localhost**(Nginx 监听 80 端口)。
轻量部署(无 RAG,适合无 CUDA / 低资源环境)
SKIP_RAG=true RAG_ENABLED=false docker-compose up -d镜像体积减少约 2GB。对话、面试模拟、报告生成等核心功能完全正常,仅知识库上传与 RAG 检索不可用。
语音功能(通过 Profile 启用)
docker compose --profile cpu up # CPU 语音(STT + TTS)
docker compose --profile gpu up # GPU 语音
docker compose --profile stt up # 仅 STT 语音识别
docker compose --profile tts up # 仅 TTS 语音合成
docker compose --profile voice up # 语音全功能首次使用会自动下载模型(Whisper 约 200MB + Piper 约 50MB),由 stt_models / tts_models 卷持久化。
服务与路由架构
Browser → Nginx (:80) ─┬─ /api/* → 反代 → FastAPI (backend:8000)
├─ /api/stt/* → 反代 → STT 服务 (stt:8000)
├─ /api/tts/* → 反代 → TTS 服务 (tts:8000)
└─ 其他路径 → 前端静态文件(SPA 回退 index.html)
常用命令
| 命令 | 说明 |
|---|---|
docker-compose up -d |
启动(标准模式) |
docker-compose down |
停止并删除容器 |
docker-compose down -v |
停止并删除容器 + 数据卷(会清空数据) |
docker-compose logs -f backend |
查看后端日志 |
docker-compose restart backend |
重启后端 |
# 构建(不含 RAG)
docker build --build-arg SKIP_RAG=true -t interview-agent .
# 运行
docker run -d \
--name interview-agent \
-p 8000:8000 \
--env-file .env \
-e RAG_ENABLED=false \
-v interview-data:/app/data \
interview-agent需要 RAG 时去掉
--build-arg SKIP_RAG=true,并加上-e RAG_ENABLED=true与-v interview-chroma:/app/chroma_data。
网页版首次使用需要注册(邮箱 + 密码,密码至少 6 位)。桌面版默认免登录,直接进入。
所有配置通过项目根目录的 .env 文件提供(模板见 .env.example),由 config.py 读取。命令行参数优先于 .env,界面上的选择(模型、思考开关、推理强度)优先于 .env 中的默认值。
| 变量 | 默认值 | 说明 |
|---|---|---|
DEEPSEEK_API_KEY |
— | DeepSeek API Key(可留空,改由界面填写) |
DEEPSEEK_BASE_URL |
https://api.deepseek.com |
API 地址 |
DEEPSEEK_MODEL |
deepseek-v4-pro |
默认模型 |
DEEPSEEK_THINKING_ENABLED |
true |
默认是否启用思考模式 |
DEEPSEEK_REASONING_EFFORT |
high |
推理强度:low(快省)/ high(官方默认)/ max(最充分) |
AVAILABLE_MODELS |
deepseek-v4-pro,deepseek-v4-flash |
仅兜底:正常由官方 GET /models 动态获取 |
MAX_CONTEXT_TOKENS |
1000000 |
上下文窗口总预算(token) |
SYSTEM_RESERVED_TOKENS |
16000 |
为 system 消息预留的 token 数 |
| 变量 | 默认值 | 说明 |
|---|---|---|
RAG_ENABLED |
true |
RAG 检索增强总开关(关闭后对话不注入知识库上下文) |
EMBEDDING_MODEL |
all-MiniLM-L6-v2 |
Embedding 模型名 |
HF_ENDPOINT |
https://hf-mirror.com |
HuggingFace 镜像(国内用户必配) |
HF_HOME |
./hf_cache |
模型缓存目录 |
CHROMA_PERSIST_PATH |
./chroma_data |
向量索引存储根目录(内部为 faiss_indexes/) |
VECTOR_SEARCH_TOP_K |
3 |
每次检索返回的文档块数 |
FAISS_VERIFY_INTEGRITY |
true |
FAISS 索引 SHA-256 完整性校验(生产环境务必开启) |
RAG 需要额外依赖:
pip install -r requirements-rag.txt(faiss-cpu+sentence-transformers+langchain-huggingface)。未安装时应用仍可启动,只是知识库上传与检索不可用(启动日志会给出提示)。
| 变量 | 默认值 | 说明 |
|---|---|---|
AUTH_REQUIRED |
true |
true 需登录且数据按用户隔离;false 为单用户免登录模式 |
JWT_SECRET |
change-me-... |
生产环境必须替换为足够长的随机串 |
JWT_ALGORITHM |
HS256 |
JWT 签名算法 |
JWT_ACCESS_TOKEN_EXPIRE_MINUTES |
30 |
访问令牌有效期(分钟) |
JWT_REFRESH_TOKEN_EXPIRE_DAYS |
30 |
刷新令牌有效期(天) |
FREE_DAILY_INTERVIEW_LIMIT |
10 |
免费用户每日面试次数限制 |
DB_TYPE |
sqlite |
sqlite(开发)/ postgresql(生产) |
DATABASE_URL |
— | 直接提供完整连接串可覆盖以下所有 DB_* 配置 |
DB_HOST / DB_PORT |
localhost / 5432 |
PostgreSQL 地址与端口 |
DB_USER / DB_PASSWORD |
interview / — |
PostgreSQL 账号 |
DB_NAME |
interview_platform |
数据库名 |
DB_POOL_SIZE / DB_MAX_OVERFLOW |
10 / 20 |
连接池大小与溢出上限 |
DB_ECHO |
false |
是否打印 SQL |
SQLite 数据库文件固定在
data/interview_platform.db(使用绝对路径,不受工作目录影响)。
| 变量 | 默认值 | 说明 |
|---|---|---|
HOST / PORT |
127.0.0.1 / 8000 |
监听地址与端口(默认仅本机可访问) |
CORS_ORIGINS |
http://localhost:5173,http://localhost:3000,http://127.0.0.1:5173 |
允许的前端来源,逗号分隔 |
LOG_LEVEL |
INFO |
DEBUG / INFO / WARNING / ERROR |
⚠️ allow_credentials=True时 CORS 不能使用*,必须写具体域名。生产环境请改为实际域名。
| 变量 | 默认值 | 说明 |
|---|---|---|
SCREENSHOT_ENABLED |
true |
截图接口总开关(关闭后路由不注册,前端提示未启用) |
SCREENSHOT_VISION_MODEL |
空 | 视觉模型兜底偏好;留空则自动选账号下第一个支持图片的模型 |
SCREENSHOT_DIR |
screenshots |
截图保存目录 |
SCREENSHOT_MAX_IMAGE_EDGE |
1280 |
上传前图片最长边(像素) |
SCREENSHOT_MAX_TOKENS |
8192 |
单次回答最大 token 数 |
SCREENSHOT_THINKING_ENABLED |
false |
截图问答默认是否启用思考模式 |
SCREENSHOT_SAVE |
true |
是否把每次截图留档到磁盘 |
| 变量 | 默认值 | 说明 |
|---|---|---|
SYSTEM_AUDIO_ENABLED |
false |
总开关(关闭时 /system-audio 路由不注册) |
SYSTEM_AUDIO_DEVICE |
空 | 捕获设备 id;留空用系统默认播放设备 |
SYSTEM_AUDIO_BLOCK_MS |
100 |
每次从回环读取的音频块长度(毫秒) |
SYSTEM_AUDIO_AUTOSTART |
true |
服务启动时是否自动开始捕获 |
SYSTEM_AUDIO_TRANSCRIPT_BUFFER |
200 |
转写结果在内存中保留的条数 |
SYSTEM_AUDIO_STT_TIMEOUT |
30 |
连接 STT 微服务的超时(秒) |
SYSTEM_AUDIO_STT_PING_TIMEOUT |
120 |
STT WebSocket 心跳超时(秒) |
| 变量 | 默认值 | 说明 |
|---|---|---|
VOICE_ENABLED |
false |
语音功能总开关 |
STT_ENABLED |
false |
语音识别(语音转文字) |
TTS_ENABLED |
false |
语音合成(文字转语音) |
STT_MODEL |
base |
Whisper 模型:tiny / base / small / medium / large-v3-turbo |
STT_DEVICE |
cpu |
推理设备:cpu / cuda |
STT_COMPUTE_TYPE |
int8 |
量化类型:int8 / float16 / int8_float16(留空按设备自动:GPU→float16,CPU→int8) |
STT_SERVICE_URL |
http://stt:8000 |
STT 微服务地址(本地运行为 http://localhost:8001) |
STT_WS_URL |
ws://stt:8000/stream |
STT WebSocket 地址 |
STT_DOCKERFILE |
Dockerfile |
Dockerfile(CPU)/ Dockerfile.gpu(GPU) |
VAD_SILENCE_TIMEOUT |
1.0 |
VAD 静默判定超时(秒) |
STT_CHUNK_MAX_SECONDS |
25 |
连续语音硬切上限(秒) |
STT_CHUNK_OVERLAP_SECONDS |
0.5 |
硬切时前后块重叠时长(秒) |
STT_CHUNK_CONTEXT_CHARS |
120 |
跨块上下文长度(字符),注入上一块尾部文本 |
TTS_VOICE |
zh_CN-huayan-medium |
Piper 中文语音模型 |
TTS_SPEED |
1.0 |
语速(0.5–2.0) |
TTS_SERVICE_URL |
http://tts:8000 |
TTS 微服务地址(本地运行为 http://localhost:8002) |
TTS_WS_URL |
ws://tts:8000/stream |
TTS WebSocket 地址 |
| 变量 | 默认值 | 说明 |
|---|---|---|
DESKTOP_MODE |
false |
是否以独立窗口模式启动(desktop.py 会自动设置) |
DESKTOP_WIDTH / DESKTOP_HEIGHT |
1280 / 800 |
窗口尺寸 |
DESKTOP_TOPMOST |
true |
窗口置顶 |
DESKTOP_CAPTURE_EXCLUDE |
true |
从屏幕捕获中排除窗口(仅 native 生效) |
DESKTOP_HIDE_TASKBAR |
false |
隐藏任务栏图标 |
DESKTOP_OPACITY |
1.0 |
窗口透明度 |
DESKTOP_ZOOM |
1.0 |
界面缩放 |
DESKTOP_BROWSER |
空 | 指定浏览器路径(仅 browser 模式),留空自动查找 Edge / Chrome |
创建岗位 → 添加 JD → 上传知识库 → 上传简历/代码 → 切换为「你是面试官」 → 向 AI 求职者提问
- 在岗位管理页创建岗位并添加职位描述(JD)
- 在知识库页上传相关文档(技术规范、FAQ 等)
- 在上传页上传候选人简历(PDF / Word)与代码文件
- 进入 AI 对话页,选择 🎯 你是面试官 模式
- 向 AI 求职者提问,AI 会结合 JD、简历、代码与知识库回答
- 结束后前往面试报告页生成评估
创建岗位 → 添加 JD → 上传知识库 → 上传简历 → 设置时长 → 推算题数 → 开始练习 → 回答 → 生成报告
- 创建目标岗位并填写 JD
- 上传知识库文档与你的简历
- 进入 AI 对话页,选择 🧑 你是求职者 模式
- 选择面试时长(15 / 30 / 45 / 60 分钟),点击 推算 获取预估题量
- 点击 开始模拟练习,AI 面试官逐题提问
- 在输入框回答,练习中实时显示进度(第 N/M 题)
- 完成后点击 结束练习,前往面试报告页生成多维度评估
- 截图答题:在 AI 对话页点击输入框右侧的 截图 按钮,或直接按
F8。截图会连同输入框中的补充要求一起发给模型,回答直接渲染在对话里。 - 系统音频转写:在设置 → 监听系统音频中开启(或设
SYSTEM_AUDIO_ENABLED=true),即可把电脑正在播放的声音实时转成文字。 - 语音输入 / 朗读:启用语音功能后,输入框出现录音按钮;AI 回复下方出现朗读按钮。
在对话中选择题库题目时,可指定 AI 的出题策略:
| 模式 | 说明 |
|---|---|
strict(严格) |
AI 必须且只能逐题照读题库题目,不可修改、跳过或自编 |
mixed(混合,默认) |
题库题目为必考题,全部问到;可自行补充不超过 2 道追问 |
adaptive(灵活改编) |
题库作为参考,可调整措辞、难度与顺序,核心考察点不变 |
{
"messages": [{"role": "user", "content": "请介绍一下你自己"}],
"mode": "interviewer",
"position_name": "前端工程师",
"jd_id": "jd_001",
"use_search": false,
"coding_enabled": false,
"model": "deepseek-v4-pro",
"thinking_enabled": true,
"reasoning_effort": "high",
"api_key": "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"candidate_level": "experienced",
"interview_round": "first",
"interview_duration_minutes": 30,
"interview_question_count": 10,
"question_bank_ids": [],
"question_bank_mode": "mixed"
}| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
mode |
string |
null |
"interviewer" 面试官 / "candidate" 求职者 |
position_name |
string |
null |
关联岗位,同时触发 RAG 检索(为空则不检索) |
jd_id |
string |
null |
指定使用某份 JD(为空则使用全部 JD) |
use_search |
bool |
false |
是否启用联网搜索 |
coding_enabled |
bool |
false |
是否启用编程题(仅 mode="candidate" 时生效) |
model |
string |
null |
覆盖默认模型 |
thinking_enabled |
bool |
null |
覆盖默认思考开关 |
reasoning_effort |
string |
null |
"low" / "high" / "max" |
api_key |
string |
null |
前端传入的 Key,优先级高于 .env |
resume_text |
string |
null |
简历文本 |
code_context |
string |
null |
代码文本(仅面试官模式使用) |
candidate_level |
string |
null |
"intern" / "new_grad" / "experienced" |
interview_round |
string |
null |
"first" / "second" / "hr" |
interview_duration_minutes |
int |
30 |
面试总时长,影响时间预算感知 |
interview_question_count |
int |
0 |
计划题目数量(0 表示按默认 8 题) |
interview_coding_min |
int |
0 |
编程题预留时间(分钟) |
prompt_notes |
string |
null |
界面 PromptEditor 的补充说明,追加到 system prompt 末尾 |
question_bank_ids |
string[] |
null |
从题库中选定的题目 ID 列表 |
question_bank_mode |
string |
"mixed" |
题库使用模式:strict / mixed / adaptive |
点击界面的 推算 按钮(或调用 POST /interview/plan)会根据面试时长、回答长度、候选人级别、面试轮次推算题量,并拆分为自我介绍、技术问答、编程题、反问四个环节。
- 题量会限制在 2 – 30 题之间,HR 面强制 30 分钟上限并禁用编程题。
- 练习过程中系统会动态重规划,根据已用时间与已答题数调整剩余题量与当前阶段。
后端路由同时注册了两套:无前缀(/chat/stream)与 /api 前缀(/api/chat/stream)。这就是为什么开发模式下 Vite 会把 /api 前缀剥离后转发到 :8000。
完整交互式文档:启动后访问 **http://localhost:8000/docs**(Swagger)或 /redoc。
| 模块 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 对话 | GET |
/chat/models |
可用模型列表(?refresh=true 强制刷新缓存) |
| 对话 | POST |
/chat/stream |
SSE 流式对话 |
| 面试 | POST |
/interview/start |
开始面试(组装 system prompt) |
| 面试 | POST |
/interview/stop |
停止面试 |
| 面试 | POST |
/interview/report |
生成报告(支持结构化 QA 逐题评估) |
| 面试 | POST |
/interview/plan |
面试时长推算(含阶段拆分与动态重规划) |
SSE 事件格式:每帧为 data: {"type": "...", "content": "..."},type 取值为 reasoning(思维链)/ content(正文)/ error;流结束发送 data: [DONE]。
| 模块 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 岗位 | GET POST |
/positions |
列表 / 创建岗位 |
| 岗位 | GET PUT DELETE |
/positions/{name} |
岗位详情 / 更新 / 删除 |
| 岗位 | POST PUT DELETE |
/positions/{name}/jds |
JD 管理(一个岗位可挂多份 JD) |
| 题库 | GET POST |
/question-bank/ |
题目列表 / 新增题目 |
| 题库 | GET PUT DELETE |
/question-bank/{id} |
题目详情 / 更新 / 删除 |
| 题库 | POST |
/question-bank/import/leetcode |
从 LeetCode 题库批量导入 |
| 题库 | GET |
/question-bank/categories/list |
分类列表 |
| 上传 | POST |
/upload/resume |
上传简历 |
| 上传 | POST |
/upload/code |
上传代码 |
| 上传 | POST |
/upload/project |
上传项目(解析目录结构与技术栈) |
| 上传 | GET DELETE |
/upload/files |
上传记录列表 / 清空 |
| 上传 | GET DELETE |
/upload/files/{id} |
单条记录详情 / 删除 |
| 知识库 | POST |
/knowledge/upload |
上传文档并建索引 |
| 知识库 | GET |
/knowledge/collections |
列出所有知识库 |
| 知识库 | GET |
/knowledge/search |
向量检索(top_k 取值 1–20) |
| 知识库 | DELETE |
/knowledge/collections/{name} |
删除知识库 |
| 知识库 | GET POST |
/knowledge/rag-status rag-toggle |
查询 / 切换 RAG 开关(运行时) |
| 模块 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 会话 | GET |
/sessions/ |
历史会话列表 |
| 会话 | GET |
/sessions/{id} |
会话详情 + 消息回放 + QA 记录 + 报告 |
| 会话 | DELETE |
/sessions/{id} |
删除会话 |
| 分析 | GET |
/analytics/dashboard |
概览仪表盘 |
| 分析 | GET |
/analytics/progress |
进步趋势(7d / 30d / 90d) |
| 分析 | GET |
/analytics/weakness |
薄弱项分析与改进建议 |
| 分析 | GET |
/analytics/comparison |
多次面试对比 |
| 分析 | GET |
/analytics/stats |
统计汇总 |
| 认证 | GET |
/auth/mode |
查询当前认证模式(无需 token) |
| 认证 | POST |
/auth/register login refresh |
注册 / 登录 / 刷新令牌 |
| 认证 | GET PUT |
/auth/me |
当前用户信息 / 更新资料 |
| 模块 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 截图 | GET |
/screenshot/info |
截图能力、显示器与视觉模型信息 |
| 截图 | POST |
/screenshot/capture |
截取主显示器并交给视觉模型识别作答 |
| 系统音频 | GET |
/system-audio/info devices status |
能力 / 设备列表 / 运行状态 |
| 系统音频 | POST |
/system-audio/start stop clear |
开始 / 停止捕获 / 清空转写 |
| 系统音频 | GET |
/system-audio/transcript |
增量拉取转写结果(?since=<seq>) |
| 语音 | GET |
/stt/health /tts/health |
微服务健康检查 |
| 语音 | POST |
/stt/transcribe /tts/synthesize |
批量识别 / 合成(兜底接口) |
截图与系统音频路由按开关条件注册:
SCREENSHOT_ENABLED=false或SYSTEM_AUDIO_ENABLED=false时路由不存在。此时/api/*的 404 会返回结构化 JSON 提示(说明「多半是功能开关未开启」),而不是让前端拿到 HTML 去JSON.parse报出难以理解的错误。
在 AI 对话页底部输入框右侧点击 「截图」 按钮(或按 F8),会截取主显示器画面,交给视觉模型提取题目并作答,回答直接显示在对话里。
前置条件
| 项目 | 要求 |
|---|---|
| 操作系统 | Windows 10 2004(build 19041)及以上 |
| Python 依赖 | windows-capture(已随 requirements.txt 安装) |
| 模型 | 必须支持图片输入 |
使用要点
- 截图使用界面模型选择器中选中的模型;不支持图片输入时会直接提示,不会把图片发给纯文本模型(纯文本模型收到图片不会报错,而是编造答案)。
- 输入框里可以先写一句要求(例如「只给答案,不要解释」),截图时会一并作为提问发出,所以可以「先打好要求,再按 F8」。
- 独立窗口模式下 F8 是系统级全局热键,窗口失焦也能触发(这正是截图场景的关键);浏览器模式下 F8 仅在窗口聚焦时生效。
- 独立窗口模式下,截图前会自动把本应用从屏幕捕获中排除,不会把自己拍进去。
- 全局热键被其它程序占用时会自动退化为窗口内快捷键。
关于思考模式:思维链与正文共享
max_tokens预算,截图答题若开启思考容易导致正文被截断,因此默认关闭。若回答仍被截断,界面会明确标注。
与截图答题对称:截图抓「屏幕上看到的」,系统音频抓「扬声器里放的」。面试时对方的提问往往来自会议软件 / 视频 / 网页播放,麦克风录不到,这里通过 Windows WASAPI 回环直接从扬声器设备抓取并实时转文字。
启用前提(三者缺一不可)
SYSTEM_AUDIO_ENABLED=true(否则接口不注册);VOICE_ENABLED=true或STT_ENABLED=true,且 STT 微服务已启动(docker compose --profile stt up,或本地跑stt_service);STT_SERVICE_URL指向该服务(本地运行为http://localhost:8001)。
仅支持 Windows。STT 未就绪时捕获仍会运行,只是不产生文字,状态里会说明原因,STT 起来后会自动重连。
pytest tests/ -v测试标记(pytest.ini):
| 标记 | 含义 |
|---|---|
windows_only |
依赖 Windows 专有依赖(windows-capture / soundcard / pywebview),在 Linux CI 上跳过 |
slow |
需要真实时间等待的用例 |
CI(.github/workflows/ci.yml)在 Ubuntu 上跑 Python 3.11 / 3.12 的后端测试,以及前端 npm run build(含 tsc 类型检查)。前端 Lint 目前不阻断构建(仓库存在历史 lint 问题)。
| 组件 | 技术 |
|---|---|
| Web 框架 | FastAPI + Uvicorn |
| LLM | DeepSeek V4 Pro / Flash(OpenAI 兼容协议) |
| 流式输出 | Server-Sent Events (SSE) |
| 数据校验 | Pydantic v2 |
| ORM | SQLAlchemy 2.0 Async(SQLite / PostgreSQL 双模式) |
| 认证 | python-jose (JWT) + passlib/bcrypt |
| RAG 管线 | LangChain LCEL + FAISS |
| Embedding | sentence-transformers (all-MiniLM-L6-v2) |
| Token 计数与裁剪 | tiktoken (cl100k_base) |
| 文档解析 | PyMuPDF + python-docx |
| 联网搜索 | DuckDuckGo Search |
| STT | faster-whisper(CTranslate2)+ Silero VAD |
| TTS | Piper TTS |
| 组件 | 技术 |
|---|---|
| 框架 | React 19 + TypeScript 6 |
| 构建工具 | Vite 8 |
| UI 库 | Ant Design 6 |
| 路由 | React Router 7 |
| 状态管理 | Zustand 5 |
| Markdown | react-markdown + remark-gfm + react-syntax-highlighter |
- 首次启动会下载模型。RAG 的 Embedding 模型约 90MB(已默认走镜像);语音模型约 250MB(Whisper + Piper)。请保持网络畅通。
- 端口占用:后端
8000、前端5173、本地语音8001(STT)/8002(TTS)、Docker 前端80。start.bat会在启动前检查端口并打印占用进程的 PID 与处置命令;desktop.py可用--auto-port自动顺延。 .env不入库(.gitignore已排除),请勿把含密钥的.env提交到版本库。JWT_SECRET务必替换默认值。默认值是公开的占位字符串,生产环境使用它等于没有签名保护。- CORS 不能用
*。因为启用了allow_credentials=True,必须写成具体域名;生产环境请把CORS_ORIGINS改为实际域名。 - 单用户模式不是「关掉一个提示」。开启后(
AUTH_REQUIRED=false),岗位 / 题库 / 会话 / 上传 / 分析等原本要求 token 的接口都会自动使用固定的本机账号desktop@local,因此桌面版能与网页版拥有一致的完整功能。该账号会在建表时插入,其hashed_password为不可逆占位值,不能用于密码登录。 - SQLite 适合单机使用。多用户 / 高并发场景请切换到
DB_TYPE=postgresql并配置DB_*。
- RAG 依赖很重(
sentence-transformers会拉入torch,约 2GB)。不需要知识库时,建议设置RAG_ENABLED=false并不安装requirements-rag.txt;Docker 部署用SKIP_RAG=true。 - 未安装 RAG 依赖但
RAG_ENABLED=true时应用仍能启动,但知识库的上传 / 列表 / 检索会返回 503;而上传文件的索引与对话中的 RAG 注入会静默跳过 —— 容易误判为「功能正常但检索不到内容」。 FAISS_VERIFY_INTEGRITY生产环境请保持开启。索引的index.pkl使用 pickle 序列化,本模块用 SHA-256 校验防止被篡改。校验失败时,确认可信后删除对应的.sha256文件再重新加载即可。- 知识库目录名会被改写。岗位名中的中文等字符会被替换为下划线并加
kb_前缀(例如「前端工程师」→kb__kb),这是正常的,界面仍显示原始岗位名。 /knowledge/rag-toggle只在当前进程内生效,重启后恢复.env中的配置。- 模型列表来自远端。
GET /models不可用时回退到.env的AVAILABLE_MODELS,此时所有模型都被标记为不支持图片,截图功能会提示不可用。 - 编程题选题带随机性,同样的输入多次调用可能选出不同题目。
/chat/stream只读取请求体里的api_key,不读取X-DEEPSEEK-API-KEY请求头(/chat/models两者都读)。若只通过请求头传 Key,对话会静默回退到.env的DEEPSEEK_API_KEY。code_context字段目前不生效,代码与项目上下文统一走 RAG 向量检索提供。position_name同时是 RAG 的触发条件:传入岗位名才会检索该岗位的知识库。- 岗位名有字符限制:2–50 个字符,仅允许字母、数字、中文、下划线、连字符(不允许空格、点、斜杠)。同一用户下重名返回 409。
position_type在岗位创建时判定后不再变化。「技术支持」「设计 / UI / UX」被归为非技术岗,此类岗位下编程题不会启用。/interview/report失败时返回 HTTP 200,正文为报告生成失败: ...,前端需自行识别。
| 接口 | 大小上限 | 接受格式 | 超限错误 |
|---|---|---|---|
/upload/resume |
5MB | .pdf .docx .doc .txt |
413 |
/upload/code |
2MB | .py .js .ts .jsx .tsx .java .go .cpp .c .h .hpp .rs .rb .php .swift .kt .scala .cs .vue .html .css .scss .sql .sh .bash .yaml .yml .json .xml .md .r .m |
413 |
/upload/project |
50MB | .zip .tar.gz .tgz .tar.bz2 .tar.xz .tar .7z(.7z 需 py7zr) |
413 |
/knowledge/upload(doc_type=faq) |
10MB | 同简历格式(不支持 .md) |
413 |
/knowledge/upload(doc_type=code) |
10MB | 同代码格式 | 413 |
/knowledge/upload(doc_type=project) |
50MB | 同压缩包格式 | 413 |
其他约定:
- 落库文本会截断至 100KB,但上传接口响应中返回的是完整文本。也就是说:刚上传时前端拿到全文,之后重新读取时只能拿到 100KB 版本。
- 知识库检索的
top_k仅接受 1–20,越界时静默回退为 3。 - 文件解析失败返回 422,扩展名不支持返回 400。
- 只有代码与项目会被索引进向量库(简历不索引)。
- 项目解析会自动跳过系统垃圾文件,并推断技术栈(Python / React / Java / Go / Rust / Docker 等)。
| 功能 | 依赖 | 缺失时的表现 |
|---|---|---|
| 截图答题 | windows-capture |
接口返回明确的安装指引,不抛 500 |
| 系统音频捕获 | soundcard(WASAPI 回环) |
明确提示「仅支持 Windows」 |
| 独立窗口 | pywebview + WebView2 运行时 |
自动回退到浏览器 --app 窗口(捕获排除失效) |
这些依赖在
requirements.txt中带平台标记,非 Windows 平台会自动跳过安装,相关功能不可用但服务可正常运行。
- 必须存在前端构建产物,否则
/会退化为 JSON 健康信息。先执行cd frontend && npm run build。 --capture-exclude仅在 native 模式真正生效,browser 模式受 Windows 跨进程限制无法生效(界面上该开关显示为禁用)。--opacity下限为 20%,避免窗口过淡导致无法操作。- 窗口项(置顶 / 透明度 / 缩放 / 捕获排除 / 隐藏任务栏)均可在界面「设置」面板中运行中实时调整,启动参数只决定初始状态。
- 三个开关默认全为
false,未启用时接口不注册、依赖不加载,对系统零影响。 - 本地运行时,
STT_SERVICE_URL/TTS_SERVICE_URL需指向http://localhost:8001/http://localhost:8002;Docker 内部网络则是http://stt:8000/http://tts:8000。start.bat会自动设置前者。 STT_DEVICE=cuda需要 CTranslate2 的 CUDA 运行库(cuBLAS / cuDNN),与torch无关;缺少时会自动回退 CPU 并在日志中告警。- Whisper 模型选择:
tiny/base体积小但中文准确率偏低,建议使用large-v3-turbo。 - 首次转录时 STT 需要加载模型,因此心跳与连接超时设置得比较宽松,避免误报「连不上」。
- API Key 保存在浏览器 localStorage 中(明文)。界面已提示「仅保存在本地浏览器中,不会上传到服务器」,但共享机器上仍需注意。清除方式:把界面的 API Key 输入框留空并确认。
- 登录令牌同样存在 localStorage,收到 401 时会自动刷新。
- 对话消息只保留最近 100 条(两种模式各自独立),QA 记录保留最近 30 条。
- 刷新页面会重置练习进度,需要重新点击「开始模拟练习」。
- 不要使用前端 dev server 作为生产部署方式。
start.bat/start.sh启动的是带热更新的 dev server;Docker 与独立窗口模式才使用构建产物。
start.bat/start.sh每次启动都会重装 Python 依赖,每次都会联网耗时。start.bat/start.sh会交互询问是否启用语音;start_app.bat则完全从.env读取,不提问。- 语音相关的安装与设备选择在
start.bat中只在当前窗口会话内生效,不会写回.env。想让配置长期生效,请手动写入.env。 - 脚本只检查「是否安装了 Node」,并未校验版本。由于前端使用 Vite 8,建议使用 Node 20+。
start_app.bat接受并透传任意参数给desktop.py,例如start_app.bat --width 1600 --height 900 --no-topmost。
/api/*返回 404 且带「功能开关未开启」提示时,说明该功能的路由压根没注册 —— 检查对应的.env开关并重启服务。- 排查前端拿到 HTML 导致的
Unexpected token '<'报错:本项目已在应用级把/api/*的 404 兜底为结构化 JSON,若仍见到该报错,请确认请求路径是否真的以/api开头。 - 桌面版日志在
logs/desktop.log(控制台会被自动隐藏)。 - 依赖装不上时优先检查网络;国内环境请确认
HF_ENDPOINT未被改动。 - HuggingFace 下载卡在 0 字节:
config.py已默认设置HF_HUB_DISABLE_XET=1。新版huggingface_hub默认启用 Xet 存储后端,其域名不受HF_ENDPOINT影响,在国内会出现「元数据全部 200,但权重停在 0 字节」的现象。该变量必须在任何 HF 相关库导入之前注入,因此写在config.py顶部。
欢迎提交 Issue 和 Pull Request。
- Fork 本项目
- 创建特性分支 (
git checkout -b feature/amazing-feature) - 提交更改 (
git commit -m 'feat: add amazing feature') - 推送分支 (
git push origin feature/amazing-feature) - 创建 Pull Request
MIT © 2025