当前位置:首页 > 技术分享 > 正文内容

AI模型部署与API服务实战——从本地模型到线上服务

admin3周前 (06-30)技术分享74

一、引言:模型训练完了,然后呢?

很多AI学习者在本地训练出模型后,都会面临同一个问题:"模型在Jupyter Notebook里跑得很好,但怎么让别人也用上?"

答案是:将模型封装成API服务

text
┌─────────────┐      HTTP请求      ┌─────────────────┐      ┌──────────────┐
│   客户端     │ ──────────────────→ │   API服务       │ ──→  │   AI模型     │
│ (网页/APP)   │ ←────────────────── │ (FastAPI/Flask) │ ←──  │   (推理)     │
└─────────────┘      JSON响应       └─────────────────┘      └──────────────┘

本文目标

  1. 用FastAPI搭建模型推理API

  2. 添加请求验证、异常处理、日志记录

  3. 用Docker容器化部署

  4. 部署到云平台(HuggingFace Spaces / 阿里云)

  5. 实现模型版本管理与灰度发布

二、技术选型:为什么选择FastAPI?

框架优点缺点适用场景
Flask轻量、灵活、生态丰富异步支持弱、性能一般简单原型、快速验证
FastAPI高性能(基于Starlette)、自动生成API文档、异步支持、类型提示学习曲线略陡生产级API(推荐)
Django功能全面、自带ORM和Admin太重、不适合纯API服务大型Web应用

本文选择FastAPI,因为它性能高(与Node.js/Go相当),且自动生成Swagger文档,方便前端或测试人员调用。

三、项目准备:训练一个简单的模型

为了专注于部署流程,我们使用第1篇中训练的房价预测模型。如果你已经有自己的模型,可以跳过此步。

3.1 训练并保存模型

python
# train_and_save_model.pyimport numpy as npimport pandas as pdfrom sklearn.datasets import fetch_california_housingfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerfrom sklearn.linear_model import LinearRegressionimport joblibimport os# 1. 加载数据housing = fetch_california_housing()X = pd.DataFrame(housing.data, columns=housing.feature_names)y = pd.Series(housing.target, name='MedHouseVal')# 2. 划分数据集X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42)# 3. 标准化scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)X_test_scaled = scaler.transform(X_test)# 4. 训练模型model = LinearRegression()model.fit(X_train_scaled, y_train)# 5. 评估from sklearn.metrics import r2_score
y_pred = model.predict(X_test_scaled)print(f"R² Score: {r2_score(y_test, y_pred):.4f}")# 6. 创建模型目录并保存os.makedirs('model_artifacts', exist_ok=True)joblib.dump(model, 'model_artifacts/housing_model.pkl')joblib.dump(scaler, 'model_artifacts/scaler.pkl')joblib.dump(housing.feature_names, 'model_artifacts/feature_names.pkl')print("✅ 模型和预处理工具已保存到 model_artifacts/ 目录")

3.2 验证保存的模型可以正常加载

python
# test_load_model.pyimport joblibimport numpy as np

model = joblib.load('model_artifacts/housing_model.pkl')scaler = joblib.load('model_artifacts/scaler.pkl')feature_names = joblib.load('model_artifacts/feature_names.pkl')# 构造一个测试样本sample = np.array([[8.3252, 41.0, 6.984127, 1.023810, 322.0, 2.555556, 37.88, -122.23]])sample_scaled = scaler.transform(sample)prediction = model.predict(sample_scaled)print(f"特征: {feature_names}")print(f"测试样本: {sample[0]}")print(f"预测房价: ${prediction[0] * 100000:.2f}")

四、用FastAPI构建模型API服务

4.1 项目结构

text
housing_api/
├── app/
│   ├── __init__.py
│   ├── main.py              # FastAPI应用入口
│   ├── models.py            # Pydantic数据模型(请求/响应)
│   ├── services.py          # 模型加载和推理逻辑
│   └── config.py            # 配置文件
├── model_artifacts/         # 训练好的模型文件
│   ├── housing_model.pkl
│   ├── scaler.pkl
│   └── feature_names.pkl
├── requirements.txt         # Python依赖
├── Dockerfile               # Docker镜像构建文件
├── docker-compose.yml       # Docker编排(可选)
└── tests/
    └── test_api.py          # API测试脚本

4.2 配置文件(config.py)

python
# app/config.pyimport osfrom pathlib import Path# 项目根目录BASE_DIR = Path(__file__).resolve().parent.parent# 模型路径MODEL_PATH = BASE_DIR / "model_artifacts" / "housing_model.pkl"SCALER_PATH = BASE_DIR / "model_artifacts" / "scaler.pkl"FEATURES_PATH = BASE_DIR / "model_artifacts" / "feature_names.pkl"# API配置API_TITLE = "房价预测API"API_VERSION = "1.0.0"API_DESCRIPTION = "基于加州房价数据的AI预测服务"# 服务配置HOST = os.getenv("HOST", "0.0.0.0")PORT = int(os.getenv("PORT", 8000))DEBUG = os.getenv("DEBUG", "False").lower() == "true"# 请求限制MAX_REQUEST_SIZE = 1024 * 1024  # 1MBREQUEST_TIMEOUT = 30  # 秒

4.3 数据模型(models.py)

python
# app/models.pyfrom pydantic import BaseModel, Fieldfrom typing import Optional, Listclass HousingFeatures(BaseModel):
    """输入特征模型 - 使用Pydantic进行自动验证"""
    MedInc: float = Field(..., ge=0, le=20, description="社区收入中位数(万美元)")
    HouseAge: float = Field(..., ge=0, le=100, description="房屋年龄(年)")
    AveRooms: float = Field(..., ge=0, le=20, description="平均房间数")
    AveBedrms: float = Field(..., ge=0, le=10, description="平均卧室数")
    Population: float = Field(..., ge=0, le=100000, description="社区人口")
    AveOccup: float = Field(..., ge=0, le=100, description="平均占用人数")
    Latitude: float = Field(..., ge=20, le=50, description="纬度")
    Longitude: float = Field(..., ge=-130, le=-110, description="经度")
    
    class Config:
        schema_extra = {
            "example": {
                "MedInc": 8.3252,
                "HouseAge": 41.0,
                "AveRooms": 6.984127,
                "AveBedrms": 1.023810,
                "Population": 322.0,
                "AveOccup": 2.555556,
                "Latitude": 37.88,
                "Longitude": -122.23
            }
        }class PredictionResponse(BaseModel):
    """预测响应模型"""
    prediction: float = Field(..., description="预测房价(十万美元)")
    price_usd: float = Field(..., description="预测房价(美元)")
    status: str = Field(default="success", description="状态")
    version: str = Field(default="1.0.0", description="模型版本")class BatchPredictionRequest(BaseModel):
    """批量预测请求"""
    samples: List[HousingFeatures] = Field(..., min_items=1, max_items=100)class BatchPredictionResponse(BaseModel):
    """批量预测响应"""
    predictions: List[float]
    prices_usd: List[float]
    count: int
    status: str

4.4 服务层(services.py)

python
# app/services.pyimport joblibimport numpy as npimport loggingfrom typing import List, Tuplefrom pathlib import Pathfrom app.config import MODEL_PATH, SCALER_PATH, FEATURES_PATH# 配置日志logging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)class ModelService:
    """模型服务单例类 - 启动时加载模型,避免每次请求都加载"""
    
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance    
    def __init__(self):
        if self._initialized:
            return
        self._initialized = True
        self._load_models()
    
    def _load_models(self):
        """加载模型和预处理工具"""
        try:
            logger.info(f"正在加载模型: {MODEL_PATH}")
            self.model = joblib.load(MODEL_PATH)
            
            logger.info(f"正在加载标准化器: {SCALER_PATH}")
            self.scaler = joblib.load(SCALER_PATH)
            
            logger.info(f"正在加载特征名称: {FEATURES_PATH}")
            self.feature_names = joblib.load(FEATURES_PATH)
            
            logger.info("✅ 所有模型组件加载完成")
        except Exception as e:
            logger.error(f"❌ 模型加载失败: {str(e)}")
            raise RuntimeError(f"Failed to load models: {str(e)}")
    
    def predict_single(self, features: dict) -> Tuple[float, float]:
        """
        单样本预测
        
        Args:
            features: 特征字典
            
        Returns:
            (预测值_十万美元, 预测值_美元)
        """        try:
            # 将字典转为有序数组(按特征顺序)
            feature_array = np.array([
                features[f] for f in self.feature_names            ]).reshape(1, -1)
            
            # 标准化
            feature_scaled = self.scaler.transform(feature_array)
            
            # 预测(单位:十万美元)
            prediction = self.model.predict(feature_scaled)[0]
            
            # 转为美元(加州房价单位是十万美元)
            price_usd = prediction * 100000
            
            return round(prediction, 4), round(price_usd, 2)
            
        except KeyError as e:
            logger.error(f"缺失特征: {str(e)}")
            raise ValueError(f"Missing feature: {str(e)}")
        except Exception as e:
            logger.error(f"预测失败: {str(e)}")
            raise RuntimeError(f"Prediction failed: {str(e)}")
    
    def predict_batch(self, features_list: List[dict]) -> List[Tuple[float, float]]:
        """批量预测"""
        results = []
        for features in features_list:
            results.append(self.predict_single(features))
        return results    
    def health_check(self) -> dict:
        """健康检查"""
        return {
            "status": "healthy",
            "model_loaded": hasattr(self, 'model'),
            "features": self.feature_names.tolist() if hasattr(self, 'feature_names') else []
        }# 创建全局服务实例(启动时加载)model_service = ModelService()

4.5 主程序(main.py)

python
# app/main.pyfrom fastapi import FastAPI, HTTPException, statusfrom fastapi.middleware.cors import CORSMiddlewarefrom fastapi.responses import JSONResponseimport loggingimport timefrom typing import Listfrom app.config import API_TITLE, API_VERSION, API_DESCRIPTIONfrom app.models import (
    HousingFeatures, 
    PredictionResponse, 
    BatchPredictionRequest,
    BatchPredictionResponse)from app.services import model_service# 配置日志logging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)# 创建FastAPI应用app = FastAPI(
    title=API_TITLE,
    version=API_VERSION,
    description=API_DESCRIPTION,
    docs_url="/docs",
    redoc_url="/redoc")# 配置CORS(允许前端跨域访问)app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 生产环境应限制为具体域名
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],)# ==================== 中间件 ====================@app.middleware("http")async def log_requests(request, call_next):
    """记录所有请求日志"""
    start_time = time.time()
    
    # 记录请求信息
    logger.info(f"Request: {request.method} {request.url.path}")
    
    try:
        response = await call_next(request)
        
        # 记录响应时间
        process_time = time.time() - start_time
        response.headers["X-Process-Time"] = str(process_time)
        logger.info(f"Response: {response.status_code} - {process_time:.4f}s")
        
        return response    except Exception as e:
        logger.error(f"Request failed: {str(e)}")
        raise# ==================== 健康检查 ====================@app.get("/health", tags=["系统"])async def health_check():
    """
    健康检查端点
    用于负载均衡器和监控系统检测服务是否正常
    """    return model_service.health_check()@app.get("/", tags=["系统"])async def root():
    """根路径"""
    return {
        "message": f"欢迎使用 {API_TITLE}",
        "version": API_VERSION,
        "docs": "/docs",
        "health": "/health"
    }# ==================== 预测端点 ====================@app.post(
    "/predict", 
    response_model=PredictionResponse,
    tags=["预测"],
    status_code=status.HTTP_200_OK)async def predict(features: HousingFeatures):
    """
    单样本房价预测
    
    - **MedInc**: 社区收入中位数(万美元)
    - **HouseAge**: 房屋年龄(年)
    - **AveRooms**: 平均房间数
    - **AveBedrms**: 平均卧室数
    - **Population**: 社区人口
    - **AveOccup**: 平均占用人数
    - **Latitude**: 纬度
    - **Longitude**: 经度
    """    try:
        # 将Pydantic模型转为字典
        features_dict = features.dict()
        
        # 调用服务层预测
        prediction, price_usd = model_service.predict_single(features_dict)
        
        return PredictionResponse(
            prediction=prediction,
            price_usd=price_usd,
            status="success",
            version=API_VERSION        )
        
    except ValueError as e:
        logger.warning(f"参数验证失败: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(e)
        )
    except Exception as e:
        logger.error(f"预测失败: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="预测服务暂时不可用,请稍后重试"
        )@app.post(
    "/predict/batch", 
    response_model=BatchPredictionResponse,
    tags=["预测"])async def predict_batch(request: BatchPredictionRequest):
    """
    批量房价预测(最多100条)
    """    try:
        features_list = [sample.dict() for sample in request.samples]
        results = model_service.predict_batch(features_list)
        
        predictions = [r[0] for r in results]
        prices_usd = [r[1] for r in results]
        
        return BatchPredictionResponse(
            predictions=predictions,
            prices_usd=prices_usd,
            count=len(results),
            status="success"
        )
        
    except Exception as e:
        logger.error(f"批量预测失败: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=str(e)
        )# ==================== 异常处理 ====================@app.exception_handler(HTTPException)async def http_exception_handler(request, exc):
    """统一HTTP异常处理"""
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "status": "error",
            "message": exc.detail,
            "code": exc.status_code        }
    )# ==================== 启动命令 ====================# 直接运行: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

4.6 依赖文件(requirements.txt)

txt
# requirements.txt
fastapi==0.104.1
uvicorn[standard]==0.24.0
pydantic==2.4.2
scikit-learn==1.3.0
numpy==1.24.3
pandas==2.0.3
joblib==1.3.2
python-multipart==0.0.6
httpx==0.25.0
pytest==7.4.2

4.7 启动服务

bash
# 方式1:直接启动cd housing_api
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload# 方式2:使用Python脚本启动# 创建 run.py# python run.py

run.py内容

python
import uvicornfrom app.config import HOST, PORT, DEBUGif __name__ == "__main__":
    uvicorn.run(
        "app.main:app",
        host=HOST,
        port=PORT,
        reload=DEBUG    )

4.8 测试API

启动服务后,访问 http://localhost:8000/docs 可以看到自动生成的Swagger文档。

用curl测试

bash
curl -X POST "http://localhost:8000/predict" \
     -H "Content-Type: application/json" \
     -d '{
           "MedInc": 8.3252,
           "HouseAge": 41.0,
           "AveRooms": 6.984127,
           "AveBedrms": 1.023810,
           "Population": 322.0,
           "AveOccup": 2.555556,
           "Latitude": 37.88,
           "Longitude": -122.23
         }'

预期响应

json
{
  "prediction": 4.526,
  "price_usd": 452600.00,
  "status": "success",
  "version": "1.0.0"}

批量测试

bash
curl -X POST "http://localhost:8000/predict/batch" \
     -H "Content-Type: application/json" \
     -d '{
           "samples": [
             {"MedInc": 8.3252, "HouseAge": 41.0, "AveRooms": 6.984127, "AveBedrms": 1.023810, "Population": 322.0, "AveOccup": 2.555556, "Latitude": 37.88, "Longitude": -122.23},
             {"MedInc": 3.5, "HouseAge": 20.0, "AveRooms": 5.0, "AveBedrms": 1.0, "Population": 1500.0, "AveOccup": 3.0, "Latitude": 34.05, "Longitude": -118.25}
           ]
         }'

五、Docker容器化部署

Docker将应用及其依赖打包成一个独立的容器,保证"在任何机器上运行结果一致"。

5.1 编写Dockerfile

dockerfile
# Dockerfile
FROM python:3.10-slim

# 设置工作目录
WORKDIR /app

# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    DEBIAN_FRONTEND=noninteractive

# 安装系统依赖(scikit-learn需要)
RUN apt-get update && apt-get install -y \
    gcc \
    g++ \
    && rm -rf /var/lib/apt/lists/*

# 复制依赖文件并安装
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制项目文件
COPY . .

# 创建非root用户运行(安全最佳实践)
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser

# 暴露端口
EXPOSE 8000

# 启动命令
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

5.2 构建镜像

bash
docker build -t housing-api:latest .

5.3 运行容器

bash
# 基本运行docker run -d -p 8000:8000 --name housing-api housing-api:latest# 带环境变量运行docker run -d -p 8000:8000 \
    -e DEBUG=False \
    -e PORT=8000 \
    --name housing-api \
    housing-api:latest# 查看日志docker logs -f housing-api# 停止并删除docker stop housing-api && docker rm housing-api

5.4 使用docker-compose(推荐生产环境)

yaml
# docker-compose.ymlversion: '3.8'services:
  api:
    build: .    container_name: housing-api    ports:
      - "8000:8000"
    environment:
      - DEBUG=False      - PORT=8000    restart: unless-stopped    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s      timeout: 10s      retries: 3
    deploy:
      resources:
        limits:
          memory: 2G        reservations:
          memory: 1G

启动:

bash
docker-compose up -ddocker-compose logs -f api

六、云平台部署

6.1 HuggingFace Spaces(最简单,免费)

HuggingFace Spaces提供免费的GPU/CPU托管服务,适合Demo和轻量应用。

步骤

  1. 注册 HuggingFace 账号(huggingface.co

  2. 点击右上角头像 → New Space

  3. 选择 Docker 类型

  4. 创建 Dockerfilerequirements.txt(同上)

  5. 添加 space.md 描述文件

  6. Git Push 到Space仓库

空间配置文件

yaml
# space.md (在Space的README中)---title: 房价预测APIemoji: 🏠colorFrom: bluecolorTo: greensdk: dockerapp_port: 8000---# 加州房价预测API这是一个基于线性回归的房价预测服务。
访问 `/docs` 查看交互式API文档。

6.2 阿里云ECS部署(生产级)

步骤

  1. 购买ECS:选择Ubuntu 22.04 LTS,至少2核4GB

  2. 安装Docker

bash
curl -fsSL https://get.docker.com | bashsudo usermod -aG docker $USER
  1. 安装Docker Compose

bash
sudo apt install docker-compose-plugin -y
  1. 上传代码到服务器

bash
scp -r housing_api/ root@your-server-ip:/app/
  1. 启动服务

bash
cd /app/housing_apidocker compose up -d
  1. 配置Nginx反向代理(可选,建议配置HTTPS):

nginx
# /etc/nginx/sites-available/housing-apiserver {
    listen 80;
    server_name api.yourdomain.com;
    
    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }}

6.3 云服务商AI平台对比

平台优点缺点适合场景
HuggingFace Spaces免费、简单、支持GPU有并发限制、需科学上网演示/Demo
阿里云PAI国内访问快、企业级支持收费、配置复杂企业生产
腾讯云TI国内访问快、ModelArts类似收费企业生产
Replicate一行代码部署、按调用计费价格较贵快速商业验证
Modal按需计费、自动扩缩容不支持国内弹性场景

七、模型版本管理与灰度发布

7.1 多版本模型加载

python
# app/services_v2.py - 支持多版本import joblibfrom pathlib import Pathfrom typing import Optionalclass ModelServiceV2:
    def __init__(self):
        self.models = {}
        self.current_version = "1.0.0"
        self._load_all_models()
    
    def _load_all_models(self):
        model_dir = Path("model_artifacts")
        for version_dir in model_dir.glob("v*"):
            version = version_dir.name.replace("v", "").replace("_", ".")
            self.models[version] = {
                "model": joblib.load(version_dir / "model.pkl"),
                "scaler": joblib.load(version_dir / "scaler.pkl"),
                "metadata": {
                    "r2_score": float(open(version_dir / "r2.txt").read()),
                    "trained_date": open(version_dir / "date.txt").read().strip()
                }
            }
    
    def predict(self, features: dict, version: Optional[str] = None):
        v = version or self.current_version        if v not in self.models:
            raise ValueError(f"版本 {v} 不存在,可用版本: {list(self.models.keys())}")
        # ... 预测逻辑

7.2 通过Header指定模型版本

python
# main.py 添加版本控制from fastapi import Header@app.post("/predict")async def predict(
    features: HousingFeatures,
    x_model_version: Optional[str] = Header(None, alias="X-Model-Version")):
    version = x_model_version or "1.0.0"
    # 使用指定版本预测

7.3 A/B测试配置

python
# config.py 添加AB_TEST_CONFIG = {
    "version_a": "1.0.0",  # 95%流量
    "version_b": "2.0.0",  # 5%流量
    "enabled": True}# 在预测端点中随机分流import randomdef get_ab_version():
    if not AB_TEST_CONFIG["enabled"]:
        return AB_TEST_CONFIG["version_a"]
    return AB_TEST_CONFIG["version_b"] if random.random() < 0.05 else AB_TEST_CONFIG["version_a"]

八、监控与日志

8.1 集成Prometheus监控

python
# 安装 prometheus-fastapi-instrumentator# pip install prometheus-fastapi-instrumentatorfrom prometheus_fastapi_instrumentator import Instrumentator# 在main.py中添加instrumentator = Instrumentator(
    should_group_status_codes=True,
    should_ignore_untemplated=True,)instrumentator.instrument(app).expose(app, endpoint="/metrics")

8.2 结构化日志

python
import structlog

logger = structlog.get_logger()@app.post("/predict")async def predict(features: HousingFeatures):
    logger.info(
        "prediction_request",
        features=features.dict(),
        version=API_VERSION    )
    # ... 预测逻辑
    logger.info(
        "prediction_success",
        prediction=prediction,
        price_usd=price_usd    )

九、API性能压测

python
# tests/test_performance.pyimport asyncioimport aiohttpimport timefrom typing import Listasync def send_request(session, data):
    async with session.post("http://localhost:8000/predict", json=data) as resp:
        return await resp.json()async def benchmark(n_requests: int = 100, concurrent: int = 10):
    async with aiohttp.ClientSession() as session:
        sample_data = {
            "MedInc": 8.3252, "HouseAge": 41.0, "AveRooms": 6.984127,
            "AveBedrms": 1.023810, "Population": 322.0, "AveOccup": 2.555556,
            "Latitude": 37.88, "Longitude": -122.23
        }
        
        start = time.time()
        tasks = [send_request(session, sample_data) for _ in range(n_requests)]
        results = await asyncio.gather(*tasks)
        elapsed = time.time() - start        
        print(f"总请求数: {n_requests}")
        print(f"总耗时: {elapsed:.2f}s")
        print(f"QPS: {n_requests / elapsed:.2f}")
        print(f"平均延迟: {elapsed * 1000 / n_requests:.2f}ms")# 运行# asyncio.run(benchmark(n_requests=1000, concurrent=50))

十、完整项目文件清单

text
housing_api/
├── app/
│   ├── __init__.py
│   ├── main.py              (约200行)
│   ├── models.py            (约60行)
│   ├── services.py          (约100行)
│   └── config.py            (约40行)
├── model_artifacts/
│   ├── housing_model.pkl
│   ├── scaler.pkl
│   └── feature_names.pkl
├── tests/
│   ├── test_api.py
│   └── test_performance.py
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── run.py
└── README.md

十一、避坑指南

常见问题解决方案
模型加载耗时(>5秒)将加载放在启动时(lifespan事件),而非每次请求时
大模型(>5GB)无法加载使用模型量化(4-bit/8-bit),或使用模型分片加载
并发请求导致OOM使用raycelery做任务队列,限制并发数
API响应超时使用异步IO(FastAPI原生支持),添加超时配置
跨域问题(CORS)添加CORSMiddleware,生产环境指定allow_origins白名单
环境变量泄露使用.env文件 + python-dotenv,不要hardcode密钥

十二、思考题

  1. 如果模型文件很大(>10GB),如何优化容器镜像大小和启动时间?

  2. 如何实现API的限流(Rate Limiting),防止恶意请求?

  3. 模型在线上运行一段时间后,数据分布发生变化(概念漂移),如何监控并自动触发重新训练?


相关文章

2026年穿梭车故障率统计:哪个品牌最省心?

“穿梭车一年修三次?那你不是买了设备,是请了个爹。”我在统计30个在运项目的故障记录后发现:不同品牌穿梭车的故障率差距高达5倍。最省心的品牌平均8000小时无故障,最闹心的品牌2000小时就要修一次。...

手把手教你从零搭建一个RAG知识库问答系统

为什么你需要一个RAG系统?先问一个问题:你调用大模型API时,有没有遇到过这些尴尬时刻?问它你公司内部的操作手册,它一本正经地胡说八道让它分析你刚收到的客户邮件,它说“我的知识截止到2024年”想让...

你的AI应用上线就翻车?这份评测与优化指南请收好

一个扎心的真相你可能遇到过这样的场景:本地测试时AI回答得头头是道,一上线客户问了个刁钻问题,直接答非所问投了两个月精力做的AI助手,老板试用三分钟就关掉了页面明明代码没动过,昨天回答准确率90%,今...

AI Agent从原理到实战:手把手教你打造一个能自主干活的智能体

先看一个场景,感受Agent的魅力假设你给AI布置一个任务:"帮我查一下本周AI领域最热门的三个开源项目,然后整理成一份Markdown格式的周报,发送到我的邮箱。"传统RAG能做...

广东型材加工中心制造商:智造升级驱动行业高质量发展

在粤港澳大湾区制造业转型升级的浪潮中,广东型材加工中心制造商正以技术创新为引擎、以精益制造为基石、以服务生态为纽带,悄然重塑中国铝型材、不锈钢型材及复合型材深加工装备领域的竞争格局。作为全国最大的有色...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。