0Pricing
AI Agents · Pelajaran

Pengujian Agen Berbasis Assertion

Periksa panggilan alat, langkah-langkah perantara, dan struktur output akhir.

Pengujian Agen Berbasis Assertion adalah pelajaran AI Agents gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Agents, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Agents mencakup 4 pelajaran total.

Beralih dari Pencocokan Teks Secara Persis

Karena output LLM tidak deterministik, mengujinya dengan assert response == 'exact text' bersifat rapuh. Sebagai gantinya, tulis asersi yang memeriksa struktur dan maksud respons tanpa bergantung pada susunan kata yang persis.

Memastikan Panggilan Alat Dilakukan

Untuk agen pemanggil fungsi, asersi yang paling andal adalah memastikan bahwa agen memilih untuk memanggil alat yang tepat. Pemeriksaan ini bersifat struktural — tidak bergantung pada susunan kata persis dalam proses berpikir LLM.

import json
from unittest.mock import patch, MagicMock

@patch('myagent.client.chat.completions.create')
def test_agent_calls_search_tool(mock_create):
    # Mock: agent decides to call search_web
    tool_call = MagicMock()
    tool_call.function.name = 'search_web'
    tool_call.function.arguments = json.dumps({'query': 'Python tutorials'})
    mock_create.return_value = MagicMock(
        choices=[MagicMock(message=MagicMock(tool_calls=[tool_call]))]
    )

    response = mock_create()  # simulating the agent call
    tc = response.choices[0].message.tool_calls

    assert tc is not None
    assert len(tc) > 0
    assert tc[0].function.name == 'search_web'

# --- demo: give unittest.mock.patch a real dotted path to patch ---
import sys
import types

_myagent = types.ModuleType('myagent')
_myagent.client = types.SimpleNamespace(
    chat=types.SimpleNamespace(completions=types.SimpleNamespace(create=lambda *a, **k: None))
)
sys.modules['myagent'] = _myagent

test_agent_calls_search_tool()
print('test_agent_calls_search_tool: PASS')

Memastikan Nama Alat yang Tepat

Selain sekadar memeriksa bahwa panggilan alat ada, pastikan nama alat tertentu sesuai dengan yang diharapkan. Hal ini dapat menemukan kasus ketika agen memilih alat yang salah untuk kueri tertentu.

import json
from unittest.mock import MagicMock

def extract_tool_calls(response) -> list:
    message = response.choices[0].message
    if not message.tool_calls:
        return []
    return [
        {
            'name': tc.function.name,
            'args': json.loads(tc.function.arguments)
        }
        for tc in message.tool_calls
    ]

# In a test:
# calls = extract_tool_calls(mock_response)
# assert calls[0]['name'] == 'get_weather'
# assert calls[0]['args']['city'] == 'Paris'
print('Tool name and argument assertions are the most reliable agent tests')

Memastikan Argumen Alat

Setelah memastikan nama alat, periksa apakah argumennya benar. Agen tidak hanya harus memilih alat yang tepat, tetapi juga mengisinya dengan parameter yang tepat dari permintaan pengguna.

import json
from unittest.mock import patch, MagicMock

@patch('myagent.client.chat.completions.create')
def test_weather_tool_gets_correct_city(mock_create):
    tool_call = MagicMock()
    tool_call.function.name = 'get_weather'
    tool_call.function.arguments = json.dumps({'city': 'Tokyo', 'unit': 'celsius'})
    mock_create.return_value = MagicMock(
        choices=[MagicMock(message=MagicMock(tool_calls=[tool_call]))]
    )

    response = mock_create()
    args = json.loads(response.choices[0].message.tool_calls[0].function.arguments)

    assert args['city'] == 'Tokyo'
    assert args.get('unit') in ['celsius', 'fahrenheit', None]  # flexible

# --- demo: give unittest.mock.patch a real dotted path to patch ---
import sys
import types

_myagent = types.ModuleType('myagent')
_myagent.client = types.SimpleNamespace(
    chat=types.SimpleNamespace(completions=types.SimpleNamespace(create=lambda *a, **k: None))
)
sys.modules['myagent'] = _myagent

test_weather_tool_gets_correct_city()
print('test_weather_tool_gets_correct_city: PASS')

Validasi Skema JSON untuk Output

Saat agen Anda mengembalikan JSON terstruktur, validasikan output terhadap Skema JSON untuk memastikan semua bidang wajib ada dan memiliki tipe yang benar. Pustaka jsonschema memudahkan hal ini.

# pip install jsonschema
import jsonschema

AGENT_RESPONSE_SCHEMA = {
    'type': 'object',
    'required': ['answer', 'sources', 'confidence'],
    'properties': {
        'answer': {'type': 'string', 'minLength': 1},
        'sources': {
            'type': 'array',
            'items': {'type': 'string', 'format': 'uri'}
        },
        'confidence': {'type': 'number', 'minimum': 0, 'maximum': 1}
    }
}

def test_agent_output_schema(agent_output: dict):
    try:
        jsonschema.validate(instance=agent_output, schema=AGENT_RESPONSE_SCHEMA)
        print('Schema validation passed')
    except jsonschema.ValidationError as e:
        raise AssertionError(f'Invalid agent output: {e.message}')

Asersi Keberadaan Kata Kunci

Untuk respons teks yang susunan katanya dapat berubah, periksa apakah konsep atau kata penting muncul dalam output. Cara ini fleksibel, tetapi tetap bermakna — jawaban agen setidaknya harus menyebutkan istilah yang relevan.

def assert_keywords_present(text: str, keywords: list, require_all: bool = True):
    lower_text = text.lower()
    found = [kw.lower() in lower_text for kw in keywords]

    if require_all:
        missing = [kw for kw, f in zip(keywords, found) if not f]
        assert not missing, f'Missing keywords: {missing}'
    else:
        assert any(found), f'None of {keywords} found in: {text[:100]}'

# Tests
response = 'The capital city of France is Paris, located in western Europe.'
assert_keywords_present(response, ['paris', 'france', 'capital'])
print('All keywords present!')  # passes

assert_keywords_present(response, ['spain', 'france'], require_all=False)
print('At least one keyword present!')  # passes

Memastikan Format Respons: Pemeriksaan Tipe

Asersi tipe berlangsung cepat dan andal. Pastikan agen mengembalikan dict, bukan None, bahwa bidang daftar berupa list, dan bahwa bidang numerik berada dalam rentang yang valid.

def test_agent_returns_valid_structure(agent_result):
    # Type checks
    assert isinstance(agent_result, dict), 'Result must be a dict'
    assert isinstance(agent_result.get('answer'), str), 'answer must be a string'
    assert isinstance(agent_result.get('steps'), list), 'steps must be a list'

    # Non-empty checks
    assert len(agent_result['answer']) > 0, 'answer must not be empty'
    assert len(agent_result['steps']) >= 1, 'must have at least one step'

    # Range checks
    confidence = agent_result.get('confidence', 0)
    assert 0.0 <= confidence <= 1.0, 'confidence must be 0-1'

print('Structural assertions are fast and reliable')

Memastikan finish_reason

Bidang finish_reason memberi tahu alasan model berhenti menghasilkan output. Memastikan bidang ini membantu mendeteksi masalah: 'stop' berarti jawaban selesai dengan baik, 'tool_calls' berarti agen ingin memanggil alat, dan 'length' berarti output terpotong.

from unittest.mock import MagicMock

def test_agent_stops_cleanly(mock_response):
    finish_reason = mock_response.choices[0].finish_reason
    assert finish_reason in ('stop', 'tool_calls'), \
        f'Unexpected finish_reason: {finish_reason}'

def test_no_truncation(mock_response):
    finish_reason = mock_response.choices[0].finish_reason
    assert finish_reason != 'length', \
        'Response was truncated — increase max_tokens'

# Example mock for a clean stop
mock = MagicMock()
mock.choices = [MagicMock(finish_reason='stop')]
test_agent_stops_cleanly(mock)
print('finish_reason: stop — clean termination')

Memastikan Jumlah Langkah dalam Perulangan

Agen yang berjalan dalam perulangan harus selesai dalam jumlah langkah yang wajar. Pastikan agen selesai dalam jumlah iterasi maksimum — hal ini dapat menemukan perulangan tak berujung yang seharusnya dicegah oleh penjaga max_iterations.

def test_agent_completes_in_bounded_steps(mock_agent):
    result = mock_agent.run('Search for the weather in Paris')

    # Agent should complete within 5 steps
    assert result['steps_taken'] <= 5, \
        f'Agent took too many steps: {result["steps_taken"]}'

    # Agent should produce a final answer, not exit on timeout
    assert result['status'] == 'completed', \
        f'Agent did not complete: {result["status"]}'

    assert result['answer'] is not None

print('Bounding step count prevents runaway agents from passing tests')

Membuat Parameter Pengujian untuk Banyak Input

@pytest.mark.parametrize milik pytest memungkinkan Anda menjalankan pengujian yang sama dengan banyak input berbeda. Cara ini ideal untuk menguji apakah agen mengarahkan berbagai jenis kueri ke alat yang tepat.

import pytest
from unittest.mock import patch, MagicMock
import json

@pytest.mark.parametrize('query,expected_tool', [
    ('What is the weather in Tokyo?', 'get_weather'),
    ('Calculate 15% of 200', 'calculator'),
    ('Search for Python books', 'web_search'),
    ('What time is it in Berlin?', 'get_time'),
])
@patch('myagent.client.chat.completions.create')
def test_agent_tool_routing(mock_create, query, expected_tool):
    tool_call = MagicMock()
    tool_call.function.name = expected_tool
    tool_call.function.arguments = json.dumps({'input': query})
    mock_create.return_value = MagicMock(
        choices=[MagicMock(message=MagicMock(tool_calls=[tool_call]))]
    )
    response = mock_create()
    actual = response.choices[0].message.tool_calls[0].function.name
    assert actual == expected_tool

Menulis Pembantu Asersi Khusus

Seiring bertambahnya rangkaian pengujian agen, ekstrak pola asersi umum ke dalam fungsi pembantu. Dengan begitu, pengujian menjadi lebih singkat, lebih mudah dibaca, dan lebih mudah dipelihara ketika format respons agen berubah.

import json

def assert_tool_called(response, tool_name: str, required_args: dict = None):
    message = response.choices[0].message
    assert message.tool_calls, 'Expected tool call but got plain text'
    names = [tc.function.name for tc in message.tool_calls]
    assert tool_name in names, f'Expected {tool_name}, got {names}'

    if required_args:
        for tc in message.tool_calls:
            if tc.function.name == tool_name:
                args = json.loads(tc.function.arguments)
                for key, val in required_args.items():
                    assert args.get(key) == val, \
                        f'Arg {key}: expected {val}, got {args.get(key)}'

# Clean test using the helper:
# assert_tool_called(response, 'get_weather', {'city': 'Paris'})

# --- demo ---
from unittest.mock import MagicMock

tool_call = MagicMock()
tool_call.function.name = 'get_weather'
tool_call.function.arguments = json.dumps({'city': 'Paris'})
response = MagicMock(choices=[MagicMock(message=MagicMock(tool_calls=[tool_call]))])

assert_tool_called(response, 'get_weather', {'city': 'Paris'})
print('assert_tool_called passed: agent called get_weather with city=Paris')

Uji Pemahaman: Pengujian Agen Berbasis Asersi

Uji pemahaman Anda tentang strategi asersi untuk pengujian agen.

Ringkasan: Pengujian Agen Berbasis Asersi

Sekarang Anda memiliki perangkat asersi lengkap untuk pengujian agen:

  • Periksa bahwa tool_calls tidak kosong ketika agen seharusnya menggunakan alat
  • Pastikan nama alat yang tepat dengan tc.function.name == 'expected_tool'
  • Validasikan argumen alat dengan mengurai tc.function.arguments sebagai JSON
  • Gunakan jsonschema.validate() untuk validasi output terstruktur
  • Gunakan pemeriksaan keberadaan kata kunci untuk asersi teks yang fleksibel
  • Periksa finish_reason dan jumlah langkah untuk agen yang menggunakan perulangan
  • Gunakan @pytest.mark.parametrize untuk berbagai skenario input

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Pengujian Agen Berbasis Assertion” gratis?

Ya — teks lengkap “Pengujian Agen Berbasis Assertion” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Agents, upgrade ke CoddyKit PRO. Kursus AI Agents mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Pengujian Agen Berbasis Assertion”?

Periksa panggilan alat, langkah-langkah perantara, dan struktur output akhir. Kamu berlatih AI Agents dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai AI Agents?

Tidak diperlukan pengalaman sebelumnya. AI Agents di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Pengujian Agen Berbasis Assertion” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Agents ini?

Ya. Setiap pelajaran AI Agents menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Mengapa Pengujian Agen Berbeda
  2. Meniru Panggilan LLM dalam Pengujian
  3. Pengujian Agen Berbasis Assertion
  4. Pengujian Integrasi untuk Pipeline Agen
← Kembali ke AI Agents