The simplest idea in agent design, and why almost everyone, including the AI that helps me build, keeps missing it.

In short: an AI agent works best when the model calling it knows nothing about its insides. It sees a name, a description and a few typed parameters, nothing more, and decides when to reach for it, the way you just know you can remember something. The agent’s result comes back into the conversation, and only its result. This is part one of four.

When you need to remember something, you don’t open a database. You don’t look up a schema, pick a table, or decide which index to use. You just know you can remember, and when something in the conversation sounds important, you do. Later, the memory is simply there.

That’s the whole idea. It’s the pattern behind every agent in a Brainstem, and I’ve watched people and models talk right past it for months. This afternoon I watched it happen again, so I’m writing it down.

A four-part series on one agent pattern.

  1. The Caller Knows Nothing (you are here)
  2. The Agent Writes the Next Prompt
  3. “Anthropic Already Does This.” Let’s Check.
  4. Antifragile by Construction

Two small files

A Brainstem (see The Personless Harness) ships with two memory agents. The first saves:

class ManageMemoryAgent(BasicAgent):
    metadata = {
      "name": "ManageMemory",
      "description": "Saves information to persistent memory for future
        conversations. You MUST call this tool whenever the user asks you to
        remember something ... or the information will be lost.",
      "parameters": { "memory_type": ..., "content": ..., "importance": ... }
    }
    def perform(self, **kwargs):
        # clamp, default, store; none of this is the caller's business

The second recalls. It doesn’t even wait to be called: before every turn it puts what it knows straight into the context.

class ContextMemoryAgent(BasicAgent):
    def system_context(self):
        return "<memory>...what you already know...</memory>"

Look at what the model, the caller, knows about these. It knows a name, a sentence about what the thing is for, and a few parameters. That’s all. It doesn’t know there’s a SQLite file. It doesn’t know about clamping, deduplication, or how a timestamp is formatted. It doesn’t need to. When the user says “remember my favorite color is blue,” the model reaches for the thing in its context that sounds like remembering. When the next turn starts, the memory is already in front of it.

We just know we have something we can invoke in our current context that lets us remember, if that sounds important in the current conversation. That’s it.

The rules

1. The caller knows nothing about the inside. Name, description, parameters. Storage, checks, shaping and formats stay hidden in perform().

2. The description is the whole interface. It says what’s needed and when it matters. Every bit of quality you want lives there, not in glue code around it.

3. The caller has full liberty. The description invites; it never confines. The caller may put anything it judges useful into a parameter. The agent takes whatever comes and handles it, and that freedom is where the surprising, good behavior comes from.

How smart people get it wrong

Today I asked my AI collaborator to make our pipeline’s lenses (small agents that each do one language job) “dynamic, with the caller armed to give the right information in the right shape.” It came back with a careful plan. Its centerpiece was a shared caller: host code that would read each lens’s contract and fill its parameters from the case, turning free-text fields into fixed lists of choices.

That’s thoughtful, rigorous, and exactly backwards. It took a few corrections to get it there:

  • First wrong turn: cages. Turning every field into a fixed list of choices does give you the “right shape,” and it kills the caller’s ability to add what you didn’t think to ask for. Constrain only where something downstream truly needs exact values.
  • Second wrong turn: a smart host. A caller that knows each agent’s insides is coupling. Every new agent now needs host code. That’s the machinery we were trying to delete.
  • The fix: nothing in between. The transcript is the input. The agents are loaded beside it. The model invokes what sounds right. Results come back into the transcript. Done.

It’s the same reason Unix pipes worked. The tools knew nothing about each other, and they composed through a plain, shared stream. Here the stream is the conversation.

The evidence is in a chat log

I asked a small model to “just make up” an agent that drafts content from a chat transcript. Its first attempt, built from a thin description, returned count: 0 twice: empty. The second, built with a precise description (who reads the draft, what goes in it, nothing sent automatically), came back as a real tool. Same model, same plumbing. The only difference was the description.

The same log shows the other half. Several times the model said it had saved or recalled something when it hadn’t, or said it hadn’t when it had. The truth was in the tool log the whole time, outside its view. When a result doesn’t come back into the conversation, the caller has to guess. So the loop has to be closed: whatever an agent does goes back into the context, the way memory does.

The transcript carries only what came out

Here is the part that makes it compound. The transcript doesn’t hold the agent’s insides, its prompts, or its working state. It holds only what came out: the relevant result of the agent that was just called.

When ContextMemory runs, what lands in the conversation is the handful of memories that matter, not the database. When LearnNew runs, what lands is “created TranscriptFollowUp, here’s what it does,” and on the next turn that new agent is hot-loaded, sitting in the context beside all the others, ready to be invoked. The conversation didn’t just get information. It got a new capability.

So every step downstream starts from a better place. The next call sees the previous result and can build on it: use the recalled preference, call the agent that didn’t exist a minute ago, refine the draft that came back. Nobody wired those steps together. Each one just leaves its output in the shared stream, and the next one picks up whatever helps.

4. The transcript carries only outputs. Each agent leaves just its relevant result. Nothing from inside it leaks, and nothing that matters is lost. Downstream steps use it to their advantage, and that’s how the whole thing gets better as it goes.

Where this goes next

That’s the core. The rest of the series builds on it: what the agent sends back and how to time travel, a fair, cited check against SKILL.md and Anthropic’s whole ecosystem, and why the pattern is antifragile.

Why it matters

If the caller has to know the insides, every agent is a project. If the caller knows nothing, an agent is a file you drop in. Anyone can write one, any model can use one, and agents you never planned to combine start working together, because each one just says plainly what it’s for.

You don’t build intelligence into the plumbing. You give the model things it can reach for, describe them well, and get out of the way.

It’s that simple. That’s why it’s so easy to miss.

Go deeper: two free companion books, written side by side with the same chapters so you can compare them page for page: The Brainstem Agent Programming Language and The Claude Agent Programming Language. Both are runnable, and the Claude one cites Anthropic’s docs throughout.