Most people building agents pour their effort into one third of the system. Better prompts, deeper chain of thought, a stronger model. Then the thing produces a flawless nine step plan and nothing in the world is different. Here is what reasoning, memory and action each own, where they belong in a real codebase, and what breaks when one quietly takes over another's job.
Companion resource to Day 05 of 30 Days of AI Agents · MewCP · @mewcp_ai
The carousel covered what the three capabilities are. This guide is for the two questions that show up about ten minutes after that lands:
Read the first section once to get the vocabulary straight. After that, treat Section 2 as the wall chart, Section 3 as the thing you open when something is going wrong, and Sections 5 and 6 as the reference you keep coming back to while you build.
None of this is tied to a framework. It applies whether you wrote your own loop, you are using a graph library, or you are on a hosted runtime. Where a recommendation genuinely depends on your stack, I have said what the trade off is rather than picking for you.
One line to carry through the whole document:
Reasoning decides. Memory makes the decision right. Action is the only part that changes anything.
Reasoning owns choosing what to do next when the answer is not obvious. Given a goal, the current observation, and whatever memory handed it, reasoning picks the next move. It also decides when to stop, which people forget is part of the same job.
That is a narrower remit than most builds give it. Reasoning owns:
Durable state. The moment reasoning is responsible for remembering, every run pays to work out again what a lookup could have handed over for free.
Effects. Reasoning that "performs" an action is reasoning that invents outcomes. The model's job stops at intent.
Authorisation. Whether an action is allowed is a property of the action layer. A prompt that says "only do this if permitted" is a suggestion. It is not a control.
One model call. It receives the goal, the current observation, and a list of available actions. It returns a single structured next step with a reason. That is a complete reasoning layer.
Not a planner, not a critic loop, not three models arguing. Those are refinements, and none of them help you if memory and action are thin. Most teams reach for them far too early.
It has taken over memory when the prompt has grown to hold facts that should be looked up, and it grows again every time a new edge case turns up.
It has taken over action when the model returns text describing a completed action instead of a request to perform one. "I have updated the config" with no call behind it is the classic version.
Memory owns relevance over time. Its job is not to store things. Storage is the easy part. Its job is to answer one question before reasoning starts: of everything that has ever happened, what matters to this task, right now?
Memory owns:
Decisions. Memory supplies, it does not choose. A retrieval layer that filters results by "what the agent ought to do" has quietly become a second reasoner, with none of the first one's visibility.
Everything. Memory that never forgets is a log. Retention is part of the design, not something you bolt on later.
Deciding which fact is true. When two remembered facts conflict, memory hands over both with their timestamps. Reasoning sorts it out.
A keyed store, which can be a table, a JSON file or a dictionary, plus one function: recall(task) returns facts.
Even a hand written rule counts. "If the task mentions a deploy, fetch the last three deploy outcomes" is real memory, because it selects. The selection is the capability. The database is not.
It has taken over reasoning when retrieval has grown scoring rules, priority overrides and special cases, until the real decision is being made inside the retrieval function where nobody looks.
It has taken over action when the memory layer writes to systems other people read. Updating a ticket "so we remember it" is an action, and it belongs on the action side of the line where it can be audited and undone.
Action owns external effect. Reading the file. Calling the API. Sending the message. Writing the row. If nothing outside the model is different, nothing happened.
Action owns:
Step choice. A tool that decides whether it should run has taken reasoning's job somewhere nobody can inspect it.
Silent failure. An action that swallows an error and returns something plausible poisons every decision downstream. The observation has to be true, and that includes "this did not work."
Unbounded scope. "Run arbitrary code" is not an action. It is the absence of an action layer.
A typed function with a name, a described purpose, validated inputs, and a return value that distinguishes success from failure.
Three or four of these beat twenty vague ones. The description is part of the interface, because it is what reasoning selects against. Badly described tools produce badly chosen steps, and it looks like a model problem when it is really a documentation problem.
It has taken over reasoning when a tool branches internally on a fuzzy input, and that branching logic has quietly become policy nobody wrote down.
It has taken over memory when a tool caches results and hands back stale data without saying so, which hides state from the layer that should own it.
This is the page to pin up. It is drawn so it can be read on its own, without the text around it.

Memory to reasoning: selected facts. Not the store, not the transcript. A small, ranked set of things that bear on this task. If this hand off is carrying everything, memory is not doing its job, and reasoning is paying for that in tokens and confusion.
Reasoning to action: intent. A named step with validated arguments. The important property of this edge is that it carries a request, not a result. Reasoning does not know whether the step worked until the observation comes back.
Action to reasoning: observation. The truth about what happened, failures included. This edge is what closes the loop. An agent missing it is a plan generator with good manners.
Action back to memory: outcome. What was attempted, what it cost, what came of it. This is the edge most first builds leave out, and its absence is the reason an agent that ran yesterday is no smarter today.
Action to the world. One capability touches the world, and only one. That single property is what makes an agent auditable, testable and safe to bound. Protect it.
Capability bleed is what happens when one capability quietly takes over part of another one's job. It is the most common structural fault in a first agent build, and you usually cannot see it until it has already cost you something.
Each one below is written as: what you actually observe, what the underlying boundary violation is, and the smallest change that fixes it. The smallest change matters. These are not rewrites.

What you see. The system prompt keeps growing. Every new edge case adds a paragraph. Runs cost more than they should and behave slightly differently each time. Someone has started a document called "things to add to the prompt."
What is actually wrong. Facts that should be fetched on demand are riding along on every single call, relevant or not. Because they live in the prompt, changing one means a deployment.
Smallest fix. Take the most volatile block in the prompt, usually the one holding specific IDs, facts or recent history, and move it behind a recall(task) call. One block. Measure the token change and the failure rate. This is a twenty minute change that normally pays for itself straight away, and it makes the next block obvious.
What you see. The model claims it did things. The logs show a confident narrative and no matching API calls. Behaviour shifts when someone edits the wording of the prompt, and nobody can explain why.
What is actually wrong. The rules about what may be done, to what, and under which conditions are living in natural language, where they cannot be tested, meaningfully versioned or enforced.
Smallest fix. Find the one instruction in your prompt that reads like a rule with consequences. "Never delete anything in production." "Only email addresses on the approved list." Implement it as a check inside the tool. Leave the sentence in the prompt if you want. The difference is that now it is enforced somewhere the model cannot talk its way past. Repeat, highest consequence first.
What you see. The context window fills up. Quality drops off partway through longer runs. The fix keeps being summarisation, and the summaries keep losing the one thing that mattered.
What is actually wrong. Memory has become storage with no selection step. Appending everything is not memory. It is the absence of memory, and you are paying for it in tokens.
Smallest fix. Put one selection step in front of the transcript. Given the current task, return at most N prior items. Start crude: same entity, same error class, last few occurrences. Crude selection beats no selection by a wide margin, and it gives you somewhere to put a better retrieval strategy later without restructuring anything.
What you see. One enormous prompt doing retrieval, decision and execution instructions together. When it fails, nobody can say which part failed. Evaluation is impossible because there is only one output to grade.
What is actually wrong. All three boundaries have collapsed into each other. This is the anti pattern that generates the other three, and it is very common in prototypes that became production without anyone deciding they had.
Smallest fix. Pull the observation out first. Have the call return a structured intent, run it in code, feed the real result back in. That one split turns a monologue into a loop, and it is usually the change that turns a demo into an agent. Pull memory out second, once you have a loop to hang it on.
This is the distinction that catches nearly everyone, including me, for longer than I would like to admit.
Context is what you put in the window on this call. Memory is what works out which things are worth putting there.
Stuffing the whole history into every prompt is not memory. It is what you do instead of memory, and the bill arrives as tokens, latency and a model whose attention is spread across four thousand irrelevant lines. Real memory selects. It answers "what from before is relevant to this task" before reasoning ever starts.
In the staging example from the carousel, memory did not recall everything that ever happened to that service. It surfaced the one prior incident that changed the plan. That is the whole capability in a sentence.

A hand written function keyed on the shape of the task. If the task references a deploy, fetch the last three deploy outcomes for that service.
Costs you an afternoon. Completely predictable, trivial to debug, no infrastructure. Brittle at the edges, and every new task shape needs a new rule.
Use it when your agent has fewer than a dozen or so recognisable task types. That covers far more real systems than people expect. Skipping straight past this to a vector store is the most common piece of over engineering in the whole space.
Facts stored as rows with real fields: entity, event type, outcome, timestamp. Retrieved with an actual query.
Costs you a schema and about a week of iteration. Precise, filterable, sortable by recency, cheap to inspect and correct when it is wrong. The limit is that you have to know what the fields are in advance, so genuinely novel recall is out of reach.
Use it when your domain has stable nouns: services, customers, tickets, repositories. Most engineering workflow agents belong here and never need anything more.
Embeddings over prior episodes, retrieved by similarity, usually re ranked.
Costs you an embedding pipeline, a store, a re ranker, and a relevance problem that does not stay solved. It handles open ended recall and phrasing you did not anticipate, which is real value. It is also the hardest to debug, because when the wrong thing comes back there is often no legible reason why, and it degrades quietly without a freshness and eviction policy.
Use it when the space of relevant prior episodes is genuinely open ended, and you have already confirmed that a structured query cannot reach it. Put it behind a structured filter rather than in place of one.
Sometimes it is, and pretending otherwise wastes a week.
Append the transcript and move on if all three of these hold: the run is short and the whole exchange fits with room to spare, nothing needs to survive past the end of the session, and it is one user with one task in one sitting.
The mistake is not using a transcript. It is using one past the point where it stopped working, and reaching for summarisation instead of selection. When you catch yourself compressing history to make it fit, that is the signal to build the selection step.
There are four places anything can live: the prompt, code, storage, or behind an interface. Where you put something is a design decision with real consequences, not a convention.
| Capability | Prompt | Code | Storage | Interface |
|---|---|---|---|---|
| Reasoning | Goal framing, selection criteria, stop conditions | The loop, step cap, structured output parsing | nothing | Model endpoint |
| Memory | Nothing durable, only the selected facts, injected per call | The recall() selection logic | Facts, outcomes, embeddings | Retrieval service, if shared |
| Action | Tool names and descriptions only | Validation, permission, execution, error mapping | Audit log of effects | The tools themselves |

The reasoning behind that table:
Put in the prompt only what the model has to weigh up. Goal framing, guidance on trade offs, stop conditions, tool descriptions. Everything in the prompt gets paid for on every call, and it is enforced by nothing at all.
Put in code anything with a consequence. Permission checks, rate limits, validation, retries, the loop itself. Code is testable, versioned, and does not negotiate. If breaking a rule would be an incident, that rule does not belong in prose.
Put in storage anything that has to outlive the run. Facts, outcomes, costs, and the audit trail of what the agent actually did. Decide retention on purpose: what expires, what gets corrected, what is never deleted.
Put behind an interface anything with an external dependency or more than one consumer. A retrieval service used by three agents. A tool wrapping a third party API. The boundary buys you the ability to mock it in tests, which is what makes the agent testable at all.
There is a real trade off here and it is worth naming. Moving logic from the prompt into code buys you determinism and costs you flexibility. Every rule you harden is a rule the model can no longer adapt around when reality turns out to be shaped slightly differently from your assumptions.
The heuristic I use: harden by consequence, not by frequency. Rules whose violation is expensive or irreversible go into code immediately, even if they almost never fire. Rules that are merely usually right can stay in the prompt, where revising them costs nothing.
Three examples, chosen to cover a read only task, a task that writes, and a task where the correct behaviour is refusing to act.
| Step | Capability | What happens |
|---|---|---|
| 1 | Memory | Recall prior latency investigations on checkout. One comes back: three months ago, an N+1 query introduced behind a feature flag. |
| 2 | Reasoning | With that fact in hand, the first probe is recent flag changes rather than generic metrics. Memory has changed the starting point. |
| 3 | Action | Query the metrics API and the flag change log. Both read only. |
| 4 | Reasoning | A flag went on Monday, latency stepped up on Monday. That is enough. Stop. |
| 5 | Action | Post the finding to the investigation channel. |
| 6 | Memory | Record it: checkout latency, flag related, second occurrence. |
Two things worth noticing.
Step 5 is an action even though it only posts a message. It changes something outside the model, so it lives in the action layer and gets the same permission and audit treatment as everything else.
Step 6 is what makes the third occurrence cheap. An agent with no write back edge starts from zero next time, and you will not notice, because it still works. It is just slower and more expensive forever.
The carousel example, in full.
| Step | Capability | What happens |
|---|---|---|
| 1 | Memory | Recall prior staging failures. Last time it was a stale env var after a secret rotation. |
| 2 | Reasoning | Check the config before the build. Without that fact, the default plan starts with the build, which is the wrong end, and burns three steps getting back. |
| 3 | Action | Read the staging config. Read only, no permission gate needed. |
| 4 | Reasoning | The env var is stale, matching the remembered pattern. Next step: correct it. |
| 5 | Action | Write. Permission checked because staging is on the allowed environment list, value validated, prior value captured for rollback, effect logged. |
| 6 | Action | Redeploy. Bounded to one environment, one service, with a timeout. |
| 7 |
The safety in this run lives entirely in step 5, in code. The prompt never had to say "be careful with production," because the tool cannot reach production. Its allowed environment list does not contain it. That is the difference between a suggestion and a control.
The intelligence lives in step 2, and it did not come from a better model. It came from a fact that arrived before the model was asked to think. That is the argument of Day 05 compressed into one row of a table.
This is the most important of the three, because the correct outcome is not to act.
| Step | Capability | What happens |
|---|---|---|
| 1 | Memory | Recall this customer's history: two prior billing disputes, one escalated, enterprise contract. |
| 2 | Action | Read the invoice and payment records. Read only. |
| 3 | Reasoning | The discrepancy is real, but fixing it means issuing a refund, which is a financial effect on a contract with negotiated terms. |
| 4 | Reasoning | Check the available actions. issue_refund exists but is scoped below a threshold this exceeds. Stop condition met: cannot proceed. |
| 5 | Action | Escalate. Send the account owner a summary with findings, evidence and a recommended resolution. |
| 6 | Memory | Record it: escalated, threshold exceeded, third dispute for this customer. |
Step 4 is not the agent being cautious. It is the action layer having a bound that reasoning can see and respect. If that threshold lived only in the prompt, a sufficiently upset customer message could talk the model straight past it. Because it lives in the tool's scope, it holds regardless of what reasoning concludes.
Escalation is an action, which is the second thing worth sitting with. Refusing to issue the refund does not mean doing nothing. The correct external effect here is a message to a human, executed and logged like any other action. An agent whose only failure mode is silence is an agent nobody will trust with anything.
And step 6 recording "escalated" rather than "failed" is what lets a human spot the pattern by the third occurrence. Memory of the things you did not do is still memory.
Day 04 laid out six capabilities of autonomy. This one gives three capabilities of the agent itself. They are different cuts, not competing lists. The six describe the control loop. The three describe what the loop is made of.
| Day 04 autonomy capability | Owned by |
|---|---|
| Goal interpretation | Reasoning |
| Next step selection | Reasoning |
| Context retention | Memory |
| Tool use | Action |
| Observation and correction | Action produces it, reasoning consumes it |
| Stop conditions | Reasoning decides, action enforces the hard bounds |
Read that table one way and it tells you which layer to open when an autonomy behaviour is failing. Read it the other way and it tells you which behaviours you lose when one of the three is thin. The second reading is the one that catches problems before they happen.
| Reasoning |
| Health check is green. Goal met. Stop. |
| 8 | Memory | Record it: staging failure, stale env var, second occurrence, time to resolve. |