Foundations Of Agentic AI
Anatomy of an Agent
This diagram is used for the rest of the curriculum. Every framework in Module 5 is an implementation of it, and every failure mode in Module 8 is a component b
Jr Codex Agentic AI Notes
Level: Beginner–Intermediate Prerequisites: Chapter 3: Classical Agent Types, Reframed Time to complete: ~20 minutes
Table of Contents
- The Reference Architecture
- The Five Components
- A Complete Minimal Agent
- Tracing One Full Run
- Where Each Component Is Developed
- Summary & Next Steps
1. The Reference Architecture
This diagram is used for the rest of the curriculum. Every framework in Module 5 is an implementation of it, and every failure mode in Module 8 is a component behaving badly.
The Agent, Assembled
─────────────────────────────────────────
┌──────────────┐
goal ──────►│ PLANNER │
│ decides the │◄──────┐
│ next action │ │
└──────┬───────┘ │
│ │
┌──────▼───────┐ │
│ EXECUTOR │ │
│ runs the tool│ │
└──────┬───────┘ │
│ │
┌──────▼───────┐ │
│ TOOLS │ │
│ the world │ │
└──────┬───────┘ │
│ result │
┌──────▼───────┐ │
│ PERCEPTION │ │
│ observe and │ │
│ normalise │ │
└──────┬───────┘ │
│ │
┌──────▼───────┐ │
│ MEMORY │───────┘
│ what is known│
└──────────────┘
─────────────────────────────────────────
This is the AI Notes' perceive-decide-act cycle with memory made explicit and the tool boundary drawn — because for an LLM agent, the tool boundary is where nearly everything interesting goes wrong.
2. The Five Components
PLANNER — the LLM
─────────────────────────────────────────
Input: goal + memory
Output: the next action, or "done"
Module 2 (the reasoning core), Module 4 (planning)
EXECUTOR — ordinary code
─────────────────────────────────────────
Validates arguments, dispatches the call, enforces
timeouts and permissions, catches exceptions.
Deliberately NOT the LLM. This is the layer where
safety is enforced, because it is the only
component whose behaviour is deterministic.
Module 8.
TOOLS — the action surface
─────────────────────────────────────────
Everything the agent can do or observe. The tool
set defines the agent's capability AND its blast
radius. Module 2, Chapter 3.
PERCEPTION — normalisation
─────────────────────────────────────────
Turns raw results into something the model can use:
truncating a 50,000-row response, summarising a
webpage, converting a stack trace into a message.
The most under-appreciated component. A tool that
returns 200KB of JSON will blow the context window
and end the run.
MEMORY — accumulated state
─────────────────────────────────────────
Short-term: this run's history.
Long-term: what persists across runs.
Module 3.
─────────────────────────────────────────
3. A Complete Minimal Agent
Every component above, in one readable class. This is the skeleton the rest of the curriculum fills in.
import json
from dataclasses import dataclass, field
@dataclass
class Agent:
client: object
tools: dict # name -> callable
schemas: list # the tool definitions sent to the model
system: str
max_steps: int = 10
memory: list = field(default_factory=list) # MEMORY (short-term)
def plan(self, messages): # PLANNER
return self.client.chat.completions.create(
model="gpt-4o", messages=messages, tools=self.schemas,
).choices[0].message
def execute(self, call): # EXECUTOR
fn = self.tools.get(call.function.name)
if fn is None:
return {"error": f"unknown tool {call.function.name}"}
try:
args = json.loads(call.function.arguments)
except json.JSONDecodeError as e:
return {"error": f"malformed arguments: {e}"}
try:
return fn(**args)
except Exception as e: # NEVER let a tool crash the loop —
return {"error": f"{type(e).__name__}: {e}"} # the model can often recover
def perceive(self, result, limit=4000): # PERCEPTION
text = json.dumps(result) if not isinstance(result, str) else result
if len(text) > limit:
text = text[:limit] + f"\n...[truncated, {len(text)} chars total]"
return text
def run(self, goal):
messages = [{"role": "system", "content": self.system},
{"role": "user", "content": goal}]
for step in range(self.max_steps):
reply = self.plan(messages)
self.memory.append(reply)
if not reply.tool_calls: # the planner judged the goal MET
return reply.content
messages.append(reply)
for call in reply.tool_calls:
observation = self.perceive(self.execute(call))
messages.append({"role": "tool", "tool_call_id": call.id,
"content": observation})
return "Step limit reached without completing the goal."Three Decisions Doing Real Work
─────────────────────────────────────────
ERRORS BECOME OBSERVATIONS, not exceptions. A
crashed tool that returns {"error": ...} lets the
model try something else. A raised exception ends
the run. This single choice accounts for a large
share of agent robustness.
PERCEPTION TRUNCATES. Without it, one verbose tool
result exhausts the context window and every
subsequent step degrades.
max_steps IS NOT OPTIONAL. It is the only thing
standing between a confused agent and an unbounded
bill.
─────────────────────────────────────────
4. Tracing One Full Run
Goal: "Is the checkout service healthy right now?"
─────────────────────────────────────────
STEP 1
PLANNER "I need current metrics."
──► get_metrics(service="checkout")
EXECUTOR validates, dispatches, 200 OK
PERCEPTION {"error_rate": 0.11, "p95_ms": 2400}
MEMORY appended
STEP 2
PLANNER sees an 11% error rate — elevated.
"I should check recent deploys."
──► list_deploys(service="checkout",
hours=6)
EXECUTOR dispatches
PERCEPTION [{"sha": "a3f", "at": "14:02"}]
MEMORY appended
STEP 3
PLANNER a deploy at 14:02, errors began
14:05. No further action needed.
──► NO tool calls ──► FINAL ANSWER
─────────────────────────────────────────
What the Trace Shows
─────────────────────────────────────────
Step 2 was NOT predetermined. It exists only
because step 1 returned an elevated error rate. A
healthy result would have ended the run at step 2
with "yes, it is healthy."
That branch — chosen at runtime, based on an
observation — is exactly Chapter 2's threshold
between a workflow and an agent.
─────────────────────────────────────────
5. Where Each Component Is Developed
The Map of This Curriculum
─────────────────────────────────────────
PLANNER Module 2 reasoning, prompting patterns
Module 4 decomposition, reflection
TOOLS Module 2 tool design, MCP
MEMORY Module 3 short-term and long-term
EXECUTOR Module 6 state, checkpointing, resume
Module 8 permissions, sandboxing, HITL
PERCEPTION Module 2 tool result design
Module 3 context budgeting
THE WHOLE Module 5 frameworks that implement it
Module 7 evaluating and operating it
Module 9 building one end to end
─────────────────────────────────────────
6. Summary & Next Steps
Key Takeaways
- An agent is five components: an LLM planner, a deterministic executor, tools, a perception layer that normalises results, and memory.
- The executor is deliberately not the model — safety is enforced in the one component whose behaviour is deterministic.
- Tool errors must become observations rather than exceptions, so the model can recover; this is one of the largest robustness levers available.
- Perception is the most under-appreciated component: an untruncated tool result will exhaust the context window and degrade every step that follows.
Concept Check
- Why is returning
{"error": ...}from a failed tool call substantially better than letting the exception propagate? - Which component would you strengthen if an agent works well for three steps and then produces confused output, and why?
- In the Section 4 trace, what makes step 2 evidence that this is an agent rather than a workflow?
Module 1 Complete — What's Next
You now have the vocabulary, the spectrum, the diagnostic taxonomy, and the reference architecture. Module 2 goes inside the planner: how an LLM actually reasons about what to do next, how to prompt it for that, and how to design the tools it chooses among.
Next Module
→ Module 2: The Reasoning Core
Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index | Back to Agentic AI Index