Что такое Guided Decoding и как оно связано с JSON schema?

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

Guided Decoding — это техника ограничения генерации токенов во время декодирования LLM, чтобы гарантировать, что выходной текст соответствует заданной формальной спецификации, например JSON Schema. В отличие от пост-обработки (попытки исправить JSON после генерации), decoding|guided decoding работает на уровне вероятностного распределения токенов, маскируя недопустимые варианты. Это критически важно для Agentic RAG, где агенты должны возвращать строго структурированные вызовы функций или JSON-объекты для дальнейшей обработки.


1. Проблема: неструктурированный вывод LLM

LLM по умолчанию генерирует свободный текст. Даже если в промпте указано «верни JSON», модель может:

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

Пост-обработка (например, json.loads() с try-except) ненадёжна: она либо падает, либо требует дорогих повторных запросов к LLM. Guided Decoding решает эту проблему на этапе генерации.


2. Guided Decoding vs пост-обработка

ХарактеристикаGuided DecodingПост-обработка
Гарантия валидностиДа (100% соответствие схеме)Нет (зависит от качества LLM)
Дополнительные запросы к LLMНетЧасто требуется повторная генерация
СкоростьНемного медленнее обычной генерации (из-за маскирования)Быстро, но с риском ошибок
ГибкостьПоддерживает любые контекстно-свободные грамматикиОграничена возможностями парсинга
ИнтеграцияТребует специальных библиотекВстроена в любой код

3. Как работает Guided Decoding

Основная идея — на каждом шаге декодирования (при генерации очередного токена) вычисляется маска допустимых токенов, которая обнуляет вероятности всех токенов, не соответствующих текущему состоянию грамматики/схемы.

Процесс

  1. Задаётся формальная спецификация (JSON Schema, грамматика, Pydantic-модель).
  2. Спецификация преобразуется в конечный автомат (DFA) или контекстно-свободную грамматику.
  3. Во время генерации LLM поддерживается текущее состояние автомата.
  4. Для каждого состояния вычисляется множество разрешённых следующих токенов (с учётом токенизации BPE).
  5. Вероятности всех остальных токенов принудительно зануляются.
  6. Выбор следующего токена происходит только из разрешённого множества.

Пример для JSON Schema

{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "age": {"type": "integer"}
  },
  "required": ["name", "age"]
}

Автомат будет разрешать:

  • сначала { (начало объекта);
  • затем "name" (ключ);
  • затем :;
  • затем строку в кавычках;
  • затем , или };
  • затем "age" и т.д.

4. JSON Schema как грамматика

JSON Schema — это декларативный язык для описания структуры JSON-документов. Guided Decoding использует её как грамматику: каждое правило схемы (type, properties, items, enum и т.д.) транслируется в ограничения на последовательность токенов.

Пример трансляции

  • "type": "string" → разрешает только токены, составляющие строку в кавычках (включая экранирование).
  • "enum": ["red", "green"] → разрешает только токены, образующие одну из строк "red" или "green".
  • "minimum": 0 → разрешает только числовые токены, которые при парсинге дают число >= 0.

Библиотеки (outlines, guidance) автоматически строят конечный автомат по схеме.


5. Реализации: outlines, lmql, guidance

5.1 Outlines (рекомендуемая)

from outlines import generate, models
from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

model = models.transformers("mistralai/Mistral-7B-v0.1")
generator = generate.json(model, Person)
result = generator("Extract person from: John is 30 years old.")
# result = Person(name="John", age=30)

Outlines поддерживает Pydantic, JSON Schema, регулярные выражения.

5.2 LMQL

from lmql import LMQL

@lmql.query
def extract_person(text):
    '''lmql
    "Extract person from: {text}"
    return {"name": name, "age": age} :: {
        "name": str,
        "age": int
    }
    '''

LMQL встраивает ограничения прямо в язык запросов.

5.3 Guidance

from guidance import models, gen

model = models.LlamaCpp("model.gguf")
result = model + f'Extract person: John is 30 years old. JSON: {gen("json", pattern=r"{.*}")}'

Guidance позволяет задавать грамматику через регулярные выражения или встроенные типы.


6. Связь с Agentic RAG

В Agentic RAG агент (LLM) должен не только отвечать на вопрос, но и выполнять действия: вызывать функции, обращаться к внешним API, обновлять состояние. Все эти действия требуют строго структурированного вывода — обычно JSON с полями action, parameters и т.д.

Пример:

{
  "action": "search_documents",
  "parameters": {
    "query": "guided decoding",
    "top_k": 5
  }
}

Без Guided Decoding агент может сгенерировать "action": "search_documents" без кавычек или с опечаткой, что приведёт к ошибке выполнения. Guided Decoding гарантирует, что вывод всегда будет соответствовать заданной схеме вызова функции.


7. Производительность и ограничения

Плюсы

  • Гарантированная валидность вывода.
  • Снижение числа повторных запросов.
  • Возможность генерировать сложные вложенные структуры.

Минусы

  • Немного увеличивает время генерации (на 5–20% в зависимости от сложности схемы).
  • Требует поддержки со стороны библиотеки (не все фреймворки умеют).
  • Сложно комбинировать с другими техниками (например, beam search).

Когда использовать

  • Агенты, вызывающие функции.
  • Извлечение структурированных данных из текста.
  • Генерация конфигурационных файлов.
  • Любой сценарий, где валидность вывода критична.

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

Задача Создать агента, который по тексту вопроса определяет, какой инструмент вызвать (поиск по документам, калькулятор, погода), и возвращает JSON с параметрами вызова. Использовать Guided Decoding для гарантии корректного JSON.

Инструменты

Шаги:

  1. Определить Pydantic-модели для каждого инструмента (SearchTool, CalculatorTool, WeatherTool).
  2. Загрузить небольшую LLM (например, Qwen2.5-1.5B-Instruct).
  3. Написать функцию, которая принимает текст и возвращает JSON, используя outlines.generate.json с объединённой схемой (discriminated union).
  4. Обернуть в FastAPI-эндпоинт.
  5. Протестировать на примерах: "Какая погода в Москве?" → {"tool": "weather", "params": {"city": "Москва"}}.

Ожидаемый результат Агент всегда возвращает валидный JSON, соответствующий одной из схем. Без Guided Decoding модель иногда ошибается (пропускает кавычки, путает типы).


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

ВопросТема
212Что такое Agentic RAG и как он отличается от обычного RAG?
214Как реализовать вызов функций (function calling) в LLM?
215Какие существуют паттерны multi-agent систем?
210Как обеспечить детерминированность ответов LLM?
205Какие методы структурированного вывода вы знаете?

10. Навигация


Навигация