Inside An AI Agent: Planning, Tool Use, Memory, Constraints, And Verification
Have you noticed how every demo of "an AI agent" looks impressive in the video and falls apart the moment you ask a sharper question? The agent confidently does the wrong thing. It forgets what it just decided. It tries to call a tool that doesn't exist. It loops forever rewriting the same file. It calmly tells you the deployment succeeded when it didn't. These aren't failures of the model. They're failures of the workflow around the model. Because that's all an agent really is: a software workflow where a language model can pick the next step and call tools. The "intelligence" sits in the prompt and the orchestration around it, not in some secret agent-flavoured fairy dust. Strip the word "agent" away and you've got five pieces of plumbing: planning, tool use, memory, constraints, verification. Every production-grade agent stands or falls on those five. This is a long walk through each one. Not the marketing version. The kind of detail you actually need before you ship something that talks to your database. The Loop You're Actually Building Before we touch any pillar individually, hold the whole loop in your head. A useful agent does roughly this on every turn: Read the goal (and whatever memory is relevant to it). Decide the next action: answer directly, call a tool, ask a clarifying question, or stop. If it called a tool, observe the tool's result and feed it back in. Update memory if anything is worth remembering. Check constraints: are we over budget, out of iterations, touching something off-limits? Verify the output before declaring success. Loop until done or stopped. That's it. Every framework (LangGraph, OpenAI Agents SDK, Claude Agent SDK, smolagents, whatever ships next month) is a different shape of the same loop with different defaults. agent-loop.ts async function runAgent ( goal : string , ctx : AgentContext ) { const state = ctx . startState ( goal ); for ( let step = 0 ; step < ctx . maxSteps ; step ++ ) { const decision = await ctx . model . decid