- Learn
- Prompt Engineering
- Structured Prompts with XML Tags
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
<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
<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
<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
<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
<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
<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
<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:
<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:
<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
<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:
<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
<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:
<a>Review this code</a>
<b>function add(x, y) { return x + y; }</b>
Good:
<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:
<prompt>
<greeting>Hello</greeting>
<request>
<main_request>Please review</main_request>
<sub_request>this code</sub_request>
</request>
</prompt>
Appropriate:
<task>Please review this code</task>
<code>[code here]</code>
4. Close Tags Properly
Always close your tags to avoid parsing confusion:
<code>
const x = 1;
</code> <!-- Don't forget this -->
Combining with Other Techniques
XML + Few-Shot
<examples>
<example>
<input>getUserById</input>
<output>get_user_by_id</output>
</example>
</examples>
<task>
Convert to snake_case: fetchOrderDetails
</task>
XML + Chain of Thought
<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:
- Refactor a legacy JavaScript function to TypeScript
- Add error handling
- Improve performance
- 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.