前端页面连接后端fastapi实现模型本地部署和open ai接入
目录
一、摘要
二、项目亮点
三、项目难点分析
四、项目流程实现
4.1 前端页面构建
部分css样式设置
前端基本页面布局
4.2 fastapi和数据库
4.2.1后端构建总览
4.2.2 数据库的创建与依赖
区块一:登录与注册接口实现
区块二:用户聊天流式输出接口:
区块三:会话功能区
区块四:聊天历史记录区
4.3 模型的切换和流式输出
4.3.1 异步处理和实时响应
SSE(Server-Sent Events)基础架构
yiled异步生成器
4.3.2 各种模型的实现
模型的选择
模型的切换
模型的调用
五、成品效果展示
六、未来展望
展望
改进空间
一、摘要
纯本地大模型推理受算力与硬件成本制约,而纯云调用则面临持续费用与数据隐私风险,且
二者在前端体验层难以无缝切换。现有方案缺少"前端无感知、后端热插拔"的混合推理框架。本文
提出双轨推理引擎:以 FastAPI 为统一网关,暴露 OpenAI-Compatible REST&SSE 接口;结合前
端css和js的相互配合,实现页面的美观化和对元素交互实现各种功能,调用各种ai api,使用
Server-Sent Events(简称SSE,中文服务器推送事件)实现各种ai或者模型的流式输出回答,本
次主要调用kimi和deepseek api实现与用户进行对话以及登录注册功能。将用户的账号信息和聊天
记录都录入sql数据库,创建Coze模型调用api实现与Coze模型的对话。让页面功能不再单调与平
常ai对话聊天。
二、项目亮点
-
编写html页面实现ai化对话页面的基本框架
-
后端fastapi搭建接口实现对前端数据交互和功能实现,数据库创建,实现存储用户的注册信息和每次对话的内容根据用户的id对应存储,下次再次登录时会重现历史记录。
-
云调用ai api实现不同的模型在本地的部署,可以切换模型得到不同的回答和不同功能。接入我在Coze部署的本地智能体,与我的智能体进行聊天,让功能不再单调。
-
各种ai功能不只是存在聊天对话,让功能不止于之前的训练模型,本次实现deepseek的深度思考功能和kimi模型的联网搜索功能
三、项目难点分析
-
页面的现代化,简洁、美观、功能全面 ---解决方式:搭建基本框架:左侧为历史记录存储列表和登录注册按钮,右侧为聊天界面,顶部是简明的信息,中间是对话内容展示,底部是输入框(带有各种功能切换图标)
-
fastapi接口和数据库的搭建 --- 解决方式:结合我想要实现的效果和功能实现,搭建了登陆注册、聊天、历史记录、联网搜索等接口。数据库方面构建用户、会话、历史记录表,分别用来存储用户的注册信息;当前会话的id和标题选择;根据用户id匹配历史记录,登录即展示。

-
模型的回答实现流式输出 --- 解决方式:使用Server-Sent Events(简称SSE),SSE使用让服务器向客户端实时传输数据。连接后端接口的yield异步每次吐出一个token,与SSE匹配使用确保数据流的持续输出。
-
模型的切换和深度思考以及联网搜索等功能完善 --- 解决方式:联合deepseek的api调用中的模型切换,前端页面点击按钮可切换deepseek-reasoner模型进行深度思考,调用月之暗面的联网搜索Tool值设置,可选择打开或者关闭实现联网搜索
四、项目流程实现
4.1 前端页面构建
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>智言引擎</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/img.css">
</head>
<body>
<div class="outer"><div class="main-container"><!-- 侧边栏 --><div class="sidebar" id="sidebar"><!-- 展开/收起按钮 --><button class="toggle-btn" id="toggleBtn" aria-label="切换侧边栏"><span class="icon">☰</span></button><div class="sidebar-section"><div class="sidebar-title">菜单</div><ul class="history-list" id="historyList"><li class="history-item new-chat-btn" id="newChatBtn"><span class="history-icon">+</span><span class="history-text">新对话</span></li><!-- 智能体按钮将通过JavaScript动态添加和管理 --></ul></div><div class="auth-buttons" id="authButtons"><button class="auth-btn login-btn" id="loginBtn"><span>登录</span></button><button class="auth-btn register-btn" id="registerBtn"><span>注册</span></button></div><!-- 用户信息显示区域 --><div class="user-info" id="userInfo"><div class="user-avatar" id="userAvatar">U</div><div class="user-name" id="userName"></div><button class="logout-btn" id="logoutBtn">退出</button></div></div><!-- 主聊天界面 --><div class="container" role="application" aria-label="聊天窗口"><div class="head" role="banner"><div class="head-left"><div class="title"><span class="iconfont icon-shiren1-copy"></span><span>智言引擎</span></div></div><div class="head-center"><div class="model-info"><span class="model-name" id="currentModel">李白 (Coze模型)</span></div></div><div class="head-right"><div class="status" aria-live="polite"><span class="dot" aria-hidden="true"></span><span>已连接</span></div><div class="head-actions"><button class="head-btn" id="settingsBtn" aria-label="设置"><span>⚙️</span></button><button class="head-btn" id="helpBtn" aria-label="帮助"><span>❓</span></button></div></div></div><div class="content" id="chatContent" role="log" aria-live="polite" aria-relevant="additions"></div><form id="chatForm" class="bottom" autocomplete="off" role="form"><div class="input-container"><div class="input-wrapper"><input id="messageInput" class="input" type="text" placeholder="给 智言引擎 发送消息..." aria-label="消息输入框" /><div class="input-actions"><select id="modelSelect" class="model-select"><option value="coze">李白</option><option value="deepseek">DeepSeek</option><option value="kimi">Kimi</option></select><button type="button" class="btn file-btn" id="fileBtn" aria-label="上传文件"><span class="file-icon">📎</span></button><input type="file" id="fileInput" class="file-input" accept=".txt,.pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif" multiple /></div><div class="input-buttons"><button type="button" class="tool-btn" id="webBtn" aria-label="联网搜索"><span>🌐</span></button><button type="button" class="tool-btn" id="searchBtn" aria-label="深度搜索"><span>🔍</span></button><button type="button" class="tool-btn" id="deepThinkBtn" aria-label="深度思考"><span>🧠</span></button><button type="button" class="btn clear" id="clearBtn" aria-label="清空">清空</button><button type="submit" class="btn send" aria-label="发送消息">发送</button></div></div><!-- 文件预览区域 --><div class="file-preview-container" id="filePreviewContainer" style="display: none;"><div class="file-preview-header"><span>已选择的文件:</span><button type="button" class="clear-files-btn" id="clearFilesBtn">清除所有</button></div><div class="file-preview-list" id="filePreviewList"></div></div></div><div class="bottom-tools"></div></form></div></div>
</div><!-- 登录模态框 -->
<div id="loginModal" class="modal"><div class="modal-content"><div class="modal-header">用户登录<span class="close">×</span></div><div class="modal-body"><div class="form-group"><label for="loginUsername">用户名</label><input type="text" id="loginUsername" class="form-control" placeholder="请输入用户名"></div><div class="form-group"><label for="loginPassword">密码</label><input type="password" id="loginPassword" class="form-control" placeholder="请输入密码"></div></div><div class="modal-footer"><button class="modal-btn submit-btn">登录</button><button class="modal-btn cancel-btn">取消</button></div></div>
</div><!-- 注册模态框 -->
<div id="registerModal" class="modal"><div class="modal-content"><div class="modal-header">用户注册<span class="close">×</span></div><div class="modal-body"><div class="form-group"><label for="registerUsername">用户名</label><input type="text" id="registerUsername" class="form-control" placeholder="请输入用户名"></div><div class="form-group"><label for="registerEmail">邮箱</label><input type="email" id="registerEmail" class="form-control" placeholder="请输入邮箱"></div><div class="form-group"><label for="registerPassword">密码</label><input type="password" id="registerPassword" class="form-control" placeholder="请输入密码"></div><div class="form-group"><label for="confirmPassword">确认密码</label><input type="password" id="confirmPassword" class="form-control" placeholder="请再次输入密码"></div></div><div class="modal-footer"><button class="modal-btn submit-btn">注册</button><button class="modal-btn cancel-btn">取消</button></div></div>
</div>
</body>
</html>
部分css样式设置
通用样式:
:root {--max-width: 1226px;--max-height: 800px;--user-bg: #2f80ed;--bot-bg: #ffffff;--card-bg: linear-gradient(90deg, #6a11cb, #2575fc);--page-bg: #f0f4fa;--sidebar-bg: #f8f9fa;--bubble-radius: 16px;--input-bg: #fff;--border-color: #e4e7eb;
}html, body {height: 100%;margin: 0;background: var(--page-bg);font-family: "Segoe UI", Roboto, "PingFang SC", "Helvetica Neue", Arial;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;
}
文字渐变:
.welcome-header h1 {font-size: 36px;font-weight: 700;background: linear-gradient(90deg, #6a11cb, #2575fc);-webkit-background-clip: text;-webkit-text-fill-color: transparent;margin: 20px 0;
}
卡片式功能:
.feature-card {background: rgba(255, 255, 255, 0.8);border-radius: 16px;padding: 20px;width: 180px;box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);transition: all 0.3s ease;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.5);
}.feature-card:hover {transform: translateY(-10px);box-shadow: 0 15px 30px rgba(106, 17, 203, 0.2);
}
按钮交互样式:
.btn {height: 40px;padding: 0 20px;border-radius: 20px;border: none;font-weight: 600;cursor: pointer;box-shadow: 0 4px 10px rgba(0,0,0,0.06);display: flex;align-items: center;justify-content: center;font-size: 14px;transition: all 0.2s;
}.btn:hover {transform: translateY(-2px);box-shadow: 0 6px 12px rgba(0,0,0,0.1);
}.btn.send {background: linear-gradient(180deg, #3f8efc, #2575fc);color: #fff;
}
响应式头部导航:
.head {height: 80px;background: linear-gradient(90deg, #1a1a2e, #16213e, #0f3460);color: #fff;display: flex;align-items: center;justify-content: space-between;padding: 0 20px;gap: 12px;box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);border-bottom: 1px solid rgba(255, 255, 255, 0.1);position: relative;overflow: hidden;transition: all 0.3s ease;
}
前端基本页面布局

4.2 fastapi和数据库
4.2.1后端构建总览

三个表分别记录:用户与ai的聊天历史记录,通过user_id来匹配用户的历史聊天记录,保证历史纪录的私有化、安全性;session表记录每次会话,通过session_id来进行历史记录的排序和读取其中的第一次聊天开头记录为历史记录标题;user表用来存储用户注册的信息,包括id、账号、密码(哈希加密)和邮箱等。
4.2.2 数据库的创建与依赖
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmakerSQLALCHEMY_DATABASE_URL = "sqlite:///./chat.db"engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}, echo=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)Base = declarative_base()
-
定义数据库连接URL SQLALCHEMY_DATABASE_URL,使用SQLite数据库文件 chat.db
创建数据库引擎 engine,配置了线程安全参数和SQL语句回显功能
2. 会话工厂设置 -
创建 SessionLocal 会话工厂,用于生成数据库会话对象
设置会话参数:禁用自动提交和自动刷新,绑定到数据库引擎 -
创建 Base 基类,作为所有ORM模型的父类
用于声明式数据库表映射
dependencies.py 文件:
from fastapi import Depends
from sqlalchemy.orm import Session
from database import SessionLocaldef get_db():db = SessionLocal()try:yield dbfinally:db.close()
-
提供了一个依赖注入函数 get_db() ,用于获取数据库会话连接
使用 SessionLocal 创建数据库会话实例
通过 try-finally 结构确保数据库连接在使用后能够正确关闭 -
利用 FastAPI 的 Depends 机制,可以在路由函数中声明对数据库会话的依赖
实现了数据库连接的自动管理和生命周期控制 -
通过 yield 关键字实现生成器模式,确保即使在异常情况下也能执行 db.close()
避免数据库连接泄漏问题
4.2.3 fastapi接口总览

各自功能:
区块一:登录与注册接口实现
注册时检验数据库uesr表是否存在相同用户名和邮箱名

若没有,则将用户的信息存入数据库,id自增,为后面通过user_id来查询历史记录铺垫。
用户密码哈希加密
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
生成随机Token值:

通过随机Token值能够更好地保护用户的信息,实现了安全的无状态认证机制,避免了服务器端会话存储的开销,通过随机Token匹配用户存储历史记录对话到匹配到的用户数据库。
区块二:用户聊天流式输出接口:

接口接入前端返回回来的模型选择和功能是否打开数据,在前端点击按钮时,返回数据内容格式改变,接口接收到返回回来的数据就会设置相应的参数来设置聊天输出的格式。
deepseek深度思考实现:

前端深度思考按钮打开的话,则会返回模型为deepseek-reasoner的数据给后端,后端就会用接收到的数据设置模型参数

流式输出的具体实现:
1.EventSourceResponse:
--使用 sse_starlette.sse.EventSourceResponse 实现服务器发送事件(SSE)
--用于向客户端流式发送数据
2.异步生成器:
--在各接口中使用
--通过 yield 逐段返回数据给客户端


创建事件流,对事件流进行处理,使用yield与SSE配合完美实现后端逐段传输数据给前端,前端事件接收到服务器流式传输的数据内容,在前端页面逐字显示,实现完美的流式输出。
更完整的工作流程:
-
创建事件生成器: 在每个路由中定义 event_generator() 异步生成器函数
-
逐块处理数据: 根据不同模型或功能,将响应内容分块处理
-
格式化数据: 将每块数据封装成 {"data": json.dumps({"delta": content})} 格式
-
发送数据: 使用 yield 发送格式化后的数据块
-
结束标记: 发送 "[DONE]" 或 "[ERROR]" 标记表示结束或错误
-
包装响应: 使用 EventSourceResponse 包装生成器,设置正确的媒体类型
区块三:会话功能区
(1)获取会话
在用户登录之后会通过user_id来匹配用户获取之前的所有会话记录

(2)创建新会话

接收一个 SessionCreate 类型的请求体数据,初始化会话标题, SessionCreate 类型中设置title="新会话"。db: Session = Depends(get_db): 通过依赖注入获取数据库会话,将新会话添加到数据库中并且提交能够通过会话id查看
(3)更新会话

在再次添加新对话时会更新上次的会话记录,标题更新为用户的第一次输出内容

判断第一次的输出内容字数,如果大于边界值就会只显示前几个字,后面的内容用 "..." 代替。
(4)删除会话

通过传入的会话id匹配相对应的会话,从数据库中获取到这个会话之后,与前端交互,若是用户点击了这个接口的按钮就会删除这个会话,从数据库中删除此次对话包括会话的所有聊天记录。

在鼠标移动到会话标题时,会话最后会出现一个按钮,点击后就会触发接口删除会话以及聊天记录
区块四:聊天历史记录区
历史记录的获取与更新

通过会话id来保存指定的会话匹配的聊天记录
然后我们通过用户登录时他的id来获取他的聊天记录标题以及所有聊天记录
历史记录信息的存储:

通过id匹配把每个用户的会话id和对应的聊天记录存储到数据库中,当用户在前端页面登陆的时候会出现之前的历史记录会话标题和所有历史聊天记录
4.3 模型的切换和流式输出


当模型被选择后会在前端页面更新模型提示,并且再之后模型切换的时候也有相应的提示。
返回相应的model选择和联网、深思功能是否打开的信息给后端,后端接口会判断用户选择是哪种输入方式,从而给出对应的回复方式。
演示部分后端代码:

如果返回的模型数据是kimi的话,会首先判断用户是否传入文件信息,如果有就采用对文件进行读取回复,如果没有的话就会使用普通聊天流式输出回复。
总结:
1.用户界面交互:
···用户通过下拉菜单或欢迎界面的按钮选择模型
···前端JavaScript捕获用户选择并更新界面显示
···选择结果保存到localStorage以持久化
2.请求发送:
···用户发送消息时,前端将选择的模型作为参数传递给后端
···通过EventSource建立SSE连接,包含模型参数
3.后端处理:
···后端根据模型参数选择相应的处理类
···调用对应模型的API进行处理
···返回流式响应给前端
4.响应处理:
···前端接收流式响应并实时显示结果
···根据不同模型的响应格式进行特殊处理
4.3.1 异步处理和实时响应
SSE(Server-Sent Events)基础架构
SSE是一种允许服务器向客户端推送实时数据的技术:
--基于HTTP协议的单向通信机制
--服务器主动向客户端发送数据更新
--客户端通过EventSource API接收数据流
SSE工作实现原理:
1.设置响应头 Content-Type: text/event-stream;保持HTTP连接持续开启
2.使用 EventSource 对象连接到服务器端点
3.监听 onmessage 事件处理数据,监听 onerror 事件处理连接错误
后端实现:
![]()
前端实现:


客户端实时发送数据更新,实现流式输出效果
yiled异步生成器
基本工作机制
- yield 关键字使函数变成生成器,可以暂停执行并逐步产生值
- 每次调用 yield 时,函数状态被保存,执行暂停
- 下次调用时从上次暂停点继续执行
yiled异步生成器的机制无缝集成,实现完美的异步流式输出:
每个 yield 返回的数据被 EventSourceResponse 自动转换为SSE格式;客户端通过 EventSource 接收并解析这些数据流
4.3.2 各种模型的实现
模型的选择
模型选择分为三部分:
第一部分:deepseek的普通chat模式和deepseek-reasoner深度思考模式
第二部分:kimi的普通聊天形式、Tool值设置联网模式和文件读取功能
第三部分:Coze大模型的本地部署智能体调用
模型的切换

模型的调用
对于模型的api调用我选择在我的主函数中对其进行类处理,定义了deepseek类和kimi类,帮助我约束他们需要传进的参数和功能更好的实现。
前端将用户选择的模型和功能传给后端,后端进行判断处理后异步生成SSE格式传给前端,前端界面流式输出最终结果。
五、成品效果展示
Coze模型李白智能体展示:

deepseek深度思考:

kimi文件处理分析:

六、未来展望
展望
1. 功能扩展
多模态支持:增强 Kimi 模型的文件处理能力,支持更多文件格式如视频、音频等
智能体生态:扩展 switchToCozeAgent 功能,支持更多预设智能体场景
个性化配置:完善 settingsBtn 设置功能,允许用户自定义模型参数和界面主题
2. 用户体验优化
会话管理:改进 updateHistoryList 和 switchSession 功能,支持会话分类和搜索
离线支持:利用 localStorage 增强 initializeSessions 的离线工作能力
响应式设计:优化移动端体验,改进 sidebar 的自适应布局
改进空间
1. 技术架构优化
错误处理:加强 EventSource 连接的错误恢复机制,提升 es.onerror 处理逻辑
性能优化:优化 addMessage 中的DOM操作,减少频繁重绘
安全性:完善用户认证和授权机制,增强 login 和 register 接口的安全性
2. 代码质量提升
模块化重构:将代码中的模型处理逻辑分离成独立模块
状态管理:引入状态管理模式替代全局变量如 sessions 和 currentSessionId
代码复用:统一 DeepSeek、Kimi 和 Coze 模型的接口调用方式
3. 用户交互改进
实时反馈:增强 performWebSearch 和 showSearchingMessage 的加载状态提示
文件上传:改进 fileInput 的上传进度显示和大文件处理能力
快捷操作:为 toolButtons 添加键盘快捷键支持
