# ГЛАВА 7. РАЗМЕТКА, ТЕГИ И АРХИТЕКТУРА СЛОЖНОГО ПРОМПТА

---

Представьте, что вы получили на работе коробку, в которую свалены вперемешку: задание от начальника, данные для анализа, пример готового отчёта и список ограничений. Вы роетесь, путаете пример с заданием, пропускаете ограничения. А теперь представьте **картотечный шкаф**: на каждом ящике — подпись. «Задание». «Данные». «Примеры». «Ограничения». Вы выдвигаете нужный ящик — и сразу понимаете, что внутри.

XML-теги в промптах — это **подписанные ящики** такого шкафа. А архитектура промпта — это **чертёж здания**: где фундамент (роль), где несущие стены (инструкции), где окна (данные), где крыша (формат вывода). Без чертежа получается сарай. С чертежом — надёжная конструкция.

> **Для тех, кто не знаком с XML.** XML (eXtensible Markup Language) — язык разметки, похожий на HTML. Его суть — **парные теги**: открывающий `<tag>` и закрывающий `</tag>`, между которыми находится содержимое. Например:
> ```xml
> <задание>Напиши отчёт</задание>
> <данные>Таблица продаж за Q1</данные>
> ```
> Теги можно называть как угодно — `<instruction>`, `<data>`, `<my_cool_block>` — главное, что они создают **чёткие границы** между секциями. В отличие от Markdown-заголовков, у XML-тегов есть закрывающая пара, поэтому модель точно знает, где секция заканчивается.

---

## 7.1. Почему структурированная разметка помогает модели

### Механизм действия

XML-теги, Markdown-заголовки и другие структурные маркеры работают не потому, что модель «понимает HTML». Они работают по трём причинам:

**1. Синтаксический паттерн-матчинг**: в тренировочных данных XML/HTML/Markdown-разметка последовательно ассоциируется с разделением контента на семантические секции. Модель выучила: `<instruction>` = «то, что нужно выполнять», `<data>` = «то, что нужно обрабатывать».

**2. Attention boundaries**: парные теги создают «рамки» в attention. Токены внутри `<context>...</context>` получают более сильные attention-связи друг с другом, чем с токенами вне этого блока. Это снижает интерференцию. Если вернуться к аналогии с картотечным шкафом — теги делают стенки между ящиками, чтобы содержимое не перемешивалось.

**3. Снижение семантической неоднозначности**: без тегов модель должна самостоятельно определить, какая часть текста — инструкция, какая — данные, какая — пример. С тегами это определено явно.

### Экспериментальное подтверждение

Anthropic к 2026 году сделала XML-теги **центральным элементом** своей системы prompt engineering для Claude. Это не просто рекомендация — это стандарт: системные промпты Claude, инструкции к инструментам, конституциональные правила — всё обёрнуто в XML. По практическому опыту и рекомендациям Anthropic, XML-теги заметно повышают accuracy на задачах extraction и снижают разброс ответов. Документация Anthropic рекомендует XML-теги как основной инструмент структурирования промптов для Claude, хотя конкретные числа из внутренних бенчмарков публично не раскрыты.
- При мультимодальных промптах (текст + изображения) структурирование даёт ещё больший эффект: модель лучше разделяет инструкции о тексте и инструкции об изображении.

OpenAI пошла другим путём: их системные промпты GPT-5.x используют **Markdown-секции** и внутренние конвенции (заголовки, нумерованные списки, блоки с тройным дефисом). JSON Schema применяется для structured output и описания инструментов. Оба подхода работают — но для разных моделей по-разному (см. 7.2).

---

## 7.2. XML-подход: стандарт структурирования

### Почему XML, а не Markdown или JSON

Перед выбором формата важно понять: каждый формат — это другой тип «контейнера». XML — жёсткий металлический ящик с крышкой и замком. Markdown — картонная коробка с надписью (удобно, но содержимое может «выпасть»). JSON — пластиковый контейнер со строгими отсеками, но в него неудобно класть текст свободной формы.

| Формат | Плюсы | Минусы | Когда использовать |
|--------|-------|--------|-------------------|
| **XML** | Парные теги, явные границы, семантические имена, вложенность | Verbose, непривычен новичкам | Сложные промпты, Claude, мультимодальные задачи |
| **Markdown** | Знаком всем, читаемый, привычен моделям | Слабые границы (# vs ##), нет закрывающих тегов | Простые промпты, GPT-4o/o3, документация |
| **JSON** | Строгая структура, парсится кодом | Плохо читается, нельзя вложить свободный текст | Structured output, tool descriptions, MCP |
| **YAML** | Читаемость, вложенность | Чувствителен к отступам | Конфигурации промптов, DSPy |

### Какой формат для какой модели (2026)

Разные семейства моделей обучались на разных внутренних промптах, поэтому «родной» формат структурирования отличается:

| Модель | Предпочтительный формат | Почему | Пример структуры |
|--------|------|--------|-------|
| **Claude (Anthropic)** | XML-теги | Обучен на XML-структурированных промптах, системные промпты Anthropic — XML | `<instructions>...<data>...<output>` |
| **GPT-5.x (OpenAI)** | Markdown + JSON | Системные промпты OpenAI — Markdown-секции, инструменты — JSON Schema; `reasoning.effort` для управления глубиной рассуждений | `## Instructions\n...\n## Data\n...` |
| **Gemini (Google)** | Markdown | Хорошо работает с Markdown, особенно в мультимодальных кейсах | `# Task\n...\n# Context\n---` |
| **Открытые (Llama, Mistral)** | Markdown или плоский текст | Меньше обучения на XML-промптах, но XML тоже работает | Простые разделители |

> **Практический совет.** Если вы пишете промпт, который должен работать с несколькими моделями, используйте Markdown (он работает везде). Если только с Claude — используйте XML. Если вы работаете через MCP и описываете инструменты — используйте JSON Schema.

**XML выигрывает** для инструкций, потому что:
1. **Парные теги** → однозначные границы. `<data>` ... `</data>` — модель точно знает, где начинаются и заканчиваются данные.
2. **Атрибуты** → метаданные без дополнительных токенов: `<section id="3" priority="high">`.
3. **Вложенность** → иерархия без двусмысленности.

### Базовые правила XML-разметки промптов

```xml
<!-- Правило 1: каждая секция — парный тег с семантическим именем -->
<instruction>Сгенерируй SQL-запрос</instruction>

<!-- Правило 2: данные отделены от инструкций -->
<data>
  <table name="users">
    columns: id, name, email, created_at
  </table>
  <table name="orders">
    columns: id, user_id, amount, status, created_at
  </table>
</data>

<!-- Правило 3: примеры изолированы -->
<examples>
  <example>
    <input>Найди всех пользователей старше 25 лет</input>
    <output>SELECT * FROM users WHERE age > 25</output>
  </example>
</examples>

<!-- Правило 4: ограничения — отдельный блок -->
<constraints>
  - PostgreSQL 16 синтаксис
  - Только SELECT
  - Используй JOIN, не подзапросы
</constraints>

<!-- Правило 5: формат вывода — явно -->
<output_format>
  JSON: {"query": "...", "tables_used": [...], "estimated_complexity": "O(...)"}
</output_format>
```

---

## 7.3. Семантическая интерференция: почему смешивание ломает промпт

### Что такое семантическая интерференция

Представьте **две радиостанции на соседних частотах**. Каждая по отдельности звучит чисто, но когда вы ловите обе одновременно — слова смешиваются в кашу. Это интерференция в физике. Точно то же происходит в attention модели, когда инструкции, данные и примеры «вещают» на одной «частоте», без разделения.

XML-теги работают как **частотный фильтр**: они разводят сигналы по разным каналам, и модель слышит каждый отдельно.

**Простейший пример интерференции:**

```
⚠ Без тегов:
"Переведи на английский. Пример: Дом → House. Теперь переведи: Кот"
→ Модель отвечает: "House" (скопировала пример!)
```

```xml
✓ С тегами:
<instruction>Переведи текст в <input> на английский.</instruction>
<example>
  <ru>Дом</ru>
  <en>House</en>
</example>
<input>Кот</input>
→ Модель отвечает: "Cat" ✓
```

Почему? Теги разделили три «радиостанции»: инструкцию, пример и входные данные. Модель больше не путает, что нужно имитировать, а что — обработать.

Когда инструкции, данные и примеры **перемешаны** в одном текстовом блоке без разделителей, модель не может надёжно определить:
- Что **выполнять** (инструкция)?
- Что **обрабатывать** (данные)?
- Что **имитировать** (пример)?

Это приводит к **интерференции** — смешению ролей, при котором модель:

### Проявления интерференции

**1. Копирование примера вместо генерации:**

```
✗ Промпт:
"Генерируй SQL. Например: SELECT * FROM users WHERE age > 25. 
Теперь сгенерируй запрос для поиска заказов дороже 100."

Ответ модели:
"SELECT * FROM users WHERE age > 25"  ← скопировала пример!
```

Почему: attention «цепляется» за конкретный SQL-запрос в промпте (он грамматически полный, токенизируется как единое целое) и воспроизводит его.

**2. Игнорирование ограничения, «растворённого» в данных:**

```
✗ Промпт:
"Вот данные пользователей (используй только SELECT, не INSERT):
id=1 name=Ivan, id=2 name=Maria... Создай запрос для добавления нового пользователя."

Ответ модели:
"INSERT INTO users (name) VALUES ('Alexei')"  ← нарушила ограничение!
```

Почему: ограничение `только SELECT` стоит в скобках внутри описания данных. Attention отдаёт приоритет последней задаче «добавления», а ограничение теряется в контексте.

**3. Генерация мета-текста вместо полезного вывода:**

```
✗ Промпт:
"Напиши JSON с данными пользователей. Формат: {name, age, city}. 
Не добавляй комментарии."

Ответ модели:
"Вот JSON с данными пользователей в запрашиваемом формате:
```json
{...}
```
Как видите, я следовал формату..."
```

Почему: без явного структурного разделения «инструкция/данные/формат» модель переходит в «обсуждающий» режим вместо «исполняющего».

### Решение: физическое разделение

```xml
<!-- ✓ Правильно: каждый элемент в своём блоке -->
<instruction>
  Сгенерируй SQL-запрос для поиска заказов дороже $100.
</instruction>

<constraints>
  - Только SELECT (не INSERT, UPDATE, DELETE)
  - PostgreSQL 16
</constraints>

<example>
  <input>Найди пользователей старше 25</input>
  <output>SELECT * FROM users WHERE age > 25</output>
</example>

<data>
  Таблица: orders (id, user_id, amount, status, created_at)
</data>

<output_format>
  Только SQL-запрос, без пояснений.
</output_format>
```

---

## 7.4. Многослойный промпт: архитектурные паттерны

### Паттерн «Semantic Exoskeleton» (для кода)

Промпт как внешний скелет, к которому прикрепляются все компоненты:

```xml
<persona>
  Senior backend engineer, Python 3.12, FastAPI, PostgreSQL
</persona>

<contract>
  <function_name>get_user_orders</function_name>
  <input>user_id: int, status: OrderStatus | None = None, limit: int = 50</input>
  <output>list[OrderSummary]</output>
  <preconditions>user_id > 0, limit in [1, 100]</preconditions>
  <postconditions>len(result) <= limit, all orders belong to user_id</postconditions>
  <side_effects>None (read-only)</side_effects>
</contract>

<examples>
  <example>
    <call>get_user_orders(user_id=42, status="completed", limit=10)</call>
    <result>[OrderSummary(id=101, amount=99.99, status="completed"), ...]</result>
  </example>
  <example>
    <call>get_user_orders(user_id=999)</call>
    <result>[]  # user with no orders</result>
  </example>
</examples>

<constraints>
  - async/await
  - SQLAlchemy 2.0 (async session)
  - Raise ValueError on invalid input, not silent fail
  - No N+1 queries
</constraints>

<output>
  Complete Python function with type hints and docstring. No explanation.
</output>
```


### Паттерн «GRACE» (семантические якоря внутри репозитория)

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

```md
# [DEF:PricingService:Module]
# @PURPOSE: Вычисление итоговой цены заказа.
# @RELATION: DEPENDS_ON -> [DiscountPolicy:Module]
# @INVARIANT: Скидка применяется до налога.
# @RATIONALE: Денежные расчёты централизованы в domain-слое.
# @REJECTED: Не дублировать расчёт цены в HTTP-handler.
# [/DEF:PricingService:Module]
```

Это и есть идея протоколов класса **GRACE**: у модуля появляется machine-readable header, который одновременно работает как краткая спецификация для модели, ориентир для retrieval и graph construction, и decision memory для следующего агента или человека.

Важно не переоценивать механику. У моделей нет «врождённого» знания о токене `[DEF]`. Anthropic прямо пишет, что у XML-тегов нет специальных «магических» имён; помогает не конкретный тег, а **последовательная, парная и семантически осмысленная структура**. Поэтому `[DEF]...[/DEF]` — не сакральный синтаксис, а инженерная конвенция. Если команда использует другие стабильные парные маркеры, эффект будет близким.

Где здесь реальная польза:

1. **Разбор секций.** Парные границы уменьшают риск смешать инструкцию, пример и реализацию.
2. **Переиспользование.** Один и тот же header можно читать в промпте, в IDE-индексе и в offline-аудите.
3. **Анти-регрессия.** Поля `@RATIONALE` и `@REJECTED` превращают устное знание команды в явный контекст.

Где не стоит обещать лишнего: фразы вроде «якоря — это аккумуляторы внимания» полезны как метафора, но сегодня это не строгая механистическая теорема. Строго подтверждено более скромное утверждение: явно размеченные и устойчиво именованные секции модели разбирать проще, чем плоский текст без границ.


### Уникальные ID-теги: меньше закрывающей неоднозначности

В `grace-marketplace` эта идея доведена до следующего уровня: в shared XML-документах повторяющиеся сущности предлагается обозначать **не generic-тегом с атрибутом ID**, а самим ID в имени тега. Не `<Module ID="M-AUTH">...</Module>`, а `<M-AUTH>...</M-AUTH>`. Не `<Verification ID="V-M-AUTH">...</Verification>`, а `<V-M-AUTH>...</V-M-AUTH>`.

Зачем это нужно. Когда модель читает длинный XML с десятками модулей, generic closing tag вроде `</Module>` заставляет её каждый раз восстанавливать, *какой именно* модуль сейчас закрывается. Это не катастрофа, но это лишняя работа внимания. Уникальный closing tag сам несёт идентичность сущности и поэтому лучше работает как устойчивый навигационный якорь.

```xml
<!-- хуже для длинных shared-artifacts -->
<Module ID="M-AUTH">
  ...
</Module>

<!-- лучше для машинной навигации -->
<M-AUTH>
  ...
</M-AUTH>
```

Здесь важно не превращать удобную практику в псевдонауку. У нас нет строгой общей теоремы вида «уникальные closing tags всегда улучшают reasoning». Но как инженерная эвристика это очень разумно: у сущности появляется **одна и та же стабильная строка**, которая открывает и закрывает блок, а значит упрощает и поиск, и diff, и построение machine-readable graph.

Практический вывод для больших репозиториев:

1. В shared XML полезно давать повторяющимся сущностям уникальные имена тегов.
2. Эти документы должны описывать **публичную границу** модуля: контракт, зависимости, verification-ref, экспортируемый интерфейс.
3. Приватные helper'ы, временные workaround'ы и локальная оркестрация лучше живут в file-local markup, а не раздувают общую graph-схему.

### Паттерн «Document Protocol» (для текстов и аналитики)

```xml
<role>
  Финансовый аналитик, специализация — SaaS-метрики.
</role>

<goal>
  Проанализировать квартальные KPI и выявить аномалии.
</goal>

<source format="csv">
  quarter,mrr,churn_rate,ltv,cac,nps
  Q1-2025,2400000,3.2,18000,4200,67
  Q2-2025,2650000,2.8,19500,4100,71
  Q3-2025,2600000,4.1,17200,5300,58
  Q4-2025,2900000,2.5,21000,3900,74
</source>

<rules>
  - Аномалия = отклонение >1.5σ от среднего ряда
  - Укажи направление тренда для каждой метрики
  - Если нет аномалий — скажи явно
  - Не используй данные, которых нет в source
</rules>

<schema>
{
  "anomalies": [{"metric": "string", "quarter": "string", "value": "number", "deviation_sigma": "number"}],
  "trends": [{"metric": "string", "direction": "up|down|stable", "confidence": "high|medium|low"}],
  "summary": "string (max 200 words)"
}
</schema>
```

### Паттерн «Role-Context-Task» (универсальный)

Самый простой паттерн для начинающих — три блока: кто, что знает, что делать. Как трёхэтажный дом: фундамент (роль), стены (контекст), крыша (задача).

```xml
<role>
  Ты — опытный технический писатель, специализация — API-документация.
</role>

<context>
  Мы разрабатываем REST API для системы управления задачами.
  Стек: FastAPI, PostgreSQL. Аудитория документации — фронтенд-разработчики.
  Текущий стиль документации — аналогичный Stripe API Docs.
</context>

<task>
  Напиши документацию для эндпоинта POST /tasks, включая:
  - Описание
  - Параметры запроса (body)
  - Пример запроса и ответа
  - Коды ошибок
</task>
```

### Паттерн «ICIO» (Input-Context-Instruction-Output)

Паттерн из мира DSPy-сигнатур. Формализует промпт как функцию с чётким входом и выходом:

```xml
<input>
  Текст отзыва клиента: "Доставка заняла 3 недели, но качество товара
  превзошло ожидания. Упаковка была повреждена."
</input>

<context>
  Категории тональности: positive, negative, mixed, neutral.
  Аспекты: delivery, quality, packaging, price, support.
</context>

<instruction>
  Определи общую тональность и разбей на аспекты.
  Для каждого аспекта укажи тональность и ключевую фразу.
</instruction>

<output_schema>
{
  "overall_sentiment": "mixed",
  "aspects": [
    {"aspect": "delivery", "sentiment": "negative", "evidence": "3 недели"},
    {"aspect": "quality", "sentiment": "positive", "evidence": "превзошло ожидания"},
    {"aspect": "packaging", "sentiment": "negative", "evidence": "повреждена"}
  ]
}
</output_schema>
```

> **DSPy.** Фреймворк DSPy (введён в [§6.7](06_prompt_is_a_protocol.md)) формализовал подобные паттерны в виде *сигнатур* — типизированных описаний вход→выход. Сигнатура `"review: str -> sentiment: str, aspects: list[Aspect]"` автоматически превращается в структурированный промпт. Если вы работаете с DSPy — думайте о паттернах как о сигнатурах.

### Паттерн «Structured Chain» (для многошаговых задач)

Когда задача требует нескольких последовательных шагов, и каждый шаг нужно контролировать:

```xml
<goal>Проведи code review PR #247</goal>

<steps>
  <step id="1" name="understand">
    Прочитай diff в <code_diff> и опиши, что делает PR, в 2–3 предложениях.
  </step>
  <step id="2" name="analyze" depends_on="1">
    Найди потенциальные проблемы: баги, уязвимости, нарушения стиля.
    Для каждой проблемы укажи файл, строку и severity (critical/warning/info).
  </step>
  <step id="3" name="suggest" depends_on="2">
    Для каждой проблемы из шага 2 предложи конкретное исправление с кодом.
  </step>
  <step id="4" name="summarize" depends_on="2,3">
    Суммируй: общая оценка (approve/request_changes), количество проблем по severity.
  </step>
</steps>

<code_diff>
  ...содержимое diff...
</code_diff>

<output_format>
  Выведи результат каждого шага в отдельном блоке <step_result id="N">.
</output_format>
```

Обратите внимание на атрибут `depends_on` — он подсказывает модели порядок выполнения. Это как зависимости в build-системе: шаг 3 не начнётся, пока не выполнен шаг 2.

---

## 7.5. Иерархическая композиция: промпт из переиспользуемых блоков

### Модульная архитектура

В production-системах промпты собираются программно из переиспользуемых компонентов. Класс `PromptBuilder` показывает принцип: каждая секция (роль, задача, ограничения, данные, схема вывода) добавляется через fluent API, а метод `build()` собирает их в XML-структурированный промпт.

> **Промпт для ИИ:** «Напиши на Python класс `PromptBuilder` с fluent API для сборки XML-структурированных промптов. Методы: `add_role(str)`, `add_task(str)`, `add_constraints(list[str])`, `add_data(str, format)`, `add_schema(dict)`. Метод `build()` возвращает строку, где каждая секция обёрнута в парные XML-теги с семантическими именами. Покажи пример использования для SQL-задачи.»

**Преимущества:**
- Переиспользование: один `add_role("SQL expert")` для всех SQL-задач.
- Версионирование: каждый блок — отдельная версия.
- Тестирование: можно тестировать блоки по отдельности.
- A/B-тесты: заменяйте один блок, сравнивайте результаты.

### Управление зависимостями между модулями

В реальных системах модули промпта зависят друг от друга. Например, блок `<constraints>` ссылается на формат из `<output_schema>`, а `<examples>` должны соответствовать обоим. Если изменить схему вывода и забыть обновить примеры — промпт сломается.

Управлять зависимостями можно так же, как зависимостями в коде — через класс `PromptAssembler` с декларативными зависимостями между модулями. Каждый `PromptModule` указывает, от каких модулей он зависит (`depends_on`). Метод `validate()` проверяет, что все зависимости удовлетворены, а `build()` собирает промпт в топологическом порядке.

> **Промпт для ИИ:** «Напиши на Python классы `PromptModule(tag, content, depends_on)` и `PromptAssembler`. Assembler регистрирует модули, валидирует зависимости (validate) и собирает промпт в топологическом порядке (build). Пример: модуль constraints зависит от output_schema, examples зависит от output_schema и constraints. Покажи пример регистрации и сборки.»

Ключевое правило: **модуль может ссылаться только на модули, от которых он зависит**. Если `<examples>` зависит от `<output_schema>`, то при обновлении схемы система предупредит: «Обновите примеры — они зависят от изменённой схемы».

---

## 7.6. Тестирование на интерференцию

### Методика

Для каждого промпта проведите 4 теста:

**Тест 1: Порядок блоков.** Переставьте `<data>` и `<constraints>`. Если результат значительно меняется — промпт хрупок.

**Тест 2: Удаление примера.** Уберите `<examples>`. Если модель перестаёт следовать формату — промпт зависит от примера, а не от инструкции.

**Тест 3: Edge-case данные.** Подайте пустые данные, данные с ошибками, данные на другом языке. Проверьте, следует ли модель `<constraints>`.

**Тест 4: Длинный контекст.** Увеличьте `<data>` до 10K токенов. Проверьте, сохраняется ли точность. Если нет — структура недостаточно явная.

### Метрики стабильности

Для каждого промпта полезно измерить две метрики:

- **Структурная стабильность (parse rate):** какая доля ответов парсится как валидный JSON / соответствует схеме?
- **Семантическая стабильность:** насколько похожи ответы между собой при нескольких запусках с `temp > 0`?

Методика: запустите промпт 5 раз на каждом тестовом входе с `temp=0.3`, подсчитайте parse rate и попарное сходство ответов.

> **Промпт для ИИ:** «Напиши на Python функцию `measure_prompt_stability(prompt_template, test_inputs, n_runs=5)`, которая для каждого входа запускает промпт n_runs раз с temp=0.3, измеряет parse rate (доля валидных JSON) и среднее попарное сходство ответов. Используй OpenAI SDK. Возвращай dict с метриками по каждому входу.»

---

## Практический вывод

### Чек-лист структурирования промптов

| # | Правило | Проверка |
|---|---------|----------|
| 1 | **Каждая секция — парный тег** | `<tag>...</tag>`, не просто маркер |
| 2 | **Инструкции отделены от данных** | Физически, не логически |
| 3 | **Примеры изолированы** | В `<examples>`, не в тексте инструкции |
| 4 | **Ограничения — отдельный блок** | Не в скобках, не в сноске |
| 5 | **Теги имеют семантические имена** | `<constraints>`, а не `<section_3>` |
| 6 | **Протестирован на интерференцию** | 4 теста из раздела 7.6 |
| 7 | **Модульная структура** | Блоки можно заменять без переписывания |

### Минимальный шаблон

```xml
<role>[Кто модель]</role>
<task>[Что сделать]</task>
<constraints>[Чего нельзя + что обязательно]</constraints>
<data>[Входные данные]</data>
<output_format>[Точная структура вывода]</output_format>
```

Пять тегов. Универсально применимо. Расширяется по необходимости: `<examples>`, `<context>`, `<edge_cases>`, `<evaluation_criteria>`.

### Задания

**Задание 1. Реструктуризация существующего промпта.** Возьмите промпт из своего проекта (или один из антипаттернов из [Главы 6](06_prompt_is_a_protocol.md)), который написан «плоским текстом» без структурной разметки. Перепишите его с XML-тегами по минимальному шаблону: `<role>`, `<task>`, `<constraints>`, `<data>`, `<output_format>`. Запустите оба варианта 10 раз и сравните parse rate и качество ответов.

**Задание 2. Тест на интерференцию.** Для одного из рабочих промптов проведите 4 теста из §7.6: (a) переставьте блоки, (b) уберите примеры, (c) подайте edge-case данные, (d) увеличьте данные до 10K токенов. Зафиксируйте, на каком тесте промпт «ломается». Ожидаемый результат: выявление хрупких мест промпта и их исправление добавлением явных границ секций.

**Задание 3. A/B-тест форматов разметки.** Возьмите одну задачу (например, extraction или classification) и напишите эквивалентный промпт в трёх форматах: XML-теги, Markdown-заголовки, плоский текст. Запустите каждый вариант 20 раз на одинаковых входных данных. Сравните по parse rate и accuracy. Ожидаемый результат: количественное понимание того, как разметка влияет на стабильность.

---

## Источники
- Anthropic. "Use XML tags to structure your prompts." Claude Documentation (2024–2026). Обновлено: XML-теги — центральный элемент prompt engineering для Claude.
- OpenAI. "Prompt engineering best practices." API Documentation (2024–2026). Конвенция: Markdown-секции + JSON Schema для инструментов.
- Bai, Y., et al. (2022). "Constitutional AI: Harmlessness from AI Feedback." Anthropic.
- Anthropic. "Prompt Engineering Guide." Claude Documentation (2024–2026). https://docs.anthropic.com/
- Anthropic. "Use XML tags to structure your prompts." Claude Documentation (2024–2026). Важный нюанс: специальных «магических» имён тегов нет; помогает последовательная парная структура.
- Khattab, O., et al. (2024–2025). "DSPy: Compiling Declarative Language Model Calls into State-of-the-Art Pipelines." Stanford NLP. Формализация prompt-паттернов через сигнатуры.
- Model Context Protocol (MCP). "Tool Description Specification." (2025–2026). JSON Schema для описания инструментов агентов.
- Ivanov, V. `osovv/grace-marketplace`: `grace-explainer` и `unique-tag-convention.md` (2026). Практика уникальных ID-тегов и разделения shared/public vs file-local/private surface.

---

**Навигация:**
- Назад: [Глава 6. Промпт — это протокол, а не просьба](06_prompt_is_a_protocol.md)
- Далее: [Глава 8. Несколько гипотез лучше одной](08_multiple_hypotheses.md)
