0Pricing
AI Agents · บทเรียน

การทดสอบตัวแทนโดยอิงการยืนยัน

ตรวจสอบการเรียกใช้เครื่องมือ ขั้นตอนระหว่างทาง และโครงสร้างข้อมูลส่งออกสุดท้าย

การทดสอบตัวแทนโดยอิงการยืนยัน เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

ก้าวพ้นการจับคู่สตริงแบบตรงทั้งหมด

เนื่องจากผลลัพธ์ของ LLM ไม่กำหนดแน่นอน การทดสอบด้วย assert response == 'exact text' จึงเปราะบาง แต่ให้เขียนการตรวจยืนยันที่ตรวจสอบโครงสร้างและเจตนาของการตอบกลับ โดยไม่ขึ้นอยู่กับถ้อยคำที่ตรงกันทุกประการ

การตรวจยืนยันว่ามีการเรียกใช้เครื่องมือ

สำหรับเอเจนต์ที่เรียกใช้ฟังก์ชัน การตรวจยืนยันที่เชื่อถือได้มากที่สุดคือการตรวจสอบว่าเอเจนต์เลือกเรียกใช้เครื่องมือที่ถูกต้องหรือไม่ นี่เป็นการตรวจสอบด้านโครงสร้าง จึงไม่ขึ้นอยู่กับถ้อยคำที่แน่นอนในกระบวนการคิดของ 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')

การตรวจยืนยันชื่อเครื่องมือที่ถูกต้อง

นอกจากตรวจสอบว่ามีการเรียกใช้เครื่องมือแล้ว ให้ตรวจสอบว่าชื่อเครื่องมือเฉพาะตรงตามที่คาดไว้ด้วย วิธีนี้ช่วยตรวจจับกรณีที่เอเจนต์เลือกเครื่องมือผิดสำหรับคำค้นหาที่กำหนด

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

การตรวจยืนยันอาร์กิวเมนต์ของเครื่องมือ

หลังจากตรวจสอบชื่อเครื่องมือแล้ว ให้ตรวจสอบว่าอาร์กิวเมนต์ถูกต้อง เอเจนต์ต้องไม่เพียงเลือกเครื่องมือที่ถูกต้องเท่านั้น แต่ยังต้องใส่พารามิเตอร์ที่ถูกต้องจากคำขอของผู้ใช้ด้วย

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

การตรวจสอบเอาต์พุตด้วย JSON Schema

เมื่อเอเจนต์ส่งคืน JSON ที่มีโครงสร้าง ให้ตรวจสอบผลลัพธ์เทียบกับ JSON Schema เพื่อให้แน่ใจว่าฟิลด์ที่จำเป็นมีอยู่ครบและมีชนิดข้อมูลถูกต้อง ไลบรารี jsonschema ช่วยให้ทำเช่นนี้ได้ง่าย

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

การตรวจยืนยันการมีอยู่ของคีย์เวิร์ด

สำหรับการตอบกลับที่เป็นข้อความและมีถ้อยคำแตกต่างกันได้ ให้ตรวจสอบว่าแนวคิดหรือคำสำคัญปรากฏอยู่ในผลลัพธ์ วิธีนี้ยืดหยุ่นแต่ยังมีความหมาย — อย่างน้อยคำตอบของเอเจนต์ต้องกล่าวถึงคำที่เกี่ยวข้อง

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

การตรวจยืนยันรูปแบบการตอบกลับ: การตรวจสอบชนิดข้อมูล

การตรวจยืนยันชนิดข้อมูลทำงานรวดเร็วและเชื่อถือได้ ให้ตรวจสอบว่าเอเจนต์ส่งคืน dict ไม่ใช่ None ฟิลด์ประเภทลิสต์เป็นลิสต์ และฟิลด์ตัวเลขอยู่ในช่วงที่ถูกต้อง

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

การตรวจยืนยัน finish_reason

ฟิลด์ finish_reason บอกเหตุผลที่โมเดลหยุดสร้างผลลัพธ์ การตรวจยืนยันฟิลด์นี้ช่วยตรวจจับปัญหาได้: 'stop' หมายถึงคำตอบเสร็จสมบูรณ์ 'tool_calls' หมายถึงเอเจนต์ต้องการเรียกใช้เครื่องมือ และ 'length' หมายถึงผลลัพธ์ถูกตัดทอน

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

การตรวจยืนยันจำนวนขั้นตอนในลูป

เอเจนต์ที่ทำงานในลูปควรทำงานเสร็จภายในจำนวนขั้นตอนที่เหมาะสม ให้ตรวจยืนยันว่าเอเจนต์เสร็จสิ้นภายในจำนวนรอบสูงสุดที่กำหนด — วิธีนี้ช่วยตรวจจับลูปไม่สิ้นสุดที่ตัวป้องกัน 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')

การทำพารามิเตอร์การทดสอบสำหรับข้อมูลเข้าหลายรูปแบบ

@pytest.mark.parametrize ของ pytest ช่วยให้คุณเรียกใช้การทดสอบเดียวกันกับข้อมูลเข้าหลายรูปแบบ วิธีนี้เหมาะอย่างยิ่งสำหรับการทดสอบว่าเอเจนต์ส่งคำค้นหาประเภทต่าง ๆ ไปยังเครื่องมือที่ถูกต้อง

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

การเขียนตัวช่วยการตรวจยืนยันแบบกำหนดเอง

เมื่อชุดการทดสอบเอเจนต์มีขนาดใหญ่ขึ้น ให้แยกรูปแบบการตรวจยืนยันที่ใช้บ่อยออกมาเป็นตัวช่วย วิธีนี้ทำให้การทดสอบสั้นลง อ่านง่ายขึ้น และดูแลรักษาได้ง่ายขึ้นเมื่อรูปแบบการตอบกลับของเอเจนต์เปลี่ยนแปลง

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

ตรวจสอบความรู้: การทดสอบเอเจนต์โดยใช้การตรวจยืนยัน

ทดสอบความเข้าใจเกี่ยวกับกลยุทธ์การตรวจยืนยันสำหรับการทดสอบเอเจนต์

ทบทวน: การทดสอบเอเจนต์โดยใช้การตรวจยืนยัน

ขณะนี้คุณมีชุดเครื่องมือการตรวจยืนยันที่ครบถ้วนสำหรับการทดสอบเอเจนต์แล้ว:

  • ตรวจสอบว่า tool_calls ไม่ว่างเปล่าเมื่อเอเจนต์ควรใช้เครื่องมือ
  • ตรวจยืนยันชื่อเครื่องมือที่ถูกต้องด้วย tc.function.name == 'expected_tool'
  • ตรวจสอบอาร์กิวเมนต์ของเครื่องมือโดยแยกวิเคราะห์ tc.function.arguments เป็น JSON
  • ใช้ jsonschema.validate() เพื่อตรวจสอบเอาต์พุตที่มีโครงสร้าง
  • ใช้การตรวจสอบการมีอยู่ของคีย์เวิร์ดสำหรับการตรวจยืนยันข้อความที่ยืดหยุ่น
  • ตรวจสอบ finish_reason และจำนวนขั้นตอนสำหรับเอเจนต์ที่ทำงานในลูป
  • ใช้ @pytest.mark.parametrize สำหรับสถานการณ์ข้อมูลเข้าหลายรูปแบบ

คำถามที่พบบ่อย

บทเรียน “การทดสอบตัวแทนโดยอิงการยืนยัน” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การทดสอบตัวแทนโดยอิงการยืนยัน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การทดสอบตัวแทนโดยอิงการยืนยัน”

ตรวจสอบการเรียกใช้เครื่องมือ ขั้นตอนระหว่างทาง และโครงสร้างข้อมูลส่งออกสุดท้าย คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การทดสอบตัวแทนโดยอิงการยืนยัน” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม

ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เหตุใดการทดสอบตัวแทนจึงแตกต่าง
  2. การจำลองการเรียก LLM ในการทดสอบ
  3. การทดสอบตัวแทนโดยอิงการยืนยัน
  4. การทดสอบการผสานรวมสำหรับไปป์ไลน์ตัวแทน
← กลับไปที่ AI Agents