从零到可运行:基于 Vue3 + FastAPI + DeepSeek-V3 的 AI 英语单词学习系统全栈实战 从零到可运行基于 Vue3 FastAPI DeepSeek-V3 的 AI 英语单词学习系统全栈实战一份真实、完整、可复现的全栈项目复盘。包含架构设计、数据库建模、AI 词库生成、前端交互、踩坑修复与实测数据。目录项目背景与目标技术选型项目结构数据库设计后端 API 设计前端页面与交互AI 词库生成DeepSeek-V3 接入实战关键问题修复记录踩坑实录运行与部署实测数据总结与后续优化方向一、项目背景与目标单词学习类 App 的最大痛点有两个词库是死板的背来背去就那一本书和学习过程没有正反馈不知道哪些词掌握了、哪些还没。本项目尝试用全栈工程手段解决这两个问题AI 动态词库接入大模型按难度四级/六级/商务/托福/雅思实时生成新词生成的词自动累计入库并去重词库越用越丰富学习闭环随机出词 → 翻卡查看释义 → 标记熟练度比较熟悉 75 / 完全掌握 100→ 进度条与统计页实时反馈形成学-记-测-查的完整闭环。项目由用户基于 AI 导出的方案郭震 AI 的英语单词学习系统方案起步经历了前端多处语法/构建错误修复、后端依赖冲突解决、AI 接口打通、词库去重策略设计等一系列工程问题最终前后端成功运行、AI 生成功能可用。二、技术选型层级技术版本用途前端框架Vue 3^3.5.40响应式 UI构建工具Vite^8.2.0开发服务器与构建状态管理Pinia^2.3.1全局学习状态当前单词/难度/熟练度路由Vue Router^4.5.0学习/词库/统计三个页面样式TailwindCSS^3.4.17原子化 CSS快速布局HTTPAxios^1.7.9前端调用后端 API后端框架FastAPI0.104.1高性能异步 Web 框架ORMSQLAlchemy2.0.23数据库映射数据库SQLite内置零配置本地存储数据校验Pydantic2.5.0请求/响应模型校验AI 接入openai SDK SiliconFlow3.x调用 DeepSeek-V3 生成单词ASGI 服务器Uvicorn0.24.0后端运行选型理由前后端分离、接口清晰Vue3 Pinia 的 Composition API 适合中小型交互应用FastAPI 自带 OpenAPI 文档便于调试SQLite 免部署适合单机学习工具AI 生成用统一 OpenAI 协议通过 SiliconFlow 平台低成本接入 DeepSeek-V3。三、项目结构English-words-app/ ├── backend/ # FastAPI 后端 │ ├── app/ │ │ ├── main.py # 应用入口 全部 API 路由 │ │ ├── database.py # SQLite 连接与 Session │ │ ├── models.py # ORM 模型words / learning_records │ │ ├── schemas.py # Pydantic 模型 │ │ ├── seeds.py # 五个难度各 12 个种子单词 │ │ └── services/ │ │ ├── ai_service.py # DeepSeek-V3 单词生成器含备用词库 │ │ └── word_service.py # 词库/随机取词/学习记录/统计业务逻辑 │ ├── requirements.txt │ └── words.db # SQLite 数据库 └── frontend/ # Vue3 前端 ├── index.html ├── vite.config.js ├── tailwind.config.js ├── postcss.config.js └── src/ ├── main.js ├── App.vue # 顶部导航 路由出口 ├── style.css # Tailwind 指令入口 ├── api/client.js # Axios 封装 ├── stores/wordStore.js # Pinia 全局状态 └── components/ ├── StudyView.vue # 学习页核心 ├── VocabView.vue # 词库浏览页 └── StatsView.vue # 统计页四、数据库设计共两张表设计上刻意保持简单词库表负责词是什么学习记录表负责你学得怎么样。4.1 words 词库表字段类型说明idInteger, PK主键wordString(100),unique, index单词本身唯一约束是累计去重的数据库层保障phoneticString(100)音标meaningText中文释义definitionText英文定义exampleText英文例句example_cnText例句中文翻译difficultyEnum(CET4/CET6/BEC/TOEFL/IELTS)所属难度posString(20)词性noun/verb/adj/advcreated_atDateTime创建时间4.2 learning_records 学习记录表字段类型说明idInteger, PK主键word_idInteger关联单词一对多一个单词可有多次学习记录times_learnedInteger, default 0学习次数last_learnedDateTime最近学习时间proficiencyFloat, default 0熟练度 0-100created_atDateTime创建时间为什么不需要重新设计数据库最初需求是每次随机词库都要累计起来但是要去重两张表天然满足words.word唯一索引 服务层插入前查重 词库累计去重learning_records按word_id独立记录熟练度 学习进度可追踪。后续所有功能迭代优先未学词、排除刚看过的词、熟练度回传都只改查询逻辑不动表结构。五、后端 API 设计全部接口集中在backend/app/main.pyCORS 已放开 5173/3000 两个开发端口。方法路径功能关键参数POST/api/words/generateAI 生成词库累计去重difficulty, count(1-100)GET/api/words获取指定难度词库列表difficulty, skip, limitGET/api/study/random获取随机学习单词difficulty, exclude_id排除刚看过的POST/api/study/record记录学习进度word_id, proficiency, mark_as_learnedGET/api/stats学习统计总词数/分难度词数-GET/api/health健康检查-5.1 核心接口随机取词的三级优先策略这是解决单词总是那几个、不跟词库走的关键逻辑staticmethoddefget_random_word(db:Session,difficulty:DifficultyLevel,exclude_id:intNone)-Word:获取随机单词优先未学过的词其次未完全掌握的最后兜底随机可排除指定词避免连续重复basedb.query(Word).filter(Word.difficultydifficulty)ifexclude_idisnotNone:basebase.filter(Word.id!exclude_id)# 1. 从未学过无学习记录unlearnedbase.outerjoin(LearningRecord,Word.idLearningRecord.word_id).filter(LearningRecord.id.is_(None)).all()poolunlearnedifnotpool:# 2. 学过但未完全掌握proficiency 100poolbase.outerjoin(LearningRecord,Word.idLearningRecord.word_id).filter(LearningRecord.id.isnot(None),LearningRecord.proficiency100).all()ifnotpool:# 3. 兜底当前难度全部单词poolbase.all()ifnotpool:returnNonereturnrandom.choice(pool)三层语义先把没学过的词喂给你 → 再复习学得不熟的 → 全都掌握了才随机复习。exclude_id由前端传入当前单词 id点下一个不会连续抽到同一词。5.2 熟练度回传WordResponse增加proficiency字段查询时左连学习记录取最新熟练度staticmethoddefto_response_with_proficiency(db:Session,word:Word)-dict:将 Word 转为 WordResponse dict并附加该词的学习熟练度dataWordResponse.from_orm(word).__dict__ recorddb.query(LearningRecord).filter(LearningRecord.word_idword.id).first()data[proficiency]record.proficiencyifrecordelse0returndata六、前端页面与交互6.1 学习页StudyView.vue—— 核心交互页面元素与交互逻辑元素交互实现难度标签四级/六级/商务/托福/雅思点击切换难度并立即加载该难度单词setDifficulty()内部调用getRandomWord()单词卡片显示单词/音标/词性点击中文区翻转显示英文定义CSS 3D 翻转熟练度进度条实时反映当前词的熟练度0/75/100后端回传proficiency⏭️ 下一个随机换词排除当前词exclude_id参数 比较熟悉熟练度记为 75 并自动换下一个markAsLearned(75)✓ 完全掌握熟练度记为 100 并自动换下一个markAsLearned(100) 生成更多词库调用 AI 生成 20 个新词去重累计generateWords(20)AI 生成期间按钮进入 disabled 加载态生成完成后随机展示一个新生成或未学过的单词6.2 词库页VocabView.vue按难度切换标签以卡片网格展示单词、音标、中文释义和英文例句支持翻页拉取。6.3 统计页StatsView.vue展示五个难度的单词分布、总词库数与学习进度百分比已学 / 总词库。6.4 全局状态wordStore.jsPinia store 集中管理currentWord、difficulty、showTranslation、learnedCount、loading、proficiencyPercentage计算属性以及setDifficulty/getRandomWord/markAsLearned/generateWords/fetchStats五个动作。切换难度的关键修复constsetDifficultyasync(level){difficulty.valuelevel showTranslation.valuefalseawaitgetRandomWord()// 切换难度后立即加载该难度单词}七、AI 词库生成DeepSeek-V3 接入实战7.1 接入配置通过 SiliconFlow 平台https://api.siliconflow.cn/v1调用deepseek-ai/DeepSeek-V3模型环境变量配置在backend/.envOPENAI_API_KEYsk-xxx OPENAI_MODELdeepseek-ai/DeepSeek-V3 OPENAI_BASE_URLhttps://api.siliconflow.cn/v17.2 生成逻辑ai_service.pyasyncdefgenerate_word(self,difficulty:DifficultyLevel,dbNone)-dict:# 1. 查询该难度已有单词拼进 prompt 提示模型避开去重的第一道防线existingdb.query(Word.word).filter(Word.difficultydifficulty).all()ifexisting:exclude_words不要生成以下已存在的单词、.join([w[0]forwinexisting][:80])# 2. 构造 prompt要求返回 JSONpromptf请生成一个{difficulty_prompts[difficulty]}的英语单词返回JSON格式包含 {{ word: ..., phonetic: ..., meaning: ..., definition: ..., example: ..., example_cn: ..., pos: ... }} 要求 1. 返回格式必须是有效的JSON 4. 不要返回markdown格式直接返回JSON 5.{exclude_words}# 3. 调用 DeepSeek-V3responseself.client.chat.completions.create(modelself.model,messages[...],temperature0.7)# 4. 清理模型返回可能用 json 代码块包裹再 json.loadscontentresponse.choices[0].message.content.strip()ifcontent.startswith():contentcontent.strip()ifcontent.startswith(json):contentcontent[4:]contentcontent.strip()word_datajson.loads(content)word_data[difficulty]difficultyreturnword_data三层去重防线Prompt 层把该难度已有单词列表告诉模型不要生成这些应用层插入前再次按word精确查重存在则跳过数据库层words.word唯一索引兜底即使并发也不会重复插入。7.3 容错降级未配置 API KeyWordGenerator的 client 为None服务照常启动生成接口回退到内置备用词库AI 调用失败_generate_fallback_word()从本地备用词列表随机返回一个保证接口永不报错返回格式异常先剥离 json 包裹再json.loads失败则走备用词库超时openai client 设置timeout60。八、关键问题修复记录踩坑实录以下是本项目中真实遇到并解决的问题按类型归类供遇到同类坑的同学参考。8.1 前端构建类问题根因修复Vite build 失败App.vue出现两个script setup块 残留HelloWorldimport合并为单个 script setup删除残留 importVite build 失败StatsView.vue有重复 script 块合并去重Tailwind 样式不生效style.css使用import tailwindcss/base非标准写法改为标准tailwind base; tailwind components; tailwind utilities;指令并补齐tailwind.config.js/postcss.config.js8.2 后端依赖与运行时类问题根因修复openai 调用报proxies参数错误openai 1.3.5 与 httpx 版本不兼容升级 openai 至 3.0.0升级后报SocketTimeoutErroropenai 3.x 与旧版 aiohttp 冲突升级 aiohttp 至 3.14.3学习记录报 None 错误times_learned字段初始为 None 1崩溃改为(record.times_learned or 0) 1生成词失败备用词库数据缺difficulty字段触发 Pydantic 校验失败备用词库补齐 difficulty端口被占用旧进程未退出lsof -ti:8000 | xargs kill -9后重启 uvicorn8.3 功能逻辑类本轮修复问题根因修复点击四级/六级/商务标签单词都一样setDifficulty只改标签状态没重新加载该难度单词切换难度后自动getRandomWord()词库固定不变、总抽到那几个词随机取词不分学过与否纯随机三级优先策略未学 → 未掌握 → 兜底下一个可能连续抽到同一词随机无排除逻辑接口新增exclude_id前端传当前词 id熟练度进度条一直 0%WordResponseschema 根本没有proficiency字段后端从不返回schema 增加字段 to_response_with_proficiency()查询学习记录回传找不到增加单词量的入口生成词库按钮只在无词时显示学习页常驻 生成更多词库按钮生成接口重复词也报成功生成未区分新增与跳过先查重再插入返回真实新增数与跳过数九、运行与部署9.1 后端cdbackend python-mvenv venv# 首次sourcevenv/bin/activate pipinstall-rrequirements.txt# 首次python-muvicorn app.main:app--host0.0.0.0--port8000首次启动会自动建表需要种子词时执行python app/seeds.py。9.2 前端cdfrontendnpminstall# 首次npmrun dev# 开发默认 http://localhost:5173npmrun build# 生产构建9.3 环境变量backend/.envOPENAI_API_KEY你的 SiliconFlow Key OPENAI_MODELdeepseek-ai/DeepSeek-V3 OPENAI_BASE_URLhttps://api.siliconflow.cn/v1不配置也能启动AI 生成会走备用词库。9.4 常见运维# 清理 8000 端口占用lsof-ti:8000|xargskill-9# 查看后端日志tail-fbackend/nohup.out# 按实际启动方式十、实测数据数据采集于 2026-08-13 10:11真实运行环境。指标数值总词库数115CET458CET613BEC12TOEFL12IELTS20学习记录数47已学单词数去重47AI 生成实测一次点击从 103 增至 115新增 12 个均为去重后真实新增随机取词不同难度返回独立词库exclude_id 生效不连续重复熟练度回传已学词返回真实值如 abandon100未学词返回 0十一、总结与后续优化方向收获全栈闭环一个需求从数据库设计到前端交互完整落地Vue3 FastAPI 的组合开发效率高、调试链路清晰AI 接入没那么神秘OpenAI 协议 第三方平台SiliconFlow把模型调用简化成一次 HTTP 请求难点在输出格式容错和业务去重去重累计是工程问题单靠数据库唯一约束不够要 prompt 提示 应用层查重 数据库约束三层配合看似简单的功能 bug 往往在数据链路断层进度条 0% 不是前端问题是后端 schema 根本没返回该字段——排查要顺着数据流从源头找。可优化方向间隔重复算法引入 SM-2 算法按熟练度与遗忘曲线安排复习节奏已掌握的单词降权出现学习记录历史learning_records增加时间维度查询统计页展示熟练度分布趋势批量生成优化当前逐词调用大模型较慢20 词约 30-60 秒可改为一次 prompt 返回 5-10 个词批量入库并加进度提示用户体系接入登录后学习记录按用户隔离词库支持多人共享前端体验单词朗读Web Speech API、答错重测、学习打卡等。