aiagenticarchitecture

The Three Layers of Agentic Systems

3 min read

When someone says they've built an "agent," they usually mean one of three very different things. Conflating them is why so many agentic demos look impressive but crumble in production.

Here's how I think about it.

Layer 1 — The Execution Layer

This is the lowest level: a function that receives a tool call from the LLM and runs it. In code:

async function executeTool(
  name: string,
  args: Record<string, unknown>
): Promise<ToolResult> {
  const tool = registry.get(name);
  if (!tool) throw new ToolNotFoundError(name);

  const validated = tool.schema.parse(args);
  return tool.execute(validated);
}

Most "agentic" tutorials stop here. They hook up a few tools — searchWeb, readFile, sendEmail — and call it an agent. The LLM decides which tool to call, the execution layer runs it, and the cycle repeats.

This is fine for demos. It falls apart when:

  • Tools can fail in ways the LLM doesn't understand
  • The task requires more than 3-4 sequential steps
  • Multiple tools need to coordinate on shared state

Layer 2 — The Planning Layer

Real agentic work requires a plan. Not a rigid script, but a mutable graph of intentions that gets updated as the agent learns what's possible.

class Plan:
    def __init__(self, goal: str):
        self.goal = goal
        self.steps: list[Step] = []
        self.completed: list[Step] = []
        self.context: dict = {}

    def next_step(self) -> Step | None:
        pending = [s for s in self.steps if not s.done]
        return pending[0] if pending else None

    def update_from_result(self, step: Step, result: ToolResult):
        step.done = True
        step.result = result
        self.context[step.id] = result
        # Re-plan if the result changes what's possible
        if result.suggests_replanning:
            self.replan()

The key insight is that the plan is data, not code. The LLM can read and modify the plan at each step. This gives you something much more useful than a linear chain of prompts: an agent that can backtrack, skip ahead, or discover that the original goal was slightly wrong.

Why this matters

I built a search system over millions of event logs where the agent needed to:

  1. Understand the user's query
  2. Decide which log sources to search
  3. Query each source (potentially in parallel)
  4. Reconcile conflicting results
  5. Generate a summary with citations

Without a planning layer, step 3 would consume the entire context window before step 4 even started. With a plan, the agent operates on a compressed representation of its progress, not the raw history.

Layer 3 — The Coordination Layer

The hardest part. This is where multiple agents, or a single agent split across time, need to coordinate.

interface AgentMessage {
  from: AgentId;
  to: AgentId | "broadcast";
  type: "request" | "result" | "clarification";
  payload: unknown;
  traceId: string; // For correlating distributed work
}

Most teams skip this entirely and then wonder why their multi-agent system produces inconsistent outputs. The answer is almost always: no shared world model, no message schema, no way to reconcile conflicting beliefs.

The Practical Takeaway

If you're building a system where an LLM takes actions, ask yourself:

  1. Do my tools fail gracefully? (Execution layer)
  2. Can my agent revise its plan mid-task? (Planning layer)
  3. If I have multiple agents, how do they agree on shared state? (Coordination layer)

You don't need all three for every problem. A simple retrieval pipeline doesn't need a coordination layer. But if your agent is supposed to "just handle it" for anything non-trivial, you need at least layers 1 and 2.

The demos that look magical are usually the ones where the demo author picked a task that only needs layer 1.