← All playbooks
Interactive
LLM vs Agent
An LLM is a function. An agent is a loop.
Both take a prompt. An LLM gives you an answer in one shot. An agent can call tools, see the results, think again, and keep going until it decides it's done. That loop — plus tools, state, and a stop condition — is what turns an LLM into an agent.
"I'm flying to Chicago tomorrow. Should I pack a coat?"
LLM one call, no tools
Agent loop + tools
What an agent is made of (beyond the LLM)
- Tools — functions with schemas the LLM can call to reach outside its own head: search, API calls, file I/O, SQL, shell, another LLM.
- State — conversation history the LLM sees every turn, so it can build on what it has learned instead of starting fresh.
- The loop — the runtime that keeps calling the LLM, executes any tool calls, feeds the results back, and repeats.
- Termination — the agent stops when it says it's done. You set a max-turn safety net in case it won't stop.
- Guardrails — max turns, tool allowlists, timeouts, sandboxes, approval prompts for risky actions, cost caps.
- A goal — usually implicit in the prompt ("find X", "fix Y"). Fancier agents plan explicitly and reflect on progress.
The agent loop, in ~30 lines
The loop is the thing. Everything else — tools, prompts, state — is setup.
# 1. TOOLS — functions the LLM is allowed to call
tools = [{"name": "get_weather", "description": ..., "input_schema": {...}}]
# 2. STATE — conversation history grows each turn
messages = [{"role": "user", "content": user_prompt}]
# 3. LOOP — this is what makes it an agent
for turn in range(MAX_TURNS):
response = client.messages.create(model=..., tools=tools, messages=messages)
messages.append({"role": "assistant", "content": response.content})
# 4. TERMINATION — the AGENT decides when it's done
if response.stop_reason == "end_turn":
return final_text(response)
# 5. TOOL USE — run tools, feed results back, loop again
tool_results = []
for block in response.content:
if block.type == "tool_use":
output = run_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": tool_results})
# ↑ next turn the LLM sees the results and decides what to do next The full runnable version
↓ agent.pySame loop as above, with a stub tool and an LLM-only baseline for comparison. Drop in your ANTHROPIC_API_KEY and run it.
pip install anthropic
export ANTHROPIC_API_KEY=sk-...
python agent.py