Как вы делаете synthetic data для multi-turn диалогов (агентов)?
Краткий тезис
Синтетические данные для многошаговых диалогов агентов генерируются с помощью LLM через self-chat (LLM поочерёдно играет роли пользователя и агента), сценарный подход (задаётся цель, например «купить билет», и LLM разыгрывает диалог) и интеграцию инструментов (вставка API-вызовов в диалог). Полученные данные обязательно валидируются вторым LLM или набором правил, чтобы обеспечить логичность, корректность вызовов инструментов и соответствие сценарию. Такой подход позволяет создать датасет для дообучения или тестирования агентных систем без дорогой ручной разметки.
1. Зачем нужен synthetic data для multi-turn диалогов агентов?
Synthetic data — это искусственно сгенерированные данные, имитирующие реальные взаимодействия. В контексте Agentic RAG (системы, где LLM может вызывать инструменты, делать несколько шагов и поддерживать контекст) синтетические диалоги решают несколько задач:
- Обучение/дообучение (fine-tuning): для улучшения способности агента следовать инструкциям, корректно вызывать инструменты и вести многошаговый диалог нужны тысячи примеров. evaluation|Ручная разметка таких диалогов крайне дорога.
- Тестирование и оценка: синтетические сценарии позволяют проверить, как агент справляется с типичными и граничными случаями (например, отказ инструмента, неполный ввод пользователя).
- Аугментация данных: если есть небольшой набор реальных диалогов, синтетические данные могут его дополнить, увеличив разнообразие.
Термин multi-turn диалог — это разговор, состоящий из нескольких реплик (turn), где каждая следующая реплика зависит от предыдущего контекста. Агент в таком диалоге может выполнять действия (call|вызовы API, поиск в БД) и возвращать результаты.
2. Self-chat: генерация диалогов с переключением ролей
Self-chat — самый простой метод: один LLM последовательно генерирует реплики пользователя и агента, переключая системный промпт. Пример структуры:
import openai
def generate_self_chat(scenario_prompt, turns=5):
messages = [
{"role": "system", "content": "Ты — пользователь, который хочет забронировать отель. Твои реплики должны быть естественными, иногда уточняющими."}
]
for i in range(turns):
# Генерация реплики пользователя
user_reply = openai.ChatCompletion.create(
model="gpt-4",
messages=messages + [{"role": "user", "content": "Сгенерируй следующую реплику пользователя."}]
)["choices"][0]["message"]["content"]
messages.append({"role": "user", "content": user_reply})
# Генерация реплики агента (с возможностью вызова инструментов)
agent_reply = openai.ChatCompletion.create(
model="gpt-4",
messages=messages + [{"role": "system", "content": "Ты — агент бронирования. Отвечай пользователю, при необходимости вызывай функции."}]
)["choices"][0]["message"]["content"]
messages.append({"role": "assistant", "content": agent_reply})
return messages
Ключевые моменты:
- Роли переключаются: для каждой реплики меняется system prompt, чтобы LLM понимал, кого он сейчас играет.
- Контекст сохраняется: все предыдущие реплики передаются в историю.
- Инструменты: в репликах агента можно эмулировать вызовы функций, например,
[SEARCH_HOTEL: Paris, dates].
Проблемы self-chat:
- Диалоги могут быть неестественными, повторяющимися.
- LLM может «забывать» цель сценария.
- Нет гарантии, что агент действительно использует инструменты.
3. Сценарный подход: задаём цель и контекст
Вместо свободной генерации лучше задать сценарий — высокоуровневое описание задачи пользователя и ожидаемого поведения агента. Пример сценария:
Пользователь хочет купить билет на поезд из Москвы в Санкт-Петербург на завтра. Бюджет — до 3000 рублей. Агент должен сначала проверить наличие билетов, затем предложить варианты, после чего пользователь выбирает и агент оформляет покупку.
Процесс генерации:
- Определяем сценарий (вручную или с помощью LLM).
- Генерируем «скелет» диалога: ключевые шаги (запрос → поиск → предложение → уточнение → покупка).
- Заполняем детали с помощью LLM, используя self-chat, но с жёсткой привязкой к шагам.
Преимущества:
- Контролируемая структура: диалог не уходит в сторону.
- Легко вставлять вызовы инструментов на определённых шагах.
- Можно создавать сценарии с ошибками (например, инструмент вернул пустой результат), чтобы проверить устойчивость агента.
4. Интеграция инструментов (API calls)
В Agentic RAG агент может вызывать внешние функции: поиск, отправка email, запрос к БД. В синтетических данных нужно явно отмечать такие вызовы. Есть два подхода:
- Эмуляция в тексте: агент пишет
SEARCH: Вики/Query|query, а затем[RESULT: ...]. Это просто, но не подходит для обучения моделей, которые используют Вики/OpenAI Functions|function calling (формат Вики/GPT-4o|OpenAI). - Структурированный вывод: генерация в формате JSON с полями
function_callилиtool_calls. Пример:
{
"role": "assistant",
"content": null,
"function_call": {
"name": "search_flights",
"arguments": "{\"from\": \"Moscow\", \"to\": \"SPb\", \"date\": \"2025-03-01\"}"
}
}
Как генерировать:
- В system prompt агента добавляем описание доступных функций (как в OpenAI API).
- LLM сам решает, когда вызвать функцию, и генерирует соответствующий JSON.
- Затем в диалоге появляется ответ функции (можно сгенерировать отдельно или взять из заранее подготовленного набора).
Пример генерации вызова инструмента:
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Получить погоду в городе",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
}
}
]
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": "Какая погода в Париже?"}],
tools=tools,
tool_choice="auto"
)
# В ответе будет tool_calls
5. Проверка качества (validation)
Сгенерированные диалоги могут содержать ошибки: нелогичные реплики, неправильные вызовы инструментов, нарушение сценария. Поэтому нужна валидация. Способы:
- LLM-валидатор: второй LLM (или та же модель с другим промптом) проверяет диалог по критериям:
- Логичность (реплики пользователя и агента связаны).
- Корректность вызовов инструментов (аргументы соответствуют описанию, результат используется).
- Достижение цели сценария.
- Правила (rule-based): например, проверка, что после вызова функции обязательно идёт ответ с результатом, или что количество шагов не превышает лимит.
- Человеческая разметка: для небольшой части данных, чтобы оценить качество автоматической валидации.
Пример промпта для валидатора:
Ты — эксперт по оценке диалогов агентов. Оцени следующий диалог по шкале 1-5 по трём критериям:
1. Логичность (реплики соответствуют контексту)
2. Корректность вызовов инструментов (правильные аргументы, своевременные вызовы)
3. Достижение цели (пользователь получил то, что хотел)
Диалог: {dialog}
Выведи JSON с оценками и комментариями.
6. Методы улучшения качества synthetic data
- Разнообразие: варьировать стиль пользователя (вежливый, нетерпеливый), сложность запросов, количество шагов.
- Баланс: включать как успешные, так и неуспешные сценарии (агент ошибся, инструмент недоступен).
- Избегание паттернов: если LLM часто генерирует однотипные фразы, можно добавить в промпт требование «используй разные формулировки».
- Итеративная генерация: сгенерировать черновик, проверить, отредактировать проблемные места, сгенерировать заново.
7. Инструменты и фреймворки
- LangChain — имеет модуль langchain.chains.loading для генерации диалогов, но чаще используется для построения агентов, а не для синтетики.
- DSPy — фреймворк для оптимизации промптов, можно использовать для автоматической генерации и валидации диалогов.
- OpenAI Evals — библиотека для оценки, включает генерацию тестовых данных.
- Собственные скрипты на Python с вызовами API — наиболее гибкий вариант.
8. Пример кода: генерация multi-turn диалога с инструментами
Ниже — упрощённый скрипт, который генерирует диалог из 3 шагов с вызовом функции search_flights.
import openai
import json
def generate_dialog(scenario, tools, max_turns=4):
messages = [{"role": "system", "content": f"Ты — пользователь. Сценарий: {scenario}"}]
dialog = []
for turn in range(max_turns):
# Пользователь
user_msg = openai.ChatCompletion.create(
model="gpt-4",
messages=messages + [{"role": "user", "content": "Сгенерируй естественную реплику пользователя, продолжая диалог."}]
)["choices"][0]["message"]["content"]
dialog.append({"role": "user", "content": user_msg})
messages.append({"role": "user", "content": user_msg})
# Агент
agent_response = openai.ChatCompletion.create(
model="gpt-4",
messages=messages + [{"role": "system", "content": "Ты — агент. Используй инструменты, если нужно."}],
tools=tools,
tool_choice="auto"
)
choice = agent_response["choices"][0]["message"]
if choice.get("tool_calls"):
dialog.append({"role": "assistant", "content": None, "tool_calls": choice["tool_calls"]})
# Эмулируем результат инструмента
for tc in choice["tool_calls"]:
# Здесь можно подставить реальный результат
result = {"role": "tool", "content": json.dumps({"flights": ["SU 123", "SU 456"]}), "tool_call_id": tc["id"]}
dialog.append(result)
messages.append(result)
else:
dialog.append({"role": "assistant", "content": choice["content"]})
messages.append({"role": "assistant", "content": choice["content"]})
return dialog
# Пример использования
tools = [{
"type": "function",
"function": {
"name": "search_flights",
"parameters": {"type": "object", "properties": {"from": {"type": "string"}, "to": {"type": "string"}}, "required": ["from", "to"]}
}
}]
dialog = generate_dialog("Пользователь хочет найти рейс из Москвы в Лондон", tools)
print(json.dumps(dialog, indent=2))
9. Ограничения и риски
- Галлюцинации: LLM может генерировать несуществующие инструменты или некорректные аргументы.
- Перекос (bias): если сценарии однотипны, модель будет плохо обобщать.
- Стоимость: генерация тысяч диалогов через API может быть дорогой.
- Качество валидации: LLM-валидатор тоже может ошибаться, поэтому нужна человеческая выборка.
Пет-проект для закрепления
Задача: Создать датасет из 100 синтетических multi-turn диалогов для агента, который помогает пользователю бронировать отели. Агент должен уметь искать отели, уточнять детали и бронировать.
Инструменты: Python, OpenAI API (или локальная LLM), JSON.
Шаги:
- Определить 5 сценариев (например, «бюджетный отель в центре», «отель с бассейном», «отмена брони»).
- Для каждого сценария написать шаблон диалога (список шагов).
- Использовать self-chat с переключением ролей, добавив инструменты
search_hotels,book_hotel,cancel_booking. - После генерации прогнать каждый диалог через LLM-валидатор с критериями: логичность, корректность вызовов, достижение цели.
- Отфильтровать диалоги с оценкой ниже 4/5.
- Сохранить результат в формате, пригодном для fine-tuning (например, в формате OpenAI messages).
Ожидаемый результат: JSON-файл с 50-70 качественными диалогами, готовыми для обучения агента.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 691 | Как спроектировать агента с инструментами? |
| 692 | Как оценивать качество работы агента? |
| 693 | Как дообучать LLM для агентных задач? |
| 694 | Как деплоить агента в production? |
| 696 | Как тестировать агента на граничные случаи? |
Навигация
- Предыдущий: 694
- Следующий: 696
- Индекс: 00. Индекс разборов