📖 Day 14 学习页:测试、优化与部署
Day 14:测试、优化与部署
🎯 今日目标:给项目装上"安全网"(pytest 测试套件,不花一分钱 API 费), 再把整个服务装进 Docker 容器——
docker run一条命令在任何机器上跑起来。
🛠 项目脚手架:project_day11_15/(开发都在这个目录里) | 📋 验收清单
⏱ 今日安排(约 2 小时)
| 时间 | 内容 | 方式 |
|---|---|---|
| 25min | 理论学习:Agent 项目的测试策略 + Docker 基础 | 阅读 |
| 60min | 测试:跑通 tests/ 三个测试文件,全部变绿 | 编码 |
| 35min | 部署:docker build + docker run | 操作 |
📖 第一步:Agent 项目怎么测(测试金字塔)
| 层级 | 测什么 | 要不要 Key/网络 |
|---|---|---|
| 单元测试 | memory 裁剪/隔离、tools 注册/安全路径——纯逻辑 | 不要(tests/test_memory.py、test_tools.py) |
| API 集成测试 | 路由 → Agent → 响应 全链路(monkeypatch 掉 LLM,返回固定回答) | 不要(tests/test_api.py) |
| 真实冒烟测试 | 打一次真实 API 验证配置 | 要(手动跑一次即可,不进 CI) |
核心技巧:monkeypatch 掉 LLM 调用——把 llm.chat 替换成返回固定响应的假函数,
Agent 循环、记忆、路由全部真实跑,只有 LLM 是假的。测试快、稳、免费,
而且能模拟各种 LLM 行为(正常回答 / 要求调工具 / 抛异常)。
Docker 三个关键点(Dockerfile 已给好,要能逐行讲清)
FROM python:3.12-slim # 1. 瘦基础镜像
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app # 2. 依赖先装、代码后拷——层缓存:
# 改代码不触发重装依赖,重建快一个量级
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
# 3. --host 0.0.0.0:容器内必须监听所有网卡,
# 否则端口映射后外面访问不到(新手第一坑)
环境变量在部署时的正确姿势:docker run -e DEEPSEEK_API_KEY=sk-xxx ...——
Key 走环境变量进容器,永远不写进镜像(.dockerignore 已排除 .env)。
🛠 第二步:动手实操
- 跑测试:
pip install pytest httpx→pytest tests/ -v。Day 12 没完成的部分现在是红的——把 TODO 补完让它们变绿, 这就是测试驱动。三个文件:test_memory(记忆/隔离)、test_tools(注册/安全路径, 含../../etc/passwd必须被拒的用例)、test_api(monkeypatch LLM 的全链路)。 - 加一条你自己的测试(TODO 7.3):往 tests/ 里补一个你认为最可能出 bug 的场景 (比如:用户消息超长时记忆被正确裁剪)。
- 构建镜像:
docker build -t dev-agent .→docker run --rm -e DEEPSEEK_API_KEY=你的key -p 8000:8000 dev-agent→ 浏览器开http://127.0.0.1:8000/static/index.html聊天。 没有 Docker 环境就先跳过,读完 Dockerfile 能讲清每一行也算达标。 - 优化盘点(对照检查):max_tokens 已设?历史已裁剪?相同请求有缓存思路? token 统计有入口?——都在前几天的代码里,确认没被删掉。
✅ 第三步:验收(Day 14 部分)
pytest tests/ -v全绿(无 Key 环境也能全绿)- docker run 后
/health通、聊天可用(或能逐行讲清 Dockerfile) .env和workspace/不在镜像里(docker run ... ls /app自查)- 对照 交付清单 Day 14 部分打勾
⚠️ 避坑
- ❌ 集成测试直接打真实 API → 慢、贵、结果不稳定;monkeypatch 是标准做法
- ❌ Dockerfile 里 COPY . . 且没有 .dockerignore → .env 里的 Key 进镜像 = 泄露密钥
- ❌ uvicorn 忘了 --host 0.0.0.0 → 容器外永远访问不通
- ❌ 异步测试直接 def → 拿不到事件循环;用 pytest-asyncio 或像 test_api 一样用 TestClient 同步壳
- ❌ 一次都不跑测试就继续开发 → 回归只能靠手点;
pytest要变成肌肉记忆