XML-теги — это парные обёртки вида <имя>…</имя>, которые вы расставляете прямо в тексте промпта. Они нужны как визуальные границы — чтобы Claude точно понял, где заканчиваются ваши инструкции и начинается, например, кусок кода или текст бага.
Anthropic пишет про это так:
XML-теги помогают Claude однозначно разбирать сложные промпты, особенно когда промпт смешивает инструкции, контекст, примеры и переменные входные данные. Оборачивание каждого типа содержимого в собственный тег (например,
<instructions>,<context>,<input>) уменьшает риск неправильной интерпретации.
«Переменные входные данные» — это всё, что меняется от запроса к запросу: содержимое файла, текст ошибки из лога, JSON ответа API, описание задачи от тимлида. Инструкции при этом остаются стабильными.
Самый частый сценарий, в котором вы будете писать промпты руками — Claude Code Desktop, когда нужно подсунуть модели несколько кусков сразу. Например:
OrderRepository.kt;OrderRepositoryIT.kt, который сейчас падает;Если просто склеить это в одну простыню, модель может не понять, где код, где тест, где задача — и начать, например, переписывать тест вместо того, чтобы починить репозиторий. Если те же три куска обернуть в <instructions>, <current_code> и <failing_test> — модель видит границы и работает по делу.
<instructions> — везде так и пишете, не чередуйте с <task> и <todo> в соседних промптах.<documents>, каждый — внутри <document index="n">).Имена тегов вы придумываете сами — у Anthropic нет «зарезервированного» списка. Но есть устоявшиеся: <instructions>, <context>, <input>, <example>, <documents>, <document>. Начните с них — они используются во всех примерах документации Anthropic.
Ниже — оригинальный пример из раздела про длинный контекст: как обернуть несколько документов, чтобы модель не путала, где что. Цитируется дословно (английский, как в оригинале):
<documents>
<document index="1">
<source>annual_report_2023.pdf</source>
<document_content>
{{ANNUAL_REPORT}}
</document_content>
</document>
<document index="2">
<source>competitor_analysis_q2.xlsx</source>
<document_content>
{{COMPETITOR_ANALYSIS}}
</document_content>
</document>
</documents>
Analyze the annual report and competitor analysis. Identify strategic advantages and recommend Q3 focus areas.
Здесь видно сразу несколько идей:
<documents>.index="1", index="2". Это удобно, потому что модель потом может ссылаться: «согласно документу 1…».<source> — имя файла) и само содержимое (<document_content>). Это и есть «вложенные теги при естественной иерархии», про которые написано выше.<task>, если это последняя строка и она читается однозначно.{{ANNUAL_REPORT}} — это плейсхолдер, в реальном промпте на это место подставляется текст отчёта.
А теперь то же самое, но под ваш сценарий: чините баг в OrderRepository.kt, у вас есть текущий код, падающий тест и описание проблемы. Открываете Claude Code и пишете примерно такое:
<instructions>
Почини баг в OrderRepository: метод findActiveByCustomer возвращает
заказы со статусом CANCELLED, хотя должен только ACTIVE.
Не трогай сигнатуру метода и API сервиса OrderService — на него
завязан фронт. Поправь только реализацию репозитория и/или
SQL-запрос. Запусти OrderRepositoryIT, чтобы убедиться, что
тест зелёный.
</instructions>
<current_code path="src/main/kotlin/.../OrderRepository.kt">
class OrderRepository(private val jdbi: Jdbi) {
fun findActiveByCustomer(customerId: Long): List<Order> =
jdbi.withHandle<List<Order>, _> { h ->
h.createQuery("SELECT * FROM orders WHERE customer_id = :id")
.bind("id", customerId)
.mapTo(Order::class.java)
.list()
}
}
</current_code>
<failing_test path="src/test/kotlin/.../OrderRepositoryIT.kt">
class OrderRepositoryIT : IntegrationTestBase() {
@Test
fun `findActiveByCustomer не возвращает отменённые`() {
// ... создаём заказ ACTIVE и заказ CANCELLED для одного клиента ...
val result = repo.findActiveByCustomer(customerId)
assertEquals(1, result.size)
assertEquals(OrderStatus.ACTIVE, result.first().status)
}
}
</failing_test>
Что здесь работает:
<instructions> — что именно нужно сделать и какие границы (не менять сигнатуру, не трогать OrderService). Модель не угадывает по коду — вы говорите явно.<current_code> и <failing_test> — два отдельных куска. Атрибут path="…" добавлен в духе примера Anthropic с <source>: модель сразу понимает, что это два разных файла, а не один склеенный кусок.<file1> и <file2> — но current_code и failing_test заодно сообщают модели роль каждого блока.В Claude Code Desktop вам не обязательно набирать XML руками каждый раз: когда вы просите «прочитай OrderRepository.kt», модель сама подгружает файл через инструмент чтения файлов. XML-обёртка нужна, когда вы вставляете фрагмент текстом — например, кусок ошибки из лога CI, ответ внешнего API, разговор с клиентом про лестницу. Тогда полезно завернуть это в <log>, <api_response>, <customer_request> — и сразу написать в <instructions>, что с этим делать.
Короткие однозначные запросы оборачивать не нужно. «Перепиши этот SQL под Postgres 16» с одним запросом ниже — нормально и без XML. Теги нужны там, где несколько разнотипных кусков и модель может перепутать, где что.