在與 LLM(大語言模型)
對話時,如果每次都等 LLM
處理完畢再返回給客戶端,會顯得比較卡頓,不友好。如何能夠像主流的AI平臺那樣:可以一點一點吐出字符呢?
本文將模仿后端流式輸出文字,前端一塊一塊的顯示文字。主要的實現路徑是:
LLM
采用qwen3
,使用stream
方式輸出- 后端使用
langchain
框架 - 使用
fastapi
實現后端接口 - 前后端之間使用
websocket
長連接通信 - 前端使用一個簡單的
html5
網頁做演示
下面是最終實現的效果:
文章目錄
- LLM流式輸出
- 實現后端接口
- 實現前端頁面
- 見證效果
- 總結
- 代碼
LLM流式輸出
在 langchain
框架中,LLM(大語言模型)
可以用 stream
的方式一點一點吐出內容。請看代碼:
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage,AIMessagemodel_name = "qwen3"llm = ChatOllama(model=model_name,temperature=0.3,verbose=True)import asyncio
async def ask_stream(question,websocket=None):"""與大模型聊天,流式輸出"""for chunk in llm.stream([HumanMessage(content=question)]):if isinstance(chunk, AIMessage) and chunk.content !='':print(chunk.content,end="^")if websocket is not None:await websocket.send_json({"reply": chunk.content})await asyncio.sleep(0.1) # sleep一下后,前端就可以一點一點顯示內容。
在
ask_stream
中使用websocket
做參數只是為了演示便利,不適合用在實際生產環境。
實現后端接口
下面使用 fastapi
實現后端的 websocket
接口,前后端通信使用 json
格式,用 uvicorn
可以啟動api。
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponseapp = FastAPI()@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):await websocket.accept()try:while True:data = await websocket.receive_json()user_message = data.get("message", "")print(f"收到用戶消息: {user_message}")await ask_stream(user_message,websocket=websocket)"""reply_message = ask(user_message)await websocket.send_json({"reply": reply_message})"""except Exception as e:print(f"連接關閉: {e}")import uvicornif __name__ == '__main__':# 交互式API文檔地址:# http://127.0.0.1:8000/docs/ # http://127.0.0.1:8000/redoc/uvicorn.run(app, host="0.0.0.0", port=8000)
從上面的代碼我們可以看出:fastapi
對 websocket 支持的不錯,實現起來也比較簡潔。
實現前端頁面
為了方便演示,我們做了一個 html5
靜態網頁,并實現一個 get
方法將網頁發送給瀏覽器。
- 發送網頁的接口
import os@app.get("/")
async def get():"""返回聊天頁面"""file_path = os.path.join(os.path.dirname(__file__), "chat.html")with open(file_path, "r", encoding="utf-8") as f:html_content = f.read()return HTMLResponse(content=html_content)
- chat.html
<!DOCTYPE html>
<html><head><title>用WebSocket與大模型聊天</title><style>#chat-box {width: 90%;height: 600px;border: 1px solid #ccc;overflow-y: scroll;margin-bottom: 10px;padding: 10px;}#user-input {width: 80%;padding: 5px;}#send-button {padding: 5px 10px;}.user-message {color: blue;}.server-message {color: green;}</style>
</head><body><h1>WebSocket 聊天測試</h1><div id="chat-box"></div><input type="text" id="user-input" placeholder="請輸入你的消息..." /><button id="send-button" onclick="sendMessage()">發送</button><script>var ws = new WebSocket("ws://localhost:8000/ws");var chatBox = document.getElementById("chat-box");var input = document.getElementById("user-input");var currentServerMessageDiv = null; // 記錄正在追加的服務器消息元素ws.onmessage = function(event) {var data = JSON.parse(event.data);handleServerReply(data.reply);};function sendMessage() {var message = input.value.trim();if (message === "") return;appendMessage("你", message, "user-message");ws.send(JSON.stringify({ "message": message }));input.value = "";// 清空服務器回復正在構建的divcurrentServerMessageDiv = null;}function appendMessage(sender, message, className) {var messageElement = document.createElement("div");messageElement.className = className;messageElement.textContent = sender + ": " + message;chatBox.appendChild(messageElement);chatBox.scrollTop = chatBox.scrollHeight;return messageElement;}function handleServerReply(partialText) {if (!currentServerMessageDiv) {// 第一次,創建一個新的divcurrentServerMessageDiv = appendMessage("服務器", partialText, "server-message");} else {// 后續,直接在當前div后面追加currentServerMessageDiv.textContent += partialText;chatBox.scrollTop = chatBox.scrollHeight;}}// 按回車發送消息input.addEventListener("keydown", function(event) {if (event.key === "Enter") {sendMessage();}});</script></body></html>
見證效果
現在我們可以啟動后端接口,然后打開瀏覽器,輸入地址:http://127.0.0.1:8000
,體驗與大語言模型聊天的快樂了。
總結
使用 qwen3
、langchian
、fastapi
、websocket
、html5
實現一個像主流AI工具那樣與 LLM(大語言模型)
聊天的功能很有意思。
當我看到前端一塊一塊的顯示大語言模型的回復的時候,心底不由得涌出一點小震撼:沒錯,它在改變世界!
代碼
本文涉及的所有代碼以及相關資源都已經共享,參見:
- github
- gitee
為便于找到代碼,程序文件名稱最前面的編號與本系列文章的文檔編號相同。
🪐感謝您觀看,祝好運🪐