Для структурированных задач — парсинг заявок, формирование BOM (bill of materials, спецификация материалов), расчёт сметы — XML-теги прямо в промпте работают как самый надёжный способ разметки входа и выхода. Модели Claude (и в целом современные LLM) отлично их узнают, потому что видели в обучающих данных. Это не настоящий XML с валидацией, а текстовые маркеры — но эффект предсказуемый.
Ты составляешь BOM (спецификацию материалов) для деревянной лестницы.
<order>
<floors>2</floors>
<wood_type>oak</wood_type>
<run_length_mm>2800</run_length_mm>
<width_mm>900</width_mm>
<type>marshevaya</type>
</order>
<rules>
- Ступеней: длина марша / 200 мм, округление вверх.
- Материалы указывай в метрах (для доски), штуках (для крепежа), литрах (для лака).
- Учитывай стандартный перерасход 15% на распил.
</rules>
Верни результат внутри тега <bom> строго в следующем JSON-виде:
<bom_schema>
{
"items": [
{"name": string, "unit": "m|pcs|l", "qty": number, "wood_type": string|null}
],
"total_wood_meters": number,
"total_estimated_cost_rub": number|null
}
</bom_schema>
Не выдумывай цены — если прайса нет, ставь total_estimated_cost_rub в null.
Что здесь происходит: заявка, правила и схема ответа —
каждый в своём теге. Модель чётко видит, где вход, где правила, где
шаблон выхода. Финальный BOM она завернёт в <bom>...</bom>.
val response = openAi.chatCompletion { ... }
val text = response.choices.first().message.content
val regex = Regex("<bom>(.*?)</bom>", RegexOption.DOT_MATCHES_ALL)
val bomJson = regex.find(text)?.groupValues?.get(1)?.trim()
?: error("BOM tag not found in model response")
val bom = Json.decodeFromString<Bom>(bomJson)
Что здесь происходит: регуляркой выдираем содержимое
тега, парсим JSON в data class. Если LLM иногда сопровождает
ответ прозой (особенно не-Anthropic модели), этот подход спасает —
вытащим только содержимое <bom>.
У OpenAI есть встроенная фича structured outputs — она гарантирует, что ответ будет валидным JSON по указанной схеме. У Anthropic аналогичное есть в API Claude. Для критичных пайплайнов это предпочтительнее, чем парсить XML-теги вручную. Подробнее — в статье про evals и полной документации.
<order> всегда так, не <Order>).src/main/resources/prompts/.
.replace("{{ORDER}}", orderXml).
<bom>,
<parsed>, <review>). Парсить
регуляркой — надёжнее, чем бороться с «почти JSON».