Intermediate15 min

Learn how to use XML tags and structured formatting to organize complex prompts for clearer AI understanding.

Structured Prompts with XML Tags

When prompts get complex, structure becomes critical. XML tags help organize information clearly, making it easier for the AI to parse and respond accurately.

Why Use Structured Prompts?

Complex prompts often contain multiple types of information:

  • Instructions
  • Context
  • Examples
  • Constraints
  • Input data

Without structure, these elements can blur together, leading to confused responses. XML tags create clear boundaries.

Basic XML Tag Structure

Terminal
<context>
You're building a user management API for a SaaS application.
The tech stack is Node.js, Express, and PostgreSQL.
</context>

<task>
Create an endpoint to update user profiles.
</task>

<requirements>
- Accept PATCH requests
- Validate all inputs
- Return updated user object
</requirements>

<constraints>
- Use Zod for validation
- Don't expose password field
- Follow REST conventions
</constraints>

Common Tag Patterns

Input/Output Tags

Terminal
<input>
const data = [1, 2, 3, 4, 5];
const doubled = data.map(x => x * 2);
</input>

<expected_output>
Explain what this code does in plain English.
</expected_output>

Code and Context Tags

Terminal
<existing_code>
// Current implementation
export const fetchUser = async (id: string) => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};
</existing_code>

<task>
Add error handling and TypeScript types to this function.
</task>

Example Tags

Terminal
<examples>
  <example>
    <input>snake_case_name</input>
    <output>snakeCaseName</output>
  </example>
  <example>
    <input>SCREAMING_SNAKE</input>
    <output>screamingSnake</output>
  </example>
</examples>

<task>
Convert this variable name using the same pattern:
<input>user_profile_data</input>
</task>

Structured Prompt Templates

Code Review Template

Terminal
<role>
You are a senior code reviewer focusing on quality and maintainability.
</role>

<code_to_review>
export async function processOrder(order) {
  const user = await db.users.find(order.userId);
  if (user.balance >= order.total) {
    user.balance -= order.total;
    await db.users.update(user);
    await db.orders.create(order);
    return { success: true };
  }
  return { success: false };
}
</code_to_review>

<review_criteria>
- Error handling
- Type safety
- Transaction safety
- Edge cases
- Naming conventions
</review_criteria>

<output_format>
For each issue found:
1. Location (line/section)
2. Severity (Critical/High/Medium/Low)
3. Description
4. Suggested fix
</output_format>

Bug Analysis Template

Terminal
<error_message>
TypeError: Cannot read property 'map' of undefined
at UserList (UserList.tsx:15)
</error_message>

<relevant_code>
const UserList = ({ users }) => {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};
</relevant_code>

<context>
- Component receives users from parent via props
- Parent fetches users from API on mount
- Error occurs on initial render
</context>

<task>
1. Explain why this error occurs
2. Provide a fix
3. Suggest preventive measures
</task>

Feature Implementation Template

Terminal
<feature_description>
Add a "save draft" feature to the blog post editor that auto-saves
every 30 seconds and allows manual save with keyboard shortcut.
</feature_description>

<existing_architecture>
- React 18 with TypeScript
- Zustand for state management
- React Query for server state
- API endpoint: POST /api/posts/drafts
</existing_architecture>

<requirements>
- Auto-save every 30 seconds when content changes
- Manual save with Cmd/Ctrl + S
- Show save status indicator
- Handle offline scenarios
- Debounce auto-save to prevent excessive API calls
</requirements>

<constraints>
- Must integrate with existing PostEditor component
- Must not block UI during save
- Must handle concurrent save attempts
</constraints>

<output>
1. Updated component code
2. New hooks if needed
3. API integration
4. Brief explanation of approach
</output>

Separating Instructions from Data

One key use of XML tags is clearly separating your instructions from user data or code being analyzed:

Terminal
<instructions>
Analyze the following SQL query for potential performance issues.
Check for missing indexes, full table scans, and N+1 patterns.
</instructions>

<sql_query>
SELECT * FROM users
WHERE created_at > '2024-01-01'
ORDER BY last_login DESC;
</sql_query>

<table_info>
users: 2.5M rows, indexes on (id), (email)
</table_info>

This prevents the AI from confusing instructions with content to be analyzed.

Nested Tags for Complex Structures

For complex prompts, use nesting:

Terminal
<task>
  <objective>
  Create a REST API for a todo application.
  </objective>

  <endpoints>
    <endpoint>
      <method>GET</method>
      <path>/todos</path>
      <description>List all todos with pagination</description>
    </endpoint>
    <endpoint>
      <method>POST</method>
      <path>/todos</path>
      <description>Create a new todo</description>
    </endpoint>
  </endpoints>

  <tech_stack>
    <runtime>Node.js 20</runtime>
    <framework>Express</framework>
    <database>PostgreSQL</database>
    <validation>Zod</validation>
  </tech_stack>
</task>

Tags for Multi-Step Tasks

Terminal
<workflow>
  <step number="1">
    <action>Analyze the current code structure</action>
    <output>List of files and their purposes</output>
  </step>

  <step number="2">
    <action>Identify areas needing refactoring</action>
    <output>Prioritized list of improvements</output>
  </step>

  <step number="3">
    <action>Implement the top priority refactor</action>
    <output>Refactored code with explanation</output>
  </step>
</workflow>

<code_to_analyze>
[your code here]
</code_to_analyze>

Response Format Tags

Control the output structure:

Terminal
<response_format>
Respond using this exact structure:

## Summary
[One paragraph overview]

## Issues Found
[Bulleted list]

## Recommendations
[Numbered list with priority]

## Code Changes
[Code blocks with before/after]
</response_format>

Tags for Conditional Logic

Terminal
<task>
Review this code and respond according to findings.
</task>

<if_no_issues>
Respond with: "Code looks good! No issues found."
</if_no_issues>

<if_issues_found>
List each issue with:
- Description
- Severity
- Fix suggestion
</if_issues_found>

<code>
[code to review]
</code>

Best Practices

1. Use Descriptive Tag Names

Bad:

Terminal
<a>Review this code</a>
<b>function add(x, y) { return x + y; }</b>

Good:

Terminal
<task>Review this code</task>
<code>function add(x, y) { return x + y; }</code>

2. Be Consistent

Use the same tag names throughout your prompts:

  • Always <code> not sometimes <code> and sometimes <source>
  • Always <task> not sometimes <task> and sometimes <instructions>

3. Don't Over-Tag

Excessive:

Terminal
<prompt>
  <greeting>Hello</greeting>
  <request>
    <main_request>Please review</main_request>
    <sub_request>this code</sub_request>
  </request>
</prompt>

Appropriate:

Terminal
<task>Please review this code</task>
<code>[code here]</code>

4. Close Tags Properly

Always close your tags to avoid parsing confusion:

Terminal
<code>
const x = 1;
</code>  <!-- Don't forget this -->

Combining with Other Techniques

XML + Few-Shot

Terminal
<examples>
  <example>
    <input>getUserById</input>
    <output>get_user_by_id</output>
  </example>
</examples>

<task>
Convert to snake_case: fetchOrderDetails
</task>

XML + Chain of Thought

Terminal
<task>
Debug this function step by step.
</task>

<reasoning_steps>
1. Identify the expected behavior
2. Trace the actual execution
3. Find the discrepancy
4. Propose a fix
</reasoning_steps>

<code>
[buggy code]
</code>

Practice Exercise

Create a structured prompt using XML tags for this scenario:

You need to:

  1. Refactor a legacy JavaScript function to TypeScript
  2. Add error handling
  3. Improve performance
  4. Add unit tests

Include tags for:

  • The original code
  • Specific requirements
  • Constraints
  • Expected output format

Summary

  • XML tags organize complex prompts into clear sections
  • Use descriptive, consistent tag names
  • Separate instructions from data/code
  • Nest tags for complex structures
  • Combine with other techniques for powerful prompts

Next Steps

Now that you can structure complex prompts, let's apply these skills to a specific use case: prompt engineering for debugging.

Mark this lesson as complete to track your progress