Welcome to your first lesson in building effective agents with Claude! Whether you're completely new to the Anthropic API or have some experience with it, this lesson will ensure you have a solid foundation for the advanced agent-building techniques we'll cover later in the path.
In this lesson, you'll learn how to send messages to Claude using the Anthropic API and understand the complete response structure. By the end, you'll be able to create a Ruby script that communicates with Claude and examine the full JSON response. This understanding is crucial because throughout this course, we'll be working with different parts of Claude's responses — from basic text content to tool usage metadata and conversation flow control.
This foundation is essential because later lessons will extend this same pattern to develop more complex workflows with Claude.
To communicate with Claude, you'll need two things: the anthropic gem and an API key from Anthropic. The gem handles all the technical details of making API requests, and you'd normally install it using gem install anthropic. The API key authenticates your requests, and the Anthropic client automatically looks for it in the ANTHROPIC_API_KEY environment variable.
In CodeSignal, we've already configured everything for you — the gem is pre-installed and your API key is set up, so you can focus on learning the core concepts without worrying about setup details.
Every interaction with Claude follows a structured conversation pattern. Understanding this structure is key to building effective agents, as you'll need to manage conversation state and interpret various response components throughout this course.
In the Messages API, Claude conversation history uses two message roles: user and assistant. Separately, the top-level system parameter sets the context and instructions for Claude's behavior — essentially Claude's job description for the conversation. The user role represents messages from you or your application users, and the assistant role represents Claude's responses. This structure helps Claude maintain context and understand conversation flow, which becomes critical when building multi-step agent workflows.
When you send a request to Claude, you package several pieces of information: the model you want to use, a system prompt that defines Claude's behavior, an array of messages representing the conversation history, and a max_tokens limit for the response length. Tokens roughly correspond to words, so max_tokens: 2000 allows to respond with approximately 1,500 – 2,000 words.
Let's build our first Claude interaction by examining each component. We'll start with the basic imports and client initialization, then define our model and system_prompt:
The client automatically finds your API key in the environment variables. The system_prompt influences how Claude responds throughout the conversation — it's like setting Claude's personality and expertise for the entire interaction. Understanding system prompts is crucial because later in the course, we'll use them to define how Claude should use tools and handle complex agent workflows.
Now we'll create the messages array representing our conversation:
Each message is a hash with role and content keys. Even for a single message, we use an array because conversations can have multiple exchanges.
With our message prepared, we can now send it to Claude:
The client.messages.create method sends an HTTP request to Anthropic's servers, where Claude processes your message according to the system_prompt and returns a structured response.
Note that max_tokens is a required parameter that limits how long Claude's response can be. Think of Claude as having two token limits: a context window (how much total conversation history it can remember) and a response limit (how much it can write back to you). The context window for Claude Sonnet is around 200,000 tokens, which can hold roughly 150,000 words of conversation history. The max_tokens parameter controls the response limit — setting it to 2000 means Claude can respond with up to about 1,500 – 2,000 words, leaving the rest of the available for your conversation history.
To understand what Claude returns, let's examine the complete response structure:
The response.to_h method converts Claude's response into a Ruby hash, and JSON.pretty_generate formats it as readable JSON. You'll see output like this:
This JSON structure contains everything you need to understand how Claude processed your request and what it returned.
Understanding this response structure is essential for the rest of the course. Key fields include:
id— Provides a unique identifier useful for logging and debugging.content— ContainsClaude's response as an array of content blocks. Notice it's an array because responses can contain multiple blocks of different types — text blocks like we see here, but also thinking blocks, tool usage blocks, and other content types we'll explore later.stop_reason— Tells you whyClaudestopped generating text."end_turn"meansClaudenaturally concluded its response, but you'll encounter other values like"tool_use"in later lessons whenClaudedecides to call a function.usage— Provides detailed token consumption information, which becomes important for monitoring agent performance and costs.
Pay special attention to the content array structure. Each block has a type field (here it's "text") and the actual content.
Most of the time, you'll want to access just Claude's text response rather than the full JSON structure. Let's see how to extract the clean text content:
Since our response contains only one content block, we can access it directly with response.content.first.text. This is the easiest approach for simple interactions, but it won't always be this straightforward — later in the course, you'll encounter responses with multiple content blocks of different types that require more sophisticated handling.
This produces the clean text output:
When we access response.content.first.text, we're getting the text from the first content block. This understanding of content block filtering will be crucial as we progress to more complex agent workflows.
Real conversations don't end after one exchange. To continue our conversation with Claude, we need to maintain the conversation history by adding Claude's response to our messages array, then append our follow-up question:
Notice how we append Claude's entire content array to maintain the conversation structure. This preserves all content blocks and their types, which becomes crucial when working with responses that contain multiple content types. Now let's see Claude's response to our follow-up question:
This produces output like:
The conversation continues naturally because Claude can see the full context of our previous exchange, allowing it to provide a focused answer about training specifically.
Claude can also show its reasoning process through thinking — internal deliberation that helps it provide better responses. Let's continue our conversation with thinking enabled to see how Claude works through problems:
When thinking is enabled, Claude's response can contain multiple content blocks of different types.
When you enable thinking, Claude's response structure becomes more complex — it may contain multiple content blocks with different type values. Let's examine the full response structure to understand what we're working with:
This produces an output like:
Notice the content array now contains two blocks with different type fields: a "thinking" block representing Claude's internal reasoning process, and a "text" block containing the final response. The "thinking" block also includes a "signature" field that Anthropic uses to verify the integrity of the thinking content. The key insight here is that responses with enabled are where different blocks serve different purposes.
When Claude's response contains multiple content blocks (like thinking and text), we need a way to extract just the parts we want. Let's filter the response to get only the text content that we'd show to a user:
This code filters through all content blocks in the response, selects only those with type == "text", extracts their text content, and joins them together. This produces the clean final output:
This pattern of filtering content blocks by type is essential for building robust agents. Later in the course, you'll encounter responses with tool usage blocks, multiple text blocks, and other content types that require similar filtering and processing techniques.
You've now successfully understood how to interact with Claude using the Anthropic API, examined the complete response structure, built multi-turn conversations, and explored extended thinking capabilities. You understand how to structure conversations with roles, send requests with system prompts, maintain conversation context, and interpret response metadata, including different content block types.
The key concepts you've learned — conversation management, content block filtering, and response structure analysis — form the foundation for the advanced agent-building techniques we'll cover throughout this course.
In the upcoming practices, you'll get hands-on experience building on these concepts and exploring different ways to interact with Claude. This foundation will serve you well as we progress through more advanced topics in the course!
