【AI大模型实战】Qwen3+QVQ-Max实现一个能吃图片的RAG,建议收藏!!

📅 发布时间:2026/8/27 14:39:51
【AI大模型实战】Qwen3+QVQ-Max实现一个能吃图片的RAG,建议收藏!! 前言在之前我制作了几个 RAG但都是吃文字吐文字的。一想到我的 RAG 只能看到冰冷的文字而看不到类似猫娘的图片时我就替 AI 感到惋惜。那么本文就手把手制作一个能吃图片的 RAG打造一个能够进行图像检索的 RAG。既然可以检索图片那么不失一般性地我们可以用这玩意挑选符合我们要求的猫娘doge。先来看一看最后的效果我制作了页面。首先我已经把这些猫娘的图片上传到系统里面了。我现在描述一下我喜欢的猫娘类型之后点击“检索并回答”。它返回的结果如下原图有点长这个还真的很符合咱们的要求咧手动傲娇。上面这个图片太长了猫娘上方的文字回复如下所示为了避免偶然性提高准确性我们再测试一番结果如下都说了大模型看人很准吧叉腰。但是当我们尝试问一些与所有图片都无关的内容时我们会直接调用 Qwen3-235b-a22b 进行回答。01、涉及的一些主要模型1将图片转化为向量表示为了判断一张图片是否与用户的提问匹配我们会把图片和提问都映射到同一个“语义空间”中得到各自的向量表示。在这个空间里语义相近的内容对应的向量距离会更小然后只需用常见的相似度度量例如余弦相似度来计算两者向量的相似度就能快速评估提问与图片之间的匹配程度。能够同时处理文字和图片我们得找一个多模态的向量嵌入模型我们选择 cohere 的 embed-v4.0 模型。Cohere 的embed-v4.0是一款面向企业级检索和智能代理的多模态嵌入模型能够直接对文本、图片以及混合的文档如 PDF 页面生成高质量向量表示适合构建大规模、低延迟的语义检索和 RAG 系统。2本项目执行流程图一开始用户提问用户的提问会被 embed-v4.0 模型编码为向量表示图库中的图片均已经被 embed-v4.0 编码为向量表示将用户的提问与图库中的所有图片的向量作点积。如果所有点积小于相似度阈值0.3则直接调用 qwen3-235b-a22b 进行文字回复如果有点积大于相似度阈值0.3则选取点积最大所对应的图片将图片传入 qvq-max-2025-03-25 进行回复。qwen3-235b-a22b 能吃文字如果这个模型是多模态的该多好。qvq-max-2025-03-25 能吃图片02、代码讲解https://github.com/mindsRiverPonder/LLM-practice/tree/main/image-RAG%20for%20catgirl1下载并引入必要的库其中 streamlit 是用来构建可视化页面的!pip install-q cohere streamlit引入必要的库importosimport ioimport timeimport zipfile#解压缩用的import base64import requestsimport numpy as npimport PIL.Imageimport streamlit as stfrom openai import OpenAIimport cohere2配置可视化页面的 css 样式可跳过css 样式你可以选择自己设计强烈推荐让画面变得更好看。我就不摆出我的 css 样式设计了毕竟是依托勾式。st.markdown( style 自己设计哈/style ,unsafe_allow_htmlTrue)3配置对于的 API-key初始化客户端为了使用图像嵌入模型 embed-v4.0你需要先去 cohere 官网申请一个免费的 API-KEY那肯定是有速率限制的。https://dashboard.cohere.com/api-keys为了使用 qwen3-235b-a22b 和 qvq-max-2025-03-25你需要先去阿里云百炼官网申请一个 API-KEY。https://bailian.console.aliyun.com/?tabmodel#/model-marketCOHERE_API_KEYos.getenv(COHERE_API_KEY,你的API-KEY)DASHSCOPE_API_KEYos.getenv(DASHSCOPE_API_KEY,你的API-KEY)EMBED_MODELembed-v4.0QUERY_MODELqwen3-235b-a22bVISION_MODELqvq-max-2025-03-25definit_clients():cocohere.ClientV2(api_keyCOHERE_API_KEY)qwen_clientOpenAI(api_keyDASHSCOPE_API_KEY,base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1)returnco,qwen_clientco,qwen_clientinit_clients()4定义一些必要的工具函数这个相似度阈值你们可以根据需要进行调整调高一些就代表检索图片更严格。MAX_PIXELS1568*1568#图片最大像素SIM_THRESHOLD 0.3 # 相似度阈值可根据需要调整1.调整图像尺寸当超过最大像素时自动缩小。defresize_image(pil_image:PIL.Image.Image):w,hpil_image.sizeifw*hMAX_PIXELS:scale(MAX_PIXELS/(w*h))**0.5pil_image.thumbnail((int(w*scale),int(h*scale)))2.将图像转化为 Base64 编码很多 API 都要求图像以 Base64 编码的字符串形式传输。defbase64_from_pil(pil_image:PIL.Image.Image)-str:fmtpil_image.formatorPNGresize_image(pil_image)withio.BytesIO()asbuf:pil_image.save(buf,formatfmt)database64.b64encode(buf.getvalue()).decode()returnfdata:image/{fmt.lower()};base64,{data}3.通过 embed-v4.0 生成图像的嵌入向量并且归一化向量便于计算余弦相似度。defembed_document(img:PIL.Image.Image):b64base64_from_pil(img)doc_in{content:[{type:image,image:b64}]}respco.embed(modelEMBED_MODEL,input_typesearch_document,embedding_types[float],inputs[doc_in])vecnp.array(resp.embeddings.float[0])returnvec/np.linalg.norm(vec)#归一化4.将用户的输入也映射为向量便于检索相关图片defembed_query(text:str):respco.embed(modelEMBED_MODEL,input_typesearch_query,embedding_types[float],texts[text])vecnp.array(resp.embeddings.float[0])returnvec/np.linalg.norm(vec)5模型生成回复因为我这里用的是 qwen3-235b-a22b 和 qvq-max-2025-03-25他们支持流式输出我在页面上也流式展示回复的内容。defgenerate_answer(question:str,img:PIL.Image.ImageNone):流式输出ifimgisnotNone:b64_imagebase64_from_pil(img)messages[{role:system,content:根据下面的图片回答问题。},{role:user,content:[{type:text,text:question},{type:image_url,image_url:{url:b64_image}}]}]responseqwen_client.chat.completions.create(modelVISION_MODEL,messagesmessages,streamTrue,# 启用流式输出 ) else: # 处理纯文本输入 messages [ {role: system, content: 请仅根据文字问题回答}, {role: user, content: question} ] response qwen_client.chat.completions.create( modelQUERY_MODEL, messagesmessages, streamTrue, # 启用流式输出 ) def stream_response(): try: for chunk in response: if chunk.choices[0].delta.content: yield chunk.choices[0].delta.content except Exception as e: st.error(f流式输出错误: {str(e)}) yield 抱歉流式输出过程中发生错误。 return stream_response()6定义存储上传图片路径和对应的嵌入向量的地方ifimg_pathsnotinst.session_state:st.session_state.img_paths[]ifdoc_embeddingsnotinst.session_state:st.session_state.doc_embeddings[]7页面搭建st.sidebar.title(导航栏)pagest.sidebar.radio(功能,[查询,上传,图库],index0)ifpage上传:st.header(上传图片资源)uploadedst.file_uploader(上传单张图片,type[png,jpg,jpeg])urlst.text_input(或输入图片 URL)zipfst.file_uploader(上传 ZIP 压缩包 (仅图片),type[zip])ifuploaded:imgPIL.Image.open(uploaded)pathfuploaded_{int(time.time())}.pngimg.save(path)st.session_state.img_paths.append(path)st.session_state.doc_embeddings.append(embed_document(img))st.success(f已添加:{path})ifurl:try:rrequests.get(url)r.raise_for_status()imgPIL.Image.open(io.BytesIO(r.content))pathfurl_{int(time.time())}.pngimg.save(path)st.session_state.img_paths.append(path)st.session_state.doc_embeddings.append(embed_document(img))st.success(f已下载并添加成功:{path})exceptExceptionase:st.error(fURL 下载失败 :{e})ifzipf:withzipfile.ZipFile(zipf)asz:forfnameinz.namelist():iffname.lower().endswith((.png,.jpg,.jpeg)):dataz.read(fname)imgPIL.Image.open(io.BytesIO(data))pathfzip_{int(time.time())}_{os.path.basename(fname)}img.save(path)st.session_state.img_paths.append(path)st.session_state.doc_embeddings.append(embed_document(img))st.success(ZIP 中的所有图片已添加)elifpage查询:st.header( 图像RAG-文字检索图片)questionst.text_input(请输入您的问题✍️)ifst.button(检索并回答):ifnotquestion:st.warning(请输入问题后再检索 )else:q_embembed_query(question)ifst.session_state.doc_embeddings:docsnp.vstack(st.session_state.doc_embeddings)simsdocs.dot(q_emb)max_simfloat(np.max(sims))idxint(np.argmax(sims))else:max_sim0answer_containerst.empty()full_answer# 判断是否命中 if max_sim SIM_THRESHOLD: # 文字回答流式渲染 ans_generator generate_answer(question) for token in ans_generator: full_answer token answer_container.markdown(f**模型文字回答无相关图片** {full_answer}) else: # 显示图片 best st.session_state.img_paths[idx] img PIL.Image.open(best) st.image(img, captionf最相关图片相关度{max_sim:.2f}, use_column_widthTrue) # 图文回答流式渲染 ans_generator generate_answer(question, img) for token in ans_generator: full_answer token answer_container.markdown(f**模型回答** {full_answer})elif page 图库: st.header( 已录入图片图库) paths st.session_state.img_paths if not paths: st.info(啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊宝宝肚肚打雷啦 。) st.info(一张图片都没有**❗️请先在“上传”页面添加图片❗️**) st.info(啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊) else: for i in range(0, len(paths), 3): cols st.columns(3) for j, path in enumerate(paths[i:i3]): with cols[j]: img PIL.Image.open(path) st.image(img, width150, captionos.path.basename(path))03注意事项1使用前-上传图片想要检索图片你先得上传图片我这里支持 3 种上传方式上传单张图片输入图片的 url 进行自动下载把一大堆图片放进 zip 压缩包里上传后台自动处理2各种库的版本要是出现问题大概率是库版本对不上。我曾经在一个项目里把 numpy 升级到了 2.0 版本整个项目瘫痪赶紧换回以前的版本悲。Python 版本:3.11.12requests 版本:2.32.3numpy 版本:2.0.2PIL/Pillow 版本:11.2.1streamlit 版本:1.45.0openai 版本:1.76.0cohere 版本:5.15.03非本地运行方法colab如果你想在 colab 上直接运行可以参考我以前的文章https://zhuanlan.zhihu.com/p/307710928334正确使用这是一个图像检索 RAG不是猫娘 RAG不要局限于猫娘啦。5模型选用如果你不想用文中的模型都可以换注意把 base_url 和 api key 给换了此外还要查看对应的 API 调用示例。04、可优化的点1.我想到一个功能让用户输入一个关键词后台自动去下载对应的图片bing-image-downloader 这个库估计挺合适。简单来说爬虫。2.如果本项目不用于猫娘检索可以用 gemini 代替 Qwen3-235b-a22b 和 qvq-max-2025-03-25因为 gemini 很容易把动漫角色给 block 掉。最后的最后感谢你们的阅读和喜欢作为一位在一线互联网行业奋斗多年的老兵我深知在这个瞬息万变的技术领域中持续学习和进步的重要性。为了帮助更多热爱技术、渴望成长的朋友我特别整理了一份涵盖大模型领域的宝贵资料集。这些资料不仅是我多年积累的心血结晶也是我在行业一线实战经验的总结。这些学习资料不仅深入浅出而且非常实用让大家系统而高效地掌握AI大模型的各个知识点。如果你愿意花时间沉下心来学习相信它们一定能为你提供实质性的帮助。这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】大模型知识脑图为了成为更好的 AI大模型 开发者这里为大家提供了总的路线图。它的用处就在于你可以按照上面的知识点去找对应的学习资源保证自己学得较为全面。经典书籍阅读阅读AI大模型经典书籍可以帮助读者提高技术水平开拓视野掌握核心技术提高解决问题的能力同时也可以借鉴他人的经验。对于想要深入学习AI大模型开发的读者来说阅读经典书籍是非常有必要的。实战案例光学理论是没用的要学会跟着一起敲要动手实操才能将自己的所学运用到实际当中去这时候可以搞点实战案例来学习。面试资料我们学习AI大模型必然是想找到高薪的工作下面这些面试题都是总结当前最新、最热、最高频的面试题并且每道题都有详细的答案面试前刷完这套面试题资料小小offer不在话下640套AI大模型报告合集这套包含640份报告的合集涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师还是对AI大模型感兴趣的爱好者这套报告合集都将为您提供宝贵的信息和启示。这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】