Обработка вызовов инструментов в приложении
Обнаружьте tool_calls в поле finish_reason ответа API, извлеките имена функций и аргументы, выполните соответствующую функцию Python и отправьте результат обратно модели.
«Обработка вызовов инструментов в приложении» — бесплатный урок AI Engineering Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Engineering Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Engineering Academy содержит 4 уроков всего.
Объект ответа на вызов инструмента
Когда модель решает вызвать функцию, ответ API содержит список tool_calls в объекте сообщения. Каждый вызов инструмента имеет уникальный id, имя вызываемой функции function.name и function.arguments — строку JSON с аргументами, которые модель хочет передать. Ваш код приложения отвечает за их разбор и выполнение функции.
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)Разбор аргументов функции
Поле function.arguments представляет собой строку в формате JSON, а не словарь Python. Необходимо разобрать её с помощью json.loads(). Всегда оборачивайте эту операцию в try/except: несмотря на рекомендации схемы, модель иногда создаёт некорректный JSON, и приложение должно корректно обработать такую ситуацию.
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'}Вызов правильной функции
Используйте function.name, чтобы направить вызов к правильной функции Python. Удобный подход — хранить функции в словаре, сопоставляющем имя и вызываемый объект. Это избавляет от ненадёжных цепочек if/elif и упрощает добавление новых инструментов в дальнейшем.
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)}'Отправка результатов обратно модели
После выполнения функции необходимо отправить результат обратно модели в следующем вызове API. Добавьте сообщение ассистента, содержащее tool_calls, в историю разговора, затем добавьте новое сообщение с role='tool', идентификатором tool_call_id и результатом функции в качестве содержимого. После этого снова вызовите 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Полный цикл разговора
Полное взаимодействие с вызовом инструмента включает в истории разговора четыре сообщения: сообщение пользователя, сообщение ассистента с запросом на вызов инструмента, сообщение с результатом инструмента и итоговый ответ ассистента, учитывающий этот результат. Понимание такой структуры необходимо для создания ассистентов, использующих инструменты в многоэтапных диалогах.
# 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)Обработка случая, когда инструмент не вызывается
Иногда модель отвечает напрямую, не вызывая инструмент: finish_reason будет иметь значение 'stop', а не 'tool_calls'. Всегда проверяйте этот случай, прежде чем пытаться обработать вызовы инструментов. Надёжная реализация корректно обрабатывает обе ветви.
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Проверка аргументов перед выполнением
Иногда модель может передать аргументы, не проходящие проверку бизнес-логики: отрицательную продолжительность, недопустимый адрес электронной почты или дату в прошлом. Проверяйте аргументы до вызова реальной функции и возвращайте информативную строку ошибки, если проверка не пройдена. В следующем цикле модель сможет исправить аргументы.
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}'Журналирование взаимодействий с вызовами инструментов
Всегда записывайте взаимодействия с вызовами инструментов в журнал для отладки и аналитики. Записывайте имя функции, аргументы, результат и время выполнения. Эти данные помогают определить, какие инструменты вызываются чаще всего, какие завершаются ошибкой и какие шаблоны аргументов создаёт модель. Они бесценны для улучшения схем и реализаций функций.
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Вопросы безопасности при вызове инструментов
Никогда не выполняйте произвольные функции на основе вывода модели без проверки. Всегда разрешайте только точные имена функций из вашего TOOL_REGISTRY, проверяйте все аргументы и удостоверяйтесь в наличии прав перед выполнением действий. Модель является недоверенным вызывающим объектом: вредоносная подсказка может попытаться вызвать опасные функции, если логика выбора функции допускает слишком многое.
- Разрешайте только функции, явно перечисленные в реестре инструментов
- Проверяйте входные данные с помощью Pydantic перед выполнением
- Требуйте авторизацию для операций записи и удаления
Возврат содержательных структурированных результатов
Результаты инструментов не обязаны быть обычными строками. Можно возвращать данные в формате JSON, таблицы или сводки. Возврат структурированных данных в формате JSON позволяет модели анализировать и упоминать отдельные поля в итоговом ответе. Для больших результатов возвращайте сводку с ключевыми фактами, а не помещайте все необработанные данные в контекст.
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"]}'
)Многоэтапные разговоры с использованием инструментов
Мощный подход — многоэтапный разговор, в котором модель вызывает инструменты в ответ на несколько сообщений пользователя, постепенно накапливая контекст. Всегда сохраняйте полную историю разговора, включая предыдущие вызовы инструментов и результаты, чтобы модель могла обращаться к прежним ответам и не вызывать инструменты повторно без необходимости.
Быстрая проверка
Проверьте, насколько хорошо вы поняли обработку вызовов инструментов в вашем приложении.
Итоги урока
В этом уроке вы узнали, что аргументы вызова инструмента поступают в виде строки JSON, которую необходимо разобрать с помощью json.loads(), словарь TOOL_REGISTRY сопоставляет имена функций с вызываемыми объектами, обеспечивая удобный выбор функции, а результаты возвращаются модели в сообщениях с role='tool' и соответствующими tool_call_ids. Далее мы рассмотрим случай, когда модель одновременно вызывает несколько функций, используя параллельные вызовы функций.
Часто задаваемые вопросы
Урок «Обработка вызовов инструментов в приложении» бесплатный?
Да — полный текст урока «Обработка вызовов инструментов в приложении» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Engineering Academy, подпишись на CoddyKit PRO. Курс AI Engineering Academy содержит 4 уроков всего.
Чему я научусь в уроке «Обработка вызовов инструментов в приложении»?
Обнаружьте tool_calls в поле finish_reason ответа API, извлеките имена функций и аргументы, выполните соответствующую функцию Python и отправьте результат обратно модели. Ты практикуешь AI Engineering Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Engineering Academy?
Предыдущий опыт не требуется. AI Engineering Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Обработка вызовов инструментов в приложении»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Engineering Academy?
Да. Каждый урок AI Engineering Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Определение схем функций для API
- Обработка вызовов инструментов в приложении
- Параллельный вызов функций
- Создание интерфейса базы данных на естественном языке