0Pricing
AI Agents · 课时

代理测试为何不同

非确定性、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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 代理测试为何不同
  2. 在测试中模拟 LLM 调用
  3. 基于断言的代理测试
  4. 代理流程的集成测试
← 返回 AI Agents