A few weeks ago I bookmarked a Medium piece called “18 Core Agentic AI Patterns Explained in 20 Minutes.” The premise was appealing: one article, eighteen patterns, twenty minutes, done. I opened it expecting a reference I could keep coming back to while I was building an internal research agent for my team. What I got instead was a fast scroll through eighteen one-paragraph definitions, a friendly “Hey devs” tone, a market research example to set the mood, and then a paywall right where the actual substance should have started.
I don’t say that to dunk on the author. Explaining eighteen patterns in twenty minutes is a real constraint, and something has to give. But it left me with exactly the kind of gap that gets people into trouble: a list of names without the tradeoffs, without code, without the failure modes you only find out about after you’ve burned a weekend of API credits watching an evaluator and an optimizer argue with each other in an infinite loop. So I did what I usually do when a summary doesn’t satisfy me. I went and built the thing. All of it. Reflection loops, ReAct loops, orchestrator-worker setups, a small multi-agent swarm, a circuit breaker to stop the swarm from bankrupting me, and a local version of most of it running against Ollama so I didn’t have to keep an API meter open while I iterated.
This is the writeup of what I found, organized the way I wish the original had been organized, with working code, honest opinions about which patterns are worth your time in 2026 and which ones are mostly conference-talk material, and the mistakes I made so you don’t have to repeat them.
What “agentic” actually means before we count patterns
Before the pattern zoo, it’s worth being precise about the word “agentic,” because it gets used loosely enough that half the disagreements online are really just people using different definitions.
The clearest framing I found, and the one I now use myself, comes from Anthropic’s engineering team: the starting point for everything agentic is what they call the augmented LLM. That’s a model that can call tools, retrieve information, and read and write to some form of memory, wired up so the model itself decides when to use each capability rather than a human deciding in advance. Everything past that point is really just different arrangements of that one building block: how many times you call it, whether the order is fixed in code or decided by the model, and whether one model is checking another model’s work.
That distinction matters because it separates two very different families of systems that both get called “agents” in casual conversation. Workflows are systems where the control flow is written in code: step one always leads to step two, a router always picks from a fixed menu of paths. Agents, in the stricter sense, are systems where the model itself decides what happens next, in a loop, based on feedback from its own tool calls, until it decides the task is done. Both are legitimate and useful. The mistake I see most often, and the one I made myself on my first attempt at the research agent, is reaching for the autonomous loop when a fixed workflow would have been cheaper, faster, and far easier to debug.
With that framing in place, here’s the full map I ended up with. It’s organized into five families plus two pieces of connective tissue (protocols) that don’t fit neatly into “pattern” but that you cannot build any of this seriously in 2026 without understanding.
+----+-------------------------------+----------------------------+---------------------------------------------+
| # | Pattern | Family | One-line gist |
+----+-------------------------------+----------------------------+---------------------------------------------+
| 1 | Prompt Chaining | Deterministic workflow | Fixed sequence, each step's output feeds the |
| | | | next, checkpoints in between |
| 2 | Routing | Deterministic workflow | Classify the input, send it down one of |
| | | | several specialized paths |
| 3 | Parallelization | Deterministic workflow | Run several calls at once, aggregate votes |
| | | | or independent sections |
| 4 | Orchestrator-Workers | Deterministic workflow | One LLM plans and delegates, workers execute, |
| | | | orchestrator synthesizes |
| 5 | Evaluator-Optimizer | Deterministic workflow | One LLM generates, a second critiques, |
| | | | loop until it passes |
| 6 | ReAct | Reasoning loop | Interleave "thought", tool call, observation, |
| | | | repeat until answer |
| 7 | Plan-and-Execute | Reasoning loop | Write the whole plan up front, then execute |
| | | | steps, replanning only on failure |
| 8 | Reflection / Reflexion | Reasoning loop | Agent critiques its own past attempt and |
| | | | stores the lesson for the next try |
| 9 | Self-Correction / Verification | Reasoning loop | Mid-task check of intermediate output against |
| | | | constraints, before continuing |
| 10 | Tool Use / Function Calling | Foundation | The model decides when and how to call |
| | | | external functions or APIs |
| 11 | Multi-Agent Collaboration | Multi-agent | Specialized agents with distinct roles and |
| | | | tools work the same problem |
| 12 | Supervisor / Sub-Agent Hierarchy| Multi-agent | A parent agent delegates to child agents and |
| | | | aggregates their results |
| 13 | Agent Swarm / Mesh Coordination | Multi-agent | Peer agents talk to each other directly, no |
| | | | central coordinator |
| 14 | Human-in-the-Loop | Safety / production | Insertable approval gate before a risky |
| | | | action is allowed to execute |
| 15 | Guardrail Layering | Safety / production | Safety checks at input, tool call, tool |
| | | | response, and final output |
| 16 | Bounded Execution / Circuit | Safety / production | Hard caps on steps, tool calls, and cost so a |
| | Breaker | | runaway loop cannot run forever |
| 17 | Memory Architecture | Foundation | Short-term scratchpad, long-term store, |
| | (short/long/episodic) | | episodic trace of past runs |
| 18 | Context Engineering | Foundation | Deliberately curating what goes into the |
| | | | context window: select, compress, isolate |
+----+-------------------------------+----------------------------+---------------------------------------------+
I’ll go through each family in turn, with code where code actually clarifies something rather than just padding the article out.
Family one: deterministic workflows
These five come from Anthropic’s own field guide to agent building, and after actually shipping things, I’ve come around to their opening argument: start here, not with an autonomous agent, because a fixed workflow is cheaper to run, easier to test, and far easier to explain to whoever has to review your architecture.
Prompt chaining is the simplest thing on this list and the one people underrate the most. You break a task into steps, each step is its own LLM call, and you put a programmatic check between steps rather than trusting the model to self-police. My own version of this: a document generator that first writes an outline, then a code check validates the outline has the required sections before the second call ever fires. That gate caught a surprising number of cases where the model would have happily written three thousand words on a structure that was already wrong.
Routing classifies the input and sends it down one of several dedicated paths. The obvious use is customer support, but the one that actually saved me money was routing by difficulty: cheap, fast model for simple lookups, and only escalating to the expensive model when the router itself flags the query as ambiguous or multi-part. This is the pattern that most directly reduces your bill, and it’s almost embarrassingly simple to implement.
Parallelization comes in two flavors that get conflated a lot. Sectioning splits a task into independent pieces that run at the same time, useful when the pieces genuinely don’t depend on each other, like running a content generator and a safety screener on the same input simultaneously instead of sequentially. Voting runs the same prompt multiple times and aggregates the results, which is a cheap way to reduce variance on judgment calls, like having three separate calls each vote on whether a piece of code has a security issue and only flagging it if two of three agree.
Orchestrator-workers is where I started to feel like I was actually building something agentic rather than just gluing calls together. A central LLM looks at the task, decides how to break it up (and crucially, it decides this dynamically, not from a hardcoded list of subtasks), dispatches pieces to worker calls, and synthesizes what comes back. This is the shape behind most “AI coding agent” products that touch multiple files: the orchestrator reads the request, figures out which files are relevant, and worker calls handle each file.
Evaluator-optimizer is the one that bit me the hardest, so I’ll spend a little more time on it. One LLM generates a candidate answer, a second LLM (or the same model with a different prompt) evaluates it against explicit criteria, and if it fails, the generator gets another shot with the feedback attached. In theory this loop converges. In practice, the first time I wired this up for a translation task, I didn’t cap the number of iterations, and the evaluator kept finding increasingly pedantic issues to reject on. Six dollars and forty-one iterations later, I had my answer: always put a hard iteration ceiling on this pattern, and log every round so you can see whether the model is actually converging or just orbiting.
# Evaluator-optimizer loop with a hard cap, using an OpenAI-compatible
# client pointed at a local Ollama instance instead of a paid API.
#
# ollama pull llama3.1
# ollama serve
# pip install openai
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
MODEL = "llama3.1"
MAX_ROUNDS = 3 # the cap I did not have the first time. Learn from me.
def generate(task: str, feedback: str | None = None) -> str:
prompt = task if not feedback else f"{task}\n\nAddress this feedback:\n{feedback}"
resp = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": prompt}],
)
return resp.choices[0].message.content
def evaluate(task: str, candidate: str) -> tuple[bool, str]:
resp = client.chat.completions.create(
model=MODEL,
messages=[{
"role": "user",
"content": (
f"Task: {task}\nCandidate answer: {candidate}\n\n"
"Does this fully satisfy the task? Reply PASS or FAIL on the "
"first line, then one sentence of feedback."
),
}],
)
text = resp.choices[0].message.content
passed = text.strip().upper().startswith("PASS")
return passed, text
def evaluator_optimizer(task: str) -> str:
candidate = generate(task)
for round_num in range(1, MAX_ROUNDS + 1):
passed, feedback = evaluate(task, candidate)
print(f"round {round_num}: {'PASS' if passed else 'FAIL'} -- {feedback[:80]}")
if passed:
return candidate
candidate = generate(task, feedback)
return candidate # ran out of rounds, return the best we have
if __name__ == " __main__":
result = evaluator_optimizer(
"Write a one-sentence explanation of TCP handshakes for a non-technical reader."
)
print("\nFinal:", result)
Point that same base_url at https://api.openai.com/v1 with a real key and a model name like gpt-4o and nothing else in the function bodies changes. That's the whole appeal of using an OpenAI-compatible local server for development: you write the logic once.
Family two: reasoning loops
This is the family that gets called “real agents” most often, because control genuinely lives inside the model’s own loop rather than in your code.
ReAct , short for reason and act, comes from the 2022 Yao et al. paper and is still the load-bearing pattern under most tool-using agents you’ll touch in 2026, including the deeper agentic loops inside Claude Code and similar tools. The model alternates between a thought (reasoning about what to do next), an action (a tool call), and an observation (the tool’s result), repeating until it decides it has enough to answer. What I like about it is how legible it is: you can print the thought, action, observation triplets and watch the reasoning happen in something close to plain English, which makes debugging dramatically easier than staring at a black-box chain-of-thought.
# A minimal ReAct loop against a local model. Two tools: a calculator
# and a fake "search" so you can see the loop work without any external
# API dependency beyond Ollama itself.
import re
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
MODEL = "llama3.1"
def calculator(expr: str) -> str:
try:
return str(eval(expr, {" __builtins__": {}}))
except Exception as e:
return f"error: {e}"
def fake_search(query: str) -> str:
facts = {
"capital of portugal": "Lisbon",
"boiling point of water at sea level": "100 degrees Celsius",
}
return facts.get(query.lower().strip(), "no result found")
TOOLS = {"calculator": calculator, "search": fake_search}
SYSTEM = """You solve tasks using a Thought/Action/Observation loop.
Available actions: calculator[expression], search[query].
Format strictly as:
Thought: ...
Action: tool_name[input]
When you know the final answer, write:
Thought: ...
Final Answer: ...
"""
def react_loop(question: str, max_steps: int = 6) -> str:
messages = [
{"role": "system", "content": SYSTEM},
{"role": "user", "content": question},
]
for step in range(max_steps):
resp = client.chat.completions.create(model=MODEL, messages=messages)
text = resp.choices[0].message.content
print(f"--- step {step} ---\n{text}\n")
messages.append({"role": "assistant", "content": text})
if "Final Answer:" in text:
return text.split("Final Answer:")[-1].strip()
match = re.search(r"Action:\s*(\w+)\[(.*?)\]", text)
if not match:
return "stopped: model did not produce a valid action or final answer"
tool_name, tool_input = match.group(1), match.group(2)
tool_fn = TOOLS.get(tool_name)
observation = tool_fn(tool_input) if tool_fn else f"unknown tool {tool_name}"
messages.append({"role": "user", "content": f"Observation: {observation}"})
return "stopped: hit max_steps without a final answer"
if __name__ == " __main__":
print(react_loop("What is the boiling point of water in Fahrenheit?"))
Notice the max_steps cap sitting right in the function signature. That is not decoration, it's the single most important line in the file, and it's the same lesson as the evaluator loop above: every reasoning loop needs a hard stop that does not depend on the model's own judgment, because the model's judgment is exactly the thing that's unreliable when it's stuck.
Plan-and-execute takes a different bet: instead of interleaving thinking and acting one step at a time, the model writes out the entire plan up front, then a much simpler executor runs through the steps, only going back to the planner if a step fails outright. This trades some adaptability for a big win in token cost and latency, because you’re not paying for a full reasoning pass between every single tool call. I reach for this over ReAct whenever the task is well-scoped enough that the plan is unlikely to need mid-flight revision, research report generation being the clearest example from my own use.
Reflection , and its more formalized cousin Reflexion from Shinn et al.’s 2023 paper, add a memory dimension to self-critique: after a failed attempt, the agent writes a verbal note to itself about what went wrong, and that note gets fed back in on the next attempt, functioning as a lightweight, non-parametric form of learning across tries. The distinction I didn’t appreciate until I built both: plain reflection critiques the current output in the same session, while Reflexion specifically persists the lesson across separate episodes, which makes it much more useful for tasks the agent will attempt again later, like a coding agent that keeps failing the same category of test.
Self-correction or self-verification is the narrowest of the four, and arguably the most underrated for production use because it’s cheap: instead of a full critique-and-retry cycle, the agent does a lightweight sanity check on an intermediate result against known constraints before continuing. Does this SQL query reference a column that actually exists in the schema? Does this generated JSON match the expected shape? Catching that early is far cheaper than discovering it three tool calls later when the whole chain has to be rolled back.
Family three: multi-agent patterns
Multi-agent systems get a lot of hype and, in my experience, deserve a good deal less of it than single well-designed agents with good tools. But there are real cases where splitting responsibility across agents earns its complexity.
Multi-agent collaboration is the general case: separate agents, each with its own system prompt, its own tools, and often its own model, working the same overall problem. The win is specialization. A researcher agent with search tools and a writer agent with no tools at all but a strong style prompt will each do their one job better than a single agent trying to be both.
Supervisor and sub-agent hierarchy is the specific topology I ended up using for my research agent, and it’s the one I’d recommend starting with if you’re going multi-agent at all. A parent agent owns the overall task and delegates pieces to specialized children, then aggregates what comes back. This is architecturally almost identical to orchestrator-workers from the deterministic family above, and honestly the line between “deterministic orchestrator” and “supervisor agent” is blurrier in practice than the naming suggests. The real distinction is whether the delegation logic is hardcoded or decided by the supervisor model at run time.
Agent swarm or mesh coordination drops the central coordinator entirely: peer agents talk directly to each other, negotiate, and converge on an answer without anyone in charge. I built a small three-agent version of this for fun (a proposer, a critic, and a synthesizer messaging each other in a shared thread) and walked away convinced this pattern is genuinely powerful for certain classes of problems, brainstorming and adversarial red-teaming among them, but it is also the hardest of the eighteen patterns to keep bounded and observable. Without a supervisor, there’s no single place to enforce a step limit, which brings me to the topology question directly.
+---------+----------------------------------------+---------------------------------------------+
| Shape | Structure | Where it earns its complexity |
+---------+----------------------------------------+---------------------------------------------+
| Chain | Agent A hands off to B, B to C, in a | Fixed pipelines where each stage genuinely |
| | straight line | needs a different specialist |
+---------+----------------------------------------+---------------------------------------------+
| Star | A supervisor talks to every agent, | Most production systems; easiest to add |
| | agents don't talk to each other | logging, budgets, and guardrails to |
+---------+----------------------------------------+---------------------------------------------+
| Mesh | Every agent can talk to every other | Open-ended collaboration and debate tasks; |
| | agent directly | hardest to bound and audit |
+---------+----------------------------------------+---------------------------------------------+
My honest recommendation after building all three shapes: default to star. Reach for chain only when the sequence really is fixed. Reach for mesh only when you specifically need agents to challenge each other’s outputs, and even then, wrap the whole mesh in an outer supervisor whose only job is to enforce a global step and cost budget, because nothing inside a pure mesh is going to do that for you.
Family four: memory patterns
This is the family the original listicle-style pieces tend to compress into a single bullet, and it’s a mistake, because getting memory wrong is one of the fastest ways to make an agent both expensive and confidently wrong.
Short-term memory is the scratchpad: the running conversation and intermediate tool results held in context for the duration of one task. Every one of the reasoning loops above already uses this, it’s the messages list in my ReAct example. The failure mode here isn't forgetting, it's the opposite: letting that list grow unbounded across a long agent run until you're paying to re-send twelve thousand tokens of stale tool output on every single step. I now truncate or summarize the scratchpad once it crosses a length threshold, and it cut my token spend on long-running tasks by more than half.
Long-term memory persists across sessions, usually backed by a vector store or a hybrid keyword-plus-vector setup, and is what lets an agent remember a user’s preferences or a project’s conventions from one conversation to the next without you re-explaining everything every time. The gotcha, and one of the emergent patterns worth naming on its own, is that entries in a long-term store don’t expire unless you explicitly make them expire. I found this out when my agent kept referencing a project decision that had been reversed three weeks earlier, because nothing had ever told the vector store the old entry was stale.
Episodic memory is a narrower, specific thing: a record of prior attempts at a task, including what was tried and what happened, used to inform future attempts at the same or a similar task. This is the storage layer that Reflexion needs to function across sessions rather than just within one. Without it, “reflection” resets every time you start a new conversation, which defeats the point.
Hierarchical RAG deserves a mention here because it’s really a memory-retrieval pattern dressed up as a search pattern: instead of one flat vector search across your whole corpus, you first select which corpus or document set is even relevant, then chunk and search within it. For anyone whose retrieval-augmented setup has started returning technically-relevant-but-wrong-domain results as the corpus grows, this two-stage approach is usually the fix, and it’s cheaper than it sounds because the first-stage selection can often be a fast classifier rather than another full LLM call.
Family five: the patterns that keep production honest
None of the above matters if the system falls over, or worse, quietly does something you didn’t authorize, the first time it meets a real user. This family is what separates a demo from something you’d actually put your name on.
Human-in-the-loop is the simplest safety pattern to describe and the one people skip most often because it adds friction. It’s an approval gate you can insert at any point in any of the patterns above: before a tool call that touches money or production data, an agent pauses and waits for a person to approve or reject before continuing. I treat this as non-negotiable for any tool that has an irreversible side effect. Everything else is a judgment call, this one isn’t.
Guardrail layering is the recognition that a single safety check anywhere in the pipeline is not enough, because different failure modes show up at different points: a malicious prompt shows up at the input, a hallucinated tool argument shows up at the tool call, a leaked secret can show up in a tool’s response before the model even sees it, and a policy violation can show up only in the final generated text. I now put a check at all four of those points rather than picking one and hoping it catches everything, because in testing, each of the four caught something the other three missed.
Bounded execution , also called a circuit breaker, is the pattern that would have saved me forty dollars across two separate incidents if I’d built it before I needed it instead of after. It’s a hard ceiling, enforced in code and not by asking the model nicely, on the number of steps, the number of tool calls, and ideally a running dollar cost, so that a loop that isn’t converging gets forcibly stopped rather than running until someone notices the bill.
# A reusable circuit breaker you can wrap around any agent loop.
# It doesn't know anything about your task, just about the limits.
import time
class BudgetExceeded(Exception):
pass
class CircuitBreaker:
def __init__ (self, max_steps: int = 15, max_seconds: float = 60.0,
max_estimated_cost_usd: float = 1.00):
self.max_steps = max_steps
self.max_seconds = max_seconds
self.max_cost = max_estimated_cost_usd
self.steps = 0
self.cost = 0.0
self.start = time.monotonic()
def check(self, step_cost_usd: float = 0.0):
self.steps += 1
self.cost += step_cost_usd
elapsed = time.monotonic() - self.start
if self.steps > self.max_steps:
raise BudgetExceeded(f"step limit hit: {self.steps} > {self.max_steps}")
if elapsed > self.max_seconds:
raise BudgetExceeded(f"time limit hit: {elapsed:.1f}s > {self.max_seconds}s")
if self.cost > self.max_cost:
raise BudgetExceeded(f"cost limit hit: ${self.cost:.2f} > ${self.max_cost:.2f}")
# usage inside any of the loops above:
#
# breaker = CircuitBreaker(max_steps=10, max_estimated_cost_usd=0.50)
# while True:
# breaker.check(step_cost_usd=0.002) # raises BudgetExceeded if over
# ... run one step of your ReAct / evaluator-optimizer / swarm loop ...
Tool sandboxing rounds out this family: any tool that can execute code, write files, or make network calls should run somewhere the agent’s mistakes can’t reach anything you care about. This one isn’t really optional once your tools go past pure read-only lookups, and the good news is it’s mostly solved by infrastructure you likely already have (containers, restricted service accounts, network egress rules) rather than anything specific to the agent framework itself.
The protocols underneath: MCP and A2A
None of the eighteen patterns above tell you how the pieces actually talk to each other in a standardized way, and this is the part that has moved the most since 2024, so it’s worth a dedicated section even though it’s not a “pattern” in the same sense.
MCP , the Model Context Protocol, is Anthropic’s open standard for how an agent connects to tools, data sources, and services, released in November 2024. The framing I keep coming back to is that MCP gives your agent hands: a standard client-server interface so a tool built once can be plugged into any MCP-compatible agent rather than being wired up bespoke for every framework. By 2026 it’s genuinely become a default rather than a novelty, with adoption across every major model provider and thousands of public MCP servers already available for common services.
A2A , Agent2Agent, is Google’s protocol, released in April 2025, and it solves a different problem: not agent-to-tool, but agent-to-agent, letting independently built agents discover each other and hand off tasks regardless of which framework built them. If MCP gives your agent hands, A2A gives your agents colleagues they didn’t have to be introduced to personally.
+------------------+----------------------------+----------------------------------+
| Protocol | Solves | Analogy |
+------------------+----------------------------+----------------------------------+
| MCP | Agent to tool / data source | USB-C port: one standard plug for |
| | integration | any compatible peripheral |
+------------------+----------------------------+----------------------------------+
| A2A | Agent to agent discovery | A shared directory and handshake |
| | and task handoff | so strangers can work together |
+------------------+----------------------------+----------------------------------+
Both protocols were donated to a vendor-neutral Linux Foundation body established in late 2025, with the major model providers as founding members, which is the strongest signal I’ve seen yet that this layer is settling into genuine infrastructure rather than staying a collection of competing vendor standards. If you’re building anything with the multi-agent patterns from family three, it’s worth designing your agent-to-agent handoffs against A2A’s shape from the start rather than inventing your own message format, even if you don’t adopt the protocol wholesale on day one.
The two patterns everyone mentions last and skips first
Two more patterns are genuinely emergent rather than settled, meaning I’d treat them as things to watch rather than things to build into a production system today.
Context engineering is the deliberate discipline of managing what actually sits inside the model’s context window at any given moment: selecting what’s relevant, compressing what’s verbose, and isolating what shouldn’t bleed between subtasks. This sounds like restating “prompt engineering” with new words, but it’s a genuinely different skill, closer to memory management in an old-school systems programming sense than to writing a good instruction. My short-term memory truncation from family four is one small instance of context engineering; a more mature version would actively decide, turn by turn, what stays, what gets summarized, and what gets dropped entirely.
Trajectory logging and replay means recording the full sequence of thoughts, actions, and observations for every run, not just the final answer, so that a failure can be replayed and inspected rather than guessed at after the fact. I didn’t take this seriously until I hit a bug where an agent silently used stale tool output three steps back in a run, and the only reason I found it was because I happened to have full logging on for an unrelated reason. I now treat trajectory logging as close to mandatory as human-in-the-loop for anything running unattended, because debugging an autonomous loop with only the final output is close to debugging a crashed program with only its exit code.
I’ll mention a third one only in passing: meta-agents , systems that modify their own prompts or tool sets based on performance feedback. I looked into this seriously and backed away. The self-improvement loop is real in research settings, but the failure modes (an agent quietly rewriting its own safety instructions being the obvious nightmare case) are serious enough that I don’t think this belongs in anyone’s production system yet, mine included.
The mistakes, collected in one place
Scattered through the sections above, but worth having together as a checklist, since these are the actual reason this article exists instead of just being another list of eighteen names.
+---------------------------------------+---------------------------------------------------+
| Mistake | What it actually costs you |
+---------------------------------------+---------------------------------------------------+
| No iteration cap on evaluator-optimizer | The loop can run indefinitely if the evaluator |
| | keeps finding new objections; I hit 41 rounds once |
+---------------------------------------+---------------------------------------------------+
| No step cap on a ReAct or swarm loop | Same failure, different pattern; unbounded loops |
| | are the single most common agentic production bug |
+---------------------------------------+---------------------------------------------------+
| Letting short-term memory grow | You re-send the entire history on every step, |
| unbounded across a long run | token cost grows roughly quadratically with steps |
+---------------------------------------+---------------------------------------------------+
| No expiry on long-term memory entries | Stale facts get retrieved and trusted as current, |
| | confidently wrong answers with no error message |
+---------------------------------------+---------------------------------------------------+
| Choosing mesh topology by default | Nobody owns the global budget or the stop condition, |
| | so bounded execution has nowhere to live |
+---------------------------------------+---------------------------------------------------+
| Skipping trajectory logging | The only debugging tool left is guessing from the |
| | final answer, which does not work for silent bugs |
+---------------------------------------+---------------------------------------------------+
None of these are exotic. Every one of them is a case of skipping a guardrail because the happy path worked fine in testing, and testing didn’t run long enough or often enough to hit the unhappy path.
Where I landed
If I had to compress everything above into the advice I’d give myself before I started: begin with the deterministic workflow patterns, specifically prompt chaining and routing, because they solve a surprising number of real problems for a fraction of the cost and debugging effort of an autonomous loop. Reach for ReAct or plan-and-execute only once you have a task that genuinely can’t be decomposed in advance. Add reflection or Reflexion only once you’ve confirmed the base loop is reliable, because layering self-critique on top of an already-flaky loop just makes the flakiness harder to diagnose, not easier. Treat human-in-the-loop, guardrail layering, and bounded execution as a package deal that ships with the very first version of anything that touches real tools, not as hardening you’ll add later, because “later” is when the incident happens. And build your local development loop against Ollama or an equivalent self-hosted model from day one, both because it’s free to iterate against and because it forces you to write your agent logic against the interface rather than against any one vendor’s quirks.
The original listicle got the inventory right. Eighteen names, roughly the right eighteen, is a defensible list. What it couldn’t fit into twenty minutes was the part that actually matters once you’re the one who gets paged when the agent does something strange at 2 a.m.: which of these eighteen are cheap insurance you should always include, which are genuine power tools you reach for only when the problem calls for them, and which are still research toys no matter how good the demo looks. Now you have all three answers, plus the code to go build it yourself.
Tags: agentic-ai, ai-agents, llm, machine-learning, software-architecture, mcp, multi-agent-systems, python
Top comments (0)