当前位置: 首页 > news >正文

使用streamlit和qwen-agent实现Qwen3-VL模型图文对话应用的思路

参考在ec2上部署qwen3-VL-2B模型一文已经自行部署了模型。之前只做过文本模型的对话实现,由于Qwen3-VL支持图像识别,需要考虑如何将客户端的图片传输给模型。

AI给出的方案有如下

  • 通过base64编码的方式将图片数据嵌入到请求中
  • 上传文件到文件服务器,将文件url传递给模型
  • 使用对象存储(S3或minio)并传递文件url或使用sdk下载

Qwen3-VL 是否会主动下载图片?在调用 Qwen3-VL 的 API(例如通过阿里云百炼平台或 Model Studio)并传入一个 图片 URL 时,服务端会主动从该 URL 下载图片,以便进行后续的视觉理解推理。所以这要求该 URL 是 公开可访问的(无需登录、无防盗链、无 IP 限制等)。如果 URL 无法被服务端访问(如内网地址、私有 OSS 未授权等),则会报错。

  • 服务端会在处理请求时将图片 临时下载到内存或临时磁盘 中,仅用于本次推理。推理完成后,图片数据会立即被清除,不会长期保存
  • 最好对文件的大小和内容提前进行限制

此外,如果需要附加安全围栏,Qwen-Guard 目前还无法对图片进行过滤。

通过base64编码方案

这种方式不需要额外写文件上传接口,直接把base64发给后端,图片数据在前端处理,不会暴露原始文件路径

按照如下方式将上传的文件经过base64编码之后直接传给后端即可,但是注意需要分辨mime_type类型

bytes_data = uploaded_file.getvalue()
base64_image = base64.b64encode(bytes_data).decode('utf-8')
content = [{"type": "text", "text": user_input},{"type": "image_url","image_url": {"url": f"data:{mime_type};base64,{base64_image}"}}
]

对于在文本对话框粘贴图片实现处理,需要前端监听监听用户的粘贴事件。

  • 在用户粘贴图片时,获取剪贴板中的图片数据,并将其转换为 Base64 编码。
  • 将 Base64 图片数据嵌入到文本输入框或隐藏字段中,以便后续处理。

使用对象存储

首先使用docker自建minio对象存储服务

services:minio:image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/minio/minio:latestcontainer_name: minioports:- 9001:9001- 9000:9000restart: alwayscommand: "server /data --console-address :9001 --address :9000"environment:MINIO_ROOT_USER: adminMINIO_ROOT_PASSWORD: passwd123networks:- 1panel-network# volumes:#   - ./data:/data#   - ./config:/root/.minio/
networks:1panel-network:external: true

创建存储桶和相应的apikey

image-20251114193556219

在前端将上传的文件存储到minio中,bucketname为chatbot

# 初始化MinIO客户端
minio_client = Minio("localhost:9000",access_key="lByNXxxxxxxFXaY9J",secret_key="xELxiQtIxxxxxxxxxxxSVCTCnpunM",secure=False
)
...
# 上传文件
bytes_data = uploaded_file.getvalue()minio_client.put_object("chatbot",object_name,BytesIO(bytes_data),len(bytes_data),content_type=uploaded_file.type)

此时如果直接访问图片http://localhost:9000/chatbot/test.png会出现如下报错

image-20251114194443795

可以考虑直接创建presign url传递

# 获取预签名URL (有效期2小时)
presigned_url = minio_client.presigned_get_object("chatbot", object_name, expires=timedelta(hours=2)
)

之后仍旧通过content传递给后端,但是此时传递的是presigned_url,后端会主动下载文件内容

content = [{"type": "text", "text": user_input},{"type": "image_url","image_url": {"url": presigned_url}}
]

传递给模型的payload为

{'model': 'Qwen3-VL', 'messages': [{'role': 'assistant', 'content': '你好!我是Qwen3-VL,可以处理图像和文本信息。请上传一张图片并提出你的问题吧!'}, {'role': 'user', 'content': [{'type': 'text', 'text': '帮我解释这幅图片的内容?'}, {'type': 'image_url', 'image_url': {'url': 'http://localhost:9000/chatbot/d3989f54-a16b-430b-9275-4692d9aff0a2.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=lByNXv0Atsaq9ZFXaY9J%2F20251114%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251114T115420Z&X-Amz-Expires=7200&X-Amz-SignedHeaders=host&X-Amz-Signature=41379bdacdb07674a53c6e7fea443beaa8b4dd0ce597c0a27224b491160df41a'}}]}], 'stream': True, 'top_p': 0.4}

最终streamlit的实现效果如下

image-20251114195441596

如果上传非常多图片会有影响吗?

image-20251114201802612

下面的代码测试了多图片的生成结果,没有压力

# 创建Pokemon文件夹
os.makedirs("Pokemon", exist_ok=True)# 下载前10个宝可梦图片
base_url = "https://raw.githubusercontent.com/HybridShivam/Pokemon/master/assets/images/"
for i in range(1, 11):# 格式化编号(三位数,前面补0)pokemon_number = f"{i:03d}"image_url = f"{base_url}{pokemon_number}.png"local_filename = f"Pokemon/{pokemon_number}.png"try:print(f"正在下载 {image_url}...")urllib.request.urlretrieve(image_url, local_filename)print(f"已保存到 {local_filename}")except Exception as e:print(f"下载 {image_url} 失败: {e}")# 初始化MinIO客户端
minio_client = Minio("localhost:9000",access_key="xxxxxx",secret_key="xxxxxxxxx",secure=False
)# 创建chatbot存储桶(如果不存在)
try:if not minio_client.bucket_exists("chatbot"):minio_client.make_bucket("chatbot")print("已创建chatbot存储桶")
except Exception as e:print(f"检查或创建存储桶时出错: {e}")# 上传图片到MinIO并生成预签名URL
presigned_urls = []
for i in range(1, 11):pokemon_number = f"{i:03d}"local_filename = f"Pokemon/{pokemon_number}.png"# 检查文件是否存在if not os.path.exists(local_filename):print(f"文件 {local_filename} 不存在,跳过")continuetry:# 上传到MinIOwith open(local_filename, 'rb') as file_data:file_stat = os.stat(local_filename)minio_client.put_object("chatbot",f"pokemon/{pokemon_number}.png",file_data,file_stat.st_size,content_type="image/png")print(f"已上传 {local_filename} 到MinIO")# 生成预签名URL(有效期2小时)presigned_url = minio_client.presigned_get_object("chatbot",f"pokemon/{pokemon_number}.png",expires=timedelta(hours=2))presigned_urls.append(presigned_url)print(f"已生成预签名URL: {presigned_url}")except Exception as e:print(f"上传 {local_filename} 或生成预签名URL时出错: {e}")# 向模型发送请求
url = "http://localhost:8000/v1/chat/completions"
headers = {"Content-Type": "application/json"
}# 为每个宝可梦图片构建消息内容
content_items = [{"type": "text","text": "这些是哪些宝可梦?请告诉我它们的名称和编号。"}
]# 添加所有图片URL到消息内容
for presigned_url in presigned_urls:content_items.append({"type": "image_url","image_url": {"url": presigned_url}})payload = {"messages": [{"role": "user","content": content_items}]
}print("\n发送请求到模型...")
response = requests.post(url, headers=headers, data=json.dumps(payload))print(f"Status Code: {response.status_code}")
print(f"Response: {response.text}")

与qwen-agent结合

参考资料

  • https://github.com/QwenLM/Qwen-Agent
  • https://qwen.readthedocs.io/en/latest/framework/qwen_agent.html

关于Qwen-Agent和AWS Strands Agents的对比

维度Qwen-AgentAWS Strands Agents
主导方阿里云(通义实验室)亚马逊云科技(AWS)
开源情况开源(Apache 2.0 或类似协议),但深度绑定阿里生态完全开源(Apache 2.0),强调开放生态与多厂商兼容
核心理念中文友好、多模态支持、电商/企业场景优化模型驱动、极简开发、生产级可用、全环境兼容
目标用户中文开发者、阿里云用户、电商/金融等垂直行业全球开发者、企业级用户、多云/混合云环境

在agent启动时就会按照tool的配置启动相应的mcp服务

image-20251114204302777

实现如下的逻辑来集成mcp服务器,实现文档搜索等功能

image-20251114205324851

from qwen_agent.agents import Assistant
from qwen_agent.utils.output_beautify import typewriter_print
import osllm_cfg = {"model": "Qwen3-VL","model_server": "http://localhost:8000/v1","api_key": os.getenv("API_KEY", "EMPTY"),"generate_cfg": {"top_p": 0.8},
}
tools = [{"mcpServers": {"time": {"command": "docker","args": ["run","-i","--rm","mcp/time","--local-timezone=Asia/Shanghai",],}}},"code_interpreter",
]bot = Assistant(llm=llm_cfg,system_message="你是一位乐于助人的小助理",name="智能助理",function_list=tools,
)messages = []  # 存储历史聊天内容
while True:query = input("\n用户请求:")if query == "quit":breakelse:messages.append({"role": "user", "content": query})response = []response_plain_text = ""print("AI 回复:")for response in bot.run(messages=messages):response_plain_text = typewriter_print(response, response_plain_text)messages.extend(response)
http://www.dtcms.com/a/611751.html

相关文章:

  • ModelScope-Agent框架:前世今生与技术详解
  • EG3033 三相P/N MOS管栅极驱动芯片技术解析
  • XPath 实例
  • 网站建设 制作教程网站建设中 动态图片
  • 企业门户网站费用实验中心网站建设
  • 网站收录查询工具网站网站是怎么做的
  • 吉林分销网站建设外贸网站建设公司案例
  • seo sem 外贸建站 网站建设 文化墙设计泉州城乡建设网站
  • 广州网站seo地址c2c电子商务网站定制开发
  • eclipse做的网站欧洲卡一卡2卡3卡4卡
  • 模板站怎么改成营销型网站工作是工作
  • 中工信融营销型网站建设seo这个行业怎么样
  • 如何在测试覆盖不足后补充验证
  • 给人做网站能赚钱吗建设机械网站
  • 潘家园做网站的公司做网站的意义
  • 编译型语言基础教学 | 掌握编译过程与语言原理,提升编程能力
  • 中国空间站是干什么的河北邢台做wap网站
  • 如何做视频网站药品包装设计
  • 怎样给网站做排名优化怎样创建网站的代码
  • 郑州做网站推免费企业建站系统源码
  • 网站的搜索功能怎么做wordpress建站插件
  • 王烨洛阳手机seo排名
  • 北仑网站建设培训有没关于做动画设计师的网站
  • h5网站开发教程创建目录 wordpress
  • 网站做一下301跳转长春专业做网站公司哪家好
  • 采光板及采光瓦营销型网站建设photoshop属于什么软件
  • 【大模型】重磅升级!文心 ERNIE-5.0 新一代原生全模态大模型,这你都不认可它吗?!
  • 打不开wordpress站点海南网站建设平台
  • 如何一个空间放两个网站app外包接单
  • 网站优化seo网站架构优化高端酒店网站模板