Skip to content

Context Engineering

Last updated 10 days ago · ee9a147

Updated 10 days ago

"fix(docs,runtime): make the anti-rot gate honest + kill the error-suggestion lies (#58)" · ee9a147 · 2026-07-12

  • ### Using Context Profiles
  • ### Inspecting the tier profiles
  • ## Context Budget

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 agent2 = await ReactiveAgents.create()
.withProvider("anthropic")
.withModel("claude-haiku-4-5-20251001")
.withContextProfile({
tier: "mid",
toolResultMaxChars: 1000, // Override the tier default
toolResultPreviewItems: 5, // Array items shown before overflow
})
.build();
PropertyDescription
tier"local" | "mid" | "large" | "frontier"
toolResultMaxCharsMax chars per tool result before compression
toolResultPreviewItemsArray items shown in a compressed tool result
maxTokensContext-window ceiling used by the pressure gates
maxIterationsMaximum kernel iterations before failing
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.

Prompt assembly happens inside the ReAct kernel — there is no public buildContext entry point to call directly. What you can import are the per-tier defaults the kernel uses, so you can inspect or extend them:

import { CONTEXT_PROFILES } from "@reactive-agents/reasoning";
const mid = CONTEXT_PROFILES["mid"];
console.log(mid.toolResultMaxChars, mid.toolResultPreviewItems);

To change how context is assembled for a run, override the profile on the builder with .withContextProfile({ ... }) (shown above) rather than calling the internal assembler yourself.

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 kernel allocates the model’s context window across sections — step history, tool schemas, memory, and the current task — and adapts the split as iterations progress. This budgeting runs automatically inside the loop; it is driven by the maxTokens and toolResultMaxChars fields of the active ContextProfile, so you tune it through .withContextProfile({ ... }) rather than a standalone API.

The recall meta-tool (part of the Conductor’s Suite) lets agents persist and retrieve notes outside the context window via native function calling. Notes survive compaction and are available across tool calls.

The model writes a note by calling recall with a store action, and reads it back with a retrieve action. Enable it via .withMetaTools({ recall: true }):

// Write: model emits tool_use { name: "recall", input: { action: "store", key: "plan", content: "Step 1: search, Step 2: write report" } }
// Read: model emits tool_use { name: "recall", input: { action: "retrieve", key: "plan" } }
// Result returned as tool_result message: { 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.