AI agents are simpler than you think. Three concepts, one metaphor, and a live reconstruction.
Text in, text out. Stateless. Smart but alone.
Marlin's brainStatelessness in practice: every API call starts fresh. The model has zero memory of your previous conversation unless you resend it. "Memory" is an illusion created by the harness replaying history.
Under the hood: models are transformer neural networks. Key parameters: temperature (randomness), top-p (sampling), max tokens (output length). Token economics: input tokens are cheaper than output tokens. Context window size determines how much the model can "see" at once.
What it does: takes a prompt, produces a completion. One call, no memory of previous calls.
Examples: Claude (Anthropic), GPT-4 (OpenAI), Gemini (Google), Llama (Meta), Mistral, Command R (Cohere)
Key insight: the model is a function — f(prompt) = completion. It cannot read files, browse the web, or remember you.
A model in a loop with tools. Decides, acts, observes, repeats.
Marlin with fins and eyesThe loop core: while run_one_turn(state): pass. The LoopState holds messages, turn count, and transition reason. Each turn: call API, append response, check stop_reason — if "tool_use", execute tools and continue. If not, return.
Tool dispatch: a flat dictionary TOOL_HANDLERS = {"bash": handler, "read_file": handler, ...}. Adding a tool means adding one dict entry + one schema. The loop never changes.
Subagents: sub_messages = [{"role":"user","content":prompt}] — a fresh list IS the isolation. Only the final text returns. Children don't get the task tool (no recursion). Safety limit: 30 iterations.
Worktree isolation: git worktree add -b wt/{name} gives each subagent its own code copy. Branch naming: wt/{name}. Closeout: keep (preserve for review) or remove (force-delete).
Three ingredients: the model (brain), the loop (persistence), the tools (hands).
The model decides everything — what tool to call, when to stop, how to combine results. The "agent framework" is just a while loop.
Examples: Claude Code, Cursor Agent, Devin, Cline, Aider, Open Interpreter, OpenCode
Everything the model can see. Its working memory. The real differentiator.
Marlin vs Dory -- same brain, different capacity4 compaction levers (progressive):
1. Persist large output: results >30K chars saved to .task_outputs/, replaced with 2K char preview in <persisted-output> tags
2. Micro-compact: silent per-turn pass — tool results older than 3 turns replaced with [Earlier tool result compacted]
3. Auto-compact: at 50K char threshold, full transcript saved to .transcripts/, LLM summarizes preserving: goal, findings, files, remaining work
4. Manual compact: model calls compact tool with optional focus parameter
Error recovery: continuation on max_tokens (3 retries), auto-compact on prompt-too-long, exponential backoff (base * 2^attempt + jitter, max 30s)
Context = prompt + history + tool results. It's all just text stacked in a window.
When full: oldest parts get compacted (summarized or dropped). This is why long sessions degrade.
Sizes: 8K (small), 128K (standard), 200K (large), 1M (Claude Opus)
Four stages checked in order. First definitive answer wins.
Circuit breaker: after 3 consecutive denials, suggests switching to plan mode. Workspace trust: checked via .claude_trusted marker.
External shell commands triggered at lifecycle events. Configured in settings.json.
HOOK_EVENT, HOOK_TOOL_NAME, HOOK_TOOL_INPUT.Timeout: 30s per hook. Trust gate: requires .claude_trusted or SDK mode.
Bash run shell commandsRead read files + imagesWrite create filesEdit modify filesGrep search contentGlob find files by patternTask spawn subagentsWebSearch search the internetStep through an example execution. Watch context grow, fill, and get compacted.
Configuration files that shape agent behavior. Your intelligence, crystallized into file structure.
Three layers of config: global (your identity and rules), user (your preferences, skills, and hooks), and project (project-specific instructions and code). The agent reads all three at startup and merges them by priority.
Persistent file-based store in .memory/. Four categories:
Each memory is one .md file with YAML frontmatter. Index capped at 200 lines. Never store: code structure, temp state, or secrets.
SystemPromptBuilder assembles 6 sections in order:
DYNAMIC_BOUNDARY marker separates cacheable from volatile sections.
Persistent JSON files in .tasks/. Fields: id, subject, status (pending/in_progress/completed/deleted), blockedBy[], blocks[]. Dependency resolution: completing a task removes it from all others' blockedBy. Background tasks run in daemon threads with stall detection (45s threshold).
External capability servers via stdio JSON-RPC. Tool namespacing: mcp__{server}__{tool}. Three risk levels: read/write/high. Plugin manifests in .claude-plugin/plugin.json. Native tools take priority on name collisions. Result preview capped at 500 chars.
Two-layer pattern: Layer 1 — skill names + one-line descriptions injected into system prompt (~100 tokens per skill, cheap). Layer 2 — full skill body loaded on-demand via load_skill tool call (~2000 tokens). Each skill is a SKILL.md file with YAML frontmatter in a skills/*/ directory. This prevents prompt bloat while preserving capability access.
Write in an editor. Read it twice. Dehumanize -- you're configuring a system, not chatting.
Create skills, agents, reusable context. Write instructions once, reuse forever.
Don't teach the model to be smart. Give it the right information. Your job is congruence.
Every wasted token is a wasted thought. Plan before you execute.
How an agent built the Finding Memo presentation. Each step is the loop in action.