1.初始化项目 #
本项目基于 FastAPI 搭建,采用 SQLAlchemy 进行 ORM 数据库操作,适用于构建以「智能体」为中心的对话或多模型应用后端。以下为初始化项目与依赖配置的详细说明:
创建虚拟环境与依赖安装
推荐使用 uv 工具进行依赖和环境管理,兼容 npm/yarn 的开发体验,大幅加快 Python 依赖管理的效率。- 初始化项目目录(生成 pyproject.toml 等):
uv init - 安装所需依赖包:
uv add python-multipart fastapi uvicorn[standard] sqlalchemy ...
- 初始化项目目录(生成 pyproject.toml 等):
依赖说明
fastapi:主框架,支持异步 RESTful API 和自动生成 OpenAPI 文档,适合敏捷开发。uvicorn[standard]:主流高性能的 ASGI 服务器,用于运行 FastAPI 项目,[standard]字样表示包含优化依赖如 uvloop、httptools 等,提高吞吐量和响应速度。sqlalchemy:行业标准的 Python ORM,统一支持多种数据库,定义模型与数据结构。pymysql:MySQL 适配驱动,配合 SQLAlchemy 实现数据存取。cryptography:用于敏感信息的加解密保障安全。pydantic/pydantic-settings:用于数据模型的类型验证和自动配置环境变量,与 FastAPI 深度集成。httpx:现代化 HTTP 客户端,支持同步与异步请求,适配微服务或第三方 API 调用。python-dotenv:方便本地开发时读取.env环境变量文件。mcp[cli]:多模型协同平台的管理 CLI,可以集中便捷地管理各类 LLM 服务/插件/模型资源。langchain&langchain-deepseek:打造基于大模型(如 DeepSeek、大语言模型等)的复杂推理、记忆和工具调用能力。
目录结构建议
├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 应用主入口 │ ├── models.py # ORM 数据模型定义 │ └── database.py # 数据库连接与Session管理 ├── main.py # 作为服务器启动入口(可选) ├── .env # 环境变量(开发/部署配置) ├── requirements.txt / pyproject.toml └── README.md开发与启动
- 初始化数据库表结构可通过 SQLAlchemy 自动生成。
- 使用如下命令启动开发服务器(默认热重载):
uvicorn app.main:app --reload - 自定义配置(端口、host、日志等)可在
uvicorn.run()或启动命令中指定。
uv init
uv add python-multipart fastapi uvicorn[standard] sqlalchemy pymysql cryptography pydantic pydantic-settings httpx python-dotenv mcp[cli] langchain langchain-deepseek| 模块名称 | 作用简介 |
|---|---|
| python-multipart | 处理 HTTP 的 multipart/form-data(常用于文件上传) |
| fastapi | 高性能 Web API 框架,支持异步、自动文档生成 |
| uvicorn[standard] | ASGI 服务器,运行 FastAPI 等异步 Python 应用,standard 包含性能增强依赖 |
| sqlalchemy | Python ORM 框架,简化数据库操作 |
| pymysql | MySQL 数据库的 Python 客户端 |
| cryptography | 常用的加密和安全功能库 |
| pydantic | 数据验证与解析库,基于 Python 类型注解 |
| pydantic-settings | 基于 pydantic 的配置管理工具 |
| httpx | 新一代 Python HTTP 客户端,支持异步和同步请求 |
| python-dotenv | 加载 .env 文件中的环境变量,便于配置管理 |
| mcp[cli] | 多模型协同平台的命令行工具,便于模型及资源管理 |
| langchain | 用于大语言模型(LLM)应用开发的框架 |
| langchain-deepseek | Langchain 的 deepseek 插件支持,便于集成 DeepSeek LLM |
2. 启动服务器 #
本项目推荐使用 FastAPI 作为 Web 服务主框架,并通过 Uvicorn 启动应用服务器,配合 SQLAlchemy 实现数据库管理。具体启动流程如下:
本地开发环境准备
确保 Python 环境和必需依赖已安装,可以参考前面提供的uv add ...安装命令。若有.env配置文件,建议同步环境变量。启动 API 服务方式
方法一:直接使用 Uvicorn 命令行
在项目根目录下运行:uvicorn app.main:app --reload --host 0.0.0.0 --port 8000其中:
app.main:app指定 FastAPI 实例的位置(app/main.py 里的app对象)。--reload让开发阶段的服务变动代码自动重启(建议开发环境开启,生产关闭)。- 可以通过
--port设置监听端口。
方法二:运行 main.py 脚本
也可直接运行根目录下的main.py,其内部会调用uvicorn.run启动服务(底层同样使用 Uvicorn)。python main.py
验证服务启动情况
启动后可访问 http://localhost:8000/docs 查看自动生成的 API 文档;
访问 http://localhost:8000/health 可验证健康检查接口是否正常响应:{ "status": "ok" }可选配置
- 可通过
.env文件自定义 FastAPI 的标题、描述、数据库地址等参数。 - 若需绑定公网 IP 或自定义域名,请确保服务器开放相关端口。
- 可通过
注意:
- 生产部署建议关闭
--reload,并可结合多进程服务(如 Gunicorn 搭配 Uvicorn worker)。- 若需对接前端/第三方系统,可提前规划跨域(CORS)、接口鉴权等中间件功能。
由此,可快速搭建起数据驱动、接口友好、可拓展的智能体后端服务。
2.1. init.py #
app/init.py
2.2. main.py #
app/main.py
# 导入FastAPI库
from fastapi import FastAPI
# 创建FastAPI应用实例,设置标题和版本号
app = FastAPI(title="智能体服务", version="0.1.0")
# 定义一个GET类型的/health路由用于健康检查
@app.get("/health")
def health():
# 返回服务状态为ok的JSON响应
return {"status": "ok"}2.3. main.py #
main.py
# 导入uvicorn库,用于运行ASGI服务器
+import uvicorn
# 判断当前模块是否为主模块(直接运行该脚本时为True)
if __name__ == "__main__":
# 启动uvicorn服务器,加载app.main模块中的app对象
# host设置为0.0.0.0,允许外部访问
# port设置为8000
# reload=True表示代码变动时自动重启服务(开发环境常用)
+ uvicorn.run(
+ "app.main:app",
+ host="0.0.0.0",
+ port=8000,
+ reload=True,
+ )3. 连接数据库 #
在「连接数据库」这一小节,我们将详细介绍如何在 FastAPI 项目中集成 SQLAlchemy 来操作 MySQL 数据库。
一般流程分为以下几步:
配置数据库连接
在.env和app/config.py中定义数据库连接字符串,便于统一管理和灵活切换。.env文件通过DATABASE_URL配置连接参数,不建议把账号密码硬编码到代码仓库。- 在
app/config.py中通过 pydantic-settings 自动加载.env配置,提供便捷的全局访问。
创建 SQLAlchemy 数据库引擎和会话
- 在
app/database.py中定义engine(连接池)、SessionLocal(数据库会话工厂)和Base(ORM模型基类)。 - 所有 ORM 模型需继承自
Base,以便 SQLAlchemy 能自动发现和创建数据表。
- 在
依赖注入数据库会话
- 在业务路由中,需要为每个请求提供一个独立的数据库会话。通常可通过 FastAPI 的依赖注入机制(
Depends)实现。 - 推荐定义
get_db函数:用yield生成 session,自动实现请求结束时的关闭和资源回收,避免连接泄漏。
- 在业务路由中,需要为每个请求提供一个独立的数据库会话。通常可通过 FastAPI 的依赖注入机制(
数据库表模型定义与迁移(可选)
- 以 Python 类的方式定义数据表结构,继承自
Base。 - 若需要自动迁移/同步表结构,可借助 Alembic 等工具完成。
- 以 Python 类的方式定义数据表结构,继承自
安全与性能建议
- 生产环境下建议使用更复杂的数据库账号密码,并控制连接池数量。
- 谨慎处理事务,避免长事务与死锁。
通过上述步骤,即可为智能体后端服务建立可靠的数据库访问能力,为后续业务开发打下坚实基础。
3.1 创建数据库 #
CREATE DATABASE ai_agent CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;3.2. .env #
.env
DATABASE_URL=mysql+pymysql://root:root@127.0.0.1:3306/ai_agent?charset=utf8mb4
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:51733.3. config.py #
app/config.py
# 导入Path用于路径处理(虽然本文件未直接用到)
from pathlib import Path
# 导入pydantic_settings中的BaseSettings和SettingsConfigDict用于配置管理
from pydantic_settings import BaseSettings, SettingsConfigDict
# 定义Settings类,继承自BaseSettings,便于环境变量和配置文件的读取
class Settings(BaseSettings):
# 配置SettingsConfigDict,指定.env文件路径及编码格式,并设置额外字段忽略
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
# 数据库连接字符串,默认连接本地mysql数据库
database_url: str = "mysql+pymysql://root:password@127.0.0.1:3306/agent?charset=utf8mb4"
# 允许跨域的前端地址,多个地址以逗号分隔
cors_origins: str = "http://localhost:5173,http://127.0.0.1:5173"
# 创建Settings实例,供项目其他部分导入使用
settings = Settings()
3.4. database.py #
app/database.py
# 导入SQLAlchemy的create_engine用于创建数据库引擎
from sqlalchemy import create_engine
# 导入sessionmaker用于会话创建,DeclarativeBase用于基类声明
from sqlalchemy.orm import sessionmaker, DeclarativeBase
# 从app.config中导入settings对象,读取配置信息
from app.config import settings
# 定义ORM模型的基类,所有模型都将继承该类
class Base(DeclarativeBase):
pass
# 创建数据库引擎,连接方式使用settings中的database_url
# pool_pre_ping=True用于防止数据库连接断开
# pool_recycle=3600设置连接池中连接的最大存活时间为3600秒
engine = create_engine(
settings.database_url,
pool_pre_ping=True,
pool_recycle=3600,
)
# 创建会话工厂,autocommit=False表示手动提交事务
# autoflush=False表示不自动刷新
# bind=engine用于绑定数据库引擎
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 定义数据库会话的生成器,在依赖中使用
def get_db():
# 创建一个数据库会话实例
db = SessionLocal()
try:
# 使用yield返回会话对象
yield db
finally:
# 关闭会话,释放资源
db.close()3.5. main.py #
main.py
# 导入uvicorn库,用于运行ASGI服务器
import uvicorn
# 从sqlalchemy导入text,用于执行原生SQL语句
+from sqlalchemy import text
# 从app.database模块导入get_db,用于获取数据库会话
+from app.database import get_db
# 尝试与数据库建立连接
+try:
# 获取数据库会话生成器
+ db_gen = get_db()
# 获取数据库会话实例
+ db = next(db_gen)
# 执行一条简单的SQL语句以测试数据库连接
+ db.execute(text("SELECT 1"))
# 打印数据库连接正常的信息
+ print("数据库连接正常")
# 如果发生异常
+except Exception as e:
# 打印数据库连接失败的错误信息
+ print(f"数据库连接失败: {e}")
# 无论是否发生异常都会执行
+finally:
+ try:
# 尝试关闭数据库会话生成器,释放资源
+ db_gen.close()
+ except Exception:
# 如果关闭时发生异常则忽略
+ pass
# 判断是否为主程序运行入口
if __name__ == "__main__":
# 启动uvicorn服务器,加载app.main模块下的app实例
# host设置为0.0.0.0以便外部主机访问
# port设置为8000
# reload设置为True用于开发时自动重启
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=8000,
reload=True,
)4. 数据模型 #
以下是对数据模型的详细讲解:
本项目的数据模型主要使用SQLAlchemy的声明式ORM方式定义,所有模型都继承自 app.database 中的 Base 基类。
McpService 模型
用于存储第三方MCP服务的配置信息,主要字段说明如下:
- id:主键,自增,为每个服务分配唯一标识。
- name:服务名称,字符串类型,设置唯一约束(
unique=True)和索引(index=True),保证每个服务名称唯一并提升查询效率。 - description:服务描述,可为
NULL,用于详细说明服务作用等扩展信息。 - protocol:协议类型,如
HTTP,MQTT等,不能为空。 - config:用于保存服务的配置信息,采用 MySQL 的
JSON类型字段,可灵活扩展不同服务的配置参数。
该表设计可拓展性强,新增服务类型或配置信息时无需改动表结构。
LlmModel 模型
用于存储大模型 API 提供方的信息和其支持的大模型列表,每个字段解释如下:
- id:主键,自增。
- provider_name:提供方名称,唯一且有索引,如
OpenAI、Azure。 - provider_icon:提供方的图标地址,可以为
NULL,便于前端展示。 - api_base_url:API 的基础请求地址,为必填项。
- api_key:用于访问该 LLM 提供方 API 的密钥,为必填项。
- api_key_url:密钥申请或获取地址,为可选项,便于新用户配置。
- model_names:存储提供方所支持的具体模型列表,数据类型为
JSON,例如["gpt-3.5-turbo", "gpt-4"]。
这种设计便于后续动态添加更多模型或多个提供方,同时支持前端读取支持模型以动态渲染 UI。
数据类型说明
- 采用
Mapped[...]注解配合mapped_column明确字段类型,兼容 SQLAlchemy 2.0+。 - 字符串长度(如
String(255),String(1024))根据实际业务场景预留充足长度。 JSON类型便于存储动态配置信息和模型名称列表(仅在支持 JSON 的数据库后端如 MySQL 5.7+ 有效)。- 支持可空字段均加了
nullable=True标记。
示例数据
McpService示例:{ "id": 1, "name": "mqtt-service", "description": "用于IoT设备通讯的MQTT服务", "protocol": "MQTT", "config": { "host": "localhost", "port": 1883, "username": "user1", "password": "*****" } }LlmModel示例:{ "id": 1, "provider_name": "OpenAI", "provider_icon": "https://example.com/icon.png", "api_base_url": "https://api.openai.com/v1/", "api_key": "sk-****************", "api_key_url": "https://platform.openai.com/account/api-keys", "model_names": ["gpt-3.5-turbo", "gpt-4"] }
通过上述模型设计,可以方便扩展系统对接多种服务和大模型能力,并具备良好的灵活性与可维护性。
4.1. models.py #
app/models.py
# 导入datetime用于处理日期和时间
from datetime import datetime
# 导入SQLAlchemy中的字段类型和时间函数
from sqlalchemy import DateTime, String, Text, func
# 导入MySQL方言中的JSON类型
from sqlalchemy.dialects.mysql import JSON
# 导入ORM映射辅助工具
from sqlalchemy.orm import Mapped, mapped_column
# 导入数据库基类
from app.database import Base
# 定义McpService服务模型
class McpService(Base):
# 设置对应的表名
__tablename__ = "mcp_services"
# 主键ID,自增
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
# 服务名,唯一且有索引
name: Mapped[str] = mapped_column(String(255), unique=True, index=True)
# 服务描述,允许为null
description: Mapped[str | None] = mapped_column(Text, nullable=True)
# 协议类型,必填
protocol: Mapped[str] = mapped_column(String(32), nullable=False)
# 配置信息,JSON格式,必填
config: Mapped[dict] = mapped_column(JSON, nullable=False)
# 定义LlmModel大模型提供方模型
class LlmModel(Base):
# 设置表名
__tablename__ = "llm_models"
# 主键ID,自增
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
# 提供方名称,唯一且有索引
provider_name: Mapped[str] = mapped_column(String(255), unique=True, index=True)
# 图标地址,允许为null
provider_icon: Mapped[str | None] = mapped_column(Text, nullable=True)
# API基础URL,必填
api_base_url: Mapped[str] = mapped_column(String(1024), nullable=False)
# API密钥,必填
api_key: Mapped[str] = mapped_column(String(1024), nullable=False)
# 密钥获取地址,可空
api_key_url: Mapped[str | None] = mapped_column(String(1024), nullable=True)
# 支持的模型名列表,JSON格式,必填
model_names: Mapped[list[str]] = mapped_column(JSON, nullable=False)
# 定义Agent智能体模型
class Agent(Base):
# 表名设置
__tablename__ = "agents"
# 主键、自增
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
# 头像,可空
avatar: Mapped[str | None] = mapped_column(Text, nullable=True)
# 名称,有索引
name: Mapped[str] = mapped_column(String(255), index=True)
# 描述,可空
description: Mapped[str | None] = mapped_column(Text, nullable=True)
# 开场消息,可空
opening_message: Mapped[str | None] = mapped_column(Text, nullable=True)
# 系统提示,必填
system_prompt: Mapped[str] = mapped_column(Text, nullable=False)
# LLM提供方名称,必填
llm_provider_name: Mapped[str] = mapped_column(String(255), nullable=False)
# LLM模型名称,必填
llm_model_name: Mapped[str] = mapped_column(String(255), nullable=False)
# MCP服务ID列表,JSON格式,必填
mcp_service_ids: Mapped[list[int]] = mapped_column(JSON, nullable=False)
# 询问提示词(模板),可空
ask_prompt_template: Mapped[str | None] = mapped_column(Text, nullable=True)
# 询问变量,默认为空列表,JSON格式,不能为空
ask_variables: Mapped[list[dict]] = mapped_column(JSON, nullable=False, default=list)
# 定义智能体对话会话模型
class AgentChatSession(Base):
# 表名
__tablename__ = "agent_chat_sessions"
# 主键,自增
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
# 智能体ID,有索引且必填
agent_id: Mapped[int] = mapped_column(nullable=False, index=True)
# 会话标题,必填,默认“新对话”
title: Mapped[str] = mapped_column(String(255), nullable=False, default="新对话")
# 创建时间,默认当前时间
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=False), server_default=func.now(), nullable=False
)
# 更新时间,默认当前时间,修改时更新
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=False), server_default=func.now(), onupdate=func.now(), nullable=False
)
# 定义智能体对话消息模型
class AgentChatMessage(Base):
# 表名
__tablename__ = "agent_chat_messages"
# 主键,自增
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
# 会话ID,有索引且必填
session_id: Mapped[int] = mapped_column(nullable=False, index=True)
# 发送者角色(如user/agent),必填
role: Mapped[str] = mapped_column(String(32), nullable=False)
# 消息内容,必填
content: Mapped[str] = mapped_column(Text, nullable=False)
# 附加元信息,默认为空dict,JSON格式
meta: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
# 消息创建时间,默认为当前时间
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=False), server_default=func.now(), nullable=False
)4.2. 表说明 #
| 表名 | 中文说明 |
|---|---|
mcp_services |
MCP 服务注册(名称唯一),存协议类型与连接配置 JSON |
llm_models |
大模型提供商配置(provider_name 唯一),含接口地址、密钥、可用模型列表 |
agents |
智能体配置:系统提示、绑定提供商/模型名、绑定的 MCP id 列表、询问变量与模板 |
agent_chat_sessions |
某智能体下的一次对话会话 |
agent_chat_messages |
会话内的单条消息(角色、正文、扩展元数据) |
4.2.1 mcp_services #
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
int |
PK,自增 | 主键 |
name |
varchar(255) |
NOT NULL,唯一 | 服务展示/管理用名称 |
description |
text |
可空 | 服务说明 |
protocol |
varchar(32) |
NOT NULL | 传输协议:stdio / sse / streamable-http 等 |
config |
json |
NOT NULL | 连接参数(命令行、URL、headers、env 等) |
4.2.2 llm_models #
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
int |
PK,自增 | 主键 |
provider_name |
varchar(255) |
NOT NULL,唯一 | 提供商/配置名称,与智能体里 llm_provider_name 对应 |
provider_icon |
text |
可空 | 图标 URL 等 |
api_base_url |
varchar(1024) |
NOT NULL | OpenAI 兼容 API 根地址 |
api_key |
varchar(1024) |
NOT NULL | 调用密钥(敏感) |
api_key_url |
varchar(1024) |
可空 | 申请密钥的页面等 |
model_names |
json |
NOT NULL | 该配置下模型 id 列表(JSON 数组) |
4.2.3 agents #
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
int |
PK,自增 | 主键 |
avatar |
text |
可空 | 头像 URL 路径等 |
name |
varchar(255) |
NOT NULL,有索引 | 智能体名称 |
description |
text |
可空 | 描述 |
opening_message |
text |
可空 | 开场白文案 |
system_prompt |
text |
NOT NULL | 系统提示词 |
llm_provider_name |
varchar(255) |
NOT NULL | 对应 llm_models.provider_name(逻辑关联,非 FK) |
llm_model_name |
varchar(255) |
NOT NULL | 模型名,须在对应配置的 model_names 中可用 |
mcp_service_ids |
json |
NOT NULL | 绑定的 mcp_services.id 列表(JSON 数组) |
ask_prompt_template |
text |
可空 | 收集完变量后拼装用户侧提示的模板(可含 {{变量}}) |
ask_variables |
json |
NOT NULL | 变量定义列表(key、question、required 等),驱动多轮提问 |
4.2.4 agent_chat_sessions #
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
int |
PK,自增 | 主键 |
agent_id |
int |
NOT NULL,有索引 | 所属智能体 agents.id(逻辑 FK) |
title |
varchar(255) |
NOT NULL | 会话标题,默认如「新对话」 |
created_at |
datetime |
NOT NULL,默认 now() |
创建时间 |
updated_at |
datetime |
NOT NULL,默认 now() |
更新时间(ORM 侧可能 onupdate,以库内实际为准) |
4.2.5 agent_chat_messages #
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
int |
PK,自增 | 主键 |
session_id |
int |
NOT NULL,有索引 | 所属会话 agent_chat_sessions.id(逻辑 FK) |
role |
varchar(32) |
NOT NULL | 角色:user / assistant 等 |
content |
text |
NOT NULL | 消息正文(含模型输出、用户追问等) |
meta |
json |
NOT NULL | 扩展信息,如 kind:opening_message、ask_variable、ask_variable_answer 等 |
created_at |
datetime |
NOT NULL,默认 now() |
创建时间 |
4.2.6 逻辑关系 #
| 从 | 到 | 关联方式 |
|---|---|---|
agent_chat_sessions.agent_id |
agents.id |
整数引用 |
agent_chat_messages.session_id |
agent_chat_sessions.id |
整数引用 |
agents.llm_provider_name |
llm_models.provider_name |
字符串匹配 |
agents.mcp_service_ids[] |
mcp_services.id |
JSON 数组中的 id |
4.3. ER 图 #
agent_chat_sessions.agent_id→agents.idagent_chat_messages.session_id→agent_chat_sessions.idagents.llm_provider_name↔llm_models.provider_name(逻辑)agents.mcp_service_ids内含mcp_services.id(逻辑 N:M)
4.4. 高层关系 #
5. 建表 #
本节将详细说明如何通过 SQLAlchemy 数据模型管理上述数据表结构。
SQLAlchemy 数据模型定义
在 app/models.py 文件中,你需要为每个表定义一个对应的 SQLAlchemy ORM 类。例如:
from sqlalchemy import Column, Integer, String, Text, DateTime, JSON, ForeignKey
from sqlalchemy.orm import declarative_base, relationship
import datetime
Base = declarative_base()
class Agent(Base):
__tablename__ = "agents"
class AgentChatSession(Base):
__tablename__ = "agent_chat_sessions"
class AgentChatMessage(Base):
__tablename__ = "agent_chat_messages"自动建表
- 自动建表:如
main.py片段所示,Base.metadata.create_all(bind=engine)可在应用启动时自动创建表结构。
关联关系说明
agent_chat_sessions.agent_id通过外键指向agents.id,实现一个 Agent 关联多个 Session 的 1:N 关系。agent_chat_messages.session_id通过外键指向agent_chat_sessions.id,实现一个 Session 关联多条 Message 的 1:N 关系。agents.llm_provider_name与llm_models.provider_name通过名称字段逻辑绑定,无物理外键。agents.mcp_service_ids以 JSON 数组存储多个mcp_services.id,实现灵活关联(N:M,需应用层逻辑管理)。
5.1. main.py #
app/main.py
# 导入FastAPI库
from fastapi import FastAPI
# 导入logging库以便后续日志记录
+import logging
# 导入asynccontextmanager用于异步上下文管理器
+from contextlib import asynccontextmanager
# 从app.database模块导入Base和engine,用于数据库相关操作
+from app.database import Base, engine
# 导入app.models模块下的所有内容(类、函数等)
+from app.models import *
# 配置日志的基本设置,日志级别为INFO
+logging.basicConfig(level=logging.INFO)
# 定义一个异步上下文管理器,用于FastAPI生命周期
+@asynccontextmanager
+async def lifespan(app: FastAPI):
# 创建所有数据库表结构(如果未存在则自动创建)
+ Base.metadata.create_all(bind=engine)
# 通过yield挂起,等待应用关闭时进行清理
+ yield
# 创建FastAPI应用实例,设置API标题和版本号,并指定生命周期管理器
+app = FastAPI(title="智能体服务", version="0.1.0", lifespan=lifespan)
# 定义一个GET类型的/health路由用于健康检查
@app.get("/health")
def health():
# 返回服务状态为ok的JSON响应
return {"status": "ok"}5.2. main.py #
main.py
# 导入uvicorn库,用于运行ASGI服务器
import uvicorn
# 判断是否为主程序运行入口
if __name__ == "__main__":
# 启动uvicorn服务器,加载app.main模块下的app实例
# host设置为0.0.0.0以便外部主机访问
# port设置为8000
# reload设置为True用于开发时自动重启
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=8000,
reload=True,
)
6. 跨域 #
在FastAPI中实现跨域(CORS)支持,最常用的方法是引入CORSMiddleware中间件。这样可以确保你的API能够被浏览器中的前端应用安全地访问,尤其是在本地和线上环境存在不同域名或端口时。
- CORS(跨域资源共享):默认情况下,浏览器出于安全考虑会阻止网页访问不同源(协议、域名或端口不同)下的API。CORS是一种机制,允许服务端声明可被哪些源访问,从而实现安全的跨域请求。
- CORSMiddleware:FastAPI中集成的中间件,配置后自动为API响应添加适当的CORS头部信息。
- 导入中间件和配置
from fastapi.middleware.cors import CORSMiddlewarefrom app.config import settings
- 解析 CORS 允许的来源
- 通常会将允许的域名列表写在环境变量(示例:
settings.cors_origins),用英文逗号分隔。 - 通过列表推导式进行分割和清理空格、去除空字符串,得到最终的
origins列表。
- 通常会将允许的域名列表写在环境变量(示例:
- 注册中间件
- 使用
app.add_middleware(...)方法,把CORSMiddleware加到FastAPI应用上。 - 通常建议设置:
allow_origins: 可访问的源组成的列表(如开发阶段通常允许所有源,生产环境请精确配置)。allow_credentials: 是否允许cookie、认证等凭证。allow_methods与allow_headers均设为["*"],表示不限制方法和头部字段。
- 使用
示例场景
- 前端(如本地 http://localhost:3000)开发时访问本后端API
- 生产环境下只允许公司域名访问API
此配置提升了服务的灵活性和安全性。
常见问题
- 配置了 CORS 但仍报跨域错误?请检查:
- 前端请求地址(端口、协议等是否与允许列表对应)
allow_origins是否包含了请求源- nginx、网关等外层代理是否覆盖或删改了CORS相关头信息
推荐做法
- 开发环境:
cors_origins可设为*或http://localhost:3000等前端地址。 - 生产环境:
cors_origins应精确枚举允许的正式域名,防止被恶意第三方利用。
6.1. main.py #
app/main.py
# 导入FastAPI库
from fastapi import FastAPI
# 导入logging库以便后续日志记录
import logging
# 导入asynccontextmanager用于异步上下文管理器
from contextlib import asynccontextmanager
# 导入FastAPI的CORS中间件,用于跨域资源共享
+from fastapi.middleware.cors import CORSMiddleware
# 导入项目配置settings对象
+from app.config import settings
# 从app.database模块导入Base和engine,用于数据库相关操作
from app.database import Base, engine
# 导入app.models模块下的所有内容(类、函数等)
from app.models import *
# 配置日志的基本设置,日志级别为INFO
logging.basicConfig(level=logging.INFO)
# 定义一个异步上下文管理器,用于FastAPI生命周期
@asynccontextmanager
async def lifespan(app: FastAPI):
# 创建所有数据库表结构(如果未存在则自动创建)
Base.metadata.create_all(bind=engine)
# 通过yield挂起,等待应用关闭时进行清理
yield
# 创建FastAPI应用实例,设置API标题和版本号,并指定生命周期管理器
app = FastAPI(title="智能体服务", version="0.1.0", lifespan=lifespan)
# 解析配置中的CORS来源列表,去除空白项和空字符串
+origins = [o.strip() for o in settings.cors_origins.split(",") if o.strip()]
# 向FastAPI应用添加CORS中间件
+app.add_middleware(
+ CORSMiddleware,
# 允许访问的来源列表,如果为空则允许所有来源("*")
+ allow_origins=origins or ["*"],
# 允许携带cookie等凭证
+ allow_credentials=True,
# 允许所有HTTP方法
+ allow_methods=["*"],
# 允许所有HTTP头
+ allow_headers=["*"],
+)
# 定义一个GET类型的/health路由用于健康检查
@app.get("/health")
def health():
# 返回服务状态为ok的JSON响应
return {"status": "ok"}7. 添加MCP服务 #
本节我们将为项目添加 MCP 服务 (即多通道处理服务,Multi-Channel Processing Service)。这一部分内容主要包括:MCP 服务的数据模型定义、接口(API)定义、数据库操作方法(Repository)实现、以及路由(Router)注册流程。
主要内容如下:
数据模型(Model)和序列化(Schema)
- 在
app/models.py中增加McpService模型,描述 MCP 服务的数据结构及其字段(如名称、描述、协议类型、配置信息等)。 - 在
app/schemas.py中定义与模型对应的输入和输出序列化类,以用于数据校验和文档自动生成。
- 在
数据库操作(Repository)
- 在
app/repositories/mcp_repository.py中实现 MCP 服务的增删查改方法。比如create_mcp_service函数,用于插入新的 MCP 服务记录,该函数会接收数据库会话对象和待插入的数据对象,处理后将数据持久化到数据库。
- 在
接口路由(Router)
- 路由文件(比如
app/routers/mcp.py)定义 MCP 服务的相关 API 接口,如创建服务、查询服务列表、获取详情、删除服务等。这些接口会调用 repository 层实现实际的数据操作。 - 路由注册通常会在 FastAPI 主实例中(如
app/main.py)统一挂载。
- 路由文件(比如
依赖注入与请求/响应模型
- 结合 FastAPI 的依赖注入特性,通过参数注入
Session数据库会话、以及参数校验自动对接相关pydantic数据模型。
- 结合 FastAPI 的依赖注入特性,通过参数注入
具体开发流程
- 先设计并创建数据表和模型;
- 再实现操作数据库的 repository 层方法;
- 编写与之匹配的 schema;
- 最后写 API 路由和接口逻辑,并将路由注册到应用主程序。
本节内容有助于你了解如何使用 FastAPI 构建结构清晰、解耦良好的 RESTful 服务,便于后续的功能扩展和维护。你可以根据需求灵活调整服务字段及接口设计。
7.1. init.py #
app/repositories/init.py
from . import mcp_repository
__all__ = ["mcp_repository"]
7.2. mcp_repository.py #
app/repositories/mcp_repository.py
# 导入SQLAlchemy的select(尽管本文件未使用,可以视情况保留或去除)
from sqlalchemy import select
# 导入SQLAlchemy的Session对象
from sqlalchemy.orm import Session
# 导入项目中的models模块
from app import models
# 导入项目中的schemas模块
from app import schemas
# 定义创建McpService的函数,接收数据库会话db和待创建数据data,返回新建的McpService对象
def create_mcp_service(db: Session, data: schemas.McpServiceCreate) -> models.McpService:
# 构造McpService模型对象,strip去除名字首尾空白
row = models.McpService(
name=data.name.strip(),#去除名字首尾空白
description=data.description,#服务描述
protocol=data.protocol.value,#协议类型
config=data.config,#配置信息
)
# 添加新对象到会话
db.add(row)
# 提交事务,将更改保存到数据库
db.commit()
# 刷新实例,确保row包含数据库自动生成的字段值
db.refresh(row)
# 返回新建的McpService对象
return row
7.3. init.py #
app/routers/init.py
# routers
7.4. mcp_services.py #
app/routers/mcp_services.py
# 引入日志模块
import logging
# 从FastAPI导入路由器、依赖项和HTTP异常类
from fastapi import APIRouter, Depends, HTTPException
# 从SQLAlchemy导入唯一性错误异常
from sqlalchemy.exc import IntegrityError
# 从SQLAlchemy导入ORM会话对象
from sqlalchemy.orm import Session
# 导入应用程序的数据模型
from app import schemas
# 导入mcp_repository模块,包含数据库操作方法
from app.repositories import mcp_repository
# 导入用于获取数据库会话的依赖函数
from app.database import get_db
# 创建API路由器,设置前缀和标签
router = APIRouter(prefix="/api/mcp-services", tags=["mcp-services"])
# 获取当前模块的日志记录器
logger = logging.getLogger(__name__)
# 定义创建服务的POST接口,响应模型为McpServiceOut
@router.post("", response_model=schemas.McpServiceOut)
def create_service(payload: schemas.McpServiceCreate, db: Session = Depends(get_db)):
# 使用try-except来捕捉数据库插入冲突
try:
# 调用仓库方法创建服务
return mcp_repository.create_mcp_service(db, payload)
# 捕获唯一性约束异常(如名称重复)
except IntegrityError:
# 回滚数据库会话,撤消操作
db.rollback()
# 抛出409冲突异常,返回错误信息
raise HTTPException(status_code=409, detail="名称已存在")
7.5. schemas.py #
app/schemas.py
# 导入枚举类型
from enum import Enum
# 导入Any类型用于类型注解
from typing import Any
# 从pydantic导入BaseModel、Field和field_validator用于数据验证
from pydantic import BaseModel, Field, field_validator
# 定义MCP协议类型的枚举类
class McpProtocol(str, Enum):
# MCP 协议类型注释
"""MCP 协议类型"""
# stdio协议
stdio = "stdio"
# streamable-http协议
streamable_http = "streamable-http"
# sse协议
sse = "sse"
# 定义MCP服务基础模型
class McpServiceBase(BaseModel):
# MCP 服务基础模型注释
"""MCP 服务基础模型"""
# 服务名称,字符串类型,长度1-255
name: str = Field(..., min_length=1, max_length=255)
# 服务描述,可选字段,字符串或None
description: str | None = None
# 协议类型,使用McpProtocol枚举
protocol: McpProtocol
# 配置信息,要求为字典类型
config: dict[str, Any]
# 对config字段添加验证器,确保其为字典类型
@field_validator("config", mode="before")
@classmethod
def config_not_empty(cls, v: Any) -> Any:
# 验证 config 是否为 JSON 对象,如果不是字典则抛出异常
"""验证 config 是否为 JSON 对象"""
if not isinstance(v, dict):
raise ValueError("config 必须为 JSON 对象")
return v
# 定义MCP服务创建模型,继承自McpServiceBase
class McpServiceCreate(McpServiceBase):
# 不增加额外内容,直接继承
pass
# 定义MCP服务输出模型
class McpServiceOut(BaseModel):
# ID字段,整型
id: int
# 服务名称
name: str
# 服务描述,可选字段
description: str | None
# 协议类型,字符串
protocol: str
# 配置信息,字典类型
config: dict[str, Any]
# 设置模型配置,允许从ORM对象属性读取数据
model_config = {"from_attributes": True}7.6. main.py #
app/main.py
# 导入FastAPI库
from fastapi import FastAPI
# 导入logging库以便后续日志记录
import logging
# 导入asynccontextmanager用于异步上下文管理器
from contextlib import asynccontextmanager
# 导入FastAPI的CORS中间件,用于跨域资源共享
from fastapi.middleware.cors import CORSMiddleware
# 导入项目配置settings对象
from app.config import settings
# 从app.database模块导入Base和engine,用于数据库相关操作
from app.database import Base, engine
# 导入app.models模块下的所有内容(类、函数等)
from app.models import *
# 导入MCP服务路由
+from app.routers import mcp_services
# 配置日志的基本设置,日志级别为INFO
logging.basicConfig(level=logging.INFO)
# 定义一个异步上下文管理器,用于FastAPI生命周期
@asynccontextmanager
async def lifespan(app: FastAPI):
# 创建所有数据库表结构(如果未存在则自动创建)
Base.metadata.create_all(bind=engine)
# 通过yield挂起,等待应用关闭时进行清理
yield
# 创建FastAPI应用实例,设置API标题和版本号,并指定生命周期管理器
app = FastAPI(title="智能体服务", version="0.1.0", lifespan=lifespan)
# 解析配置中的CORS来源列表,去除空白项和空字符串
origins = [o.strip() for o in settings.cors_origins.split(",") if o.strip()]
# 向FastAPI应用添加CORS中间件
app.add_middleware(
CORSMiddleware,
# 允许访问的来源列表,如果为空则允许所有来源("*")
allow_origins=origins or ["*"],
# 允许携带cookie等凭证
allow_credentials=True,
# 允许所有HTTP方法
allow_methods=["*"],
# 允许所有HTTP头
allow_headers=["*"],
)
# 包含MCP服务路由
+app.include_router(mcp_services.router)
# 定义一个GET类型的/health路由用于健康检查
@app.get("/health")
def health():
# 返回服务状态为ok的JSON响应
return {"status": "ok"}