XML-теги в промпте — это просто открывающие и закрывающие угловые
скобки вокруг кусков текста: <instructions>…</instructions>,
<example>…</example>,
<user_notes>…</user_notes>. Это не XML
в строгом смысле — модели всё равно, валидный документ или нет.
Это визуальные маркеры, которые помогают модели
понять: «вот здесь начинается контекст, а вот здесь — инструкция».
Anthropic в оригинале формулирует так: «XML-теги помогают модели однозначно разбирать сложные промпты — особенно когда промпт смешивает инструкции, контекст, примеры и переменные входные данные».
Не везде. Если промпт — два предложения, теги только мешают. Польза появляется, когда:
<thinking> и <answer>.
<result>…</result>,
ты в Python вытащишь это регуляркой и не зависишь от того,
как модель построила окружающий текст.
Имена — на твой вкус, главное — описательные. Несколько устоявшихся вариантов:
<instructions> — основные инструкции для
модели.
<context> — справочная информация.
<example> внутри <examples> —
few-shot блоки (см.
«Примеры»).
<user_notes> — заметки о пользователе из
твоей таблицы user_notes.
<crisis_protocol> — кризисный протокол.
<thinking> / <answer> —
внутреннее рассуждение и финальный ответ.
Главное правило: используй одни и те же
имена тегов во всём проекте. Если в одном промпте у тебя
<notes>, а в другом —
<user_notes>, ты в коде запутаешься, и модель
тоже немного.
Допустим, при каждом сообщении ты подкладываешь модели информацию из БД: имя, заметки, последнюю цель. Без тегов:
Информация о пользователе. Имя: Анна. Заметки: любит работать утром,
не любит ставить много задач сразу. Цель: научиться играть на гитаре.
Сообщение пользователя: помоги составить план на сегодня.
Модель может перепутать «заметки» с «сообщением» или включить «научиться играть на гитаре» прямо в план дня (а это долгосрочная цель, не сегодняшняя задача).
С тегами:
<user_context>
<name>Анна</name>
<preferences>любит работать утром, не любит ставить много задач сразу</preferences>
<long_term_goal>научиться играть на гитаре</long_term_goal>
</user_context>
Сообщение пользователя ниже. Используй данные из <user_context> как
справочник, но не вставляй долгосрочную цель в сегодняшний план,
если пользователь её сам не упомянул.
Теперь модель видит структуру и инструкцию, как этой структурой пользоваться.
Если ты просишь модель отвечать в формате с тегами, ответ можно надёжно распарсить:
Структура ответа:
<thinking>
Внутреннее рассуждение, не показывается пользователю.
</thinking>
<answer>
Финальный ответ для пользователя.
</answer>
<suggested_tools>
Если по итогу ты считаешь, что нужно вызвать add_task или save_goal —
напиши их через запятую. Если ничего не нужно — пиши «none».
</suggested_tools>
import re
raw = response.choices[0].message.content
def extract(tag: str, text: str) -> str | None:
m = re.search(rf"<{tag}>(.*?)</{tag}>", text, re.DOTALL)
return m.group(1).strip() if m else None
answer = extract("answer", raw) or raw
tools_raw = extract("suggested_tools", raw) or "none"
await message.answer(answer)
Это надёжнее, чем пытаться угадать формат ответа по «сначала ответ, потом перечисление tools через запятую». Модель иногда меняет порядок, иногда забывает разделитель — теги это стабилизируют.
<instructions>. Теги нужны, когда
есть несколько разных типов содержимого, чтобы их
различать.
<notes> и <user_notes> в
разных частях проекта — путаница.
<answer>что-то</answer> — у тебя
сломается парсинг. Либо санируй вход, либо используй редкие
имена тегов (<__sys_answer>).
<thinking> / <answer> /
произвольные служебные поля.