TL;DR
SHERPA — фреймворк, который структурирует работу LLM через конечные автоматы (state machines): явные состояния, переходы между ними, и сохранение промежуточных результатов. Вместо одного большого промпта задача разбивается на цепочку шагов, где каждый шаг — это состояние с чёткой целью, а переходы определяются правилами или решениями LLM.
Проблема: LLM теряет фокус при выполнении сложных многошаговых задач. Модель пытается одновременно (1) понять задачу, (2) спланировать решение, (3) выполнить каждый подшаг, (4) проверить результат. Это приводит к ошибкам, особенно когда есть устоявшиеся лучшие практики (test-driven development, итеративная проверка, routing по типу задачи), но они не отражены в промпте. Модель генерирует всё за раз, не сохраняя промежуточные результаты и не возвращаясь к предыдущим шагам при необходимости.
Решение: Декомпозировать задачу в явную структуру состояний. Каждое состояние выполняет одну подзадачу, результат сохраняется в belief (память агента), переход к следующему состоянию определяется policy (правило или LLM). Например, для генерации кода: Start → Generate Tests → Generate Code → Run Tests → (если не прошло) Return to Code. Для вопросов: Start → Classify Question Type → (routing) Extract Objects OR Query Directly → Answer. Структура делает процесс прозрачным и управляемым, а модель на каждом шаге фокусируется на одной задаче.
Схема метода
Три компонента:
КОМПОНЕНТ 1: State Machine — определяет структуру
Состояния: Start, Step1, Step2, Inspect, End
Переходы: Step1 → Step2 (если условие), Step2 → Inspect, Inspect → Step1 (если нужна доработка)
Действия: при переходе вызывается LLM или внешний инструмент
КОМПОНЕНТ 2: Belief — память агента
Trajectory Store: [Start → Step1 → Step2 → Inspect → Step1 → Step2 → End]
Execution Log: {step1_output: "...", step2_output: "...", key_data: {...}}
КОМПОНЕНТ 3: Policy — выбор перехода
Rule-based: if condition then transition_A else transition_B
LLM-based: "Given current state and history, select next transition"
Цикл выполнения:
1. Получить event (от юзера или policy)
2. Выполнить transition → выполнить action
3. Обновить belief (добавить результат action)
4. Policy выбирает следующий transition
5. Повторить до End или ожидания внешнего input
Пример применения
Задача: Проанализировать бизнес-идею стартапа (например, "сервис аренды дронов для съёмки событий") по модели Shark Tank — продукт, рынок, команда, финансы.
Промпт:
Ты — аналитик стартапов. Проанализируй бизнес-идею по структуре:
ЭТАП 1: Классификация
Определи тип идеи (продукт, услуга, платформа, hardware) и основной сегмент рынка.
Сохрани: {тип_идеи}, {сегмент_рынка}
ЭТАП 2: Анализ продукта
Оцени: уникальность, болевую точку, конкурентные преимущества.
Используй данные из ЭТАПА 1.
Сохрани: {оценка_продукта}, {ключевые_риски}
ЭТАП 3: Анализ рынка
Оцени: размер рынка, темп роста, барьеры входа.
Используй {сегмент_рынка} из ЭТАПА 1.
Сохрани: {оценка_рынка}
ЭТАП 4: Проверка согласованности
Проверь: согласуются ли выводы ЭТАПОВ 2 и 3?
Если НЕТ → вернись к ЭТАПУ 2 с уточнением
Если ДА → переход к ЭТАПУ 5
ЭТАП 5: Итоговая оценка
Синтез всех этапов: {оценка_продукта}, {оценка_рынка}, {ключевые_риски}
Вывод: инвестировать / отклонить / доработать, аргументы.
---
Идея для анализа:
{описание стартапа}
Выполни ЭТАП 1.
Результат:
Модель пройдёт через 5 этапов пошагово. На ЭТАПЕ 1 определит тип и сегмент, сохранит. На ЭТАПЕ 2 использует эти данные для анализа продукта. На ЭТАПЕ 4 проверит согласованность — если найдёт противоречие между оценкой продукта и рынка, вернётся к ЭТАПУ 2. В итоге выдаст структурированную оценку с аргументами, где каждый компонент проанализирован отдельно и проверен на согласованность.
Почему это работает
Слабость LLM: Модель плохо держит несколько подзадач одновременно в контексте. При сложной задаче ("проанализируй стартап") она пытается думать обо всём сразу — продукт, рынок, команда, финансы — и теряет детали. Промежуточные выводы не фиксируются явно, поэтому последующие шаги не опираются на предыдущие структурированно. Это как пытаться решать уравнение с 5 переменными в уме, не записывая.
Сильная сторона LLM: Модель отлично следует явным инструкциям и работает с структурированным контекстом. Если дать чёткое указание "сейчас делай только X, сохрани результат, используй его на следующем шаге", модель сфокусируется. Явная структура {ключ: значение} для промежуточных данных помогает модели "помнить" что уже сделано.
Как метод использует это: Декомпозиция + явная память + условные переходы. Задача разбита на изолированные шаги, каждый с одной целью. Результаты каждого шага сохраняются явно (Сохрани: {оценка_продукта}), что создаёт контекст для следующих шагов. Переходы управляются условиями ("если согласуется → далее, если нет → вернись"), что даёт возможность итеративной доработки. Это структурирует хаос многошаговой задачи в управляемый процесс.
Рычаги управления:
- Число шагов — уменьши для простых задач (3 вместо 5), экономя токены
- Условия перехода — замени "согласованность" на "полнота данных" под свою задачу
- Сохраняемые данные — укажи что именно фиксировать (
{риски},{преимущества}) для нужного фокуса - Итеративность — убери шаг проверки если не нужна доработка, или добавь циклы с лимитом попыток
Шаблон промпта
Ты — {роль}. Выполни задачу по структуре:
ЭТАП 1: {название_этапа_1}
{описание_что_делать}
Сохрани: {ключ_1}, {ключ_2}
ЭТАП 2: {название_этапа_2}
{описание_что_делать}
Используй данные из ЭТАПА 1: {ключ_1}
Сохрани: {ключ_3}
ЭТАП 3: Проверка
Проверь: {условие_проверки}
Если НЕТ → вернись к ЭТАПУ {номер} с уточнением
Если ДА → переход к ЭТАПУ 4
ЭТАП 4: Итоговый результат
Синтез: {ключ_1}, {ключ_2}, {ключ_3}
Формат: {требуемый_формат}
---
Задача:
{описание_задачи}
Выполни ЭТАП 1.
Как заполнять:
{роль}— кто выполняет (аналитик, редактор, юрист){название_этапа}— что делается на этом шаге (Классификация, Анализ, Проверка){ключ}— конкретные данные для сохранения (тип_задачи, риски, оценка){условие_проверки}— критерий для перехода или возврата (согласованность, полнота, логичность){описание_задачи}— конкретная задача для выполнения
🚀 Быстрый старт — вставь в чат:
Вот шаблон для многошагового анализа с проверкой. Адаптируй под мою задачу: [твоя задача].
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит про этапы задачи и данные для сохранения — это нужно чтобы выделить ключевые компоненты и определить переходы между шагами. Она возьмёт паттерн "шаги → сохранить → проверить → итог" и адаптирует под конкретную задачу.
Ограничения
⚠️ Сложность структуры: Нужно заранее знать как разбить задачу на шаги. Для задач без устоявшихся best practices (например, креативное письмо) структура неочевидна — метод менее эффективен.
⚠️ Стоимость выполнения: Больше шагов = больше вызовов LLM = выше стоимость. В исследовании test-driven подход требовал на ~50% меньше вызовов чем agent coder, но всё равно дороже чем один промпт.
⚠️ Overhead для простых задач: Если задача решается напрямую (например, "переведи текст"), добавление state machine только усложняет и замедляет.
⚠️ Эффект размера модели: Для крупных LLM (GPT-4o, Qwen2.5-72B) структура даёт меньший прирост — они уже справляются хорошо. Максимальный эффект на малых LLM (GPT-4o Mini, Qwen2.5-7B), где декомпозиция критична.
Как исследовали
Команда проверила гипотезу на трёх разных задачах с разной степенью формализации best practices:
- Генерация кода (HumanEval, 164 задачи) — есть чёткая практика: test-driven development
- Генерация имён классов (8 доменных моделей, 135 классов) — есть паттерны моделирования, но мало данных для обучения
- Ответы на вопросы (CLEVR, 100 вопросов) — нет устоявшихся практик
Для каждой задачи создали несколько вариантов state machine с разной структурой. Например, для кода: (a) test-driven — сначала тесты, потом код; (b) agent coder — сразу код и тесты одновременно. Для вопросов: (a) routing — классифицировать тип вопроса, потом специфический метод; (b) ReAct — последовательные операции над графом; (c) planning — заданная последовательность операций.
Сравнили с Direct подходом (zero-shot для кода, one-shot для классов, chain-of-thought для вопросов). Тестили на 5 LLM: GPT-4o Mini, GPT-4o, Qwen2.5-7B, Qwen2.5-72B, плюс специализированные (Qwen2.5-Coder для кода, Llama3.1-70B для остального). Измеряли качество (Pass@1, F1-score, Accuracy) и число вызовов LLM.
Результаты показали логичный паттерн: state machine улучшила производительность в 12 из 15 случаев. Максимальный эффект там где есть best practices (код +3.25 п.п., классы +6.58 п.п.) и на малых моделях (которым нужна структура). Крупные модели (GPT-4o, Qwen2.5-72B) справлялись хорошо и без структуры. Удивительно: для вопросов routing дал всего +2.56 п.п. — подтвердило, что без чётких практик структура менее критична.
Инсайт для практики: Если задача имеет формализуемые шаги (как TDD, итеративная проверка, routing) — структура даёт измеримый прирост. Если задача творческая или ad-hoc — прирост минимален. Это значит: для рутинных бизнес-задач с устоявшимися процессами (анализ, проверка, routing) принципы SHERPA работают. Для креатива — лучше полагаться на способности модели.
Оригинал из исследования
Test-driven state machine для генерации кода:
States:
- Start (initial)
- Test Cases Generated
- Function Generated
- End (final)
Transitions:
- Start → Test Cases Generated: generate_tests(problem_description)
- Test Cases Generated → Function Generated: generate_function(problem_description, test_cases)
- Function Generated → End: if all_tests_passed(function, test_cases)
- Function Generated → Test Cases Generated: if NOT all_tests_passed AND attempts < budget
Контекст: Исследователи моделировали процесс test-driven development — сначала пишутся тесты, описывающие желаемое поведение, потом функция, которая эти тесты проходит. Если функция не проходит все тесты, процесс возвращается к состоянию Test Cases Generated для новой попытки (с лимитом попыток). Это улучшило Pass@1 на 3-5 п.п. для большинства моделей по сравнению с прямой генерацией.
Routing state machine для scene graph QA:
States:
- Start (initial)
- QuestionClassification
- ObjectExtraction (for counting questions)
- DirectAnswer (for judging/querying questions)
- End (final)
Transitions:
- Start → QuestionClassification: classify_question(question, scene_graph)
- QuestionClassification → ObjectExtraction: if question_type == "counting"
- QuestionClassification → DirectAnswer: if question_type in ["judging", "querying"]
Actions:
- On entering ObjectExtraction: extract_objects(scene_graph, question_criteria)
- On exiting ObjectExtraction: count_objects(extracted_objects)
- On entering DirectAnswer: generate_answer(question, scene_graph)
Контекст: Для вопросов на подсчёт (counting) критично сначала извлечь нужные объекты, потом посчитать детерминированно. LLM часто ошибается считая напрямую. Routing решает: для counting — extraction + детерминированный подсчёт, для остальных — прямой ответ. Accuracy +2.56 п.п. для малых моделей, 0 для крупных (они справляются и так).
Адаптации и экстраполяции
💡 Адаптация для юридической проверки договора:
Структура: Classify Type → Extract Key Terms → Check Risks by Type → Inspect → Finalize
ЭТАП 1: Классификация
Определи тип договора (поставка, услуги, аренда, NDA).
Сохрани: {тип_договора}
ЭТАП 2: Извлечение ключевых условий
Извлеки: стороны, предмет, сроки, цена, ответственность, условия расторжения.
Используй {тип_договора} для фокуса на специфичных пунктах.
Сохрани: {ключевые_условия}
ЭТАП 3: Проверка рисков по типу
Для {тип_договора} проверь типовые риски:
- Поставка: сроки, штрафы, приёмка
- Услуги: критерии качества, акты выполненных работ
- Аренда: ремонт, коммуналка, субаренда
Сохрани: {найденные_риски}
ЭТАП 4: Инспекция согласованности
Проверь: согласуются ли {ключевые_условия} с {найденные_риски}?
Есть ли противоречия в пунктах?
Если ДА → вернись к ЭТАПУ 2 для уточнения
Если НЕТ → ЭТАП 5
ЭТАП 5: Итоговое заключение
Синтез: {тип_договора}, {ключевые_условия}, {найденные_риски}
Формат: критичные риски → рекомендации по доработке
Договор:
{текст_договора}
🔧 Техника: Прозрачность шагов для отладки
В оригинале промпт не выводит промежуточные шаги. Для отладки или обучения полезно видеть что происходит на каждом этапе.
Измени шаблон:
ЭТАП 1: {название}
{действие}
Сохрани: {ключ}
>>> ВЫВОД: Покажи что сохранено в {ключ} перед переходом к ЭТАПУ 2
Добавь >>> ВЫВОД: ... после каждого этапа. Это заставит модель явно показать промежуточные данные, что помогает понять где теряется фокус или появляются ошибки.
💡 Комбинация: Routing + Chain-of-Thought для сложных вопросов
Для задач где нужен и routing (выбор метода по типу), и пошаговое рассуждение (для каждого типа), комбинируй подходы:
ЭТАП 1: Определение типа задачи
Определи: {аналитическая | креативная | техническая}
Сохрани: {тип_задачи}
ЭТАП 2: Выбор метода
Если аналитическая → ЭТАП 3A (декомпозиция)
Если креативная → ЭТАП 3B (brainstorming)
Если техническая → ЭТАП 3C (алгоритм)
ЭТАП 3A: Аналитика через декомпозицию
Шаг 1: Разбей вопрос на компоненты
Шаг 2: Для каждого компонента — цепочка рассуждений:
"Известно: ... → Следовательно: ... → Вывод: ..."
Шаг 3: Синтез выводов
ЭТАП 3B: Креатив через brainstorming
{другая структура для креатива}
[...]
ЭТАП 4: Финализация
Здесь routing выбирает ветку, внутри каждой ветки — свой метод рассуждения. Это универсальнее чем "сделай за раз", но не перегружает модель выбором на каждом шаге.
Ресурсы
Работа: SHERPA: A Model-Driven Framework for Large Language Model Execution (2025)
Авторы: Boqi Chen, Kua Chen, José Antonio Hernández López, Gunter Mussbacher, Dániel Varró, Amir Feizpour
Организации: McGill University (Canada), University of Murcia (Spain), Linköping University (Sweden), Aggregate Intellect Inc. (Canada)
Репозиторий: https://zenodo.org/records/16338136
