LangChain4j 视觉理解入门指南 - AI 也能看懂图片了!
前言
你有没有想过:AI 能不能像人类一样看图说话?“这张 K 线图走势怎么样?”"这个报错信息是什么意思?"LangChain4j 的多模态能力让 Java 应用也能实现这些功能。本文基于实战项目,详细解析如何让大模型"看懂"图片。
一、什么是视觉理解(Vision)?
传统大模型只能处理文字输入和输出
视觉大模型可以理解图片内容,实现图文对话
比如你给一张上证指数截图,它可以告诉你:
从图片来看,上证指数近期呈现震荡上行态势...成交量明显放大,显示资金活跃度高...MACD 指标金叉向上,趋势偏乐观...这就是所谓的“多模态”—— 图像 + 文本的混合输入输出。
二、项目结构概览
2.1 pom.xml —— 依赖与基础版相同
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="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 —— 超长等待时间设置
packagecom.study.ai.config;importcom.study.ai.service.ChatAssistant;importdev.langchain4j.model.chat.ChatModel;importdev.langchain4j.model.openai.OpenAiChatModel;importdev.langchain4j.service.AiServices;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.time.Duration;@ConfigurationpublicclassChatConfig{@Value("${langchain4j.open-ai.chat-model.api-key}")privateStringapiKey;@Value("${langchain4j.open-ai.chat-model.model-name}")privateStringmodelName;@Value("${langchain4j.open-ai.chat-model.base-url}")privateStringbaseUrl;@BeanpublicChatModelchatModel(){returnOpenAiChatModel.builder().apiKey(apiKey).modelName(modelName).baseUrl(baseUrl)// 🔑 视觉场景的关键配置:关闭重试.maxRetries(0)// ❗ 关闭自动重试:视觉请求耗时长,重试会成倍增加总等待时间// 🔑 超时时间大幅延长至 5 分钟.timeout(Duration.ofMinutes(5))// 图像处理需要上传 + 推理,默认 60 秒远远不够.build();}@BeanpublicChatAssistantchatAssistant(ChatModelchatModel){returnAiServices.create(ChatAssistant.class,chatModel);}}为什么要特殊处理超时和重试?
| 参数 | 默认值 | 视觉场景调整 | 原因 |
|---|---|---|---|
maxRetries | 2 | 改成 0 | 图片分析通常要几十秒,第一次失败就立刻重试会让总等待时间翻倍,不如慢工出细活直接单次完成 |
timeout | 60 秒 | 改成 5 分钟 | 上传图片后需要额外时间:①网络传输耗时 ②模型解析像素 ③生成分析结果,总共可能超过 2 分钟 |
⚠️重要经验:很多初学者做多模态时报 TimeoutException,就是因为用了默认 60 秒超时!
4.2 ApplicationTests.java —— 测试代码全解析
这是本次学习的核心,我们逐行拆解:
packagecom.study.ai;importdev.langchain4j.data.message.ImageContent;importdev.langchain4j.data.message.TextContent;importdev.langchain4j.data.message.UserMessage;importdev.langchain4j.model.chat.ChatModel;importdev.langchain4j.model.chat.response.ChatResponse;importlombok.SneakyThrows;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.core.io.Resource;importjava.util.Base64;@SpringBootTestclassApplicationTests{@AutowiredprivateChatModelchatModel;@Value("image/上证指数.png")// 📌 重点:从 resources 目录读取图片资源privateResourceimage;字段说明:
@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@TestvoidtestImg(){// base64DataStringbase64Data=Base64.getEncoder().encodeToString(image.getContentAsByteArray());// mimeTypeStringmimeType="image/png";每步解释:
①@SneakyThrows是啥?
- Lombok 提供的语法糖,自动把方法声明的
throws IOException隐藏起来 - 否则你得写:
@TestvoidtestImg()throwsException{// 👈 烦不烦?...}②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:构建混合消息(文字 + 图片)
UserMessageuserMessage=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:发送并获取回答
ChatResponsechat=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:
UserMessageuserMessage=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")publicStringanalyzeImage(@RequestParamMultipartFilefile){// 读取图片 -> Base64 -> 调用 chatModel}前端用户上传图片,后端实时返回分析结果。
6.3 结构化解析
如果你只需要特定数据(如股票价格),可以让 AI 返回 JSON 格式,方便进一步处理。
七、总结与下一步
通过这篇博客,你已经掌握了:
✅ 视觉理解的基本原理(为什么需要 Base64)
✅ Spring Resource 读取静态资源
✅ 构建UserMessage混合图文内容
✅ 超长短重试配置的重要性
接下来你可以尝试:
- OCR 识别:让 AI 提取图片中的文字(表格、代码、公式)
- 目标检测:识别图片中的物体(人脸、商品、动物)
- 图像描述:自动生成一段话总结图片内容
- 多轮对话:基于图片继续追问细节
八、参考资料
- LangChain4j 官方文档
- 学习来源:JavaAI pig4cloud 中文文档(感谢大佬的学习资料,大家一起进步)
- 我的项目源码
本文档基于实际项目实战编写,所有代码均已测试通过。作者:8 年 Java 开发者自学转型 AI Agent,专注 Java/Spring AI + LangChain4j 技术栈。如有问题欢迎讨论交流!