Skip to content
Let's Talk
AI Engineering

AI Agents — Patterns and Pitfalls

Felix Schmidt

AI Agents — Patterns and Pitfalls

AI agents — systems that use LLMs to reason, plan, and take actions autonomously — have moved from research curiosity to production reality. But building reliable agents is significantly harder than building a chatbot. Agents introduce loops, branching logic, external tool calls, and compounding error rates that can spiral out of control.

This post covers the major architectural patterns for AI agents, the pitfalls that catch most teams, and practical strategies for building agents that actually work in production.

What Makes an Agent an Agent?

A chatbot takes input and produces output. An agent takes input, decides what to do, executes actions, observes results, and iterates until a goal is achieved. The key difference is the action loop: the agent can call external tools, inspect the results, and adjust its strategy.

A minimal agent loop looks like this:

async function agentLoop(task: string, tools: Tool[], maxSteps = 10) {
  const messages: Message[] = [
    { role: "system", content: SYSTEM_PROMPT },
    { role: "user", content: task },
  ];

  for (let step = 0; step < maxSteps; step++) {
    const response = await llm.chat(messages, { tools });

    if (response.finishReason === "stop") {
      return response.content; // Agent is done
    }

    // Execute tool calls
    for (const toolCall of response.toolCalls) {
      const result = await executeTool(toolCall);
      messages.push(
        { role: "assistant", content: null, toolCalls: [toolCall] },
        { role: "tool", content: result, toolCallId: toolCall.id }
      );
    }
  }

  throw new Error("Agent exceeded maximum steps");
}

This is the foundation. Everything else is refinement.

The ReAct Pattern

ReAct (Reasoning + Acting) is the most widely adopted agent pattern. The LLM alternates between reasoning (thinking about what to do) and acting (calling a tool). Each step produces an explicit thought before the action.

The reasoning trace makes the agent''s decision process transparent and debuggable. In practice, you implement ReAct by structuring your system prompt to elicit step-by-step reasoning:

You are an agent that solves tasks step by step.
For each step:
1. THINK: Analyze the current situation and decide what to do next
2. ACT: Call the appropriate tool
3. OBSERVE: Review the tool result
4. Repeat until the task is complete

ReAct works well for tasks with 3–8 steps. Beyond that, context window pressure and compounding errors become problematic.

Tool Design: The Make-or-Break Factor

The quality of your agent depends more on your tool design than on your prompt engineering. Well-designed tools make the agent''s job easy; poorly designed tools lead to confusion, errors, and infinite loops.

Principles for good tool design:

  1. Clear, unambiguous names: searchDocumentsByDate is better than search
  2. Focused scope: Each tool should do one thing well
  3. Informative error messages: Return errors that help the agent recover, not stack traces
  4. Input validation: Validate parameters before execution and return clear errors for invalid inputs
  5. Idempotent where possible: If the agent retries a tool call, it should not cause duplicate side effects

Here is an example of a well-designed tool definition:

const tools: Tool[] = [
  {
    name: "searchKnowledgeBase",
    description: "Search the knowledge base for documents matching a query. " +
      "Returns the top 5 most relevant documents with title and snippet. " +
      "Use this when you need factual information to answer a question.",
    parameters: {
      type: "object",
      properties: {
        query: {
          type: "string",
          description: "Natural language search query (2-100 characters)"
        },
        category: {
          type: "string",
          enum: ["engineering", "product", "finance", "hr"],
          description: "Optional category filter to narrow results"
        }
      },
      required: ["query"]
    }
  }
];

Multi-Agent Systems

For complex tasks, a single agent often struggles. Multi-agent architectures split the work across specialized agents that collaborate:

Orchestrator-Worker pattern: A planning agent breaks down the task and delegates subtasks to specialized worker agents. The orchestrator synthesizes the results.

Pipeline pattern: Agents are chained sequentially, where the output of one becomes the input of the next. Good for workflows with clearly defined stages (e.g., research, draft, review, publish).

Debate pattern: Multiple agents propose solutions and critique each other. Useful for tasks where diverse perspectives improve quality.

The key challenge in multi-agent systems is coordination. How do agents share context? How do you handle conflicting outputs? How do you prevent cost explosion when agents spawn sub-agents recursively?

My recommendation: start with a single agent. Only move to multi-agent when you can clearly identify distinct roles that require different capabilities or context windows.

Guardrails and Safety

Agents acting autonomously in the real world need constraints. Without guardrails, agents can:

  • Spend unlimited money on API calls
  • Execute destructive operations (deleting data, sending emails)
  • Enter infinite loops that burn through tokens
  • Hallucinate tool names or parameters that do not exist
  • Leak sensitive information through tool calls

Essential guardrails:

interface AgentConfig {
  maxSteps: number;           // Hard limit on reasoning steps
  maxTokenBudget: number;     // Maximum tokens across all LLM calls
  allowedTools: string[];     // Whitelist of permitted tools
  requireConfirmation: string[]; // Tools that need human approval
  timeoutMs: number;          // Wall-clock timeout
}

function validateToolCall(call: ToolCall, config: AgentConfig): boolean {
  if (!config.allowedTools.includes(call.name)) {
    throw new Error(`Tool "${call.name}" is not in the allowed list`);
  }
  // Add more validation as needed
  return true;
}

For high-stakes operations, implement a human-in-the-loop pattern where the agent pauses and asks for confirmation before executing sensitive actions.

The Five Pitfalls That Will Burn You

1. The Infinite Loop. The agent gets stuck in a cycle — calling the same tool with the same parameters, or alternating between two tools without making progress. The fix: implement loop detection that tracks recent tool calls and breaks out when it detects repetition.

2. Hallucinated Tool Calls. The LLM invents tool names or parameters that do not exist. This happens more often with smaller models and ambiguous tool definitions. The fix: strict validation of every tool call against your schema before execution.

3. Context Window Overflow. Long-running agents accumulate messages until they exceed the context window. The fix: implement a sliding window or summarization strategy that condenses earlier conversation history.

4. Cost Explosion. An agent that runs 50 steps with GPT-4-class models can cost several dollars per invocation. Multiply by thousands of users, and you have a budget problem. The fix: set hard token budgets, use cheaper models for simple reasoning steps, and cache tool results aggressively.

5. Insufficient Error Handling. When a tool call fails, the agent often does not know how to recover gracefully. It either retries indefinitely or gives up entirely. The fix: design your tools to return structured errors with recovery suggestions, and include error handling guidance in your system prompt.

Practical Advice for Production Agents

  1. Log everything. Every LLM call, every tool call, every decision point. You will need these logs for debugging, optimization, and cost tracking.

  2. Start with deterministic fallbacks. If the agent cannot solve a problem in N steps, fall back to a deterministic code path or escalate to a human.

  3. Test with adversarial inputs. Users will ask your agent to do things you never imagined. Build a test suite that includes edge cases, ambiguous requests, and deliberately confusing inputs.

  4. Monitor cost per task. Track the median and p95 cost per agent invocation. Set alerts for anomalies.

  5. Version your prompts and tools. Treat them like code — version control, code review, staged rollouts.

Conclusion

AI agents represent a genuine leap in what software systems can do. But they also introduce a new category of failure modes that traditional software engineering does not prepare you for. The teams that succeed with agents are the ones that treat them as engineering systems — with testing, monitoring, guardrails, and healthy skepticism — rather than magical black boxes.

Build incrementally, measure relentlessly, and always have a fallback plan.

This topic relevant to your team? Let's discuss how I can help.

This website uses third-party services (Google reCAPTCHA, Calendly) that may set cookies. See our Privacy Policy for details.