Обновлено: 12 мая 2026 г.

Oleg Puzanov.

Олег Пузанов Сооснователь, CSO

Машинное обучение

[Как развивать промпты узлов на OSS-моделях через GEPA]

Анализ с помощью ИИ

Получите аналитику на основе ИИ для этой технической статьи Enji:

Читать с Claude Читать с ChatGPT

В прошлой статье мы рассказали о первом шаге перехода от SOTA-моделей к опенсорсным: разбить агентный пайплайн на максимально возможное количество узлов, где каждый узел обслуживается отдельной моделью со своим промптом. Чем больше узлов — тем менее мощные модели нам нужны, а значит, тем шире выбор: qwen3-32b, gpt-oss-20b и так далее.

Теперь перед нами следующая задача: повысить качество промптинга для открытых моделей. Вариантов по сути два: перебирать промпты вручную или автоматизировать процесс. Именно здесь на помощь приходит GEPA.

GEPA опенсорсный фреймворк из экосистемы DSPy, использующий рефлексивную эволюцию для оптимизации промптов. Он работает по принципу генетического подхода с Pareto-отбором: анализирует траектории выполнения программы, рефлексирует над ошибками через LLM и строит дерево эволюционировавших промпт-кандидатов, накапливая улучшения итерация за итерацией.

Мы интегрировали GEPA в пайплайн для узлов с просевшим качеством: он берет execution traces из продакшена, анализирует текстовый фидбек (не только скалярные метрики) и за 10-15 итераций выдает промпт, который позволяет qwen3-4b работать на уровне Claude Sonnet 4.5. При этом GEPA требует значительно меньше вычислительных ресурсов, чем RL-подходы вроде GRPO, и позволяет одновременно оптимизировать несколько компонентов — например, системный промпт и few-shot-примеры в одном узле.

Приятные сюрпризы. Узлы timeframe и table_chain показали впечатляющий рост accuracy: с 0.33 и 0.52 до 0.79 и 0.91 соответственно. При этом мы спокойно пересели с Claude Haiku на qwen3-4b. Узел detect_language вообще не потребовал оптимизации — с первого запуска на qwen3-4b показал accuracy 1.0, что ожидаемо для такой прямолинейной задачи.

Проблемные узлы. agent_choice стал самым сложным: из-за комплексности выбора accuracy стартовала с 0.01, и даже после оптимизации через GEPA со схемой и SGR мы выжали лишь 0.57. Планировщик (planning) тоже преподнес сюрприз — accuracy упала с 0.089 до 0.032 при переходе на схему валидации, и пока мы не нашли способ заставить небольшие модели справляться с этим узлом. Узлы criticize_stage и process_language также требуют либо ручной валидации, либо остаются на SOTA-моделях.

Честный вывод: не каждый узел стоит переводить на небольшие опенсорсные модели. Рядом всегда должна быть большая — хотя бы на 32B параметров.

Если хотите попробовать GEPA у себя, то процесс старта достаточно простой. Устанавливаете pip install gepa и dspy-ai, готовите датасет в простом CSV формате — у нас это timeframe_dataset.csv с колонками input_message, output и input_type.

Пример данных:

input-message output
User's query: формируй мне отчет, когда Аксамай не сдавала стендап с июля ... {"needs_timeframe": false}
User's query: возьми все активности Максима и Дастана за этот месяц ... {"needs_timeframe": false}
User's query: какие проблемы были в проекте в октябре? ... {"needs_timeframe": false}
User's query: кто из сотрудников был наиболее продуктивен в сентябре? ... {"needs_timeframe": false}
User's query: who in Enji team did not have any worklogs yesterday? ... {"needs_timeframe": false}
User's query: какие задачи вчера назначил Вадим? ... {"needs_timeframe": false}
User's query: ежедневно в 09:00 хочу видеть отчет о деятельности Вадима ... {"needs_timeframe": false}
User's query: What did the team discuss in chats yesterday? ... {"needs_timeframe": false}
User's query: чем занимался Роман Панарин в первой половине сентября? ... {"needs_timeframe": false}

Это реальные продакшен-логи узла timeframe_stage, где модель определяет, нужен ли таймфрейм для ответа на запрос пользователя. Датасет содержит системный промпт, пользовательские запросы на русском и английском и эталонные ответы в формате {"needs_timeframe": true/false}. Мы взяли около сотни примеров, разбили пополам на трейн и валидацию и запустили оптимизацию с бюджетом в 100 вызовов модели. Стартовый промпт на qwen3-vl-8b-instruct-fp8 давал accuracy 0.4 — после GEPA вышли на 0.92. Почти в два с половиной раза лучше за один вечер работы.

Под капотом GEPA использует две модели: локальную для выполнения задачи и мощную reflection-модель для анализа ошибок. Мы использовали gpt-5-mini (в отдельных случаях gpt-5) как reflection LLM, а локальную модель подключили через litellm_proxy. Важный момент: прямой доступ к API провайдера не нужен — можно проксировать через собственный инфраструктурный эндпойнт.

Исходный промпт для SOTA-моделей:

You are given a single user query. Your job is to decide whether answering that query meaningfully requires knowing a specific point or range of time (a timeframe), and to return exactly one JSON object with the single key "needs_timeframe" and a boolean value (true or false). Output only the JSON object and nothing else.

Strict output format (exactly):
{"needs_timeframe": true}
or
{"needs_timeframe": false}

Оптимизированный промпт через GEPA для qwen3-vl-8b-instruct-fp8:

Task: For a given user input, decide if answering or fulfilling it requires a specific point or range of time that is not already provided by the user. Output only a JSON object of the form {"needs_timeframe": boolean} with a lowercase boolean and no extra text.

Core rule:
Return {"needs_timeframe": false} if:
The request already includes a specific time or period (absolute or relative), OR
The request can be answered meaningfully without knowing any specific time.
Return {"needs_timeframe": true} only if:
The request cannot be answered or carried out usefully without a time point or range AND the user has not provided one.

What counts as a timeframe:
Absolute times/dates: specific clock times, dates, months, years, quarters (e.g., "at 3pm", "on 2025-05-14", "in September", "Q2 2023").
Ranges: "from Monday to Wednesday", "last month", "the past year".
Relative/anchored terms: "today", "tomorrow", "yesterday", "now", "currently", "this week", "next Friday", "latest". Treat these as valid timeframes (i.e., present → false).

Typical cases:
Scheduling/reminders/bookings:
If a specific time/date (or clear relative time) is given → false.
If missing any time info needed to execute (e.g., "Set a reminder to pay rent") → true.

Time-varying data or status (weather, prices, exchange rates, news, rankings, service status):

If the request specifies a time/period or implies "current/now/latest" → false.
If the period is needed to answer (e.g., "summarize performance" without period) → true.

Historical or period-bound summaries:
If the period is given (e.g., "in September") → false.
If no period is given and one is necessary for a useful answer → true.
Questions that are about time (e.g., "When is the meeting?") do not themselves require a timeframe to understand; they are asking for one → false.
General knowledge/definitions/how-to/opinions that are time-invariant → false.
Single-word or vague queries where time is not inherently needed (e.g., "telegram") → false.

Multilingual considerations:
Detect time expressions across languages (e.g., Russian: "в сентябре", "сегодня", "завтра", "в 3 часа"; English: "on Friday at 3pm", "last year", "Q1").

Output constraints:
Output exactly one JSON object with the single key "needs_timeframe" and a lowercase boolean value.
Do not add explanations, extra fields, or surrounding text.

Guiding examples:
"Какие проблемы обсуждались в чатах проекта в сентябре?" → {"needs_timeframe": false}
"Remind me to send the quarterly report on Friday at 3pm" → {"needs_timeframe": false}
"telegram" → {"needs_timeframe": false}

If uncertain, prefer false unless a time specification is clearly required to make the response actionable or accurate.

Честно говоря, мы поначалу скептически относились к идее генетической оптимизации промптов — казалось, ручной перебор даст более предсказуемый результат. Но после десятка успешных прогонов стало очевидно: GEPA улавливает паттерны, которые человек просто не замечает в большом датасете.

Если у вас есть хотя бы 50-100 размеченных примеров для конкретного узла и желание сэкономить на inference-costs — прогоните через GEPA прежде чем оставлять дорогую SOTA-модель в продакшене.

И хозяйке на заметку: когда строите пайплайн ВСЕГДА логируйте трейсы, чтобы копить датасет, он может вам пригодиться в будущем как раз для этих случаев.

Читайте также:

ML

[SnowBall: итеративная обработка контекста, который не влезает в окно LLM]

сли оптимизации промптов недостаточно — попробуйте умнее выстраивать контекст для каждого узла. В статье о SnowBall показываем, как поэтапная обработка контекста через несколько вызовов дает лучший результат, чем один объёмный промпт.