Building an AI Agent
Implement multi-step reasoning with a tool loop and state management.
Yesterday we dispatched single tool calls. But what if the AI needs multiple tools to answer one question?
That's an agent — an AI that runs a loop: think, act, observe, repeat. Instead of one tool call, it chains multiple steps until it has enough information to answer.
Here's the core loop:
def agent_loop(messages, tools, registry, max_steps=5):
for step in range(max_steps):
# 1. Think — ask the LLM what to do
response = call_llm(messages, tools)
# 2. Check — is it a final answer or a tool call?
if response.get("tool_call"):
# 3. Act — execute the tool
tool_call = response["tool_call"]
result = dispatch(tool_call["name"], tool_call["args"], registry)
# 4. Observe — add result to conversation
messages.append({"role": "tool", "content": str(result)})
else:
# Final answer
return response["content"]
return "Max steps reached"
Each iteration: the LLM decides whether to call a tool or give a final answer. If it calls a tool, the result gets added to the conversation and the loop continues.
What does the state look like during the loop?
The messages list grows with each step:
# Step 0: User asks
[{"role": "user", "content": "What's 15% tip on a $48 meal in Tokyo?"}]
# Step 1: LLM calls calculate
# → tool_call: calculate("48 * 0.15")
# → result: 7.2
[..., {"role": "tool", "content": "7.2"}]
# Step 2: LLM calls get_weather (for context)
# → tool_call: get_weather("Tokyo")
# → result: {"temp": 22}
[..., {"role": "tool", "content": "{\"temp\": 22}"}]
# Step 3: LLM gives final answer
# → "A 15% tip on $48 is $7.20. Enjoy Tokyo at 22°C!"
The conversation is the agent's memory. Each tool result gives it more context for the next decision.
How do I prevent infinite loops?
The max_steps parameter is your safety net. Most questions resolve in 1-3 steps. If the agent hits the limit, something is wrong — return a graceful error.
You can also add cost tracking:
def agent_loop(messages, tools, registry, max_steps=5, max_tokens=1000):
total_tokens = 0
for step in range(max_steps):
response = call_llm(messages, tools)
total_tokens += response.get("usage", {}).get("total_tokens", 0)
if total_tokens > max_tokens:
return {"answer": "Token budget exceeded", "tokens_used": total_tokens}
# ... rest of loop
Is this basically how ChatGPT plugins and Claude's tool use work?
Exactly the same pattern. The models got better at deciding when to use tools and interpreting results, but the loop is identical. Think → act → observe → repeat. Your code controls the tools, the safety limits, and the conversation flow. The LLM provides the reasoning.
Practice your skills
Sign up to write and run code in this lesson.
Building an AI Agent
Implement multi-step reasoning with a tool loop and state management.
Yesterday we dispatched single tool calls. But what if the AI needs multiple tools to answer one question?
That's an agent — an AI that runs a loop: think, act, observe, repeat. Instead of one tool call, it chains multiple steps until it has enough information to answer.
Here's the core loop:
def agent_loop(messages, tools, registry, max_steps=5):
for step in range(max_steps):
# 1. Think — ask the LLM what to do
response = call_llm(messages, tools)
# 2. Check — is it a final answer or a tool call?
if response.get("tool_call"):
# 3. Act — execute the tool
tool_call = response["tool_call"]
result = dispatch(tool_call["name"], tool_call["args"], registry)
# 4. Observe — add result to conversation
messages.append({"role": "tool", "content": str(result)})
else:
# Final answer
return response["content"]
return "Max steps reached"
Each iteration: the LLM decides whether to call a tool or give a final answer. If it calls a tool, the result gets added to the conversation and the loop continues.
What does the state look like during the loop?
The messages list grows with each step:
# Step 0: User asks
[{"role": "user", "content": "What's 15% tip on a $48 meal in Tokyo?"}]
# Step 1: LLM calls calculate
# → tool_call: calculate("48 * 0.15")
# → result: 7.2
[..., {"role": "tool", "content": "7.2"}]
# Step 2: LLM calls get_weather (for context)
# → tool_call: get_weather("Tokyo")
# → result: {"temp": 22}
[..., {"role": "tool", "content": "{\"temp\": 22}"}]
# Step 3: LLM gives final answer
# → "A 15% tip on $48 is $7.20. Enjoy Tokyo at 22°C!"
The conversation is the agent's memory. Each tool result gives it more context for the next decision.
How do I prevent infinite loops?
The max_steps parameter is your safety net. Most questions resolve in 1-3 steps. If the agent hits the limit, something is wrong — return a graceful error.
You can also add cost tracking:
def agent_loop(messages, tools, registry, max_steps=5, max_tokens=1000):
total_tokens = 0
for step in range(max_steps):
response = call_llm(messages, tools)
total_tokens += response.get("usage", {}).get("total_tokens", 0)
if total_tokens > max_tokens:
return {"answer": "Token budget exceeded", "tokens_used": total_tokens}
# ... rest of loop
Is this basically how ChatGPT plugins and Claude's tool use work?
Exactly the same pattern. The models got better at deciding when to use tools and interpreting results, but the loop is identical. Think → act → observe → repeat. Your code controls the tools, the safety limits, and the conversation flow. The LLM provides the reasoning.
Practice your skills
Sign up to write and run code in this lesson.