LLM Agents Academy
Module 8: Agent Frameworks & SDKs

Anthropic Claude Agent SDK

Building agents with Claude: extended thinking, tool use, and the agent loop

~12 min readIncludes code

Overview

Throughout Modules 1-6, we built a deep understanding of what agents are, how they reason, how they use tools, and how they manage memory and state. Every concept -- the ReAct loop, tool calling, chain-of-thought reasoning, structured outputs, and multi-turn conversation management -- converges in this lesson. Here we put it all together using the Anthropic Python SDK and Claude, the core framework of this academy.

Unlike higher-level orchestration frameworks that abstract away the agent loop, working directly with the Anthropic SDK gives you full control over every decision the agent makes. You define the tools, manage the conversation history, control when and how the model thinks, and decide how to process each response. This is the foundation that frameworks like LangGraph and CrewAI build upon -- and understanding it deeply will make you a better agent builder regardless of which framework you ultimately choose.

This lesson covers the architecture of a Claude-powered agent, the mechanics of the Messages API, tool_use blocks, adaptive thinking, streaming, and the agentic loop pattern. By the end, you will build a complete research agent from scratch.

The Claude Agent Architecture

A Claude-powered agent is built from five core components that work together in a cycle. The Messages API is the single endpoint through which all interaction flows. System prompts define the agent's persona and capabilities. Tools extend the model beyond text generation. Adaptive thinking gives the model space to reason deeply before acting. And the agentic loop ties everything together, cycling between the model's decisions and your code's execution of those decisions.

graph TD
  subgraph Agent["Claude Agent Architecture"]
      SP["System Prompt<br/>Agent persona & rules"] --> API["Messages API<br/>POST /v1/messages"]
      TOOLS["Tool Definitions<br/>JSON Schema for each tool"] --> API
      THINK["Adaptive Thinking<br/>Internal reasoning"] --> API
      HISTORY["Conversation History<br/>Multi-turn messages array"] --> API
      API --> RESPONSE["Response Content Blocks"]
      RESPONSE --> TEXT["text block<br/>Final answer to user"]
      RESPONSE --> TU["tool_use block<br/>Tool call request"]
      RESPONSE --> TB["thinking block<br/>Reasoning trace"]
  end

  subgraph Loop["Agentic Loop (Your Code)"]
      TU --> EXEC["Execute Tool<br/>Run function locally"]
      EXEC --> TR["tool_result block<br/>Send result back"]
      TR --> HISTORY
  end

  style Agent fill:#ebf8ff,stroke:#3182ce,stroke-width:2px
  style Loop fill:#f0fff4,stroke:#276749,stroke-width:2px
  style API fill:#bee3f8,stroke:#3182ce
  style SP fill:#e2e8f0,stroke:#4a5568
  style TOOLS fill:#e2e8f0,stroke:#4a5568
  style THINK fill:#e2e8f0,stroke:#4a5568
  style HISTORY fill:#e2e8f0,stroke:#4a5568
  style RESPONSE fill:#bee3f8,stroke:#3182ce
  style TEXT fill:#c6f6d5,stroke:#276749
  style TU fill:#fed7d7,stroke:#c53030
  style TB fill:#fefcbf,stroke:#d69e2e
  style EXEC fill:#c6f6d5,stroke:#276749
  style TR fill:#c6f6d5,stroke:#276749

This architecture should look familiar. The tool_use and tool_result blocks are the mechanism that implements the tool calling pattern from Module 3. The conversation history is the state management concept from Module 6. And adaptive thinking is the native implementation of the extended reasoning we explored in Module 2. The difference here is that we are working directly with the API primitives rather than through an abstraction layer.

The Anthropic Python SDK

The Anthropic Python SDK is the primary interface for building agents with Claude. It wraps the Messages API with type-safe Python objects and handles authentication, retries, and streaming.

basic_message.py

The response.content is a list of content blocks -- not a single string. This is a critical design decision. A single response can contain text blocks, thinking blocks, and tool_use blocks, all in one list. Your code must iterate through these blocks and handle each type appropriately. This is the foundation of everything that follows.

Tool Definitions and tool_use Blocks

Tools are defined as JSON Schema objects and passed to the Messages API in the tools parameter. When Claude decides to use a tool, it returns a tool_use content block containing the tool name, a unique ID, and the parsed input parameters. Your code executes the tool and sends the result back as a tool_result block.

tool_definitions.py

Notice that the tool definition's description field is not just documentation -- it is the primary signal Claude uses to decide when to call a tool. A prescriptive description like "Search academic papers on a given topic" works better than a vague one like "paper search function." This connects directly to the tool design principles we covered in Module 3.

Adaptive Thinking for Reasoning

Adaptive thinking is Claude's native reasoning capability. When enabled, Claude generates internal thinking blocks where it works through complex problems before responding. Unlike Chain-of-Thought prompting from Module 2, this reasoning happens in a dedicated phase with the model dynamically controlling how much thought is needed per request.

On Claude Opus 4 and Sonnet 4.6, the recommended approach is thinking: {"type": "adaptive"}, which lets Claude decide when and how deeply to think. You control the cost-quality tradeoff using the effort parameter rather than a fixed token budget.

adaptive_thinking.py

The effort parameter provides a practical way to balance cost and quality:

  • low -- minimal thinking, best for simple classification or short tasks
  • medium -- balanced thinking, good for most applications
  • high -- deep reasoning, ideal for complex analysis and agentic work

This replaces the older budget_tokens approach. Instead of guessing how many tokens to allocate for reasoning, you describe the level of effort you want and let the model allocate accordingly. On Opus 4 models, you also have access to max effort for the most demanding tasks.

The Agentic Loop

The agentic loop is the pattern that transforms a single API call into a persistent agent. It is the implementation of the ReAct cycle from Module 1: the model reasons, decides on an action (tool call), your code executes that action, and the result feeds back into the next iteration.

The loop runs until Claude returns a stop_reason of "end_turn", meaning it has finished its work and is ready to present a final answer.

sequenceDiagram
  participant User
  participant Loop as Agentic Loop<br/>(Your Code)
  participant Claude as Claude API<br/>(Messages)
  participant Tools as Tool Functions

  User->>Loop: "Research LLM agent architectures"
  
  Loop->>Claude: messages.create(tools, messages)
  Note over Claude: Adaptive Thinking...<br/>Reasons about the task
  Claude-->>Loop: thinking + tool_use block<br/>(search_papers, stop_reason=tool_use)

  Loop->>Tools: search_papers(query="LLM agent architectures")
  Tools-->>Loop: [paper1, paper2, paper3, ...]

  Note over Loop: Append assistant content +<br/>tool_result to messages

  Loop->>Claude: messages.create(tools, updated messages)
  Note over Claude: Adaptive Thinking...<br/>Analyzes search results
  Claude-->>Loop: thinking + tool_use block<br/>(get_paper_details, stop_reason=tool_use)

  Loop->>Tools: get_paper_details(paper_id="arxiv:2401.123")
  Tools-->>Loop: {title, abstract, citations, ...}

  Note over Loop: Append assistant content +<br/>tool_result to messages

  Loop->>Claude: messages.create(tools, updated messages)
  Note over Claude: Adaptive Thinking...<br/>Synthesizes findings
  Claude-->>Loop: text block<br/>(final analysis, stop_reason=end_turn)

  Loop->>User: "Here is my analysis of LLM agent architectures..."

Each iteration of the loop follows the same pattern:

  1. Call the API with the current messages array and tool definitions
  2. Check stop_reason -- if "tool_use", the model wants to call a tool; if "end_turn", the model is done
  3. Append the full response.content to messages as an assistant message (this preserves tool_use blocks, thinking blocks, and text blocks)
  4. Execute each tool_use block and collect the results
  5. Append tool_result blocks as a user message, each linked to its tool_use by tool_use_id
  6. Repeat from step 1

The critical detail is step 3: you must append response.content as-is, not just the text. If you extract only the text and discard tool_use blocks, the conversation history breaks and Claude cannot match tool results to tool calls.

Building a Complete Research Agent

Let us bring everything together into a working research agent. This agent uses adaptive thinking to plan its approach, multiple tools to gather information, and the agentic loop to iterate until it has a complete answer.

research_agent.py

This code implements everything we have discussed throughout the academy. The system prompt defines the agent's strategy (Module 2). The tools extend its capabilities (Module 3). The messages array manages state across turns (Module 6). Adaptive thinking enables deep reasoning (Module 2). And the agentic loop orchestrates the entire cycle (Module 1).

Streaming Responses

For real-time applications, Claude supports streaming responses. Instead of waiting for the entire response, you receive content as it is generated. This is essential for agents that interact with users in real time and for long-running tasks where you want to show progress.

streaming.py

Streaming is also recommended when setting max_tokens above approximately 16,000 -- non-streaming requests with high token limits can hit HTTP timeouts. For agentic loops with streaming, use client.messages.stream() inside the loop and call stream.get_final_message() to get the complete message object for processing tool_use blocks and checking stop_reason.

Claude Compared to Other Agent SDKs

Understanding where Claude's SDK sits relative to the frameworks we cover in this module helps you choose the right tool for each situation.

| Aspect | Anthropic SDK (Claude) | OpenAI Agents SDK | LangGraph | |--------|----------------------|-------------------|-----------| | Abstraction level | Low-level API primitives | Mid-level with Runner | High-level graph framework | | Loop control | You write the loop | SDK manages the Runner loop | Framework manages the graph | | Thinking | Adaptive thinking (native) | No equivalent | Not built-in | | Tool execution | You implement tool functions | Decorated Python functions | Tool nodes in graph | | Multi-agent | Manual orchestration or Managed Agents | Built-in handoffs | Graph-based routing | | State management | Manual messages array | Automatic context | Typed state objects |

The Anthropic SDK gives you the most control and transparency. You see every thinking block, every tool call, and every decision the model makes. This is ideal when you need fine-grained control, want to implement custom routing logic, or are building the foundation for a larger system. The tradeoff is that you must manage the conversation state and the agentic loop yourself.

For teams that want Anthropic to manage the loop and provide a hosted execution environment, Managed Agents provides a server-managed alternative where you create a persistent agent configuration, start sessions, and stream events -- but the orchestration runs on Anthropic's infrastructure.

Summary

This lesson covered the complete architecture of building agents with the Anthropic SDK and Claude. You learned how the Messages API returns structured content blocks (text, thinking, and tool_use), how tool definitions use JSON Schema to extend Claude's capabilities, how adaptive thinking provides native reasoning without manual token budgets, and how the agentic loop ties these together into a persistent agent that can reason, act, and iterate.

The research agent we built demonstrates how every concept from Modules 1-6 converges in practice: the ReAct pattern drives the loop, tool calling extends capabilities, conversation history manages state, and extended thinking enables deep reasoning.

In the next lesson, we will see how LangGraph takes these same primitives and wraps them in a graph-based abstraction, trading low-level control for a declarative way to define complex agent workflows.

Check Your Understanding

Question 1 of 4

In Claude's Messages API, what content block type does the model return when it wants to call a tool?