这段时间刚好在做一个智能考试系统的课程设计收尾。项目立项时定的主题是“基于SpringAI的智能考试系统”,从一开始的框架选型讨论,到后面真正把智能组卷、AI批改、学情分析这些模块跑通,前前后后花了大约六周。这篇文章不写什么宏伟蓝图,就老老实实把系统设计时踩过的坑、想清楚的和没想清楚的关键点都整理出来,给后面打算用Spring AI做类似业务系统的同学一份可参考的设计笔记。
写之前先把项目背景交代清楚:这是一个面向高校课程考核场景的考试系统设计项目,目标不是做一个颠覆传统考试流程的平台,而是把现有考务管理中的高频痛点用AI能力解决掉。整个系统基于Spring Boot 3.x搭建,AI能力层依赖Spring AI框架,大模型底座在开发期用的是某开放平台提供的Chat模型(具体到生产环境再替换成私有化部署的模型或商用模型),数据库采用MySQL存储业务数据,Redis负责会话与缓存,向量数据库选用Milvus承载知识库检索。系统的核心用户角色是教师和学生:教师负责建题库、组卷、批改、查看学情分析;学生负责在线答题、查看解析、使用AI助教提问。
下面按设计顺序把整件事展开讲,涉及到的关键代码和配置都是项目里实际用过的简化版本,重点看设计思想和取舍逻辑。
1. 先从业务侧拆解:考试系统的“智能”到底加在哪
开始动手开发之前,我拉过一个清单,把传统在线考试系统从出题到考后分析的完整链路走了一遍。发现真正能让教师“少干活”、让学生“学得更准”的环节,其实是比较固定的几个点。
1.1 传统考试系统最让人头疼的四个环节
第一个环节是组卷。大多数老系统的组卷逻辑是“随机抽题+人工调整”。教师选定章节和题型之后,系统按比例随机抽,抽出来的试卷可能难度分布不合理,可能同一知识点重复出现,还可能覆盖不到关键考点。教师不得不一遍遍预览、替换、再预览,非常耗时。
第二个环节是题库维护。题库里的题往往是多年积累下来的,没有知识点标签,没有难度系数,更没有考查能力维度的标注。技术再好,抽题引擎也不可能从一堆没有元数据的题目里抽出高质量的试卷。所以要让组卷“智能”,必须先让题目“结构化、标签化”。
第三个环节是主观题批改。填空、选择、判断这类客观题计算机很好判,但是简答、论述、计算题常年依赖教师人工批改。教师在一百多份卷子里来回翻,手写评语、给步骤分,工作量极大并且评分一致性很难保证。
第四个环节是考后分析。传统系统给出的学情报告基本是分数分布、及格率这些统计数字。教师想知道“这个班对哪个知识点掌握得差”,得自己拿Excel去筛数据、看错题,光是把错题对应到知识点再做汇总,就需要大半天。
这四个环节对应的共同特征,是“重复度高、规则复杂、标准模糊”。而这类问题恰恰是大语言模型的强项——它可以理解语义、生成文本、归纳模式,并且支持一定程度的逻辑推理。用Spring AI来做系统级集成,主要就是让这些能力以工程化的方式进入真实考务流程,而不是做一个“聊天窗口”。
1.2 明确智能化的边界:能做和不该做的
这一步非常关键。定义“智能边界”不是说能否调用大模型,而是“什么环节可以让模型自主决策,什么环节必须由人来兜底”。我在设计文档里明确写了几条线,项目组后面所有争议都靠这几条线裁决。
第一条:AI负责“生成建议”,不负责“执行决策”。智能组卷生成一套试卷后,必须经过教师确认才能发布。AI批改给出的分数和评语是“初评建议”,系统只自动保存客观题分数,主观题分数默认进入“复核队列”,由教师一键通过或调整。这样设计既保留了AI的效率,也守住了考试公平的底线。
第二条:允许AI在无歧义环境中自由发挥,但输出必须符合强约束格式。大模型的自由文本能力在生成解析、学习建议、问答题点评这些场景中非常有效,但要接进业务系统,输出必须转换成稳定的JSON或Markdown结构,否则下游解析会出大问题。Spring AI给我们提供了ChatClient和结构化输出转换能力,在Spring生态里做这件事非常自然,不需要自己拼一堆普通HTTP调用和JsonParser代码。
第三条:模型错误要有显式的“已知风险”。AI批改不是100%正确,所以系统内必须保留人工复核入口、修改留痕、操作日志。我们在UI上给AI初评结果加了一个橙色的“AI建议”标识,教师一次点击即可替换为本人评分。从实际试运行反馈看,教师对AI批改的信任度,很大程度上取决于这个“可纠错”的入口是否足够顺畅。
1.3 最终确定的功能全景
边界定好以后,功能全景就很清晰了。系统拆成七个一级模块:用户与权限、题库管理、智能组卷、在线考试、AI批改、学情分析和AI助教。七个模块里与Spring AI强相关的有三个半。
强相关的是:智能组卷、AI批改、学情分析,再加半个AI助教(助教本质上是检索增强的问答,和学情分析共享一套知识库底座)。剩下的是传统业务模块,走正常CRUD路线。这样设计的好处是AI模块不会无限蔓延,整个系统的技术风险可控。同时也是对整个项目的预期管理——毕竟项目周期有限,先把AI价值最高的模块做扎实。
2. 技术选型:为什么在多个方案里用了Spring AI
技术选型阶段我们内部做过三轮比较。第一轮比较的是起来,感觉方案差异并不在“能不能调用大模型”,而在“调用的复杂度、与业务代码的耦合度、后续替换大模型的成本”。
2.1 先比较的三条路
第一条路线是直接在业务代码里用HTTP客户端调用模型API。用RestTemplate或OkHttp发请求,自己拼Prompt,自己解析返回的JSON。好处是简单直接、没有额外依赖。坏处是提示词管理、流式响应、函数调用、多模型切换都要自己封装,而且封装质量取决于团队水平,容易在接入第二个模型时推倒重来。
第二条路线是引入LangChain4j。这个框架支持多种模型,也提供了很多AI原生组件。但项目组大部分成员都是Spring技术栈,LangChain4j虽然也能在Spring里用,但在依赖管理、注解支持方面还是多少有些“外来感”,部分组件的更新节奏和Spring Boot版本之间对不齐。
第三条路线是Spring AI。它最核心的优势是:它是一个“由Spring生态官方维护”的AI集成层,模型接入、提示词模板、结构化输出、向量存储抽象、流式API这些能力都被封装成与Spring风格一致的形式。数据源、Redis、消息队列怎么在Spring里配,大模型就怎么在Spring里配——这大大的降低了团队的心理负担和上手成本。
2.2 Spring AI里真正用到的几个能力点
先交代一下项目里用的版本:Spring Boot 3.2.x,Spring AI 0.8.x(0.8系列API较新,但设计思路和后续版本没有根本性变化)。
应用里实际用到的能力点如下。
第一是ChatClient,这是Spring AI 0.8后推荐的同步/流式调用入口。以前控制层需要注入一个ChatModel,手动做系统提示词、用户消息、参数设置的拼装组合。现在只需要注入ChatClient,链式调用非常清爽:
ChatClient chatClient = ChatClient.builder(chatModel) .defaultSystem("你是一名考试命题专家,请严格根据要求生成试题。") .build(); String response = chatClient.prompt() .user("请生成3道关于Spring事务传播机制的选择题,难度为中等") .call() .content();第二是PromptTemplate,这个功能解决了项目里最头疼的“提示词模板管理”问题。智能组卷的提示词非常长,里面有很多变量,比如知识点、题型、难度分布、分值。如果直接在Java代码里写字符串拼接,维护起来是一场灾难。PromptTemplate允许我们把提示词以资源文件形式放在classpath下,用类似Mustache的模板语法填充变量,并且支持结构化参数校验。这个设计思路和Spring的View层模板几乎是同一个模式,团队接受度非常高。
第三是结构化输出。考试系统里AI的返回内容最终都要转成业务对象,例如生成一个题目对象,包含题干、选项、答案、解析和知识点标签。直接用字符串返回再解析非常不可靠,而用Spring AI的BeanOutputConverter,可以把目标结构定义成普通Java记录类,框架会替我们做模型输出格式约束与类型转换:
record Question( String stem, List<String> options, String answer, String analysis, List<String> knowledgePoints ) {} BeanOutputConverter<Question> converter = new BeanOutputConverter<>(Question.class);第四是流式API。在线答题后的“逐字显示AI解析”体验,以及AI助教对话框里的打字机效果,都用的是ChatClient的stream方法。这个手感上的提升对产品评价影响很大,后面会专门讲实现细节。
2.3 和直接裸调大模型API相比,Spring AI带来的工程化收益
这里不吹捧框架,纯讲实际收益。
裸调API的问题在小型Demo里完全看不出来,一旦系统里超过五个地方要调用模型,就会出现三种脏代码:提示词拼接逻辑在业务代码里反复出现、模型返回结果的解析散落在各个Service、模型切换时需要修改每个调用点的包名和参数格式。Spring AI用统一的ChatClient把这些都收口了,业务代码只需要描述“我要这次对话完成什么”,至于模型供应商是谁、用什么API格式、返回JSON怎么解析,交给框架层去适配。
从版本升级角度看也有收益。大模型的供应商API调整频率并不低,以前裸调API时每次供应商更新接口签名,我们都要改五六个模块的代码。使用Spring AI后,多数情况下只需要升级依赖版本并调整少量配置,业务层代码基本不动。
还有一点容易被忽略:Spring AI对Observability(可观测性)有原生支持。接入Micrometer的tracing能力之后,一次AI调用的完整链路(从业务方法到模型服务)都能被追踪。联调时候非常有用,可以直接定位是提示词生成慢了,还是模型响应超时了。
3. 系统整体架构与模块详细设计
架构上我采用的分层方式比较常规:前端Vue3 + 后端Spring Boot + MySQL + Redis + Milvus + 大模型服务侧(由Spring AI适配层屏蔽差异)。但在关键业务链路里,增加了一层“AI编排服务”,这是全系统重要的设计决策之一。
3.1 架构分层与一条完整请求的流转路径
所谓“AI编排服务”,可以理解为所有AI能力的入口层。它不直接写业务逻辑,而是负责把业务Service传来的“任务描述”翻译成一连串模型调用、知识库检索、结果清洗、降级兜底。这样做的好处是AI相关的技术细节不会污染到业务Service,方便单元测试和后续替换。
拿学生向AI助教提问来举例。一次完整请求的流转路径是:前端发送问题到Controller,Controller调用AI助教Service,该Service先把问题写入Redis记录会话上下文,再从向量数据库检索与问题相关的内容片段,最后调用AI编排服务,把用户问题、检索片段、历史消息整合成上下文,请求大模型,最终以SSE流式返回给前端。
关键点在于“整合上下文”这个动作。原始的大模型API调用并不会自动帮你做知识库检索,也不会自动管理多轮会话。如果这个工作散落在业务Service里,每次新功能都会重复实现一遍。放在AI编排服务里统一处理,相当于为整个系统提供了一个“AI网关”,后续新增一个“AI生成试卷分析报告”的功能时,只需要新增一个编排方法,公共逻辑直接复用。
3.2 智能组卷模块的设计:从“随机抽”到“策略生成”
一开始我对智能组卷有一个误区,以为可以直接让大模型“从零生成一张完整卷子”。实际做了两次原型测试后发现:让模型从零生成一张高质量、结构稳定、且覆盖指定知识点分值的完卷,成功的概率并不高。模型会在题量较多时漏掉要求,也容易重复出相似题目。最后放弃了这个思路,改成了“人工策略 + 大模型细调”的混合方案,效果更稳定,也更符合真实考试系统的约束。
具体流程是这样的:
- 教师选择本次考试的基本要求:考试范围(章节)、题型结构(选择、填空、简答、论述)、难度分布(易:中:难 = 3:5:2)、总分值100分。
- 系统从题库里根据知识点和难度标签,用配置好的策略抽出一批候选题目,保证知识点覆盖和难度比例都在约束范围内。
- 把候选题目、试卷框架、约束规则统统放到一条Prompt里,让大模型做“优先级排序”和“冲突消解”。例如模型发现两份备选题目都在考同一个概念,会结合考点的均衡度挑更合适的一题。
- 最后生成试卷预览,并输出一份组卷说明,解释为什么选择这几题(例如“第3题和第7题虽然考点相同,但第3题考查概念理解,第7题考查应用能力,均予保留”)。
第4步里的组卷说明非常受教师欢迎。以前的抽题引擎只能回复“已按比例抽题”,教师完全不知道机器为什么这么选。现在大模型给出可阅读的组卷思路,教师在确认时心里有底,也愿意去手动替换个别的题目。
这段功能里有一段核心代码值得展示,是“策略抽题”触发的Prompt构造:
public String buildPaperPrompt(PaperFrame frame, List<Question> candidates) { PromptTemplate promptTemplate = new PromptTemplate( """ 请根据以下试卷框架和候选题目生成建议试卷。 试卷框架信息: 总分:{{totalScore}},题型要求:{{structure}} 难度分布要求:{{difficultyRatio}} 候选题目编号:{{candidateIds}} 候选题目详情:{{candidateDetails}} 请严格按照试卷框架选择题目,并输出最终试卷结构。 """); Map<String, Object> params = Map.of( "totalScore", frame.getTotalScore(), "structure", frame.getStructure(), "difficultyRatio", frame.getDifficultyRatio(), "candidateIds", String.join(",", candidates.stream().map(Question::getId).toList()), "candidateDetails", buildCandidateDetailPrompt(candidates) ); return promptTemplate.render(params); }这段代码想说明的是提示词模板与变量的天然解耦。后面的Prompt模板维护完全可以交给业务分析师去调整,这段工作不应该是开发者的日常负担。所以把提示词内容放到resources/prompts/paper-assembly.st放在文件中,而不是Java代码的字符串模板里。
3.3 AI批改模块:主观题怎么判才能服人
AI批改模块是整个系统里争议最多、也最考察“工程心态”的部分。它的设计思路是“初评建议 + 教师复核”,整个流程分为三步。
第一步是规则初始化。系统从数据库取出答卷和对应题目,根据题型路由到不同批改器。客观题直接走答案比对,不走大模型。主观题和编程题进入AI批改流程。
第二步是AI初评。把待批改的题目文本、标准答案、评分细则和学生的作答内容放入一个大Prompt中,让模型输出一个结构化的评分结果。评分结果包括:得分、扣分点列表、给分依据、建议评语。重点说一下评分细则,它必须有明确的档位描述,例如“能写出核心公式得4分,能正确代入数据再得3分,最终计算结果正确再得3分”。模型对档位式评分细则的遵循程度,会比让它“自由判断给几分”高很多。
第三步是人工复核。所有AI初评结果统一展示在教师复核页面,教师可以一键接受,也可以修改分数或评语。
项目组同样做了一个重要决定——AI批改的得分不与最终成绩直接挂钩,只有当教师点击“确认”后,AI分数才会进入成绩表。这个设计在校园场景测试中极大降低了教师对系统公平性的质疑。
编程题批改是项目里一个比较亮眼的亮点,同时也最难。我们采用的做法是“静态检查 + 测试用例 + 大模型评语”三条线并行:代码先过一遍编译和静态检查,然后跑预置的单元测试用例,最后把测试结果、代码片段、题目要求交给大模型,请它给出针对性的改进建议和学习方向。大模型不直接决定分数,它只负责提供“反馈评语”。最终成绩按照测试用例通过率来折算,教师可调整。
这里面有一个值得注意的经验:不要试图让大模型自己“运行”代码。模型对代码输出的解析经常不稳定。让代码在真实的执行环境里跑,把执行结果作为客观事实交给模型去解读,比让模型凭代码文本判分稳定得多。
3.4 学情分析与AI助教:让数据变成可对话的知识
学情分析这个模块早期计划做成“套用模板生成报告”。教师选择班级和考试后,系统自动汇总出知识点掌握度、分数段分布、成绩趋势等指标并渲染成图表,同时附带大模型生成的文字解读。这个目标看似不难,但有一个细节需要谨慎处理——生硬拼接统计数字会让模型生成一堆“正确的废话”。
解决方式是用结构化的数据分析结果来约束提示词。统计学指标用程序精确计算,模型只负责对“统计结论”做符合语境的转译。举例来说,如果数据显示“某知识点正确率42%,低于全班平均12个百分点”,我们不会让模型从原始数据里现算,而是直接把结论和阈值喂给模型,让它围绕这个结论组织解读语句。这么做的原因是:大模型做数值计算容易出错,而精确的数字和结论应由工程侧完成。
AI助教模块是面向学生的问答式学习助手。它背后的知识库由两部分组成:一部分是课程讲义被切割、向量化后存入Milvus的文档切片,另一部分是历次考试的错题解析和知识点文档。学生询问“为什么AOP代理在Spring中有时不生效”这类问题时,助教会先从向量库检索出与概念相关的讲义片段,再把片段和问题一起送给大模型,回答时附带参考来源。这个功能本质上是RAG应用的校园场景变体,也是项目里与Spring AI结合得最顺畅的模块。
向量化的实现没有想象中神秘。项目里用了一个简单的文档处理管线:先把PDF或Word讲义转成纯文本,按固定长度(比如每500字)切片并保留段落标题作为元数据,然后调用嵌入模型生成向量写入Milvus集合。检索时直接用Spring AI的VectorStore抽象,不需要自己处理向量计算细节:
List<Document> documents = vectorStore.similaritySearch( SearchRequest.builder() .query("Spring AOP代理失效的原因") .topK(4) .build() );4. 实操过程中的关键点:提示词、幂等与流式输出
Spring AI的封装确实省了很多事,但实际做下来发现,模型的稳定性和系统的鲁棒性还是要靠应用层设计和大量的细节打磨。这里挑四个影响最大的关键点展开。
4.1 提示词工程的设计模板:先写结构,再写参数,最后写示例
我在项目里反复迭代出的一个经验是:提示词不是“让模型听懂”,而是“让模型拿到所有决策信息后按固定流程输出”。我通常把每条复杂的提示词分成五个部分:角色定义、任务描述、输入数据、输出约束、输出示例。
举个例子,AI批改的提示词结构如下:
你是《计算机基础》课程阅卷助教,具有严格、公正的评分风格。 任务:根据给定的题目、标准答案和评分细则,对学生的作答进行评分。 输入: 题目:{{question}} 标准答案:{{answer}} 评分细则:{{rubric}} 学生作答:{{studentAnswer}} 输出约束: 1. 仅输出JSON,不要输出任何解释性文字。 2. 得分必须是数字,范围在0到{{maxScore}}之间。 3. 扣分点必须明确引用学生作答中的具体表述。 输出示例: {"score": 8, "deductions": [{"point": "缺少对事务隔离级别的说明", "deductedScore": 2}], "comments": "总体回答完整,但事务隔离级别部分表述不清晰"}。这部分工作没有多高深的技术含量,但非常考验耐心。项目进行到一半时我总结出一个原则:提示词里的“变量”数量不要超过7个,变量太多模型会顾此失彼。如果某条提示词变量数量超过10个,拆分任务或减少输入信息,效果通常会有明显提升。
4.2 结构化输出与Token截断的博弈
结构化输出虽然使用方便,但它会让模型消耗更多token来生成JSON结构。在业务设计里需要考虑一个容易被忽略的问题:模型输出长度受maxTokens限制,如果回答内容过大,结构化输出会被截断,后端在反序列化时直接抛异常。
针对这个问题,项目组设计了三级防线。第一,把maxTokens设置到足够大,但同时设置合理的超时时间,避免因为等待长输出而占满线程池;第二,在解析环节捕获转换异常并做降级处理——如果是批改模块,返回“AI建议生成失败,请教师人工批改”的提示,而不是让请求直接报500;第三,大输出内容尽量改成流式输出并增量渲染,前端看到的是逐步生成的内容,即使内容较长也不会感知到等待很久。
在设计阶段我专门做过一次压测,单次批改请求的平均token消耗大约在2000到3000之间,回答内容约600到900字。把这个数据乘以批改份数和调用次数,才真正理解了为什么“AI应用的瓶颈往往不在模型能力,而在成本”。后面成本控制部分会重点展开。
4.3 幂等、重试与熔断:AI接口也必须按分布式服务的要求来做
大模型API的本质是远程服务,它可能出现网络抖动、限流、超时以及偶发的返回格式异常。我们不能因为“它是AI”就放低对它的稳定性要求。
项目里在AI调用层统一做了三件事。第一件事是增加重试机制,但重试策略不是简单的“失败就重试”。模型API返回限流状态码或超时时,采用指数退避重试,最大次数设为2,避免对供应商产生过大压力。第二件事是熔断降级,如果连续多次调用失败,AI编排服务直接短路,返回一个固定的降级结果,例如智能组卷模块会回退到Step 2的“纯随机抽题”逻辑,保证考试系统的主流程不受AI服务波动影响。第三件事是幂等控制,AI调用不是天然幂等的,批改任务在重试时可能造成重复扣费,因此所有AI调用在发出去之前先检查Redis里的任务状态,处理完成后写入已完成标记,重复请求直接返回旧结果。
这一层代码虽然不多,但很值得。上线后真实遇到过一次模型供应商服务异常,那时候熔断策略把影响范围控制在AI助教这一个模块,在线考试主流程完全未受影响。如果当初没有做这层防护,一个上游AI服务抖动,整个系统可能都瘫了。
4.4 成本与并发控制实测:一口价和按量包之间的取舍
在真实项目里,AI成本往往比预期高得多。组卷这个高频场景尤其明显,教师每次点“预览试卷”都会触发一次模型调用。测试阶段因为调用量不大没有发现,模拟真实使用时,50个教师同时组卷预览,一个上午就能消耗几百万token。
最终我做了两个调整。一是大量使用“缓存 + 异步预生成”。同一套组卷策略可以缓存生成结果,限定缓存有效期为6小时,如果教师使用完全相同的条件再次预览则直接返回缓存。二是把非实时性AI任务放到消息队列里异步处理。学情分析报告允许管理员点击后异步生成,生成完通过站内消息通知教师查看,不占用用户的同步请求时间。
成本控制的一个有效手段是限制“无效输出”。模型输出的JSON越短、字数越少,成本越低。我们在提示词里加上一句话:“只输出JSON,不要附加任何解释。”看似简单,实际却能节约大约20%的token消耗。不要小看这20%,按学期峰值调用量来计算,这是一笔非常可观的开支。
5. 踩坑实录:从联调到上线的细节清单
最后这部分是碎片化的踩坑总结,每一条都是从真实联调中整理出来的,放在一起可以作为一张“避坑速查表”使用。
5.1 流式输出与WebSocket的配合:别让浏览器卡在“缓冲”
第一个坑出现在AI助教的打字机效果。最初实现时用的是普通的HTTP请求配合response body流式读取。实测中发现某些浏览器对SSE的支持会有缓冲延迟,前端收不到逐字效果,只会在全部内容到达后一次性渲染。后来改成WebSocket通道,服务端一次一条消息推送,前端实时渲染,体验问题才解决。
Spring AI的流式API本身很好用,但流式API在网关层有时会被缓冲,排查时需要确认从Spring Boot到前端之间有Nginx时,Nginx的proxy_buffering是否关闭。这是一个非常不起眼但能卡住项目一两天的坑。
5.2 同一套接口兼容多模型:配置项要先抽象
开发期我们频繁切换模型。最初代码里直接硬编码了模型名称和温度参数,每次切模型都要改三四处代码。后来把所有AI模型相关参数抽到配置中心,通过环境变量动态指定。像这样设计抽象后,联调阶段从Chat模型切到更强的推理模型,只改了application.yml里的模型名和endpoint,业务代码没有做任何改动。
Spring AI的一大好处是模型名称是标准化配置项。只要你选定的模型厂商提供了OpenAI兼容协议,或者有Spring AI官方适配,切换成本就非常低。这也是当初选型时的一个重要加分项。
5.3 评测反馈闭环:模型答得好不好,必须用数据说话
项目做到后期,我发现团队对AI效果的评价一直停留在“感觉很好”层面。后来花了一周时间搭建了一个简单的评测集:从真实题库和真实学生作答中抽取了200道题,给每道题标注了“标准答案”和“教师评分”,然后在每次模型版本调整后跑一遍这200个测试用例,比对模型给出的批改分数与教师评分之间的一致率。这个环比数字从此成为AI模块发版的标准门槛。
评测集这件事强烈建议项目一开始就做,不要等模块完成后再补。模型提示词微调的影响往往在单个例子上看不出来,而在批量评测集上能看到明显波动。没有数据基线,团队内部关于“AI效果好坏”的争论会永远停留在直觉层。
条条框框说了不少,最后再回到这个项目本身讲两句。通过这次设计,我最大的体会是:Spring AI确实让AI能力接入Java后端变得更加规范和顺畅,搞定了模型调用方式、结构化输出和流式响应等基础设施层的问题。但它没有解决“在考试系统中AI只能做辅助决策,不能做终审执行”这个业务设计问题,也没有解决成本预判、评测闭环和监控告警这些工程化问题。真正的难点从来不是“怎么调大模型”,而是“怎么让AI稳定地为业务产生确定性价值”。这个定位想清楚,选型框架就不再是风险,反而能加速整个系统的落地,这也是我在下一个AI项目中仍然愿意继续使用Spring AI的原因。