Агенты и вызов инструментов: как строить function-calling
Модель сама решает, когда дёрнуть вашу функцию. Разбираю схему tools, цикл выполнения и типичные грабли с параллельными вызовами.
Сергей Панов
Агенты и вызов инструментов: как строить function-calling
Агент — это модель, которой дали инструменты и цикл. Она сама решает, что нужно вызвать: посмотреть погоду, сходить в базу, отправить письмо. Вы описываете функции, модель возвращает намерение их вызвать, вы исполняете и отдаёте результат обратно.
Описываем инструменты
Инструмент — это JSON Schema. Чем точнее описание, тем реже модель ошибается с аргументами.
tools = [{ "type": "function", "function": { "name": "get_order_status", "description": "Возвращает статус заказа по его номеру", "parameters": { "type": "object", "properties": { "order_id": {"type": "string", "description": "Номер заказа, например A-1024"} }, "required": ["order_id"], }, },}]Цикл выполнения
Ключевое, что упускают: один запрос к модели — это не весь разговор. Модель может попросить инструмент, получить ответ и попросить следующий. Нужен цикл.
messages = [{"role": "user", "content": "Где мой заказ A-1024?"}]while True: resp = client.chat.completions.create( model="qwen-pro", messages=messages, tools=tools, ) msg = resp.choices[0].message messages.append(msg) if not msg.tool_calls: print(msg.content) break for call in msg.tool_calls: args = json.loads(call.function.arguments) result = run_tool(call.function.name, args) messages.append({ "role": "tool", "tool_call_id": call.id, "content": json.dumps(result, ensure_ascii=False), })Выбор модели
Function-calling — не для всех моделей одинаково. Нужна та, что обучена под tools и держит структуру:
qwen-pro,deepseek-pro— уверенно выбирают инструменты и не выдумывают несуществующие.- Flash-модели тоже умеют вызывать функции, но на длинных цепочках чаще путаются в порядке шагов. Для простого «один вызов — один ответ» их хватает.
- Модель с контекстом 128K важна, когда история диалога с результатами инструментов растёт.
Грабли
- Параллельные вызовы. Модель может вернуть несколько
tool_callsза раз. Исполняйте все и добавляйте по одномуtool-сообщению на каждыйtool_call_id. Пропустите один — следующий запрос упадёт с ошибкой рассинхрона. - Бесконечный цикл. Поставьте лимит итераций (8–10). Иногда модель зацикливается, вызывая один инструмент снова и снова.
- Мусор в аргументах. Валидируйте
argumentsдо исполнения. Модель иногда шлётorder_id: "мой заказ"вместо номера. Возвращайте ей осмысленную ошибку вtool-сообщении — она исправится. - Побочные эффекты. Инструменты, которые что-то меняют (отправить платёж, удалить запись), оборачивайте в подтверждение. Не давайте агенту право на необратимое без человека в цикле.
Совет по проектированию
Делайте инструменты крупными и осмысленными, а не «одна функция — один SQL». Вместо select_users, join_orders, filter_by_date дайте один find_customer_orders(customer, period). Модель хуже собирает сложные цепочки из мелких примитивов, чем дёргает один понятный инструмент. И описание — на языке домена, а не на языке вашей БД.