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!') # passesMemastikan 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_toolMenulis 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_callstidak kosong ketika agen seharusnya menggunakan alat - Pastikan nama alat yang tepat dengan
tc.function.name == 'expected_tool' - Validasikan argumen alat dengan mengurai
tc.function.argumentssebagai JSON - Gunakan
jsonschema.validate()untuk validasi output terstruktur - Gunakan pemeriksaan keberadaan kata kunci untuk asersi teks yang fleksibel
- Periksa
finish_reasondan jumlah langkah untuk agen yang menggunakan perulangan - Gunakan
@pytest.mark.parametrizeuntuk 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
- Mengapa Pengujian Agen Berbeda
- Meniru Panggilan LLM dalam Pengujian
- Pengujian Agen Berbasis Assertion
- Pengujian Integrasi untuk Pipeline Agen