基于FastAPI构建功能完备的AI助手API(上):基础架构与用户系统

在AI技术快速迭代的今天,拥有一个自主可控的AI助手API变得越来越有价值。本系列将分两篇详细介绍如何使用FastAPI构建功能完善的AI助手后端服务,本文作为第一篇,将重点介绍基础架构设计、数据库搭建和用户认证系统的实现,适合有一定Python基础的开发者参考。

技术栈选型与架构设计

做技术选型时,主要考虑了开发效率、性能和生态完整性三个因素:

  • FastAPI:作为主框架,它的异步支持、自动类型检查和API文档生成功能非常适合快速开发高性能API。相比Flask,它能提供更好的并发处理能力(尤其在IO密集型场景下);相比Django,它更轻量灵活,没有过多的内置组件约束。
  • AI模型集成:采用了阿里云的DashScope服务(兼容OpenAI接口格式),选用"qwen-plus-2025-04-28"模型,支持联网搜索和思维链展示。选择兼容OpenAI格式的服务可以降低未来切换模型的成本。
  • 数据库:使用SQLite作为基础存储,适合中小型应用,无需额外部署服务,开箱即用。其文件型数据库特性也便于开发和调试。生产环境可无缝迁移到PostgreSQL或MySQL等更强大的关系型数据库。
  • 辅助工具
    • bcrypt:用于密码加密存储,比MD5等哈希算法更安全,具有自适应哈希特性,可抵御暴力破解
    • smtplib:处理邮件验证码发送,实现用户身份验证
    • PyPDF2/python-docx/pandas:处理不同格式文件的内容提取,支持文档交互
    • python-multipart:支持文件上传功能,处理表单数据

项目架构采用经典的三层结构:

  1. 接口层:负责处理HTTP请求与响应,定义API端点
  2. 服务层:实现核心业务逻辑,如AI交互、文件处理
  3. 数据层:处理数据存储与读取,与数据库交互

核心基础实现详解

1. 项目初始化与配置

首先看项目的基础配置,这部分代码虽然简单但至关重要,决定了应用的基本行为:

from fastapi import FastAPI, HTTPException, Request
import smtplib
from email.mime.text import MIMEText
from email.header import Header
import random
from datetime import datetime, timedelta
import bcrypt
from fastapi.middleware.cors import CORSMiddleware
import os
import sqlite3
import logging

# 日志配置,开发阶段设为DEBUG级别很有用,便于问题排查
logging.basicConfig(level=logging.DEBUG)

# 加载环境变量,存储敏感配置信息(如API密钥)
from dotenv import load_dotenv
load_dotenv()

# 初始化FastAPI应用
app = FastAPI()

# 解决跨域问题,开发环境允许所有源访问
# 生产环境应限制为特定域名,增强安全性
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 开发环境配置,生产环境需修改
    allow_credentials=True,
    allow_methods=["*"],  # 允许所有HTTP方法
    allow_headers=["*"],  # 允许所有HTTP头
)

代码说明

  • 日志配置:DEBUG级别在开发时能输出详细信息,帮助调试;生产环境可改为INFOWARNING
  • 环境变量:使用python-dotenv加载.env文件中的配置,避免敏感信息硬编码
  • CORS配置:前后端分离开发时必须配置,否则浏览器的同源策略会阻止跨域请求。生产环境中,allow_origins应设置为具体的前端域名,而非通配符*

2. 数据库设计与初始化

一个完善的AI助手API需要存储多种类型的数据,合理的数据库设计是系统可维护性的基础。以下是三个主要数据库表结构的初始化代码:

# 获取数据库连接的工具函数(补充定义)
def get_db():
    """获取数据库连接的上下文管理器"""
    conn = sqlite3.connect('ai_assistant.db')  # 统一数据库文件,便于管理
    conn.row_factory = sqlite3.Row  # 使查询结果可通过列名访问
    try:
        yield conn
    finally:
        conn.close()

# 用户数据库初始化
def init_user_db():
    conn = sqlite3.connect('users.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS users (
                 id INTEGER PRIMARY KEY AUTOINCREMENT,
                 username TEXT UNIQUE NOT NULL,  # 用户名唯一
                 password TEXT NOT NULL,         # 存储加密后的密码
                 email TEXT NOT NULL,            # 用户邮箱,用于验证
                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')  # 注册时间
    conn.commit()
    conn.close()

# 聊天数据库初始化
def init_chat_db():
    with get_db() as conn:
        # 聊天会话表,存储会话元信息
        conn.execute("""
        CREATE TABLE IF NOT EXISTS chat_sessions (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            session_id TEXT UNIQUE NOT NULL,  # 会话唯一标识
            title TEXT NOT NULL,              # 会话标题
            username TEXT NOT NULL,           # 所属用户
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP  # 创建时间
        )""")

        # 聊天消息表,存储具体聊天内容
        conn.execute("""
        CREATE TABLE IF NOT EXISTS chat_messages (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            session_id TEXT NOT NULL,  # 关联的会话ID
            role TEXT NOT NULL,        # 角色(user/assistant/system)
            content TEXT NOT NULL,     # 消息内容
            username TEXT NOT NULL,    # 所属用户
            timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP  # 消息时间
        )""")
        conn.commit()

# 文件记录数据库初始化
def init_file_record_db():
    with get_db() as conn:
        conn.execute("""
        CREATE TABLE IF NOT EXISTS file_records (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            chat_id TEXT NOT NULL,             # 关联的聊天ID
            filename TEXT NOT NULL,            # 存储的文件名(唯一)
            original_filename TEXT NOT NULL,   # 原始文件名
            file_path TEXT NOT NULL,           # 文件存储路径
            file_content TEXT,                 # 提取的文件内容
            upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP  # 上传时间
        )""")
        conn.commit()

设计说明

  • 采用分离式表结构,每个表专注于存储一类数据,符合单一职责原则
  • chat_sessionschat_messages通过session_id关联,实现一对多关系
  • 文件记录与聊天会话关联,支持"聊天+文件"的交互模式
  • 所有表都包含时间戳字段,便于跟踪数据创建/修改时间
  • 使用get_db()上下文管理器统一管理数据库连接,确保资源正确释放

2. 用户认证系统:构建安全可靠的访问控制

用户认证系统是任何应用程序的基础安全组件,它负责验证用户身份并保护敏感信息。以下是基于FastAPI实现的包含邮箱验证的用户认证系统,涵盖验证码生成、邮件发送和安全登录等核心功能。

验证码系统:防止恶意注册

验证码是防止自动化程序恶意注册的有效手段,我们实现了一个简单而安全的验证码机制:

# 验证码存储,生产环境建议用Redis替代
verification_codes = {}

def generate_code(length=6):
    """生成指定长度的数字验证码"""
    return ''.join(random.choice('0123456789') for _ in range(length))

验证码设计特点

  1. 长度可控:默认生成6位数字,可通过参数调整,平衡安全性和用户体验
  2. 纯数字设计:便于用户输入,减少字母大小写混淆问题
  3. 随机性保证:使用random.choice确保每个数字的随机性

存储说明

  • 示例中使用内存字典verification_codes临时存储验证码
  • 生产环境建议替换为Redis等分布式缓存系统,支持:
    • 自动过期机制
    • 多实例共享数据
    • 持久化存储
邮件验证码发送:确保用户身份

为了验证用户提供的邮箱真实性,我们实现了邮件验证码发送功能:

def send_verification_email(sender, auth_code, receiver):
    verification_code = generate_code()
    content = f"您的验证码是:{verification_code}\n请勿将验证码泄露给他人。\n该验证码将在5分钟后失效。"
    message = MIMEText(content, 'plain', 'utf-8')
    message['From'] = sender
    message['To'] = receiver
    message['Subject'] = Header("注册验证码", 'utf-8')

    try:
        # 使用QQ邮箱SMTP服务,其他邮箱类似
        # 常见邮箱SMTP配置:
        # - 163邮箱:smtp.163.com,端口465
        # - Gmail:smtp.gmail.com,端口465
        with smtplib.SMTP_SSL("smtp.qq.com", 465) as smtp:
            smtp.login(sender, auth_code)  # auth_code通常是邮箱的授权码,非登录密码
            smtp.sendmail(sender, receiver, message.as_string())
        # 存储验证码并设置过期时间
        verification_codes[receiver] = {
            "code": verification_code,
            "expires_at": datetime.now() + timedelta(minutes=5)
        }
        return True
    except Exception as e:
        logging.error(f"邮件发送失败:{e}")
        return False

邮件发送流程

  1. 创建邮件内容:包含验证码、安全提示和过期时间
  2. 配置邮件服务器:使用QQ邮箱的SMTP服务(465端口,SSL加密)
  3. 发送并记录验证码:成功发送后,将验证码与过期时间关联存储

安全考虑

  • 验证码设置5分钟有效期,平衡安全性和用户体验
  • 使用SSL加密连接SMTP服务器,防止验证码被窃听
  • 详细日志记录,便于排查发送失败问题
  • 注意:auth_code通常是邮箱的专用授权码,而非登录密码,需在邮箱设置中单独获取
登录接口:安全验证用户身份

登录是用户访问系统的入口,我们实现了一个安全的登录接口:

# 定义登录请求模型(Pydantic)
from pydantic import BaseModel

class LoginRequest(BaseModel):
    username: str
    password: str

@app.post("/api/login")
async def login_user(login_data: LoginRequest):
    username = login_data.username
    password = login_data.password

    try:
        conn = sqlite3.connect('users.db')
        cursor = conn.cursor()
        # 使用参数化查询,防止SQL注入攻击
        cursor.execute("SELECT username, password FROM users WHERE username=?", (username,))
        user = cursor.fetchone()
        conn.close()  # 及时关闭数据库连接,释放资源

        if not user:
            raise HTTPException(status_code=400, detail="用户不存在")

        # 验证密码,注意存储的是加密后的哈希值
        stored_hash = user[1].encode('utf-8')
        # 使用bcrypt验证密码,不直接比较明文
        if not bcrypt.checkpw(password.encode('utf-8'), stored_hash):
            raise HTTPException(status_code=400, detail="密码错误")

        return {"success": True, "message": "登录成功", "data": {"username": username}}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"登录失败:{str(e)}")

登录接口安全特性

  1. 密码验证机制

    • 使用bcrypt算法验证密码,而非明文比较
    • 从数据库获取加密后的哈希值进行比对
    • 避免在代码中处理和传输明文密码
  2. 防注入攻击

    • 使用参数化查询(?占位符)
    • 避免直接拼接SQL字符串
  3. 错误处理

    • 对"用户不存在"和"密码错误"提供明确提示
    • 统一捕获异常并返回适当的错误信息
    • 及时关闭数据库连接,防止资源泄露
  4. 安全最佳实践

    • 不向客户端泄露系统内部细节
    • 对敏感操作进行日志记录(示例中可补充)
    • 后续可扩展添加登录频率限制,防止暴力破解

3. 系统安全考量

在实现用户认证系统时,我们特别关注了以下安全要点:

  1. 密码存储:永远不存储明文密码,使用bcrypt等现代哈希算法。bcrypt具有自适应特性,可通过增加工作因子提高破解难度。
  2. 验证码安全:设置合理的过期时间(如5分钟),防止重复使用;使用足够复杂的验证码(6位及以上数字)。
  3. 传输安全:所有认证相关接口应使用HTTPS加密传输,防止数据在传输过程中被窃听。
  4. 错误信息:避免返回过于详细的错误信息,防止信息泄露。例如,不区分"用户不存在"和"密码错误"的具体耗时,防止枚举攻击。
  5. 暴力破解防护:可考虑添加登录次数限制(如连续5次失败后临时锁定账号),防止暴力破解(示例中可作为扩展实现)。

4. 总结与扩展方向

本文实现的用户认证系统包含了注册验证和登录的核心功能,为应用程序提供了基础的安全保障。在实际应用中,还可以考虑以下扩展:

  1. 完善注册流程:实现完整的用户注册接口,包括用户名唯一性检查、密码强度验证、邮箱验证码验证等步骤。
  2. 密码重置功能:通过邮箱验证码实现安全的密码重置流程,允许用户在忘记密码时重新设置。
  3. JWT令牌认证:登录后颁发JWT(JSON Web Token)令牌,用于后续API访问授权。JWT是一种紧凑的、URL安全的方式,用于在双方之间传递声明。
  4. 多因素认证:提供更高级别的安全保障,特别是针对敏感操作,可结合手机验证码、人脸识别等方式。
  5. 登录日志:记录登录行为(时间、IP、设备等),便于安全审计和异常检测,及时发现可疑登录。

技术亮点与实践总结

在构建基础架构和用户系统时,有几个实践经验值得分享:

  1. 安全性优先:用户认证系统是安全的第一道防线,密码加密、验证码机制都是必不可少的。安全设计应在项目初期就纳入考量,而非后期补丁。
  2. 数据库设计:合理的表结构设计能减少后续维护成本,单一职责原则在这里同样适用。清晰的表关系和字段定义能提高代码可读性。
  3. 错误处理:完善的异常处理机制能提高系统的健壮性,也能给用户更友好的提示。避免将原始错误信息直接返回给用户,应进行适当包装。
  4. 配置管理:使用环境变量管理敏感信息(如API密钥、数据库密码),避免硬编码带来的安全风险。开发环境与生产环境应使用不同的配置。
  5. 代码组织:模块化的代码结构便于维护和扩展,将不同功能拆分为独立函数或模块,提高代码复用性。

小结

本文介绍了基于FastAPI构建AI助手API的基础架构部分,包括技术选型、数据库设计和用户认证系统的实现。这些基础组件虽然不直接体现AI功能,但却是构建一个稳定、安全、可扩展的API服务的关键。

扎实的基础架构能够支撑后续更复杂功能的实现,并确保系统在用户量增长时仍能保持良好的性能和安全性。

在下一篇文章中,我们将聚焦于AI助手的核心功能实现,包括智能聊天(含流式响应、思维链展示和联网搜索)和文件上传处理功能,这些功能将使我们的API真正具备AI助手的能力。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐