【AgentScope Java新手村系列】(19)多模态-图像音频视频

第十九章 多模态:图像、音频、视频------ImageBlock 统一替换为 DataBlock

1.x 的 ImageBlock / AudioBlock / VideoBlock 在 2.0.0中被统一替换为 DataBlock ,通过 mediaType 字段区分类型。

旧代码里 new ImageBlock(...) / new AudioBlock(...) 在 2.0 中仍然兼容 (标 @Deprecated),但新代码请统一用 DataBlock

本章先给旧 API 对照,再讲 DataBlock 的统一用法。
依赖 :多模态模型需通过 OpenAI 兼容端点调用,依赖 agentscope-extensions-model-openai

DashScope 专有协议暂不支持多模态------示例使用 OpenAIChatModel + DashScope 兼容地址

https://dashscope.aliyuncs.com/compatible-mode/v1

19.1 1.x 与 2.0 对照

1.x 2.0
ImageBlock DataBlock.builder().mediaType("image/png")
AudioBlock DataBlock.builder().mediaType("audio/mp3")
VideoBlock DataBlock.builder().mediaType("video/mp4")
Base64Source Base64Source(保留,但走 DataBlock.source(...)
URLSource URLSource(保留,走 DataBlock.source(...)

19.2 第一个多模态消息

这个例子在演示什么?

DataBlock 把一张本地图片(PNG 文件)编码为 Base64,和文本拼成一条多模态消息发给 Agent。Agent 收到的不再是纯文本,而是"文字 + 图片"------它能看到图片内容并给出描述。

java 复制代码
import io.agentscope.core.agent.RuntimeContext;
import io.agentscope.core.message.Base64Source;
import io.agentscope.core.message.DataBlock;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.message.UserMessage;
import io.agentscope.extensions.model.openai.OpenAIChatModel;
import io.agentscope.harness.HarnessAgent;

import java.nio.file.Files;
import java.util.Base64;
import java.util.List;

public class Chapter19_Multimodal {

    public static void main(String[] args) throws Exception {
        // 1. 读图片并编码 base64
        byte[] bytes = Files.readAllBytes(Path.of("./cat.png"));
        String b64 = Base64.getEncoder().encodeToString(bytes);

        // 2. 构造 DataBlock
        DataBlock imageBlock = DataBlock.builder()
                .source(Base64Source.builder()
                        .data(b64)
                        .mediaType("image/png")
                        .build())
                .build();

        // 3. 多模态 user 消息
        UserMessage msg = new UserMessage(
                "user",
                TextBlock.builder().text("这张图片里有什么?").build(),
                imageBlock);

        // 4. agent 调用
        HarnessAgent agent = HarnessAgent.builder()
                .name("vision_bot")
                .sysPrompt("你是一个视觉助理,详细描述图片。")
                .model(OpenAIChatModel.builder()
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .modelName("qwen3-omni-flash")
                        .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                        .stream(true)
                        .formatter(new OpenAIChatFormatter())
                        .build())
                .workspace(Path.of("./workspace"))
                .build();

        System.out.println(agent.call(List.of(msg), RuntimeContext.empty()).block().getTextContent());
    }
}

19.3 DataBlock 字段

DataBlockio.agentscope.core.message)核心字段:

字段 类型 说明
source Source 数据来源,必填
mediaType String MIME 类型(image/png / audio/mp3 / video/mp4 / application/pdf
name String 可选文件名
metadata Map 业务方附加元信息

Source 有两种:

  • Base64Source ------ 内联 base64 编码数据
  • URLSource ------ 远程 URL,agent 自动下载

19.4 音频 / 视频

视频和音频的 DataBlock 构造和图片一模一样,换个 mediaType 就行。但同样的代码,图片能跑、音频和视频跑不了。

限制不在 DashScope 本身,在 AgentScope 的内置 ChatModel。OpenAIChatModel 也好、DashScopeChatModel 也好,底层全部走 REST HTTP 协议。HTTP 请求体的多模态字段只支持 image_url 这一种格式,input_audio 和视频帧都塞不进去。

DashScope 自己是能处理音频的,只是需要走不同的协议通道:

  • WebSocket 实时流。Qwen-Omni 实时对话、语音识别都是 WebSocket 长连接,双向推流。AgentScope 没有内置 WebSocket 客户端。
  • DashScope 原生 HTTP 多模态格式。ONE-PEACE、Qwen-VL 这些模型支持 HTTP 传入音频 URL,但走的是 DashScope 自有协议,不是 OpenAI 兼容格式。

两条路都能通,但都不在 AgentScope 的 ChatModel 覆盖范围内。想用的话,学第 20 章 TTS 的做法:自己用 @Tool 包装 DashScope SDK,AgentScope 只负责调工具、不在框架层处理音频字节。

本章示例聚焦图片。图片是 REST 端点唯一稳定支持的多模态输入,也是绝大多数业务最常用的多模态场景。

19.5 与 Tool 的协作

业务方工具可以返回 DataBlock ------ 适合 OCR / 截图 / 渲染结果:

less 复制代码
@Tool(name = "screenshot_page", description = "截图某个 URL")
public DataBlock screenshot(
        @ToolParam(name = "url") String url) throws Exception {

    // 调 puppeteer / playwright 拿 png
    byte[] png = playwright.screenshot(url);
    String b64 = Base64.getEncoder().encodeToString(png);

    return DataBlock.builder()
            .source(Base64Source.builder()
                    .data(b64)
                    .mediaType("image/png")
                    .build())
            .name("screenshot.png")    // name 挂在 DataBlock 上,不是 Base64Source
            .build();
}

LLM 看到工具返回 DataBlock 会自动把它纳入上下文------框架内部会把 DataBlock 包装成 ToolResultBlock 再发给 LLM,无需手动构造。

19.6 1.x 旧代码的兼容路径

ImageBlock / AudioBlock / VideoBlock 在 2.0 仍存在(标 @Deprecated):

java 复制代码
// 1.x 老代码仍能跑
import io.agentscope.core.message.ImageBlock;

ImageBlock oldStyle = new ImageBlock(b64, "image/png");   // 2.0 编译会出 warning

ImageBlock 内部就是 DataBlock.builder().mediaType("image/...")。建议新代码统一用 DataBlock;老代码改起来很便宜(1 行换 1 行)。

19.7 完整可运行示例

这个例子在演示什么?

一条消息里同时拼了两种来源的图片:本地文件走 Base64Source(内联编码)、远程 URL 走 URLSource(引用地址)。Agent 一次性拿到两张图,可以对比分析。这就是多模态的本质------不限制 Block 数量,需要几个拼几个。

less 复制代码
public class Chapter19_FullMultimodal {

    public static void main(String[] args) throws Exception {
        // 方式一:本地文件 Base64 内联
        DataBlock fromFile = DataBlock.builder()
                .source(Base64Source.builder()
                        .data(Base64.getEncoder().encodeToString(
                                Files.readAllBytes(Path.of("./cat.png"))))
                        .mediaType("image/png")
                        .build())
                .build();

        // 方式二:远程 URL 引用
        DataBlock fromUrl = DataBlock.builder()
                .source(URLSource.builder()
                        .url("https://example.com/dog.jpg")
                        .mimeType("image/jpeg")
                        .build())
                .build();

        UserMessage msg = new UserMessage(
                "user",
                TextBlock.builder().text("对比这两张图片有什么不同。").build(),
                fromFile,
                fromUrl);

        HarnessAgent agent = HarnessAgent.builder()
                .name("multimodal_bot")
                .sysPrompt("你是多模态助理,可以理解图片内容。")
                .model(OpenAIChatModel.builder()
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .modelName("qwen3-omni-flash")
                        .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                        .stream(true)
                        .formatter(new OpenAIChatFormatter())
                        .build())
                .workspace(Path.of("./workspace"))
                .build();

        System.out.println(agent.call(List.of(msg), RuntimeContext.empty()).block().getTextContent());
    }
}

音频/视频的 DataBlock 构造方式和图片完全一样,只需改 mediaType。是否能被模型理解取决于模型端点的协议支持------大部分 REST HTTP 端点仅支持图片多模态,音频/视频输入需要 WebSocket 实时协议。

19.8 最小迁移清单(1.x ImageBlock → 2.0 DataBlock)

1.x 用法 2.0 等价
new ImageBlock(b64, "image/png") DataBlock.builder().source(Base64Source.builder().data(b64).mediaType("image/png").build()).build()
new AudioBlock(b64, "audio/mp3") DataBlock.builder().mediaType("audio/mp3")
new VideoBlock(url, "video/mp4") DataBlock.builder().source(URLSource.builder().url(url).mimeType("video/mp4").build())
msg.getContentBlocks(ImageBlock.class) msg.getContentBlocks(DataBlock.class) 后按 mediaType 区分

19.9 本章小结

  • 2.0 用 DataBlock 统一图片 / 音频 / 视频 / 文件。
  • ImageBlock / AudioBlock / VideoBlock 仍兼容但被弃用。
  • 工具可返回 DataBlock ------ LLM 自动纳入上下文。
  • mediaType 字段决定 LLM 端路由(视觉 / 音频 / 视频模型)。
相关推荐
Javatutouhouduan2 小时前
国内大厂Java面试高频题库(2026突击版)
java·架构师·java面试·java面试题·后端开发·java程序员·java八股文
Tim_103 小时前
【C++】017、new/delete与malloc/free的区别
java·数据结构·算法
青山木3 小时前
一把 Redis 分布式锁,踩透四个坑:锁争抢、僵尸锁、锁过期、锁丢失
java·数据库·redis·后端
Java内核笔记3 小时前
万字避坑!Spring Boot 3.x 升 4.0 最全升级指南(附 Migration Checklist,建议收藏⭐)
java·后端
cfm_29144 小时前
SpringBoot启动原理
java·spring boot·spring
漫随流水5 小时前
Java——springboot web案例
java·开发语言·spring boot
从此以后自律5 小时前
Java Object 类常用方法全讲解
java·开发语言
念何架构之路5 小时前
Docker 容器状态机 学习
java·学习·docker
你有我备注吗5 小时前
SQL学习之查询
java·sql·学习