FastAPI 快速上手(AI 服务接口)¶
图源:FastAPI 官方文档首页。该图是官方品牌标识,保留它主要用于帮助学习者快速识别框架。
为什么 AI 工程师经常会用到 FastAPI? 因为很多模型推理、数据处理和评测逻辑本来就写在 Python 生态里,而 FastAPI 能把这些逻辑较平滑地暴露成 HTTP API,同时兼顾类型校验、异步 I/O 和自动文档。
一、FastAPI 的定位¶
| 特性 | 说明 |
|---|---|
| 异步 I/O | 原生支持 async / await,适合调用网络、对象存储、消息队列等 I/O 任务 |
| 类型校验 | 基于 Pydantic 校验请求与响应,减少“字段名写错但运行时才炸”的问题 |
| 自动文档 | 自动生成 Swagger UI 和 ReDoc,适合团队联调 |
| Python 生态兼容 | 易于接入 transformers、vLLM、onnxruntime、数据库和数据处理库 |
不要把“FastAPI 快”理解成“任何场景都比别的框架快”。AI 服务的瓶颈常常在模型推理、序列化、网络、GPU 调度,而不是 Web 框架本身。
二、快速起步¶
2.1 安装¶
pip install "fastapi[standard]" uvicorn
# 如果你在用 uv,也可以这样安装
uv pip install "fastapi[standard]" uvicorn
2.2 一个可直接运行的最小示例¶
下面这份 main.py 把几个 AI 服务里最常见的模式放到了一起:
- 健康检查
- Pydantic 请求/响应模型
- 应用启动时预加载模型
- 并发限制
- SSE 流式输出
- 日志中间件
from __future__ import annotations
import asyncio
import json
import logging
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("ai-service")
class PredictRequest(BaseModel):
text: str = Field(..., min_length=1, max_length=10_000)
model: str = Field(default="demo-llm")
temperature: float = Field(default=0.7, ge=0.0, le=2.0)
max_tokens: int = Field(default=256, ge=1, le=4096)
class PredictResponse(BaseModel):
text: str
usage: dict[str, int]
model: str
class ChatMessage(BaseModel):
role: str
content: str
class ChatStreamRequest(BaseModel):
messages: list[ChatMessage]
class MockAIModel:
"""教学用 mock;真实项目里换成 vLLM / transformers / ONNX Runtime 等实现。"""
async def generate(
self,
prompt: str,
*,
temperature: float,
max_tokens: int,
) -> dict[str, object]:
await asyncio.sleep(0.05)
output = f"AI 回复:{prompt[:80]}"
return {
"text": output,
"usage": {
"prompt_tokens": min(len(prompt.split()), max_tokens),
"completion_tokens": min(32, max_tokens),
},
}
async def stream_text(self, prompt: str) -> AsyncIterator[str]:
for token in prompt.split():
await asyncio.sleep(0.03)
yield token
def load_model() -> MockAIModel:
return MockAIModel()
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.model = load_model()
logger.info("Model loaded")
yield
del app.state.model
logger.info("Model released")
app = FastAPI(title="AI Service API", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["https://your-frontend.example.com"],
allow_methods=["GET", "POST"],
allow_headers=["*"],
)
inference_semaphore = asyncio.Semaphore(4)
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = asyncio.get_running_loop().time()
response = await call_next(request)
duration = asyncio.get_running_loop().time() - start
logger.info(
"%s %s -> %s (%.3fs)",
request.method,
request.url.path,
response.status_code,
duration,
)
return response
@app.get("/health")
async def health() -> dict[str, str]:
return {"status": "ok"}
@app.post("/predict", response_model=PredictResponse)
async def predict(req: PredictRequest, request: Request) -> PredictResponse:
model: MockAIModel = request.app.state.model
async with inference_semaphore:
result = await model.generate(
req.text,
temperature=req.temperature,
max_tokens=req.max_tokens,
)
return PredictResponse(
text=result["text"],
usage=result["usage"],
model=req.model,
)
async def sse_events(model: MockAIModel, prompt: str) -> AsyncIterator[str]:
async for token in model.stream_text(prompt):
payload = json.dumps({"token": token}, ensure_ascii=False)
yield f"data: {payload}\n\n"
yield "event: done\ndata: [DONE]\n\n"
@app.post("/chat/stream")
async def chat_stream(req: ChatStreamRequest, request: Request) -> StreamingResponse:
if not req.messages:
raise HTTPException(status_code=400, detail="messages 不能为空")
prompt = "\n".join(item.content for item in req.messages)
model: MockAIModel = request.app.state.model
return StreamingResponse(sse_events(model, prompt), media_type="text/event-stream")
运行:
默认文档地址:
http://127.0.0.1:8000/docshttp://127.0.0.1:8000/redoc
三、AI 模型服务里的常见模式¶
3.1 流式输出(SSE)¶
LLM 常见需求不是“最后一次性返回整段文本”,而是“边生成边推给前端”。SSE 的关键格式只有两点:
- 每条消息都以
data:开头。 - 每条消息都以空行结束,也就是
\n\n。
async def sse_events(model: MockAIModel, prompt: str) -> AsyncIterator[str]:
async for token in model.stream_text(prompt):
yield f"data: {json.dumps({'token': token}, ensure_ascii=False)}\n\n"
yield "event: done\ndata: [DONE]\n\n"
3.2 文件上传(图片 / PDF)¶
这个场景很常见,比如 OCR、文档理解、图片审核。下面示例只演示上传流程,真正的 OCR 逻辑由 run_ocr 替换:
import io
from fastapi import File, UploadFile
from PIL import Image
def run_ocr(image: Image.Image) -> str:
# 这里换成真实 OCR 模型调用
return f"image-size={image.size}"
@app.post("/ocr")
async def ocr(file: UploadFile = File(...)) -> dict[str, str]:
contents = await file.read()
image = Image.open(io.BytesIO(contents))
return {"text": run_ocr(image)}
3.3 预加载与生命周期¶
很多模型初始化非常慢,如果把模型加载写在请求函数内部,每个请求都要重新初始化,延迟会非常糟糕。更稳妥的做法是:
- 启动应用时加载模型。
- 把模型放到
app.state。 - 请求处理时只拿现成对象使用。
3.4 并发控制¶
AI 服务最容易犯的错误之一是“框架支持高并发”就等于“模型也支持高并发”。这两者不是一回事。
如果你的模型推理依赖 GPU、显存或某个外部服务配额,就应该显式做并发上限:
inference_semaphore = asyncio.Semaphore(4)
@app.post("/predict", response_model=PredictResponse)
async def predict(req: PredictRequest, request: Request) -> PredictResponse:
model: MockAIModel = request.app.state.model
async with inference_semaphore:
result = await model.generate(
req.text,
temperature=req.temperature,
max_tokens=req.max_tokens,
)
return PredictResponse(text=result["text"], usage=result["usage"], model=req.model)
3.5 日志与 CORS¶
两点必须记住:
allow_origins=["*"]不能和allow_credentials=True一起用于浏览器场景。- 日志里至少要有方法、路径、状态码和耗时,否则线上排障非常痛苦。
四、部署¶
4.1 Docker 部署¶
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
4.2 生产环境建议¶
注意:
- 对 CPU 型 API,多 worker 往往能提升吞吐。
- 对 GPU 型模型服务,多 worker 可能意味着每个 worker 都各自加载一份模型,显存会迅速爆掉。
- 如果你的主要瓶颈已经是模型推理,而不是 Python Web 层,往往要把重点放在批处理、KV cache、模型服务框架和队列治理上。
五、边缘计算框架:Hono¶
如果你的目标不是“直接在 Python 里跑模型”,而是做边缘网关、鉴权、限流、轻量代理或 API 编排,那么 Hono 这类 TypeScript 框架通常更合适。
| 维度 | FastAPI | Hono |
|---|---|---|
| 主要生态 | Python | TypeScript / JavaScript |
| 常见部署面 | 容器、虚机、Kubernetes | Cloudflare Workers、Vercel Edge、Bun、Node |
| 典型职责 | 模型推理、数据处理、Python 服务 | 边缘代理、鉴权、转发、轻量 API |
import { Hono } from 'hono';
import { cors } from 'hono/cors';
type Bindings = {
OPENAI_API_KEY: string;
};
const app = new Hono<{ Bindings: Bindings }>();
app.use('/*', cors());
app.post('/v1/responses', async (c) => {
const body = await c.req.json();
const response = await fetch('https://api.openai.com/v1/responses', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${c.env.OPENAI_API_KEY}`,
},
body: JSON.stringify(body),
});
return new Response(await response.text(), {
status: response.status,
headers: {
'content-type': response.headers.get('content-type') ?? 'application/json',
},
});
});
export default app;
选择建议:
- 优先 FastAPI:模型逻辑、数据处理、评测服务本来就在 Python 里。
- 优先 Hono:你要做边缘代理、请求归一化、鉴权和低延迟转发。
- 两者同时用:前面 Hono 做网关,后面 FastAPI 做模型服务。
上面的 Hono 示例故意只演示单一上游。真实项目里如果要同时代理多个提供商,应该分别处理各家的鉴权头、请求体和响应格式,而不是把同一条
/v1/responses路径硬套到所有厂商上。
📎 相关: Flask Web 开发 | 后端架构 | MLOps
最后更新:2026 年 3 月
⚠️ 核验说明(2026-03-29):本页已按当前教程库统一复核,示例优先保证可运行与概念正确;涉及具体第三方模型、托管平台和产品特性时,请再对照对应官方文档。
最后更新日期:2026-03-29
