mistral.rs 多模态流式推理实战:图像与音频联合输入的 Rust 实现
【免费下载链接】mistral.rsFast, flexible LLM inference项目地址: https://gitcode.com/GitHub_Trending/mi/mistral.rs
本指南围绕 mistral.rs 官方文档中的多模态示例(multimodal.md,由 multimodal/main.rs 自动渲染生成)展开,完整讲解如何用MultimodalModelBuilder加载 Phi-4 Multimodal 类模型、通过MultimodalMessages构造同时包含文本、图像、音频的多模态消息,并使用stream_chat_request实现流式解码。读完本文,你将掌握在 Rust 中一步到位地完成「图 + 音 + 文」联合输入推理的完整实战方案,并能基于源码理解其内部工作方式。
示例概览:一条命令跑通图文音联合推理
文档给出的核心示例是一个可直接运行的完整 Rust 程序,运行命令为:
cargo run --release --example multimodal -p mistralrs它的工作流程非常清晰:通过 HTTP 分别拉取一段鸟鸣音频(Bird_singing.ogg)和一张鸟类图片,将其分别封装为AudioInput与DynamicImage,构造一条同时携带图片、音频和文本提示的多模态用户消息,交给 Phi-4-multimodal-instruct 模型以流式方式生成回答,并把每个增量分块实时打印到终端。
完整源码如下(与 mistralrs/examples/models/multimodal/main.rs 完全一致):
//! Multimodal streaming with combined image and audio inputs. //! //! Run with: `cargo run --release --example multimodal -p mistralrs` use std::io::Write; use anyhow::Result; use mistralrs::{ AudioInput, ChatCompletionChunkResponse, ChunkChoice, Delta, MultimodalMessages, MultimodalModelBuilder, Response, TextMessageRole, }; #[tokio::main] async fn main() -> Result<()> { let model = MultimodalModelBuilder::new("microsoft/Phi-4-multimodal-instruct") .with_logging() .build() .await?; let audio_bytes = reqwest::get("https://upload.wikimedia.org/wikipedia/commons/4/42/Bird_singing.ogg") .await? .bytes() .await? .to_vec(); let audio = AudioInput::from_bytes(&audio_bytes)?; let image_bytes = reqwest::get("https://www.allaboutbirds.org/guide/assets/og/528129121-1200px.jpg") .await? .bytes() .await? .to_vec(); let image = image::load_from_memory(&image_bytes)?; let messages = MultimodalMessages::new().add_multimodal_message( TextMessageRole::User, "Describe in detail what is happening.", vec![image], vec![audio], vec![], ); let mut stream = model.stream_chat_request(messages).await?; while let Some(chunk) = stream.next().await { if let Response::Chunk(ChatCompletionChunkResponse { choices, .. }) = chunk { if let Some(ChunkChoice { delta: Delta { content: Some(content), .. }, .. }) = choices.first() { print!("{content}"); std::io::stdout().flush()?; }; } else { // Handle errors } } Ok(()) }程序主要分为四个环节:构建模型→准备媒体输入→构造多模态消息→流式请求与增量输出。下文逐一深入拆解,并补充源码级的实现细节。
构建多模态模型:MultimodalModelBuilder 详解
示例通过MultimodalModelBuilder加载模型,其定义位于 mistralrs/src/multimodal_model.rs,是一个「配置加载、运行与推理行为的各种参数」的构建器。调用new(model_id)时会自动应用以下默认值(见 multimodal_model.rs):
- Token 来源:默认从缓存读取(
TokenSource::CacheToken,即.cache/huggingface/token),私有或需鉴权的模型需先登录; - 最大并发序列数:
max_num_seqs默认为 32; - 设备映射:默认按
AutoDeviceMapParams自动分配设备; - 数据类型:
dtype默认ModelDType::Auto,由模型自动决定; - 强制 CPU:
force_cpu默认false,有可用 CUDA/Metal 设备时自动使用; - Web 搜索:默认关闭与 OpenAI
web_search_options兼容的搜索能力。
除了示例中使用的.with_logging()(启用吞吐量日志,配合--release运行可观察 tok/s 指标),该构建器还提供多个高价值配置方法:
| 方法 | 作用 | 注意事项 |
|---|---|---|
with_max_edge(u32) | 自动缩放并填充图像至指定最大边长,保持宽高比 | 目前仅 Qwen2-VL 与 Idefics 2 支持,其余模型内部自行处理 |
with_max_model_len(usize) | 设置运行时上下文长度 | 内部断言必须大于 0,对应测试见 multimodal_model.rs |
with_hf_config_overrides(HfConfigOverrides) | 递归合并 Hugging Faceconfig.json覆盖项 | 用于微调加载细节 |
with_encoder_cache_memory_bytes(usize) | 设置编码器缓存最大内存字节数 | 断言必须非零 |
with_loader_type(MultimodalLoaderType) | 手动指定模型加载器类型 | 不指定时自动探测 |
with_isq(...)/with_auto_isq(...) | 应用 ISQ 量化 | 见下方多模型示例 |
build()是异步加载入口,内部调用build_multimodal_pipeline组装流水线后再包装成Model(multimodal_model.rs)。对于 UQFF 格式的多模态模型,仓库还提供了专用构建器UqffMultimodalModelBuilder,分片模型只需指定第一个分片文件,其余分片会自动发现。
多模态消息构造:MultimodalMessages 消息 API
MultimodalMessages是承载多模态对话历史的容器,实现在 mistralrs/src/messages.rs 中。示例使用的是最完整的add_multimodal_message,签名接受五个参数:
pub fn add_multimodal_message( self, role: TextMessageRole, // 角色:User / Assistant 等 text: impl ToString, // 文本提示 images: Vec<DynamicImage>, // 图像列表 audios: Vec<AudioInput>, // 音频列表 videos: Vec<VideoInput>, // 视频列表 ) -> Self从源码(messages.rs)可以看到它的内部行为:
- 将新增的 images / audios / videos 分别追加到内部独立存储
self.images、self.audios、self.videos,并记录各自的全局索引区间; - 构建 OpenAI 风格的
content数组:每张图对应一个{"type": "image"}条目,每段音频对应{"type": "audio"},每个视频对应{"type": "video"},最后追加{"type": "text", "text": ...}文本条目; - 将消息以
IndexMap形式推入messages,同时把媒体索引登记到pending_prefixes中,等待发送时由模型注入各自的特殊前缀 token(resolve_pending_prefixes,默认实现为空操作,多模态模型会覆盖它)。
这套设计让「模型特有前缀」与「用户消息」解耦:调用方只需准备原始媒体与文本,模型专属的<|image_1|>、音频/视频占位符等前缀在send_chat_request/stream_chat_request发送时自动应用(见 messages.rs 的 doc 注释)。
此外,MultimodalMessages还提供若干便捷方法,全部内部委托给add_multimodal_message:
add_message(role, text):纯文本消息,适合多轮对话的文本轮;add_image_message(role, text, images):仅携带图像;add_audio_message(role, text, audios):仅携带音频;add_video_message(role, text, videos):仅携带视频。
流式推理与响应解析
示例调用model.stream_chat_request(messages)发起流式请求,返回一个可迭代的Stream(定义于 mistralrs/src/model.rs)。每次stream.next().await会得到一个Response枚举值,常见分支有:
Response::Chunk(ChatCompletionChunkResponse { choices, .. }):携带一个增量分块;- 其他变体用于错误处理(示例中以
else分支占位)。
解析增量文本时,逐层解构choices.first()→ChunkChoice.delta→Delta.content,当content为Some(content)时即为本次新增的文本片段,直接print!输出并flush刷新 stdout,从而在终端上实现类似 ChatGPT 的逐字输出效果。
音频输入底层:AudioInput 的解码原理
示例中的AudioInput::from_bytes(&audio_bytes)接收的是Bird_singing.ogg的原始字节流。AudioInput定义在独立的 mistralrs-audio/src/lib.rs crate 中:
pub struct AudioInput { pub samples: Vec<f32>, // PCM 采样值(归一化到浮点) pub sample_rate: u32, // 采样率(Hz) pub channels: u16, // 声道数 }它提供两种构造方式:
from_bytes(&[u8]):使用symphonia解码任意常见音频容器(Ogg、MP3、WAV 等),自动探测格式、读取采样率与声道数,并逐帧解码出f32PCM 采样(lib.rs)。本示例即走此路径;read_wav(path):直接读取本地 WAV 文件(基于hound),对 PCM16 整数采样按32768.0归一化,与 libsndfile/soundfile 的行为保持一致(lib.rs)。
音频在消息构造阶段以AudioInput形式进入MultimodalMessages,最终由多模态流水线中的音频编码器(如 Phi-4-multimodal 的音频塔)处理为 token 或 embedding 参与生成。
更多能力:支持模型清单、量化与多轮对话
同一个文档目录(docs/src/content/docs/examples/rust/models)还对应了仓库中其他多模态示例,可用于横向扩展:
统一多模态示例与模型清单
multimodal_models/main.rs 提供了一个「改一个常量即可切换模型」的统一示例,通过ModelBuilder+.with_auto_isq(IsqBits::Four)自动 INT4 量化加载,其注释中列出了已在仓库中测试过的多模态模型 ID 清单:
| 模型 | MODEL_ID |
|---|---|
| Llama 3.2 Vision | lamm-mit/Cephalo-Llama-3.2-11B-Vision-Instruct-128k |
| Phi-3.5 Vision | microsoft/Phi-3.5-vision-instruct |
| Phi-4 Multimodal | microsoft/Phi-4-multimodal-instruct |
| LLaVA 1.5 * | llava-hf/llava-1.5-7b-hf |
| LLaVA-NeXT | llava-hf/llava-v1.6-mistral-7b-hf |
| LFM2.5-VL | LiquidAI/LFM2.5-VL-450M |
| Idefics2 | HuggingFaceM4/idefics2-8b-chatty |
| Idefics3 | HuggingFaceM4/Idefics3-8B-Llama3 |
| Qwen2-VL | Qwen/Qwen2-VL-2B-Instruct |
| Qwen2.5-VL | Qwen/Qwen2.5-VL-3B-Instruct |
| Qwen3-VL | Qwen/Qwen3-VL-4B-Instruct |
| SmolVLM | HuggingFaceTB/SmolVLM-Instruct |
| Gemma 3 | google/gemma-3-4b-it |
| Gemma 3n | google/gemma-3n-E4B-it |
| Gemma 4 | google/gemma-4-E4B-it |
| DiffusionGemma | google/diffusiongemma-26B-A4B-it |
| MiniCPM-o 2.6 | openbmb/MiniCPM-o-2_6 |
| Mistral Small 3.1 | mistralai/Mistral-Small-3.1-24B-Instruct-2503 |
| Llama 4 Scout | meta-llama/Llama-4-Scout-17B-16E-Instruct |
注意:LLaVA 1.5 需要显式指定聊天模板,即追加
.with_chat_template("chat_templates/vicuna.json")(模板文件位于 chat_templates/vicuna.json)。
多轮多模态对话
multimodal_multiturn/main.rs 展示了跨轮次携带图像的多模态对话:先用add_message发文本问候,再把模型回答以add_message(TextMessageRole::Assistant, resp)追加回历史,随后用add_image_message连续插入多张图片提问。它同时演示了两个实用点:
- 用
RequestBuilder::from(messages.clone()).set_sampler_max_len(100)控制单轮最大生成长度; - 用
.with_isq(mistralrs::IsqType::Q8_0)做定点量化加载,降低显存占用。
音频专项示例
如果只关心音频输入,audio/main.rs 与 asr/main.rs 提供了仅携带音频消息(add_audio_message)的精简版本,可配合 Phi-4-multimodal 的语音理解与 ASR 能力使用。
小结
本文以官方文档中的 multimodal 示例为骨架,完整拆解了 mistral.rs 多模态流式推理的四个步骤:用MultimodalModelBuilder加载模型、用AudioInput/image::load_from_memory准备媒体、用MultimodalMessages::add_multimodal_message组装「图 + 音 + 文」联合消息、用stream_chat_request流式消费增量分块。配合MultimodalMessages内部「媒体独立存储 + 发送时注入模型前缀」的设计,以及mistralrs-audio中AudioInput基于 symphonia 的通用音频解码能力,开发者只需少量代码即可在 Rust 侧实现与主流多模态大模型(Phi-4-multimodal、Qwen3-VL、Gemma 4、MiniCPM-o 2.6 等)的联合推理、量化部署与多轮对话。
【免费下载链接】mistral.rsFast, flexible LLM inference项目地址: https://gitcode.com/GitHub_Trending/mi/mistral.rs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考