AI教案生成平台全栈源码解析:从模板引擎到Spring Boot实践
先说结论这套豫唐智能教案在线生成平台源码是我过去大半年从零到一攒起来的一套全栈项目。核心功能一句话就能讲明白老师输入教材版本、学科、年级、课时主题平台自动生成一节结构完整、能直接拿去改的教案。从学科知识结构化、生成引擎、内容审核到在线预览和文档导出全部包含在源码里不是那种只有一个登录页的演示品。这套东西适合谁看我觉得两类人最值得参考一类是做教育信息化方向的技术开发想知道 AI 生成能力怎么真正落到具体业务场景里另一类是自己有教学资源平台建设需求、想拿一套能跑通全流程代码做二次开发的团队。文章后面所有表结构、目录划分、部署步骤都是可以直接落地的不是概念层面的泛泛而谈。如果只挑一个模块读我建议直接跳到生成引擎那一节那是整套源码的灵魂。1. 从“找教案”到“写教案”这个项目解决的现实问题1.1 一线教师找教案的真实痛点我和几个做教师培训的朋友聊过很多次他们提到一个很普遍的现象很多老师备课时第一反应不是自己从头写教案而是上网搜现成的。可搜出来的东西问题一大堆。一是内容旧。教材在改版课程标准在更新但网上大量的教案还是三五年前的很多表述早就不适用了。二是匹配难。同一个知识点人教版、部编版、北师大版学段安排都不一样找到的教案常常对应不上手头那本教材的具体单元。三是排版乱。下载下来要么是图片格式没法编辑要么是复制粘贴后格式全丢表格错位、编号混乱整理一份能用的文档比重新写一遍还费劲。四是质量参差。有些教案的教学目标写得像口号重难点不清晰教学过程就是简单的“教师讲、学生听”看不出设计思路。这些痛点叠加在一起老师的真实需求其实不是“下载一份现成教案”而是“获得一份结构规范、内容匹配当前教材、又能快速修改成自己风格的底稿”。这和很多工具产品理解的需求是有偏差的。1.2 市面工具和刚需之间的落差市面上也不是没有教案生成工具。一类是传统资源库本质是把上面说的“搜索下载”搬到网上内容库做大了匹配和更新的问题依然在。一类是后来出现的 AI 辅助写作工具能生成一大段文本但生成出来的东西很多是“正确的废话”——教学目标、重难点、教学过程都写得像教科书例句没有针对性老师还是得逐句改。我分析过这类工具的毛病核心是它们把“写教案”当成了“写作文”。教案不是一篇自由发挥的文章它是一份有固定结构、有明确要素、要服务于课堂教学过程的工程文档。教学目标要对应课标要求重难点要基于学生学情教学过程要有导入、新授、巩固、小结这样的环节递进。没有结构约束的生成内容再华丽也落不了地。1.3 我最终想做的产品形态所以我对这个平台的定位从一开始就定死了不是帮老师“代写”而是帮老师“起草一份合格的底稿”。具体来说就是让老师输入尽量少的信息平台基于内置的学科教案结构模板结合大模型的生成能力产出一份结构完整、环节齐全、表述基本规范的初稿然后老师在线编辑、修改、导出整个过程不超过十分钟。整个项目我起名叫“豫唐智能教案在线生成平台”名字里的“豫唐”其实就是我当时接这个项目时服务的教研室名称没有太多讲究。源码除了生成能力还包括用户登录、生成历史、教案列表、在线编辑、导出 PDF、收藏评价这些周边模块目的是让它成为一个可以真实托管运行的产品而不是一个只包含核心算法的代码片段。2. 平台总体架构与源码目录设计2.1 技术栈选型与取舍先说技术选型。这套源码后端用的是 Spring Boot 3.x MyBatis-Plus MySQL Redis前端是 Vue 3 Element Plus生成侧对接的是大模型 API但我在代码里抽象了一层 Provider 接口理论上可以切换不同的模型服务商。为什么要这么选我个人的原则是教育类项目通常要交给学校或区域教研室部署他们手头的运维能力普遍不强所以技术栈越“常见”越安全。Spring Boot 加 MySQL 这套组合懂的人多出了问题好找人排查Redis 就用来做生成任务的队列缓冲和用户登录态的存取不引入太重的消息中间件前端用 Vue 3 是因为它生态成熟Element Plus 的表格、表单组件能很快拼出后台管理的界面适合这种重交互、重列表的工具型产品。没有选微服务这是一个有意的决定。这类项目的并发量不会特别高单体能省掉大量部署和运维成本。等真的需要拆分了按源码里的模块边界去拆也不难。2.2 后端源码模块划分拿到源码后先看后端根目录我按业务边界分成了这几个 Maven 模块模块名职责说明yutang-common通用工具类、统一返回结果、异常处理、常量定义yutang-system用户、角色、登录认证、权限拦截yutang-template教案模板管理、学科配置、结构节点定义yutang-generator生成引擎核心模块提示词组装、模型调用、结果解析yutang-doc教案文档管理、在线编辑历史、导出服务yutang-admin后台管理接口面向管理员的前端接口聚合层这样的划分有什么好处简单说就是让“生成”和“管理”两条链路互不干扰。老师调用生成接口时走的是 yutang-generator 模块管理员维护模板时走的是 yutang-template 模块。后续如果把生成引擎单独抽出来做一个独立服务模块边界已经替你划好了直接把 yutang-generator 依赖的接口抽成 Feign 或者 HTTP 调用就行。2.3 前端页面组成前端不算特别重但页面覆盖了完整的用户操作闭环。我数了数一共有这几组主要页面登录注册页支持账号密码登录后按角色切换路由工作台首页展示最近生成的教案、快捷模板入口、使用统计新建教案页选择学科、年级、教材版本、单元课时填写主题点击生成生成结果页左侧展示教案结构目录右侧展示可编辑的 Markdown 内容教案列表页支持按学科、时间筛选支持删除和导出模板管理页管理员可维护教案结构模板和每个节点的撰写要求系统设置页管理员可配置模型 API 地址、密钥、生成参数。有一点我想特别强调生成结果页的编辑区是这套产品体验的关键。老师拿到 AI 生成结果后大概率是要动手改的。所以我没有把生成结果做成一张静态图片或不可编辑的纯文本而是嵌入了 Markdown 编辑器左侧目录支持点击跳转右侧内容支持直接修改。修改后重新导出系统保存的是修改后的版本。2.4 核心数据表设计数据库表不算多但每一张都有用处。最核心的是这五张template_info教案模板表存学科、年级、版本、结构节点。template_node模板节点表定义一份教案内部有哪些固定环节以及每个环节的撰写要点。lesson_plan教案主表存生成结果、最终内容、状态、所属用户。generate_record生成记录表存每次调用模型的入参、出参、耗时、成功失败标记用于排查和优化。user_info用户表存教师和管理员账号。以lesson_plan表为例核心字段大概是这样的CREATE TABLE lesson_plan ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL COMMENT 所属用户ID, template_id bigint(20) DEFAULT NULL COMMENT 使用的模板ID, title varchar(200) NOT NULL COMMENT 课时标题, subject varchar(50) DEFAULT NULL COMMENT 学科, grade varchar(50) DEFAULT NULL COMMENT 年级, version varchar(100) DEFAULT NULL COMMENT 教材版本, content longtext COMMENT 教案内容Markdown格式, status tinyint(4) DEFAULT 0 COMMENT 状态0草稿 1已生成 2已修订, create_time datetime DEFAULT NULL, update_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_subject_grade (subject, grade) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT教案主表;这里有一个细节content字段直接存 Markdown 原文而不是存 HTML。原因是 Markdown 是纯文本后续无论是渲染成网页还是再编辑都方便如果直接存 HTML再回填到编辑器时格式还原很痛苦这也是我踩过坑之后才定的方案。3. 教案生成引擎这是源码里最值得读的部分3.1 教案结构模板化整个生成引擎的地基是“教案结构模板”。我在前面提过教案不是自由文本所以引擎里不能只给模型一句“帮我写一份教案”而是必须告诉模型这份教案包含哪些固定环节每个环节应该包含什么内容。以最通用的一节新授课为例我内置的模板节点是这样的教学目标知识目标、能力目标、情感目标教学重点与难点重点、难点、设计意图学情分析学生已有基础、可能存在的问题教学过程导入环节、新授环节、巩固练习、课堂小结板书设计结构化板书内容作业设计基础作业、进阶作业教学反思预设反思要点每个节点不只是名称还要有“撰写要求”。比如教学目标节点模板里会写“表述要使用‘学生能够……’的句式目标要可观察、可检测不要泛泛而谈‘培养兴趣’这类不可评价的表述”。这些撰写要求会在组装提示词时跟着结构一起传给模型。你可能会问为什么不把结构直接写在提示词里非要搞一张模板表答案是为了“可配置”。不同学科的教案环节差异很大体育课没有板书设计但有多准备活动和游戏环节英语课强调热身导入和情景对话科学课强调实验器材和探究过程。如果结构写死那这套系统就只适合某一门课。模板表就是把学科差异从代码里剥离出来管理员可以随时新增一门课的模板不用改一行代码。3.2 提示词上下文的组装策略模板定义好了接下来就是生成链路的核心环节提示词组装。这一步直接决定生成质量。我最初的做法很粗暴就是把用户输入的学科、年级、课时标题和模板节点拼接成一段话丢给模型。结果生成的教案非常“空”教学目标写的是“通过本节课的学习学生能够掌握相关知识”重难点写的是“重点本节课的重点内容”完全是正确的废话。后来我调整了策略在提示词里增加了三类信息第一类是教材背景约束。比如“人教版小学数学三年级上册第二单元‘万以内的加法和减法’课时主题是‘两位数加两位数口算’请结合该学段学生的认知规律设计教学内容”。第二类是格式约束。要求模型必须按给定的 JSON 结构返回每个节点对应一个对象。第三类是质量约束。就是前面提到的节点撰写要求。组装后的系统提示词大致长这样你是拥有20年教龄的【小学数学】教研员请根据以下要求撰写一份教案 教材版本人教版 年级三年级上册 单元第二单元 万以内的加法和减法 课时主题两位数加两位数的口算 教案必须包含以下结构节点并严格按 JSON 格式返回 { teaching_objectives: 教学目标使用学生能够...句式表述, key_points: 教学重点, difficult_points: 教学难点, student_analysis: 学情分析, teaching_process: 教学过程需包含导入、新授、巩固、小结四个环节每个环节标明时间分配, blackboard_design: 板书设计用层级结构表达, homework_design: 作业设计分为基础题和拓展题, reflection: 教学反思 } 要求 1. 教学目标要可观察、可评价不要泛化表述 2. 教学过程的设计意图要写在对应环节后面用括号标注 3. 所有内容使用简体中文控制在一课时40分钟容量。这个提示词模板我调了很多个版本最后沉淀成一个 JSON 配置存在数据库里管理员可以随时改。这也是一个经验提示词不要硬编码在代码里否则每次调优都要重新发一次版本。3.3 格式化输出与校验兜底生成环节最让人头疼的是模型返回结果不稳定。你要求它返回 JSON它偶尔会夹带解释文字你要求每个字段都有值它偶尔会把两个节点合并在一起写。我在代码里做了一个三层兜底第一层拿到模型返回后先用正则把 JSON 块抽取出来。我会在系统提示词里明确要求“只返回 JSON不要包含任何解释文字”但模型不总是听话所以代码里要先做一次清洗把前后多余的说明文字去掉。第二层用 JSON 解析库解析解析失败就进入重试逻辑重新调用一次模型并且给模型附加一句提醒“你上次返回的格式不是合法 JSON请严格按 JSON 结构重新生成。”这个“错误反馈重试”机制实测下来能挽回相当一部分失败请求。第三层解析成功但字段缺失。比如某个节点没生成内容我会用兜底文案填充比如“请根据班级实际情况调整本节教学目标”并给教案打上一个“需人工完善”的标记提醒老师不要直接拿去用。这三层处理写完整理下来生成失败率从最初的 20% 左右降到了 3% 以内。源码里的JsonOutputParser和RetryHandler两个类就是做这些事的。3.4 异步任务与状态机生成是一个耗时操作不能像普通查询接口那样让前端一直等着。我的做法是引入异步任务机制把生成过程做一个状态机。状态一共五个PENDING、PROCESSING、SUCCESS、FAILED、CANCELED。用户在前端点击“生成”后后端会创建一条生成记录状态置为PENDING然后把任务信息放进 Redis 队列前端轮询生成结果。线程池里的消费者从队列里取任务把状态改为PROCESSING然后调用模型接口拿到结果后落库状态置为SUCCESS。如果某个环节异常状态置为FAILED并把失败原因写入error_msg字段。为什么用 Redis 队列而不是数据库轮询因为数据库轮询在高并发下会打大量无效查询而 Redis 的BLPOP命令能实现可靠的阻塞式消费代码简单资源占用还小。线程池参数我也做了调优核心线程数 8最大线程数 32队列容量 200超过上限直接提示“系统繁忙请稍后再试”保护了后端服务不会被突发的生成请求打垮。4. 源码的本地部署与跑通流程4.1 环境准备清单拿到源码想在本地跑起来我建议先确认环境。这套代码没有用很新奇的框架比较挑版本的地方主要是 JDK 和 Node。软件版本要求说明JDK17 及以上Spring Boot 3.x 强制要求Maven3.6.3 及以上后端依赖管理MySQL8.0 及以上使用 utf8mb4 字符集Redis6.2 及以上用作队列和缓存Node.js18 及以上前端构建npm/yarn任一即可前端依赖安装这里提醒一句JDK 不一定要装 Oracle JDKOpenJDK 完全没问题我用的是 Eclipse Temurin 17稳定得很。4.2 数据库初始化后端项目src/main/resources下有一个db文件夹里面放着schema.sql和data.sql。前者建表后者初始化模板数据和管理员账号。执行方式很简单mysql -u root -p schema.sql mysql -u root -p data.sql如果数据库已经跑起来了也可以直接通过客户端导入。导入完成后建议先检查template_info和template_node两张表有没有数据。我第一次跑起来后页面还是空的排查半天发现是忘了执行data.sql模板数据没初始化导致生成页面选不了学科。4.3 配置文件与密钥后端配置文件是application.yml主要是数据库连接、Redis 连接和模型 API 配置三块spring: datasource: url: jdbc:mysql://localhost:3306/yutang_edu?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai username: root password: your_password redis: host: localhost port: 6379 database: 0 ai: provider: openai api-base: https://your-model-endpoint api-key: ${AI_API_KEY} model: your-model-name max-tokens: 4000 temperature: 0.7特别注意api-key这一项。我没有把密钥明文写在application.yml里而是使用${AI_API_KEY}读取环境变量。这样做的原因是项目很可能被传到代码仓库一旦密钥提交上去随时可能被扫描工具抓到并造成费用损失。启动前先在系统环境变量里设置好AI_API_KEY代码就能正确读取。4.4 启动前后的自检清单启动步骤不复杂后端先起再起前端# 后端 cd yutang-backend mvn clean package -DskipTests java -jar yutang-admin/target/yutang-admin.jar # 前端 cd yutang-web npm install npm run dev但每次启动完我建议按这个顺序自检一遍访问/api/template/list是否能返回模板列表不能就查数据库连接和data.sql是否执行登录后台用默认管理员账号admin登录账号密码在data.sql里注释了第一次登录后立刻改掉新建教案页选择模板后点击生成看生成记录状态是否能从PENDING变成SUCCESS随便打开一份生成好的教案确认编辑器和导出按钮正常。这四步走完能通说明环境没问题了。后面再定制功能就可以在这个基础上改。5. 内容质量的守门员生成后的处理链路5.1 敏感信息过滤与合规检查教育类内容平台内容安全是绕不开的一环。模型生成的文本里偶尔会出现不适合课堂教学的内容比如不合适的表述、超出学段认知范围的知识点或者一些商业广告性的例子。我在生成结果落库前做了两层处理。第一层是关键词过滤整理了一个动态词库放在sensitive_word表里生成内容按节点逐个匹配命中就把对应节点标记出来。第二层是结构校验检查每个节点是否有实际内容如果某个节点内容过短或者和模板主题无关也会被标记为“待完善”。这里说一个实际的现象网上很多教案工具不重视这个环节直接拿模型生成结果给用户。一旦出现问题伤的是教师对工具的信任而且是不可逆的。我宁可让系统多几次“需人工完善”的提示也不让风险内容直接通过。5.2 教案版本管理与评价闭环老师拿到生成结果后一定会修改修改完之后这份教案其实已经“私有化”了它的价值甚至比 AI 初稿更高。因此源码里做了一个轻量的版本管理每次保存修订都会把当前 Markdown 内容作为新版本记录在lesson_plan_version表里保留修改前和修改后的对照。这份数据积累起来之后能做很多事。目前源码里已经实现了几个很基础的功能教师可以对教案进行收藏和评分管理员可以在后台看到每个模板的生成量、平均满意打分。这些数据能反向指导模板优化——比如某门课的教案打分一直偏低多半是模板节点设置和该学科的实际教学逻辑有冲突需要调整节点顺序或撰写要求。5.3 人工修订的协同逻辑还有一个比较细的设计就是“协同修订”的权限控制。管理员和老师看到的同一份教案操作权限不一样老师可以改内容管理员通常只看不改。在源码里我在教案主表上加了owner_type和audit_status两个字段。前者区分是老师个人创建还是平台示例教案后者区分是否经过审核。这样做的目的是让学校用起来时能做到“先审后发”避免内容问题流传到课堂。我知道这套逻辑对纯个人开发者来说可能有点重但如果目标是推到学校场景它反而是必要的。学校对内容审核的要求远比一般 C 端应用严格早点在数据结构里留出这个位置后续开发就不用推倒重来。6. 研发过程中的五个坑与排查思路6.1 长文本生成超时从同步等待到流式接入第一个坑出现在联调阶段。教案内容动辄一千多字模型推理时间有时会超过 30 秒。但网关和前端默认的超时时间都只有 30 秒导致页面经常报“网关超时”。排查链路是这样的先看日志发现模型接口调用非常久但接口逻辑没有报错再看前端发现请求被中断最后定位到是网关超时。最直接的解决方式是把网关超时调到 120 秒但这治标不治本用户等待的体验是在裸奔。后来我把生成接口改成了流式返回。yutang-generator模块里加了一个StreamCallBack接口模型生成的内容按段推送到前端老师能看到内容一个字一个字“打”出来等待的焦虑感小了很多。这是一个体验上的关键优化也是我觉得这套源码里最值得抄的代码之一。6.2 模型返回 JSON 解析失败的兜底机制第二个坑是 JSON 解析。模型经常返回一些超出预期的格式比如在 JSON 前面加了一段“好的以下是生成内容”的废话或者把某个字段的值里混入了换行符。我的排查思路其实和上面 3.3 节写得一样这里只补充一个细节第一次重试仍然失败时我最后再加一道低保逻辑就是把模型返回的原始文本整段存入数据库直接当成纯文本教案展示给教师。宁可展示格式乱一点也不能让用户点生成后什么都没有。这个“最差体验”的兜底反而保证了产品永远可用。6.3 Markdown 表格在预览端渲染错乱第三坑是前端渲染。生成结果中有相当比例的内容是表格形式的比如教学过程时间分配表。但模型返回的 Markdown 表格经常不规范要么缺少表头分隔线要么列数不对齐渲染出来后排版七零八落。我最初想从前端入手写一个表格修复插件越写越复杂。后来换了个思路在后端生成阶段就加了一个表格规范化函数检测到 Markdown 表格块先把分隔线补齐再把每行列数强制对齐缺的补空单元格多的截断。这个问题在源码里的MarkdownNormalizer类中处理是整个渲染体验的重要补丁。6.4 PDF 导出中文字体缺失第四个坑看似技术含量不高但坑得很深。用 OpenPDF 导出 PDF 时服务器上没安装中文字体导出的文档所有中文都变成了方块。我一开始在本地开发环境测试一切正常部署到 Linux 服务器后立刻翻车。原因很简单Windows 本地有完整的中文字体库而干净的 Linux 服务器没有。解决方案也不是去服务器上乱装字体而是在项目里打包一个开源中文字体文件用 FontFactory 注册进 PDF 导出组件里。这样换任何一台服务器导出效果都一致。源码里font/目录下放的就是这个字体资源不要删掉。6.5 高并发生成时数据库连接池打满最后一个坑是数据库连接池。上线后做了一次小规模的模拟并发测试用 50 个线程同时触发教案生成结果 MySQL 连接直接打满。看了日志发现大量请求卡在数据库连接等待上。排查下来问题不在 SQL而在生成接口的事务边界。生成调用模型那段时间长达几十秒如果整个生成流程都包在一个大事务里数据库连接就会被长时间占用。解决方式很简单把事务边界缩小只在插入教案记录和更新生成状态时开启事务调用模型的部分完全脱离事务使用独立的线程池连接。改造后连接池的占用率骤降同样的并发量不再报错。7. 想基于这套源码做二次开发可以先动哪里7.1 扩展更多学科和地区模板目前内置的模板覆盖了语数英这三大主科以及科学、体育等少数科目结构模板相对通用。如果要做区域化落地首先应该丰富模板库。比如小学英语的教案通常要加入“Lets talk”和“Lets learn”环节语文的阅读课要加入“品读感悟”节点这些差异通过后台模板管理就能配置不需要动代码。甚至同一个学科不同地区有不同课标要求也可以做地区版本模板。7.2 沉淀校本教案资源库这份源码其实很适合学校内部的教研场景。老师生成的教案经过修改、审核后沉淀下来就是一个不断增长的本校教案库。新教师备课前可以直接检索校内优秀教案学习本校本学科的教学思路比看网上的通用教案更有针对性。按现在的数据结构只需要在lesson_plan表上增加一个school_id字段就能支撑这个场景。7.3 把生成能力包装成接口服务如果你不想要前端界面只想要一套能对接现有教务系统的教案生成接口源码里的yutang-generator模块可以直接脱离主项目使用。它依赖的 Redis 和数据库表很少只需要template_info、template_node、generate_record三张表对外暴露一个类似POST /api/generateLessonPlan的接口就能跑起来。这个模块我尽量保持了内部逻辑的独立没有嵌套太多业务耦合。四月底我会抽时间把示例教案数据扩充一部分顺便把 PDF 导出的样式调整成更接近学校传统教案本的排版到时候会在项目文档里更新说明。现在的版本作为基础骨架我认为已经足够支撑大多数教育信息化场景的二次开发了有具体问题可以直接拉着源码跑一遍再聊。个人体会最深的一点生成质量不是靠调一次提示词就能做好而是靠模板结构、提示词、后处理、人工反馈这几层一起迭代出来的。源码里很多地方看起来“不优雅”比如那层 JSON 清洗正则但正是这些不起眼的兜底拼接起了可用性和稳定性的底线。如果你准备在这个方向自己做产品我的建议是先跑通最小闭环别急着堆功能把一份教案的生成质量磨到老师愿意用再谈规模。