Skip to content
Phase 2: Core Agent SkillsStep 5 of 14Intermediate2 weeks

Agent Architectures

Core patterns for autonomous agents

ReAct patternPlan-and-ExecuteSelf-reflection loopsDecision policiesGoal decomposition

Getting Started

Until now you have built tools and connected to APIs. In this step you will learn the patterns that tie everything together into an autonomous agent. An architecture is the control flow that determines how an agent reasons, acts, and decides when it is done.

The most important architecture to understand is ReAct (Reasoning + Acting). It was introduced in a 2022 research paper and has become the default pattern for most agent implementations. The idea is simple: before every action, the agent writes out its reasoning. After every action, it observes the result and decides what to do next.

Here is the core loop in pseudocode:

while not done:
    thought = llm("Given the goal and observations so far, what should I do next?")
    action = parse_action(thought)
    observation = execute_tool(action)
    history.append(thought, action, observation)
    if thought contains final answer:
        done = True

Key Concepts

Plan-and-Execute separates planning from execution. Instead of interleaving thought and action on every step, the agent first creates a complete plan, then executes each step:

async def plan_and_execute(goal: str, tools: list) -> str:
    # Phase 1: Create the plan
    plan = await llm_call(
        f"Break this goal into 3-5 concrete steps: {goal}\n"
        f"Available tools: {[t['name'] for t in tools]}\n"
        f"Return a numbered list of steps."
    )

    results = []
    # Phase 2: Execute each step
    for step in parse_steps(plan):
        result = await execute_step(step, tools)
        results.append(result)

    # Phase 3: Synthesize
    return await llm_call(
        f"Goal: {goal}\nResults: {results}\nSynthesize a final answer."
    )

This architecture works well for multi-step tasks where the overall strategy can be determined upfront. It is less adaptive than ReAct but more predictable.

Self-reflection adds a review loop where the agent evaluates its own output before returning it. This catches errors, hallucinations, and incomplete answers:

draft = await generate_answer(question, context)
critique = await llm_call(
    f"Review this answer for accuracy and completeness:\n"
    f"Question: {question}\nAnswer: {draft}\n"
    f"List any issues or improvements needed."
)
if "no issues" not in critique.lower():
    final = await llm_call(
        f"Revise this answer based on the critique:\n"
        f"Original: {draft}\nCritique: {critique}"
    )

Goal decomposition is the skill of breaking complex tasks into manageable subtasks. A well-designed agent can recursively decompose goals: "Research competitors and write a report" becomes "1. Identify top 5 competitors, 2. Gather data on each, 3. Compare features, 4. Write summary." Each subtask can be handled by a focused sub-agent or tool sequence.

Hands-On Practice

Implement ReAct from scratch without LangChain or any framework. Give the agent access to two or three simple tools (a calculator, a search stub, a string manipulation function) and test it with questions that require multiple steps. Watch how the reasoning trace helps you debug when the agent goes wrong.

The key lesson here is that architectures are just patterns. Once you understand the core loop, you can adapt it. Most production agents use a hybrid approach: ReAct for general problem solving, Plan-and-Execute for structured workflows, and self-reflection for high-stakes outputs.

Exercises

Implement ReAct from Scratch

Build a ReAct (Reasoning + Acting) agent without using any frameworks. The agent should accept a question, reason about what tool to use, execute the tool, observe the result, and repeat until it can provide a final answer.

Knowledge Check

What distinguishes the ReAct pattern from a simple tool-calling loop?

Milestone Project

Implement a ReAct agent from scratch without using frameworks