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

การจำลองการเรียก LLM ในการทดสอบ

unittest.mock, ฟิกซ์เจอร์ pytest และการบันทึก/เล่นซ้ำการตอบกลับของ LLM

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

การจำลองคืออะไร

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

โมดูล unittest.mock ของ Python เป็นเครื่องมือมาตรฐานสำหรับงานนี้

พื้นฐาน unittest.mock.patch()

unittest.mock.patch(target) จะแทนที่ออบเจ็กต์ที่ระบุชื่อไว้ชั่วคราวตลอดระยะเวลาของการทดสอบ target คือสตริงที่คั่นด้วยจุด ซึ่งชี้ไปยังออบเจ็กต์ตามที่ออบเจ็กต์นั้นถูกนำเข้าในโมดูลที่กำลังทดสอบ

from unittest.mock import patch, MagicMock

# The function under test calls openai.chat.completions.create
# We patch it so no real API call is made

def ask_llm(question: str) -> str:
    import openai
    client = openai.OpenAI(api_key='test')
    resp = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': question}]
    )
    return resp.choices[0].message.content

with patch('openai.OpenAI') as mock_client_class:
    mock_instance = MagicMock()
    mock_client_class.return_value = mock_instance
    mock_instance.chat.completions.create.return_value = MagicMock(
        choices=[MagicMock(message=MagicMock(content='Paris'))]
    )
    result = ask_llm('Capital of France?')
    print(result)  # 'Paris' — no API call made

การใช้ patch เป็นตัวตกแต่งใน pytest

เมื่อใช้เป็นตัวตกแต่งร่วมกับ pytest แล้ว @patch() จะแทรกม็อกเป็นพารามิเตอร์ของฟังก์ชัน ม็อกจะถูกนำออกโดยอัตโนมัติหลังการทดสอบเสร็จสิ้น

from unittest.mock import patch, MagicMock
import pytest

# Assume agent.py contains: import openai; client = openai.OpenAI(...)

@patch('agent.openai.OpenAI')
def test_agent_calls_llm(mock_openai_class):
    # Set up the mock chain
    mock_client = MagicMock()
    mock_openai_class.return_value = mock_client
    mock_client.chat.completions.create.return_value = MagicMock(
        choices=[MagicMock(message=MagicMock(content='Paris is the capital of France.'))]
    )

    from agent import ask_llm
    result = ask_llm('What is the capital of France?')

    assert 'Paris' in result
    mock_client.chat.completions.create.assert_called_once()

การสร้างการตอบกลับจำลองที่นำกลับมาใช้ใหม่ได้

การสร้างออบเจ็กต์การตอบกลับจำลองด้วยตนเองมีรายละเอียดมาก ให้สร้างฟังก์ชันช่วยสำหรับสร้างออบเจ็กต์จำลองที่มีโครงสร้างถูกต้องและตรงกับรูปแบบการตอบกลับของ OpenAI SDK

from unittest.mock import MagicMock

def make_mock_response(content: str, tool_calls: list = None) -> MagicMock:
    message = MagicMock()
    message.content = content
    message.tool_calls = tool_calls or []

    choice = MagicMock()
    choice.message = message
    choice.finish_reason = 'stop' if not tool_calls else 'tool_calls'

    response = MagicMock()
    response.choices = [choice]
    response.usage = MagicMock(total_tokens=42)
    return response

# Usage in tests:
# mock_create.return_value = make_mock_response('Hello!')
# mock_create.return_value = make_mock_response('', tool_calls=[...])

# --- demo ---
response = make_mock_response('The weather in Paris is 18C and sunny.')
print('content:', response.choices[0].message.content)
print('finish_reason:', response.choices[0].finish_reason)
print('total_tokens:', response.usage.total_tokens)

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

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

import json
from unittest.mock import MagicMock

def make_tool_call_response(tool_name: str, arguments: dict) -> MagicMock:
    tool_call = MagicMock()
    tool_call.id = 'call_abc123'
    tool_call.type = 'function'
    tool_call.function = MagicMock()
    tool_call.function.name = tool_name
    tool_call.function.arguments = json.dumps(arguments)

    message = MagicMock()
    message.content = None
    message.tool_calls = [tool_call]

    response = MagicMock()
    response.choices = [MagicMock(message=message, finish_reason='tool_calls')]
    return response

# mock.return_value = make_tool_call_response('search_web', {'query': 'Python tutorials'})

# --- demo ---
response = make_tool_call_response('search_web', {'query': 'Python tutorials'})
call = response.choices[0].message.tool_calls[0]
print('tool name:', call.function.name)
print('tool arguments:', call.function.arguments)
print('finish_reason:', response.choices[0].finish_reason)

ฟิกซ์เจอร์ pytest สำหรับการจำลอง

ฟิกซ์เจอร์ pytest ช่วยให้คุณกำหนดโค้ดสำหรับการตั้งค่าที่นำกลับมาใช้ใหม่ได้ ให้สร้างฟิกซ์เจอร์ที่แพตช์ไคลเอ็นต์ LLM และจัดเตรียมไคลเอ็นต์นั้นให้กับการทดสอบใดก็ตามที่ร้องขอ — ไม่ต้องเขียนซ้ำ

import pytest
from unittest.mock import patch, MagicMock

@pytest.fixture
def mock_openai(make_mock_response):
    with patch('myagent.client.chat.completions.create') as mock_create:
        mock_create.return_value = MagicMock(
            choices=[MagicMock(message=MagicMock(
                content='Default mocked response',
                tool_calls=[]
            ))]
        )
        yield mock_create

# Now any test can use it:
def test_agent_responds(mock_openai):
    from myagent import agent
    result = agent.run('Hello')
    assert result is not None
    mock_openai.assert_called_once()

ฟิกซ์เจอร์ mocker จาก pytest-mock

pytest-mock มีฟิกซ์เจอร์ mocker ที่ทำให้การแพตช์ง่ายขึ้น ฟิกซ์เจอร์นี้ล้างม็อกโดยอัตโนมัติและมีไวยากรณ์ที่สะอาดกว่า unittest.mock.patch แบบดิบ

# pip install pytest-mock

# In your test file:
def test_agent_with_mocker(mocker):
    mock_create = mocker.patch('myagent.client.chat.completions.create')
    mock_create.return_value = mocker.MagicMock(
        choices=[mocker.MagicMock(message=mocker.MagicMock(
            content='Mocked answer',
            tool_calls=[]
        ))]
    )

    from myagent import agent
    result = agent.run('What is 2+2?')
    assert 'answer' in result.lower() or '4' in result

    mock_create.assert_called_once()
    # No cleanup needed — mocker handles it

การบันทึกและเล่นซ้ำด้วย vcr.py

vcrpy จะบันทึกการโต้ตอบ HTTP จริงลงในไฟล์ 'cassette' เมื่อเรียกใช้ครั้งแรก จากนั้นจะเล่นการโต้ตอบเหล่านั้นซ้ำในการเรียกใช้ครั้งต่อไป วิธีนี้เหมาะอย่างยิ่งสำหรับการทดสอบโค้ดที่ใช้ HTTP API โดยตรงแทน SDK

# pip install vcrpy
import vcr
import httpx

@vcr.use_cassette('fixtures/cassettes/openai_chat.yaml')
def test_with_recorded_response():
    # First run: makes a real HTTP call and records it
    # Subsequent runs: uses the recorded cassette (no network, no cost)
    response = httpx.post(
        'https://api.openai.com/v1/chat/completions',
        json={'model': 'gpt-4o-mini', 'messages': [{'role': 'user', 'content': 'Hello'}]},
        headers={'Authorization': 'Bearer YOUR_KEY'}
    )
    data = response.json()
    assert data['choices'][0]['message']['content'] is not None

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

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

from unittest.mock import patch, MagicMock, call

@patch('myagent.client.chat.completions.create')
def test_agent_sends_correct_model(mock_create):
    mock_create.return_value = MagicMock(
        choices=[MagicMock(message=MagicMock(content='ok', tool_calls=[]))]
    )

    from myagent import agent
    agent.run('Hello')

    # Verify the mock was called with correct arguments
    mock_create.assert_called_once()
    call_kwargs = mock_create.call_args.kwargs
    assert call_kwargs['model'] == 'gpt-4o-mini'
    assert len(call_kwargs['messages']) >= 1
    assert call_kwargs['messages'][0]['role'] == 'system'

การจำลองข้อผิดพลาดของ API ในการทดสอบ

ทดสอบว่าเอเจนต์จัดการกับความล้มเหลวของ LLM อย่างไร โดยกำหนดให้ม็อกส่งข้อยกเว้น วิธีนี้จะตรวจสอบการจัดการข้อผิดพลาดและตรรกะการลองใหม่ได้โดยไม่ทำให้ API จริงล้มเหลว

from unittest.mock import patch
import openai

@patch('myagent.client.chat.completions.create')
def test_agent_handles_rate_limit(mock_create):
    # Simulate a rate limit error
    mock_create.side_effect = openai.RateLimitError(
        message='Rate limit exceeded',
        response=None,
        body=None
    )

    from myagent import agent
    result = agent.run('Hello')

    # Agent should handle this gracefully
    assert result['error'] == 'rate_limit'
    # or
    assert result['retry_after'] is not None

การจัดระเบียบฟิกซ์เจอร์จำลองใน conftest.py

วางฟิกซ์เจอร์ที่ใช้ร่วมกันไว้ใน conftest.py ที่รากของไดเรกทอรีการทดสอบ pytest จะค้นหาไฟล์นี้โดยอัตโนมัติและทำให้ฟิกซ์เจอร์พร้อมใช้งานกับไฟล์การทดสอบทั้งหมดโดยไม่ต้องนำเข้า

# tests/conftest.py
import pytest
from unittest.mock import patch, MagicMock

@pytest.fixture(autouse=False)
def mock_llm():
    with patch('myagent.client.chat.completions.create') as mock_create:
        mock_create.return_value = MagicMock(
            choices=[MagicMock(message=MagicMock(
                content='Test response',
                tool_calls=[]
            ))]
        )
        yield mock_create

@pytest.fixture
def mock_search_tool():
    with patch('myagent.tools.search_web') as mock_search:
        mock_search.return_value = [{'title': 'Test', 'url': 'https://example.com'}]
        yield mock_search

ตรวจสอบความรู้: การจำลองการเรียกใช้ LLM

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

ทบทวน: การจำลองการเรียกใช้ LLM ในการทดสอบ

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

  • ใช้ unittest.mock.patch() เพื่อแทนที่ไคลเอ็นต์ LLM ด้วยม็อก
  • สร้างตัวช่วยการตอบกลับจำลองที่นำกลับมาใช้ใหม่ได้และตรงกับรูปแบบการตอบกลับของ SDK
  • จำลองการเรียกใช้เครื่องมือด้วยฟิลด์ tool_calls ที่มีโครงสร้างถูกต้อง
  • ใช้ฟิกซ์เจอร์ pytest และ conftest.py เพื่อแบ่งปันม็อกระหว่างการทดสอบ
  • ใช้ pytest-mock เพื่อให้ไวยากรณ์สะอาดขึ้น
  • ใช้ vcrpy เพื่อบันทึกและเล่นการโต้ตอบ HTTP จริงซ้ำ

ม็อกคือรากฐานของชุดการทดสอบเอเจนต์ที่รวดเร็วและดูแลรักษาได้

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

บทเรียน “การจำลองการเรียก LLM ในการทดสอบ” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “การจำลองการเรียก LLM ในการทดสอบ”

unittest.mock, ฟิกซ์เจอร์ pytest และการบันทึก/เล่นซ้ำการตอบกลับของ LLM คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

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

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

บทเรียน “การจำลองการเรียก LLM ในการทดสอบ” ใช้เวลานานแค่ไหน

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

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

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

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

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