从零搭建 AI 智能体陪练系统:基于 FastAPI + DeepSeek-V3 的题库生成 MVP 全记录
从零搭建 AI 智能体陪练系统基于 FastAPI DeepSeek-V3 的题库生成 MVP 全记录一个周末一行代码都不浪费——用 OpenAI 兼容接口调用国产大模型打造可落地的 AI 出题引擎。一、项目背景与定位在 CTFCapture The Flag安全竞赛培训场景中有一个持续存在的痛点出题成本极高。一道高质量的 CTF 题目从构思环境、编写 Dockerfile、埋设 Flag 到撰写题解往往需要数小时甚至数天。对于培训方而言手动出题根本跟不上学员的练习节奏。于是就有了这个项目的核心命题能不能让 AI 来出题但这里有一个现实约束——很多开发者没有 OpenAI 的 API Key信用卡门槛、地区限制、费用顾虑却拥有国产模型平台的 API 额度硅基流动、DeepSeek 官方、阿里百炼等。于是技术方案聚焦为一个验证性问题用 OpenAI SDK 国产大模型 API硅基流动 / DeepSeek-V3能不能搭出一套可用的 AI 出题系统答案是完全可以而且比想象中简单得多。本文将完整记录tiku_mvp从零搭建的全过程包括后端架构、前端交互、模型选型、部署配置以及开发过程中踩过的坑和解决方案。二、系统架构总览┌─────────────────────────────────────────────────────┐ │ Frontend (5501) │ │ vanilla HTML/CSS/JS │ │ http://127.0.0.1:5501/index.html │ └────────────────────┬────────────────────────────────┘ │ HTTP REST (CORS) ▼ ┌─────────────────────────────────────────────────────┐ │ Backend (8000) │ │ FastAPI Uvicorn │ │ │ │ POST /generate ──▶ _call_openai_json() ──▶ LLM │ │ GET /history ──▶ _read_history_raw() │ │ GET /history/{id} │ └────────────────────┬────────────────────────────────┘ │ OpenAI SDK (base_url override) ▼ ┌─────────────────────────────────────────────────────┐ │ SiliconFlow API (OpenAI 兼容) │ │ Model: deepseek-ai/DeepSeek-V3 │ └─────────────────────────────────────────────────────┘核心设计原则前端零框架依赖纯 HTML/CSS/JS一个index.html搞定无需 npm/webpack后端极简FastAPI 3 个 REST 端点 本地 JSON 文件持久化模型可替换通过base_url 环境变量任何 OpenAI 兼容接口都能即插即用三、技术栈详解3.1 后端FastAPI OpenAI SDK# backend/main.py 核心依赖fromfastapiimportFastAPIfromopenaiimportOpenAIfromdotenvimportload_dotenvimportuvicorn选择 FastAPI 的理由原生异步支持与 Uvicorn 搭配性能优秀自动生成 Swagger 文档/docs调试体验极佳Pydantic 数据校验请求/响应格式一目了然3.2 大模型DeepSeek-V3通过硅基流动# backend/.envOPENAI_API_KEYsk-xxxxxxxx# 硅基流动 API KeyOPENAI_MODELdeepseek-ai/DeepSeek-V3# main.py 第 28 行clientOpenAI(api_keyOPENAI_API_KEY,base_urlhttps://api.siliconflow.cn/v1# 关键覆写 base_url)为什么选择硅基流动完全兼容 OpenAI SDK一行base_url替换即可提供 DeepSeek-V3 等国产模型无需 OpenAI 账号国内网络直连延迟低3.3 前端原生 HTML JavaScript!-- frontend/index.html — 单文件应用 --scriptconstAPI_BASEhttp://127.0.0.1:8000;// 表单提交 → fetch /generate → 渲染题目 → fetch /history → 历史列表/script前端采用 SPA单页应用思路没有任何构建工具学科选择难度选择easy/medium/hard逐题作答一次生成 5 题每次展示 1 题答完切换下一题历史题库左侧卡片列表点击可回顾任意历史题集四、核心代码走读4.1 Prompt 工程_build_promptdef_build_prompt(subject:str,difficulty:str)-List[dict]:sys{role:system,content:(You are a test item writer. Return exactly 5 multiple-choice questions as strict JSON (utf-8), with keys: items:[{id,subject,difficulty,stem,choices:[{key,text}],answer,explanation}]. Choices must be A,B,C,D; answer must be one of A-D. Keep stems concise; explanations correct.)}user{role:user,content:(fSubject:{subject}\nDifficulty:{difficulty}\nWrite 5 diverse multiple-choice questions covering the core knowledge points.)}return[sys,user]设计要点强约束 JSON 输出通过response_format{type: json_object}确保结构化选项规范化强制 4 选项 A-D前端 UI 依赖此约定解释必填每道题都要求explanation提升学习价值4.2 调用 容错_call_openai_jsondef_call_openai_json(subject:str,difficulty:str)-List[QAItem]:respclient.chat.completions.create(modelOPENAI_MODEL,messagesmessages,response_format{type:json_object},temperature0.7,)contentresp.choices[0].message.content datajson.loads(content)itemsdata.get(items,[])# 兜底不足 4 选项自动补全foritinitems[:5]:choicesit.get(choices,[])iflen(choices)!4:keys[A,B,C,D]fixed[]fori,kinenumerate(keys):ifilen(choices)andisinstance(choices[i],dict):fixed.append(choices[i])else:fixed.append({key:k,text:fOption{k}})choicesfixed...容错策略选项数量不匹配→ 自动用 A/B/C/D 补全不足 5 题→ 抛出 RuntimeError 提示重试JSON 解析失败→ 透传异常给前端展示4.3 REST API 设计端点方法功能/generatePOST接收{subject, difficulty}返回 5 道选择题 存入 history.json/historyGET返回所有历史题集摘要学科、难度、题数、前 3 题预览/history/{batch_id}GET返回指定题集的完整内容含答案和解析4.4 数据持久化HISTORY_PATHDATA_DIR/history.jsondef_write_history_raw(data:List[dict]):HISTORY_PATH.write_text(json.dumps(data,ensure_asciiFalse,indent2),encodingutf-8)MVP 阶段使用本地 JSON 文件持久化每次/generate调用后insert(0, record)写入文件头部最新题集在前。对于单用户本地使用场景完全够用未来可平滑迁移到 SQLite 或 PostgreSQL。五、前端交互设计5.1 逐题作答流[选择学科难度] → [点击生成] → [展示第1题] ↓ [选择选项A/B/C/D] ↓ [提交答案] → [揭晓答案解析] ↓ [下一题] → [展示第2题] ↓ ... 重复 ... ↓ [第5题完成] → [显示总分 5/5]核心 JS 逻辑// mountQuestion(i) — 每次只渲染 1 题functionmountQuestion(i){constitemcurrentBatch.items[i];currentIndexi;selectedKeynull;nextBtn.style.displaynone;submitBtn.style.displayinline-block;stemEl.textContentitem.stem;choicesEl.innerHTML;item.choices.forEach(ch{constdivdocument.createElement(div);div.classNamechoice;div.textContent${ch.key}.${ch.text};div.dataset.keych.key;div.addEventListener(click,(){selectedKeych.key;[...choicesEl.children].forEach(nn.classList.remove(selected));div.classList.add(selected);});choicesEl.appendChild(div);});}5.2 答案标色逻辑functionrevealAnswer(){constitemcurrentBatch.items[currentIndex];[...choicesEl.children].forEach(n{constkn.dataset.key;if(kitem.answer){n.classList.add(correct);// 正确答案 → 绿色}if(selectedKeykselectedKeyselectedKey!item.answer){n.classList.add(wrong);// 选错 → 红色}});}视觉效果正确答案绿色高亮如果用户选错则红色标记自己的选择对比一目了然。六、开发踩坑实录6.1 CORS 跨域问题最耗时现象前端http://127.0.0.1:5501访问后端http://127.0.0.1:8000时浏览器报错Access to fetch at http://127.0.0.1:8000/history from origin http://127.0.0.1:5501 has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource.排查过程第一反应检查main.py是否有 CORS 中间件 → 有allow_origins[*]怀疑规范冲突allow_credentialsTrueallow_origins[*]在 CORS 规范中不被允许 → 改为显式[http://127.0.0.1:5501, http://localhost:5501]改了还是不行→ 用curl直接测试发现 CORS 头正常返回最终根因lsof -i :8000发现/tmp/ctf-agent-server进程抢占 8000 端口所有请求被它拦截解决方案kill61235# 停掉 ctf-agent-serverkill98899895# 停掉旧的 main.py 进程python main.py# 重新启动教训CORS 中间件配置正确但仍然报错时优先排查端口是否被其他进程抢占而非反复改中间件配置。lsof -i :PORT是排查利器。6.2 浏览器缓存导致问题残留现象CORS 修好后无痕模式正常正常模式依然报错。原因浏览器缓存了之前失败的 OPTIONS 预检响应。解决Cmd Shift Delete清除浏览器缓存或 DevTools → Network → Disable cache。6.3 模型替换从 DeepSeek API 到硅基流动项目原本base_url指向https://api.deepseek.com需要 DeepSeek 官方的 API Key。迁移到硅基流动只需改动两处- base_urlhttps://api.deepseek.com base_urlhttps://api.siliconflow.cn/v1 - OPENAI_MODELdeepseek-chat OPENAI_MODELdeepseek-ai/DeepSeek-V3模型名称前缀规则{提供商}/{模型名}。硅基流动支持的模型列表可在其控制台查看。6.4 依赖安装注意如果使用.venv虚拟环境务必先source .venv/bin/activate然后在 venv 内执行pip install -r requirements.txt。全局安装的包不会被 venv 内的 Python 解释器识别。七、部署指南7.1 环境准备# 1. 克隆项目cdtiku_mvp# 2. 创建虚拟环境推荐python-mvenv tiku_venvsourcetiku_venv/bin/activate# macOS# tiku_venv\Scripts\activate # Windows# 3. 安装依赖pipinstall-rrequirements.txt7.2 配置 API Key编辑backend/.envOPENAI_API_KEYsk-your-siliconflow-api-key OPENAI_MODELdeepseek-ai/DeepSeek-V37.3 启动服务# 终端 1启动后端cdbackend python main.py# → http://127.0.0.1:8000/docs 可查看 API 文档# 终端 2启动前端cdfrontend python-mhttp.server5501# → http://127.0.0.1:5501/index.html7.4 验证打开http://127.0.0.1:5501/index.html选择学科和难度点击「生成题库」应该能看到 AI 生成的 5 道选择题。八、项目结构与文件清单tiku_mvp/ ├── backend/ │ ├── main.py # FastAPI 后端242 行 │ ├── .env # API Key 和模型配置 │ └── data/ │ └── history.json # 题库历史记录自动生成 ├── frontend/ │ └── index.html # 前端单页应用289 行 ├── requirements.txt # Python 依赖 └── readme.txt # 部署说明总代码量约 530 行非常适合作为 AI 应用开发的入门参考项目。九、系统截图9.1 首页包含学科/难度选择表单、生成按钮、历史题库卡片列表。9.2 答题界面AI 生成的题目4 个选项可点击选择顶部进度条显示当前进度。9.3 答案揭晓提交后显示正确答案绿色和详细解析如果选错会红色标记。9.4 API 文档FastAPI 自动生成的 Swagger UI可直接在线调试接口。9.5 项目目录极简的目录结构核心文件仅 4 个。十、总结与展望10.1 核心收获OpenAI 兼容接口的生态价值国产大模型平台普遍提供 OpenAI 兼容 API这意味着换个base_url就能复用整个 OpenAI SDK 生态迁移成本几乎为零。FastAPI 的极致开发体验自动生成的/docs页面让前后端联调变得轻松Pydantic 的类型校验省去了大量参数检查代码。MVP 思维本地 JSON 文件做持久化、单文件 HTML 做前端——这些简陋的选择在原型验证阶段反而是最高效的。10.2 未来扩展方向数据库迁移从 JSON 文件升级到 SQLite支持多用户、题目搜索、统计分析出题质量优化引入 Few-shot 示例 难度校准减少模型生成水题的概率题型扩展支持判断题、填空题、简答题用户系统接入 OAuth支持多用户各自维护题库进度CTF 专项出题从通用题库转向 CTF 场景支持 Web/Misc/Crypto/Reverse/Pwn 五个方向的定向出题技术栈FastAPI SiliconFlow API (DeepSeek-V3) Vanilla JS

相关新闻

第7章:页面静态化-02-生成移动端静态页面

第7章:页面静态化-02-生成移动端静态页面

5. 生成移动端静态页面 前面我们已经学习了Freemarker的基本使用方法,下面我们就可以将Freemarker应用到项目中,帮我们生成移动端套餐列表静态页面和套餐详情静态页面。接下来我们需要思考几个问题: (1)什么时候生成…

2026/10/7 5:16:23 阅读更多 →
npm-g_nosudo:彻底告别Ubuntu下npm全局安装sudo依赖的终极解决方案

npm-g_nosudo:彻底告别Ubuntu下npm全局安装sudo依赖的终极解决方案

npm-g_nosudo:彻底告别Ubuntu下npm全局安装sudo依赖的终极解决方案 【免费下载链接】npm-g_nosudo A shell script which will fix the problem where you want to stop using sudo for npm -g on Ubuntu. 项目地址: https://gitcode.com/gh_mirrors/np/npm-g_nos…

2026/10/11 23:27:32 阅读更多 →
网页里的视频、音频、图片怎么保存?猫抓浏览器资源嗅探扩展帮你一次搞定

网页里的视频、音频、图片怎么保存?猫抓浏览器资源嗅探扩展帮你一次搞定

网页里的视频、音频、图片怎么保存?猫抓浏览器资源嗅探扩展帮你一次搞定 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 你在网页上看到…

2026/10/13 16:52:10 阅读更多 →

最新新闻

数据中心机柜温湿度精细化监测:分层多点以太网采集系统实践

数据中心机柜温湿度精细化监测:分层多点以太网采集系统实践

1. 从"空调没报警但服务器降频"说起:粗粒度监测的盲区到底在哪去年夏天做机房巡检时碰到一个很典型的案例:动环平台显示机房平均温度24.5℃,一个告警都没有,但监控大屏上某一排机柜的服务器核心温度持续走高&#xff0c…

2026/10/14 16:08:13 阅读更多 →
大模型落地不只是写提示词:企业用AI,算法工程师在解决哪4个问题

大模型落地不只是写提示词:企业用AI,算法工程师在解决哪4个问题

很多自媒体把“会用AI工具”讲得很简单,仿佛写几句提示词就能替代整个技术团队。实际企业落地时,算法工程师的工作量往往在工具背后:让模型懂业务、不出错、成本低、可审计。一、问题1:模型不了解企业内部数据通用大模型见过公开互…

2026/10/14 16:08:13 阅读更多 →
MCU直连CDC设备实战:CH32V307 USB Host枚举与批量收发

MCU直连CDC设备实战:CH32V307 USB Host枚举与批量收发

简介:这份资料面向嵌入式开发者与USB协议学习者,聚焦MCU作为USB Host识别并驱动CDC类设备这一实际难题,帮助打破传统RS232接口在嵌入式板卡上的技术壁垒。内容从插入检测、总线复位、NRZI编码与同步域讲起,逐步拆解SOP/EOP、PID类…

2026/10/14 16:08:13 阅读更多 →
2024网络安全产业人才发展报告深度拆解:岗位画像、薪资对照与AI技能缺口

2024网络安全产业人才发展报告深度拆解:岗位画像、薪资对照与AI技能缺口

简介:《2024年网络安全产业人才发展报告》面向政府决策者、企业管理者、教育机构以及网络安全从业者和学生,系统梳理全球与国内网络安全产业的人才供需现状,帮助读者把握行业趋势、规划职业路径与制定用人策略。报告涵盖从业人员画像、薪酬水…

2026/10/14 16:08:13 阅读更多 →
KRONE农业机械来料检验案例_大型部件为什么不再搬到质检区

KRONE农业机械来料检验案例_大型部件为什么不再搬到质检区

​农业机械制造商 KRONE 使用安宝特合作伙伴 Visometry 拓影 Twyn,把部分大型来料的视觉质量检查直接放在到货位置完成,无需为了常规 CAD 实物核对,先将工件统一搬运到专门质检区。​质检人员通过平板电脑将 3D CAD 设计状态与现场实物叠加&a…

2026/10/14 16:08:13 阅读更多 →
UK-5099在肿瘤代谢与线粒体功能研究中的应用指南

UK-5099在肿瘤代谢与线粒体功能研究中的应用指南

1. 项目概述UK-5099作为一种线粒体丙酮酸载体(MPC)抑制剂,近年来在肿瘤代谢研究和线粒体功能调控领域引起了广泛关注。这个实验指南旨在为科研人员提供从基础原理到实操细节的完整解决方案,帮助研究者快速掌握UK-5099在肿瘤微环境…

2026/10/14 16:07:13 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →