Skip to content

用 FastAPI 给 Ollama 造个聊天室?手把手教你搭一个可视化界面!

还在命令行里跟 Ollama 模型“尬聊”?是不是想给它配个更直观、更酷炫的聊天界面?好消息!用 Python 的 FastAPI 框架加上一点点前端魔法,我们就能轻松搞定!

这篇指南会带你一步步,从零开始搭建一个基于 FastAPI 的 Ollama 可视化聊天应用。我们会用到 WebSocket 技术来实现实时的、像打字一样的聊天效果。准备好了吗?Let's build something cool!

第一步:蓝图规划 - 看看我们的“工地”长啥样

在动手之前,先熟悉下项目的基本结构。代码都在 handy-ollama 仓库的 fastapi_chat_app 文件夹里。

fastapi_chat_app/
├── app.py             # FastAPI 应用的“大脑”,处理请求和逻辑
├── websocket_handler.py # (如果逻辑复杂,可以单独放这,本例简化在 app.py) - 处理实时聊天消息
├── static/            # 存放静态文件的地方
│   └── index.html     # 我们聊天界面的“脸面” (HTML)
└── requirements.txt   # 项目需要的“建材”清单 (Python 库)
  • app.py: FastAPI 应用的核心,负责启动服务、定义访问路径(比如聊天窗口的 WebSocket 连接)。
  • (本例简化) websocket_handler.py😗* (如果聊天逻辑复杂,会单独放在这里) 专门处理 WebSocket 连接,负责接收用户消息、发给 Ollama、再把 Ollama 的回复实时传回给前端。
  • static/index.html: 用户看到的那个聊天窗口页面。
  • requirements.txt: 列出了我们项目需要安装的所有 Python 库。

第二步:开工准备 - 获取代码和“建材”

  1. 把代码“搬”回家 (克隆仓库): 打开你的终端(命令行),运行这个命令,把代码下载到你的电脑上:

    bash
    git clone https://github.com/AXYZdong/handy-ollama
  2. 进入“施工现场”并安装“建材” (依赖):

    bash
    cd handy-ollama/fastapi_chat_app
    pip install -r requirements.txt

    第二条命令会自动读取 requirements.txt 文件,帮你把 FastAPI、Ollama Python 库等所有需要的东西都装好。

    重要前提: 在进行下一步之前,请确保你的 Ollama 服务已经在本地运行了!FastAPI 应用需要连接到它才能工作。

第三步:核心揭秘 - WebSocket 如何让聊天“实时”起来

我们的魔法核心就在 app.py 文件里,它利用 WebSocket 技术实现了浏览器和后端之间的实时双向通信。来看看关键代码:

python
import ollama # 用于和 Ollama 模型交流
from fastapi import FastAPI, WebSocket # FastAPI 框架 和 WebSocket 支持
from fastapi.staticfiles import StaticFiles # 用于提供静态文件 (我们的 HTML 页面)
import os # 用于处理文件路径

app = FastAPI()

# --- 设置静态文件目录 ---
# 让 FastAPI 知道可以从 'static' 文件夹提供文件
# __file__ 是当前脚本路径, os.path.dirname 获取目录名
# os.path.join 拼接路径,确保跨平台兼容
static_dir = os.path.join(os.path.dirname(__file__), "static")
app.mount("/static", StaticFiles(directory=static_dir), name="static")

@app.get("/") # 增加一个根路径,方便直接访问聊天页面
async def read_root():
    # 可以重定向到聊天页面,或者提供一个简单的欢迎信息
    # 这里我们直接返回 HTML 文件内容 (更推荐用 Jinja2 模板引擎,但为了简单直接返回)
    # 更简单的方式是让用户直接访问 /static/index.html
    from fastapi.responses import FileResponse
    return FileResponse(os.path.join(static_dir, 'index.html'))


# --- WebSocket 聊天核心逻辑 ---
@app.websocket("/ws") # 定义 WebSocket 的连接路径为 /ws
async def websocket_endpoint(websocket: WebSocket):
    print("客户端尝试连接 WebSocket...")
    await websocket.accept()  # 接受来自浏览器的 WebSocket 连接请求
    print("WebSocket 连接已建立!")
    try:
        # 进入一个循环,持续等待和处理来自客户端的消息
        while True:
            user_input = await websocket.receive_text()  # 等待接收用户发来的文本消息
            print(f"收到用户消息: {user_input}")

            # 调用 Ollama API,进行流式聊天
            stream = ollama.chat(
                model='llama3.1',  # 你想用的 Ollama 模型,确保本地有
                messages=[{'role': 'user', 'content': user_input}],
                stream=True  # 关键!开启流式响应
            )

            print("正在从 Ollama 获取流式响应...")
            # 接收 Ollama 的流式回复,并逐块发回给前端
            try:
                full_response = "" # 可以选择累积完整回复再打印,或者直接流式打印
                for chunk in stream:
                    model_output_chunk = chunk['message']['content']
                    full_response += model_output_chunk
                    # 把这一小块回复,立刻通过 WebSocket 发送回浏览器
                    await websocket.send_text(model_output_chunk)
                    # print(f"发送块: {model_output_chunk}") # 可以在后台看到发送的内容
                print(f"Ollama 回复完毕: {full_response[:100]}...") # 打印部分完整回复到后台
            except Exception as e:
                error_msg = f"与 Ollama 交互出错: {e}"
                print(error_msg)
                await websocket.send_text(error_msg) # 把错误信息也发给前端
                # 可以在这里决定是否断开连接 break
    except Exception as e:
        # 处理 WebSocket 连接本身可能出现的错误,比如客户端突然断开
        print(f"WebSocket 连接出错: {e}")
    finally:
        # 无论如何,最后都尝试关闭连接
        print("正在关闭 WebSocket 连接...")
        await websocket.close()
        print("WebSocket 连接已关闭。")

# --- (可选) 添加启动命令说明,方便直接运行 ---
if __name__ == "__main__":
    import uvicorn
    print("启动 FastAPI 应用,访问 http://localhost:8000 或 http://localhost:8000/static/index.html")
    # 允许所有来源访问(开发时方便,生产环境需要更严格设置)
    uvicorn.run(app, host="0.0.0.0", port=8000)

代码亮点解读:

  1. import: 引入 FastAPI、WebSocket 和 Ollama 库。
  2. app = FastAPI(): 创建 FastAPI 应用实例。
  3. app.mount("/static", ...): 告诉 FastAPI 把 static 文件夹里的内容(比如 index.html)作为静态文件提供服务。
  4. @app.get("/"): 添加了一个根路径处理,方便用户直接访问 http://localhost:8000 就能看到聊天页面(虽然直接访问 /static/index.html 也行)。
  5. @app.websocket("/ws"): 这是魔法发生的地方!它定义了一个处理 WebSocket 连接的函数。浏览器里的 JavaScript 会连接到这个 /ws 路径。
  6. await websocket.accept(): 握手成功,浏览器和服务器建立了持久的 WebSocket 连接。
  7. while Trueawait websocket.receive_text(): 服务器持续监听,等待用户在浏览器里输入消息并发送过来。
  8. ollama.chat(..., stream=True): 把用户消息发给 Ollama 模型,并告诉它我们要流式接收回复。
  9. for chunk in stream: 遍历 Ollama 返回的一小块一小块的回复。
  10. await websocket.send_text(model_output_chunk): 核心! 把拿到的每一小块回复,立刻通过 WebSocket 发回给浏览器。前端 JavaScript 收到后就能实时更新聊天界面,实现打字效果。
  11. try...except...finally: 健壮性处理,确保程序出错或连接断开时能妥善处理。

简单说,就是:浏览器发消息 -> FastAPI 接收 -> 转发给 Ollama -> Ollama 流式回复 -> FastAPI 逐块接收 -> 逐块发回浏览器 -> 浏览器实时显示。

第四步:点火启动!让应用跑起来

  1. 确保你在正确的目录: 你的终端应该还在 handy-ollama/fastapi_chat_app 目录下。

  2. 启动 FastAPI 应用:

    bash
    uvicorn app:app --reload --host 0.0.0.0 --port 8000
    • uvicorn 是一个高性能的 ASGI 服务器,专门用来跑 FastAPI 这种异步应用。
    • app:app 指的是运行 app.py 文件里的那个名为 app 的 FastAPI 实例。
    • --reload 是个开发时的好帮手,当你修改代码保存后,它会自动重启服务,不用手动停掉再启动。
    • --host 0.0.0.0 让局域网内的其他设备也能访问你的应用(如果需要的话),只在本机访问用 127.0.0.1 或不指定也行。
    • --port 8000 指定服务监听的端口,你可以换成别的,只要没被占用。
  3. 打开你的浏览器: 在浏览器地址栏输入 http://localhost:8000 (或者你启动时指定的 IP 和端口)。如果一切顺利,你应该能看到那个简洁的聊天界面了!

  4. 开始聊天吧! 在输入框里输入你的问题,回车或点击发送。见证奇迹的时刻:Ollama 的回复会像打字一样,实时出现在聊天窗口里!

看看效果:

  • 前端界面大概长这样:聊天界面截图
  • 你的终端(后台)会打印出类似这样的日志:后台日志截图