Building Multi-Step Agents
From the course AI Function Calling & Tool Use
Agent Architectures with Function Calling
Function calling is the foundation for building AI agents that can plan and execute complex tasks.
The Agent Loop
async function agentLoop(userMessage: string, maxRounds = 10) {
const messages = [{ role: "user", content: userMessage }];
for (let i = 0; i < maxRounds; i++) {
const response = await callModel(messages, tools);
if (response.stopReason === "end_turn") {
return response.content; // Agent is done
}
if (response.stopReason === "tool_use") {
messages.push({ role: "assistant", content: response.content });
const results = await executeToolCalls(response.toolCalls);
messages.push({ role: "user", content: results });
}
}
return "Max rounds reached";
}
Planning Patterns
- ReAct — Reason, Act, Observe in a loop
- Plan-then-execute — Generate a full plan, then execute steps
- Tree of thought — Explore multiple approaches, pick the best
Real-World Agent Examples
Research Agent:
web_search("latest AI benchmarks 2026")fetch_page(url)for top resultsextract_data(content, "benchmark scores")create_summary(data)
Customer Support Agent:
lookup_customer(email)check_order_status(order_id)create_ticket(issue_details)orprocess_refund(order_id)
Safety Considerations
- Require human approval for destructive actions (deletes, payments)
- Implement permission levels for different function categories
- Log full agent traces for debugging and compliance
- Add circuit breakers for runaway agent behavior
Common Pitfall: Agent Loop Without Loop Detection
Agents can get stuck calling the same function repeatedly when a tool returns an unexpected result. Detect repeated calls and break the loop:
const toolCallHashes = new Set<string>();
for (const toolCall of response.toolCalls ?? []) {
const hash = JSON.stringify({ name: toolCall.name, args: toolCall.arguments });
if (toolCallHashes.has(hash)) {
return "Agent stuck in loop — stopping with partial result.";
}
toolCallHashes.add(hash);
}
Best Practice: Require Human Approval for Destructive Tool Calls
For tools that modify data (deletes, payments, sends), require explicit confirmation before execution:
const DESTRUCTIVE_TOOLS = ["delete_file", "process_payment", "send_email"];
if (DESTRUCTIVE_TOOLS.includes(toolCall.name)) {
const approved = await requestHumanApproval(toolCall);
if (!approved) return { error: "Action was rejected by the user." };
}
Named Product Reference: Composio for Pre-Built Tool Integrations
Composio provides 250+ pre-built tool integrations (GitHub, Slack, Salesforce, Gmail) for AI agents. Instead of writing custom function schemas and API calls for every service, import the Composio tool connector and it handles authentication and execution automatically — significantly reducing the time to build a multi-tool agent.
Key Takeaways
- •The agent loop iterates between model responses and tool execution
- •Planning patterns like ReAct structure multi-step reasoning
- •Require human approval for destructive or high-stakes actions
- •Log full agent traces for debugging and compliance
Progress is kept in this browser only.