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

4 阅读4分钟

第十九章 多模态:图像、音频、视频——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.x2.0
ImageBlockDataBlock.builder().mediaType("image/png")
AudioBlockDataBlock.builder().mediaType("audio/mp3")
VideoBlockDataBlock.builder().mediaType("video/mp4")
Base64SourceBase64Source(保留,但走 DataBlock.source(...)
URLSourceURLSource(保留,走 DataBlock.source(...)

19.2 第一个多模态消息

这个例子在演示什么?

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

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)核心字段:

字段类型说明
sourceSource数据来源,必填
mediaTypeStringMIME 类型(image/png / audio/mp3 / video/mp4 / application/pdf
nameString可选文件名
metadataMap业务方附加元信息

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 / 截图 / 渲染结果:

@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):

// 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 数量,需要几个拼几个。

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 端路由(视觉 / 音频 / 视频模型)。