LLM 学习工作流(七):AI 计划驱动学习流
这一步在做什么
这一步是前面所有 AI 改造里第一个真正开始"进入主流程"的阶段。
在 Task 6 之前,AI 教练更多还是:
- 有入口
- 有页面
- 有协议
- 有服务端骨架
但并没有真的影响用户的学习过程。
Task 7 做的事情是:
让 AI 计划开始驱动学习页。
最终主要改动在:
src/utils/learning-workflow.jssrc/stores/aiCoach.jssrc/stores/learning.jssrc/views/Study.vuesrc/views/StudyQuiz.vuesrc/App.vuetests/ai-study-session.test.js
为什么这一步很关键
AI 项目要避免停留在"页面有个 AI 按钮"的层面。
真正有价值的阶段,是 AI 开始改变主用户流程。
这个任务完成后,AI 不再只是一个独立页面,而是开始参与:
- 学什么词
- 先学哪些词
- 学到什么时候可以进入测验
- 何时进入下一阶段
也就是说,这一步开始把:
AI 教练
变成:
AI 驱动的学习流程
一开始做了什么
最初实现对应提交:
7fed2874feat: connect ai plan to study session
最开始做的事情大方向是对的:
1. 新增计划词合并逻辑
在 src/utils/learning-workflow.js 增加了:
mergeRecommendedWords(primary, fallback)
这让页面有能力把 AI 推荐词和其他词源合并处理。
2. 学习页开始优先用 AI 推荐词
在 src/views/Study.vue 里:
- 优先加载
dailyPlan.recommendedWords - 只有没有推荐词时才退回随机词
3. 页面顶部开始显示 AI 学习目标
学习页开始出现:
AI 学习目标
这意味着用户在学习时,已经不再只是"随机刷词",而是知道:
- 这一轮 AI 想让你重点学什么
第一轮问题:测试太弱
最开始的 Task 7 测试虽然通过了,但 review 很快指出:
- 它主要还是在查源码字符串
- 不能真正证明行为
这一步教你的第一个重点是:
当功能开始进入主流程时,字符串断言就不够了。
于是测试开始升级:
1. 直接测纯函数行为
例如:
mergeRecommendedWordsgetLearnedRecommendedWordsgetNextRecommendedWordcanStartPlannedQuiz
2. 直接跑 store 行为
例如:
startPlannedStudy()markPlanTaskDone()resetCoachState()
这样测试就不再只是看"有没有这行代码",而是看:
- 状态是不是会真的变
第二轮问题:随机 fallback 词被错误算进 AI 计划进度
这是 Task 7 里最重要的业务问题之一。
最初逻辑里,学习页会把:
dailyPlan.recommendedWordslearningStore.sessionQueue
混在一起用于判定"是否达到 quiz_ready"。
问题是:
- sessionQueue 里有 fallback 随机词
- 这些词本来不属于 AI 计划
结果就变成:
- 用户随便学了几个随机词
- 系统也可能误以为"AI 计划学够了"
这个问题告诉你:
AI 计划词和普通学习词必须是两套概念
不能因为它们都出现在学习会话里,就把它们混成一个集合。
修正后新增了纯函数:
getLearnedRecommendedWords()
只统计:
- 属于计划推荐词
- 同时又已经学过的词
canStartPlannedQuiz()
只根据:
- 计划推荐词
- 已学推荐词
来决定是否能进入 quiz_ready。
第三轮问题:坏推荐词会把流程卡死
这也是非常典型的真实业务问题。
如果 AI 推荐词里有一个词:
- 字典里不存在
- 或详情加载失败
最初逻辑会不断尝试同一个失败词,然后永远卡在这里。
这类问题很像生产环境里的"脏数据污染流程"。
修复方式是:
getNextRecommendedWord()
它会在推荐词中只找:
- 还没学过的
- 也没被标记成无效的
学习页现在会:
- 优先找下一条有效推荐词
- 如果某个推荐词加载失败,就把它加入"无效推荐词集合"
- 继续尝试后面的推荐词
- 如果所有推荐词都不可用,才回退到随机词
这个设计很重要,因为它说明:
AI 系统接进主流程后,必须能容忍坏数据,而不是一碰到坏数据就卡死。
第四轮问题:测验入口必须和同一套计划状态一致
如果学习页已经开始按 AI 计划驱动,那测验页也必须跟着这套状态走。
否则会出现:
- 学习页说"还没学够"
- 测验页却因为学过任意 1 个词就直接能进
这会让工作流完全失真。
所以后面把:
src/views/Study.vuesrc/views/StudyQuiz.vue
都统一改成依赖:
aiCoachStore.isPlannedStudyActive
aiCoachStore.activeRun?.currentStage === 'quiz_ready'
这意味着:
- 不是"学了词就能测"
- 而是"计划词学够,工作流进入 quiz_ready 才能测"
第五轮问题:登出后 AI 计划状态泄漏
这是 Task 7 里最典型、也最容易被忽略的状态问题。
最开始 AI coach 的状态:
dailyPlanactiveRunreflection
在登出时没有被清空。
于是就会出现:
- A 用户登录,生成了 AI 计划
- A 退出
- B 登录
- 页面仍然可能带着 A 的 AI 计划状态
这不只是体验问题,更是状态泄漏问题。
最后加了:
resetCoachState()
在 src/stores/aiCoach.js 中统一清空:
profileactiveRundailyPlanreflection
然后在 src/App.vue 的 SIGNED_OUT 分支里一起调用。
这一步非常值得记住:
一旦有新的全局 store,登出清理路径就必须同步更新。
这一步最后做成了什么
学习页现在可以:
- 顶部显示 AI 当前学习目标
- 优先加载 AI 推荐词
- 遇到坏推荐词时自动跳过
- 推荐词学够后推动流程到
quiz_ready
测验页现在可以:
- 在 AI 计划激活时只接受
quiz_ready状态进入 - 不再被普通学习会话轻易绕过
Store 现在可以:
- 表达当前计划推荐词
- 表达当前学习目标
- 启动计划学习
- 标记计划任务
- 在登出时彻底清空 AI 状态
测试现在可以:
- 验证计划词不会和随机词混算
- 验证推荐词会按顺序推进
- 验证 store 状态会真实推进
- 验证登出能清理 AI 状态
这一步真正学到什么
1. AI 驱动主流程以后,状态边界必须更严格
在普通页面里,少一个字段也许只是 UI 小问题。
但当 AI 已经驱动:
- 学习顺序
- 流程推进
- 测验解锁
状态边界就会变成主业务逻辑的一部分。
2. "推荐词"和"普通会话词"必须严格区分
它们都属于学习内容,但业务含义完全不同:
- 推荐词:属于 AI 计划
- 普通词:只是 fallback 或自然学习过程的一部分
如果把它们混在一起,工作流就会失真。
3. 脏数据要被隔离,而不是拖死流程
坏推荐词不会永远消失,真实系统里几乎一定会出现。
所以真正可靠的流程必须做到:
- 识别它
- 跳过它
- 继续走
这类"坏数据容忍能力"是 AI 工作流项目很重要的工程素质。
4. 新增全局 store 时,要立刻补登出/重置路径
这是很多项目都会漏掉的。
只要你新增了新的跨页面状态,就必须问自己:
- 登出时它怎么清
- 用户切换时它怎么清
- 重开一轮流程时它怎么重置
你可以自己复现什么
跑 Task 7 针对测试
node --test tests/ai-study-session.test.js -v
跑全部测试
node --test tests/*.test.js
跑构建
npm run build
看关键提交演进
git log --oneline -5
这一步最终会对应到:
7fed2874初始接入674bdf47收紧推荐词、计划阈值、sign-out reset 等问题
这一步你应该学会什么
- 如何让 AI 计划真正进入主业务流程
- 为什么"计划词"和"随机词"必须分离
- 为什么工作流 gate 必须前后一致
- 为什么脏推荐词不能拖死流程
- 为什么新增 store 后一定要补登出清理
下一步会做什么
下一步是 Task 8:
- AI 测验
- AI 批改
- AI 复盘
也就是说,接下来你会从:
AI 驱动学习
进入到:
AI 驱动学习后的评价与反馈闭环