从提示词到多模态:Spring AI 企业级应用开发实践
大语言模型正在从"能聊天的工具"变成企业系统中的一部分。对于 Java 开发者来说,真正有价值的不是单独体验某个 AI 产品,而是把模型能力接入到已有业务系统里,让它参与内容生成、图像生成、语音合成、智能问答、流程自动化等场景。
Spring AI 的定位正好落在这个方向上:它把不同厂商、不同类型的模型能力抽象成统一的 Spring 风格 API,让开发者可以沿用熟悉的工程方式完成 AI 应用开发。与其把 AI 看成一个神秘的黑盒,不如把它当成一种新的基础设施:有输入、有配置、有响应、有异常、有监控,也需要清晰的工程规范。
学习这类技术时,最重要的不是只记住代码怎么写,而是理解为什么要这样组织:为什么要抽象模型接口,为什么要把密钥放到环境变量里,为什么要控制输出格式,为什么要用规则约束 AI 生成代码。只有理解这些底层思路,后续遇到 Spring AI 版本升级、模型厂商切换、业务场景变化时,才能独立完成迁移和扩展。
先学会和 AI 正确协作
很多人刚开始使用 AI 时,会直接输入一句很模糊的话,例如"帮我写个登录页面""帮我优化代码""写个故事"。这种提问方式看起来省事,但输出往往不稳定:有时风格不对,有时结构不对,有时遗漏关键限制,甚至会生成不符合项目架构的代码。
原因很简单:大语言模型虽然拥有大量知识,但它并不知道你的真实业务背景、目标用户、技术约束和输出要求。它只能根据你提供的信息生成结果。提示词的质量,直接决定了 AI 输出的相关性、可用性和专业度。
一个高质量提示词,本质上是一份清晰的任务说明。通常可以从以下几个维度组织:
| 要素 | 作用 | 示例 |
|---|---|---|
| 目标 | 明确希望 AI 完成什么任务 | 将技术说明改写成通俗中文 |
| 背景 | 说明任务发生在什么场景 | 面向一家智能家居创业公司的新品发布 |
| 受众 | 决定表达难度和信息密度 | 给 CEO 汇报、面向高中生、发布到公众号 |
| 风格 | 控制内容类型和表达方式 | 科普风、技术博客风、口语化风格 |
| 语气 | 决定情绪色彩 | 正式严谨、轻松自然、客观中立 |
| 格式 | 约束输出结构 | Markdown、表格、JSON、固定段落结构 |
| 限制 | 明确不能做什么 | 不虚构数据、不使用夸张宣传词、不超过 500 字 |
比如,要让 AI 写一份 Java 后端工程师招聘文案,不要只说"写个招聘启事"。更好的写法是:说明公司背景、目标岗位、目标候选人、技术栈、发布渠道、语气风格、字数范围,以及哪些表达不能出现。这样 AI 才能从"自由发挥"变成"按要求交付"。
用结构化方法提升提示词稳定性
当提示词变复杂之后,只靠临场发挥很容易遗漏信息。CO-STAR 是一种常用的结构化提示词框架,它把任务拆成 Context、Objective、Style、Tone、Audience、Response 六个部分,分别对应背景、目标、风格、语气、受众和响应格式。
它的价值不在于创造新概念,而是把零散的提示词要素变成一套可复用的清单。个人使用时,它能减少遗漏;团队协作时,它能统一大家和 AI 沟通的方式。
对于格式固定、风格特殊或逻辑复杂的任务,还可以使用少样本提示。也就是先给 AI 几组"输入到输出"的示例,再让它处理新的输入。相比单纯描述规则,示例更直观,尤其适合情感分类、数据提取、风格仿写和固定格式生成。
text
示例:
世界越来越糟,没有改善的可能。 -> 悲观
每天都是新的开始,充满希望! -> 乐观
只要努力,梦想终会实现。 -> 乐观
现在判断:
生活太艰难了,看不到出路。 ->
遇到数学计算、逻辑推理、决策分析这类任务时,可以使用思维链提示,让 AI 在给出结果前先拆解问题。比如让它先分析已知条件,再列出计算过程,最后输出结论。这样不仅能提升复杂任务的准确率,也方便开发者定位 AI 到底在哪一步出了问题。
如果问题本身比较容易产生多种推理路径,可以再加入自我一致性策略:让 AI 从不同角度独立推理多次,再比较多个结果,选择最稳定、最合理的结论。这种方式尤其适合复杂推理、数据判断和业务规则分析。
在 AI 编程工具中,提示词能力还可以沉淀为长期规则。以 Cursor Rules 为例,它相当于给 AI 编程助手设置一套行为准则,让它在生成或修改代码时遵循固定的技术规范、架构约定和编码风格。
对于 Spring Boot 项目,规则可以包含这些内容:
- 使用标准分层结构组织代码,例如 controller、service、repository、model、configuration。
- 接口设计遵循 RESTful 风格。
- 类名使用 PascalCase,方法和变量名使用 camelCase,常量使用大写下划线。
- 优先使用构造器注入,避免字段注入。
- 使用
@ControllerAdvice和@ExceptionHandler做统一异常处理。 - 使用 JUnit 5、MockMvc、
@DataJpaTest编写对应层级的测试。 - 使用 Spring Security、BCrypt、CORS 配置保障安全性。
- 使用 SLF4J、Logback、Actuator 完成日志与运行状态观测。
规则越具体,AI 越容易执行。与其写"代码写好一点",不如写"Controller 只负责参数接收和响应返回,业务逻辑放到 Service 层"。与其每次都纠正 AI,不如提前把团队经验写成可复用的规则。
在 Spring AI 中接入图像生成能力
图像模型主要处理视觉相关任务,可以分为两类:一类是图像生成,根据文本或图片条件生成新图像;另一类是图像理解,对输入图片做分类、检测、分割或内容分析。
在应用开发中,图像生成是很常见的入口。例如输入"一个古朴小镇,雨夜,霓虹灯倒映在湿润街道上",模型就能根据描述生成画面。Spring AI 为这类能力提供了统一的 Image Model API,开发者可以用相似的调用方式接入不同图像模型,并在需要时切换服务商。
项目基础配置通常会选择 Java 17、Spring Boot 3.x 和 Spring AI 1.x。父工程中统一管理版本:
xml
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-ai.version>1.0.1</spring-ai.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
子模块引入 Web 和 OpenAI 模型 starter:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
密钥不要硬编码在代码里,建议通过环境变量读取:
yaml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
完成配置后,可以通过 OpenAiImageModel 发起图像生成请求:
java
@RestController
@RequestMapping("/openai")
public class OpenAIController {
private final OpenAiImageModel imageModel;
public OpenAIController(OpenAiImageModel imageModel) {
this.imageModel = imageModel;
}
@GetMapping("/image")
public String image() {
ImageResponse response = imageModel.call(
new ImagePrompt("孩子在海边玩耍",
OpenAiImageOptions.builder()
.quality("hd")
.N(1)
.height(1024)
.width(1024)
.build())
);
return response.getResult().getOutput().getUrl();
}
}
如果希望直接把图片输出到浏览器,可以读取生成结果中的图片 URL,再把图片字节写入 HttpServletResponse。如果模型支持 b64_json,也可以让接口返回 Base64 数据,再在服务端解码为字节数组输出或保存为文件。
Spring AI 的图像抽象主要由几个对象组成:
ImageModel:图像模型调用入口,核心方法是call(ImagePrompt request)。ImagePrompt:封装图像生成请求,包含文本消息和模型选项。ImageMessage:表示图像提示文本,也可以携带权重。ImageOptions:描述图像数量、模型、宽高、响应格式等通用参数。ImageResponse:封装模型返回结果。ImageGeneration:表示单张图像的生成结果和元数据。
开发时还要注意常见异常。参数不合法时,接口可能返回 400,例如图像质量、生成数量、尺寸等参数不被当前模型支持;账户额度不足时,会出现计费限制相关错误;部分模型还要求组织认证,认证未完成时可能返回 403。处理这类问题时,优先查看模型官方参数范围、账户状态和认证状态。
除了 OpenAI,Spring AI 也支持 Azure OpenAI 的 DALL-E 图像生成能力。可以把 OpenAI 理解为模型提供方,把 Azure OpenAI 理解为微软云上的企业级托管入口。企业项目如果已经使用 Azure 云资源,通常会更关注权限管理、合规、区域部署和账单体系。
国内平台方面,百度千帆的新版本接口已经兼容 OpenAI 标准协议,因此可以继续复用 spring-ai-starter-model-openai,通过调整 base-url、模型名和接口路径完成接入。例如图像生成可以配置千帆的 base URL、图片生成路径和具体模型,从而用相似代码调用不同平台。
在 Spring AI 中实现文本转语音
语音模型用于处理人类语音信号,常见能力包括语音识别、语音合成和语音助手。这里重点关注文本转语音,也就是把一段文本转换为自然语音音频。
文本转语音的应用场景很多:把博客内容朗读出来,为多语言内容生成配音,为客服系统生成语音回复,或者在实时交互场景中流式输出音频。Spring AI 对 OpenAI TTS API 做了封装,提供 SpeechModel、StreamingSpeechModel 等抽象,使开发者可以通过简单调用完成音频生成。
基础配置和图像生成类似,可以复用 OpenAI API Key,并继续使用同一个 starter:
yaml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
一个典型的文本转语音接口如下:
java
@RestController
@RequestMapping("/openai")
public class SpeechController {
private final OpenAiAudioSpeechModel speechModel;
public SpeechController(OpenAiAudioSpeechModel speechModel) {
this.speechModel = speechModel;
}
@GetMapping("/tts")
public void tts() throws IOException {
OpenAiAudioSpeechOptions options = OpenAiAudioSpeechOptions.builder()
.model("tts-1")
.voice(OpenAiAudioApi.SpeechRequest.Voice.ALLOY)
.responseFormat(OpenAiAudioApi.SpeechRequest.AudioResponseFormat.MP3)
.speed(1.0f)
.build();
SpeechPrompt prompt = new SpeechPrompt(
"Hello, this is a text-to-speech example.",
options
);
SpeechResponse response = speechModel.call(prompt);
File file = new File(System.getProperty("user.dir") + "/output.mp3");
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(response.getResult().getOutput());
}
}
}
其中 OpenAiAudioSpeechOptions 用来控制语音生成参数:
model:语音生成模型,例如tts-1。voice:发音人,例如 alloy、echo、fable、onyx、nova、shimmer。responseFormat:输出格式,例如 mp3、wav、aac、opus。speed:语速,1.0表示正常速度,大于1.0更快,小于1.0更慢。
如果要生成更柔和的女声,并降低朗读速度,可以这样配置:
java
OpenAiAudioSpeechOptions options = OpenAiAudioSpeechOptions.builder()
.model("tts-1")
.voice(OpenAiAudioApi.SpeechRequest.Voice.NOVA)
.responseFormat(OpenAiAudioApi.SpeechRequest.AudioResponseFormat.OPUS)
.speed(0.5f)
.build();
语音请求和响应也有清晰的对象模型:
SpeechPrompt:把待转换文本和语音选项组合成完整请求。SpeechMessage:封装文本内容。SpeechResponse:封装模型返回结果。Speech:保存生成后的音频二进制数据。
最终拿到的音频本质上是 byte[],可以保存成文件,也可以写入 HTTP 响应直接播放。对于真实业务系统,还需要进一步考虑文件存储、访问权限、异步任务、失败重试、成本控制和日志追踪。
写在最后
Spring AI 应用开发可以从三条线逐步推进:先掌握提示词工程,保证 AI 输出足够稳定;再熟悉 Spring AI 的模型抽象,理解请求、选项、响应和异常处理;最后把图像、语音等多模态能力接入业务流程。
真正的重点不是调用某一个模型接口,而是建立一套可维护的 AI 工程方式。提示词让模型听得懂需求,Rules 让 AI 编程符合团队规范,Spring AI 的统一抽象让模型能力进入 Java 应用体系。把这三者结合起来,AI 才能从演示效果走向可落地的企业级能力。