下载安装PythonDownload Python | Python.orgThe official home of the Python Programming Languagehttps://www.python.org/downloads/下载【离线安装包】直接点击https://www.python.org/ftp/python/3.15.0/python-3.15.0-amd64.exe开始安装Python下载安装Ollama具体流程参考这个文章用Ollama在本地CMD窗口运行deepseek大模型_运行ollama命令-CSDN博客文章浏览阅读2.2k次点赞10次收藏7次。博客介绍了在本地CMD窗口用Ollama运行deepseek大模型的方法。Ollama安装过程全自动默认装在C盘若不想装在C盘可在安装包所在目录运行CMD执行特定命令。首次运行需下载约5GB的deepseek大模型包第二次运行直接输入特定命令即可。https://blog.csdn.net/qq_37860634/article/details/145511369下载安装千问大模型qwen2.5:3b运行cmd输入代码ollama pull qwen2.5:3b回车根据网速快慢等待约10~20分钟安装完毕下载安装VSCODE用Ollama在本地CMD窗口运行deepseek大模型_运行ollama命令-CSDN博客文章浏览阅读2.2k次点赞10次收藏7次。博客介绍了在本地CMD窗口用Ollama运行deepseek大模型的方法。Ollama安装过程全自动默认装在C盘若不想装在C盘可在安装包所在目录运行CMD执行特定命令。首次运行需下载约5GB的deepseek大模型包第二次运行直接输入特定命令即可。https://blog.csdn.net/qq_37860634/article/details/145511369安装过程超简单这里略过截图新建项目文件夹起什么名字无所谓这里我用AgentDemo打开VSCODE-文件-打开文件夹-找到AgentDemo初始化Python项目CtrlJ打开底部【终端】窗口输入python -m pip install openai fastapi uvicorn回车编写并运行main.py代码在左侧右键新建文件main.py复制以下代码粘贴到main.pyfrom fastapi import FastAPI, Query, Body from fastapi.responses import StreamingResponse from fastapi.middleware.cors import CORSMiddleware from openai import AsyncOpenAI app FastAPI(title本地大模型流式接口) # 解决跨域教学演示允许全部来源 app.add_middleware( CORSMiddleware, allow_origins[*], allow_credentialsFalse, allow_methods[*], allow_headers[*], ) # 对接ollama AsyncOpenAI兼容接口 client AsyncOpenAI(base_urlhttp://localhost:11434/v1/, api_keydemo) async def stream_llm_response(user_msg: str): # AsyncOpenAI sdk stream是异步放到线程执行避免阻塞事件循环 stream await client.chat.completions.create( modelqwen2.5:3b, messages[ {role: system, content: 你是一个助教回答简洁易懂适合学生学习}, {role: user, content: user_msg}, ], streamTrue, ) async for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: text chunk.choices[0].delta.content print(text, end, flushTrue) yield fdata: {text}\n\n yield data: [DONE]\n\n app.api_route(/chat/stream, methods[GET, POST]) async def chat_stream( q: str | None Query(defaultNone), q_post: str | None Body(defaultNone, embedTrue), ): GET请求示例: http://127.0.0.1:8000/chat/stream?q你的问题 POST请求示例(json body): POST http://127.0.0.1:8000/chat/stream Content-Type: application/json { q_post: 你的问题 } # 优先取post参数没有再取get查询参数 user_query q_post if q_post is not None else q if not user_query: return StreamingResponse( iter([data: 缺少提问参数 q\n\ndata: [DONE]\n\n]), media_typetext/event-stream, ) return StreamingResponse( stream_llm_response(user_query), media_typetext/event-stream ) if __name__ __main__: import uvicorn uvicorn.run(main:app, host0.0.0.0, port8000, reloadTrue)运行main.py在底部【终端】输入python main.py回车此刻本地调用大模型的接口已经启动了创建并运行前端页面在左侧新建index.html文件!DOCTYPE html html langzh-CN head meta charsetUTF-8 title本地大模型演示/title style body { max-width: 800px; margin: 40px auto; padding: 0 20px; font-family: system-ui; } #input-box { width: 100%; height: 80px; font-size: 16px; padding:10px; box-sizing: border-box; } #btn-send { margin-top:10px; padding:8px 24px; font-size:16px; cursor:pointer; } #output-area{ margin-top:20px; padding:12px; border:1px solid #ccc; min-height:200px; white-space: pre-wrap; } /style /head body h3本地Ollama大模型演示回车 / 按钮发送ShiftEnter换行/h3 textarea idinput-box placeholder请输入你的问题/textarea br button idbtn-send发送/button div idoutput-area/div script const inputEl document.getElementById(input-box); const outputEl document.getElementById(output-area); const sendBtn document.getElementById(btn-send); let es null; // 封装发送逻辑回车和按钮共用一套代码 function doSend(){ const question inputEl.value.trim(); if(!question) return; // 关闭上一次SSE连接 if(es){ es.close(); } outputEl.innerText ; inputEl.disabled true; sendBtn.disabled true; const url http://127.0.0.1:8000/chat/stream?q${encodeURIComponent(question)}; es new EventSource(url); es.onmessage function(event){ if(event.data [DONE]){ es.close(); inputEl.disabled false; sendBtn.disabled false; return; } outputEl.innerText event.data; } es.onerror function(){ outputEl.innerText \n\n【连接出错请确认ollama和后端服务已经启动】; es.close(); inputEl.disabled false; sendBtn.disabled false; } } // 回车发送 inputEl.addEventListener(keydown, function (e) { if(e.key Enter !e.shiftKey){ e.preventDefault(); doSend(); } }) // 点击按钮发送 sendBtn.addEventListener(click, doSend); /script /body /html然后CtrlX安装插件聚焦到index.html文件使用快捷键AtlL AtlO 或者右键选择【Open with Live Server】会运行打开http://127.0.0.1:5500/index.html网页输入你的问题大模型会回复你至此一个超级简单的调用本地大模型的demo完成进阶这里前端使用的EventSource痛点EventSource 原生是 GET 请求不能自定义 header如果你要传 token 就很麻烦例如你要写一个接口是需要验证用户登录没有才提供答案回复 如果后面你遇到CORS 配置好了普通接口 okSSE 还是跨域方案请求方式底层 API优点缺点EventSource仅 GET浏览器内置代码简短自带断线重连不能 POST参数暴露 URL不能自定义请求头fetchReadableStreamPOST / GETfetch 流读取支持 POST参数放请求体更规范需要手动解析 SSE 字符串代码多一点没有自动重连在实际的商用项目里面我们极力推荐前端使用POST方式请求这里就需要对index.html代码做调整代码如下index.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 title本地大模型演示 POST流式/title style body { max-width: 800px; margin: 40px auto; padding: 0 20px; font-family: system-ui; } #input-box { width: 100%; height: 80px; font-size: 16px; padding:10px; box-sizing: border-box; } #btn-send { margin-top:10px; padding:8px 24px; font-size:16px; cursor:pointer; } #output-area{ margin-top:20px; padding:12px; border:1px solid #ccc; min-height:200px; white-space: pre-wrap; } /style /head body h3本地Ollama大模型演示POST流式回车 / 按钮发送ShiftEnter换行/h3 textarea idinput-box placeholder请输入你的问题/textarea br button idbtn-send发送/button div idoutput-area/div script const inputEl document.getElementById(input-box); const outputEl document.getElementById(output-area); const sendBtn document.getElementById(btn-send); let abortController null; // 用于中断fetch流 // 封装发送逻辑改用 fetch POST 读取SSE流 async function doSend(){ const question inputEl.value.trim(); if(!question) return; // 如果上一次请求还在运行直接终止 if(abortController){ abortController.abort(); } abortController new AbortController(); outputEl.innerText ; inputEl.disabled true; sendBtn.disabled true; try { const resp await fetch(http://127.0.0.1:8000/chat/stream, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ q_post: question }), signal: abortController.signal }); if (!resp.ok) { throw new Error(HTTP ${resp.status}); } // 获取可读流 const reader resp.body.getReader(); const decoder new TextDecoder(utf-8); let buffer ; while(true){ const {done, value} await reader.read(); if(done) break; buffer decoder.decode(value, {stream:true}); // 按SSE分隔符 \n\n 切分一条条事件 const parts buffer.split(\n\n); buffer parts.pop(); // 剩下不完整片段留在buffer for(const part of parts){ if(!part.trim()) continue; // SSE格式 data: xxx if(part.startsWith(data: )){ const data part.substring(data: .length).trimEnd(); if(data [DONE]){ // 流式结束 reader.cancel(); return; } outputEl.innerText data; } } } } catch(err){ if(err.name ! AbortError){ outputEl.innerText \n\n【连接出错请确认ollama和后端服务已经启动】; console.error(err); } } finally { inputEl.disabled false; sendBtn.disabled false; abortController null; } } // 回车发送 inputEl.addEventListener(keydown, function (e) { if(e.key Enter !e.shiftKey){ e.preventDefault(); doSend(); } }) // 点击按钮发送 sendBtn.addEventListener(click, doSend); /script /body /html重新运行main.py和index.html也可以实现同样的提问回复效果