Qwen-7B-Chat:下载、测试、FastAPI 部署与 algo 界面设计

发布时间:2026/10/11 9:03:09
Qwen-7B-Chat:下载、测试、FastAPI 部署与 algo 界面设计
Linux Docker 部署千问 Qwen-7B-Chat下载、测试、algo 服务与界面设计本文适用于已经准备好 Linux Docker、Python、PyTorch、Transformers、ModelScope、FastAPI 和 Uvicorn 的环境。部署重点只有algo/server.py与algo/static/index.html模型下载和测试使用容器内的 Python 命令完成。一、目录结构project/ ├─ algo/ │ ├─ server.py │ └─ static/index.html └─ models/ # 模型缓存建议挂载到宿主机假设容器工作目录为/workspace/qwen宿主机模型目录挂载到/workspace/qwen/models。如果当前镜像已经有模型也可以直接把MODEL_PATH指向现有快照目录。二、容器环境检查进入容器dockerexec-itqwen7bbashcd/workspace/qwen确认 GPU 和 Pythonnvidia-smi python--versionpython-cimport torch; print(torch.__version__); print(torch.cuda.is_available())本文不重复安装依赖。只要以下导入可用就可以继续python-cimport modelscope, transformers, fastapi, uvicorn; print(environment ok)三、下载 Qwen-7B-Chat在容器中执行mkdir-p/workspace/qwen/models python -PY from modelscope import snapshot_download path snapshot_download( qwen/Qwen-7B-Chat, cache_dir/workspace/qwen/models ) print(model path:, path) PYModelScope 会返回实际快照目录。不同版本的缓存结构可能不同不要手写snapshots/master以命令输出为准。也可以通过环境变量指定自定义目录exportQWEN_CACHE_DIR/data/models下载完成后检查关键文件find/workspace/qwen/models-maxdepth5-typef\(-nameconfig.json-o-nametokenizer_config.json\)四、下载后的最小推理测试将下面代码保存为容器中的/workspace/qwen/test_qwen.py或直接使用 heredoc 执行python -PY from pathlib import Path from modelscope import AutoModelForCausalLM, AutoTokenizer model_path /workspace/qwen/models/qwen--Qwen-7B-Chat/snapshots/master tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_path, device_mapauto, trust_remote_codeTrue, ).eval() response, history model.chat(tokenizer, 你好请用一句话介绍 Qwen 7B。, historyNone) print(response) response, _ model.chat(tokenizer, 把刚才的话改成技术博客开场白。, historyhistory) print(response) PY如果实际快照路径不同将model_path替换为snapshot_download的输出。测试通过后再启动 Web 服务。显存不足时可以把模型加载改为 4-bit 量化但需要镜像中已有bitsandbytes并重新验证输出质量。五、启动 algo 服务algo/server.py会在进程启动时加载模型只加载一次请求通过 SSE 持续返回增量文本。设置模型目录后启动exportQWEN_MODEL_PATH/workspace/qwen/models/qwen--Qwen-7B-Chat/snapshots/master python-muvicorn algo.server:app--host0.0.0.0--port2003如果模型目录就是代码中的默认目录也可以直接python algo/server.pyDocker 启动时需要映射端口和模型目录dockerrun--gpusall--nameqwen7b\-p2003:2003\-v/data/qwen/models:/workspace/qwen/models\qwen7b:latest\python-muvicorn algo.server:app--host0.0.0.0--port2003浏览器访问http://宿主机IP:2003。接口测试curl-N-XPOST http://127.0.0.1:2003/chat\-HContent-Type: application/json\-d{message:请介绍一下 Qwen 7B}清空服务端上下文curl-XPOST http://127.0.0.1:2003/reset六、algo 界面设计页面是三段式聊天布局顶部 Header、中部消息区、底部输入区。Header 显示应用名称和清空对话按钮消息区独立滚动用户消息右对齐、模型消息左对齐输入区固定在底部支持多行输入。视觉上使用浅灰背景、白色模型气泡和蓝色用户气泡最大消息宽度为 75%长文本使用word-break自动换行。请求开始时显示“思考中”首个 SSE 片段到达后替换为真实内容。前端使用TextDecoder读取响应流按\n\n分割 SSE 事件再把data字段解析为 JSON。pending保存已经收到但尚未绘制的文本requestAnimationFrame每帧取少量字符渲染避免网络分片导致文字跳动。输入区通过input事件自动增高最大高度 120pxEnter 发送ShiftEnter 换行请求期间禁用发送按钮防止重复提交。消息使用textContent写入避免模型输出被当成 HTML 执行。生产环境应增加用户级 history 隔离、鉴权、限流、请求长度限制、异常提示和日志脱敏。当前示例的全局history只适合单用户演示。七、完整代码algo/server.pyFastAPI streaming service for the local Qwen-7B-Chat model.importjsonimportosfrompathlibimportPathfromfastapiimportFastAPIfromfastapi.responsesimportFileResponse,StreamingResponsefromfastapi.staticfilesimportStaticFilesfrommodelscopeimportAutoModelForCausalLM,AutoTokenizerfrompydanticimportBaseModel BASE_DIRPath(__file__).resolve().parent STATIC_DIRBASE_DIR/staticMODEL_PATHos.getenv(QWEN_MODEL_PATH,/workspace/qwen/models/qwen--Qwen-7B-Chat/snapshots/master,)appFastAPI(titleQwen 7B Algo API)print(fLoading model from{MODEL_PATH}...)tokenizerAutoTokenizer.from_pretrained(MODEL_PATH,trust_remote_codeTrue)modelAutoModelForCausalLM.from_pretrained(MODEL_PATH,device_mapauto,trust_remote_codeTrue).eval()print(Model loaded.)historyNoneclassChatRequest(BaseModel):message:strapp.post(/chat)asyncdefchat(req:ChatRequest):globalhistorydefgenerate():globalhistory lastforresponseinmodel.chat_stream(tokenizer,req.message,historyhistory):deltaresponse[len(last):]lastresponseyieldfdata:{json.dumps({text:delta},ensure_asciiFalse)}\n\n_,historymodel.chat(tokenizer,req.message,historyhistory)yielddata: [DONE]\n\nreturnStreamingResponse(generate(),media_typetext/event-stream)app.post(/reset)asyncdefreset():globalhistory historyNonereturn{ok:True}app.get(/)asyncdefindex():returnFileResponse(STATIC_DIR/index.html)app.mount(/static,StaticFiles(directorystr(STATIC_DIR)),namestatic)八、完整代码algo/static/index.html!DOCTYPEhtmlhtmllangzh-CNheadmetacharsetUTF-8metanameviewportcontentwidthdevice-width, initial-scale1.0titleQwen 7B Chat/titlestyle*{box-sizing:border-box;margin:0;padding:0;}body{font-family:-apple-system,Segoe UI,Microsoft YaHei,sans-serif;background:#f5f5f7;height:100vh;display:flex;flex-direction:column;}header{background:#fff;border-bottom:1px solid #e5e5e5;padding:14px 20px;display:flex;align-items:center;justify-content:space-between;}header h1{font-size:16px;font-weight:600;color:#333;}header button{background:#fff;border:1px solid #ddd;border-radius:8px;padding:6px 14px;font-size:13px;cursor:pointer;color:#555;}#messages{flex:1;overflow-y:auto;padding:24px;display:flex;flex-direction:column;gap:16px;}.msg{max-width:75%;padding:12px 16px;border-radius:14px;line-height:1.6;font-size:15px;white-space:pre-wrap;word-break:break-word;}.user{align-self:flex-end;background:#007aff;color:#fff;border-bottom-right-radius:4px;}.bot{align-self:flex-start;background:#fff;color:#333;border-bottom-left-radius:4px;box-shadow:0 1px 3pxrgba(0,0,0,.06);}.bot.thinking{color:#999;}#input-area{background:#fff;border-top:1px solid #e5e5e5;padding:14px 20px;display:flex;gap:10px;}#input{flex:1;border:1px solid #ddd;border-radius:10px;padding:10px 14px;font-size:15px;outline:none;resize:none;font-family:inherit;max-height:120px;}#input:focus{border-color:#007aff;}#send{background:#007aff;color:#fff;border:none;border-radius:10px;padding:0 22px;font-size:15px;cursor:pointer;}#send:disabled{background:#b0d0ff;cursor:not-allowed;}/style/headbodyheaderh1Qwen 7B Chat/h1buttononclickresetChat()清空对话/button/headerdividmessages/divdividinput-areatextareaidinputrows1placeholder输入消息Enter 发送ShiftEnter 换行/textareabuttonidsendonclicksendMsg()发送/button/divscriptconstmessagesEldocument.getElementById(messages);constinputEldocument.getElementById(input);constsendBtndocument.getElementById(send);letbusyfalse;functionaddMsg(text,cls){constdivdocument.createElement(div);div.classNamemsg cls;div.textContenttext;messagesEl.appendChild(div);messagesEl.scrollTopmessagesEl.scrollHeight;returndiv;}asyncfunctionsendMsg(){consttextinputEl.value.trim();if(!text||busy)return;busytrue;sendBtn.disabledtrue;inputEl.value;inputEl.style.heightauto;addMsg(text,user);constbotDivaddMsg(思考中…,bot thinking);letdisplayText,pending,firsttrue,finishedfalse;functiontick(){if(pending.length){conststeppending.length20?3:1;displayTextpending.slice(0,step);pendingpending.slice(step);botDiv.textContentdisplayText;messagesEl.scrollTopmessagesEl.scrollHeight;}if(pending.length||!finished)requestAnimationFrame(tick);}requestAnimationFrame(tick);try{constrespawaitfetch(/chat,{method:POST,headers:{Content-Type:application/json},body:JSON.stringify({message:text})});constreaderresp.body.getReader();constdecodernewTextDecoder();letbuffer;while(true){const{done,value}awaitreader.read();if(done)break;bufferdecoder.decode(value,{stream:true});constpartsbuffer.split(\n\n);bufferparts.pop();for(constpartofparts){if(!part.startsWith(data: ))continue;constdatapart.slice(6);if(data[DONE])continue;try{constobjJSON.parse(data);if(first){botDiv.classList.remove(thinking);botDiv.textContent;firstfalse;}pendingobj.text;}catch(e){}}}}catch(err){pending\n[请求出错err];}finally{finishedtrue;busyfalse;sendBtn.disabledfalse;inputEl.focus();}}asyncfunctionresetChat(){awaitfetch(/reset,{method:POST});messagesEl.innerHTML;addMsg(对话已清空有什么可以帮你,bot);}inputEl.addEventListener(keydown,e{if(e.keyEnter!e.shiftKey){e.preventDefault();sendMsg();}});inputEl.addEventListener(input,(){inputEl.style.heightauto;inputEl.style.heightMath.min(inputEl.scrollHeight,120)px;});addMsg(你好我是小笨熊有什么可以帮你,bot);/script/body/html九、部署检查清单容器能识别 GPUtorch.cuda.is_available()返回True。snapshot_download成功并确认实际模型快照目录。最小两轮model.chat测试成功。QWEN_MODEL_PATH指向同一个快照目录。浏览器能访问http://宿主机IP:2003。/chat能持续输出 SSE并以[DONE]结束。生产环境已隔离多用户 history并配置鉴权和限流。十、总结在已有 Linux Docker 环境中部署流程可以保持很小先用 ModelScope 下载模型再用两轮model.chat验证最后只启动algo/server.py提供 Web UI 和流式 API。前端通过 SSE、输出缓冲、自动增高输入框和独立滚动消息区完成一个可直接使用的 Qwen 7B 聊天界面。