Skip to content

Context Engineering

Context engineering is the practice of finding the smallest set of high-signal tokens that maximize the likelihood of desired outcomes. Reactive Agents provides a systematic context engineering system that adapts to your model’s capabilities.

Every model has different context capacity, latency characteristics, and instruction-following quality. Context Profiles let you tune all context-related thresholds to match your model tier.

TierModelsCompactionTool Result SizeRules
localOllama, llama, phi, qwenEvery 4 steps400 charsSimplified
midhaiku, mini, flashEvery 6 steps800 charsStandard
largesonnet, gpt-4oEvery 8 steps1,200 charsStandard
frontieropus, o1, o3Every 12 steps2,000 charsDetailed
// Use the tier auto-detection (inferred from model name)
const agent = await ReactiveAgents.create()
.withProvider("ollama")
.withModel("qwen3:4b")
.withReasoning()
.withTools()
.withContextProfile({ tier: "local" })
.build();
// Override specific thresholds
const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withModel("claude-haiku-4-5-20251001")
.withContextProfile({
tier: "mid",
toolResultMaxChars: 1000, // Override default 800
compactAfterSteps: 8, // Start compacting later
})
.build();
PropertyDescription
tier"local" | "mid" | "large" | "frontier"
compactAfterStepsSteps before older history is compacted
fullDetailStepsSteps kept at full detail during compaction
toolResultMaxCharsMax chars for tool result in context
rulesComplexity"simplified" | "standard" | "detailed"
promptVerbosity"minimal" | "standard" | "full"
toolSchemaDetail"names-only" | "names-and-types" | "full"

The ContextEngine replaces static context builders with a per-iteration scoring pipeline. Every step in the agent’s history gets a score each iteration, and the context window is assembled from the highest-scoring items within the available budget.

Each history item receives a combined score:

SignalFormulaNotes
Recencye^{-0.3 × iterDiff}Exponential decay; items from 3 iterations ago score ~0.4
Relevancekeyword overlap with taskStops words filtered; case-insensitive
Type weightobs 0.8 · action 0.6 · thought 0.4Observations carry the most signal
Urgency×1.5 if step is a failureErrors boosted so recovery context stays visible
Pin1.0Tool schemas and system context always included

Pinned items (tool reference, rules block) always appear regardless of budget. Memory items with relevance below 0.3 are dropped.

The number of steps kept at full detail scales with the model tier:

TierFull-detail steps
local3
mid5
large7
frontier10

Older steps are compacted to one-line summaries automatically.

import { buildContext } from "@reactive-agents/reasoning";
import { CONTEXT_PROFILES } from "@reactive-agents/reasoning";
const prompt = buildContext({
task: "Write a report on TypeScript performance",
iteration: 4,
maxIterations: 10,
steps: previousSteps,
memoryItems: semanticMemory,
toolSchemas: "web-search(query), file-write(path, content)",
profile: CONTEXT_PROFILES["mid"],
});

The buildContext function is the same one the ReAct kernel uses internally — you can call it to assemble prompts for custom kernels or test harnesses.

As agents work through multi-step tasks, context grows. Reactive Agents uses a four-level progressive compaction strategy:

LevelApplied ToFormat
Level 1 — Full DetailLast fullDetailSteps stepsComplete ReAct format
Level 2 — SummarySteps within compactAfterSteps windowOne-line preview
Level 3 — GroupedOlder steps"Steps 3-8: file-read ×2, file-write ×1"
Level 4 — DroppedAncient steps without preserveOnCompactionRemoved entirely

Preservation rules: Error observations and the first file-write per path are always preserved, regardless of their age.

The budget system allocates tokens across context sections and adapts as iterations progress:

import { allocateBudget, estimateTokens } from "@reactive-agents/reasoning";
const budget = allocateBudget(
128_000, // total model context tokens
profile, // ContextProfile
3, // current iteration
10, // max iterations
);
// budget.allocated.stepHistory → tokens reserved for history
// budget.allocated.toolSchemas → tokens for tool definitions
// budget.remaining → tokens still available

The scratchpad-write / scratchpad-read built-in tools let agents persist notes outside the context window. Notes survive compaction and are available across tool calls.

ACTION: scratchpad-write({"key": "plan", "content": "Step 1: search, Step 2: write report"})
Observation: {"saved": true, "key": "plan"}
ACTION: scratchpad-read({"key": "plan"})
Observation: {"key": "plan", "content": "Step 1: search, Step 2: write report"}

This implements Anthropic’s recommended structured note-taking pattern for long-horizon tasks.

Every tool result is now tracked as a typed ObservationResult:

import { categorizeToolName, deriveResultKind } from "@reactive-agents/reasoning";
// Category is automatically derived from tool name
// "file-write" → category: "file-write", resultKind: "side-effect"
// "web-search" → category: "web-search", resultKind: "data"
// "file-read" → category: "file-read", resultKind: "data"
// any error → category: "error", preserveOnCompaction: true

.withAgentTool() now creates real sub-agents with clean context windows:

const coordinator = await ReactiveAgents.create()
.withProvider("anthropic")
.withReasoning()
.withTools()
.withAgentTool("researcher", {
name: "researcher",
description: "Research specialist for web searches",
provider: "anthropic",
model: "claude-haiku-4-5-20251001",
maxIterations: 5,
systemPrompt: "You are a research specialist. Search the web and summarize findings.",
})
.build();
// coordinator can now call "researcher" as a tool
// Sub-agent runs with clean context + focused prompt
// Returns structured: { subAgentName, success, summary, tokensUsed }

Sub-agents are depth-limited to 3 levels (MAX_RECURSION_DEPTH) to prevent infinite delegation.

For ad-hoc delegation where you don’t know ahead of time what sub-tasks the agent will need to delegate, use .withDynamicSubAgents(). This registers the built-in spawn-agent tool, which the model can invoke freely at runtime:

const agent = await ReactiveAgents.create()
.withTools()
.withDynamicSubAgents({ maxIterations: 5 })
.build();

The model calls spawn-agent(task, name?, model?, maxIterations?) whenever it decides a subtask benefits from a clean context window. Sub-agents inherit the parent’s provider and model by default.

Comparison:

ApproachWhen to use
.withAgentTool("name", config)Named, purpose-built sub-agent with a specific role
.withDynamicSubAgents()Ad-hoc delegation at model’s discretion, unknown tasks

Depth is capped at MAX_RECURSION_DEPTH = 3. Spawned sub-agents do not inherit the spawn-agent tool by default, naturally containing recursion.

Prompt templates automatically select tier-specific variants when available:

TemplateAvailable Tiers
reasoning.react-systembase, :local, :frontier
reasoning.react-thoughtbase, :local, :frontier

The system resolves reasoning.react-system:local first, then falls back to reasoning.react-system.

Verified with cogito:14b (Ollama) across 9 scenarios:

CategoryAvg StepsAvg TokensAvg Time
Tool use (S1-S5)6.31,8994.1s
Error recovery (S6)10.02,6305.1s
Compaction stress (S7)13.03,9788.9s
Pure reasoning (S8)1.01,0172.5s
Overall (9 scenarios)6.42,0934.4s

All well within targets: <= 8 steps, <= 5,000 tokens, <= 15s.