【用 FastAPI + Ollama + WebSocket 实现可视化对话:全链路流程完全拆解】
·
用 FastAPI + Ollama + WebSocket 实现可视化对话:全链路流程完全拆解
本文阅读动手学Ollama 课程 以及对应代码,使用ai做了一些理解
一、这套程序总共包含哪些部分?
-
app.py
FastAPI 服务端入口,负责 HTTP GET(返回页面)+ WebSocket(实时对话)。 -
websocket_handler.py
专门处理和浏览器之间的双向流式通道,调用 Ollama。 -
index.html(在static/文件夹)
浏览器端界面,用户输入、显示对话、JS 脚本管理前后端通信。
阶段 0:运行程序,FastAPI 启动
执行:
python app.py
内部其实是:
uvicorn.run("app:app", host="127.0.0.1", port=5001, reload=True)
这时:
- FastAPI 挂载了静态文件夹
/static - 定义了
/的 GET 请求 - 定义了
/ws的 WebSocket 通道
阶段 1:用户输入网址,触发 HTTP GET
用户在浏览器输入:
http://127.0.0.1:5001/
浏览器发起:
GET /
FastAPI 用:
@app.get("/", response_class=HTMLResponse)
async def get_form():
with open("static/index.html") as f:
html_content = f.read()
return HTMLResponse(content=html_content)
读取本地 static/index.html,把它原封不动发给浏览器。
阶段 2:浏览器收到 HTML,渲染页面

index.html 里包含:
- 样式(CSS)
- 输入框
<textarea> - 发送按钮
<button> - 核心:
<script>里的 JS 脚本,负责前端和后端如何实时对话。
阶段 3:用户输入内容 + JS 脚本逐步执行
先看点击按钮和按下回车都触发 submitForm()
<textarea id="user_input" ... onkeydown="handleKeyDown(event)"></textarea>
<button onclick="submitForm()">Send</button>
解释:
-
点击
Send👉onclick="submitForm()" -
按
Enter👉onkeydown检测到Enter,触发handleKeyDown,它内部:function handleKeyDown(event) { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); submitForm(); } }⏩ 所以两种方式都会调用
submitForm()。
submitForm() 的每一步
async function submitForm() {
const userInput = document.getElementById('user_input').value;
if (!userInput) return; // 如果输入为空,不做事
addMessage("User", userInput, "user"); // 把用户输入写到对话框
document.getElementById('user_input').value = ''; // 清空输入框
const websocket = new WebSocket(`ws://${location.host}/ws`); // 新建 WebSocket
websocket.onopen = () => {
websocket.send(userInput); // 连接成功后把输入发给后端
};
websocket.onmessage = (event) => {
if (!botMessageDiv) {
botMessageDiv = document.createElement('div');
botMessageDiv.className = 'message bot';
conversationDiv.appendChild(botMessageDiv);
}
botMessageDiv.textContent += event.data; // 追加收到的 chunk
scrollToBottom();
};
websocket.onclose = () => {
console.log('WebSocket connection closed');
botMessageDiv = null; // 一次会话结束
};
}
addMessage() 的作用
function addMessage(sender, text, className) {
const messageDiv = document.createElement('div');
messageDiv.className = 'message ' + className;
messageDiv.textContent = sender + ": " + text;
conversationDiv.appendChild(messageDiv);
scrollToBottom();
}
作用:
- 动态生成一条
<div>。 - 把
"User: 你好"写进去。 - 附加 CSS 类(
user或bot),区别样式。 conversationDiv追加一条消息。- 滚动到最底部。
scrollToBottom() 的作用
function scrollToBottom() {
conversationDiv.scrollTop = conversationDiv.scrollHeight;
}
保证对话窗口总是滚动到最新一条消息。
阶段 4:WebSocket 客户端如何连接后端
new WebSocket(...) 和 app.websocket(...) 的关系
const websocket = new WebSocket(`ws://${location.host}/ws`);
这里告诉浏览器:
用
ws://协议连接http://127.0.0.1:5001/ws。
FastAPI 在 app.py 里:
app.websocket("/ws")(websocket_endpoint)
意思是:
- 所有到
/ws的 WebSocket 请求,都交给websocket_endpoint处理。 websocket_endpoint用fastapi.WebSocket做参数,先执行accept(),然后收消息、调用 Ollama、再一块块发回去。
阶段 5:后端收到消息,调用 Ollama
user_input = await websocket.receive_text()
stream = ollama.chat(
model='llama3.1',
messages=[{'role': 'user', 'content': user_input}],
stream=True
)
这里:
- 把用户输入丢给 Ollama 大模型。
stream=True代表模型边生成边返回。
阶段 6:后端流式发送,前端实时接收
for chunk in stream:
model_output = chunk['message']['content']
await websocket.send_text(model_output)
每收到一个 chunk,立刻发给前端。
前端:
websocket.onmessage = (event) => {
...
botMessageDiv.textContent += event.data;
};
一点点把 LLM 输出拼出来,用户实时看到回复。
总结
| 阶段 | 发生了啥 |
|---|---|
| 0 | 启动 Uvicorn |
| 1 | 浏览器 GET /,FastAPI 返回 HTML |
| 2 | HTML 里 JS 脚本准备好 |
| 3 | 用户输入,点击按钮或回车都触发 submitForm() |
| 4 | submitForm 把输入写到页面,创建 WebSocket |
| 5 | FastAPI /ws 接收消息,调用 Ollama |
| 6 | Ollama 流式生成,FastAPI 一段段发,JS 一段段接 |
流程图(Mermaid)
✨ 结论
✔️ GET 负责返回页面
✔️ WebSocket 负责实时对话
✔️ Ollama 负责生成内容
✔️ JS 负责前端渲染和用户输入处理
更多推荐

所有评论(0)