pytest-aitest:单元测试无法测试你的 MCP 服务器。AI 可以。
I’m happy to help translate the article, but I need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, I’ll translate it into Simplified Chinese while preserving the formatting, markdown, and any code blocks or URLs.
我是这么硬着头皮学到的
我构建了两个 MCP 服务器——Excel MCP Server 和 Windows MCP Server。两者都有完善的测试套件,但一旦真实的 LLM 试图使用它们,就立刻崩溃。
我花了数周时间使用 GitHub Copilot 进行手动测试:打开聊天,输入提示,观察 LLM 选错工具,调整描述后再试。
有时设计根本就有问题,我追逐了好几周的“野鹅”,最后才意识到整个思路需要重新考虑。
失败模式总是相同的
| # | 症状 |
|---|---|
| 1 | LLM 在 15 个听起来相似的选项中挑选了错误的工具 |
| 2 | 当参数是 account 时,它传递了 {"account_id": "checking"} |
| 3 | 它完全忽略了系统提示 |
| 4 | 它会询问用户 “Would you like me to do that?” 而不是直接执行 |
为什么? 因为我在测试 代码,而不是 AI 接口。
对于 LLM 来说,你的 API 不仅仅是函数和类型——它是 工具描述、参数模式和系统提示。这才是模型实际读取的内容。
- 没有编译器能捕获错误的工具描述。
- 没有单元测试能验证 LLM 会选择正确的工具。
- 如果你还注入了 Agent Skills——它们真的有帮助,还是会让情况更糟?LLM 真会按你想的那样表现吗?
(不,它们不会。)
介绍 pytest‑aitest
受 Dmytro Mykhaliev 的 agent‑benchmark 启发,pytest‑aitest 是一个 pytest 插件,让你能够在不新增任何 CLI 或语法的情况下编写 AI‑中心 的测试。它可以与现有的 fixtures、markers 以及 CI/CD 流程无缝配合。
工作原理
你的测试 就是一个提示。写下用户可能说的话,让 LLM 推断如何使用你的工具,然后对发生的事情进行断言。
# test_balance_query.py
from pytest_aitest import Agent, Provider, MCPServer
async def test_balance_query(aitest_run):
agent = Agent(
provider=Provider(model="azure/gpt-5-mini"),
mcp_servers=[MCPServer(command=["python", "-m", "my_banking_server"])],
)
result = await aitest_run(agent, "What's my checking balance?")
assert result.success
assert result.tool_was_called("get_balance")
如果此测试失败,问题不在你的代码——而在你的 工具描述。LLM 未能弄清楚该调用哪个工具或传入什么参数。修改描述后重新运行。这就是 AI 接口的 TDD。
async def test_transfer(aitest_run):
result = await aitest_run(agent, "Move $200 from checking to savings")
assert result.tool_was_called("transfer")
描述完善前后对比
# 前 — 过于模糊
@mcp.tool()
def transfer(from_acct: str, to_acct: str, amount: float) -> str:
"""Transfer money."""
# 后 — LLM 完全清楚该怎么做
@mcp.tool()
def transfer(from_account: str, to_account: str, amount: float) -> str:
"""Transfer money between accounts (checking, savings).
Amount must be positive. Returns new balances for both accounts."""
再次运行——测试现在通过。
自动化故障分析
pytest‑aitest 不仅仅给出通过/失败的结果。它会启动 第二个 LLM,分析每一次失败并告诉你 为什么会失败以及如何改进。传统测试需要人工解释失败;这里 AI 为你完成这一步。
- 报告会告诉你应该部署哪个模型,为什么它表现更好,以及需要修复的地方。
- 它会分析成本效率、工具使用模式以及在所有配置下的提示有效性。
- 未使用的工具?已标记。
- 导致请求权限行为的提示?已解释。
查看完整示例报告 → (link placeholder)
比较配置
您可以针对相同的测试套件 测试多个配置:
MODELS = ["gpt-5-mini", "gpt-4.1"]
PROMPTS = {"brief": "Be concise.", "detailed": "Explain your reasoning."}
AGENTS = [
Agent(
name=f"{model}-{prompt_name}",
provider=Provider(model=f"azure/{model}"),
mcp_servers=[banking_server],
system_prompt=prompt,
)
for model in MODELS
for prompt_name, prompt in PROMPTS.items()
]
@pytest.mark.parametrize("agent", AGENTS, ids=lambda a: a.name)
async def test_balance_query(aitest_run, agent):
result = await aitest_run(agent, "What's my checking balance?")
assert result.success
排行榜(通过率 → 成本)
| Agent | 通过率 | Tokens | 成本 |
|---|---|---|---|
| gpt-5-mini‑brief | 100 % | 747 | $0.002 |
| gpt-4.1‑brief | 100 % | 560 | $0.008 |
| gpt-5-mini‑detailed | 100 % | 1,203 | $0.004 |
部署方案: 使用 brief 提示的 gpt-5-mini —— 以最低成本实现 100 % 的通过率。
相同的模式同样适用于:
- A/B 测试服务器版本(您的重构是否破坏了工具可发现性?)
- 比较系统提示
- 测量 Agent 技能的影响
基于会话的测试(对话流程)
真实用户不会只问一个问题;他们进行对话。
@pytest.mark.session("banking-chat")
class TestBankingConversation:
async def test_check_balance(self, aitest_run, agent):
result = await aitest_run(agent, "What's my checking balance?")
assert result.success
async def test_transfer(self, aitest_run, agent):
# Agent remembers we were talking about checking
result = await aitest_run(agent, "Transfer $200 to savings")
assert result.tool_was_called("transfer")
async def test_verify(self, aitest_run, agent):
# Agent remembers the transfer
result = await aitest_run(agent, "What are my new balances?")
assert result.success
测试共享会话历史,报告会展示完整的会话流程以及序列图。
谁受益?
| 受众 | 好处 |
|---|---|
| MCP 服务器作者 | 验证 LLM 实际上能使用你的工具,而不仅仅是代码能运行 |
| 代理构建者 | 找到通过你的测试套件的最便宜模型 + 提示组合 |
| 发布 AI 产品的团队 | 在 CI/CD 中基于面向 LLM 的回归测试来控制部署 |
| 所有人 | 通过 LiteLLM 与 100+ LLM 提供商合作 – Azure、OpenAI、Anthropic、Google、本地模型等。 |
TL;DR
测试是一个提示。LLM 是测试工具。报告告诉你需要修复什么。
传统测试验证你的代码是否工作。pytest‑aitest 验证LLM 能够理解并使用你的代码。这两者不同,但都很重要。
pytest‑aitest
测试你的 AI 接口。 AI 分析你的结果。
一个用于 MCP 服务器、工具、提示和技能的测试驱动开发的 pytest 插件。先编写测试,让 AI 分析驱动你的设计。
为什么?
你的 MCP 服务器通过了所有单元测试。随后有 LLM 试图使用它时:
- 选择了错误的工具,
- 传入了垃圾参数,或
- 忽略了你的系统提示。
因为你测试的是 代码,而不是 AI 接口。
对于 LLM 来说,你的 API 包含:
- 工具描述,
- 架构,和
- 提示
——而不是函数和类型。没有编译器能捕获错误的工具描述。没有 linter 能标记混乱的架构。传统测试无法验证它们。
工作原理
使用自然语言提示编写测试。Agent 将 LLM 与你的工具捆绑在一起——你对发生的事情进行断言:
from pytest_aitest import Agent, Provider, MCPServer
async def test_balance_query(aitest_run):
agent = Agent(
provider=Provider,
# … configure your tools / server here …
)
# … write your natural‑language test here …
快速链接
- 文档 – 完整指南和 API 参考
- PyPI –
uv add pytest-aitest - 示例报告 – 查看 AI 分析的实际效果
- GitHub – 在 GitHub 上给 pytest‑aitest 加星
欢迎贡献!开源且社区驱动。