日期:2026-07-22
我们服务的客户是某 K12 连锁教育机构,旗下数十个校区分布在多个城市。项目启动前,排课这件事完全依赖各校区教务用 Excel 手工拼表:每周把老师、教室、班级时间填进一张大表,靠肉眼找冲突,撞课、撞教室、师资连堂过劳是常态,月底调课更是教务的噩梦。总部想统一看一眼各校区课时饱和度,只能等教务上报,且各校区口径不一,横比没有意义。
更深层的问题是学情数据散落。每个校区的测验、作业、错题都各自存着,总部教研拿不到跨校区汇聚的学情,分层教学、个性化练习推送更无从谈起,好的教研经验也只在单个校区内部流转。我们当时定的目标分两层:先把"排课"这件高频痛点用约束求解真正解决,再把各校区学情数据汇聚到总部教研中台,支撑分层教研与个性化学习。我们采用的是统一数据中台能力,将各校区业务系统通过标准化接口接入,作为后续数据打通的基础,总部做口径治理,校区做本地执行与上报。
落地场景分两条主线。第一条是校区排课引擎:教务在系统里维护师资资质、教室容量、班级课表需求,引擎做多约束求解,输出无冲突课表,并支持"假设变更"预演,改课前先看影响面。第二条是跨校区学情汇聚:各校区测验与作业数据按统一口径(知识点维度、难度维度、得分率)上报到总部教研中台,总部可看区域学情热力,并基于汇聚数据做分层教学与个性化练习推送。教务改动一节课,系统即时做增量冲突检测,影响到的班级、老师、教室一览无余,再决定要不要发布,家长通知与老师考勤也随之联动更新。
挑战一:多约束排课求解(师资/教室/时间)。 排课本质是带硬约束和软约束的约束满足问题(CSP),不是简单排班。我们把硬约束设为:同一师资同段不可重复、教室容量大于等于班级人数、师资科目资质白名单;软约束设为:教师日课时均衡、减少学生换教室次数、连堂优先。求解引擎我们用 OR-Tools 的 CP-SAT,目标函数是软约束惩罚加权和,时间窗口设 30 秒兜底,超时返回当前最优解并标注未满足项。踩坑点:一开始把"教师连续上课不超过 N 节"设成硬约束,结果小规模校区直接无解,改回软约束并加权重后求解率明显回升,课表质量也更自然。
挑战二:跨校区学情数据汇聚与口径。 各校区对"知识点"的命名不统一,A 校区叫"一元二次方程",B 校区叫"二次方程(一)",直接汇聚会分裂成两个知识点。我们建了一套知识点本体映射表,把各校区本地标签映射到总部标准知识点树,映射不到的进入待审队列人工补,不让脏标签进主库。上报管道用分批增量加校验和,断点可续传。为避免总部库被高频写爆,汇聚走消息队列削峰,落湖后再做口径归一化,查询走读副本,写入与查询压力彻底解耦。
挑战三:分层教学与个性化练习推送。 汇聚后的学情若直接按总分分层,会掩盖偏科,给数学弱语文强的学生推了语文拔高题毫无意义。我们用知识点维度的得分率向量做聚类,把学员分到"基础巩固 / 能力提升 / 拓展拔高"三层,每层匹配不同的练习题库与讲解资源。推送环节我们接入了我们采用的 AI 私有化部署底座:向量数据库层存储知识点—题目—讲解的嵌入,RAG 知识库层按学员薄弱知识点召回相似题与讲解,由 AI 网关统一鉴权限流后下发到校区端。这里关键是把"召回相关性"与"难度梯度"分开控制,避免给基础层推了拔高题,反而打击学习信心。
挑战四:排课变更影响面评估。 教务常因老师请假要临时调课,如果全量重排会牵动大片课表,引发家长投诉和教务返工。我们做了增量重排:只锁定受影响的时间片与资源,做局部求解,并用影响面分析给出"本次变更波及 X 个班级 / Y 名老师 / Z 间教室"的清单,教务确认后发布。架构上排课结果缓存为不可变版本,每次变更生成新版本并保留 diff,便于回滚与审计,总部也能回溯任意一周的课表演进。
以下为脱敏示意数据,仅反映趋势量级,非审计级数字:
| 指标 | 改造前 | 改造后 | 说明 |
|---|---|---|---|
| 排课冲突率 | 约 12% | 降至约 1.5% | 约束求解替代手工拼表 |
| 教室利用率 | 约 58% | 提升至约 76% | 连堂与换教室优化 |
| 师资利用率 | 约 64% | 提升至约 80% | 日课时均衡分配 |
| 学情数据覆盖率 | 约 45% | 提升至约 92% | 统一口径汇聚 |
| 学员续费率 | 约 71% | 提升至约 81% | 分层教学 + 个性化推送 |
案例片段(已脱敏): 排课约束与冲突检测配置(节选): hard: teacher_no_double_book, room_capacity >= class_size, teacher_qualification(白名单) soft: balance_daily_load(权 3), minimize_room_switch(权 2), prefer_continuous_blocks(权 1) solver: engine=or_tools_cp_sat, time_limit_sec=30, objective=weighted_soft_penalty conflict_detect.on_change: incremental_recheck, scope=[same_room, same_teacher, same_student_group] 影响面:调课 1 节 -> 波及 3 班 / 2 师 / 1 教室,局部重排耗时 0.8s
第一,排课是约束求解,不是排班。这个项目里最值得强调的认知是:不要把排课当成"把人填进格子",它是一组带优先级的目标优化。硬约束保底线、软约束提质感,求解器超时返回次优解也比卡死强。先把模型建对,再谈性能与交互,否则再漂亮的界面也排不出能用的课表。
第二,学情先汇聚、再个性化。我们当时顶住"直接上 AI 推题"的催促,先花力气做知识点本体映射与口径归一,否则汇聚层就是一锅粥,个性化推送再花哨也只是精准地推错题。数据底座不正,上层智能全是空中楼阁,这点对做任何跨机构数据打通都适用。
第三,变更要可控、可回滚。排课结果一旦发布就关联家长通知、老师考勤,必须版本化。增量重排 + 影响面清单 + 版本 diff,让教务敢改、总部可审,也把"改一课"的代价从不可预知变成了可量化。
第四,跨校区系统接入别追求一步统一。先标准化接口接入、做增量汇聚与待审队列,把"不一致"显式暴露出来,比强行要求各校区改系统更现实。承认异构长期存在,用映射与待审消化差异,项目推进反而更快。