Ch15 §03 代码库设计 · 讲解稿¶
章节定位: Ch15 架构层。教"如何设计可维护的代码库": 模块化 / API 设计 / 设计模式 / 文档。 承接: §02 Git → §03 代码组织 走向: §04 测试 / §05 DevOps 篇幅: ~3700 字 / 阅读 11 分钟 / 讲解 22-25 分钟
一 · 一句话总览¶
代码设计 = "如何让代码活得久": 模块化 (包/库) + API (REST/gRPC) + 设计模式 (SOLID) + 文档。AI 工程师的项目 = 模型 + 数据 + 服务, 三者都需好设计。
二 · 章节主线 (5 段)¶
第 1 段 · 模块化与项目结构 (8 分钟)¶
3.1.1 Python 包结构¶
my_ai_project/
├── pyproject.toml # 依赖 + 元数据
├── README.md
├── src/
│ └── mypkg/
│ ├── __init__.py
│ ├── model.py
│ ├── data.py
│ ├── train.py
│ └── api.py
├── tests/
│ ├── test_model.py
│ ├── test_data.py
│ └── conftest.py
├── configs/ # YAML 配置
├── scripts/ # 入口脚本
└── docs/
3.1.2 src layout 优势¶
- 避免 import 混乱
- 强制包路径, 利于测试
- 业界标准
3.1.3 依赖管理¶
| 工具 | 特点 |
|---|---|
| pip + requirements.txt | 简单 |
| poetry | 依赖 + 打包 + lock |
| uv | 快 (10× pip) |
| conda | 适合 ML + GPU |
| pip-tools | 编译 requirements |
3.1.4 配置管理¶
# 用 Pydantic
from pydantic import BaseModel
class TrainConfig(BaseModel):
lr: float = 1e-3
batch_size: int = 32
epochs: int = 10
device: str = "cuda"
优于 hard-code 或 argparse。
第 2 段 · API 设计 (10 分钟)¶
3.2.1 REST API¶
资源导向, HTTP 方法:
| 方法 | 用途 | 例 |
|---|---|---|
| GET | 读 | GET /users/1 |
| POST | 创建 | POST /users |
| PUT | 全量更新 | PUT /users/1 |
| PATCH | 部分更新 | PATCH /users/1 |
| DELETE | 删除 | DELETE /users/1 |
3.2.2 REST 状态码¶
| 码 | 含义 |
|---|---|
| 200 | OK |
| 201 | Created |
| 400 | Bad Request |
| 401 | Unauthorized |
| 404 | Not Found |
| 500 | Server Error |
3.2.3 FastAPI 示例¶
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
name: str
email: str
users = {}
@app.post("/users", status_code=201)
async def create_user(user: User):
users[user.email] = user
return user
@app.get("/users/{email}")
async def get_user(email: str):
if email not in users:
raise HTTPException(404, "User not found")
return users[email]
自动生成 OpenAPI 文档 (/docs)。
3.2.4 GraphQL¶
客户端定字段, 一次查询拿全部。
适用: 多端, 字段多变。
3.2.5 gRPC¶
Protobuf + HTTP/2, 强类型, 流式。
service LLMService {
rpc Generate (GenerateRequest) returns (GenerateResponse);
rpc Stream (StreamRequest) returns (stream StreamResponse);
}
适用: 微服务内部通信。
第 3 段 · SOLID 与设计模式 (10 分钟)¶
3.3.1 SOLID 五原则¶
| 原则 | 含义 | 例子 |
|---|---|---|
| Single Responsibility | 一个类一个职责 | UserRepository 只管用户数据 |
| Open/Closed | 扩展开放, 修改关闭 | 通过继承扩展功能 |
| Liskov Substitution | 子类可替换父类 | Square extends Rectangle |
| Interface Segregation | 接口要小 | 拆分大接口 |
| Dependency Inversion | 依赖抽象, 不依赖具体 | DI 容器 |
3.3.2 常用模式¶
工厂 (Factory):
class ModelFactory:
@staticmethod
def create(name: str):
if name == "gpt":
return GPTModel()
elif name == "llama":
return LlamaModel()
raise ValueError(name)
单例 (Singleton):
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
策略 (Strategy):
class LossStrategy:
def compute(self, pred, target): ...
class MSELoss(LossStrategy):
def compute(self, p, t):
return ((p - t) ** 2).mean()
# 运行时切换
loss_fn = MSELoss() if task == "regression" else CrossEntropyLoss()
装饰器 (Decorator):
import functools
import time
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time()-t0:.4f}s")
return result
return wrapper
@timer
def train():
...
依赖注入 (DI):
class Trainer:
def __init__(self, model, optimizer, scheduler):
self.model = model
self.opt = optimizer
self.sched = scheduler
依赖由外部传入, 便于测试和替换。
3.3.3 反模式¶
- God Object: 一个类做所有事
- Spaghetti Code: 逻辑混乱
- Magic Number: 硬编码
- Premature Optimization: 过早优化
第 4 段 · 文档与可维护性 (8 分钟)¶
3.4.1 README 标准¶
# Project Name
简短描述 (1 句话)
## 安装
pip install mypkg
## 快速开始
\`\`\`python
import mypkg
\`\`\`
## 文档
docs.example.com
## 许可
MIT
3.4.2 Docstring (Google 风格)¶
def tokenize(text: str, max_len: int = 512) -> list[str]:
"""分词函数.
Args:
text: 输入文本
max_len: 最大长度
Returns:
token 列表
Raises:
ValueError: text 为空时
"""
3.4.3 类型提示¶
from typing import Optional, List, Dict
def predict(
model: nn.Module,
inputs: torch.Tensor,
threshold: float = 0.5
) -> List[int]:
...
mypy 静态检查。
3.4.4 ADR (Architecture Decision Records)¶
# ADR-001: 选择 vLLM 作为推理引擎
## 状态
已采纳 (2026-01-15)
## 背景
LLM 推理服务需要高吞吐, 低延迟。
## 决策
采用 vLLM, 因其 PagedAttention 节省显存 4×。
## 后果
- 优点: 吞吐 +24×
- 缺点: 学习曲线
- 风险: 项目活跃度依赖 Berkeley
记录关键决策, 避免重复争论。
第 5 段 · AI 项目特定设计 (8 分钟)¶
3.5.1 模型与代码分离¶
src/mypkg/
├── model.py # 模型定义
├── data.py # 数据加载
├── train.py # 训练循环
├── evaluate.py # 评估
└── inference.py # 推理 (与 train 解耦)
3.5.2 配置外置¶
# configs/train.yaml
model:
name: resnet50
num_classes: 1000
train:
lr: 0.001
batch_size: 64
epochs: 100
data:
root: /data/imagenet
workers: 8
用 hydra / omegaconf 加载。
3.5.3 日志与监控¶
import logging
import wandb
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
wandb.init(project="myproj")
for epoch in range(epochs):
loss = train_one_epoch(...)
logger.info(f"epoch {epoch}: loss={loss:.4f}")
wandb.log({"loss": loss})
3.5.4 错误处理¶
try:
model = load_model(path)
except FileNotFoundError:
logger.error(f"Model not found: {path}")
raise
except RuntimeError as e:
logger.error(f"CUDA error: {e}")
# fallback CPU
不静默吞错, 用结构化日志。
三 · 易错点 (5 条)¶
- 全局变量: 难测试, 用依赖注入。
- 混合关注点: 数据加载与训练混在一起, 难复用。
- 缺文档: 一个月后自己看不懂。
- 硬编码路径: 用配置。
- 过度工程: 小项目别用微服务。
四 · 跨章连接¶
| 章节 | 关联 |
|---|---|
| Ch13 §05 编程语言 | 类型 / 范式 |
| Ch14 §04 测试 | 单元测试 |
| Ch15 §05 DevOps | Docker 化 |
五 · 自测 5 题¶
- SOLID 五原则?
- FastAPI 与 Flask 区别?
- REST GET vs POST?
- 单例模式适用?
- ADR 用途?
六 · 一句话总结¶
代码设计 = 模块化 + API + SOLID + 文档。AI 工程师的代码 = 模型 + 数据 + 服务, 三者都需要好架构。Ch15 §04 进入测试。
七 · 板书建议¶
[板书 1: 包结构]
src layout
pyproject.toml
[板书 2: API]
REST GET/POST
FastAPI 类型提示
[板书 3: SOLID]
单一职责 + 开闭 + ...
[板书 4: 文档]
README + docstring
ADR
八 · 参考资料¶
- Robert C. Martin, "Clean Architecture"
- FastAPI 官方文档
- Google Python Style Guide
- hydra (配置管理)
- Ch13 §05 编程语言 (本套书)