Deep Agents
AgentContextOrchestratorRetrievalText2SQLToolbox

XML Renderer

Render context fragments as well-formed XML for structured LLM prompts

The XmlRenderer is the default renderer, producing well-formed XML output that works well with Claude, GPT-4, and other models trained to understand XML structure.

Basic Usage

import { XmlRenderer, role, hint, fragment } from '@deepagents/context';

const renderer = new XmlRenderer();

const fragments = [
  role('You are a helpful assistant.'),
  hint('Be concise.'),
];

const output = renderer.render(fragments);

Output:

<role>You are a helpful assistant.</role>
<hint>Be concise.</hint>

Rendering Rules

Primitives

Strings, numbers, and booleans render as tag content:

{ name: 'temperature', data: 0.7 }
// <temperature>0.7</temperature>

{ name: 'enabled', data: true }
// <enabled>true</enabled>

Objects

Object properties become nested tags:

{
  name: 'config',
  data: {
    maxTokens: 1000,
    format: 'json',
  }
}

Output:

<config>
  <maxTokens>1000</maxTokens>
  <format>json</format>
</config>

Arrays

Array items use singularized tag names:

{
  name: 'rules',
  data: ['Be helpful', 'Be concise', 'Be accurate']
}

Output:

<rules>
  <rule>Be helpful</rule>
  <rule>Be concise</rule>
  <rule>Be accurate</rule>
</rules>

Nested Fragments

Fragments within fragments maintain hierarchy:

fragment('domain',
  fragment('terminology',
    hint('LTV = Lifetime Value'),
    hint('MRR = Monthly Recurring Revenue'),
  ),
  fragment('constraints',
    hint('Never expose PII'),
  ),
)

Output:

<domain>
  <terminology>
    <hint>LTV = Lifetime Value</hint>
    <hint>MRR = Monthly Recurring Revenue</hint>
  </terminology>
  <constraints>
    <hint>Never expose PII</hint>
  </constraints>
</domain>

XML Escaping

Special characters are automatically escaped:

CharacterEscaped As
&&amp;
<&lt;
>&gt;
"&quot;
'&apos;
{ name: 'query', data: 'SELECT * FROM users WHERE age > 18' }
// <query>SELECT * FROM users WHERE age &gt; 18</query>

Multiline Content

Multiline strings are indented for readability:

{
  name: 'instructions',
  data: `Follow these steps:
1. Read the query
2. Generate SQL
3. Validate output`
}

Output:

<instructions>
  Follow these steps:
  1. Read the query
  2. Generate SQL
  3. Validate output
</instructions>

Fragment Grouping

Enable groupFragments to collect same-named fragments:

const renderer = new XmlRenderer({ groupFragments: true });

const fragments = [
  hint('Be helpful'),
  hint('Be concise'),
  hint('Use examples'),
];

renderer.render(fragments);

Without grouping:

<hint>Be helpful</hint>
<hint>Be concise</hint>
<hint>Use examples</hint>

With grouping:

<hints>
  <hint>Be helpful</hint>
  <hint>Be concise</hint>
  <hint>Use examples</hint>
</hints>

This uses the pluralize library for correct pluralization.

Null and Undefined Handling

Null and undefined values are filtered out:

{
  name: 'config',
  data: {
    enabled: true,
    value: null,
    name: undefined,
  }
}
// <config>
//   <enabled>true</enabled>
// </config>

When to Use XML

Advantages:

  • Clear structural boundaries
  • Well-understood by modern LLMs
  • Familiar to developers
  • Easy to parse programmatically

Trade-offs:

  • Most verbose format (highest token count)
  • Closing tags add overhead

Example with ContextEngine

import {
  ContextEngine,
  InMemoryContextStore,
  XmlRenderer,
  role,
  hint,
} from '@deepagents/context';

const context = new ContextEngine({ store: new InMemoryContextStore() })
  .set(
    role('You are a code reviewer.'),
    hint('Focus on security issues.'),
    hint('Check for performance problems.'),
  );

// XML is the default
const { systemPrompt } = await context.resolve();

// Explicit XML renderer
const { systemPrompt: xml } = await context.resolve({
  renderer: new XmlRenderer(),
});

// Grouped hints
const { systemPrompt: grouped } = await context.resolve({
  renderer: new XmlRenderer({ groupFragments: true }),
});

Next Steps