尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于 Kimi-VL-A3B-Thinking 构建多模态对话助手:Flask 前后端分离应用的完整搭建指南

基于 Kimi-VL-A3B-Thinking 构建多模态对话助手:Flask 前后端分离应用的完整搭建指南 基于 Kimi-VL-A3B-Thinking 构建多模态对话助手Flask 前后端分离应用的完整搭建指南【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/datawhalechina/self-llmKimi-VL-A3B-Thinking 是 Moonshot AI 推出的支持思考过程的多模态推理模型本教程基于 Datawhale 开源仓库《开源大模型食用指南》中的 Kimi-VL-对话助手参考实现讲解如何用 Flask Transformers 搭建一个前后端分离的 Web 对话应用支持同时上传图片与文本、展示模型思考过程、多轮对话与实时参数调节。读完本文你将掌握从环境准备、模型下载到后端推理接口与前端交互界面的完整落地方法并能直接复制参考代码部署运行。应用概览与功能特点该应用是一个基于 Moonshot AI 的 Kimi-VL-A3B-Thinking 多模态模型的前后端分离应用提供简洁的网页界面与模型对话。应用完整代码位于仓库 app 目录包含三个核心文件app.pyFlask 后端负责模型加载、多模态推理、会话管理与接口暴露requirements.txtPython 依赖清单templates/index.html前端聊天界面与交互逻辑。主要功能特点如下多模态输入可同时处理文本和图像让模型理解和分析图像内容图像自动压缩与优化前端 Canvas 与后端 PIL 双重压缩保证大图片也能顺利上传可视化思考过程展示模型分析推理的步骤◁think▷标签并以可折叠面板形式呈现多图上传一次最多上传 2 张图片支持图像对比类问题预加载模型应用启动时在独立线程中加载模型避免每次请求重复加载加载状态反馈前端轮询检查模型加载状态顶部橙色通知条直观提示多轮对话自动保存会话历史可一键清除开启新对话实时参数调节通过滑动条调整生成长度上限与历史记录长度健壮的错误处理覆盖请求超时、网络错误、数据解析错误等场景。环境准备参考文档给出了经验证的基础环境ubuntu 22.04 python 3.12 cuda 12.4 pytorch 2.6.0同时需要保证足够的 GPU 显存模型以 bfloat16 精度加载参考显存占用约 40GB即最低要求为双卡 4090 或单卡 A6000。配置 pip 镜像源国内网络环境下建议先将 pip 换源加速下载pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple安装依赖pip install transformers4.48.2 pip install accelerate1.6.0 pip install flask3.1.0 pip install blobfile3.0.0 pip install pillow10.4.0 pip install modelscope1.22.3仓库中的 requirements.txt 与上述命令保持一致的版本约束torch2.6.0 transformers4.48.2 accelerate1.6.0 flask3.1.0 blobfile3.0.0 pillow10.4.0 modelscope1.22.3也可以直接执行pip install -r requirements.txt一次性安装。其中blobfile是模型权重读取所需依赖modelscope用于在国内环境下载模型pillow用于后端图像处理。模型下载使用modelscope提供的snapshot_download函数下载模型该方法对国内用户十分友好。新建model_download.py文件写入以下代码并运行python model_download.py# model_download.py from modelscope import snapshot_download model_dir snapshot_download(moonshotai/Kimi-VL-A3B-Thinking, cache_dir请修改我, revisionmaster) print(f模型下载完成保存路径为{model_dir})注意请记得修改cache_dir为你自己的模型下载路径。下载完成后将得到模型的本地路径该路径将作为后端代码中MODEL_ID的值。后端实现app.py 详解后端是整个应用的核心代码位于 app.py。下面按模块拆解其关键实现。全局配置与常量from flask import Flask, request, jsonify, render_template, session import torch from transformers import AutoTokenizer, AutoModelForCausalLM, AutoProcessor import gc import re import uuid import json import base64 import logging from io import BytesIO from PIL import Image app Flask(__name__) app.secret_key kimi-chatbot-secret-key # 用于session加密 app.config[MAX_CONTENT_LENGTH] 100 * 1024 * 1024 # 限制上传文件大小为100MB app.config[MAX_CONTENT_PATH] None # 全局变量存储预加载的模型和tokenizer MODEL_ID 请修改我 # 替换为实际的模型路径或名称 tokenizer None model None processor None # 用于存储对话历史的字典 chat_histories {} # 默认值设置 DEFAULT_MAX_NEW_TOKENS 1024 DEFAULT_MAX_HISTORY_LENGTH 10关键点说明MODEL_ID需要手动替换为上一步模型下载的本地路径或 Hugging Face / ModelScope 上的模型名称app.config[MAX_CONTENT_LENGTH]限制上传请求最大 100MB注意这是 Flask 层面对 HTTP body 的限制而前端单张图片的 10MB 限制是另一层约束全局变量tokenizer、model、processor用于存储预加载的模型组件进程内共享chat_histories是一个以会话 UUID 为键的字典用于在服务器端维护每个会话的对话历史。模型预加载函数def load_model(): global tokenizer, model, processor print(正在加载模型和tokenizer请稍候...) processor AutoProcessor.from_pretrained(MODEL_ID, trust_remote_codeTrue) tokenizer AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( MODEL_ID, device_mapauto, torch_dtypetorch.bfloat16, trust_remote_codeTrue ) print(模型加载完成)该函数在应用启动时由独立线程执行if __name__ __main__: import threading threading.Thread(targetload_model).start() app.run(debugTrue, host0.0.0.0, port5000, use_reloaderFalse)要点使用AutoProcessor加载多模态处理器负责图像与文本的联合编码AutoTokenizer加载分词器AutoModelForCausalLM加载生成模型device_mapauto让 accelerate 自动分配设备这是多卡部署如双卡 4090能够跑满 40GB 显存的关键torch_dtypetorch.bfloat16使用 bfloat16 精度加载显著降低显存占用trust_remote_codeTrue允许加载模型的远程自定义代码Kimi-VL 系列需要将模型加载放进独立线程并设置use_reloaderFalse避免 Flask debug 模式下的自动重载导致模型重复加载。响应清理函数 clean_response模型原始输出中包含|eot|、|im_end|、[EOS]等特殊结束标记需要清理后再返回前端。同时要保留思考标签◁think▷...◁/think▷这是 Kimi-VL-Thinking 系列模型可视化思考过程特性的关键。def clean_response(text): 清理模型响应中的特殊标记 text re.sub(r\|im_end\|(\s*\[EOS\])?, , text) text re.sub(r\[EOS\], , text) thinking_pattern r◁think▷([\s\S]*?)◁/think▷ if re.search(thinking_pattern, text): # 思考部分的内容 def clean_thinking_content(match): thinking_content match.group(1) thinking_content re.sub(r[\|/]?eot[\|]?, , thinking_content) thinking_content thinking_content.replace(|eot|, ) thinking_content re.sub(r\|im_end\|(\s*\[EOS\])?, , thinking_content) thinking_content re.sub(r\[EOS\], , thinking_content) return f◁think▷{thinking_content}◁/think▷ text re.sub(thinking_pattern, clean_thinking_content, text) remaining_text re.sub(thinking_pattern, , text) cleaned_remaining re.sub(r[\|/]?eot[\|]?, , remaining_text) cleaned_remaining cleaned_remaining.replace(|eot|, ) cleaned_remaining re.sub(r\|im_end\|(\s*\[EOS\])?, , cleaned_remaining) cleaned_remaining re.sub(r\[EOS\], , cleaned_remaining) text re.sub(r◁/think▷[\s\S]*, f◁/think▷{cleaned_remaining}, text) return text.strip() else: patterns [|eot|, |im_end|, [EOS]] for pattern in patterns: text text.replace(pattern, ) text re.sub(r[\|/]?eot[\|]?, , text) return text.strip()逻辑拆解先用正则匹配◁think▷(...)◁/think▷思考块若存在思考块则分别清理思考块内部与剩余正文中的结束标记并重新拼接确保◁think▷标签本身被保留若不存在思考块则直接移除所有形式的结束标记|eot|、|im_end|、[EOS]及变体eot、/eot等最终返回清洗后的文本思考内容与最终答案一并交付给前端展示。图像处理函数 base64_to_image前端上传的图片以 base64 字符串形式随请求体传输后端需要解码并做压缩处理def base64_to_image(base64_str): if base64, in base64_str: base64_str base64_str.split(base64,)[1] try: image_bytes base64.b64decode(base64_str) image Image.open(BytesIO(image_bytes)) # 压缩大图片如果宽度或高度超过1500像素则按比例缩小 max_size 1500 original_width, original_height image.size if original_width max_size or original_height max_size: if original_width original_height: new_width max_size new_height int(original_height * (max_size / original_width)) else: new_height max_size new_width int(original_width * (max_size / original_height)) image image.resize((new_width, new_height), Image.LANCZOS) # 如果是RGBA模式带透明通道转换为RGB if image.mode RGBA: background Image.new(RGB, image.size, (255, 255, 255)) background.paste(image, maskimage.split()[3]) image background return image except Exception as e: return Image.new(RGB, (100, 100), color(200, 200, 200))要点解析data:image/xxx;base64,...前缀解码为 PIL Image超过 1500px 的图片按比例缩放使用Image.LANCZOS高质量重采样RGBA带透明通道图片粘贴到白色背景上转换为 RGB避免透明通道影响后续处理解码失败时返回一个灰色占位图100×100保证请求流程不中断。路由一首页 /app.route(/) def home(): # 创建会话ID if chat_id not in session: session[chat_id] str(uuid.uuid4()) chat_id session[chat_id] if chat_id not in chat_histories: chat_histories[chat_id] [] return render_template(index.html, chat_idchat_id)首次访问时为该浏览器会话分配唯一uuid存入 Flask session在服务器端字典chat_histories中为该会话初始化空历史列表渲染 index.html并把chat_id注入模板前端通过会话 ID 与后端保持对话关联从而支持多用户同时使用互不干扰。路由二/api/generate核心推理接口该接口同时支持 JSON 与表单FormData两种请求方式核心流程如下1. 模型未就绪保护if tokenizer is None or model is None or processor is None: return jsonify({error: 模型正在加载中请稍后再试}), 503前端轮询时会收到 HTTP 503从而持续显示模型加载中提示。2. 参数获取与范围钳制max_new_tokens int(request.form.get(max_new_tokens) or request.json.get(max_new_tokens, DEFAULT_MAX_NEW_TOKENS)) max_history_length int(request.form.get(max_history_length) or request.json.get(max_history_length, DEFAULT_MAX_HISTORY_LENGTH)) max_new_tokens max(256, min(max_new_tokens, 2048)) max_history_length max(2, min(max_history_length, 20))即使前端滑动条越界后端也会把参数钳制在合法区间生成长度 256-2048历史轮数 2-20这是服务端安全校验的一层保障。3. 多模态输入处理含图像前端会把最近的历史记录含 base64 图片以 JSON 字符串放入表单字段chat_history上传。后端解析后遍历用户消息的content列表type image的条目调用base64_to_image解码为 PIL 图像放入images列表type text的条目保留文本内容处理完成后用processor.apply_chat_template应用聊天模板再调用processor(imagesimages, texttext, ...)完成多模态输入的联合编码text processor.apply_chat_template(messages, add_generation_promptTrue, return_tensorspt) inputs processor(imagesimages, texttext, return_tensorspt, paddingTrue, truncationTrue).to(model.device)4. 推理生成与输出裁剪with torch.no_grad(): generated_ids model.generate(**inputs, max_new_tokensmax_new_tokens) generated_ids_trimmed [ out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids) ] response processor.batch_decode( generated_ids_trimmed, skip_special_tokensTrue, clean_up_tokenization_spacesFalse )[0] cleaned_response clean_response(response)torch.no_grad()关闭梯度计算减少显存占用通过zip(inputs.input_ids, generated_ids)裁剪掉输入部分的 token只保留新生成的 token用processor.batch_decode解码而非 tokenizer保证多模态场景下图像占位 token 能被正确还原。5. 历史记录管理与缓存清理chat_histories[chat_id].append({role: assistant, content: cleaned_response}) if len(chat_histories[chat_id]) max_history_length * 2: chat_histories[chat_id] chat_histories[chat_id][-max_history_length*2:] torch.cuda.empty_cache() gc.collect()历史按轮管理每轮包含 1 条用户消息 1 条助手消息因此保留上限是max_history_length * 2条消息每次请求后主动调用torch.cuda.empty_cache()与gc.collect()清理显存与内存碎片。6. 纯文本输入的兼容路径若请求中没有图像has_input False走传统文本处理分支将用户消息追加到历史取最近max_history_length * 2条构建 messages通过tokenizer.apply_chat_template应用聊天模板后直接生成。这一分支保证应用在不传图时也能作为普通聊天助手使用。路由三/api/clear_historyapp.route(/api/clear_history, methods[POST]) def clear_history(): data request.json chat_id data.get(chat_id, session.get(chat_id)) if chat_id and chat_id in chat_histories: chat_histories[chat_id] [] return jsonify({success: True, message: 聊天历史已清除}) else: return jsonify({success: False, error: 无效的会话ID}), 400按会话 ID 清空服务器端历史记录前端随之重置界面并重新显示欢迎语。前端实现index.html 交互逻辑前端页面位于 templates/index.html是单页应用风格核心交互逻辑全部在原生 JavaScript 中实现。模型加载状态轮询页面初始化后即调用checkModelStatus()向前端发起一个空请求user_input: 若收到 HTTP 503 则 5 秒后再次轮询直到返回非 503 状态码后隐藏顶部橙色通知条并启用发送、上传、清除按钮function checkModelStatus() { fetch(/api/generate, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ user_input: , chat_id: chatId }) }) .then(response { if (response.status 503) { setTimeout(checkModelStatus, 5000); } else { modelLoading.style.display none; sendBtn.disabled false; userInput.disabled false; uploadBtn.disabled false; clearBtn.disabled false; // ... } }) .catch(error { setTimeout(checkModelStatus, 5000); }); }前端 Canvas 图像压缩在用户选择图片后前端先用 Canvas 做一次压缩再以 base64 形式发送与服务端压缩形成双重保障超过 1200×1200 的图片按比例缩放compressImage(file, maxWidth 1200, maxHeight 1200, quality 0.7)根据文件大小动态调整 JPEG 压缩质量大于 3MB 用 0.5大于 1MB 用 0.6其余用 0.7通过采样检查像素 Alpha 通道只有 PNG 且确实含透明像素时才保留 PNG 格式其余一律转 JPEG 以获得更小体积压缩后若仍超过 1MB再以 0.4 质量二次压缩单张图片超过 10MB 会在前端直接拦截并提示用户更换小图。async function compressImage(file, maxWidth 1200, maxHeight 1200, quality 0.7) { // FileReader 读取 - Image 加载 - Canvas 绘制 - // toDataURL(image/jpeg, compressionQuality) 输出 base64 }图片预览与多图管理上传区支持一次选择最多 2 张图片超出上限会弹出提示每张图片生成 100×100 的预览缩略图右上角×按钮可移除发送时总图片大小超过 5MB 会弹出二次确认避免因请求体过大导致失败用户消息在聊天界面同时展示图片缩略图与文本。思考过程折叠展示前端在收到回复后用正则拆分◁think▷...◁/think▷思考块与真实回复const thinkPattern /◁think▷([\s\S]*?)◁\/think▷([\s\S]*)/; const match content.match(thinkPattern); if (match) { const thinkContent match[1].trim(); const realResponse match[2].trim(); // 创建查看思考过程可折叠面板 真实回复区 }点击查看思考过程头部即可展开/收起模型的详细推理步骤真实回复独立显示在面板下方。请求超时与错误处理发送请求时通过AbortController设置 120 秒超时对应服务端图像处理较慢的场景并针对不同错误类型给出用户可读的提示AbortError→ 请求超时: 服务器响应时间过长请稍后重试或尝试上传较小的图片TypeErrorNetworkError→ 网络错误: 连接服务器失败SyntaxError→ 数据解析错误: 服务器返回的数据格式不正确其他错误 → 展示具体错误信息。应用运行与使用指南启动应用python app.py应用将在 http://localhost:5000 上运行。注意启动后模型会在后台自动加载这可能需要 1-2 分钟。在此期间界面会显示模型正在加载中的提示加载完成后才能开始对话。使用步骤在浏览器中打开 http://localhost:5000等待模型加载完成顶部的橙色通知条消失根据需要调整参数滑动条生成长度上限控制每次回复生成的最大 token 数范围256-2048历史记录长度控制对话中保留的最大轮数范围2-20使用多模态功能点击上传图片按钮选择图片支持 JPG、PNG 等常见格式一次最多上传 2 张图片在输入框中输入文本问题例如请描述这个图片中的内容也可以不上传图片仅使用文本进行对话点击发送按钮或按 Enter 键发送问题等待模型生成回复处理图像可能需要更长时间查看分析过程如果回复包含查看思考过程链接可以点击查看模型的详细分析步骤继续进行多轮对话模型会记住之前的对话内容如需清除对话历史点击清除对话历史按钮。参数说明参数作用默认值可调范围服务端钳制max_new_tokens生成长度上限控制每次回复生成的最大 token 数1024256-2048256-2048max_history_length最大对话记忆轮数控制对话中保留的最大轮数102-202-20较大的max_new_tokens允许生成更长的回复但会增加生成时间和资源消耗较大的max_history_length让模型记住更多上下文但会拉长输入序列、增加显存开销。滑动条调整的参数立即生效并应用于下一次对话。注意事项与排查要点确保服务器有足够的 GPU 显存来运行该模型bfloat16 精度下约 40GB参考双卡 4090 或单卡 A6000图片上传单张限制 10MB超过此限制的图片将被前端拒绝上传Flask 层整体请求上限为 100MB系统会自动压缩大尺寸图片但过大的图片仍可能影响性能默认生成限制为 1024 个 token可通过界面滑动条调整范围 256-2048默认保留最近 10 轮对话历史可通过界面滑动条调整范围 2-20启用了torch.no_grad()以减少内存使用处理图像时的请求超时时间为 120 秒如果响应时间过长请尝试上传较小的图片常见坑位MODEL_ID未修改模型加载会失败请替换为实际下载路径或模型名称模型加载卡在模型正在加载中检查显存是否充足、模型路径是否正确、trust_remote_code是否开启上传图片后报错优先尝试小尺寸图片或检查请求体是否超过 100MB 限制。示例问题使用本多模态模型时可以尝试以下类型的问题图像描述请详细描述这张图片中的内容视觉分析这张图片中有哪些物体它们各自的特点是什么图像比较[上传两张图] 比较这两张图片的异同点内容识别图片中的文字内容是什么场景理解这个场景可能是在什么地方为什么情感分析图片中人物的情绪如何基于什么判断视觉推理根据图片内容推测这可能是什么场合或事件。Kimi-VL 多模态对话助手模型加载状态界面Kimi-VL 多模态对话助手多图对话与思考过程展示Kimi-VL 多模态对话助手详细回答展示进一步阅读完整参考代码与使用说明01-Kimi-VL-对话助手/app/README.md配套教程文档01-Kimi-VL-对话助手.md其中包含效果展示、环境准备、模型下载与完整后端代码模型技术细节02-Kimi-VL-技术报告解读.md可了解 Kimi-VL 的 MoE 架构、MoonViT 视觉编码器与思考模型Thinking Model的设计思路仓库还提供了其他模型的 WebDemo 参考实现如 Qwen、ChatGLM、Baichuan 等目录下的 FastApi / WebDemo 文档可作为扩展更多模型的对照参考。【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/datawhalechina/self-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表