Spring AI驱动的智能作业全链路闭环:出题、批改、学情一体化设计
做教育系统的团队大概都有过这种体会作业模块看着简单真正要跑通却处处是坑。早些年我们做的作业功能说白了就是“老师传附件、学生交附件、老师再手动批改”批改结果存在一堆Excel里连基础的正确率统计都要人肉汇总。后来团队接到一个更具体的任务——在已有的企业级智能教学考试平台上建设智能作业模块要求作业从生成、布置、作答、批改到学情反馈全链条自动流转形成全业务闭环。我当时的第一反应不是兴奋而是头疼这哪里是做一个功能分明是要把教学流程里最琐碎、最依赖人力的环节全部数字化。现在回头看这次改造里最值得记录的不是某个AI接口调得多溜而是把一个业务闭环拆解清楚、再把Spring AI框架按需嵌入每段链路的过程。这篇文章我会把整套方案从头到尾讲透为什么选型Spring AI、闭环里到底哪些节点需要AI、出题/批改/学情报告这三类高频动作怎么工程化、模型不可靠时怎么设计兜底、上线前怎么算清成本和延迟最后走查一遍从教师建作业到学生错题重练的完整链路。适合正在做教育类系统、想在企业级Java栈里稳妥引入大模型能力的开发者参考尤其是那些已经受够了“demo能跑、上线就崩”的团队。1. 先画闭环作业全流程里哪些节点该由AI接手1.1 一个完整的作业生命周期长什么样很多人一提智能作业第一反应就是“AI批改”但真正落到企业级平台上批改只是中间一环。完整生命周期至少包含这几个阶段题目准备从题库选题或按知识点生成新题。作业组装把题目按题型、难度、分值组装成一份作业并绑定班级和截止时间。作业发布与作答学生端在线答题或拍照上传手写内容。批改与评价客观题自动判分主观题由AI按评分标准给分并写评语。成绩审核异常批改结果进入复核教师确认后发布成绩。学情分析与反馈按知识点聚合错误率、分析班级薄弱点给学生推送个性化反馈。错题重练根据分析结果生成新一轮巩固作业闭环进入下一轮。这条链路里最容易被忽略的是最后一步。如果学情分析结果没有回流到出题环节那整个系统只是“自动化批改工具”谈不上闭环。所谓全业务闭环核心就是让每个环节的数据都能被下一个环节消费最终让“学情数据”重新变成“出题输入”。1.2 六处AI介入点与“必须人来做”的边界我们把闭环拉直后逐个环节判断AI能做什么、不能做什么最终圈定了六处AI介入点环节AI介入方式人必须保留的动作题目准备按知识点、难度、题型生成题目和解析教师审核题目质量、修改选项作业组装根据学情数据推荐题目组合教师调整题量和分值作答录入视觉模型识别手写作业转成文本学生对识别结果确认批改评价主观题按评分标准自动给分并写评语复核异常分数、处理争议学情分析自动聚合知识点失分率、生成班级报告教师决定教学干预动作错题重练基于薄弱知识点生成巩固题教师确认作业范围与难度这里有一条很关键的边界AI在闭环里充当的是“高并发执行者”不是“决策者”。题目可以批量生成但必须有人审核分数可以自动给出但必须能被复核报告可以自动产出但教学动作必须由人拍板。把这句原则写进需求文档里后面设计兜底和权限模型时能少走很多弯路。1.3 闭环的回路学情结果反向驱动出题闭环之所以叫“闭环”是因为它有一条反向回路。学生完成作业后系统会得到一组结构化数据每道题的正确率、每个知识点的错误率、每个学生的薄弱标签。这些数据不能只躺在报表里要成为下一轮出题的输入。我们的做法是在题库表里增加两个冗余字段knowledge_point_code知识点编码和difficulty_level难度系数。每次作业批改完成后异步任务会更新这两个字段对应的班级历史错误率。出题模块在选题时可以按“薄弱知识点优先、难度逐步递增”的规则来推荐题目。这样从第一份作业到第N份作业系统会慢慢形成一条针对班级实际学情的训练链而不是永远随机出题。2. 技术底座选型为什么是Spring AI而不是自研HTTP调用2.1 自研调用大模型API的三个现实阻碍团队里一开始有同学主张自己封装一个HTTP客户端理由是“就一个接口几行代码的事”。这个想法在Demo阶段没问题但放到企业级平台里会遇到三个现实阻碍。第一个是适配层问题。大模型服务商的接口风格并不一致鉴权方式、报文结构、流式协议各有差异。如果我们在业务代码里直接写死某一家的HTTP调用以后换服务商或者做多服务商负载均衡几乎要改动所有调用点。第二个是结构化输出问题。批改、出题这些场景都需要模型返回JSON而JSON解析、非严格输出、字段缺失这些问题如果每个业务都自己处理一遍代码会非常散排查问题也麻烦。第三个是通用能力缺失。Prompt模板管理、对话上下文、Embedding、输出转换器这些能力自研的成本远比想象中高。Spring AI解决的问题本质上就是把“大模型能力”像一个数据源或消息中间件那样接入Spring Boot应用的编程模型。业务开发人员不需要关心底层HTTP协议细节只需要面向ChatClient接口编程。2.2 接入后的核心编程模型从“发请求”到“像查数据库一样用模型”接入Spring AI后最直观的变化是调用方式。我们不需要手动构建HTTP请求、处理鉴权头、解析返回体而是通过一个注入进来的ChatClient来发起交互。配置也走统一的Spring配置体系不同适配器只是配置前缀不同。spring: ai: model: api-key: ${LLM_API_KEY} base-url: ${LLM_BASE_URL} chat: options: model: ${LLM_MODEL_NAME} temperature: 0.2换个模型服务商时业务代码不用动改配置和依赖即可。这一点在企业项目里价值非常大因为模型厂商的定价和质量变化很快平台要保持可选性和可替换性。核心编程模型有两个让人舒服的点。第一是PromptTemplate可以把评分标准、题目内容、学生答案都变成模板变量而不是靠字符串拼接第二是entity()方法可以直接把模型输出映射成Java对象省去了手写JSON解析的步骤。整体感觉是你在写业务逻辑而不是在研究模型协议。2.3 一个最小可运行的AI批改服务骨架以主观题批改为例我给出一个能跑的骨架。先定义批改结果的结构public record GradingResult( Integer score, String comment, ListString knowledgePoints, String errorType, String reviewStatus ) {}然后是核心批改服务Service public class AiGradingService { private final ChatClient chatClient; private final PromptTemplate promptTemplate; public AiGradingService( ChatClient.Builder builder, Value(classpath:prompts/grading-prompt.st) Resource promptResource) { this.chatClient builder.build(); this.promptTemplate new PromptTemplate(promptResource); } public GradingResult grade(StudentAnswer answer, Question question, GradingRubric rubric) { ChatResponse response chatClient.prompt() .system(promptTemplate.render(Map.of( rubric, rubric.toPromptText(), question, question.getContent(), standardAnswer, question.getStandardAnswer()))) .user(answer.getContent()) .options(completionOptions()) .call(); return response.getResult().getOutput() .get(answer, GradingResult.class); } private CompletionOptions completionOptions() { return ChatOptions.builder() .withTemperature(0.2) .build(); } }这段代码看起来简单但它已经涵盖了三个核心动作组Prompt、调用模型、把输出映射成结构体。后面所有AI业务动作包括出题和学情报告都是在这个骨架上叠加业务规则。2.4 结构化输出的坑JSON可靠性比你想的更脆弱用entity()虽然方便但别天真地以为模型永远会输出合法JSON。我们在联调阶段遇到过三种情况返回了多余的前缀文字、JSON里多了一个逗号、某个字段在特定输入下被省略。解决办法是双保险Spring AI本身会做一次转换和修复但我们在业务层还会再做一次校验和填充。比如score为空时直接判为“待复核”而不是给默认值。另外对于批改这种对数字敏感的场景建议在提示词里强制要求模型“只输出JSON不要输出任何解释文字”并且把结构样例写进系统提示词。等到跑稳定了再逐渐放宽。3. 三类核心业务动作的工程化落地出题、批改、学情报告3.1 智能出题把知识点变成可入库的题目数据智能出题的第一步不是写提示词而是定义题目的结构化标准。我们给题库设计了这样的核心字段题型、知识点编码、难度系数、认知维度、题干、选项列表、正确答案、解析、标签列表。AI生成题目的任务就是输出一个符合这些字段的JSON。提示词设计上我们把“知识点描述”和“难度说明”作为输入要求模型按指定格式生成题目。为了控制质量还要求模型在同一道题里同时输出“解析”解析里必须包含用到该知识点的推理过程。这样做有两个好处一是题目入库时就能直接带上解析二是批改主观题时这份解析可以作为参考答案的一部分。出题结果不能直接入库。我们在入库前加了两层校验硬校验检查选项数量、答案是否在选项中、题干是否为空软校验则由教师在一键导入前抽检。这里的原则是宁可让AI少出几道题也不能让有问题的题目直接流到学生端。3.2 主观题批改评分标准、参照答案与一致性控制主观题批改是整个闭环里最难也是最容易出争议的部分。我们总结了三个关键做法。第一把评分标准完全塞进提示词。评分标准不能只写“满分10分答对给分”而要按维度拆开。比如一道材料分析题我们会定义逻辑性、知识点准确性、表达完整性三个维度每个维度对应不同分值和得分档位描述把这些结构化成评分量表文本。第二温度参数调低。批改不是创作分数稳定性比多样性重要。所以我们的批改Prompt统一使用0.2左右的目标温度尽量让同水平的答案得到相近的分数。第三让模型“先写理由再打分”。提示词里明确要求模型先输出简短的评分理由再输出总分。实际测试下来强制先推理再给分能让分数波动明显下降因为模型在推理过程中会自然对照评分标准。输出结构设计成只允许输出JSON格式如下 { score: 0-100, comment: 评分理由, knowledgePoints: [知识点编码1], errorType: concept_error|calculation_error|incomplete }批改完成后我们还会做一次波动性抽测对同一份标准答案重复批改三次分差超过设定阈值时把该题标记为“低置信度”进入人工复核。这个方法成本不高但能显著提升教师对AI批改的信任度。3.3 学情报告生成让AI只做文字加工不做数据编造学情报告是另一个翻车高发区。早期版本我们让AI直接生成“本月全班作业情况报告”结果AI经常编造数字比如“正确率提升15%”这种没有任何依据的说法这在教学场景里是不能接受的。后来我们把生成思路改成“数据由系统算文字由AI写”。系统先把每个学生的知识点失分率、错题分布、作业完成时长、正确率趋势算成结构化数据这些数据来自批改结果表是确定的、可追溯的。提示词里明确要求AI只能基于给定的JSON数据做文字描述不得自行增减数字。输出同样走结构化映射随后把“数据摘要”和“AI评语”分开展示教师一眼就能看到哪些数字是系统算出来的哪些文字是AI润色的。实测下来这个模式既保住了效率又避免了最致命的“AI凭空捏造数据”问题。4. 模型不可靠闭环就得兜底可靠性设计与降级方案4.1 三档兜底规则判分、二次批改、人工复核队列大模型再强也是概率系统不能把业务命脉压在单次调用上。我们设计了三档兜底策略从轻到重依次生效规则判分优先客观题、填空题、公式题这种有明确标准答案的完全不经过大模型用传统正则或精确匹配直接判分。这不仅省钱也避免了大模型在简单题上犯错。二次批改与置信度校验主观题AI批改完成后若分数落在边界区间或与班级均值偏差过大触发二次批改。两次分差超过阈值直接进入人工复核队列。人工复核队列所有进入复核的题目都带着“AI评分理由、原始答案、评分标准”三件套展示给教师教师只需做确认或修改。复核后的分数会覆盖AI结果并且记录操作日志。这套机制上线后教师投诉率比预期低很多因为大多数争议题都能通过复核队列被人工接住而不是直接变成最终成绩。4.2 批改任务的状态机与幂等设计作业提交不是一次性同步请求批改过程要支持重试和状态流转。我们为批改任务设置了一个状态机待批改、批改中、批改完成、复核中、复核完成、已发布。状态变化全部记录在业务表里每次重试都基于当前状态判断是否允许执行。幂等设计是这里最容易遗漏的点。重试逻辑如果没有幂等保护可能出现“同一份作业被重复扣费、重复批改”的问题。我们的做法是用submissionId作为唯一键在批改任务表上建立唯一索引消费批改消息前先查询任务状态如果是“批改完成”就直接返回不再调用模型。同时给任务表加一个retry_count字段超过最大重试次数的任务自动转人工复核避免死循环。4.3 提交接口为什么要异步化而不是同步等待AI刚做第一版时我们的提交接口是同步调用模型批改的结果学生提交完作业后接口平均要等8到10秒才返回配合上重试甚至能卡到30秒。教学场景里断网、弱网、误点提交都是常态同步等待几乎不可接受。改造方案是异步任务化学生提交作业后接口立即返回“已提交批改中”同时发送一条批改消息到任务队列消费者收到消息后执行规则判分和AI批改批改完成后更新成绩表并通过WebSocket或轮询通知学生端刷新。这样把“写作业的交互链路”和“AI批改的耗时可变链路”彻底解耦用户体验和系统稳定性都得到了改善。5. 账单、延迟与限流上线前最容易被低估的三件事5.1 把token成本按天算清楚再定上线节奏很多项目立项时没有算过模型调用成本上线后才被账单打醒。这里给一个计算思路假设每日批改主观题5000道每道请求约2000 token题目、答案、评分标准、学生答案模型输出约300 token合计单次2300 token。按某模型服务商1M token价格X元估算单次成本约为0.0023X元月成本约等于0.0023X × 5000 × 30。如果X是20元月成本约6900元如果X是80元就到27600元。算清这张表的意义在于你可以根据预算反推功能边界。我们的实际做法是把“规则能判的题绝不让AI判”和“低置信度才二次批改”这两条策略执行到位后整体成本直接下降三分之一效果却没有明显变化。5.2 P95延迟过高时先改业务时序而不是加机器AI服务的延迟是你花钱也难压缩的。模型推理的P95延迟可能高达10秒以上加机器并不能让单次推理变快但可以通过改业务时序来“藏”掉延迟。我们把耗时操作全部放进后台批处理作业截止后统一批改而不是每个人提交时立刻调模型班级作业量较大时按题目分组给不同任务队列并发批改前端用“批改中”状态配合轮询而不是让用户傻等。另外模型调用要做超时控制。我们的经验是设成5秒超时、最多重试两次超过就把任务转人工复核。宁可让人来兜底也不能让批改任务在超时重试里无限堆积。5.3 限流、重试与熔断的参数取舍多人同时提交作业时模型服务商API往往有QPM限制。我们用了两层限流应用层用信号量限制并发调用数比如单实例最多20个并发批改请求业务层用队列控制批改任务的消费速率保证单位时间内调用量在配额内。重试策略也要分场景。出题这类低优先级任务重试三次、失败丢弃都行批改这类高优先级任务重试两次后转人工学情报告这类每天只跑一次的任务失败后直接进告警第二天重跑即可。熔断方面如果连续调用失败率超过30%我们直接打开熔断开关所有任务进复核队列避免模型故障把整个系统拖垮。6. 从教师创建作业到错题重练一次完整的闭环走查6.1 教师端选题、出题与作业发布教师创建作业时系统会根据最近一次学情分析结果默认勾选薄弱知识点。教师可以选择“从题库选题”或“AI生成新题”。选题走推荐逻辑按知识点错误率排序优先推荐薄弱知识点下难度系数适中的题目生成新题则调用智能出题服务批量产出候选题。教师核验后确认发布作业自动关联班级和截止时间状态变为“已发布”。这一步里的关键设计是“教师永远有最终确认权”。无论是AI推荐的题目还是AI生成的题目都只进入“待选列表”不会自动进入正式作业。这个设计让教师愿意用这个功能因为没有哪位老师愿意把教学内容的控制权完全交给系统。6.2 学生端在线作答、拍照上传与结果反馈学生收到作业后可以在线作答。客观题直接勾选主观题在文本框输入或拍照上传。拍照场景走视觉模型识别把手写答案转成文本学生确认无误后提交。提交接口按前面说的异步模型处理学生立刻看到“已提交”不用等着同一道题批改完。批改完成后学生端展示的不只是分数还包括评分理由、涉及的知识点标签以及一句简短的改进建议。我们特意把“评分理由”和“知识点标签”放在显著位置因为对错题而言学生最需要知道的是“错在哪里”而不是一个干巴巴的数字。6.3 系统端批改、复核、学情归档与下一轮出题所有提交进入队列后系统先做规则判分再对主观题做AI批改最后经过置信度校验。异常题进入人工复核队列教师处理完成后成绩正式发布。同一时间异步任务开始聚合学情数据每道题的正确率、每个知识点的错误率、每位学生的薄弱标签统一写入学情宽表。周期结束后教师后台会出现“生成错题重练作业”的按钮系统根据本周期薄弱知识点自动组装一份巩固练习难度相较于原作业略微下调。教师确认发布后同一个闭环进入第二轮。到了这一步才算真正跑通了“作业—批改—学情—再作业”的完整回路。最后说一点个人体会。我在整个方案落地过程中受益最大的一条原则是永远不要对大模型的输出抱百分之百信任也永远不要把业务核心逻辑设计成依赖某一次模型调用的成功。大模型在闭环里像一位效率极高的助教能帮你承担大量并发和重复判断但最终的教学决策、质量审核、争议处理必须保留人的介入通道。No matter demo跑得多顺先设计好兜底和降级再谈智能。这套思路不只在作业模块适用任何把AI能力嵌入企业业务闭环的项目都值得提前想清楚这一点。