Skip to content

Agentic Loops & Core API

Claude Messages API gives you fine-grained control over the agent’s behavior. But with that control, your app is responsible for managing the loop, executing tools, and sending results back to Claude. That’s where agentic loops can simplify things.

1. Core Concept

An agentic loop is a deterministic control-flow pattern around Claude. It is not a prompt trick, not a retry loop, and not a normal chatbot turn.

The canonical loop is:

1. User sends task.
2. App calls Messages API with messages + tools.
3. Claude responds.
4. App checks stop_reason.
5. If stop_reason == tool_use:
      a. Extract all tool_use blocks.
      b. Execute corresponding tools in app code.
      c. Append assistant message to history.
      d. Append user tool_result message to history.
      e. Call Claude again.
6. If stop_reason == end_turn:
      Return final answer.
7. If stop_reason is max_tokens / refusal:
      Run the appropriate fallback path.

For this module, the two stop reasons to know cold are:

stop_reason Meaning Loop action
tool_use Claude wants one or more tools executed Continue the loop
end_turn Claude has finished naturally Terminate and return final answer

Officially, stop_reason is part of every successful Messages API response and tells you why Claude stopped generating; broader production code should also handle values such as max_tokens and refusal, but this module's emphasis is tool_use vs end_turn.

1.1 Request Fields

While making a call to Claude Message API, you need to forward these arguments:

Field Purpose
model Selects Claude model
max_tokens Caps output tokens
system Top-level system instruction
messages Conversation history
tools Tool definitions
tool_choice Controls whether Claude may/must call tools
output_config.format Structured JSON output

1.2 Message Roles

role Meaning
user User/app-provided turn, including tool_result blocks
assistant Claude’s prior response, including tool_use blocks
system Instructional authority; normally top-level, with newer support for mid-conversation system messages under placement rules

1.3 content Blocks

Block type Direction Purpose
text Claude → app/user Natural language
tool_use Claude → app Structured request to run a tool
tool_result app → Claude Result of a prior tool call
thinking Claude → app Thinking/summarized thinking when enabled
citation blocks Claude → app Source-grounded references when citations are enabled

1.4 Stop Reasons

stop_reason is a top-level field on the assistant Message response, just like content, role, usage, etc.

Stop reason Meaning Correct handling
end_turn Claude finished naturally Return/process final answer
tool_use Claude wants a tool executed Execute tool(s), send tool_result, continue loop
max_tokens Output hit your token cap Treat as truncated; continue or show truncation notice
stop_sequence Custom stop sequence hit Respect the sequence and inspect output
refusal Claude declined to answer Handle as a successful response with fallback/escalation

Sample JSON response with role, content, and stop_reason:

{
  "id": "msg_...",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Here's the answer..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

1.5 The Most Important Rule

Use stop_reason as the authoritative loop-control signal.

Do not terminate because Claude produced text and stopped. Claude can return text and tool_use then for a user message (app response) with tool_result block. The classic bug scenario is that code checks response.content[0].type == "text", sees text, and returns early even though Claude also requested a tool.

Correct logic:

if response.stop_reason == "tool_use":
    # execute tools and continue
elif response.stop_reason == "end_turn":
    # final answer
else:
    # fallback handling for other stop reasons

Incorrect logic:

if response.content[0].type == "text":    
    return response.content[0].text

That is the premature-termination bug.


2. Tool-Use Contract

Tool use is a contract between your application and Claude:

  1. You define tools and schemas.
  2. Claude decides when to call a tool.
  3. Claude emits a structured tool_use block.
  4. Your application executes the tool.
  5. Your application returns a tool_result.
  6. Claude reasons over that result and either calls another tool or ends.

Anthropic’s docs are explicit that Claude does not execute client tools itself; your code executes the operation and sends the result back.

The next request after a tool call must include:

original messages
+ assistant message containing tool_use
+ user message containing tool_result

This is critical. If you execute the tool but do not append the tool result to history, Claude cannot reason over the new information in the next iteration.

2.1 tool_choice

Option Meaning
auto Claude decides whether to call a tool
any Claude must call one of the tools
tool Claude must call a specific tool
none Claude cannot use tools

2.2 strict Tool Use

Use strict: true when tool arguments must conform to JSON Schema. Anthropic describes strict tool use as grammar-constrained sampling that guarantees Claude’s tool inputs match the schema. If the scenario says “tool arguments must be valid,” “nested schema,” “downstream type safety,” or “malformed parameters are causing failures,” look for strict tool use and/or schema validation.

2.3 Handling Tool Calls Correctly

assistant:
  content:
      type: tool_use
      id: toolu_123
      name: get_order
      input: { "order_id": "A123" }

user:
  content:
    - type: tool_result
      tool_use_id: toolu_123
      content: "{ \"status\": \"shipped\" }"

When Claude returns a client tool call, the response has stop_reason: "tool_use" and one or more tool_use blocks containing id, name, and input. Your next message should include tool_result blocks whose tool_use_id matches the original tool_use_id.

2.4 Parallel Tool Use

Claude may return multiple tool_use blocks in one assistant turn. Anthropic says the API does not prescribe execution order; your app can execute them concurrently, sequentially, or in another pattern that fits your tool semantics.


3. Model-Driven Decision-Making

Agentic loops are normally model-driven, not fixed decision trees. Claude chooses which tool to call based on the current context and available tools.

Example interpretation:

Scenario Preferred architecture
Claude should decide whether to search, calculate, or answer directly Model-driven tool selection
Compliance requires approval before refunding money Deterministic app logic overrides model flexibility
Security policy must always block certain operations Enforce in code, not only prompt instructions

The key nuance: model-driven flexibility is preferred until business rules require deterministic enforcement.

3.1 Anti-Patterns

Three anti-patterns highly relevant:

Anti-pattern Why wrong Correct answer
Parsing natural language like “I’m done” Natural language is ambiguous Use stop_reason
Using arbitrary iteration caps as the primary stop condition Can cut off useful work or waste calls Use stop_reason; cap only as safety net
Checking response.content[0].type == "text" Claude can return text plus tool calls Use stop_reason

A safety cap is still good engineering. A cap such as MAX_ITERATIONS = 20 is recommended, but only as a runaway-loop guard, not as the primary termination mechanism.


4. Decision Heuristics

Scenario wording Best answer pattern
“Must call external system” Tool use
“Must not proceed without approval” Loop with human gate
“Must produce parseable JSON” Structured outputs
“Tool inputs must be schema-valid” Strict tool use
“Multiple independent lookups” Parallel tool use
“Order matters” Disable/avoid parallel tool use
“Long-running task, many turns” Compaction / Managed Agents
“Need custom audit logs” Manual loop
“Output cut off” Handle max_tokens / continue generation
“Context limit reached” Compaction/context management

4.1 Common Mistakes

  1. “Just improve the prompt” when the issue requires schema validation, code validation, or human approval.
  2. Ignoring stop_reason.
  3. Assuming one and only one tool call.
  4. Returning tool results without matching tool_use_id.
  5. Letting Claude decide irreversible writes without approval.
  6. Returning huge raw tool payloads into context.
  7. Treating max_tokens as an API error.
  8. Treating refusal as an exception instead of a successful response state.
  9. Using parallel tool execution when calls mutate shared state.
  10. Using a managed abstraction when the scenario requires custom execution control.
  11. Depending on assistant prose formatting rather than content block types.
  12. Summarizing away facts needed for audit/compliance.
  13. Letting tool descriptions be vague or overlapping.
  14. Failing to test error cases, not just happy paths.

Test yourself on this topic Interactive questions for Task 1.1 — Agentic Loops & Core API, with instant explanations and scoring.
Start quiz →