19.【LangChain学院】Foundation (1.4)-multimodal_messages(1)_多模态输入 | 标准化封装 | 厂家原生 | OpenAI兼容|URLBase64 这是这是我们《AI开发实践》系列的第19篇我们继续学习Langchain。前面两节中我们学习了langchain的Short Memory 这一节我们学习多模态交互。Langchain为了屏蔽多厂家的差异对输入和返回的消息都做了标准化的处理。比如对于输入消息HumanMessage以image为例在OpenAI的厂家定义的原生type为“image_url”在 langchain中使用content_blocks统一封装为了”image”。 在给模型发送请求时langchain会将standard content blocks转换为Provider-native format. 同样对于输出消息AIMessageopenAI 返回的原始内容返回体也做了标准化。这样的好处是一定程度上屏蔽了多厂家的差异对于切换模型的时候会相对的容易一点。但是这种标准化就需要针对于不同的厂家来做翻译和封装。由于各个厂家不同的支持力度以及langchain的封装的进度往往导致能力参差不齐很难使用。比如针对于deepseek、Qwen、openAI的支持都是残缺不全的Typeimageaudiovideolangchain-deepseek❌❌❌langchain-qwq✅❌✅langchain-openai✅✅❌同时国内基本上所有的模型都是支持OpenAI兼容的消息模式的所以我们完全可以用 OpenAI的原生的消息模式进行多模态的处理这样一定程度上解决多厂家消息格式不一致问题。由于阿里云上的模型非常丰富而且模型的资料写得相当的完善因此呢,我们在本次多模态的使用当中使用阿里的Qwen模型。一、申请Qwen模型点击https://bailian.console.aliyun.com/cn-beijing#/home登录创建完成后保存。里面有base ui以及apik在后面会用到。点击“用量和费用”中找到”免费额度“ 找到 qwen3-omini-flash。开启免费流量。https://bailian.console.aliyun.com/cn-beijing?tabcosting-balance#/costing-balance/free-quota?modelTypeMultimodal选择支持多种模态组合输入的qwen3.5-omni-plus.二、多模态对话1、准备资源本次的资源采用阿里云帮助中提供的资源https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250211/tixcef/cherry.wavhttps://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241115/cqqkru/1.mp4https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg分别在浏览器窗口中输入以上地址下载文件到本地。(注我也用Gitee来试了一下免费的资源平台可能存在对模型访问的限流或者约束容易导致模型下载不到需要的媒体资料。如果要用也要使用正规的商用云存储平台。)2、初始化模型在这里我们同样使用langchain的init_chat_model进行模型初始化。在初始化过程当中模型会根据指定的model provider来自动的调用封装的模型初始化接口。因为我们要使用open ai的兼容模式所以这里的model provider就写成了openai。但是呢我们实际使用的就是Q问提供的模型因此我们需要将base UI设置为实际的地址。这个地址信息和API_KEY在同一个文件中。# 初始化模型llm_modelinit_chat_model(modelqwen3.5-omni-plus,model_provideropenai,#使用openai的兼容模式。base_urlhttps://ws-6ng7bva4fxlaep40.cn-beijing.maas.aliyuncs.com/compatible-mode/v1,api_keydash_key,temperature0.1,top_p0.3,max_tokens200,timeoutNone,max_retries2,callbacks[LLMDebugCallback()]##stop [\n\n])agentcreate_agent(modelllm_model,system_promptYou are a 12 years old child.,)https://help.aliyun.com/zh/model-studio/qwen-omni?spma2c4g.11186623.help-menu-2400256.d_0_3_9_1.28622b07SAqHP6scm20140722.H_2867839._.OR_help-T_cn~zh-V_1#single-modal-input3、图片对话方式1-1URL- Provider-native### Image input - url - contentquestion_With_URLImageHumanMessage(content[{type:text,text:Tell me about this man in the picture.},{type:image_url,image_url:{url:https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg}}])responseagent.invoke({messages:[question_With_URLImage]})print(response[messages][-1].content)这里的provide provide native也就是open ai的兼容模式。因为国内的各个厂家都支持open ai的兼容模式所以也可以认为其是其中的一种标准。方式1-2 URL - LangChain’s standard content blocksquestion_With_URLImagehuman_messageHumanMessage(content_blocks[{type:text,text:describe the picture},{type:image,url:https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg},])responseagent.invoke({messages:[question_With_URLImage]})print(response[messages][-1].content)这里的content blocks同样的会有langchain转换成 provide native的content模式。从下方的日志当中可以看出来最终传递给模型的是type: image_url,而不是代码中写到的type: image。2026-07-0109:12:47,556-openai._base_client-DEBUG Request options:{method:post,url:/chat/completions,headers:{X-Stainless-Raw-Response:true},files:None,idempotency_key:stainless-python-retry-07661cc1-4f9a-4527-b2eb-5961cd203b56,content:None,json_data:{messages:[{content:You are a football commentator , know everything about world cup.,role:system},{content:[{type:text,text:describe the picture},{type:image_url,image_url:{url:https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg}}],role:user}],model:qwen3-omni-flash,max_completion_tokens:200,stream:False,temperature:0.1,top_p:0.3}}如前所述这种转换方式目前参差不齐还比较难以使用因此我们后续也不再使用这种方式。方式2Base64Base64实际上是继承了前面url的方式区分于前面采用http或者https远程地址的URL表达方式(https:xxx.jpeg)。这里采用了内联url的模式,也是url表达方式的一种。由rfc2397标准定义,专门用于把小型的二进制资源直接嵌入到URI字符串的特殊协议。具体定义的格式为data:[mediatype][;base64],data当我们把本地文件编码成 base64码型之后就可以采用上述同样的方式通过url把数据传递给模型。importbase64 audio_pathrresources\dog_and_girl.jpegwithopen(audio_path,rb)asf:img_binf.read()img_b64_rawbase64.b64encode(img_bin).decode(utf-8)question_with_Base64ImageHumanMessage(content[{type:text,text:describe the picture.},{type:image_url,image_url:{url:fdata:image/png;base64,{img_b64_raw}}}###内联URL方式传递base64编码])responseagent.invoke({messages:[question_with_Base64Image]})print(response[messages][-1].content)从上面可以看出从本地采用base64编码上传文件与使用普通的url的区别并不大。因此后续我们也主要采用普通url(http)的方式。4、音频对话fromlangchain.messagesimportHumanMessage question_With_AudioURLHumanMessage(content[{type:text,text:Tell me what is in the voice.},{type:input_audio,input_audio:{data:https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250211/tixcef/cherry.wav,format:wav}}])responseagent.invoke({messages:[question_With_AudioURL]})print(response)如果在本地采用base64编码上传的音频文件只需要改为{type:input_audio,input_audio:{data:fdata:audio/wav;base64,{audio_b64_raw}}区别只在于的类型由image/png改为了audio/wav。5、视频对话fromlangchain.messagesimportHumanMessage question_With_URLVideoHumanMessage(content[{type:text,text:Tell me what is in the video.},{type:video_url,video_url:{url:https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241115/cqqkru/1.mp4}}])responseagent.invoke({messages:[question_With_URLVideo]})print(response)如果在本地采用base64编码上传的音频文件只需要改为{type:video_url,video_url:{url:fdata:video/mp4;base64,{video_b64_raw}}}区别只在于的类型改为video/mp4。6、多模态组合输入fromlangchain.messagesimportHumanMessage question_With_multiMediaHumanMessage(content[{type:image_url,image_url:{url:https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg},},{type:input_audio,input_audio:{data:https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250211/tixcef/cherry.wav,format:wav}},{type:text,text:describe the picture and tell me what it is said in the voice.},])responseagent.invoke({messages:[question_With_multiMedia]})print(response)不同类型的模式也可以同时输入比如这里是将图片和音频通讯输入。前提是要求模型必须能够支持比如qwen3-omni-flash就不支持同时混合多个模态。三、总结对于多模态输入建议统一采用 openAI兼容模式来初始化模型客户端实例交互消息遵循openAI的规范相对还是比较容易的。在实际的处理过程中还是建议统一采用URL的方式来进行处理。比较简单整洁。使用前要确认需求和模型的能力比如qwen3-omni-flash就只支持一文本一种媒体而qwen3.5-omni-plus就支持同时组合输入一文本多种媒体。四、附录1、主要参考的文档1集成主要参考https://docs.langchain.com/oss/python/integrations/providers/alibaba_cloud2原生多模态的输入https://help.aliyun.com/zh/model-studio/qwen-omni?spma2c4g.11186623.help-menu-2400256.d_0_3_9_1.28622b07SAqHP6scm20140722.H_2867839._.OR_help-T_cn~zh-V_1#5a06826b49793错码清单Error codes - Alibaba Cloud Model Studio - Alibaba Cloud Documentation Center2、模型初始化说明init_chat_model内部有一套厂商映射分支逻辑传入参数model_providerxxx函数会自动匹配对应的 LangChain 聊天模型类model_provideropenai→ 导入并实例化langchain_openai.ChatOpenAImodel_provideranthropic→ 实例化langchain_anthropic.ChatAnthropicmodel_providergoogle_vertexai→ 实例化 Gemini 对应客户端model_providerollama→ 实例化ChatOllama我们在使用阿里的QWEN模型的时候因为采用的是open_ai的兼容模式所以我们在这个mode provider里面传入的还是 open ai.