One-Shot vs. Agent
Most AI apps are just single API calls. You send a prompt, get a response, done.
An agent is different. It’s a loop that:
- Receives messages
- Decides what to do next
- Calls tools when needed
- Remembers context
- Knows when to ask for help
The Basic Agent Loop
Here’s the core loop every agent follows:
Breaking Down the Loop
1. Receive User Message
The agent gets input. Could be text, could include context like user_id.
user_message = "What's my subscription status?"
user_id = "user-123"
2. Decide Next Action
The LLM looks at the message and available tools. It decides:
- Can I answer this directly?
- Do I need to call a tool?
- Should I escalate this?
This is where the agent’s “brain” lives.
3. Take Action
Based on the decision:
Call a tool:
tool_call = {
"name": "get_subscription_status",
"arguments": {"user_id": "user-123"}
}
Answer directly:
response = "SimpleSaaS is a subscription management platform..."
Escalate:
escalation = {
"type": "human_handoff",
"reason": "Billing dispute detected"
}
4. Repeat or Finish
If a tool was called, add the result to the conversation and loop back. The agent might need multiple tool calls before it can answer.
Why Not Build a General Agent?
You might think: “Why limit it? Let’s build an agent that does everything!”
Here’s why that’s a bad idea:
The Danger of Too Much Freedom
Problem 1: Unpredictable Behavior
- Agent might call tools you didn’t expect
- Could make changes you didn’t authorize
- Hard to debug when things go wrong
Problem 2: Safety Risks
- Might answer questions it shouldn’t
- Could expose sensitive data
- No clear boundaries
Problem 3: Poor User Experience
- Users don’t know what it can do
- Agent might try things that fail
- Hard to set expectations
Why “Ask for Help” Is Valid
Escalation isn’t failure. It’s a feature.
When the agent escalates, it’s saying: “I’m not sure about this, let me get a human involved.”
That’s smart. Better to ask for help than to guess wrong.
Clear Boundaries
Our support agent has clear limits:
Can do:
- Answer FAQs from knowledge base
- Check subscription status
- Provide basic account info
Cannot do:
- Change billing details
- Process refunds
- Answer legal questions
- Delete accounts
Will escalate:
- Billing disputes
- Account deletion requests
- Legal questions
- Angry or frustrated users
These boundaries keep the agent safe and predictable.
The Four Pillars
Every agent needs four things:
1. Loop
The cycle of receive → decide → act → repeat.
2. Tools
Functions the agent can call to get information or take actions.
3. Memory
Short-term (current conversation) and long-term (user preferences).
4. Guardrails
Rules that prevent dangerous or unwanted behavior.
We’ll build all four in this tutorial.
Visualizing the Loop
Here’s how the loop looks in code:
def run_agent(user_message, user_id):
messages = [{"role": "user", "content": user_message}]
max_steps = 5
for step in range(max_steps):
# Decide what to do
response = llm_call(messages, tools)
if response.tool_calls:
# Call tool and add result
for tool_call in response.tool_calls:
result = execute_tool(tool_call)
messages.append({
"role": "tool",
"content": result
})
elif response.should_escalate:
# Escalate to human
return escalate(user_id, user_message)
else:
# Final answer
return response.content
This is the core pattern. Everything else builds on top.
Key Takeaways
Before moving on, remember:
- Agents are loops, not single calls
- Narrow agents are safer than general ones
- Escalation is a feature, not a bug
- Clear boundaries prevent problems
- Four pillars: loop, tools, memory, guardrails
What’s Next?
Now that you understand what an agent is, let’s design our support agent. We’ll define what it can do, what tools it needs, and when it should escalate.