Ejen AI · Pelajaran

Pengujian Ejen Berasaskan Penegasan

Menyemak panggilan alat, langkah perantaraan dan struktur output akhir.

Pelajaran 3 daripada 413 langkah

Pengujian Ejen Berasaskan Penegasan ialah pelajaran Ejen AI percuma di CoddyKit. Ini ialah pelajaran 3 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Ejen AI, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Ejen AI merangkumi sejumlah 4 pelajaran.

Melangkaui Padanan Rentetan Tepat

Memandangkan keluaran LLM tidak deterministik, mengujinya dengan assert response == 'exact text' adalah rapuh. Sebaliknya, tulis penegasan yang memeriksa struktur dan niat respons tanpa bergantung pada susunan kata yang tepat.

Mengesahkan Panggilan Alat Dibuat

Bagi ejen panggilan fungsi, penegasan yang paling boleh dipercayai ialah mengesahkan bahawa ejen memilih untuk memanggil alat yang betul. Ini bersifat struktur — ia tidak bergantung pada susunan kata tepat dalam proses pemikiran 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')

Mengesahkan Nama Alat yang Betul

Selain sekadar memeriksa kewujudan panggilan alat, sahkan bahawa nama alat tertentu sepadan dengan jangkaan. Ini mengesan keadaan apabila ejen memilih alat yang salah untuk pertanyaan 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')

Mengesahkan Hujah Alat

Selepas mengesahkan nama alat, periksa bahawa hujahnya betul. Ejen bukan sahaja mesti memilih alat yang betul, malah mesti mengisinya dengan parameter yang betul daripada 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')

Pengesahan Skema JSON untuk Keluaran

Apabila ejen anda mengembalikan JSON berstruktur, sahkan keluaran itu berdasarkan Skema JSON untuk memastikan semua medan yang diperlukan wujud dan mempunyai jenis yang betul. Pustaka jsonschema memudahkan perkara 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}')

Penegasan Kehadiran Kata Kunci

Bagi respons teks yang susunan katanya boleh berubah, periksa sama ada konsep atau perkataan utama muncul dalam keluaran. Pendekatan ini fleksibel tetapi masih bermakna — jawapan ejen sekurang-kurangnya mesti menyebut istilah yang berkaitan.

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

Mengesahkan Format Respons: Pemeriksaan Jenis

Penegasan jenis adalah pantas dan boleh dipercayai. Sahkan bahawa ejen mengembalikan kamus (bukan None), bahawa medan senarai ialah senarai, dan bahawa medan angka berada dalam julat yang sah.

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')

Mengesahkan finish_reason

Medan finish_reason memberitahu sebab model berhenti menjana keluaran. Menegaskannya membantu mengesan masalah: 'stop' bermaksud jawapan yang selesai dengan baik, 'tool_calls' bermaksud ejen mahu memanggil alat, dan 'length' bermaksud keluaran 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')

Mengesahkan Bilangan Langkah dalam Gelung

Ejen yang berjalan dalam gelung sepatutnya selesai dalam bilangan langkah yang munasabah. Tegaskan bahawa ejen selesai dalam bilangan lelaran maksimum — ini mengesan gelung tidak terhingga yang sepatutnya dihalang oleh pengawal 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')

Parametrize Ujian untuk Pelbagai Masukan

@pytest.mark.parametrize pytest membolehkan anda menjalankan ujian yang sama dengan banyak masukan berbeza. Ini sesuai untuk menguji bahawa ejen mengarahkan jenis pertanyaan yang berbeza kepada alat yang betul.

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 Penegasan Tersuai

Apabila set ujian ejen anda berkembang, keluarkan corak penegasan yang biasa kepada fungsi pembantu. Ini menjadikan ujian lebih pendek, lebih mudah dibaca, dan lebih mudah diselenggara apabila format respons ejen 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')

Semak Pengetahuan: Pengujian Ejen Berasaskan Penegasan

Uji pemahaman anda tentang strategi penegasan untuk ujian ejen.

Imbas Kembali: Pengujian Ejen Berasaskan Penegasan

Kini anda mempunyai set alat penegasan yang lengkap untuk ujian ejen:

  • Periksa bahawa tool_calls tidak kosong apabila ejen sepatutnya menggunakan alat
  • Tegaskan nama alat yang betul dengan tc.function.name == 'expected_tool'
  • Sahkan hujah alat dengan menghuraikan tc.function.arguments sebagai JSON
  • Gunakan jsonschema.validate() untuk pengesahan keluaran berstruktur
  • Gunakan pemeriksaan kehadiran kata kunci untuk penegasan teks yang fleksibel
  • Periksa finish_reason dan bilangan langkah bagi ejen bergelung
  • Gunakan @pytest.mark.parametrize untuk pelbagai senario masukan
Percuma untuk bermula

Pelajari Ejen AI dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
60
Pelajaran
239

Soalan Lazim

Adakah pelajaran “Pengujian Ejen Berasaskan Penegasan” percuma?

Ya — teks penuh “Pengujian Ejen Berasaskan Penegasan” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Ejen AI, tingkat taraf kepada CoddyKit PRO. Kursus Ejen AI merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Pengujian Ejen Berasaskan Penegasan”?

Menyemak panggilan alat, langkah perantaraan dan struktur output akhir. Anda berlatih Ejen AI menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan Ejen AI?

Tiada pengalaman terdahulu diperlukan. Pembelajaran Ejen AI di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 3 daripada 4.

Berapa lamakah pelajaran “Pengujian Ejen Berasaskan Penegasan” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran Ejen AI ini?

Ya. Setiap pelajaran Ejen AI menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. Mengapa Pengujian Ejen Berbeza
  2. Memalsukan Panggilan LLM dalam Ujian
  3. Pengujian Ejen Berasaskan Penegasan
  4. Ujian Integrasi untuk Pipeline Ejen
← Kembali ke Ejen AI