Skip to content

Ephemeral vs Persisted Content ​

AIDK distinguishes between content that persists in the conversation history and content that exists only for the current tick. Understanding this distinction is key to effective context management.

Quick Comparison ​

ComponentPersisted?Rebuilds Each Tick?Use For
SectionYesNoRole definitions, static instructions
GroundingNoYesCurrent state, real-time data
EphemeralNoYesLow-level ephemeral primitive
TimelineYesNoConversation history

Persisted Content: Section ​

Content inside <Section> is persisted as part of the system message. It becomes part of the conversation history.

tsx
<Section id="instructions" audience="model">
  You are a helpful customer support agent.
  Always be polite and professional.
</Section>

Characteristics:

  • Added to system message
  • Persists across all ticks
  • Part of conversation history
  • Use for: role definitions, static rules, consistent instructions

When to Use Section ​

tsx
class SupportAgent extends Component {
  render() {
    return (
      <>
        {/* Static instructions - persist across conversation */}
        <Section audience="model">
          <H2>Your Role</H2>
          <Paragraph>You are a customer support agent for TechCorp.</Paragraph>
          <List>
            <ListItem>Be helpful and professional</ListItem>
            <ListItem>Escalate complex issues to human agents</ListItem>
            <ListItem>Never make promises about refunds</ListItem>
          </List>
        </Section>

        <Timeline>{/* conversation history */}</Timeline>
      </>
    );
  }
}

Ephemeral Content: Grounding ​

Content inside <Grounding> is NOT persisted. It provides current-state context to the model but is not part of the conversation history. It's rebuilt fresh each tick.

tsx
<Grounding type="account_status" position="before-user">
  Current balance: ${account.balance}
  Account status: {account.status}
  Last login: {account.lastLogin}
</Grounding>

Characteristics:

  • NOT added to conversation history
  • Rebuilt fresh each tick
  • Provides current-state context
  • Use for: dynamic data, real-time state, changing context

When to Use Grounding ​

tsx
class SupportAgent extends Component {
  render() {
    const ctx = context();
    const account = ctx.metadata.account;

    return (
      <>
        <Section audience="model">
          You are a customer support agent.
        </Section>

        {/* Dynamic context - changes frequently, NOT persisted */}
        <Grounding type="customer_context" position="before-user">
          <H3>Current Customer Context</H3>
          <Table
            headers={['Field', 'Value']}
            rows={[
              ['Name', account.name],
              ['Tier', account.tier],
              ['Balance', `$${account.balance}`],
              ['Open Tickets', String(account.openTickets)],
            ]}
          />
        </Grounding>

        {/* Real-time inventory - ephemeral */}
        <Grounding type="inventory" position="after-system">
          Stock levels as of {new Date().toISOString()}:
          {JSON.stringify(inventory.current)}
        </Grounding>

        <Timeline>{/* conversation history */}</Timeline>
      </>
    );
  }
}

The Ephemeral Primitive ​

<Ephemeral> is the low-level primitive that <Grounding> wraps. Use it when you need direct control over ephemeral content positioning.

tsx
<Ephemeral
  type="tools"
  position="start"
  order={10}
  id="available-tools"
>
  Available tools: {toolList.join(', ')}
</Ephemeral>

Position Options ​

Ephemeral content can be positioned at various points in the message list:

PositionDescription
'start'At the beginning of messages
'end'At the end of messages (default for Ephemeral)
'after-system'After system messages
'before-user'Before the user's message
'after-user'After the user's message
tsx
{/* Appears at the very start */}
<Ephemeral position="start" type="preamble">
  Session started at {sessionStart}
</Ephemeral>

{/* Appears after system message */}
<Grounding position="after-system" type="context">
  Additional context...
</Grounding>

{/* Appears just before the user's latest message */}
<Grounding position="before-user" type="state">
  Current state: {state}
</Grounding>

Ordering Within Position ​

When multiple ephemeral items have the same position, use order to control their sequence:

tsx
{/* These both appear before-user, but in specific order */}
<Grounding position="before-user" order={1} type="preferences">
  User preferences: {prefs}
</Grounding>

<Grounding position="before-user" order={2} type="recent_activity">
  Recent activity: {activity}
</Grounding>

Lower order values appear first.

Why Does This Matter? ​

Token Efficiency ​

Ephemeral content is not stored in conversation history, which means:

  • It doesn't consume tokens in stored messages
  • Fresh data is injected each tick
  • Previous states don't accumulate
tsx
// BAD: This persists and accumulates
onTickEnd(com, state) {
  this.timeline.update(t => [
    ...t,
    { role: 'system', content: `Balance: ${balance}` }, // Accumulates!
  ]);
}

// GOOD: This is ephemeral - rebuilt fresh each tick
render() {
  return (
    <>
      <Grounding type="balance">Balance: ${balance}</Grounding>
      <Timeline>{this.timeline()}</Timeline>
    </>
  );
}

Consistent Context ​

Grounding ensures the model always has current information:

tsx
class TradingAgent extends Component {
  render() {
    const prices = fetchCurrentPrices(); // Fresh each tick

    return (
      <>
        <Section audience="model">
          You are a trading assistant.
        </Section>

        {/* Always current - not stale from 10 messages ago */}
        <Grounding type="market_data" position="before-user">
          <H3>Current Prices</H3>
          <Table
            headers={['Symbol', 'Price', 'Change']}
            rows={prices.map(p => [p.symbol, p.price, p.change])}
          />
        </Grounding>

        <Timeline>{this.timeline()}</Timeline>
      </>
    );
  }
}

Comparison with Examples ​

Static Instructions (Use Section) ​

tsx
// Persist across the entire conversation
<Section audience="model">
  You are a legal assistant. Always cite sources.
  Never provide advice that could be construed as legal representation.
</Section>

Dynamic User Context (Use Grounding) ​

tsx
// Changes between ticks, shouldn't be in history
<Grounding type="user_status">
  User is currently: {user.status}
  Cart items: {cart.items.length}
  Session duration: {sessionDuration}
</Grounding>

Real-Time Data (Use Grounding) ​

tsx
// Fetch fresh each tick
<Grounding type="weather" position="before-user">
  Current weather in {location}: {weather.current}
</Grounding>

One-Time Setup (Use Section) ​

tsx
// Set once, persists
<Section id="capabilities">
  You have access to the following tools:
  - search_database: Query the customer database
  - send_email: Send emails to customers
  - create_ticket: Create support tickets
</Section>

Audience Options ​

Both Section and Grounding support audience targeting:

tsx
// Only model sees this
<Section audience="model">
  Internal instructions...
</Section>

// Only user sees this (useful for UI-rendered content)
<Section audience="user">
  Visible to user...
</Section>

// Both see this (default)
<Section audience="all">
  Shared content...
</Section>

Grounding defaults to audience="model" since it's typically context for the model.

Best Practices ​

1. Default to Grounding for Dynamic Data ​

If the data changes frequently or represents current state, use Grounding:

tsx
// Good: Current state is ephemeral
<Grounding type="session">
  Session: {session.id}, Duration: {session.duration}
</Grounding>

2. Use Section for Stable Instructions ​

If the content is the same across the entire conversation, use Section:

tsx
// Good: Role definition persists
<Section audience="model">
  You are a helpful coding assistant.
</Section>

3. Don't Duplicate ​

Avoid putting the same information in both:

tsx
// Bad: Duplicate information
<Section>User tier: {user.tier}</Section>
<Grounding>User tier: {user.tier}</Grounding>

// Good: Choose based on whether it changes
<Grounding type="user_context">
  User: {user.name}, Tier: {user.tier}
</Grounding>

4. Position Thoughtfully ​

Put context where it's most relevant:

tsx
// Good: User context right before their message
<Grounding position="before-user" type="context">
  {userContext}
</Grounding>

// Good: System context after system message
<Grounding position="after-system" type="state">
  {systemState}
</Grounding>

Released under the MIT License.