LangChain4j 视觉理解入门指南 - AI 也能看懂图片了!
前言
你有没有想过:AI 能不能像人类一样看图说话?“这张 K 线图走势怎么样?”"这个报错信息是什么意思?"LangChain4j 的多模态能力让 Java 应用也能实现这些功能。本文基于实战项目,详细解析如何让大模型"看懂"图片。
一、什么是视觉理解(Vision)?
传统大模型只能处理文字输入和输出
视觉大模型可以理解图片内容,实现图文对话
比如你给一张上证指数截图,它可以告诉你:
从图片来看,上证指数近期呈现震荡上行态势...成交量明显放大,显示资金活跃度高...MACD 指标金叉向上,趋势偏乐观...
这就是所谓的 “多模态” —— 图像 + 文本的混合输入输出。
二、项目结构概览
2.1 pom.xml —— 依赖与基础版相同
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.study.ai</groupId>
<artifactId>study-ai</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>langchain4j-04-chatapi-image</artifactId>
<dependencies>
<!-- Spring Boot 基础 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- LangChain4j 核心与 OpenAI 适配器 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
说明:视觉理解和纯文本对话的依赖完全一致,因为底层模型本身就支持多模态。
三、核心原理:图片怎么传给大模型?
3.1 为什么需要 Base64 编码?
网络传输数据都是二进制流,但 JSON 协议只认文本格式。所以必须把图片转换成字符串才能打包发送:
原始图片 (PNG) → 字节数组 → Base64 编码 → 字符串
Base64 是什么?
- 一种编码方式,能把任何二进制数据转成
A-Za-z0-9+/组成的 ASCII 字符 - 类似 URL 编码(把空格变成
%20),但专门用于图片等二进制数据 - 解码后能恢复成原始文件(不会丢失数据)
类比:就像快递要把易碎品打包到纸箱里——图片本身是玻璃杯,Base64 就是把玻璃杯放进泡沫箱再贴上标签的过程。
四、关键代码逐行详解
4.1 ChatConfig.java —— 超长等待时间设置
package com.study.ai.config;
import com.study.ai.service.ChatAssistant;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Configuration
public class ChatConfig {
@Value("${langchain4j.open-ai.chat-model.api-key}")
private String apiKey;
@Value("${langchain4j.open-ai.chat-model.model-name}")
private String modelName;
@Value("${langchain4j.open-ai.chat-model.base-url}")
private String baseUrl;
@Bean
public ChatModel chatModel() {
return OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName(modelName)
.baseUrl(baseUrl)
// 🔑 视觉场景的关键配置:关闭重试
.maxRetries(0) // ❗ 关闭自动重试:视觉请求耗时长,重试会成倍增加总等待时间
// 🔑 超时时间大幅延长至 5 分钟
.timeout(Duration.ofMinutes(5)) // 图像处理需要上传 + 推理,默认 60 秒远远不够
.build();
}
@Bean
public ChatAssistant chatAssistant(ChatModel chatModel) {
return AiServices.create(ChatAssistant.class, chatModel);
}
}
为什么要特殊处理超时和重试?
| 参数 | 默认值 | 视觉场景调整 | 原因 |
|---|---|---|---|
maxRetries | 2 | 改成 0 | 图片分析通常要几十秒,第一次失败就立刻重试会让总等待时间翻倍,不如慢工出细活直接单次完成 |
timeout | 60 秒 | 改成 5 分钟 | 上传图片后需要额外时间:①网络传输耗时 ②模型解析像素 ③生成分析结果,总共可能超过 2 分钟 |
⚠️ 重要经验:很多初学者做多模态时报 TimeoutException,就是因为用了默认 60 秒超时!
4.2 ApplicationTests.java —— 测试代码全解析
这是本次学习的核心,我们逐行拆解:
package com.study.ai;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.Resource;
import java.util.Base64;
@SpringBootTest
class ApplicationTests {
@Autowired
private ChatModel chatModel;
@Value("image/上证指数.png") // 📌 重点:从 resources 目录读取图片资源
private Resource image;
字段说明:
@Autowired chatModel:注入已配置的 ChatModel Bean@Value("image/上证指数.png"):Spring 的资源注入,从src/main/resources/image/文件夹加载图片
为什么用 Resource 而不是 File?
| 方式 | 优点 | 缺点 |
|---|---|---|
Resource | 跨平台兼容,IDE 和 JAR 包都能运行 | 需要先 read 成 byte[] |
File | 简单直观 | Windows/Linux 路径不同容易出错 |
✅ 最佳实践:Spring Boot 项目推荐用
Resource读取静态资源。
Step 1:把图片转成 Base64 字符串
@SneakyThrows // ❗ Lombok 注解:自动抛出异常,不用写 try-catch
@Test
void testImg() {
// base64Data
String base64Data = Base64.getEncoder().encodeToString(image.getContentAsByteArray());
// mimeType
String mimeType = "image/png";
每步解释:
① @SneakyThrows 是啥?
- Lombok 提供的语法糖,自动把方法声明的
throws IOException隐藏起来 - 否则你得写:
@Test
void testImg() throws Exception { // 👈 烦不烦?
...
}
② image.getContentAsByteArray()
- 调用 Spring 的
Resource接口,把图片转成字节数组(二进制) - 相当于:
file.jpg → byte[]
③ Base64.getEncoder().encodeToString(...)
- Java 自带的 Base64 工具类
- 把字节数组转成 ASCII 字符串,便于 JSON 传输
④ String mimeType = "image/png"
- MIME 类型:告诉接收方这是什么格式的文件
- 常见值:
image/pngimage/jpeg/image/jpgimage/gifimage/webp
类比:就像寄快递时填写包裹类型——“这是衣服"还是"这是电子产品”,防止运输中损坏。
Step 2:构建混合消息(文字 + 图片)
UserMessage userMessage = UserMessage.from(
TextContent.from("这是上证指数的截图,我需要你从该图片中观察量价信息,判断后续走势"),
ImageContent.from(base64Data, mimeType)
);
核心类解析:
TextContent.from(...)
- 创建纯文本内容对象
- 你的问题:“从图片中观察量价信息…”
ImageContent.from(...)
- 创建一个图片对象,包含:
base64Data:图片的 Base64 编码mimeType:图片格式(如image/png)
UserMessage.from(...)
- 关键点:可以一次性塞入多个内容(文字、图片、甚至未来还有音频/视频)
- 顺序很重要:先文字后图片(更符合阅读逻辑)
内部结构示意:
UserMessage {
contents: [
TextContent(text="这是上证指数的截图..."),
ImageContent(data="iVBORw0KGgoAAAANSUhh...", type="image/png")
]
}
Step 3:发送并获取回答
ChatResponse chat = chatModel.chat(userMessage);
System.out.println("结果:" + chat.aiMessage().text());
}
返回对象解析:
chatModel.chat(...)→ 同步等待 AI 分析完成ChatResponse:包含 AI 的所有回复内容chat.aiMessage().text():提取纯文本部分
预期输出示例:
结果:从这张上证指数截图中,我观察到以下关键技术特征:
1. **价格走势**:近期呈现震荡上行态势,从约 3050 点逐步攀升至当前 3120 点左右...
2. **成交量变化**:今日成交额显著放大,约为 XXXX 亿元,高于前几日均值...
3. **均线系统**:日线级别的 MA5/MA10/MA20 呈多头排列...
4. **MACD 指标**:快线(DIF)上穿慢线(DEA),形成金叉形态...
综合判断:短期走势偏乐观,但需注意上方套牢盘压力,建议关注量能持续性...
五、常见问题解答(FAQ)
Q1:为什么不能直接用 File.path 读图片?
A:在 IDEA 开发环境运行时,资源文件可能在 target/classes 目录下,但在打包后的 JAR 文件中会变成压缩状态,无法直接用 new File() 访问。Resource 接口会自动适配这两种情况。
Q2:如果图片太大怎么办?
A:
- 压缩:使用
javax.imageio缩放图片 - 裁剪:只截取关键区域(如 K 线图主体)
- 阿里云百炼限制:单张图片最大 2MB,超过会被拒绝
Q3:支持 JPG 吗?
A:支持,只需要改两件事:
- 准备
.jpg图片放到image/目录 mimeType改为"image/jpeg"
Q4:可以同时传多张图吗?
A:可以!只要重复添加 ImageContent:
UserMessage userMessage = UserMessage.from(
TextContent.from("对比这两张图表..."),
ImageContent.from(base64Data1, "image/png"),
ImageContent.from(base64Data2, "image/jpeg")
);
Q5:为什么我的程序卡住不动了?
A:最常见的原因是超时时间不够!确保设置:
.timeout(Duration.ofMinutes(5)) // 至少 2 分钟以上
六、进阶思考:如何优化体验?
6.1 异步流式输出
目前我们是阻塞式等待——用户得盯着控制台看半天。可以考虑改成流式打字机效果(类似之前的 StreamingChatLanguageModel)。
6.2 前端集成
将本模块封装为 RESTful API:
@PostMapping("/analyze")
public String analyzeImage(@RequestParam MultipartFile file) {
// 读取图片 -> Base64 -> 调用 chatModel
}
前端用户上传图片,后端实时返回分析结果。
6.3 结构化解析
如果你只需要特定数据(如股票价格),可以让 AI 返回 JSON 格式,方便进一步处理。
七、总结与下一步
通过这篇博客,你已经掌握了:
✅ 视觉理解的基本原理(为什么需要 Base64)
✅ Spring Resource 读取静态资源
✅ 构建 UserMessage 混合图文内容
✅ 超长短重试配置的重要性
接下来你可以尝试:
- OCR 识别:让 AI 提取图片中的文字(表格、代码、公式)
- 目标检测:识别图片中的物体(人脸、商品、动物)
- 图像描述:自动生成一段话总结图片内容
- 多轮对话:基于图片继续追问细节
八、参考资料
- LangChain4j 官方文档
- 学习来源:JavaAI pig4cloud 中文文档(感谢大佬的学习资料,大家一起进步)
- 我的项目源码
本文档基于实际项目实战编写,所有代码均已测试通过。作者:8 年 Java 开发者自学转型 AI Agent,专注 Java/Spring AI + LangChain4j 技术栈。如有问题欢迎讨论交流!
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_15720875/article/details/164184611




