English translation is not available yet. Showing Russian content.

Как вы fine-tune модель для функции "вызов внешнего API"?

Краткий тезис

Fine-tuning для вызова внешнего API — это обучение языковой модели генерировать структурированные вызовы функций (function calls]]) вместо обычного текстового ответа. Для этого создаётся специальный датасет, где на инструкцию пользователя модель должна выдать команду вида <function_call>имя_функции(параметры)</function_call>. После дообучения модель способна понимать, когда нужно обратиться к внешнему сервису, а когда ответить самой. Это основа для построения AI-агентов, которые могут взаимодействовать с реальными системами.


1. Зачем fine-tune для вызова API?

Стандартная языковая модель обучена генерировать связный текст, но не умеет «понимать», что ответ должен быть вызовом функции. Если просто попросить модель «скажи погоду в Москве», она может написать «Погода в Москве сегодня +15°C», хотя у неё нет доступа к реальным данным. Function calling (вызов функций) решает эту проблему: модель учится распознавать намерение пользователя и формировать команду для внешнего API.

Почему fine-tuning, а не промпт-инжиниринг?

  • Промпт может работать для простых случаев, но плохо масштабируется на множество функций и сложные параметры.
  • Fine-tuning даёт более надёжное и детерминированное поведение, модель реже «выдумывает» вызовы.
  • Модель запоминает точный формат функции и правила заполнения параметров.

2. Формат датасета для function calling

Датасет должен состоять из пар «инструкция пользователя → вызов функции». Самый распространённый формат — обернуть вызов в специальные теги или JSON.

Пример:

Инструкция: "Какая погода в Москве?"
Ассистент: <function_call>get_weather(city="Москва")</function_call>

Или в формате JSON:

Инструкция: "Отправь письмо Ивану на почту с темой 'Встреча' и текстом 'Привет, Иван!'"
Ассистент: {"function": "send_email", "parameters": {"to": "ivan@example.com", "subject": "Встреча", "body": "Привет, Иван!"}}

Термин «слоты» (slots) — параметры функции, которые модель должна заполнить. В датасете нужно показать разные комбинации слотов: обязательные, опциональные, с дефолтными значениями.

Для каждого примера можно добавить контекст разговора (историю сообщений), если вызов зависит от предыдущего общения.

ИнструкцияОжидаемый вызов
"Закажи пиццу с пепперони"place_order(item="пицца с пепперони", quantity=1)
"Мне нужно две пиццы, одну с грибами, другую с курицей"place_order(item="пицца с грибами", quantity=1) + place_order(item="пицца с курицей", quantity=1)
"Отмени заказ #123"cancel_order(order_id=123)

Совет для повышения устойчивости модели добавляйте примеры, где функция не должна вызываться (например, вопрос о личных предпочтениях). Модель должна отвечать текстом, а не вызовом.


3. Обучаем модель генерировать function call

Шаги обучения

  1. Выбор базовой модели Лучше всего подходят модели, уже умеющие следовать инструкциям (instruct-версии), например Llama‑3‑Instruct, Mistral‑Instruct, Qwen‑Instruct.
  2. Подготовка датасета в формате чата Каждый пример — это список сообщений:
    • Роль user: инструкция.
    • Роль assistant: вызов функции в заданном формате.
  3. Обучение с учителем (SFT). Используется стандартный loss (cross-entropy) только на токенах ответа ассистента. Функциональный вызов маскируется как обычный текст.
  4. Добавление системного сообщения В системном сообщении можно описать доступные функции, их параметры и правила. Это помогает модели, но не заменяет fine-tuning.

Пример кода обучения (PyTorch + transformers):

from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments

model_name = "meta-llama/Llama-3.2-1B-Instruct"  # или другая модель
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

# Подготовка датасета (пример одного элемента)
def format_example(user_input, api_call):
    return f"<|user|>\n{user_input}\n<|assistant|>\n{api_call}"

# Токенизация, даталоадер, аргументы обучения
training_args = TrainingArguments(
    output_dir="./function-calling-model",
    per_device_train_batch_size=4,
    num_train_epochs=3,
    learning_rate=2e-5,
    save_steps=500,
)
trainer = Trainer(model=model, args=training_args, train_dataset=dataset)
trainer.train()

Термин «контролируемое обучение» (supervised fine-tuning, SFT) — модель учится на примерах с правильными ответами. После обучения она будет выдавать вызовы в том же формате, что в датасете.


4. Обработка результата вызова API и многошаговый диалог

После того как модель сгенерировала вызов, нужно:

  • Парсить теги или JSON (например, с помощью регулярных выражений или json.loads).
  • Выполнить внешний API (например, запрос к погодному сервису).
  • Вернуть результат обратно в разговор как системное сообщение или сообщение пользователя.

Пример пайплайна для одного шага

response = model.generate(prompt)
if "<function_call>" in response:
    # Извлекаем имя функции и параметры
    match = re.search(r'<function_call>(.*?)</function_call>', response)
    call_str = match.group(1)
    function_name, params = parse_call(call_str)
    api_result = call_external_api(function_name, params)
    # Возвращаем результат в контекст
    new_prompt = prompt + f"\n<|system|>\nРезультат вызова {function_name}: {api_result}\n<|user|>\nПродолжай."
else:
    # модель ответила текстом — готово
    print(response)

Термин «multi-turn» (многошаговость) — модель может последовательно вызывать несколько API, опираясь на предыдущие результаты. Fine-tuning поддерживает это, если в датасете есть примеры продолжения после получения результата.


5. Пайплайн: от запроса к ответу через API

Полный цикл работы дообученной модели:

  1. Пользователь вводит запрос.
  2. Система формирует промпт (системное сообщение + история).
  3. Модель генерирует ответ (текст или вызов).
  4. Если ответ содержит вызов:
    • Извлечь имя функции и параметры.
    • Проверить безопасность (валидация параметров, контроль доступа к API).
    • Выполнить реальный вызов (например, к REST API или библиотеке).
    • Результат (успех/ошибка) передаётся обратно в контекст.
    • Модель генерирует следующий ответ (завершающий, или следующий вызов).
  5. Если ответ — обычный текст, отдать его пользователю.

Графическое представление (текстовая схема):

[Пользователь] -> Промпт -> [Модель] -> <function_call> -> [Парсер] -> [API] -> Результат -> [Контекст] -> [Модель] -> Текст (ответ)

6. Инструменты и фреймворки

ИнструментОписаниеОсобенности
LangChain Tool CallingАбстракция над вызовами функций. Позволяет определить Tool с именем, описанием, схемой параметров. Модель генерирует JSON с tool_calls.Встроен в ChatOpenAI, ChatAnthropic. Не требует отдельного fine-tuning, если модель уже поддерживает tool calling.
LlamaIndex Function CallingАналогично LangChain, интеграция с LLM и индексами.Поддерживает автогенерацию схем из Python-функций через декоратор @tool.
OpenAI Function Calling (fine-tuning)OpenAI позволяет дообучать модели (GPT-3.5, GPT-4) с функциональными вызовами. Датасет должен включать вызовы в формате messages с role: function.Закрытый API, нет контроля над обучением.
AutoGen (Microsoft)Фреймворк для многолетних агентов. Модели могут вызывать функции друг у друга.Хорош для сложных multi-agent сценариев.
CrewAI / smolagentsЛёгкие библиотеки для AI-агентов с tool calling.Открытый код, простота интеграции.

Термин «tool calling» — синоним function calling, часто используется в современных LLM API.

Если модель не поддерживает нативно tool calling (например, Mistral 7B), fine-tuning — единственный способ научить её этому.


7. Особенности и подводные камни

  • Безопасность Модель может генерировать опасные вызовы (удаление данных, отправка писем). Нужно ограничивать набор функций и валидировать параметры на стороне сервера.
  • Переобучение на конкретные API Если датасет содержит только один API для погоды, модель не сможет вызывать другие сервисы. Лучше использовать абстрактные функции с разными именами и параметрами.
  • Обработка ошибок API Если внешний сервис вернул ошибку, модель должна уметь переспросить пользователя или предложить альтернативу. В датасет нужно включать примеры с ошибочными вызовами и корректной реакцией.
  • Формат тегов Выбор разделителей (теги, JSON, маркдаун) влияет на стабильность генерации. Рекомендуется тестировать несколько вариантов на небольшом наборе.
  • Смешанные сценарии Иногда модель должна сначала ответить текстом, потом вызвать API, потом снова текстом. В датасете должны быть примеры таких последовательностей.
  • Размер датасета Для простых 1-2 функций достаточно 200–500 примеров. Для 10+ функций нужно 1000+.

8. Пет-проект для закрепления

Задача создать чат-бота, который умеет искать информацию в интернете через API (например, SerpAPI или DuckDuckGo Instant Answer API) и отвечать на вопросы на основе результатов.

Инструменты

  • Модель: Llama‑3.2‑1B‑Instruct (или любая другая instruct-модель из Hugging Face)
  • Фреймворк: Hugging Face Transformers + TRL (SFTTrainer)
  • API: SerpAPI (ключ бесплатно, 100 запросов/месяц)
  • Датасет: 300–500 пар вопрос-поисковый запрос (например, "Какая столица Франции?" -> <function_call>search(query="столица Франции")</function_call>)

Шаги:

  1. Собрать датасет вопросов, которые требуют интернета (текущие события, погода, новости). Для каждого вопроса вручную написать корректный вызов search.
  2. Загрузить базовую модель, токенизировать датасет, выполнить SFT на 3 эпохи.
  3. Написать пайплайн: модель отвечает -> парсинг <function_call> -> вызов SerpAPI -> получение результатов -> отправка их обратно в контекст -> модель генерирует финальный ответ.
  4. Протестировать на новых вопросах.

Ожидаемый результат бот, который вместо "Я не знаю" делает поиск и даёт ответ с актуальными данными. При этом модель не должна вызывать поиск для вопросов вроде "Что такое 2+2?", а отвечать сама.

Дополнительно добавить функцию get_current_time для проверки даты, чтобы модель научилась выбирать между разными вызовами.


9. Связь с другими вопросами

ВопросТемаСвязь
2RAG и внешние данныеFine-tuning для вызова API — альтернативный способ интеграции внешних данных (когда RAG не подходит из-за динамики).
20Prompt engineering vs fine-tuningСравнение подходов: промпт для function calling (меньше контроля) против fine-tuning (больше надёжности).
24Как обучить модель на собственном датасетеТехника SFT, применимая и для function calling.
37Fine-tuning для чат-ботаЧастный случай: чат-бот с вызовом API (погода, поиск).
39Проблемы безопасности при fine-tuningБезопасность function calling (защита от инъекций).
45AI-агенты и tool useFunction calling — основа для агентов, см. также вопросы про ReAct, планирование.

Навигация