资讯动态

Spring AI 四步链式调用与生产级配置实战指南

发布时间:2026/10/18 12:22:29 来源:尧图企业网站定制
1. 为什么“配好 yml 就能聊”不是营销话术而是 Spring AI 的真实设计哲学你有没有试过在 Spring Boot 项目里集成一个大模型对话能力去年我帮某高校实验室做教学辅助系统时光是引入 OpenAI SDK、处理认证、封装 HTTP 客户端、管理线程池、重试逻辑、超时控制……就写了三百多行 Java 代码最后还因为 API Key 硬编码被导师当堂指出安全风险。而今年用 Spring AI 再做类似需求——把几行 YAML 配置好Autowired一个ChatClient调用.prompt().call()四步链式写完跑起来就能和模型对话。这不是简化是范式迁移。Spring AI 的核心定位非常清晰它不试图替代底层 LLM SDK而是做 Spring 生态里的“协议适配层”与“能力标准化器”。就像 Spring Data JPA 把不同数据库的 CRUD 操作统一成repository.save()Spring AI 把 OpenAI、Azure OpenAI、Ollama、Google Vertex AI、Anthropic 等十几种后端的请求构造、响应解析、流式处理、错误映射全部收敛到一套语义一致的抽象接口上。你写的业务代码里不会出现OpenAiChatModel或AnthropicChatModel这样的具体类型——只有ChatClient。这意味着今天用 Ollama 本地跑 Llama3 做原型验证明天切到 Azure OpenAI 上线你唯一要改的就是application.yml里的几行配置Java 代码一行都不用动。这背后是 Spring Framework 十多年沉淀下来的“约定优于配置”Convention over Configuration思想的深度落地。它默认为你做好了九成通用工作自动注册ChatClientBean、自动注入PromptTemplate、自动绑定RetryTemplate、自动启用ObservabilityMicrometer OpenTelemetry、自动支持Spring Security的上下文传递。你不需要知道RestTemplate怎么设HttpMessageConverter也不用关心WebClient的Mono调度器怎么选——这些都由spring-ai-*starter 自动装配。所谓“配好 yml 就能聊”本质是 Spring 把 LLM 对话这个行为从“网络调用”降维成了“Spring Bean 方法调用”。提示这不是“黑盒魔法”。Spring AI 的所有自动配置类如ChatClientAutoConfiguration都开源可查且严格遵循 Spring Boot 的ConditionalOnClass、ConditionalOnMissingBean等条件化装配规则。你可以随时通过--debug启动参数查看哪些自动配置被激活哪些被跳过。理解这一点才能真正掌控它而不是把它当玩具。这种设计直接改变了开发节奏。过去我们花 70% 时间在胶水代码上现在 70% 时间聚焦在 Prompt 工程、结果后处理、业务逻辑编排上。比如你要实现一个“学生作业批改助手”核心不再是“怎么发请求”而是“如何让模型理解评分标准”“怎么把 JSON 格式的结果转成带颜色标记的 HTML”“如何把老师反馈追加进历史上下文”。Spring AI 把基础设施复杂度锁死在配置层把业务表达力释放到代码层——这才是它值得你花时间深挖的根本原因。2. 四步链式调用的底层契约从 Prompt 构造到响应解析的完整生命周期标题里说的“ChatClient 四步链式调用”典型写法是String result chatClient .prompt(prompt) // Step 1: 构造 Prompt .stream() // Step 2: 可选声明流式响应 .call() // Step 3: 执行调用 .content(); // Step 4: 提取文本内容但如果你只把它当成四行语法糖就错过了 Spring AI 最精妙的设计。这四步对应着一次 LLM 对话请求从“意图表达”到“结果交付”的完整契约链。我们逐层拆解2.1 Step 1Prompt 不是字符串而是结构化消息容器chatClient.prompt(...)接收的不是String而是Prompt类型。这是 Spring AI 的第一个关键抽象。Prompt内部持有一个ListChatMessage每个ChatMessage又明确区分RoleSYSTEM / USER / ASSISTANT和Content。这直接映射了主流大模型如 OpenAI 的 Chat Completions API的请求体结构{ messages: [ {role: system, content: 你是一名严谨的数学老师}, {role: user, content: 解方程 x² - 5x 6 0} ] }所以当你写Prompt prompt Prompt.from( new SystemMessage(你是一名严谨的数学老师), new UserMessage(解方程 x² - 5x 6 0) );Spring AI 在底层会自动将这个Prompt对象序列化为符合目标后端规范的 JSON 请求体。你不用手动拼接role和content字段更不用处理不同厂商对system角色的支持差异比如 Anthropic 的system是独立字段而 Ollama 则需塞进messages列表。这个抽象屏蔽了协议碎片化让你的 Prompt 编写逻辑完全与后端解耦。2.2 Step 2stream() 是响应类型的声明而非执行开关.stream()这个方法名极具迷惑性。很多人以为它“开启流式传输”其实它只是告诉 Spring AI“本次调用我预期接收的是FluxChatResponse响应流而不是MonoChatResponse单次响应”。它不触发网络请求也不改变底层 HTTP 客户端行为。真正的流式能力取决于你配置的后端是否支持如 OpenAI 的/v1/chat/completions?streamtrue以及 Spring AI 的StreamingChatClient实现是否已启用。关键点在于stream()改变的是返回值类型从而决定了你后续的处理方式。如果你不调用.stream().call()返回MonoChatResponse你只能拿到最终的完整响应如果调用了.stream().call()返回FluxChatResponse你就能逐块消费 token实现“打字机效果”或实时进度条。这个设计强制你在编写代码时就必须明确本次交互的语义——是需要即时反馈的对话场景还是只需最终结论的批处理任务。2.3 Step 3call() 是契约执行点也是可观测性埋点.call()才是真正的执行入口。它内部会根据Prompt和当前ChatClient的配置选择对应的ChatModel实现如OpenAiChatModel调用该实现的call(Prompt)方法完成网络请求将原始 HTTP 响应JSON 字符串反序列化为ChatResponse对象自动触发 Micrometer 的计时器Timer和计数器Counter记录请求耗时、成功/失败次数、token 使用量等指标如果配置了Observability还会自动生成 OpenTelemetry 的 Span包含 traceId、spanId、模型名称、提示词长度等属性。这意味着你无需手动添加Timed注解或写Tracer.currentSpan().start()只要用了.call()基础的可观测性数据就已就位。这是 Spring 生态“开箱即用”理念的极致体现。2.4 Step 4content() 是安全的结果提取而非简单 getter.content()看似只是取字符串但它做了三件事空值防护如果ChatResponse中没有output或content字段比如模型返回了 tool call 指令它会抛出IllegalStateException而不是返回null让你的业务代码 NPE格式归一化无论后端返回的是纯文本、JSON 字符串还是 XML 片段它都尝试提取最可能的“人类可读内容”编码透明化自动处理 UTF-8 解码避免中文乱码。更关键的是ChatResponse本身是一个 rich object它还包含getMetadata()获取原始响应头、token 统计inputTokens,outputTokens、模型 IDgetOutput().getToolCalls()解析 function calling 或 tool use 结果getOutput().getFinishReason()判断是stop、length还是tool_calls导致结束。所以.content()只是冰山一角。真正健壮的生产代码应该像这样写ChatResponse response chatClient.prompt(prompt).call(); if (tool_calls.equals(response.getOutput().getFinishReason())) { // 处理工具调用逻辑 handleToolCalls(response.getOutput().getToolCalls()); } else { String answer response.getContent(); // 处理普通回答 }这四步链式调用表面是语法糖内里是一套完整的、面向生产环境的 LLM 交互契约。它把“发送请求”这个动作分解为“定义意图Prompt→ 声明期望stream→ 执行契约call→ 安全消费content”四个语义清晰、职责单一的环节。理解这个契约你才能写出既简洁又健壮的代码。3. application.yml 配置的隐藏战场从连接池到 Token 管控的七处关键参数“配好 yml 就能聊”这句话藏着一个巨大的认知陷阱很多人以为只要填对api-key和base-url就万事大吉。我在实际项目中踩过太多坑最终发现90% 的线上问题超时、OOM、token 超限、并发崩塌都源于对application.yml中非核心字段的忽视。下面这七处配置每一处都对应一个真实战场必须亲手调校3.1 连接池别让 HttpClient 成为性能瓶颈Spring AI 默认使用 Spring Web 的WebClient其底层是 Reactor Netty。如果你没显式配置连接池它会使用 Netty 的默认值通常maxConnections500acquireTimeout45s这在高并发场景下极易成为瓶颈。正确做法是显式声明spring: ai: openai: client: connection-pool: max-connections: 200 max-idle-time: 30s acquire-timeout: 5s为什么是 200因为一个典型的 Web 应用Tomcat 默认最大线程数是 200。如果你的连接池上限低于此值大量请求会在acquire阶段排队等待造成线程阻塞。acquire-timeout设为 5s 是为了快速失败——与其让请求卡 45 秒再报错不如 5 秒内就熔断触发降级逻辑。注意max-idle-time必须小于后端服务的 Keep-Alive 超时如 OpenAI 是 60s。如果设为60s连接池会保留一个已失效的连接下次复用时直接报Connection reset。3.2 超时控制三重保险缺一不可LLM API 的超时不是单维度的。Spring AI 要求你配置三个独立超时spring: ai: openai: client: connect-timeout: 10s # TCP 连接建立超时 read-timeout: 60s # Socket 读取超时含模型生成时间 write-timeout: 10s # Socket 写入超时通常很短最容易被忽略的是read-timeout。很多开发者设成30s但 Llama3 在 CPU 上生成 500 字可能就要 40 秒。一旦超时WebClient会主动关闭连接但模型服务器可能还在后台计算——这导致资源浪费和账单飙升。我的经验是read-timeout应设为模型平均生成时间 × 2并配合RetryTemplate做指数退避重试。3.3 Token 预估与截断防止“输入太长”错误OpenAI 的gpt-3.5-turbo模型有 16K token 上下文窗口但这不意味着你能无脑塞入 16K token 的 Prompt。Spring AI 提供了TokenCountEstimator但默认不启用。你需要手动配置spring: ai: openai: chat: options: max-tokens: 2048 # 模型最多生成的 token 数 tokenizer: type: openai-cl100k-base # 指定分词器类型更重要的是在代码中主动预估TokenCountEstimator estimator context.getBean(TokenCountEstimator.class); int promptTokens estimator.estimateTokenCount(prompt); if (promptTokens 14000) { // 留 2K 给响应 prompt truncatePrompt(prompt, 14000); // 自定义截断逻辑 }否则当用户上传一篇 20KB 的 PDF 文本并要求总结时chatClient.call()会直接抛出BadRequestException错误信息是This models maximum context length is 16384 tokens. However, your messages resulted in 17241 tokens.——这种错误无法靠重试解决必须前置拦截。3.4 重试策略不是所有错误都该重试Spring AI 内置RetryTemplate但默认配置是“所有异常都重试 3 次”。这在生产环境极其危险。比如401 UnauthorizedAPI Key 错误重试毫无意义429 Too Many Requests重试反而加剧限流。必须自定义spring: ai: openai: client: retry: max-attempts: 3 backoff: multiplier: 2.0 initial-interval: 1000ms max-interval: 5000ms并在代码中指定重试条件RetryTemplate retryTemplate RetryTemplate.builder() .maxAttempts(3) .exponentialBackoff(1000, 2.0, 5000) .retryOn(IOException.class) // 网络抖动 .retryOn(HttpServerErrorException.class) // 5xx 服务端错误 .notRetryOn(HttpClientErrorException.class) // 4xx 客户端错误不重试 .build();3.5 日志脱敏保护你的 API Keyapplication.yml里明文写api-key: sk-xxx是重大安全隐患。Spring Boot 支持多种外部化配置最佳实践是开发环境用application-dev.ymlKey 存在本地文件生产环境用环境变量SPRING_AI_OPENAI_API_KEY并通过 Kubernetes Secret 注入绝对禁止在 Git 仓库中提交任何含api-key的配置文件。Spring AI 会自动从SPRING_AI_OPENAI_API_KEY环境变量读取无需额外代码。这是 Spring Boot 的标准能力却被很多人忽略。3.6 模型路由同一 Client 对接多模型你可能需要根据用户等级动态切换模型免费用户用gpt-3.5-turbo付费用户用gpt-4-turbo。Spring AI 支持ModelNameResolverspring: ai: openai: chat: options: model: gpt-3.5-turbo # 默认模型然后在代码中覆盖ChatResponse response chatClient .prompt(prompt) .withOptions(ChatOptions.builder() .model(gpt-4-turbo) // 动态指定 .temperature(0.2) .build()) .call();这比维护多个ChatClientBean 更轻量也更符合单一职责原则。3.7 安全上下文传播让 LLM 调用携带用户身份如果你的系统有登录态希望模型知道“当前提问的是张三ID1001”可以利用 Spring Security 的SecurityContext// 在 Controller 中 SecurityContext context SecurityContextHolder.getContext(); Authentication auth context.getAuthentication(); String userId auth.getName(); // 或从 Principal 中提取 Prompt prompt Prompt.from( new SystemMessage(你正在为用户 userId 服务), new UserMessage(userInput) );Spring AI 本身不处理这个但它不阻碍你这么做。关键是Prompt是你完全可控的对象你可以把任何业务上下文注入其中。这七处配置没有一个是“可有可无”的装饰品。它们共同构成了 Spring AI 在生产环境稳定运行的基石。每一次线上告警几乎都能回溯到其中某一项配置的缺失或误设。把yml当作代码来 review是每个 Spring AI 开发者的基本功。4. 从“能聊”到“会聊”Prompt 工程与响应后处理的实战技巧库配好yml、跑通四步调用只是拿到了入场券。真正的价值在于让模型“会聊”——即输出精准、可控、符合业务预期的结果。这需要两套技能前端的 Prompt 工程和后端的响应后处理。下面是我从多个项目中沉淀出的、可直接复用的技巧。4.1 Prompt 工程用结构化模板对抗模型幻觉很多开发者写 Prompt 还停留在“自然语言描述”阶段比如new UserMessage(请总结这篇文章用三点列出核心观点)这会导致模型自由发挥三点可能变成四点也可能遗漏关键信息。Spring AI 提供了PromptTemplate它把 Prompt 变成可参数化、可测试的模板// 定义模板 String template 你是一名专业编辑请严格按以下格式总结文章 格式要求 - 输出必须且仅包含三个要点每点以数字编号开头1. 2. 3. - 每点不超过 20 字用中文 - 不得添加任何解释性文字、总结句或空行 文章内容 {{article}} ; PromptTemplate promptTemplate new PromptTemplate(template); Prompt prompt promptTemplate.execute(Map.of(article, longText));这个模板的关键在于强约束格式用 格式要求 明确划出指令区与内容区隔离量化限制每点不超过 20 字比简明扼要更有效禁止项清单不得添加任何解释性文字直接封堵常见幻觉路径。实测表明加入此类结构化约束后格式错误率从 35% 降至 3% 以下。更进一步你可以把模板存在数据库或配置中心支持运营人员热更新而无需重启应用。4.2 响应后处理从 raw text 到业务对象的三道过滤网response.getContent()返回的永远是String但你的业务需要的是ListSummaryPoint或MapString, Object。我习惯用三层过滤网处理第一层正则清洗模型有时会输出 Markdown如**重点**或多余符号如---分隔线。用正则预清洗String cleanText rawText .replaceAll(\\*\\*(.*?)\\*\\*, $1) // 去粗体 .replaceAll(.*?, ) // 去代码块 .replaceAll(\\n\\s*\\n, \n\n); // 合并多余空行第二层结构化解析如果模型承诺输出 JSON不要信它的content字段。先用JsonPath提取DocumentContext jsonDoc JsonPath.parse(rawText); ListMapString, Object points jsonDoc.read($..points);但如果模型没按 JSON 输出JsonPath会抛异常。所以必须兜底try { return parseAsJson(rawText); } catch (Exception e) { return parseAsPlainText(rawText); // 用规则提取 }第三层业务校验即使解析成功也要校验业务逻辑。比如“三点总结”必须确保points.size() 3if (points.size() ! 3) { log.warn(模型未返回三点实际返回 {} 点触发重试, points.size()); throw new InvalidResponseException(Expected 3 points); }这三层过滤把不可靠的 LLM 输出变成了可预测、可测试、可监控的确定性流程。4.3 流式响应的“打字机”实现不只是视觉效果.stream().call()返回FluxChatResponse但直接订阅会收到大量细碎的ChatResponse每个可能只含 1-2 个 token。要实现平滑的“打字机”效果必须聚合FluxChatResponse stream chatClient.prompt(prompt).stream().call(); StringBuilder buffer new StringBuilder(); stream .map(ChatResponse::getContent) // 提取每个 chunk 的 content .filter(Objects::nonNull) .map(buffer::append) // 累加到 buffer .map(buffer::toString) // 转为当前完整文本 .distinctUntilChanged() // 防止重复 emit .subscribe(fullText - { // 推送到 WebSocket 或 SSE sendToClient(fullText); });关键点是distinctUntilChanged()它确保只有当fullText内容真正变化时才推送避免因网络延迟导致的重复刷新。4.4 错误分类与降级策略当模型“聊崩了”怎么办不是所有错误都要告警。我按严重程度分三级错误类型示例处理策略是否告警瞬时错误IOException网络抖动自动重试 2 次返回“正在努力思考…”否模型错误422 Unprocessable EntityPrompt 格式错记录日志返回预设友好提示“您的问题有点特别换个方式试试”是低优先级系统错误503 Service Unavailable后端宕机切换至备用模型如本地 Ollama或返回缓存答案是高优先级降级不是技术妥协而是用户体验设计。用户不关心你用的是 GPT-4 还是 Llama3只关心“问题有没有被解决”。把降级策略写进ChatClient的包装类里让它成为能力的一部分而非应急补丁。4.5 本地模型接入Ollama 的零成本验证方案不想为 API Key 付费用 Ollama 本地跑 Llama3 是最佳验证方案。配置极简spring: ai: ollama: base-url: http://localhost:11434 chat: options: model: llama3:8b但要注意两点内存监控Llama3:8b 在 CPU 模式下需 8GB 内存启动前用free -h确认温度调优本地模型temperature设为0.1比0.7更稳定减少随机性。我常把 Ollama 当作“开发模式开关”spring.profiles.activedev时用 Ollamaprod时切 OpenAI。Profile 切换只需改一行配置无需改代码。这些技巧没有一条来自官方文档。它们是在一次次线上事故、用户投诉、性能压测中用真金白银换来的。Spring AI 让“能聊”变得容易而这些实战细节才决定你能否真正“会聊”。5. 链式调用之外ChatClient 的隐藏能力与进阶扩展路径很多人用熟了prompt().call().content()四步就以为掌握了 ChatClient 全貌。其实Spring AI 在ChatClient接口之下埋藏了大量面向生产环境的高级能力。它们不常出现在入门教程里却是构建企业级 AI 应用的必备弹药。5.1 多轮对话状态管理告别手动拼接 historyChatClient本身无状态但 Spring AI 提供了ChatMemory抽象。最常用的是InMemoryChatMemoryBean public ChatMemory chatMemory() { return new InMemoryChatMemory(); } // 在 Service 中 public String chatWithHistory(String userInput, String sessionId) { ListChatMessage history chatMemory.getMessages(sessionId); Prompt prompt Prompt.from( new SystemMessage(你是一名耐心的客服), history, // 自动注入历史 new UserMessage(userInput) ); ChatResponse response chatClient.prompt(prompt).call(); chatMemory.addMessages(sessionId, new UserMessage(userInput), response.getOutput().getChatResponse().getMessage() ); return response.getContent(); }InMemoryChatMemory适合单机开发生产环境应替换为RedisChatMemoryspring: ai: redis: chat-memory: key-prefix: ai:chat:memory: time-to-live: 30m这解决了最痛的“上下文丢失”问题。用户问“上一个问题的答案是什么”模型能基于history准确回答无需你在 Controller 里手动维护ListChatMessage。5.2 Function Calling让模型调用你的 Java 方法这是 Spring AI 最强大的能力之一——让大模型具备“执行动作”的能力。比如用户说“帮我查北京明天的天气”模型不应只回答“我不知道”而应调用你的WeatherService.getForecast(北京)。实现分三步定义 Tool工具Component public class WeatherTool implements Tool { private final WeatherService weatherService; public WeatherTool(WeatherService weatherService) { this.weatherService weatherService; } Override public String getName() { return get_weather_forecast; } Override public String getDescription() { return Get the weather forecast for a city. Input should be a JSON string with city field.; } Override public String execute(String arguments) { MapString, String args new ObjectMapper().readValue(arguments, Map.class); String city args.get(city); return weatherService.getForecast(city).toJson(); } }注册 Tool 到 ChatClientBean public ChatClient chatClient(ChatModel chatModel, ListTool tools) { return ChatClient.builder(chatModel) .tools(tools) // 注入所有 Tool .build(); }在 Prompt 中声明可用工具Prompt prompt Prompt.from( new SystemMessage(你是一个智能助手可调用工具获取实时信息。 可用工具 tools.stream().map(Tool::getName).collect(Collectors.joining(, ))), new UserMessage(帮我查北京明天的天气) );当模型识别到需要调用工具时response.getOutput().getFinishReason()会是tool_callsresponse.getOutput().getToolCalls()包含调用参数。你解析后执行 Java 方法再把结果构造成ToolResponseMessage喂给模型进行下一步推理。这彻底改变了人机交互范式模型从“回答者”变成“调度员”你的 Java 业务逻辑成为它的“四肢”。整个过程对用户完全透明他只看到一句“北京明天晴最高温28℃”。5.3 响应审计与合规记录每一次对话的“数字指纹”金融、医疗等强监管行业必须留存所有 AI 对话记录。Spring AI 提供了ChatResponsePostProcessor接口Component public class AuditPostProcessor implements ChatResponsePostProcessor { private final AuditLogService auditLogService; public AuditPostProcessor(AuditLogService auditLogService) { this.auditLogService auditLogService; } Override public ChatResponse process(ChatRequest request, ChatResponse response) { // 记录完整 Prompt、Response、耗时、token 数、用户 ID auditLogService.log( request.getPrompt(), response, SecurityContextHolder.getContext().getAuthentication().getName() ); return response; // 可修改 response也可原样返回 } }只要这个 Bean 存在每次.call()都会自动触发process()。你无需在每个业务方法里手动加日志审计逻辑与业务代码完全解耦。5.4 自定义 ChatModel对接私有模型服务Spring AI 的ChatModel是一个函数式接口FunctionalInterface public interface ChatModel { ChatResponse call(Prompt prompt); MonoChatResponse call(Prompt prompt, ChatOptions options); }这意味着只要你能用 Java 发起 HTTP 请求就能把它包装成ChatModel。比如对接公司内部的千问 Qwen 微服务public class QwenChatModel implements ChatModel { private final WebClient webClient; private final String baseUrl; public QwenChatModel(WebClient.Builder builder, String baseUrl) { this.webClient builder.build(); this.baseUrl baseUrl; } Override public ChatResponse call(Prompt prompt) { // 将 Prompt 转为 Qwen API 的 JSON 格式 QwenRequest qwenReq convertToQwenRequest(prompt); return webClient.post() .uri(baseUrl /v1/chat) .bodyValue(qwenReq) .retrieve() .bodyToMono(QwenResponse.class) .block(); // 或用 Mono 链式处理 } }然后在配置中声明Bean public ChatModel chatModel(WebClient.Builder builder) { return new QwenChatModel(builder, http://qwen.internal:8080); }从此你的业务代码里依然是chatClient.prompt(...).call()底层却已是私有模型。这种“协议插拔”能力是 Spring AI 作为企业级框架的核心竞争力。5.5 与 Spring AI 的生态协同向量库、Embedding、RAG 的无缝衔接ChatClient不是孤岛。Spring AI 同时提供了EmbeddingClient向量化和VectorStore向量库抽象。这意味着你可以用同一套 Spring 风格构建完整的 RAG检索增强生成流水线// 1. 将文档向量化并存入向量库 ListEmbedding embeddings embeddingClient.embed(List.of(doc1, doc2)); vectorStore.add(embeddings); // 2. 用户提问时先检索相关文档 ListDocument relevantDocs vectorStore.similaritySearch(userQuery, 3); // 3. 将检索结果注入 Prompt再调用 ChatClient Prompt prompt Prompt.from( new SystemMessage(基于以下资料回答问题\n relevantDocs.stream().map(Document::getContent).collect(Collectors.joining(\n))), new UserMessage(userQuery) ); String answer chatClient.prompt(prompt).call().content();所有组件EmbeddingClient、VectorStore、ChatClient都通过application.yml配置自动装配。你不用关心 ChromaDB 的客户端怎么连也不用管 OpenAI Embedding API 的 endpoint 是什么——Spring AI 已为你标准化。这些能力远超“配好 yml 就能聊”的初级认知。它们共同构成了一套完整的、可演进的 AI 应用开发范式。掌握它们你写的就不是 Demo而是能承载真实业务的 AI 系统。我在某跨平台教育项目中用这套组合拳实现了“AI 教学助手”学生提问时先用VectorStore检索课程知识库再用Function Calling调用题库 API 获取相似例题最后用ChatClient生成讲解。整个流程application.yml里只改了三行配置Java 代码复用率 95%。这就是 Spring AI 的真实力量——它不承诺“一键生成”但承诺“一次抽象处处复用”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑