孔蓝把一张手写试卷摊在桌上:"AI 能看懂这个吗?"白歌接过话:"Spring AI 的 Media 类就是为这种场景设计的——把图片封装好,塞进 UserMessage,视觉模型就能理解内容。"
图像输入与视觉模型
定义与作用
Spring AI 通过 Media 类封装非文本数据(图片、音频等),将其附加到 UserMessage 中,实现多模态交互——AI 可以"看到"图片并基于视觉内容回答问题。
核心原理
图释:图片经过 Media 封装后成为 UserMessage 的一部分,与文本 Prompt 一并发送给视觉模型。视觉模型分析图片内容后生成文本回复。
完整示例一:试卷批改
场景说明
孔蓝要给"飞翔大学在线考试系统"加上自动批改功能——学生拍照上传手写试卷,AI 识别答案并评分。
依赖引入与配置
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o # GPT-4o 支持视觉
关键代码
@RestController
public class ExamController {
private final ChatClient chatClient;
public ExamController(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("""
你是一个专业阅卷老师。收到试卷图片后:
1. 识别所有题目和学生的答案
2. 逐题判断对错,给出得分
3. 最后给出总分和评语
""")
.build();
}
@PostMapping("/exam/grade")
public String gradeExam(@RequestParam("file") MultipartFile file)
throws IOException {
// 封装图片为 Media 对象
Media examImage = new Media(
MimeTypeUtils.IMAGE_PNG,
new InputStreamResource(file.getInputStream())
);
return chatClient.prompt()
.user(userSpec -> userSpec
.text("请批改这张试卷")
.media(examImage))
.call()
.content();
}
}
运行结果
POST /exam/grade (上传手写试卷图片)
AI 回复:
「试卷批改结果:
一、选择题(每题 5 分)
1. B ✓ 2. C ✓ 3. A ✗(正确答案 D) 4. D ✓
二、填空题(每题 10 分)
5. "多态" ✓ 6. "1536" ✗(正确答案 "768")
三、简答题(20 分)
7. 学生答案基本正确,描述了 RAG 的核心流程,扣 5 分(缺少 ETL 阶段说明)
总分:70 / 100
评语:基础知识扎实,简答题逻辑清晰但细节需加强。加油!」
完整示例二:证件信息提取
场景说明
飞翔科技人事系统需要自动提取学生证上的姓名、学号等信息。
关键代码
@RestController
public class IdCardController {
private final ChatClient chatClient;
public IdCardController(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("""
你是一个证件信息提取助手。从图片中提取:
- 姓名、学号、院系、专业
以 JSON 格式返回:
{"name":"...","studentId":"...","department":"...","major":"..."}
""")
.build();
}
@PostMapping("/idcard/extract")
public String extractInfo(@RequestParam("file") MultipartFile file)
throws IOException {
Media cardImage = new Media(
MimeTypeUtils.IMAGE_JPEG,
new InputStreamResource(file.getInputStream())
);
return chatClient.prompt()
.user(userSpec -> userSpec
.text("提取这张学生证上的信息")
.media(cardImage))
.call()
.content();
}
}
多模态支持矩阵
| 提供商 | 模型 | 视觉 | 音频 | 适用场景 |
|---|---|---|---|---|
| OpenAI | GPT-4o / GPT-4V | 支持 | 支持 | 试卷批改、证件识别 |
| Ollama | LLaVA | 支持 | — | 本地离线图片分析 |
| Anthropic | Claude 3 | 支持 | — | 高精度文档解析 |
| Gemini | 支持 | 支持 | 多模态全场景 |
易错场景与面试考点
易错场景一:Media 未正确传入
// ❌ 错误:user() 只传文字,图片被忽略
chatClient.prompt()
.user("描述这张图片") // 未使用 userSpec
.call()
.content();
问题分析
普通 .user(String) 只能传文本,图片必须通过 .user(Consumer<UserSpec>) 的 .media() 方法附加。
// ✅ 正确:使用 userSpec + media()
chatClient.prompt()
.user(userSpec -> userSpec
.text("描述这张图片")
.media(imageMedia))
.call()
.content();
面试高频题
Q1:Spring AI 如何实现多模态输入?
通过 Media 类封装非文本数据(图片、音频),使用 MimeTypeUtils 指定类型(如 IMAGE_PNG),将 Media 对象通过
userSpec.media()附加到 UserMessage 中。ChatClient 将整条消息发送给视觉模型处理。
Q2:多模态输入会增加多少 Token 消耗?
视觉模型的 Token 计算与文本不同:图片按分辨率和复杂度计费,GPT-4o 大约是每 512x512 分辨率区域消耗 85-170 Token。一张标准 1024x1024 图片约消耗 765 Token。
本章小结
- Media 类封装图片/音频等非文本数据
- userSpec.text().media() 构建多模态 UserMessage
- 视觉模型分析图片后返回文本,不需要额外的 OCR 管道
- GPT-4o、LLaVA、Claude 3、Gemini 均支持视觉输入