爱吃香蕉的螃蟹头像
关注
3. LangChain Chat API 视觉理解封面图

3. LangChain Chat API 视觉理解

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);
    }
}
为什么要特殊处理超时和重试?
参数默认值视觉场景调整原因
maxRetries2改成 0图片分析通常要几十秒,第一次失败就立刻重试会让总等待时间翻倍,不如慢工出细活直接单次完成
timeout60 秒改成 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/png
    • image/jpeg / image/jpg
    • image/gif
    • image/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:支持,只需要改两件事:

  1. 准备 .jpg 图片放到 image/ 目录
  2. 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 混合图文内容
✅ 超长短重试配置的重要性

接下来你可以尝试:

  1. OCR 识别:让 AI 提取图片中的文字(表格、代码、公式)
  2. 目标检测:识别图片中的物体(人脸、商品、动物)
  3. 图像描述:自动生成一段话总结图片内容
  4. 多轮对话:基于图片继续追问细节

八、参考资料


本文档基于实际项目实战编写,所有代码均已测试通过。作者:8 年 Java 开发者自学转型 AI Agent,专注 Java/Spring AI + LangChain4j 技术栈。如有问题欢迎讨论交流!

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_15720875/article/details/164184611

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--