Doodle 子系统 · Harrow International School Bangkok 学习辅导
Harrow 学习辅导系统 — 项目交接
目标:孩子(Cordelia,Year 7,用户名 coco)每天按课表自动生成复习任务,AI 多轮对话判定是否讲清楚,
数学额外配套现场出题,家长收欠账/告警通知,孩子收任务提醒;另外把学校的课表、校历、(未来)邮件都汇总展示。
需求文档是仓库根目录的 HARROW_SPEC.md / HARROW_IMPL.md,开发顺序按 IMPL §7 走。
时间规划 + 弹性配额任务
Review 多轮判定(5科)
数学配套测试 + 告警队列
孩子日历 / 家长日历
双通道通知 + 5 条 crontab(时区已修)
Review 记录查询页
Gmail 同步未接(缺凭证)
家长管理界面未做(只有 API)
已做 / 未做
✅ 已完成(全部用真实数据/真实 LLM 调用/真实浏览器操作验证过)
- 时间规划主干:
HarrowTaskTemplate/HarrowDailyTask/HarrowMainSubjectConfig,模板展开、按课表自动定 review 科目、欠账结转、幂等保护。
- 弹性配额任务(比如"跑步 2km 每周 2 次"):周一一次性生成本周全部实例,孩子可以用延迟按钮挪到周五之前任意一天,周一到周五都没处理会持续在"今日任务"露出,周六强制变成正式欠账。
- 通知:家长走 ClawBot 主 + Telegram 兜底,孩子走她自己的 Telegram(
coco 的 chat_id 已配置);到点未完成提醒,16-20 点每 30 分钟一次,其余时段 2 小时一次。
- 课表批量同步:一次登录抓未来 30 天(避免频繁登录触发学校反自动化检测),每天晚上跟数据库比对差异,有变化同时通知孩子+家长。
- Review 判定:数学/英语/科学(生物+化学+物理)/历史/地理五科通用机制,5 轮自动放行;数学额外触发配套非选择题现场出题判分;英语可传图片走 Gemini 多模态语法分析。
- 告警队列:数学知识点错题 + 英语语法问题,FIFO 复测。
- 孩子日历:日视图默认入口,课表 + 固定课外班 + 孩子自己安排了时间的任务,合并成一条时间线;周视图显示每天完成情况徽章。
- 家长日历:
calendar.harrowschool.ac.th(免登录公开网站)抓校历,按 SR5 标签近似过滤 Year 7 相关事项,已抓到 92 条真实数据并翻译。
- 每周汇总报告:数学告警 + 英语语法问题 + 科学 review 记录,一份报告三科都有。
- 前端:
/doodle/harrow.html 枢纽页 + tasks.html/day.html/week.html/review.html/parent-calendar.html/review-log.html,首页已加导航入口。
- Review 记录查询页(新,2026-08-25):
GET /doodle/api/harrow/reviews 按科目/按周(可组合)查历史 review,review-log.html 列表点开看完整对话,"上一条/下一条"在当前筛选结果内翻页。真实数据验证:孩子已经在真实使用 review(8/24 数学"角的分类"、英语"自我介绍PPT"都通关,生物那次 5 轮没通关走了自动放行)。
- crontab:5 条新增任务,见下方"环境信息";2026-08-25 修正了时区错位(详见下方"已知坑")。
🚫 被阻塞 / 没做
- Gmail 学校邮件同步:doodle
.env 没有任何 Gmail API 凭证。已经和用户讨论了两个方案:① 用户自己去 Google Cloud Console 走一遍 OAuth 授权(一次性,在有浏览器的机器上做,不能在 VPS 上做);② 用户提议让 Mac 上的 cowork(另一个有 Gmail 访问权限的会话)定期把邮件摘要 POST 过来。倾向方案②,已经设计了接收端点 POST /doodle/api/harrow/emails/ingest + Authorization: Bearer 鉴权,但还没写代码——需要先确认鉴权 token 怎么定、cowork 推送的是原文还是摘要。
- 家长管理界面:任务模板 / 主课清单配置 / 固定课外班的 CRUD API 都有了(
/doodle/api/harrow/templates、/main-subjects),但没做可视化编辑页面,现在改配置只能直接调 API 或改数据库。
- 科学大纲准确性:
data/harrow_curriculum/science_biology.json/science_chemistry.json/science_physics.json 是 AI 根据公开的英国 KS3 课程框架整理的,不是官方文档摘录,建议找老师或官方资料核对一遍覆盖面和准确性。
- 思辨讨论题前端:API 有了(
/discussion-topics、/discussion/{id}/message),没做页面——SPEC 里这块本来就是"低优先级,兴趣向"。
- 睡前总结:SPEC 自己标注"占位,细节留待下一轮讨论",不是遗漏,是按 SPEC 原意暂缓。
环境信息
| 线上入口 | /doodle/harrow.html(枢纽页)→ tasks.html / day.html / week.html / review.html / parent-calendar.html / review-log.html,都在 /var/www/edmund-home/doodle/harrow/ |
| 后端路由 | routers/harrow.py,前缀 /doodle/api/harrow,main.py 已挂载 |
| 核心服务模块 | services/harrow_tasks.py(任务生成/欠账/配额)、harrow_review.py(Review 判定)、harrow_review_quiz.py(数学配套测试)、harrow_kp_alerts.py(告警队列)、harrow_timetable_month.py(课表批量同步+差异)、harrow_diary_scraper.py(校历抓取)、harrow_weekly_report.py |
| 数据表 | storage/models.py 里所有 Harrow* 开头的类;迁移脚本 tools/migrate_v17.py 到 v21.py(v17 课表、v18 任务模板、v19 固定作息、v20 Review/告警/日历、v21 弹性配额) |
| 课程大纲数据 | data/harrow_curriculum/*.json(math 复用已有 knowledge_points.json;science_* 是 AI 整理,见上方"未做") |
| 账号密码 | .env 的 HARROW_ISAMS_USER/HARROW_ISAMS_PASS(课表+校历抓取共用,已配置);Gmail 凭证未配置 |
| 孩子/家长 Telegram | users 表 coco/dad 两条记录各自的 telegram_chat_id 字段,已配置 |
crontab(本次新增的 5 条,都已确认没有影响其他项目原有任务)
# 下面的钟点是 crontab 实际写的值(VPS 系统时区 Asia/Tokyo),注释里的
# "曼谷 xx:xx" 才是这些任务真正想要触发的曼谷时刻——已经是"曼谷时刻+2小时"换算过的
5 2 * * * harrow_task_rollover.py # 曼谷 00:05,生成当天任务 + 结转欠账
*/15 * * * * harrow_reminder_check.py # 每 15 分钟检查到点未完成(纯频率型,不受时区影响)
30 23 * * * harrow_timetable_month_sync.py # 曼谷 21:30,抓未来 30 天课表 + 差异通知
0 */2 * * * harrow_diary_gmail_sync.py # 每 2 小时同步校历(纯频率型;Gmail 部分待接)
30 23 * * 0 harrow_weekly_report.py # 曼谷周日 21:30,每周汇总报告
常用命令
cd /home/ubuntu/doodle
git status --short
pm2 restart doodle && pm2 logs doodle --lines 20 --nostream
# 查看某个学生今天的任务
sqlite3 data/doodle.db "SELECT task_type, label, subject, status, is_debt FROM harrow_daily_tasks WHERE owner='coco' ORDER BY task_type;"
# 手动跑一次任务生成(补数据用)
.venv/bin/python -c "
from datetime import date
from storage.db import SessionLocal
from services.harrow_tasks import generate_daily_tasks
db = SessionLocal()
print(generate_daily_tasks(db, 'coco', date.today()))
db.close()
"
curl -s http://127.0.0.1:8090/doodle/api/harrow/tasks/today?owner=coco
已知坑(详见 LESSONS.md 的 "Harrow" 相关章节)
| 反自动化检测 | calendar.harrowschool.ac.th/socscms.com 默认 headless UA 会 ERR_TOO_MANY_REDIRECTS,必须 --disable-blink-features=AutomationControlled + 桌面 UA |
| 抓一个月课表不能循环登录 | 用 fetch_range_timetable() 一次登录内连续翻页,不要循环调用单日版本 |
Gemini max_tokens 太小会截断 JSON | thinkingConfig.thinkingBudget=1024 也占用 max_tokens 预算,结构化输出场景至少留 800-1500 |
Review 判定的 kp_ids | 大纲片段必须显式标注 id(如 [id=ks3_num_05]),否则 LLM 会自己编中文名字回填,覆盖度追踪对不上 |
| 前端日期字符串 | 不要用 d.toISOString().slice(0,10) 取本地日期——会先转 UTC,时区一偏移就跨天错位;统一用本地 getFullYear/getMonth/getDate 拼(toDateStr()) |
| SR5 ≠ Year 7 | 校历只有 School Section 标签(Early Years/Pre Prep/Prep/SR5/Sixth Form),没有精确到年级,SR5 是近似值 |
| VPS 系统时区是东京不是曼谷(重要) | 系统时区 Asia/Tokyo(UTC+9),应用逻辑全用 Asia/Bangkok(UTC+7),差 2 小时。crontab 按系统时区触发,且这个 cron 版本不支持 CRON_TZ。已踩过一次坑:harrow_task_rollover.py 原定"曼谷00:05"写成 5 0 * * *,实际在曼谷 22:05(前一天)触发,导致每天任务永远生成不出来。已修正为"目标曼谷时刻 + 2 小时",见 crontab 注释。新增任何要求"曼谷几点触发"的定点任务,先 timedatectl 确认系统时区再换算。wealth_daily_accrual.py 的 crontab 同日已核实并修正为 0 23 * * *,详见 Wealth 交接页 →。 |
下一步建议
- 敲定 Gmail 同步方案:跟用户确认走 cowork 推送还是 OAuth,如果是 cowork 推送,需要生成鉴权 token 并实现
/emails/ingest 端点。
- 家长管理界面:任务模板 / 主课清单 / 固定课外班的可视化编辑页,API 都现成,工作量主要在前端。
- 找人核对科学大纲:三个
science_*.json 文件的知识点覆盖面和准确性。
- 观察真实使用几周后,SPEC §8 里的几个"临时默认规则"(5 轮自动放行、16-20点30分钟冷却、弹性配额延迟窗口)看是否需要调整。