代理测试为何不同
非确定性、LLM 成本,以及标准单元测试为何不够完善。
代理测试为何不同 是 CoddyKit 上的免费 AI Agents 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Agents 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Agents 课程共包含 4 节课。
测试软件与测试智能体
传统软件具有确定性:给定相同的输入,就会得到相同的输出。单元测试依赖这一特性来断言精确的预期值。
人工智能智能体打破了这一假设。同一个提示词每次运行都可能产生不同的输出,因此仅采用标准测试方法是不够的。
非确定性:相同输入,不同输出
LLM 本质上是概率性的。temperature 参数控制随机性——即使设置为 temperature=0,不同模型版本或基础设施变更也可能导致输出发生变化。
这意味着今天通过的智能体测试,明天可能在代码未发生任何变化的情况下失败。
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
# Same prompt, potentially different outputs each run
for i in range(3):
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': 'Name a planet.'}],
temperature=0.9 # High randomness
)
print(f'Run {i+1}: {response.choices[0].message.content}')
# Run 1: Mars
# Run 2: Jupiter
# Run 3: Saturn成本问题:真实 LLM 调用十分昂贵
运行会向 OpenAI 或 Anthropic 发起真实 API 调用的测试套件,每次运行可能花费数美元。持续集成流水线如果运行 100 个测试,每个测试迭代 10 次,每月可能花费数百美元。
因此,无法像运行单元测试那样运行智能体测试——您需要采取策略来控制成本。
# A test that calls the real API costs tokens every run
# 100 tests x 500 tokens each x 10 CI runs/day = 500,000 tokens/day
# At $0.15/1M tokens (gpt-4o-mini): ~$0.075/day = ~$27/year for a tiny suite
# For gpt-4o: 15x more expensive = ~$400/year
# This is why mocking and recording API responses is essential
print('Real API calls in tests = expensive and slow')
print('Solution: Mock or record LLM responses in unit tests')
print('Reserve real calls for scheduled integration tests')延迟问题
真实的 LLM API 调用通常需要 2 到 20 秒。包含 50 个测试的测试套件需要 100 到 1000 秒才能运行。这会严重降低开发效率,而快速反馈是优质测试的核心价值。
模拟 LLM 调用可以让测试在几毫秒内运行完成。
import time
# Simulating what a test suite looks like with real vs mocked calls
num_tests = 50
# Real API calls
real_time = num_tests * 5 # avg 5 seconds per call
print(f'With real API calls: {real_time}s = {real_time/60:.1f} minutes')
# Mocked calls
mock_time = num_tests * 0.001 # <1ms per mock
print(f'With mocked calls: {mock_time:.3f}s = nearly instant')
# Conclusion: mock in unit tests, use real calls in integration tests智能体测试中的外部依赖
智能体经常会调用外部工具:搜索 API、数据库、文件系统和网页抓取器。在测试中,这些依赖可能会:
- 不可用(网络中断、API 停机)
- 每次运行返回不同的数据
- 受到速率限制,从而阻塞持续集成流水线
在单元测试中,必须控制或模拟这些依赖。
# An agent might call multiple external services
# Each is a potential test failure point
def agent_pipeline(query: str) -> str:
search_results = search_web(query) # External: Tavily/Serper API
documents = fetch_documents(search_results) # External: HTTP calls
answer = llm_summarize(documents) # External: OpenAI API
saved = database_store(answer) # External: PostgreSQL
return answer
# In unit tests: mock ALL of these
# In integration tests: use sandboxed versions of real services
print('Each external call is a test reliability risk')标准单元测试的假设
像 pytest 这样的标准单元测试框架假设:
- 测试运行速度快(毫秒级)
- 测试具有确定性
- 测试没有外部副作用
- 测试可以按任意顺序运行
除非明确围绕这些假设进行设计,否则智能体测试会违反其中的全部四项。
# Standard unit test — works perfectly for deterministic code
def add(a, b):
return a + b
def test_add():
assert add(2, 3) == 5 # Always passes — deterministic
# Agent 'unit test' that calls a real LLM — problematic
# def test_agent_answers_question():
# response = agent.run('What is 2+2?')
# assert response == '4' # Might return 'The answer is 4' or 'Four'
print('Exact string matching fails for LLM outputs')
print('Need structural or semantic assertions instead')智能体测试金字塔
实用的智能体测试策略遵循金字塔结构:
- 单元测试(数量多、速度快):使用模拟的 LLM 调用测试单个工具和函数
- 集成测试(数量较少、速度较慢):使用沙箱服务端到端测试智能体流水线
- 评估测试(较少、成本高):使用真实 LLM 调用测试输出质量,并采用类似人工的评分方式
结构断言与语义断言
智能体测试不应匹配完全相同的字符串,而应使用结构断言(智能体是否调用了正确的工具?)或语义检查(输出是否包含相关概念?)。
# Fragile: exact string match
# assert response.content == 'The capital of France is Paris.'
# Better: structural assertion
# assert response.tool_calls[0]['function']['name'] == 'search_web'
# Better: semantic check
def test_capital_in_response(response_text: str) -> bool:
key_words = ['paris', 'france', 'capital']
lower = response_text.lower()
return all(word in lower for word in key_words)
response = 'Paris is the capital city of France.'
print(test_capital_in_response(response)) # True评估工具与 LLM 评审
在质量评估中,业界会使用 LLM 评审:让第二个 LLM 为智能体的输出评分。DeepEval 和 RAGAS 等框架可以自动化这一模式。
这种方式只用于成本高昂的评估运行,不用于日常持续集成。
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
def llm_judge(question: str, answer: str) -> dict:
prompt = f'Question: {question}\nAnswer: {answer}\nRate the answer 1-5 for accuracy. Reply with only a number.'
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}]
)
score = int(response.choices[0].message.content.strip())
return {'score': score, 'pass': score >= 4}
# result = llm_judge('What is the capital of France?', 'Paris')
# print(result) # {'score': 5, 'pass': True}智能体回归测试
更新提示词或更改智能体逻辑时,回归测试可以验证您没有破坏现有行为。请记录黄金样例(输入 → 预期结构),并在每次提交时自动运行这些样例。
# golden_examples.py
GOLDEN_EXAMPLES = [
{
'input': 'Search for the weather in Paris',
'expected_tool': 'get_weather',
'expected_args': {'city': 'Paris'}
},
{
'input': 'Calculate 15% tip on $45',
'expected_tool': 'calculate',
'expected_args': {'expression': '45 * 0.15'}
}
]
def run_regressions(agent, examples: list) -> int:
failures = 0
for ex in examples:
result = agent.plan(ex['input']) # mocked LLM
if result['tool'] != ex['expected_tool']:
print(f'FAIL: expected {ex["expected_tool"]}, got {result["tool"]}')
failures += 1
return failures
# --- demo: a stub agent whose .plan() mimics an LLM's tool choice ---
class _StubAgent:
def plan(self, text):
if 'weather' in text.lower():
return {'tool': 'get_weather'}
if 'tip' in text.lower() or 'calculate' in text.lower():
return {'tool': 'wrong_tool'} # simulate a regression
return {'tool': 'unknown'}
failures = run_regressions(_StubAgent(), GOLDEN_EXAMPLES)
print(f'{failures} of {len(GOLDEN_EXAMPLES)} golden examples failed')
设置基本的智能体测试文件
下面是一个智能体的最小 pytest 测试文件结构。它将快速单元测试(使用模拟)与慢速集成测试(使用真实调用)分开,让您可以只运行所需的测试。
# tests/test_agent.py
import pytest
# Fast unit tests — run on every commit
class TestAgentTools:
def test_tool_returns_dict(self, mock_llm):
result = my_tool(query='test')
assert isinstance(result, dict)
assert 'data' in result
def test_agent_selects_correct_tool(self, mock_llm):
response = agent.run('Search for Python tutorials')
assert response['tool_used'] == 'web_search'
# Slow integration tests — run nightly or on release
@pytest.mark.integration
class TestAgentIntegration:
def test_full_pipeline_with_real_api(self):
# Uses real OpenAI + sandboxed services
result = agent.run('Summarize the Python docs')
assert len(result['answer']) > 50知识检查:智能体测试为何不同
请测试您对测试人工智能智能体所面临的独特挑战的理解。
回顾:为什么测试智能体不同
测试 AI 智能体需要采用不同于标准单元测试的思路:
- 非确定性:相同的输入可能产生不同但都有效的输出
- 成本:真实的 LLM 调用成本高昂——在单元测试中应使用模拟对象
- 延迟:真实接口调用需要数秒,而模拟对象只需几毫秒
- 外部依赖:必须在测试中控制工具和接口
- 断言:使用结构检查和语义检查,而不是精确字符串匹配
请采用测试金字塔:使用模拟对象编写大量低成本单元测试,并使用真实调用编写较少的高成本集成测试。
常见问题解答
「代理测试为何不同」课时是免费的吗?
是的 — 「代理测试为何不同」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Agents 课程的其余内容,请升级到 CoddyKit PRO。 AI Agents 课程共包含 4 节课。
「代理测试为何不同」这节课中我会学到什么?
非确定性、LLM 成本,以及标准单元测试为何不够完善。 你通过在浏览器中直接运行的动手代码来练习 AI Agents,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 AI Agents 需要有经验吗?
无需任何先前经验。CoddyKit 上的 AI Agents 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「代理测试为何不同」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 AI Agents 课中编写并运行代码吗?
能。每节 AI Agents 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 代理测试为何不同
- 在测试中模拟 LLM 调用
- 基于断言的代理测试
- 代理流程的集成测试