
文章摘要Spring AI同时提供ChatModel和ChatClient真实企业项目通常还会再封装一层AI Service。三者并不是重复接口ChatModel负责底层模型抽象ChatClient负责Prompt、Advisor和响应转换AI Service负责业务语义、权限、路由、成本与错误治理。本文通过架构分层、代码示例和典型场景说明不同层级应该承担什么职责以及如何避免Controller直接耦合模型SDK。一、先看三者各自负责什么推荐分层Controller → 业务AI Service → ChatClient → ChatModel → 模型ProviderChatModel定位底层模型调用抽象。负责接收Prompt调用具体模型返回ChatResponse暴露模型能力处理模型级选项。ChatClient定位面向应用开发的流式API。负责System和User消息Prompt模板AdvisorMemoryRAGTool Calling结构化输出同步和流式调用。AI Service定位面向业务的稳定接口。负责业务用例模型路由用户权限配额Prompt版本错误码审计降级领域对象。二、什么时候直接使用ChatModelChatModel适合框架基础设施开发自己构造完整Prompt需要访问完整ChatResponse做模型适配器实现自定义ChatClient需要控制底层选项编写框架级测试。示例ServicepublicclassRawModelService{privatefinalChatModelchatModel;publicRawModelService(ChatModelchatModel){this.chatModelchatModel;}publicChatResponsecall(Stringmessage){PromptpromptnewPrompt(newUserMessage(message));returnchatModel.call(prompt);}}问题是业务代码需要自己处理System消息、模板变量、Advisor、内容提取、结构化输出和RAG增强。因此普通Controller通常不应直接依赖ChatModel。三、ChatClient适合应用层编排ServicepublicclassSimpleChatService{privatefinalChatClientchatClient;publicSimpleChatService(ChatClient.Builderbuilder){this.chatClientbuilder.defaultSystem(你是企业AI助手。).build();}publicStringchat(Stringmessage){returnchatClient.prompt().user(message).call().content();}}ChatClient带来的价值更少样板代码 Prompt模板 Advisor链 响应转换 同步/流式统一入口它类似于Spring中的RestClient或JdbcClient是对底层能力的应用级封装。四、为什么还要自建AI Service很多项目在Controller中直接写ChatClient调用Demo没有问题企业项目很快会出现每个Controller重复System Prompt业务层写死模型名称无法统一限流无法统计用户成本异常直接向外暴露Prompt升级要改多个类RAG权限散落同一个业务被多个入口调用时逻辑不一致。推荐业务接口publicinterfaceCustomerServiceAiService{CustomerReplyanswer(CustomerQuestionquestion,AiRequestContextcontext);}领域请求publicrecordCustomerQuestion(Stringquestion,StringconversationId){}上下文publicrecordAiRequestContext(StringuserId,StringtenantId,StringrequestId,Stringchannel){}返回对象publicrecordCustomerReply(Stringanswer,ListSourceReferencesources,Stringmodel,longdurationMs){}业务代码不需要知道ChatClient细节。五、推荐的统一调用层publicinterfaceEnterpriseAiClient{TTcall(AiTasktask,ClassTresponseType);FluxStringstream(AiTasktask);}任务对象publicrecordAiTask(StringtaskType,StringpromptVersion,MapString,Objectvariables,AiRequestContextcontext,ModelTiermodelTier){}模型等级publicenumModelTier{FAST,BALANCED,POWERFUL}实现层通过不同ChatClient完成模型路由而不是让Controller判断具体模型名称。六、三层职责如何划分能力ChatModelChatClientAI Service模型调用是间接不直接Prompt对象是是使用业务模板System/User消息手动方便按业务定义Advisor否是选择和传参Memory手动Advisor业务会话规则RAG手动Advisor权限与知识库选择结构化输出手动entity领域对象模型路由手动多客户端业务策略用户配额否可扩展负责错误码模型异常调用异常业务错误审计底层信息Trace业务审计七、多模型项目应该在哪一层路由不推荐Controller判断if(request.vip()){usePowerfulModel();}应该放在业务AI ServicepublicModelTierroute(StringtaskType,UserPlanplan,intcomplexity){if(complexity8){returnModelTier.POWERFUL;}if(planUserPlan.FREE){returnModelTier.FAST;}returnModelTier.BALANCED;}路由考虑任务复杂度用户等级数据敏感性延迟要求成本预算模型可用性失败次数。八、Prompt应该在哪一层管理ChatModel层不应该感知业务Prompt。ChatClient层定义通用默认System和Advisor。AI Service层选择业务场景 Prompt ID Prompt版本 变量 输出类型Prompt不应散落在Controller和Service的字符串中。九、异常应该如何转换底层异常可能包括4014295xx超时连接中断输出解析失败内容安全拦截。AI Service应转换成业务错误publicenumAiErrorCode{MODEL_UNAVAILABLE,RATE_LIMITED,INVALID_OUTPUT,CONTENT_BLOCKED,REQUEST_TIMEOUT,QUOTA_EXCEEDED}Controller只处理稳定错误码不暴露SDK异常。十、什么情况下不需要AI Service小型验证项目可以直接使用ChatClient单一模型单一接口无权限无成本统计不需要长期维护代码只用于演示。只要出现以下任意两项就建议封装多个业务场景 多个模型 多租户 配额 Prompt版本 RAG权限 工具调用 成本统计 审计十一、测试策略ChatModel测试验证Provider配置、模型连接、基础响应和Metadata。ChatClient测试验证Prompt模板、Advisor、结构化输出和流式响应。AI Service测试验证业务路由、权限、配额、降级、错误转换、Prompt版本和领域对象。AI Service单元测试可以Mock ChatClient适配层不需要每次调用真实模型。十二、最终建议框架和基础设施层使用ChatModel普通AI应用调用使用ChatClient企业业务系统ChatClient之上再封装AI Service三者不是互相替代而是分层协作。总结一个可维护的Spring AI项目应该形成业务接口稳定 → AI Service表达业务语义 → ChatClient负责调用编排 → ChatModel负责模型抽象不要让Controller、模型SDK、Prompt和业务规则直接绑在一起。