Как вы строите DSL (Domain-Specific Language) для вашей LLM-системы?

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

DSL (Domain-Specific Language) — это мини-язык, созданный для узкой предметной области. В контексте LLM-систем (особенно Agentic RAG) DSL даёт модели структурированный способ описывать действия, вызывать инструменты и управлять потоком данных. Построение DSL включает пять ключевых шагов: выделение core operations домена, определение типов и их композиции, создание синтаксиса (часто на базе JSON или подмножества Python), подготовку few-shot примеров и итеративное расширение на основе реального использования.


1. Что такое DSL и зачем он нужен в LLM-системах

DSL (Domain-Specific Language) — язык программирования или спецификации, нацеленный на конкретную предметную область. В отличие от общего языка (Python, Java), DSL имеет ограниченный набор конструкций, понятных специалистам домена и эффективных для LLM.

Зачем DSL в LLM-системе:

  • Структурированный вывод: LLM генерирует не свободный текст, а формальные инструкции, которые легко парсить и выполнять.
  • Контролируемость: DSL ограничивает пространство действий модели, снижая риск галлюцинаций и небезопасных операций.
  • Интеграция с инструментами: DSL может напрямую вызывать API, функции, ретриверы.
  • Интерпретируемость: сгенерированный DSL-скрипт можно логировать, анализировать и отлаживать.

В Agentic RAG DSL часто используется для описания цепочек: «сначала поищи в базе знаний, потом, если не нашёл, выполни веб-поиск, затем сгенерируй ответ».


2. Шаг 1: Выделение core operations домена

Первый шаг — определить, какие операции (actions) система должна уметь выполнять. Это атомарные действия, которые LLM может вызывать.

Примеры для Agentic RAG:

  • retrieve(query) — поиск по векторной БД
  • web_search(query) — поиск в интернете
  • generate(prompt, context) — генерация ответа LLM
  • execute_code(code) — выполнение Python-кода
  • send_email(to, subject, body) — отправка письма
  • read_document(path) — чтение файла

Методология: проанализируйте сценарии использования системы, составьте список всех действий, которые пользователь ожидает. Сгруппируйте их по частоте и важности. Начните с 5–10 операций.

Термин: Core operations — базовые, неделимые действия домена. Они должны быть ортогональны (не перекрываться) и достаточны для реализации всех сценариев.


3. Шаг 2: Определение типов и их композиция

После операций нужно описать типы данных, которыми они оперируют. Типы задают структуру входных и выходных параметров.

Пример типов для Agentic RAG:

  • Document — {id, text, metadata, score}
  • Query — {text, filters, top_k}
  • ToolCall — {name, arguments}
  • Plan — список шагов (каждый шаг — ToolCall)

Композиция: типы могут вкладываться друг в друга. Например, Plan содержит массив ToolCall, а ToolCall содержит Query или Document.

Практический совет: используйте Pydantic или dataclasses в Python для описания типов — это даёт валидацию и автодополнение.

from pydantic import BaseModel
from typing import List, Optional

class Query(BaseModel):
    text: str
    top_k: int = 5
    filters: Optional[dict] = None

class ToolCall(BaseModel):
    name: str
    arguments: dict

class Plan(BaseModel):
    steps: List[ToolCall]

4. Шаг 3: Создание синтаксиса

Синтаксис DSL — это то, как LLM будет записывать инструкции. Три популярных подхода:

ПодходПримерПлюсыМинусы
JSON{"action": "retrieve", "params": {"query": "..."}}Просто парсить, строгая структураМногословно, LLM может ошибиться в скобках
Подмножество Pythonretrieve(query="...", top_k=5)Естественно для LLM, компактноНужен безопасный exec/eval
YAMLaction: retrieve\n params:\n query: ...Читаемо, меньше скобокЧувствителен к отступам
Кастомный синтаксисRETRIEVE "..." TOP 5Максимально лаконичноТребует написания парсера

Рекомендация: для Agentic RAG чаще всего выбирают JSON — он универсален, легко валидируется через Pydantic, и LLM (особенно GPT-4, Claude) хорошо генерируют JSON при правильном prompt.

Термин: Синтаксис — правила записи конструкций DSL. Должен быть однозначным и легко генерируемым LLM.


5. Шаг 4: Few-shot примеры использования DSL

LLM не умеет «понимать» DSL без примеров. В system prompt нужно включить несколько few-shot примеров — пар «запрос пользователя → правильный DSL-скрипт».

Пример few-shot:

Пользователь: Найди последние новости про ИИ и отправь мне на почту.
DSL:
[
  {"action": "web_search", "params": {"query": "новости искусственный интеллект 2025"}},
  {"action": "generate", "params": {"prompt": "Сделай краткую сводку из результатов поиска", "context": "$1"}},
  {"action": "send_email", "params": {"to": "user@example.com", "subject": "Новости ИИ", "body": "$2"}}
]

Важно: используйте переменные ($1, $2) для передачи результатов между шагами. Это учит LLM строить цепочки.

Количество примеров: 3–5 хорошо подобранных примеров покрывают 80% типовых сценариев. Остальные доучиваются через итеративное расширение.


6. Шаг 5: Итеративное расширение

DSL не создаётся раз и навсегда. После запуска в production собирайте логи — какие DSL-скрипты сгенерировала LLM, какие из них были успешно выполнены, где были ошибки.

Процесс итерации:

  1. Анализируйте частые ошибки парсинга (например, LLM забыла закрыть скобку).
  2. Добавляйте новые операции, если пользователи запрашивают действия, не покрытые DSL.
  3. Упрощайте синтаксис, если LLM систематически генерирует невалидные конструкции.
  4. Обновляйте few-shot примеры на основе реальных успешных сценариев.

Термин: Итеративное расширение — циклический процесс улучшения DSL на основе обратной связи от LLM и пользователей.


7. Интеграция DSL с LLM: prompt engineering и парсинг

Чтобы LLM генерировала корректный DSL, нужно правильно оформить prompt:

  • System prompt: опишите синтаксис, типы, приведите 2–3 примера.
  • User prompt: запрос пользователя.
  • Output format: явно укажите, что ответ должен быть только DSL-скриптом (без пояснений).

После получения ответа от LLM:

  1. Парсинг: извлеките структуру (JSON.loads, yaml.safe_load).
  2. Валидация: проверьте типы через Pydantic.
  3. Исполнение: выполните шаги последовательно, передавая результаты.
import json
from pydantic import ValidationError

def parse_and_execute(llm_output: str, tools: dict):
    try:
        plan = json.loads(llm_output)
        # валидация через Pydantic
        validated = Plan(steps=[ToolCall(**step) for step in plan])
    except (json.JSONDecodeError, ValidationError) as e:
        return {"error": f"Invalid DSL: {e}"}
    # выполнение
    context = {}
    for step in validated.steps:
        func = tools.get(step.name)
        if not func:
            return {"error": f"Unknown tool: {step.name}"}
        result = func(**step.arguments)
        context[f"${len(context)+1}"] = result
    return context

8. Пример: DSL для агента RAG с инструментами

Рассмотрим агента, который отвечает на вопросы по документам компании и при необходимости ищет в интернете.

Core operations:

  • search_kb(query) — поиск по внутренней базе знаний
  • search_web(query) — веб-поиск
  • answer(question, context) — генерация ответа на основе контекста

Типы:

  • SearchParams — {query: str, top_k: int}
  • AnswerParams — {question: str, context: list[str]}

Синтаксис (JSON):

[
  {"action": "search_kb", "params": {"query": "отпускная политика", "top_k": 3}},
  {"action": "answer", "params": {"question": "Сколько дней отпуска?", "context": "$1"}}
]

Few-shot пример:

Пользователь: Сколько дней отпуска положено сотрудникам?
DSL:
[
  {"action": "search_kb", "params": {"query": "отпуск дни", "top_k": 5}},
  {"action": "answer", "params": {"question": "Сколько дней отпуска?", "context": "$1"}}
]

Итеративное расширение: через месяц обнаружили, что пользователи часто спрашивают про праздники — добавили операцию get_holidays(year).


9. Оценка качества DSL

Как понять, что DSL хорош? Используйте метрики:

МетрикаЧто измеряетКак считать
Синтаксическая валидностьДоля сгенерированных скриптов, которые успешно парсятся(валидные / всего) * 100%
Семантическая корректностьДоля скриптов, которые выполняются без ошибок и дают ожидаемый результатРучная оценка или автоматические тесты
Покрытие сценариевДоля запросов пользователей, для которых DSL может построить корректный планАнализ логов
Средняя длина скриптаКоличество шагов в планеСреднее арифметическое

Целевые значения: валидность > 95%, корректность > 80%, покрытие > 90%.


10. Сравнение с альтернативами

ПодходПлюсыМинусы
DSLСтруктурирован, легко парсить, контролируемТребует проектирования и обучения LLM
Натуральный языкГибко, не нужно учить синтаксисLLM может быть неоднозначной, сложно парсить
Код на PythonПолная мощь языкаОпасно (выполнение кода), LLM может генерировать небезопасный код
JSON SchemaСтандартизировано, валидацияМногословно, LLM может ошибаться в схеме

DSL — золотая середина между контролем и гибкостью.


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

Задача: Создать DSL для личного AI-помощника, который управляет задачами (todo list).

Инструменты: Python, Pydantic, OpenAI API (или любая LLM), FastAPI (для демонстрации).

Шаги:

  1. Определите core operations: create_task(title, due_date, priority), list_tasks(filter), complete_task(task_id), delete_task(task_id).
  2. Опишите типы: Task (id, title, due_date, priority, status), TaskFilter (status, priority).
  3. Выберите синтаксис: JSON.
  4. Напишите system prompt с 3–4 few-shot примерами (например, «Добавь задачу купить молоко на завтра» → [{"action": "create_task", "params": {"title": "купить молоко", "due_date": "2025-04-01", "priority": "high"}}]).
  5. Реализуйте парсер и исполнитель (функции, которые вызывают реальные методы todo-списка).
  6. Протестируйте на 10–20 запросах, соберите статистику валидности.
  7. Итеративно улучшите: добавьте операцию update_task, исправьте частые ошибки (например, LLM путает due_date и priority).

Ожидаемый результат: Работающий прототип, который принимает запрос на естественном языке, генерирует DSL, выполняет его и возвращает результат. Вы получите опыт проектирования DSL, интеграции с LLM и итеративного улучшения.


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

ВопросТема
190Проектирование архитектуры AI-агента
191Инструменты (tools) и их регистрация
192Планирование (planning) в агентах
193Память (memory) и её типы
195Безопасность и контроль выполнения агента
196Оценка качества агентных систем

Навигация