Imagine you hire a brilliant consultant. They have two PhDs, speak seven languages, and solve problems you didn’t even know you had. You sit them in a room and say: “I need you to refactor the authentication in the project.”

The consultant looks at you, nods, and asks: “What project?”

You haven’t given them access to the code. You haven’t explained the architecture. They don’t know if you use JWT tokens or session cookies. They don’t know what language you’re using, how many microservices you have, or why the last migration attempt ended in disaster.

That consultant is your LLM. And you just made the mistake that 90% of people working with AI agents make: worrying about the brain instead of worrying about what the brain sees.

Prompt engineering is dead. Long live context engineering.

I’ve been seeing the same conversation for months in every forum, every Twitter thread, every team meeting: “GPT-5 or Claude Opus?”, “which model is better for code?”, “which one reasons better?”

And the answer, every time I run the numbers, is the same: it doesn’t matter. Well, it doesn’t exactly not matter. But the difference between one top model and another top model is marginal compared to the difference between giving it good context or giving it garbage.

A mediocre model with perfect context beats a top model with garbage context. Always. No exceptions.

This has a name: context engineering. And no, it’s not the same as prompt engineering.

Prompt engineering is writing a good prompt. It’s choosing the right words, structuring the request, adding examples. It’s important, but it’s just one piece.

Context engineering is designing everything the model sees: what information goes in, in what order, what gets discarded when it doesn’t fit, what gets compressed, what gets preserved at all costs. It’s information architecture for LLMs.

Prompt engineering is writing a good question. Context engineering is deciding which books the student has on their desk before starting the exam.

The four phases of memory: a lifecycle you don’t see

OpenAI recently published two Cookbook articles that break down how context management works in agents with long-term memory. It’s not RAG. It’s not a vector database. It’s a state system that works like a field notebook with strict rules.

The pattern is local-first and state-based: a structured state object that travels with the agent and gets updated at each phase.

flowchart TD
    A["1. INJECTION\n(session start)"] --> B["2. DISTILLATION\n(during conversation)"]
    B --> C["3. CONSOLIDATION\n(post-session)"]
    C --> D["4. TRIMMING\n(preservation)"]
    D -->|"New session"| A

    A1["Render state as YAML\n+ global memories (max 6)\n+ precedence rules"] -.-> A
    B1["save_memory_note()\nValidate durability\nRequire actionability\nReject PII and speculation"] -.-> B
    C1["Async job\nMerge session → global\nDeduplication with LLM\nFilter ephemeral notes"] -.-> C
    D1["TrimmingSession: last N\nReinject trimmed notes\ninto system prompt"] -.-> D

    style A fill:#2d3748,stroke:#4a9eed,color:#fff
    style B fill:#2d3748,stroke:#ed9a4a,color:#fff
    style C fill:#2d3748,stroke:#9a4eed,color:#fff
    style D fill:#2d3748,stroke:#4aed5c,color:#fff

Phase 1: Injection — the exam desk

When a session starts, the agent assembles its initial context. It’s not random. It’s a concrete structure:

  • YAML frontmatter with user state (preferences, configuration).
  • List of global memories: maximum 6, ordered by recency. Why 6? Because more than 6 compete with each other for the model’s attention and start to dilute. Less is more.
  • <memory_policy> block with explicit precedence rules.

Precedence rules are key: Current input > Session memory > Global memory > Recency within the same scope. If the user tells you “I use Vim now” but your global memory says “uses VS Code”, what they just said wins. Seems obvious, but without explicit rules the model sometimes clings to what it “remembers” over what you’re telling it.

Phase 2: Distillation — capture without contamination

During the conversation, the agent can capture memories in real-time with a tool like save_memory_note(). But not everything goes. The tool has strict guardrails:

  • Validates durability: “user wants pizza tonight” isn’t a durable memory. It gets rejected.
  • Requires actionability: the memory has to be useful for something in future sessions.
  • Rejects PII: full names, addresses, card numbers. Out.
  • Rejects speculation: “I think the user prefers Python” isn’t a fact. It’s an assumption.
  • Requires user confirmation: before saving, it asks.

This filter is brutal, and for good reason. A contaminated memory poisons all future sessions. It’s like having a false note in your field notebook: every time you consult it, you make decisions based on incorrect information.

Phase 3: Consolidation — the nightly cleanup

After each session, an async job collects session notes and merges them with global memory. It’s not an append. It’s intelligent consolidation:

  • LLM-assisted deduplication: if two notes say the same thing with different words, they get merged.
  • Filtering of ephemeral notes: anything with “this time”, “today”, “right now” gets discarded.
  • Conflict resolution by recency: if a new note contradicts an old one, the new one wins.

Think of this as the person who cleans your desk at the end of the day. They don’t throw everything away — they save what’s important, consolidate the post-its that say the same thing, and throw away the ones that no longer apply.

Phase 4: Trimming — cutting without losing

When the history grows too long, you have to trim. TrimmingSession keeps only the last N interactions. But — and this is important — the memory notes that lived in the trimmed turns don’t get lost. They get reinjected into the system prompt of the next turn.

It’s like tearing out the old pages of a notebook but copying the important notes to the first page before throwing them away.

Trimming vs Summarization: two philosophies, one dilemma

For managing short-term memory (conversation history within a session), there are two fundamental techniques. Each with advantages and traps.

flowchart LR
    subgraph Trimming["Trimming (Last-N Turns)"]
        direction TB
        T1["Complete history\n(40 turns)"]
        T2["Cut turns 1-30"]
        T3["Keep turns 31-40\n(intact, unaltered)"]
        T1 --> T2 --> T3
    end

    subgraph Summarization["Summarization (Compression)"]
        direction TB
        S1["Complete history\n(40 turns)"]
        S2["LLM summarizes turns 1-30\nin ~400 tokens"]
        S3["Inject synthetic summary\n+ turns 31-40"]
        S1 --> S2 --> S3
    end

    style Trimming fill:#1a2332,stroke:#4a9eed,color:#fff
    style Summarization fill:#2a1a32,stroke:#9a4eed,color:#fff

Trimming: the deterministic guillotine

Scans the history backwards, keeps the last N complete interactions, and everything before disappears.

Advantage: total fidelity of recent context. What remains hasn’t been altered, summarized, or interpreted. It’s the original messages, as-is.

Disadvantage: abrupt amnesia. Turn N-1 exists in full detail. Turn N-2 doesn’t exist at all. There’s no gradual degradation — there’s a binary cut between “I remember everything” and “I remember nothing”.

It’s like the memory of a goldfish with an external hard drive: the last 10 seconds are perfect, everything before simply doesn’t exist.

Summarization: compression with risk

When history exceeds a threshold, an LLM compresses the old parts and injects them as a synthetic user/assistant pair at the beginning of the conversation. The summary prompt has strict principles:

  • Preserve milestones (decisions made, agreements).
  • Maintain temporal order.
  • Detect contradictions and mark them.
  • Uncertain facts marked as “UNVERIFIED”.
  • Maximum 400 tokens per summary.

Advantage: you preserve the essence of the entire conversation. No abrupt amnesia. The model “knows” that 30 turns ago you decided to use PostgreSQL instead of MongoDB, even though it no longer has the original messages.

Disadvantage: compounding errors. If an incorrect fact enters the summary, it poisons all future behavior. And since the summary is generated with an LLM, it’s not immune to hallucinations. A model that summarizes poorly generates an incorrect summary that the next turn treats as absolute truth.

The audacity is incredible: you use an LLM to summarize the history of another LLM, and if the first one makes a mistake, the second inherits the error without knowing it.

To distinguish real from synthetic, each record carries observability metadata: {"synthetic": bool, "kind": "...", "summary_for_turns": "..."}. So at least you can audit which part of the context is original and which part is a summary.

You’re already doing this (and didn’t know it)

If you use Claude Code, you already have a context engineering system running. You just didn’t design it — Anthropic did. But if you stop to look, the pieces fit:

Your global CLAUDE.md + per-project CLAUDE.md + SKILL.md files = manual injection. You’re deciding what context the model sees when starting each session. You’re the one choosing which “books go on the desk”.

The ~/.claude/projects/*/memory/ directory where Claude Code saves notes between sessions = direct implementation of the injection + distillation pattern. The model captures facts during the session and retrieves them in the next one.

Automatic context compression that Claude Code does when conversations get long = trimming + summarization. You don’t see it because it’s transparent, but every time your session exceeds a certain threshold, part of the conversation gets compressed.

Skills (/blog, /commit, etc.) = specialized context injection on demand. Instead of loading all possible context at the beginning, you load only what you need when you need it.

And here’s what I find most interesting: the quality of your CLAUDE.md determines the quality of your agent much more than which model you use. A well-structured CLAUDE.md — with clear conventions, correct paths, documented architecture decisions — turns any decent model into a useful assistant. An empty or disorganized CLAUDE.md turns the world’s best model into a brilliant consultant locked in a room without light.

Prompt debt: the technical debt you don’t see

Do you know the concept of technical debt? Code that works but accumulates future problems. Shortcuts you pay for later.

Context engineering has its own debt: prompt debt. It’s all those config files, instructions, memories, and notes that accumulate and nobody maintains.

A CLAUDE.md with contradictory instructions. Global memories that no longer apply. Skills with paths that changed three months ago. Implicit precedence rules that nobody documented.

Every piece of obsolete context is noise. And noise competes with signal for the model’s attention. More noise → worse results. Not because the model is worse, but because you’re giving it garbage mixed with useful information and expecting it to know how to distinguish them.

The hygiene of your context engineering layer is as important as the hygiene of your code. Maybe more, because a bug in code fails loudly. A bug in context fails silently — the model simply makes worse decisions without anyone noticing.

The actionable: what you can do today

All this theory is fine, but what do you do with it on a Tuesday morning?

1. Audit your CLAUDE.md (or equivalent). Does it have contradictory instructions? Paths that no longer exist? Rules that no longer apply? Clean up. Every extra line is noise.

2. Order your context by stability. What never changes goes first (conventions, stack). What changes often goes at the end (current task). This maximizes cache hits and reduces costs. It’s not cosmetic — it’s economic.

3. Establish explicit precedence rules. If the user says one thing and memory says another, who wins? If you don’t define it, the model decides for you. And you’re taking a risk.

4. Filter aggressively. Not everything deserves to be remembered. An architecture decision, yes. That the user prefers tabs over spaces, maybe. That it was raining when the session started, no.

5. Distinguish real context from synthetic. If you use summarization, mark summaries as such. When something fails, you need to know if the model was working with real data or a potentially incorrect summary.

6. Treat context maintenance as technical debt. Put it in the backlog. Review it periodically. It’s not glamorous, but it’s what separates an agent that works from one that hallucinates.

The skill nobody puts on their resume

Context engineering is the invisible skill. It doesn’t show up in job postings. It has no certification. There’s no 40-hour course on Udemy with a diploma at the end.

But it’s what separates people who “use ChatGPT” from people who build agents that work. It’s the difference between asking an LLM something and designing a system where the LLM has everything it needs to give you the right answer.

Next time your agent does something stupid, before blaming the model, look at what context you were giving it. There’s a good chance the problem isn’t the brain — it’s what the brain was seeing.

And that, unlike the model, you do control.


Sources: The two OpenAI Cookbook articles on Context Engineering for Long-Term Personalization and Short-Term Memory Management with Sessions. If you’re interested in how the inner loop of a coding agent works, read Your AI coding agent is a while loop with delusions of grandeur. And if you want to understand why prompt order affects cost, Why 99% of what you send to Claude is already cached.

This article was originally written in Spanish and translated with the help of AI.