What is an Agent
Chatbot answers. Workflow follows. Agent decides. The autonomy spectrum — and the honest guide to when NOT to use agents.
▶ Watch this reelWhat you'll learn
- The autonomy spectrum
- The agent loop
- Anatomy of an agent
- When NOT to use agents
Remember this
- The litmus test: who picks the next step — programmer (workflow) or model (agent)?
- The agent loop = think → act → observe → repeat, bounded by budgets; agents re-plan, workflows replay
- Anatomy: model + instructions + tools + memory; use workflows for known paths and auditability, start single-agent
Autonomy spectrum
- Chatbot: responds, human acts.
- Workflow: code controls flow; model is a step. Testable, cheap, rigid.
- Agent: model picks next step via tools. Flexible, adaptive, costly.
- Litmus: who decides the next step?
The loop (ReAct)
- Think → act (tool call) → observe (result as message) → repeat.
- Stops on: final answer / budgets (iterations, time, cost).
- The power: re-planning after every observation. The cost: LLM call per decision.
Anatomy
- Model · instructions (cheapest quality lever) · tools (AG-02) · memory (context = short-term; store = long-term).
When NOT to use agents
- Steps known · auditability required · latency/cost sensitive → workflow.
- Start single-agent; escalate to multi-agent only on proven need.
- Credibility check: name the decision the model must make that code cannot.
Code: The agent loop in ~30 lines
from dataclasses import dataclass
@dataclass
class Agent:
model: LLM
instructions: str
tools: dict[str, callable] # name -> function
max_iters: int = 10 # the budget
def run(self, goal: str) -> str:
messages = [
{"role": "system", "content": self.instructions},
{"role": "user", "content": goal},
]
for _ in range(self.max_iters): # hard stop
resp = self.model.chat(
messages, tools=schema(self.tools))
msg = resp.choices[0].message
messages.append(msg)
if not msg.tool_calls: # done: final answer
return msg.content
for call in msg.tool_calls: # act + observe
try:
result = self.tools[call.name](**call.args)
except Exception as e:
result = f"ERROR: {e}" # errors go back as observations
messages.append({"role": "tool",
"content": str(result)})
return "Stopped: iteration budget exhausted."