Siddhant Deval
Siddhant Deval
backend5 min read

Dynamic Context Window Management & Long-Context Packing

Stuffing 100k+ tokens into modern long-context models triggers the "Lost-in-the-Middle" attention deficit, inflates inference bills, and causes latency spikes that break streaming UX guarantees. This article implements dynamic token budget scheduling, greedy context packing, and attention-aware prompt layout for the Workspace context assembler.

Series·Part 14 of 24

Full-Stack AI Architecture: From Streaming Foundations to Resilient Agentic Systems in Node.js & React

Dynamic Context Window Management & Long-Context Packing

A 200,000-token context window does not mean you should fill it. Model attention follows a well-documented U-shaped curve: tokens near the beginning and end of the context receive disproportionately more attention than tokens in the middle. Researchers call this the "Lost-in-the-Middle" problem — if you place five retrieved RAG chunks in positions 40,000–80,000 of a 200k context, the model may answer as if they were not there.

This article implements the Context Window Scheduler: a component that treats the prompt as a finite scheduled resource, enforces deterministic token budgets across competing sections, and positions critical evidence according to attention geometry — preventing both silent overflow and middle-of-context blind spots.

Architectural Note

Who this is for: Engineers who have seen RAG systems with high retrieval recall still generate poor answers, or who have hit silent 400 errors from context window overflow with no indication of which part of the prompt caused it.


1. The Four Competing Budget Sections

Every prompt in the Workspace competes across four sections:

The scheduler allocates tokens to each section in priority order: system instructions are non-negotiable, RAG evidence is high-priority (without it, answers hallucinate), conversation history is prunable, and the output reserve must always be preserved.


2. Token Budget Scheduler

TYPESCRIPT
// ✅ Context Window Scheduler — deterministic token budget allocation
// file: src/gateway/ContextScheduler.ts

import { TokenAwareRequestBuilder } from './TokenAwareRequestBuilder'
import type { ChatMessage } from '../types'

const MODEL_CONTEXT_LIMITS: Record<string, number> = {
  'claude-3-5-sonnet-20241022': 200_000,
  'gpt-4o': 128_000,
  'gpt-4o-mini': 128_000,
}

export interface ContextBudget {
  systemTokens: number
  ragTokens: number
  historyTokens: number
  outputTokens: number
  totalAvailable: number
}

export interface PackedContext {
  messages: ChatMessage[]
  budget: ContextBudget
  pruned: number  // Number of history turns pruned
  ragChunksIncluded: number
}

export class ContextScheduler {
  private tokenizer = new TokenAwareRequestBuilder()

  pack(
    systemPrompt: string,
    ragChunks: { content: string; tokenCount: number }[],
    history: ChatMessage[],
    model: string,
    maxOutputTokens: number,
  ): PackedContext {
    const limit = MODEL_CONTEXT_LIMITS[model] ?? 128_000
    const safetyMargin = 200  // Buffer against tokeniser approximation errors

    // ── Budget allocation ───────────────────────────────────────────────────
    const systemTokens = this.tokenizer.countTokens(systemPrompt) + 4
    const outputReserve = maxOutputTokens
    const availableForContent = limit - systemTokens - outputReserve - safetyMargin

    // Allocate 55% to RAG, 45% to history.
    // 55/45 is calibrated for knowledge-retrieval tasks where context quality
    // outweighs history continuity. For conversational applications with minimal
    // RAG, adjust to 40/60. These values should be A/B tested against your eval
    // dataset (Part 22) using Context Recall as the guide metric.
    const ragBudget = Math.floor(availableForContent * 0.55)
    const historyBudget = availableForContent - ragBudget

    // ── RAG packing — greedy, respects budget ───────────────────────────────
    const includedChunks: string[] = []
    let ragTokensConsumed = 0

    for (const chunk of ragChunks) {
      if (ragTokensConsumed + chunk.tokenCount > ragBudget) break
      includedChunks.push(chunk.content)
      ragTokensConsumed += chunk.tokenCount
    }

    // ── History pruning — newest turns first ────────────────────────────────
    const prunedHistory: ChatMessage[] = []
    let historyTokensConsumed = 0
    let pruned = 0

    for (let i = history.length - 1; i >= 0; i--) {
      const cost = this.tokenizer.countTokens(history[i].content) + 4
      if (historyTokensConsumed + cost > historyBudget) {
        pruned++
        continue
      }
      prunedHistory.unshift(history[i])
      historyTokensConsumed += cost
    }

    // ── Attention-optimal prompt layout ─────────────────────────────────────
    // Critical: place RAG evidence LAST (before user turn) — highest attention position
    // Place system instructions FIRST — second-highest attention position
    const ragSection = includedChunks.length > 0
      ? `<knowledge_vault>\n${includedChunks.join('\n\n---\n\n')}\n</knowledge_vault>`
      : ''

    const messages: ChatMessage[] = [
      { role: 'system', content: systemPrompt },
      ...prunedHistory,
      // RAG evidence as the final assistant/system turn before user message
      ...(ragSection ? [{ role: 'user' as const, content: ragSection }] : []),
    ]

    return {
      messages,
      budget: {
        systemTokens,
        ragTokens: ragTokensConsumed,
        historyTokens: historyTokensConsumed,
        outputTokens: outputReserve,
        totalAvailable: limit,
      },
      pruned,
      ragChunksIncluded: includedChunks.length,
    }
  }
}

3. Conversation History Summarisation

When history pruning drops too many turns, critical context is lost. The alternative: summarise older turns into a compact representation that preserves key facts without retaining full verbatim text.

TYPESCRIPT
// ✅ Conversation summariser — replaces oldest turns with summary
// file: src/gateway/HistorySummariser.ts

import { generateText } from 'ai'
import { createAnthropic } from '@ai-sdk/anthropic'

const anthropic = createAnthropic()

export async function summariseHistory(
  turns: ChatMessage[],
  maxSummaryTokens = 400,
): Promise<string> {
  const formattedHistory = turns
    .map(m => `${m.role.toUpperCase()}: ${m.content}`)
    .join('\n\n')

  const { text } = await generateText({
    model: anthropic('claude-3-haiku-20240307'),
    maxTokens: maxSummaryTokens,
    temperature: 0,
    prompt: `Summarise the following conversation turns into a compact factual summary.
Preserve: decisions made, facts established, user goals stated, technical constraints mentioned.
Omit: pleasantries, repeated information, intermediate reasoning.

CONVERSATION:
${formattedHistory}

SUMMARY (max ${maxSummaryTokens} tokens):`,
  })

  return `[Earlier conversation summary]: ${text}`
}

4. The "Lost-in-the-Middle" Fix: Position Matters

Research confirms that transformer attention degrades for tokens in the middle of very long contexts. The mitigation: position the most critical information at the very beginning (system instructions) and the very end (RAG evidence immediately before the user message) of the prompt.

Position Attention What to Place Here
First 10% of context Highest System prompt, critical constraints, output schema
Middle 10–90% Lowest Conversation history (less critical), tool call results
Last 10% before user Highest RAG evidence, current documents being edited
TYPESCRIPT
// ✅ Attention-optimal prompt assembler used in ContextScheduler.pack()
// Critical layout rule: [system] [history] [RAG] [user_message]
// NOT:                  [system] [RAG] [history] [user_message]

// The RAG section immediately before the user turn maximises retrieval attention.
// This is the single layout change with the largest measurable impact on RAG quality.

Summary

Component Rule
Token budget Allocate in priority order: system → output reserve → RAG → history
RAG budget 55% of remaining content budget (tunable); never overflow for RAG
History pruning Prune oldest turns first; consider summarisation if pruning drops >30% of turns
Prompt layout RAG evidence last before user message — highest attention position in long context
Safety margin Reserve 200 tokens beyond computed usage — tokeniser approximations accumulate

What's Next

Part 15 builds the autonomous layer. We model agent execution as a Finite State Machine with explicit transition guards, step budgets, and structured error recovery — replacing naive while-loop tool-calling with a deterministic, auditable execution graph.

Research & Synthesis Note

This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.

#Context Window#Token Budget#RAG#LLM#Node.js#Performance#Prompt Engineering
Siddhant Deval

Written by Siddhant Deval

Senior Full-Stack Engineer building high-scale architectures, browser performance engineering systems, and SaaS platforms.

Series·Part 14 of 24

Full-Stack AI Architecture: From Streaming Foundations to Resilient Agentic Systems in Node.js & React

Curriculum Roadmap
01The Full-Stack AI Paradigm Shift: Beyond Toy Wrappers & Prompt Concatenation
02The Anatomy of an LLM Call: Tokens, Context Windows & Probabilistic Mechanics
03Structured Outputs & Formal Invariants: Taming Non-Determinism with Zod & TypeBox
04Full-Stack Streaming Protocols: SSE, Fetch Streams & WebSockets
05V8 Streams, Server-Side Backpressure & Cancellations in Node.js
06React 18/19 Streaming UI & Generative Components
07Generative UI & Interactive Canvas Synchronization
08High-Dimensional Vector Embeddings & Similarity Math
09Vector Storage & Indexing at Scale: HNSW vs IVF in PostgreSQL (pgvector)
10Hybrid Search: Dense Vectors Meet Sparse Lexical BM25
11Multi-Stage Retrieval & Cross-Encoder Reranking
12Advanced RAG Architectures: Contextual Retrieval & Parent-Document Splitting
13Query Transformation, Decomposition & Multi-Query Expansion
14Dynamic Context Window Management & Long-Context Packing
Reading
15Agent Loops & Deterministic State Machines
16Model Context Protocol (MCP) in Full-Stack Architecture
17Human-in-the-Loop & Durable Agent Workflows
18Multi-Agent Orchestration & Collaborative Swarms
19Semantic Caching & Provider Cost Optimization
20Multi-Provider AI Gateways: Fallbacks, Rate Limiting & Hedged Requests
21Client-Side AI & Edge Hybrid Execution: WebGPU & Transformers.js
22Production Evals & Guardrails: Measuring Retrieval and Generation
23Full-Stack AI Observability & Tracing with OpenTelemetry
24Production Capstone: Assembling The Enterprise Collaborative AI Workspace