Skip to content

Key Concepts

This page is the model. 2 primitives, 4 compositions, N named patterns, the Injection cross-cut, the infrastructure layer underneath. Once these are in your head, every agent paper, every framework, every system you encounter can be located on the grid — including yours.

If you haven’t yet, read Why agentfootprint? first. It’s the why. This page is the what.

TermMeaning
LLMOne prompt in, one response out. The atomic invocation.
AgentAn LLM that loops: calls tools, reads results, calls more tools, responds. Agent = ReAct — if it doesn’t loop-with-tools, it isn’t an Agent.
ReAct”Reasoning + Acting” — the loop where an LLM thinks, acts (calls a tool), observes the result, and repeats. (Yao et al. 2022)
CompositionHow primitives arrange: Sequence (one after another), Parallel (at the same time), Conditional (branch on a decider), Loop (iterate until quality bar).
PatternA named configuration — Reflexion, Tree-of-Thoughts, Hierarchy. Every named paper is a recipe of primitives + compositions, not a new class.
Context engineeringWhat you inject into an Agent’s three slots (system / messages / tools). Skills, Steering, Guardrails, RAG, Memory, Tool APIs — all live here.
InjectionThe unified primitive behind every flavor of context engineering — slot × trigger × cache. Skills, Steering, Instructions, Facts, Memory, RAG all reduce to this.
SlotOne of three fixed regions of every LLM call: system, messages, tools. Fixed by the LLM API surface.
TriggerWhen an Injection fires: always (build-time), rule (predicate), on-tool-return (after a tool result), llm-activated (LLM calls read_skill).
CacheHow stable the injection’s content is across iterations. Determines where the framework places provider cache markers (80–90% cheaper prefixes for stable content).
NarrativeA structured execution trace — what happened, in what order, with what data. Not logs — connected entries with provenance.
RecorderA passive observer that collects data (tokens, cost, tool usage) during execution without affecting behavior.
ProviderThe LLM backend — Claude, GPT, Bedrock, Ollama, or your own. Swap with one line.

agentfootprint organizes AI work into five layers. Each layer is built from the one above it.

PRIMITIVES (2 — atomic invocation units)
1. LLMCall — one call
2. Agent — a loop of calls + tools + decisions (= ReAct)
COMPOSITIONS (4 — how primitives arrange)
1. Sequence — one after another
2. Parallel — at the same time, with merge
3. Conditional — branch on a decider
4. Loop — iterate until quality bar
PATTERNS (N — named configurations; every named paper is a recipe)
ReAct = Agent (default)
Reflexion = Sequence(Agent, LLM-critique, Agent)
Tree-of-Thoughts = Parallel(Agent × N) + LLM-rank
Self-Consistency = Parallel(Agent × N) + majority-vote
Debate = Loop(Agent × 2 + judge)
Map-Reduce = Parallel(Agent × N) + LLM-merge
Swarm = Agent whose tools are other Agents
CONTEXT ENGINEERING (cross-cutting — Injection primitive into Agent slots)
Steering → system-prompt (always-on)
Instruction → system-prompt|messages (rule-gated)
Skill → system-prompt + tools (LLM-activated)
Fact → system-prompt|messages (always-on data)
Memory → messages (cross-run)
RAG → messages (retrieve + score-threshold)
FEATURES (infrastructure)
Providers · Mocks-first · Observability · Pause/Resume · Resilience · Reliability gate

Two theses to hold in your head:

  1. Agent = ReAct. Not “Agent with ReAct as default.” The Agent primitive IS the ReAct loop. If it doesn’t loop-with-tools, it isn’t an Agent — it’s an LLM call.
  2. Every named pattern = a composition of the 2 primitives + 4 compositions. Don’t invent new Agent classes for every paper (that’s LangChain’s mistake). Express papers as recipes.

All runners share the same shape — create → configure → build → run → observe.


The atom. One prompt in, one response out. No tools, no loop.

examples/core/01-llm-call.ts (region: build)
const llm = LLMCall.create({
provider: provider ?? exampleProvider('core', { reply: "It's sunny in San Francisco." }),
model: 'mock-weather',
temperature: 0.2,
})
.system('You are a terse weather assistant. One sentence answers.')
.build();

Use for: summarization, classification, translation, extraction.

2. Agent — a loop of calls + tools + decisions (= ReAct)

Section titled “2. Agent — a loop of calls + tools + decisions (= ReAct)”

Agent = ReAct. This is the definition. The Agent primitive IS the ReAct loop — thinks, acts (tool call), observes, repeats.

examples/core/02-agent-with-tools.ts (region: build)
const agent = Agent.create({
provider: provider ?? exampleProvider('feature', { respond: weatherRespond }),
model: 'mock',
maxIterations: 5,
})
.system('You answer weather questions using the `weather` tool.')
.tool({
schema: {
name: 'weather',
description: 'Get current weather for a city.',
inputSchema: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
},
execute: async (args) => `${(args as { city: string }).city}: sunny, 72°F`,
})
.build();

Use for: research, code generation, customer support, data analysis — anywhere you need tools-in-a-loop.

agentfootprint’s Agent loops back to SystemPrompt every iteration, not just CallLLM. Slots recompose every pass — injections that fired on the previous tool result get a chance to recompose the next prompt. Classic ReAct freezes the slots at the top of the turn.

Classic ReAct loops back to CallLLM (slots frozen, 12 tools every iteration). Dynamic ReAct loops back to SystemPrompt (slots recompose, tools shrink from 1 to 5 as skills activate).

IterationClassic ReActDynamic ReAct (agentfootprint)
112 tools shown1 tool (read_skill)
212 tools shown5 tools (skill activated)
312 tools shown5 tools

📖 Dynamic ReAct guide for the full taxonomy of what this unlocks.


The four ways primitives arrange. Every named pattern is built from these.

The 4 control flows: Sequence (linear chain A → B → C), Parallel (fan-out + fan-in), Conditional (diamond branch), Loop (cycle back).

Chains multiple runners into a sequential pipeline. Each step’s output flows into the next via pipeVia (when shapes don’t match):

examples/core-flow/01-sequence.ts (region: build)
const pipeline = Sequence.create({ name: 'IntakePipeline' })
.step('classify', classify)
.pipeVia((label) => ({ message: `Intent: ${label.trim()}` }))
.step('respond', respond)
.build();

Use for: multi-step workflows, classify-then-respond, ETL pipelines.

Run multiple runners simultaneously and merge their results. Merge can be a function (deterministic) or an LLM (synthesis):

examples/core-flow/02-parallel.ts (region: build)
// STRICT (default): any branch failure → whole Parallel throws
const committee = Parallel.create({ name: 'Committee' })
.branch('legal', brief('legal'))
.branch('ethics', brief('ethics'))
.branch('cost', brief('cost'))
.mergeWithFn((results) =>
Object.entries(results)
.map(([id, r]) => ` ${id}: ${r}`)
.join('\n'),
)
.build();

Use for: multi-perspective analysis, A/B comparison, ensemble approaches.

if/else routing between runners. First matching predicate wins; fallback runs .otherwise().

examples/core-flow/03-conditional.ts (region: build)
const triage = Conditional.create({ name: 'Triage' })
.when(
'urgent',
(input) => /\b(urgent|asap|outage|down|critical)\b/i.test(input.message),
urgent,
)
.otherwise('normal', normal)
.build();

Use for: triage, content classification, intent-based routing.

Repeat a runner until a condition is met or a budget is exhausted. The Reflexion recipe (below) uses Loop under the hood.

See examples/core-flow/04-loop.ts for the full pattern.


Every paper in the agent literature is a composition of 2 primitives + 4 compositions. Same alphabet, different word.

6 named multi-agent patterns reduce to compositions of the same 4 control flows: Swarm = Loop(Parallel(Agent×N) → merge); Tree-of-Thoughts = Loop(Parallel(Agent×N) → Conditional(score)); Reflexion = Loop(Agent → Conditional(critique) → Agent); Debate = Parallel(Agent_pro, Agent_con) → Agent_judge; Router = Conditional → Agent_A | Agent_B | Agent_C; Hierarchical = Agent_planner → Sequence(Agent_worker×N) → synth.

PatternBuilt fromPaper
ReActAgent (default)Yao 2022
ReflexionSequence(Agent, LLM-critique, Agent) — wraps LoopShinn 2023
Tree-of-ThoughtsParallel(Agent × N) + LLM-rankYao 2023
Self-ConsistencyParallel(Agent × N) + majority-voteWang 2022
DebateLoop(Agent × 2 + judge)Du 2023
Map-ReduceParallel(Agent × N) + LLM-mergeDean 2004
SwarmAgent whose tools are other AgentsOpenAI 2024

Iterative propose/critique/revise. The recipe is Sequence(Agent, critique-LLM, Agent) wrapped in a Loop that exits when the critic says DONE:

examples/patterns/02-reflection.ts (region: reflexion-recipe)
const runner = reflection({
provider: provider ?? exampleProvider('pattern', { respond: () => replies[i++ % replies.length]! }),
model: 'mock',
proposerPrompt: 'Write or revise a short poem about night.',
criticPrompt:
'Critique the poem. When it is good enough include the marker DONE.',
maxIterations: 5,
});

LLM-driven routing to specialist runners. A route function (sync, pure) decides which agent handles each turn; handoffs continue until route returns undefined:

examples/patterns/06-swarm.ts (region: swarm-recipe)
const router = swarm({
agents: [
{ id: 'triage', runner: triage },
{ id: 'billing', runner: billing },
{ id: 'tech', runner: tech },
],
// Route function — pure sync over the current message. First turn goes
// to triage, then to billing or tech based on content, then halts.
route: (input) => {
const msg = input.message.toLowerCase();
if (msg.includes('[billing]')) return undefined; // billing done → halt
if (msg.includes('[tech]')) return undefined; // tech done → halt
if (msg.includes('refund') || msg.includes('bill')) return 'billing';
if (msg.includes('status') || msg.includes('error')) return 'tech';
return 'triage'; // first turn
},
maxHandoffs: 5,
});

Use for: customer support triage, multi-domain assistants, expert routing.

See examples/patterns/ for the rest — Tree-of-Thoughts, Self-Consistency, Debate, Map-Reduce all run end-to-end with npm run example examples/patterns/<file>.ts.


These are not primitives. They’re flavors of the Injection primitive — content that lands in one of the agent’s three slots (system / messages / tools) under one of four triggers.

Injection = slot × trigger × cache

Every LLM call has 3 fixed slots (system, messages, tools); every flavor lands in one slot under one of 4 fixed triggers. The grid is the entire context-engineering surface.

FlavorSlotTriggerPurpose
SteeringsystemalwaysPersona, tone, output format, safety rules
Instructionsystem or messagesruleConditional behavior (activeWhen predicate)
Skillsystem + toolsllm-activatedLLM calls read_skill('billing') to unlock body + tools
Factsystem or messagesalwaysDeveloper-supplied data (user profile, env, current time)
MemorymessagesrulePersist conversation / facts across turns
RAGmessagesrule + scoreRetrieve relevant chunks at query time

Why this grid matters — four backtrack questions

Section titled “Why this grid matters — four backtrack questions”

Because the framework owns the injection, every LLM call backtracks to four typed answers:

QuestionWhat the trace tells you
What was injected?Every flavor of content the LLM saw on this iteration
Who triggered it?Which rule fired (always / rule / on-tool-return / llm-activated)
When it fired?Which iteration of the ReAct loop, after which event
How it landed?Which slot, what position, what cache strategy

Those four answers are why contextual errors stop being invisible. See Why agentfootprint? for the full bug-class argument.

defineRAG is sugar over defineMemory({ type: SEMANTIC, strategy: TOP_K }) with RAG-friendly defaults — same plumbing, different intent label:

examples/context-engineering/07-rag.ts (region: define-and-attach)
const docs = defineRAG({
id: 'product-docs',
description: 'Product documentation chunks',
store,
embedder,
topK: 2, // up to 2 most-relevant docs per query
threshold: 0.5, // strict — drop weak matches
asRole: 'user', // chunks land as user-role context (RAG default)
});
const agent = Agent.create({
provider: provider ?? mock({ reply: 'Refunds are processed within 3 business days.' }),
model: 'mock',
maxIterations: 1,
})
.system('You answer support questions using the retrieved docs.')
.rag(docs)
.build();

See dedicated guides: Memory, Skills, Instructions, RAG, Tools.


Every primitive and composition supports:

FeatureMethodDescription
Recorders.attach(recorder)Passive observation (tokens, cost, narrative)
Event listeners.on('agentfootprint.<domain>.<event>', fn)Typed event subscription
Streamingprovider.stream(...)Token-by-token output
Pause / ResumeaskHuman / pauseHereJSON-checkpointed, cross-server resumable
Memory.memory(defineMemory({...}))Cross-run state with multi-tenant identity
Reliability gate.reliability({ preCheck, postDecide, ... })Rules-based retry / fallback / fail-fast (v2.11.5+)