中文翻译暂不可用,显示俄语原文。
Как вы строите 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 может ошибиться в скобках |
| Подмножество Python | retrieve(query="...", top_k=5) | Естественно для LLM, компактно | Нужен безопасный exec/eval |
| YAML | action: 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, какие из них были успешно выполнены, где были ошибки.
Процесс итерации:
- Анализируйте частые ошибки парсинга (например, LLM забыла закрыть скобку).
- Добавляйте новые операции, если пользователи запрашивают действия, не покрытые DSL.
- Упрощайте синтаксис, если LLM систематически генерирует невалидные конструкции.
- Обновляйте few-shot примеры на основе реальных успешных сценариев.
Термин: Итеративное расширение — циклический процесс улучшения DSL на основе обратной связи от LLM и пользователей.
7. Интеграция DSL с LLM: prompt engineering и парсинг
Чтобы LLM генерировала корректный DSL, нужно правильно оформить prompt:
- System prompt: опишите синтаксис, типы, приведите 2–3 примера.
- User prompt: запрос пользователя.
- Output format: явно укажите, что ответ должен быть только DSL-скриптом (без пояснений).
После получения ответа от LLM:
- Парсинг: извлеките структуру (JSON.loads, yaml.safe_load).
- Валидация: проверьте типы через Pydantic.
- Исполнение: выполните шаги последовательно, передавая результаты.
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)— генерация ответа на основе контекста
Типы:
Синтаксис (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 (для демонстрации).
Шаги:
- Определите core operations:
create_task(title, due_date, priority),list_tasks(filter),complete_task(task_id),delete_task(task_id). - Опишите типы:
Task(id, title, due_date, priority, status),TaskFilter(status, priority). - Выберите синтаксис: JSON.
- Напишите system prompt с 3–4 few-shot примерами (например, «Добавь задачу купить молоко на завтра» →
[{"action": "create_task", "params": {"title": "купить молоко", "due_date": "2025-04-01", "priority": "high"}}]). - Реализуйте парсер и исполнитель (функции, которые вызывают реальные методы todo-списка).
- Протестируйте на 10–20 запросах, соберите статистику валидности.
- Итеративно улучшите: добавьте операцию
update_task, исправьте частые ошибки (например, LLM путаетdue_dateиpriority).
Ожидаемый результат: Работающий прототип, который принимает запрос на естественном языке, генерирует DSL, выполняет его и возвращает результат. Вы получите опыт проектирования DSL, интеграции с LLM и итеративного улучшения.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 190 | Проектирование архитектуры AI-агента |
| 191 | Инструменты (tools) и их регистрация |
| 192 | Планирование (planning) в агентах |
| 193 | Память (memory) и её типы |
| 195 | Безопасность и контроль выполнения агента |
| 196 | Оценка качества агентных систем |
Навигация
- Предыдущий: 193
- Следующий: 195
- Индекс: 00. Индекс разборов