0Pricing
AI Agents · 강의

단정문 기반 에이전트 테스트

도구 호출, 중간 단계, 최종 출력 구조를 확인합니다.

단정문 기반 에이전트 테스트은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 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

응답 형식 단언: 유형 검사

유형 단언은 빠르고 신뢰할 수 있습니다. 에이전트가 없음이 아닌 사전을 반환하는지, 목록 필드가 목록인지, 숫자 필드가 유효한 범위에 있는지 확인하십시오.

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 필드는 모델이 생성을 중단한 이유를 알려 줍니다. 이를 단언하면 문제를 발견하는 데 도움이 됩니다. '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')

반복문 단계 수 단언하기

반복문에서 실행되는 에이전트는 합리적인 단계 수 안에 완료되어야 합니다. 에이전트가 최대 반복 횟수 이내에 끝나는지 단언하십시오. 이를 통해 최대 반복 횟수 보호 장치가 방지하려는 무한 반복을 발견할 수 있습니다.

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의 @pytest.mark.parametrize를 사용하면 여러 가지 입력으로 동일한 검사를 실행할 수 있습니다. 에이전트가 서로 다른 질의 유형을 올바른 도구로 라우팅하는지 검사하는 데 적합합니다.

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를 사용하십시오

자주 묻는 질문

“단정문 기반 에이전트 테스트” 강의는 무료인가요?

네 — “단정문 기반 에이전트 테스트” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

“단정문 기반 에이전트 테스트”에서 뭘 배우나요?

도구 호출, 중간 단계, 최종 출력 구조를 확인합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Agents을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“단정문 기반 에이전트 테스트” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 에이전트 테스트가 다른 이유
  2. 테스트에서 LLM 호출 모의 처리
  3. 단정문 기반 에이전트 테스트
  4. 에이전트 파이프라인의 통합 테스트
← AI Agents(으)로 돌아가기