Workflow Intermediate medium · 6 min decision_step

Claude prompting: XML preference

What you will learn
Decide whether to structure your Claude prompts with XML tags instead of markdown or plain text for better parsing and reliability.
Step 2.3: Format Selection: in the larger prompt engineering workflow after defining task requirements and before writing the system prompt

Why this matters

Claude's training heavily weights XML-tagged content for structured extraction and reasoning. Skipping this step leads to inconsistent parsing, hallucinated outputs, and lower instruction adherence, especially when chaining prompts or expecting structured JSON responses.

Explanation

Why XML matters for Claude specifically: Claude was trained on vast amounts of XML in code repositories, documentation, and web content. It treats XML boundaries as semantic delimiters, not just text. When you wrap instructions or context in XML tags, Claude treats the content with higher confidence and maintains structure more reliably than markdown-style formatting.

How to decide: Use XML tags when you need (1) structured extraction with guaranteed format, (2) clear separation between instructions and user input, (3) multi-turn conversations where context isolation prevents cross-contamination, or (4) any case where you'd normally use JSON Schema but need human readability. Use plain text only for simple, single-turn completions where output format is flexible.

What to watch for: XML nesting depth matters: deeply nested tags reduce parsing reliability. Also, if your XML contains user input, always escape special characters (<, >, &) to prevent tag injection attacks that cause Claude to misparse the structure.

Code

python
# pip install anthropic

import anthropic
import html

client = anthropic.Anthropic()

def prompt_with_xml_structure(user_input: str) -> str:
    """Demonstrate XML-structured prompt with proper escaping."""
    
    escaped_input = html.escape(user_input)
    
    message = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=300,
        system="You are a customer service chatbot. Always respond with structured XML tags.",
        messages=[
            {
                "role": "user",
                "content": f"""<request>
<type>customer_inquiry</type>
<user_message>{escaped_input}</user_message>
<instructions>
<instruction>Analyze sentiment (positive/negative/neutral)</instruction>
<instruction>Provide one-sentence response</instruction>
<instruction>Suggest next action</instruction>
</instructions>
</request>"""
            }
        ]
    )
    
    return message.content[0].text

def prompt_without_xml_structure(user_input: str) -> str:
    """Compare: same task without XML structuring."""
    
    message = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=300,
        system="You are a customer service chatbot.",
        messages=[
            {
                "role": "user",
                "content": f"""Customer says: {user_input}

Analyze their sentiment (positive/negative/neutral), give a one-sentence response, and suggest a next action."""
            }
        ]
    )
    
    return message.content[0].text

if __name__ == "__main__":
    test_input = "I love your product but the checkout process took forever!"
    
    print("=== With XML Structure ===")
    xml_response = prompt_with_xml_structure(test_input)
    print(xml_response)
    print()
    
    print("=== Without XML Structure ===")
    plain_response = prompt_without_xml_structure(test_input)
    print(plain_response)
Output
=== With XML Structure ===
<response>
<sentiment>mixed</sentiment>
<reply>We're thrilled you love our product, and we apologize for the checkout experience.</reply>
<next_action>Follow up with customer about checkout flow feedback within 24 hours.</next_action>
</response>

=== Without XML Structure ===
The customer's sentiment is mixed: they're very positive about the product itself but frustrated with the checkout process. A suitable response would be: "Thank you for the kind words about our product! We're sorry the checkout took longer than expected: we're actively working to streamline that experience." The next action would be to gather specific feedback about what made the checkout process slow and route it to the product team for optimization.

Your options

XML tags for structure

Any production workflow, structured extraction, multi-turn conversations, or when you need guaranteed output format. Default choice for enterprise prompts.

Pros

Higher parsing accuracy, semantic clarity, prevents instruction injection, works reliably with response_format validation, scales across multiple turns

Cons

Slightly more verbose, requires escaping user input, adds ~5-10% to token count

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=500,
    messages=[
        {
            "role": "user",
            "content": "<task>Write a haiku about AI. Keep it under 50 words.</task>"
        }
    ]
)

XML tags with response_format (structured outputs)

When you need guaranteed JSON schema compliance and don't want to parse text. Enterprise extraction, API integrations, model-to-model chains.

Pros

Guaranteed valid JSON output, zero parsing ambiguity, automatic schema validation, production-grade reliability

Cons

Requires defining JSON schema upfront, slightly higher latency, not suitable for open-ended creative tasks

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=500,
    messages=[
        {
            "role": "user",
            "content": "<task>Extract entities from: 'John Smith works at Google in NYC'</task>"
        }
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "entity_extraction",
            "schema": {
                "type": "object",
                "properties": {
                    "person": {"type": "string"},
                    "company": {"type": "string"},
                    "location": {"type": "string"}
                },
                "required": ["person", "company", "location"]
            }
        }
    }
)

Validation step

After calling the API, check that Claude's response either (1) includes XML tags matching your schema if you requested structured output, or (2) parses cleanly without errors if you're extracting data from the response. If using response_format, validate the returned JSON against your schema. If not, manually inspect the first response to confirm tag boundaries are present and content is logically separated.

At scale

At scale (1000+ requests/day), XML tag overhead accumulates: each tag adds ~5-10 tokens, multiplying your costs by 5-10%. Also, deeply nested XML (more than 4 levels) begins to degrade parsing accuracy in Claude. If scaling beyond 10k req/day, profile your actual tag overhead and consider batching or flattening structure. Response_format adds ~100-200ms latency per request due to schema validation: acceptable for async workflows but problematic for real-time APIs.

Rollback plan

If Claude stops respecting your XML structure mid-conversation (usually after 10+ turns), rollback by (1) explicitly restating the XML schema in a fresh system prompt, (2) reducing nesting depth, or (3) switching to response_format if you need guaranteed parsing. If response_format validation fails, check that your schema is valid JSON Schema draft 2020-12 and that your prompt doesn't contradict the schema constraints.

Debug symptoms

Claude's response suddenly cuts off mid-sentence or returns incomplete XML

Diagnosis

User input contains unescaped XML special characters (`<`, `>`, `&`), breaking the tag structure. Claude stops parsing when it encounters the malformed tag.

Fix

Use `html.escape(user_input)` before embedding it in the XML template. Replace `&` first, then `<` and `>`.

Without XML tags, Claude ignores your embedded instructions and treats them as suggestions; with XML tags, it follows them reliably

Diagnosis

This is expected behavior: Claude weights XML-tagged directives much higher. You're not doing anything wrong; XML is just working as designed.

Fix

No fix needed. This validates that XML structuring is the right choice. If you want even stricter adherence, add response_format.

Token count increased by 20-30% after switching to XML, but output quality barely improved

Diagnosis

You added XML tags but didn't simplify the plain-text instructions that were already there. You're duplicating information.

Fix

Remove the markdown-style instructions and rely solely on the XML structure. The XML tags replace the need for verbose prose instructions.

Production upgrade path

Production version: Use response_format with JSON schema instead of relying on XML parsing alone. Tutorial version relies on Claude respecting your XML structure and parsing it correctly. Production version guarantees valid JSON and schema compliance by using the response_format parameter, eliminating the need to parse Claude's XML response with regex or fragile string splits. Example: wrap your XML instructions in a system prompt, but add response_format to guarantee the output shape. This gives you both semantic clarity (XML) and output validation (schema).

Common gotcha

The most common mistake: forgetting to escape user input before embedding it in XML tags. If a user input contains `` or `&`, Claude will misparse it as XML markup instead of literal text. Always use `html.escape()` or equivalent before inserting user data into XML templates. This causes silent failures where Claude parses only part of the user message or corrupts the tag structure entirely.

Experienced dev note

Senior practitioners realize that XML preference is not about elegance: it's about Claude's training distribution. Claude saw structured XML in training far more than in markdown, so it learned to respect XML boundaries as hard semantic markers. Many tutorials still recommend plain text or markdown because they work 80% of the time, but they fail silently on edge cases (ambiguous instructions, instruction injection, multi-turn confusion). XML doesn't make a difference for simple tasks, but it compounds into reliability over hundreds of requests. Also: response_format with XML in the system prompt is redundant: if you're using response_format, your schema is already machine-readable, so the XML becomes visual scaffolding for debugging, not functional.

Check your understanding

You're building a multi-turn customer support chatbot where each turn includes user input. The first turn works great, but by turn 5, Claude stops following your formatting instructions and starts mixing response formats. Why does adding XML structure (not response_format) to your system prompt likely fix this, and what would you need to do before embedding user input in your XML template?

Show answer hint

XML provides semantic boundaries that prevent context bleed across turns. User input must be escaped before insertion to prevent tag injection. The key is that Claude maintains instruction adherence better when instructions are semantically bounded by XML rather than just embedded in prose.

Community Notes

No notes yetBe the first to share a version-specific fix or tip.