AI Engineering Academy · Lekcja

Przetwarzanie wywołań narzędzi w aplikacji

Wykrywaj finish_reason tool_calls w odpowiedzi API, wyodrębniaj nazwy funkcji i argumenty, wykonuj odpowiednią funkcję Python, a następnie odsyłaj wynik do modelu.

Lekcja 2 z 413 kroki

Przetwarzanie wywołań narzędzi w aplikacji to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.

Obiekt odpowiedzi wywołania narzędzia

Gdy model zdecyduje się wywołać funkcję, odpowiedź API zawiera listę tool_calls w obiekcie wiadomości. Każde wywołanie narzędzia ma unikalny id, nazwę funkcji w function.name oraz function.arguments — ciąg JSON zawierający argumenty, które model chce przekazać. Kod aplikacji odpowiada za przeanalizowanie tych danych i wykonanie funkcji.

from openai import OpenAI
import json

client = OpenAI()

response = client.chat.completions.create(
    model='gpt-4o',
    messages=[{'role': 'user', 'content': 'What is the weather in Paris?'}],
    tools=tools  # defined previously
)

message = response.choices[0].message

if response.choices[0].finish_reason == 'tool_calls':
    for tool_call in message.tool_calls:
        print('Call ID:', tool_call.id)
        print('Function name:', tool_call.function.name)
        print('Arguments (JSON string):', tool_call.function.arguments)

Analizowanie argumentów funkcji

Pole function.arguments zawiera ciąg zakodowany w formacie JSON, a nie słownik języka Python. Należy je przeanalizować za pomocą json.loads(). Zawsze umieszczaj tę operację w bloku try/except — mimo wskazówek wynikających ze schematu model czasami generuje niepoprawny JSON, dlatego trzeba obsłużyć to w kontrolowany sposób.

import json

def parse_tool_call(tool_call) -> dict:
    '''Parse a tool call's arguments from JSON string to dict.'''
    try:
        args = json.loads(tool_call.function.arguments)
        return args
    except json.JSONDecodeError as e:
        print(f'Failed to parse arguments for {tool_call.function.name}: {e}')
        print(f'Raw arguments: {tool_call.function.arguments}')
        return {}

# Usage
tool_call = message.tool_calls[0]
args = parse_tool_call(tool_call)
print('Parsed args:', args)  # {'location': 'Paris', 'unit': 'celsius'}

Przekierowywanie do właściwej funkcji

Użyj function.name, aby przekierować wywołanie do właściwej funkcji języka Python. Przejrzysty wzorzec polega na przechowywaniu funkcji w słowniku mapującym nazwy na obiekty wywoływalne. Unika to podatnych na błędy łańcuchów if/elif i ułatwia późniejsze dodawanie nowych narzędzi.

def get_current_weather(location: str, unit: str = 'celsius') -> str:
    # Real implementation calls a weather API
    return f'{location}: 18{chr(176)}C, partly cloudy'

def create_calendar_event(title: str, start_time: str, duration_minutes: int, **kwargs) -> str:
    return f'Event created: {title} at {start_time} for {duration_minutes} minutes'

# Tool registry: maps function names to callables
TOOL_REGISTRY = {
    'get_current_weather': get_current_weather,
    'create_calendar_event': create_calendar_event
}

def execute_tool_call(tool_call) -> str:
    name = tool_call.function.name
    args = parse_tool_call(tool_call)

    if name not in TOOL_REGISTRY:
        return f'Unknown function: {name}'

    try:
        result = TOOL_REGISTRY[name](**args)
        return str(result)
    except Exception as e:
        return f'Function {name} raised an error: {str(e)}'

Odsyłanie wyników do modelu

Po wykonaniu funkcji należy odesłać wynik do modelu w kolejnym wywołaniu API. Dodaj do konwersacji wiadomość asystenta zawierającą tool_calls, a następnie dodaj nową wiadomość z role='tool', identyfikatorem tool_call_id i wynikiem funkcji jako treścią. Następnie ponownie wywołaj API.

def run_tool_call_loop(messages: list, tools: list) -> str:
    response = client.chat.completions.create(
        model='gpt-4o',
        messages=messages,
        tools=tools
    )
    message = response.choices[0].message
    messages.append(message)  # Add assistant's tool_calls message

    # Execute all tool calls and collect results
    for tool_call in (message.tool_calls or []):
        result = execute_tool_call(tool_call)
        # Add each tool result as a 'tool' role message
        messages.append({
            'role': 'tool',
            'tool_call_id': tool_call.id,
            'content': result
        })

    # Second API call with results appended
    final_response = client.chat.completions.create(
        model='gpt-4o',
        messages=messages,
        tools=tools
    )
    return final_response.choices[0].message.content

Pełny przebieg konwersacji

Kompletna interakcja z wywołaniem narzędzia obejmuje cztery wiadomości w historii konwersacji: wiadomość użytkownika, wiadomość asystenta z żądaniem wywołania narzędzia, wiadomość z wynikiem narzędzia oraz końcową odpowiedź asystenta uwzględniającą ten wynik. Zrozumienie tej struktury jest niezbędne do budowania asystentów korzystających z narzędzi w konwersacjach wieloetapowych.

# The full message history for a tool-calling conversation:
conversation = [
    {'role': 'user', 'content': 'What is the weather in Tokyo?'},

    # Model requests a tool call (added by run_tool_call_loop)
    # {'role': 'assistant', 'content': None, 'tool_calls': [...]},

    # Application sends tool result back
    # {'role': 'tool', 'tool_call_id': 'call_abc123', 'content': 'Tokyo: 22C, sunny'},

    # Model produces final human-readable response
    # {'role': 'assistant', 'content': 'The weather in Tokyo is 22 degrees Celsius and sunny.'}
]

final_answer = run_tool_call_loop(
    [{'role': 'user', 'content': 'What is the weather in Tokyo?'}],
    tools
)
print(final_answer)

Obsługa sytuacji, w której nie wywołano narzędzia

Czasami model odpowiada bezpośrednio, nie wywołując żadnego narzędzia — finish_reason będzie mieć wartość 'stop', a nie 'tool_calls'. Zawsze sprawdzaj ten przypadek przed próbą przetworzenia wywołań narzędzi. Solidna implementacja powinna prawidłowo obsługiwać obie ścieżki.

def smart_complete(user_message: str) -> str:
    messages = [{'role': 'user', 'content': user_message}]
    response = client.chat.completions.create(
        model='gpt-4o',
        messages=messages,
        tools=tools
    )
    choice = response.choices[0]

    if choice.finish_reason == 'stop':
        # Model answered directly without calling a tool
        return choice.message.content

    elif choice.finish_reason == 'tool_calls':
        # Process tool calls
        messages.append(choice.message)
        for tc in choice.message.tool_calls:
            result = execute_tool_call(tc)
            messages.append({'role': 'tool', 'tool_call_id': tc.id, 'content': result})
        # Get final answer
        final = client.chat.completions.create(model='gpt-4o', messages=messages)
        return final.choices[0].message.content

    return 'Unexpected finish reason: ' + choice.finish_reason

Weryfikowanie argumentów przed wykonaniem

Model może czasami przekazać argumenty, które nie przejdą walidacji logiki biznesowej — ujemny czas trwania, nieprawidłowy adres e-mail lub datę z przeszłości. Zweryfikuj argumenty przed wywołaniem właściwej funkcji i zwróć opisowy ciąg błędu, jeśli walidacja się nie powiedzie. Model będzie wtedy mógł poprawić argumenty w następnej turze.

from pydantic import BaseModel, ValidationError
from datetime import datetime

class CreateEventArgs(BaseModel):
    title: str
    start_time: str  # ISO 8601
    duration_minutes: int

def safe_create_event(tool_call) -> str:
    try:
        raw_args = json.loads(tool_call.function.arguments)
        validated = CreateEventArgs(**raw_args)
        # Additional business rule
        event_time = datetime.fromisoformat(validated.start_time)
        if event_time < datetime.now():
            return 'Error: start_time must be in the future.'
        return create_calendar_event(**validated.dict())
    except ValidationError as e:
        return f'Invalid arguments: {e}'

Rejestrowanie interakcji z wywołaniami narzędzi

Zawsze rejestruj interakcje z wywołaniami narzędzi na potrzeby debugowania i analityki. Rejestruj nazwę funkcji, argumenty, wynik oraz czas wykonania. Dane te pomagają ustalić, które narzędzia są wywoływane najczęściej, które kończą się błędem i jakie wzorce argumentów generuje model — są niezwykle cenne przy ulepszaniu schematów i implementacji funkcji.

import time
import logging

logger = logging.getLogger('tool_calls')

def logged_execute(tool_call) -> str:
    name = tool_call.function.name
    args_str = tool_call.function.arguments
    start = time.time()
    result = execute_tool_call(tool_call)
    elapsed = time.time() - start

    logger.info(
        'Tool call executed',
        extra={
            'function': name,
            'arguments': args_str,
            'result_length': len(result),
            'elapsed_ms': round(elapsed * 1000)
        }
    )
    return result

Zagadnienia bezpieczeństwa wywołań narzędzi

Nigdy nie wykonuj dowolnych funkcji na podstawie danych wyjściowych modelu bez ich weryfikacji. Zawsze umieszczaj dokładne nazwy funkcji z TOOL_REGISTRY na liście dozwolonych, weryfikuj wszystkie argumenty i sprawdzaj uprawnienia przed wykonaniem operacji. Model jest niezaufanym wywołującym — złośliwy prompt może próbować wywołać funkcje destrukcyjne, jeśli logika przekierowywania będzie zbyt liberalna.

  • Zezwalaj wyłącznie na funkcje jawnie wymienione w TOOL_REGISTRY
  • Weryfikuj dane wejściowe za pomocą Pydantic przed wykonaniem
  • Wymagaj autoryzacji dla operacji zapisu i usuwania

Zwracanie rozbudowanych wyników strukturalnych

Wyniki narzędzi nie muszą być zwykłymi ciągami tekstu. Można zwracać dane w formacie JSON, tabele lub podsumowania. Zwracanie uporządkowanych danych jako JSON pozwala modelowi analizować konkretne pola i odwoływać się do nich w końcowej odpowiedzi. W przypadku dużych wyników zwróć podsumowanie z najważniejszymi informacjami zamiast umieszczać w kontekście wszystkie surowe dane.

def get_order_status(order_id: str) -> str:
    # Fetch from real database
    order = {'id': order_id, 'status': 'shipped', 'estimated_delivery': '2024-03-15', 'carrier': 'FedEx', 'tracking': 'FX123456'}
    # Return concise summary, not raw DB record
    return (
        f'Order {order_id}: Status={order["status"]}, '
        f'Estimated delivery: {order["estimated_delivery"]}, '
        f'Carrier: {order["carrier"]}, Tracking: {order["tracking"]}'
    )

Konwersacje wieloetapowe z użyciem narzędzi

Skuteczny wzorzec stanowi wieloetapowa konwersacja, w której model wywołuje narzędzia w odpowiedzi na kolejne wiadomości użytkownika, stopniowo budując kontekst. Zawsze zachowuj pełną historię konwersacji, w tym wcześniejsze wywołania narzędzi i ich wyniki, aby model mógł odwoływać się do poprzednich odpowiedzi bez niepotrzebnego ponownego wywoływania narzędzi.

Szybkie sprawdzenie

Sprawdź, czy rozumiesz przetwarzanie wywołań narzędzi w aplikacji.

Podsumowanie lekcji

W tej lekcji poznali Państwo następujące zagadnienia: argumenty wywołania narzędzia są przekazywane jako ciąg JSON, który należy przeanalizować za pomocą json.loads(), słownik TOOL_REGISTRY mapuje nazwy funkcji na obiekty wywoływalne, zapewniając przejrzyste przekierowywanie, a wyniki wracają do modelu jako wiadomości role='tool' z pasującymi identyfikatorami tool_call_ids. W następnej części zajmiemy się sytuacją, w której model wywołuje jednocześnie wiele funkcji, korzystając z równoległych wywołań funkcji.

Bezpłatny start

Ucz się Python dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
30
Lekcje
120

Często zadawane pytania

Czy lekcja „Przetwarzanie wywołań narzędzi w aplikacji” jest bezpłatna?

Tak — pełny tekst „Przetwarzanie wywołań narzędzi w aplikacji” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Przetwarzanie wywołań narzędzi w aplikacji”?

Wykrywaj finish_reason tool_calls w odpowiedzi API, wyodrębniaj nazwy funkcji i argumenty, wykonuj odpowiednią funkcję Python, a następnie odsyłaj wynik do modelu. Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?

Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Przetwarzanie wywołań narzędzi w aplikacji”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?

Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Definiowanie schematów funkcji dla API
  2. Przetwarzanie wywołań narzędzi w aplikacji
  3. Równoległe wywoływanie funkcji
  4. Budowanie interfejsu bazy danych w języku naturalnym
← Powrót do AI Engineering Academy