从零到可运行:基于 Vue3 + FastAPI + DeepSeek-V3 的 AI 英语单词学习系统全栈实战
2026/9/4 11:23:14 网站建设 项目流程

从零到可运行:基于 Vue3 + FastAPI + DeepSeek-V3 的 AI 英语单词学习系统全栈实战

一份真实、完整、可复现的全栈项目复盘。包含架构设计、数据库建模、AI 词库生成、前端交互、踩坑修复与实测数据。

目录

  1. 项目背景与目标
  2. 技术选型
  3. 项目结构
  4. 数据库设计
  5. 后端 API 设计
  6. 前端页面与交互
  7. AI 词库生成:DeepSeek-V3 接入实战
  8. 关键问题修复记录(踩坑实录)
  9. 运行与部署
  10. 实测数据
  11. 总结与后续优化方向

一、项目背景与目标

单词学习类 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/adv)
created_atDateTime创建时间

4.2 learning_records 学习记录表

字段类型说明
idInteger, PK主键
word_idInteger关联单词(一对多:一个单词可有多次学习记录)
times_learnedInteger, default 0学习次数
last_learnedDateTime最近学习时间
proficiencyFloat, default 0熟练度 0-100
created_atDateTime创建时间

为什么不需要重新设计数据库?最初需求是"每次随机词库都要累计起来但是要去重",两张表天然满足:words.word唯一索引 + 服务层插入前查重 = 词库累计去重;learning_recordsword_id独立记录熟练度 = 学习进度可追踪。后续所有功能迭代(优先未学词、排除刚看过的词、熟练度回传)都只改查询逻辑,不动表结构。


五、后端 API 设计

全部接口集中在backend/app/main.py,CORS 已放开 5173/3000 两个开发端口。

方法路径功能关键参数
POST/api/words/generateAI 生成词库(累计去重)difficulty, count(1-100)
GET/api/words获取指定难度词库列表difficulty, skip, limit
GET/api/study/random获取随机学习单词difficulty, exclude_id(排除刚看过的)
POST/api/study/record记录学习进度word_id, proficiency, mark_as_learned
GET/api/stats学习统计(总词数/分难度词数)-
GET/api/health健康检查-

5.1 核心接口:随机取词的三级优先策略

这是解决"单词总是那几个、不跟词库走"的关键逻辑:

@staticmethoddefget_random_word(db:Session,difficulty:DifficultyLevel,exclude_id:int=None)->Word:"""获取随机单词:优先未学过的词,其次未完全掌握的,最后兜底随机;可排除指定词避免连续重复"""base=db.query(Word).filter(Word.difficulty==difficulty)ifexclude_idisnotNone:base=base.filter(Word.id!=exclude_id)# 1. 从未学过(无学习记录)unlearned=base.outerjoin(LearningRecord,Word.id==LearningRecord.word_id).filter(LearningRecord.id.is_(None)).all()pool=unlearnedifnotpool:# 2. 学过但未完全掌握(proficiency < 100)pool=base.outerjoin(LearningRecord,Word.id==LearningRecord.word_id).filter(LearningRecord.id.isnot(None),LearningRecord.proficiency<100).all()ifnotpool:# 3. 兜底:当前难度全部单词pool=base.all()ifnotpool:returnNonereturnrandom.choice(pool)

三层语义:先把没学过的词喂给你 → 再复习学得不熟的 → 全都掌握了才随机复习exclude_id由前端传入当前单词 id,点"下一个"不会连续抽到同一词。

5.2 熟练度回传

WordResponse增加proficiency字段,查询时左连学习记录取最新熟练度:

@staticmethoddefto_response_with_proficiency(db:Session,word:Word)->dict:"""将 Word 转为 WordResponse dict,并附加该词的学习熟练度"""data=WordResponse.from_orm(word).__dict__ record=db.query(LearningRecord).filter(LearningRecord.word_id==word.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.js)

Pinia store 集中管理:currentWorddifficultyshowTranslationlearnedCountloadingproficiencyPercentage计算属性,以及setDifficulty/getRandomWord/markAsLearned/generateWords/fetchStats五个动作。切换难度的关键修复:

constsetDifficulty=async(level)=>{difficulty.value=level showTranslation.value=falseawaitgetRandomWord()// 切换难度后立即加载该难度单词}

七、AI 词库生成:DeepSeek-V3 接入实战

7.1 接入配置

通过 SiliconFlow 平台(https://api.siliconflow.cn/v1)调用deepseek-ai/DeepSeek-V3模型,环境变量配置在backend/.env

OPENAI_API_KEY=sk-xxx OPENAI_MODEL=deepseek-ai/DeepSeek-V3 OPENAI_BASE_URL=https://api.siliconflow.cn/v1

7.2 生成逻辑(ai_service.py)

asyncdefgenerate_word(self,difficulty:DifficultyLevel,db=None)->dict:# 1. 查询该难度已有单词,拼进 prompt 提示模型避开(去重的第一道防线)existing=db.query(Word.word).filter(Word.difficulty==difficulty).all()ifexisting:exclude_words="不要生成以下已存在的单词:"+"、".join([w[0]forwinexisting][:80])# 2. 构造 prompt,要求返回 JSONprompt=f"""请生成一个{difficulty_prompts[difficulty]}的英语单词,返回JSON格式,包含: {{ "word": "...", "phonetic": "...", "meaning": "...", "definition": "...", "example": "...", "example_cn": "...", "pos": "..." }} 要求: 1. 返回格式必须是有效的JSON 4. 不要返回markdown格式,直接返回JSON 5.{exclude_words}"""# 3. 调用 DeepSeek-V3response=self.client.chat.completions.create(model=self.model,messages=[...],temperature=0.7)# 4. 清理模型返回(可能用 ```json 代码块包裹),再 json.loadscontent=response.choices[0].message.content.strip()ifcontent.startswith("```"):content=content.strip("`")ifcontent.startswith("json"):content=content[4:]content=content.strip()word_data=json.loads(content)word_data["difficulty"]=difficultyreturnword_data

三层去重防线

  1. Prompt 层:把该难度已有单词列表告诉模型"不要生成这些";
  2. 应用层:插入前再次按word精确查重,存在则跳过;
  3. 数据库层words.word唯一索引兜底,即使并发也不会重复插入。

7.3 容错降级

  • 未配置 API KeyWordGenerator的 client 为None,服务照常启动,生成接口回退到内置备用词库;
  • AI 调用失败_generate_fallback_word()从本地备用词列表随机返回一个,保证接口永不报错;
  • 返回格式异常:先剥离 ```json 包裹再json.loads,失败则走备用词库;
  • 超时:openai client 设置timeout=60

八、关键问题修复记录(踩坑实录)

以下是本项目中真实遇到并解决的问题,按类型归类,供遇到同类坑的同学参考。

8.1 前端构建类

问题根因修复
Vite build 失败App.vue出现两个<script setup>块 + 残留HelloWorldimport合并为单个 script setup,删除残留 import
Vite build 失败StatsView.vue有重复 script 块合并去重
Tailwind 样式不生效style.css使用@import 'tailwindcss/base'非标准写法改为标准@tailwind base; @tailwind components; @tailwind utilities;指令,并补齐tailwind.config.js/postcss.config.js

8.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后重启 uvicorn

8.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/.env

OPENAI_API_KEY=<你的 SiliconFlow Key> OPENAI_MODEL=deepseek-ai/DeepSeek-V3 OPENAI_BASE_URL=https://api.siliconflow.cn/v1

不配置也能启动,AI 生成会走备用词库。

9.4 常见运维

# 清理 8000 端口占用lsof-ti:8000|xargskill-9# 查看后端日志tail-fbackend/nohup.out# 按实际启动方式

十、实测数据

数据采集于 2026-08-13 10:11,真实运行环境。

指标数值
总词库数115
CET458
CET613
BEC12
TOEFL12
IELTS20
学习记录数47
已学单词数(去重)47
AI 生成实测一次点击从 103 增至 115(新增 12 个,均为去重后真实新增)
随机取词不同难度返回独立词库;exclude_id 生效,不连续重复
熟练度回传已学词返回真实值(如 abandon=100);未学词返回 0

十一、总结与后续优化方向

收获

  1. 全栈闭环:一个需求从数据库设计到前端交互完整落地,Vue3 + FastAPI 的组合开发效率高、调试链路清晰;
  2. AI 接入没那么神秘:OpenAI 协议 + 第三方平台(SiliconFlow)把模型调用简化成一次 HTTP 请求,难点在输出格式容错业务去重
  3. "去重累计"是工程问题:单靠数据库唯一约束不够,要 prompt 提示 + 应用层查重 + 数据库约束三层配合;
  4. 看似简单的功能 bug 往往在数据链路断层:进度条 0% 不是前端问题,是后端 schema 根本没返回该字段——排查要顺着数据流从源头找。

可优化方向

  • 间隔重复算法:引入 SM-2 算法,按熟练度与遗忘曲线安排复习节奏,已掌握的单词降权出现;
  • 学习记录历史learning_records增加时间维度查询,统计页展示熟练度分布趋势;
  • 批量生成优化:当前逐词调用大模型较慢(20 词约 30-60 秒),可改为一次 prompt 返回 5-10 个词批量入库,并加进度提示;
  • 用户体系:接入登录后,学习记录按用户隔离,词库支持多人共享;
  • 前端体验:单词朗读(Web Speech API)、答错重测、学习打卡等。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询