基于FastAPI与Kimi AI的智能聊天应用开发实践
一、项目概述
本文介绍一个基于现代Web技术栈的智能对话系统,前端采用响应式设计实现聊天界面,后端通过FastAPI框架构建高性能API服务,集成Moonshot AI大语言模型实现智能对话功能。系统具备完整的消息交互流程,支持参数定制化配置,适用于智能客服、在线咨询等多种场景。
二、项目准备
1、首先要去Kimi开放者平台的用户中心去注册一个自己的API key。
Moonshot AI 开放平台https://platform.moonshot.cn/docs/intro#%E6%96%87%E6%9C%AC%E7%94%9F%E6%88%90%E6%A8%A1%E5%9E%8B
这里一点要复制保存好自己的密钥,因为密钥只会出现一次,后去就不能查到了。
三、技术栈全景
跨栈技术选型:
- 前端三件套:HTML5/CSS3/JavaScript
- 服务端框架:FastAPI(Python 3.8+)
- HTTP客户端:httpx(异步支持)
- AI服务:Moonshot API(月之暗面大模型)
- 部署工具:Uvicorn ASGI服务器
四、前后端代码展示
4.1、后端代码
from fastapi import FastAPI, HTTPException, Query # 从fastapi库导入FastAPI类、HTTPException类和Query类
import httpx # 导入httpx库,用于异步HTTP请求
from pydantic import BaseModel, Field # 从pydantic库导入BaseModel和Field类,用于数据模型定义
import logging # 导入logging库,用于日志记录
import uvicorn
from fastapi.middleware.cors import CORSMiddleware # 导入CORS中间件
app = FastAPI() # 创建一个FastAPI应用实例
# 启用CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许所有来源,生产环境中请指定具体域名
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
APIKEY = "sk-SQ5lF3g2OajgNRSi71DXanPbOajSdsx6WJHtWBu52QlDR4XP" # 定义API密钥,用于授权访问外部API
MOONSHOT_URL = "https://api.moonshot.cn/v1/chat/completions" # 定义外部API的URL
# 配置模型参数
class ChatRequest(BaseModel): # 定义一个名为ChatRequest的Pydantic模型类,继承自BaseModel
msg: str = Field(..., min_length=1, example="你好") # 定义msg字段,类型为str,最小长度为1,示例值为"你好"
temperature: float = Field(0.7, ge=0, le=1) # 定义temperature字段,类型为float,默认值为0.7,范围在0到1之间
max_tokens: int = Field(2000, ge=100, le=8000) # 定义max_tokens字段,类型为int,默认值为2000,范围在100到8000之间
@app.get("/chat", response_model=str) # 定义一个路由,处理GET请求,路径为/chat
async def chat(
msg: str = Query(..., min_length=1, example="你好"), # 定义msg查询参数
temperature: float = Query(0.7, ge=0, le=1), # 定义temperature查询参数
max_tokens: int = Query(2000, ge=100, le=8000) # 定义max_tokens查询参数
):
"""
与Moonshot AI交互的聊天接口
- 支持流式响应(需前端配合)
- 参数验证与错误处理
"""
async with httpx.AsyncClient(timeout=30) as client: # 创建一个异步HTTP客户端实例,设置超时时间为30秒
try:
response = await client.post( # 发送POST请求到外部API
MOONSHOT_URL, # 目标URL
headers={"Authorization": f"Bearer {APIKEY}"}, # 设置请求头,包含授权信息
json={ # 设置请求体,包含模型参数
"model": "moonshot-v1-8k", # 指定使用的模型
"messages": [{"role": "user", "content": msg}], # 设置请求消息内容
"temperature": temperature, # 设置temperature参数
"max_tokens": max_tokens # 设置max_tokens参数
}
)
response.raise_for_status() # 如果响应状态码不是200,抛出异常
return response.json()["choices"][0]["message"]["content"] # 解析响应JSON数据,提取回复消息内容并返回
except httpx.HTTPStatusError as e: # 捕获HTTP状态错误
logging.error(f"API错误: {e.response.text}") # 记录错误日志
raise HTTPException(status_code=e.response.status_code, detail="模型服务异常") # 抛出HTTP异常,状态码和错误信息
except Exception as e: # 捕获其他异常
logging.error(f"系统错误: {str(e)}") # 记录错误日志
raise HTTPException(status_code=500, detail="内部服务器错误") # 抛出HTTP异常,状态码500和错误信息
if __name__ == "__main__":
uvicorn.run("msg:app", host="127.0.0.1", port=8000, reload=True) # 启动FastAPI应用,监听127.0.0.1的8000端口,并启用自动重载
4.2、前端代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"> <!-- 设置字符编码为UTF-8 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 设置视口,使页面适应不同设备 -->
<title>聊天界面</title> <!-- 设置页面标题 -->
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
}
#chat-container {
width: 100%; /* 设置宽度为100% */
height: 100%; /* 设置高度为100% */
background-color: #fff; /* 设置背景颜色 */
border-radius: 10px; /* 设置圆角 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 设置阴影 */
overflow: hidden; /* 隐藏溢出内容 */
display: flex; /* 使用Flexbox布局 */
flex-direction: column; /* 设置垂直方向 */
}
#chat-title {
background-color: #007bff; /* 设置标题背景颜色 */
color: #fff; /* 设置标题文字颜色 */
padding: 20px; /* 设置内边距 */
text-align: center; /* 文字居中 */
border-top-left-radius: 10px; /* 设置左上角圆角 */
border-top-right-radius: 10px; /* 设置右上角圆角 */
}
#messages {
flex: 1; /* 占据剩余空间 */
padding: 20px; /* 设置内边距 */
overflow-y: auto; /* 设置垂直滚动条 */
border-bottom: 1px solid #ddd; /* 设置底部边框 */
}
.message {
margin: 10px 0; /* 设置消息间距 */
padding: 10px; /* 设置内边距 */
border-radius: 10px; /* 设置圆角 */
max-width: 70%; /* 设置最大宽度 */
}
.user {
background-color: #dcf8c6; /* 设置用户消息背景颜色 */
align-self: flex-end; /* 消息靠右对齐 */
}
.bot {
background-color: #e0e0e0; /* 设置机器人消息背景颜色 */
align-self: flex-start; /* 消息靠左对齐 */
}
#input-container {
display: flex; /* 使用Flexbox布局 */
padding: 20px; /* 设置内边距 */
}
#user-input {
flex: 1; /* 占据剩余空间 */
padding: 10px; /* 设置内边距 */
border: 1px solid #ddd; /* 设置边框 */
border-radius: 20px; /* 设置圆角 */
margin-right: 10px; /* 设置右边距 */
outline: none; /* 移除聚焦时的默认轮廓 */
}
#user-input:focus {
border-color: #007bff; /* 聚焦时改变边框颜色 */
}
#send-button {
padding: 10px 20px; /* 设置内边距 */
background-color: #007bff; /* 设置背景颜色 */
color: #fff; /* 设置文字颜色 */
border: none; /* 移除边框 */
border-radius: 20px; /* 设置圆角 */
cursor: pointer; /* 设置鼠标悬停时的光标样式 */
}
#send-button:hover {
background-color: #0056b3; /* 悬停时改变背景颜色 */
}
</style>
</head>
<body>
<div id="chat-container">
<div id="chat-title">My Kimi</div> <!-- 添加聊天标题 -->
<div id="messages"></div> <!-- 用于显示聊天消息 -->
<div id="input-container">
<input type="text" id="user-input" placeholder="输入消息..."> <!-- 用户输入框 -->
<button id="send-button">发送</button> <!-- 发送按钮 -->
</div>
</div>
<script>
document.getElementById('send-button').addEventListener('click', sendMessage); // 绑定发送按钮点击事件
document.getElementById('user-input').addEventListener('keypress', function(event) {
if (event.key === 'Enter') {
sendMessage(); // 如果按下Enter键,调用sendMessage函数
}
});
async function sendMessage() {
const userInput = document.getElementById('user-input').value; // 获取用户输入的内容
if (!userInput) return; // 如果输入为空,直接返回
// 显示用户消息
displayMessage('user', userInput);
const params = new URLSearchParams({
msg: userInput,
temperature: 0.7,
max_tokens: 2000
});
try {
const response = await fetch(`http://127.0.0.1:8000/chat?${params}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('网络响应异常');
}
const data = await response.json();
displayMessage('bot', data);
} catch (error) {
console.error('发送消息时出错:', error);
displayMessage('bot', '发生错误,请重试。');
}
// 清空输入框
document.getElementById('user-input').value = '';
}
function displayMessage(sender, message) {
const messagesContainer = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.className = `message ${sender}`;
if (sender === 'user') {
messageElement.textContent = `你: ${message}`; // 显示用户消息时添加“你:”前缀
} else {
messageElement.textContent = message; // 显示机器人消息时不添加前缀
}
messagesContainer.appendChild(messageElement);
messagesContainer.scrollTop = messagesContainer.scrollHeight; // 滚动到底部
}
</script>
</body>
</html>
<!--前端的启动: python -m http.server 8080-->
六、本地部署
# 前端服务
python -m http.server 8080
# 后端服务
uvicorn main:app --reload --port 8000
七、项目运行截图
代码的温度,源于对用户体验的极致追求;智能的未来,始于每一次勇敢的技术探索。
如果觉得对你有帮助的话,留下一个点赞和关注把。