共计 2486 个字符,预计需要花费 7 分钟才能阅读完成。
为什么智能 Agent 测试更困难?
最近在团队里折腾 AI Agent 的测试方案,发现和传统软件测试相比有三个头疼问题:

- 非确定性输出:同样的输入可能得到不同回复(比如带随机性的 LLM)
- 复杂状态管理:Agent 内部状态机可能随对话变化
- 外部依赖黑洞:API 调用、数据库访问、第三方服务像定时炸弹
分层测试策略
我们的解决方案是分层拆解,就像吃蛋糕要一层层来:
graph TD
A[单元测试] -->| 验证决策逻辑 | B(组件测试)
B -->| 验证服务集成 | C[端到端测试]
C -->| 监控生产环境 | D[Canary 发布]
第一层:单元测试(核心逻辑)
用这个测试基类搞定大部分场景:
from typing import Any, Dict
import pytest
class AgentTestBase:
"""所有测试类的父类,包含通用工具方法"""
@staticmethod
def assert_response_contains(
response: str,
keywords: list[str],
threshold: int = 1
) -> None:
"""检查响应是否包含至少 threshold 个关键词"""
count = sum(1 for kw in keywords if kw in response)
assert count >= threshold, f"预期包含 {threshold} 个关键词,实际匹配 {count} 个"
@pytest.fixture
def mock_openai(self, mocker):
"""自动 mock OpenAI API 调用"""
return mocker.patch('openai.ChatCompletion.create')
第二层:集成测试(服务协作)
重点处理外部依赖,推荐两种模式:
- Mock 模式:快速但可能失真
- Recording 模式:录制真实响应后回放
import json
from pathlib import Path
class WeatherAgentTest(AgentTestBase):
"""测试天气查询 Agent 的集成场景"""
@pytest.mark.asyncio
async def test_weather_query(self, tmp_path: Path):
# 使用 Recording 模式
recording_file = tmp_path / "weather_api.json"
if recording_file.exists():
# 回放模式
with open(recording_file) as f:
mock_data = json.load(f)
self.mock_weather_api.return_value = mock_data
else:
# 录制模式
real_data = await real_api_call()
with open(recording_file, 'w') as f:
json.dump(real_data, f)
agent = WeatherAgent()
response = await agent.query("北京天气")
self.assert_response_contains(response, ["气温", "摄氏度"])
第三层:端到端测试(用户视角)
用这个模板测试完整链路:
@pytest.mark.e2e
class ShoppingAgentE2ETest:
@pytest.mark.timeout(30) # 设置超时防止死循环
async def test_complete_order_flow(self):
"""模拟从商品咨询到下单的全流程"""
agent = ShoppingAgent()
# 第一轮:商品咨询
resp1 = await agent.chat("想买无线耳机")
assert "推荐" in resp1
# 第二轮:加入购物车
resp2 = await agent.chat("把 AirPods Pro 加入购物车")
assert "已添加" in resp2
# 第三轮:支付验证
resp3 = await agent.chat("用支付宝支付")
assert "订单号" in resp3
生产环境实战技巧
测试数据管理
建议目录结构:
tests/
├── unit/
├── integration/
│ ├── __recordings__/ # 存放录制的 API 响应
│ └── test_weather.py
└── e2e/
└── fixtures/ # 端到端测试的初始数据
CI/CD 流水线配置
GitLab CI 示例片段:
stages:
- test
agent_tests:
stage: test
image: python:3.10
script:
- pip install -r requirements-test.txt
- pytest tests/unit/ --cov=src # 单元测试要求 100% 覆盖
- pytest tests/integration/ --record-mode=none # 禁止录制模式
- pytest tests/e2e/ -m "not slow" # 跳过标记为 slow 的测试
踩坑经验分享
-
时间敏感测试:遇到检查「今天天气」这类用例,可以用 freezegun 固定时间:
from freezegun import freeze_time @freeze_time("2023-08-01") def test_date_sensitive(): assert agent.get_date() == "2023 年 8 月 1 日" -
并行化陷阱:使用 pytest-xdist 并行运行时要小心共享状态,建议:
- 每个测试创建独立 Agent 实例
- 用 tmp_path 处理文件操作
扩展思考
试着为你的 Agent 设计测试矩阵,考虑这些维度:
- 输入覆盖:边界值(空输入、超长文本)、异常字符
- 状态路径:测试状态机所有可能转移
- 失败模式:故意断开 API 连接、返回错误响应
- 性能基线:记录关键操作的耗时区间
可以先用这个表格规划:
| 测试类型 | 验证目标 | 工具链 | 执行频率 |
|---|---|---|---|
| 单元测试 | 决策逻辑正确性 | pytest+mock | 每次提交 |
| 集成测试 | 服务交互可靠性 | pytest-vcr | 每日 |
| 端到端测试 | 用户体验完整性 | playwright | 发布前 |
| 压力测试 | 高并发稳定性 | locust | 月度 |
正文完
