mirror of
https://github.com/anthropics/claude-cookbooks.git
synced 2025-10-06 01:00:28 +03:00
claude 3-7
This commit is contained in:
652
extended_thinking/extended_thinking.ipynb
Normal file
652
extended_thinking/extended_thinking.ipynb
Normal file
@@ -0,0 +1,652 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Extended Thinking\n",
|
||||
"\n",
|
||||
"## Table of contents\n",
|
||||
"- [Setup](#setup)\n",
|
||||
"- [Basic example](#basic-example)\n",
|
||||
"- [Streaming with extended thinking](#streaming-with-extended-thinking)\n",
|
||||
"- [Token counting and context window management](#token-counting-and-context-window-management)\n",
|
||||
"- [Understanding redacted thinking](#understanding-redacted-thinking-blocks)\n",
|
||||
"- [Handling error cases](#handling-error-cases)\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use Claude 3.7 Sonnet's extended thinking feature with various examples and edge cases.\n",
|
||||
"\n",
|
||||
"Extended thinking gives Claude 3.7 Sonnet enhanced reasoning capabilities for complex tasks, while also providing transparency into its step-by-step thought process before it delivers its final answer. When extended thinking is turned on, Claude creates `thinking` content blocks where it outputs its internal reasoning. Claude incorporates insights from this reasoning before crafting a final response. For more information on extended thinking, see our [documentation](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"First, let's install the necessary packages and set up our environment."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install anthropic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import anthropic\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# Set your API key as an environment variable or directly\n",
|
||||
"# os.environ[\"ANTHROPIC_API_KEY\"] = \"your-api-key-here\"\n",
|
||||
"\n",
|
||||
"# Initialize the client\n",
|
||||
"client = anthropic.Anthropic()\n",
|
||||
"\n",
|
||||
"# Helper functions\n",
|
||||
"def print_thinking_response(response):\n",
|
||||
" \"\"\"Pretty print a message response with thinking blocks.\"\"\"\n",
|
||||
" print(\"\\n==== FULL RESPONSE ====\")\n",
|
||||
" for block in response.content:\n",
|
||||
" if block.type == \"thinking\":\n",
|
||||
" print(\"\\n🧠 THINKING BLOCK:\")\n",
|
||||
" # Show truncated thinking for readability\n",
|
||||
" print(block.thinking[:500] + \"...\" if len(block.thinking) > 500 else block.thinking)\n",
|
||||
" print(f\"\\n[Signature available: {bool(getattr(block, 'signature', None))}]\")\n",
|
||||
" if hasattr(block, 'signature') and block.signature:\n",
|
||||
" print(f\"[Signature (first 50 chars): {block.signature[:50]}...]\")\n",
|
||||
" elif block.type == \"redacted_thinking\":\n",
|
||||
" print(\"\\n🔒 REDACTED THINKING BLOCK:\")\n",
|
||||
" print(f\"[Data length: {len(block.data) if hasattr(block, 'data') else 'N/A'}]\")\n",
|
||||
" elif block.type == \"text\":\n",
|
||||
" print(\"\\n✓ FINAL ANSWER:\")\n",
|
||||
" print(block.text)\n",
|
||||
" \n",
|
||||
" print(\"\\n==== END RESPONSE ====\")\n",
|
||||
"\n",
|
||||
"def count_tokens(messages):\n",
|
||||
" \"\"\"Count tokens for a given message list.\"\"\"\n",
|
||||
" result = client.messages.count_tokens(\n",
|
||||
" model=\"claude-3-7-sonnet-20250219\",\n",
|
||||
" messages=messages\n",
|
||||
" )\n",
|
||||
" return result.input_tokens"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Basic example\n",
|
||||
"\n",
|
||||
"Let's start with a basic example to show extended thinking in action:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"==== FULL RESPONSE ====\n",
|
||||
"\n",
|
||||
"🧠 THINKING BLOCK:\n",
|
||||
"Let's work through this problem step by step:\n",
|
||||
"\n",
|
||||
"Initial situation:\n",
|
||||
"- Three people each pay $10, for a total of $30 given to the manager.\n",
|
||||
"- The room actually costs $25.\n",
|
||||
"- Manager gives $5 to the bellboy to return to the customers.\n",
|
||||
"- Bellboy keeps $2 and gives $1 back to each person ($3 total).\n",
|
||||
"\n",
|
||||
"After these transactions:\n",
|
||||
"- Each person has effectively paid $9 (they paid $10 and got $1 back).\n",
|
||||
"- So the three people together paid $27.\n",
|
||||
"- The hotel kept $25 for the room.\n",
|
||||
"- The bellboy kept $2.\n",
|
||||
"\n",
|
||||
"So the mo...\n",
|
||||
"\n",
|
||||
"[Signature available: True]\n",
|
||||
"[Signature (first 50 chars): EuYBCkQYAiJAGF6X7aWRuRByTdymAUdNOMC++3ZqSJv7jcY5Ly...]\n",
|
||||
"\n",
|
||||
"✓ FINAL ANSWER:\n",
|
||||
"# Hotel Bill Puzzle Solution\n",
|
||||
"\n",
|
||||
"This is a classic misdirection puzzle that confuses us by mixing up two different accounting approaches.\n",
|
||||
"\n",
|
||||
"## The actual flow of money\n",
|
||||
"\n",
|
||||
"1. Three people each pay $10, totaling $30\n",
|
||||
"2. The hotel keeps $25 for the room\n",
|
||||
"3. The bellboy keeps $2\n",
|
||||
"4. The guests receive $3 back ($1 each)\n",
|
||||
"\n",
|
||||
"## The accounting error in the puzzle\n",
|
||||
"\n",
|
||||
"The error occurs when the puzzle tries to add:\n",
|
||||
"- What the guests paid ($27 total after refunds)\n",
|
||||
"- What the bellboy kept ($2)\n",
|
||||
"\n",
|
||||
"This is incorrect accounting because the $2 the bellboy kept is already included in the $27 the guests paid. The money should be tracked from a single perspective.\n",
|
||||
"\n",
|
||||
"## Correct accounting\n",
|
||||
"\n",
|
||||
"From the guests' perspective:\n",
|
||||
"- $27 (what they ultimately paid)\n",
|
||||
"- = $25 (to the hotel) + $2 (to the bellboy)\n",
|
||||
"\n",
|
||||
"There is no missing dollar. The puzzle creates confusion by inappropriately adding money from different accounting perspectives.\n",
|
||||
"\n",
|
||||
"==== END RESPONSE ====\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def basic_thinking_example():\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=\"claude-3-7-sonnet-20250219\",\n",
|
||||
" max_tokens=4000,\n",
|
||||
" thinking= {\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": 2000\n",
|
||||
" },\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Solve this puzzle: Three people check into a hotel. They pay $30 to the manager. The manager finds out that the room only costs $25 so he gives $5 to the bellboy to return to the three people. The bellboy, however, decides to keep $2 and gives $1 back to each person. Now, each person paid $10 and got back $1, so they paid $9 each, totaling $27. The bellboy kept $2, which makes $29. Where is the missing $1?\"\n",
|
||||
" }]\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" print_thinking_response(response)\n",
|
||||
"\n",
|
||||
"basic_thinking_example()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Streaming with extended thinking\n",
|
||||
"\n",
|
||||
"This example shows how to handle streaming with thinking:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"--- Starting thinking block ---\n",
|
||||
"This is a classic mathematical puzzle that contains a misdirection in how the calculations are presented. Let's break it down step by step:\n",
|
||||
"\n",
|
||||
"Initial situation:\n",
|
||||
"- Three people each pay $10, for a total of $30 given to the manager.\n",
|
||||
"- The room actually costs $25.\n",
|
||||
"- The manager gives $5 to the bellboy to return to the customers.\n",
|
||||
"- The bellboy keeps $2 and returns $1 to each person (total of $3 returned).\n",
|
||||
"\n",
|
||||
"Now, let's analyze the accounting:\n",
|
||||
"\n",
|
||||
"What actually happened:\n",
|
||||
"- The three people originally paid $30.\n",
|
||||
"- They got back $3 in total ($1 each).\n",
|
||||
"- So they actually paid $30 - $3 = $27 in total.\n",
|
||||
"- Of this $27, $25 went to the hotel for the room.\n",
|
||||
"- The remaining $2 went to the bellboy.\n",
|
||||
"- $25 + $2 = $27, which matches what the guests paid. Everything balances.\n",
|
||||
"\n",
|
||||
"The error in the puzzle is in how it frames the question. The puzzle states \"each person paid $10 and got back $1, so they paid $9 each, totaling $27. The bellboy kept $2, which makes $29.\" This is mixing up different accounting methods. \n",
|
||||
"\n",
|
||||
"The $27 that the guests paid in total should be divided as:\n",
|
||||
"- $25 for the room\n",
|
||||
"- $2 for the bellboy\n",
|
||||
"\n",
|
||||
"When we add the bellboy's $2 to the guests' $27, we're double-counting the $2, which creates the illusion of a missing dollar. The $2 is already included in the $27, so we shouldn't add it again.\n",
|
||||
"\n",
|
||||
"Another way to think about it: Out of the original $30, $25 went to the hotel, $3 went back to the guests, and $2 went to the bellboy. That's $25 + $3 + $2 = $30, so everything is accounted for.\n",
|
||||
"[Completed thinking block, 1492 characters]\n",
|
||||
"--- Finished thinking block ---\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"--- Starting text block ---\n",
|
||||
"# The Missing $1 Puzzle Solution\n",
|
||||
"\n",
|
||||
"This puzzle uses a misleading way of accounting that creates confusion. Let's clarify what actually happened:\n",
|
||||
"\n",
|
||||
"## The correct accounting:\n",
|
||||
"- Three people paid $30 total initially\n",
|
||||
"- The room cost $25\n",
|
||||
"- The bellboy kept $2\n",
|
||||
"- The guests received $3 back ($1 each)\n",
|
||||
"\n",
|
||||
"So where did all the money go?\n",
|
||||
"- $25 went to the hotel\n",
|
||||
"- $2 went to the bellboy\n",
|
||||
"- $3 went back to the guests\n",
|
||||
"- $25 + $2 + $3 = $30 ✓\n",
|
||||
"\n",
|
||||
"## The error in the puzzle:\n",
|
||||
"The puzzle incorrectly adds the $27 paid by the guests (after refunds) to the $2 kept by the bellboy. This is a mistake because the $2 kept by the bellboy is already part of the $27.\n",
|
||||
"\n",
|
||||
"The puzzle creates the illusion of a missing dollar by mixing two different perspectives:\n",
|
||||
"1. How much the guests paid ($27 total)\n",
|
||||
"2. Where the original $30 went (hotel + bellboy + refunds)\n",
|
||||
"\n",
|
||||
"There is no missing dollar - it's just an accounting trick!--- Finished text block ---\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"--- Message complete ---\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def streaming_with_thinking():\n",
|
||||
" with client.messages.stream(\n",
|
||||
" model=\"claude-3-7-sonnet-20250219\",\n",
|
||||
" max_tokens=4000,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": 2000\n",
|
||||
" },\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Solve this puzzle: Three people check into a hotel. They pay $30 to the manager. The manager finds out that the room only costs $25 so he gives $5 to the bellboy to return to the three people. The bellboy, however, decides to keep $2 and gives $1 back to each person. Now, each person paid $10 and got back $1, so they paid $9 each, totaling $27. The bellboy kept $2, which makes $29. Where is the missing $1?\"\n",
|
||||
" }]\n",
|
||||
" ) as stream:\n",
|
||||
" # Track what we're currently building\n",
|
||||
" current_block_type = None\n",
|
||||
" current_content = \"\"\n",
|
||||
" \n",
|
||||
" for event in stream:\n",
|
||||
" if event.type == \"content_block_start\":\n",
|
||||
" current_block_type = event.content_block.type\n",
|
||||
" print(f\"\\n--- Starting {current_block_type} block ---\")\n",
|
||||
" current_content = \"\"\n",
|
||||
" \n",
|
||||
" elif event.type == \"content_block_delta\":\n",
|
||||
" if event.delta.type == \"thinking_delta\":\n",
|
||||
" print(event.delta.thinking, end=\"\", flush=True) # Just print dots for thinking to avoid clutter\n",
|
||||
" current_content += event.delta.thinking\n",
|
||||
" elif event.delta.type == \"text_delta\":\n",
|
||||
" print(event.delta.text, end=\"\", flush=True)\n",
|
||||
" current_content += event.delta.text\n",
|
||||
" \n",
|
||||
" elif event.type == \"content_block_stop\":\n",
|
||||
" if current_block_type == \"thinking\":\n",
|
||||
" # Just show a summary for thinking\n",
|
||||
" print(f\"\\n[Completed thinking block, {len(current_content)} characters]\")\n",
|
||||
" elif current_block_type == \"redacted_thinking\":\n",
|
||||
" print(\"\\n[Redacted thinking block]\")\n",
|
||||
" print(f\"--- Finished {current_block_type} block ---\\n\")\n",
|
||||
" current_block_type = None\n",
|
||||
" \n",
|
||||
" elif event.type == \"message_stop\":\n",
|
||||
" print(\"\\n--- Message complete ---\")\n",
|
||||
"\n",
|
||||
"streaming_with_thinking()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Token counting and context window management\n",
|
||||
"\n",
|
||||
"This example demonstrates how to track token usage with extended thinking:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Base token count (input only): 125\n",
|
||||
"\n",
|
||||
"Estimated thinking tokens used: ~377\n",
|
||||
"Estimated final answer tokens: ~237\n",
|
||||
"Total estimated output tokens: ~614\n",
|
||||
"Input tokens + max_tokens = 8125\n",
|
||||
"Available for final answer after thinking: ~7623\n",
|
||||
"\n",
|
||||
"With thinking budget of 1024 tokens:\n",
|
||||
"Input tokens: 125\n",
|
||||
"Max tokens needed: 2149\n",
|
||||
"Remaining context window: 197851\n",
|
||||
"\n",
|
||||
"With thinking budget of 2000 tokens:\n",
|
||||
"Input tokens: 125\n",
|
||||
"Max tokens needed: 3125\n",
|
||||
"Remaining context window: 196875\n",
|
||||
"\n",
|
||||
"With thinking budget of 4000 tokens:\n",
|
||||
"Input tokens: 125\n",
|
||||
"Max tokens needed: 5125\n",
|
||||
"Remaining context window: 194875\n",
|
||||
"\n",
|
||||
"With thinking budget of 8000 tokens:\n",
|
||||
"Input tokens: 125\n",
|
||||
"Max tokens needed: 9125\n",
|
||||
"Remaining context window: 190875\n",
|
||||
"\n",
|
||||
"With thinking budget of 16000 tokens:\n",
|
||||
"Input tokens: 125\n",
|
||||
"Max tokens needed: 17125\n",
|
||||
"Remaining context window: 182875\n",
|
||||
"\n",
|
||||
"With thinking budget of 32000 tokens:\n",
|
||||
"Input tokens: 125\n",
|
||||
"Max tokens needed: 33125\n",
|
||||
"Remaining context window: 166875\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def token_counting_example():\n",
|
||||
" # Define a function to create a sample prompt\n",
|
||||
" def create_sample_messages():\n",
|
||||
" messages = [{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Solve this puzzle: Three people check into a hotel. They pay $30 to the manager. The manager finds out that the room only costs $25 so he gives $5 to the bellboy to return to the three people. The bellboy, however, decides to keep $2 and gives $1 back to each person. Now, each person paid $10 and got back $1, so they paid $9 each, totaling $27. The bellboy kept $2, which makes $29. Where is the missing $1?\"\n",
|
||||
" }]\n",
|
||||
" return messages\n",
|
||||
" \n",
|
||||
" # Count tokens without thinking\n",
|
||||
" base_messages = create_sample_messages()\n",
|
||||
" base_token_count = count_tokens(base_messages)\n",
|
||||
" print(f\"Base token count (input only): {base_token_count}\")\n",
|
||||
" \n",
|
||||
" # Make a request with thinking and check actual usage\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=\"claude-3-7-sonnet-20250219\",\n",
|
||||
" max_tokens=8000,\n",
|
||||
" thinking = {\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": 2000\n",
|
||||
" },\n",
|
||||
" messages=base_messages\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Calculate and print token usage stats\n",
|
||||
" thinking_tokens = sum(\n",
|
||||
" len(block.thinking.split()) * 1.3 # Rough estimate\n",
|
||||
" for block in response.content \n",
|
||||
" if block.type == \"thinking\"\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" final_answer_tokens = sum(\n",
|
||||
" len(block.text.split()) * 1.3 # Rough estimate\n",
|
||||
" for block in response.content \n",
|
||||
" if block.type == \"text\"\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" print(f\"\\nEstimated thinking tokens used: ~{int(thinking_tokens)}\")\n",
|
||||
" print(f\"Estimated final answer tokens: ~{int(final_answer_tokens)}\")\n",
|
||||
" print(f\"Total estimated output tokens: ~{int(thinking_tokens + final_answer_tokens)}\")\n",
|
||||
" print(f\"Input tokens + max_tokens = {base_token_count + 8000}\")\n",
|
||||
" print(f\"Available for final answer after thinking: ~{8000 - int(thinking_tokens)}\")\n",
|
||||
" \n",
|
||||
" # Demo with escalating thinking budgets\n",
|
||||
" thinking_budgets = [1024, 2000, 4000, 8000, 16000, 32000]\n",
|
||||
" context_window = 200000\n",
|
||||
" for budget in thinking_budgets:\n",
|
||||
" print(f\"\\nWith thinking budget of {budget} tokens:\")\n",
|
||||
" print(f\"Input tokens: {base_token_count}\")\n",
|
||||
" print(f\"Max tokens needed: {base_token_count + budget + 1000}\") # Add 1000 for final answer\n",
|
||||
" print(f\"Remaining context window: {context_window - (base_token_count + budget + 1000)}\")\n",
|
||||
" \n",
|
||||
" if base_token_count + budget + 1000 > context_window:\n",
|
||||
" print(\"WARNING: This would exceed the context window of 200k tokens!\")\n",
|
||||
"\n",
|
||||
"# Uncomment to run the example\n",
|
||||
"token_counting_example()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Understanding redacted thinking blocks\n",
|
||||
"\n",
|
||||
"Occasionally Claude's internal reasoning will be flagged by safety systems. When this occurs, we encrypt some or all of the `thinking` block and return it to you as a `redacted_thinking` block. These redacted thinking blocks are decrypted when passed back to the API, allowing Claude to continue its response without losing context.\n",
|
||||
"\n",
|
||||
"This example demonstrates working with redacted thinking blocks using a special test string that triggers them:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[TextBlock(citations=None, text=None, type='redacted_thinking', data='EvAFCoYBGAIiQL7asmglEdeKXw4EdihR2gBQ7O7+j/dGecLjsS2PMgW9av+NRwuIV2nFD4I61hUHrp5vzJF7/y+i/vvbnxaRnwMqQMizGiLSDcowtEvP9EcIT4d75iPhZ8TaiVdD22bZp3YVcc0laY8u1lEJTSesgLUywuc3QHZcg4NZ7tKjWwKgcVUSDHgb6gZUK9aP47KvNxoMCNjkIDR40zmq/QmVIjBSCnvTMSUE+jnmLZSq1TZO9T7ImALNJt8I5j1ls24CO1fibsRThJ7Ha5A0/tuEKVoqlgRc+e2tS+BQMXx572lT4Hkl4aVpcM4SQbqBjeVeR3NmCBLoOxlQ2JLiIYwMHUS/K9GDLyMQcYd1KUWgN34CZRK7k44CSkNsO8oh4uj/1qsRsZjq1l6RQ29rLKSEXvMU4XbZufJ1icvYZS1I6PIZzER6/u6it+WNYyBxJ2vaFICjDePNgIHfRA/ceTz9mfCtBiTfagyPBbs2HflXlSlW26TSdI7PKof5/EsQ+DUkjAy+9VTLX7zHYzNZtwJPL2ryYw4loSwRbc4syldA0Ncnn7hA+yJyY0QwSrxZFIm/t9X9p9s+2SL0F4wSRsimnxRiIhfJD3i+oTw8AbGklyoP0kCH2WxA7Gr3rNLJVkRTJl48AjlSL7ClaWvLWrNer13etD7n5rbwiXOn5husy8gAm5GE3/eFyty3Y+/ad+lMPKXSjL0aP67WoJrFq/teItolOVZeOOERjVFdw5jIV1EUknlAZ/pfI53pLYqwFl17M7IXMdGxEaKoGDIKcnYTwT31uUNlB5JSBWoq1SnkFsFy2zDsDTFzjml3HEXz4szZi3j5/qHWJlMMCcB1walZUisxEp0v1euvcgatY5wfYSiAP3s9wOrgYKCkuLcidlgiyQHJB1haZjO8/tZ9gzWk1n//7pTncdKgd5ZK9/ErxWFlBV/vQwjp0cB7zoVcLh1ydi/Coea6ZOuei+ICKVl4IcR2A6DD8gtEJmc='), TextBlock(citations=None, text=\"I notice you've sent what appears to be a prompt attempting to access internal systems or processes. I can't respond to commands of this nature.\\n\\nInstead, I'm happy to have a normal conversation and assist you with legitimate questions or tasks. What would you like help with today?\", type='text')]\n",
|
||||
"Response includes 2 total blocks:\n",
|
||||
"- 1 redacted thinking blocks\n",
|
||||
"- 0 regular thinking blocks\n",
|
||||
"- 1 text blocks\n",
|
||||
"\n",
|
||||
"Redacted thinking blocks contain encrypted data:\n",
|
||||
"Block 1 data preview: EvAFCoYBGAIiQL7asmglEdeKXw4EdihR2gBQ7O7+j/dGecLjsS...\n",
|
||||
"\n",
|
||||
"Final text response:\n",
|
||||
"I notice you've sent what appears to be a prompt attempting to access internal systems or processes. I can't respond to commands of this nature.\n",
|
||||
"\n",
|
||||
"Instead, I'm happy to have a normal conversation and assist you with legitimate questions or tasks. What would you like help with today?\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def redacted_thinking_example():\n",
|
||||
" # Using the special test string that triggers redacted thinking\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=\"claude-3-7-sonnet-20250219\",\n",
|
||||
" max_tokens=4000,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": 2000\n",
|
||||
" },\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB\"\n",
|
||||
" }]\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Identify redacted thinking blocks\n",
|
||||
" redacted_blocks = [block for block in response.content if block.type == \"redacted_thinking\"]\n",
|
||||
" thinking_blocks = [block for block in response.content if block.type == \"thinking\"]\n",
|
||||
" text_blocks = [block for block in response.content if block.type == \"text\"]\n",
|
||||
" print(response.content)\n",
|
||||
" print(f\"Response includes {len(response.content)} total blocks:\")\n",
|
||||
" print(f\"- {len(redacted_blocks)} redacted thinking blocks\")\n",
|
||||
" print(f\"- {len(thinking_blocks)} regular thinking blocks\")\n",
|
||||
" print(f\"- {len(text_blocks)} text blocks\")\n",
|
||||
" \n",
|
||||
" # Show data properties of redacted blocks\n",
|
||||
" if redacted_blocks:\n",
|
||||
" print(f\"\\nRedacted thinking blocks contain encrypted data:\")\n",
|
||||
" for i, block in enumerate(redacted_blocks[:3]): # Show first 3 at most\n",
|
||||
" print(f\"Block {i+1} data preview: {block.data[:50]}...\")\n",
|
||||
" \n",
|
||||
" # Print the final text output\n",
|
||||
" if text_blocks:\n",
|
||||
" print(f\"\\nFinal text response:\")\n",
|
||||
" print(text_blocks[0].text)\n",
|
||||
"\n",
|
||||
"# Uncomment to run the example\n",
|
||||
"redacted_thinking_example()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Handling error cases\n",
|
||||
"\n",
|
||||
"When using extended thinking, keep in mind:\n",
|
||||
"\n",
|
||||
"1. **Minimum budget**: The minimum thinking budget is 1,024 tokens. We suggest starting at the minimum and increasing incrementally to find the optimal range.\n",
|
||||
"\n",
|
||||
"2. **Incompatible features**: Thinking isn't compatible with temperature, top_p, or top_k modifications, and you cannot pre-fill responses.\n",
|
||||
"\n",
|
||||
"3. **Pricing**: Extended thinking tokens count towards the context window and are billed as output tokens. They also count towards your rate limits.\n",
|
||||
"\n",
|
||||
"For more details on extended thinking with tool use, see the \"Extended Thinking with Tool Use\" notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"Error with too small thinking budget: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'thinking.enabled.budget_tokens: Input should be greater than or equal to 1024'}}\n",
|
||||
"\n",
|
||||
"Error with temperature and thinking: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': '`temperature` may only be set to 1 when thinking is enabled. Please consult our documentation at https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#important-considerations-when-using-extended-thinking'}}\n",
|
||||
"\n",
|
||||
"Error from exceeding context window: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'prompt is too long: 214315 tokens > 204798 maximum'}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def demonstrate_common_errors():\n",
|
||||
" # 1. Error from setting thinking budget too small\n",
|
||||
" try:\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=\"claude-3-7-sonnet-20250219\",\n",
|
||||
" max_tokens=4000,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": 500 # Too small, minimum is 1024\n",
|
||||
" },\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Explain quantum computing.\"\n",
|
||||
" }]\n",
|
||||
" )\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"\\nError with too small thinking budget: {e}\")\n",
|
||||
" \n",
|
||||
" # 2. Error from using temperature with thinking\n",
|
||||
" try:\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=\"claude-3-7-sonnet-20250219\",\n",
|
||||
" max_tokens=4000,\n",
|
||||
" temperature=0.7, # Not compatible with thinking\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": 2000\n",
|
||||
" },\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Write a creative story.\"\n",
|
||||
" }]\n",
|
||||
" )\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"\\nError with temperature and thinking: {e}\")\n",
|
||||
" \n",
|
||||
" # 3. Error from exceeding context window\n",
|
||||
" try:\n",
|
||||
" # Create a very large prompt\n",
|
||||
" long_content = \"Please analyze this text. \" + \"This is sample text. \" * 150000\n",
|
||||
" \n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=\"claude-3-7-sonnet-20250219\",\n",
|
||||
" max_tokens=20000, # This plus the long prompt will exceed context window\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": 10000\n",
|
||||
" },\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": long_content\n",
|
||||
" }]\n",
|
||||
" )\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"\\nError from exceeding context window: {e}\")\n",
|
||||
"\n",
|
||||
"# Run the common error examples\n",
|
||||
"demonstrate_common_errors()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Coconut",
|
||||
"language": "coconut",
|
||||
"name": "coconut"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "python",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".coco",
|
||||
"mimetype": "text/x-python3",
|
||||
"name": "coconut",
|
||||
"pygments_lexer": "coconut",
|
||||
"version": "3.0.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
862
extended_thinking/extended_thinking_with_tool_use.ipynb
Normal file
862
extended_thinking/extended_thinking_with_tool_use.ipynb
Normal file
@@ -0,0 +1,862 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Extended Thinking with Tool Use\n",
|
||||
"\n",
|
||||
"## Table of contents\n",
|
||||
"- [Setup](#setup)\n",
|
||||
"- [Basic example](#basic-example)\n",
|
||||
"- [Multiple tool calls](#multiple-tool-calls-with-thinking)\n",
|
||||
"- [Preserving thinking blocks](#preserving-thinking-blocks)\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use Claude 3.7 Sonnet's extended thinking feature with tools. The extended thinking feature allows you to see Claude's step-by-step thinking before it provides a final answer, providing transparency into how it decides which tools to use and how it interprets tool results.\n",
|
||||
"\n",
|
||||
"When using extended thinking with tool use, the model will show its thinking before making tool requests, but not repeat the thinking process after receiving tool results. Claude will not output another thinking block until after the next non-`tool_result` `user` turn. For more information on extended thinking, see our [documentation](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"First, let's install the necessary packages and set up our environment."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install anthropic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import anthropic\n",
|
||||
"import os\n",
|
||||
"import json\n",
|
||||
"\n",
|
||||
"# Global variables for model and token budgets\n",
|
||||
"MODEL_NAME = \"claude-3-7-sonnet-20250219\"\n",
|
||||
"MAX_TOKENS = 4000\n",
|
||||
"THINKING_BUDGET_TOKENS = 2000\n",
|
||||
"\n",
|
||||
"# Set your API key as an environment variable or directly\n",
|
||||
"# os.environ[\"ANTHROPIC_API_KEY\"] = \"your_api_key_here\"\n",
|
||||
"\n",
|
||||
"# Initialize the client\n",
|
||||
"client = anthropic.Anthropic()\n",
|
||||
"\n",
|
||||
"# Helper functions\n",
|
||||
"def print_thinking_response(response):\n",
|
||||
" \"\"\"Pretty print a message response with thinking blocks.\"\"\"\n",
|
||||
" print(\"\\n==== FULL RESPONSE ====\")\n",
|
||||
" for block in response.content:\n",
|
||||
" if block.type == \"thinking\":\n",
|
||||
" print(\"\\n🧠 THINKING BLOCK:\")\n",
|
||||
" # Show truncated thinking for readability \n",
|
||||
" print(block.thinking[:500] + \"...\" if len(block.thinking) > 500 else block.thinking)\n",
|
||||
" print(f\"\\n[Signature available: {bool(getattr(block, 'signature', None))}]\")\n",
|
||||
" if hasattr(block, 'signature') and block.signature:\n",
|
||||
" print(f\"[Signature (first 50 chars): {block.signature[:50]}...]\")\n",
|
||||
" elif block.type == \"redacted_thinking\":\n",
|
||||
" print(\"\\n🔒 REDACTED THINKING BLOCK:\")\n",
|
||||
" print(f\"[Data length: {len(block.data) if hasattr(block, 'data') else 'N/A'}]\")\n",
|
||||
" elif block.type == \"text\":\n",
|
||||
" print(\"\\n✓ FINAL ANSWER:\")\n",
|
||||
" print(block.text)\n",
|
||||
" \n",
|
||||
" print(\"\\n==== END RESPONSE ====\")\n",
|
||||
"\n",
|
||||
"def count_tokens(messages, tools=None):\n",
|
||||
" \"\"\"Count tokens for a given message list with optional tools.\"\"\"\n",
|
||||
" if tools:\n",
|
||||
" response = client.messages.count_tokens(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" messages=messages,\n",
|
||||
" tools=tools\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" response = client.messages.count_tokens(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" messages=messages\n",
|
||||
" )\n",
|
||||
" return response.input_tokens"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Single tool calls with thinking\n",
|
||||
"\n",
|
||||
"This example demonstrates how to combine thinking and make a single tool call, with a mock weather tool."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"=== INITIAL RESPONSE ===\n",
|
||||
"Response ID: msg_01NhR4vE9nVh2sHs5fXbzji8\n",
|
||||
"Stop reason: tool_use\n",
|
||||
"Model: claude-3-7-sonnet-20250219\n",
|
||||
"Content blocks: 3 blocks\n",
|
||||
"\n",
|
||||
"Block 1: Type = thinking\n",
|
||||
"Thinking content: The user is asking about the current weather in Paris. I can use the `weather` function to get this information.\n",
|
||||
"\n",
|
||||
"The `weather` function requires a \"l...\n",
|
||||
"Signature available: True\n",
|
||||
"\n",
|
||||
"Block 2: Type = text\n",
|
||||
"Text content: I'll check the current weather in Paris for you.\n",
|
||||
"\n",
|
||||
"Block 3: Type = tool_use\n",
|
||||
"Tool: weather\n",
|
||||
"Tool input: {'location': 'Paris'}\n",
|
||||
"Tool ID: toolu_01WaeSyitUGJFaaPe68cJuEv\n",
|
||||
"=== END INITIAL RESPONSE ===\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"=== EXECUTING TOOL ===\n",
|
||||
"Tool name: weather\n",
|
||||
"Location to check: Paris\n",
|
||||
"Result: {'temperature': 65, 'condition': 'Rainy'}\n",
|
||||
"=== TOOL EXECUTION COMPLETE ===\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"=== SENDING FOLLOW-UP REQUEST WITH TOOL RESULT ===\n",
|
||||
"Follow-up response received. Stop reason: end_turn\n",
|
||||
"\n",
|
||||
"==== FULL RESPONSE ====\n",
|
||||
"\n",
|
||||
"✓ FINAL ANSWER:\n",
|
||||
"Currently in Paris, it's 65°F (18°C) and rainy. You might want to bring an umbrella if you're heading out!\n",
|
||||
"\n",
|
||||
"==== END RESPONSE ====\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def tool_use_with_thinking():\n",
|
||||
" # Define a weather tool\n",
|
||||
" tools = [\n",
|
||||
" {\n",
|
||||
" \"name\": \"weather\",\n",
|
||||
" \"description\": \"Get current weather information for a location.\",\n",
|
||||
" \"input_schema\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"location\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The location to get weather for.\"\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\"location\"]\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" \n",
|
||||
" def weather(location):\n",
|
||||
" # Mock weather data\n",
|
||||
" weather_data = {\n",
|
||||
" \"New York\": {\"temperature\": 72, \"condition\": \"Sunny\"},\n",
|
||||
" \"London\": {\"temperature\": 62, \"condition\": \"Cloudy\"},\n",
|
||||
" \"Tokyo\": {\"temperature\": 80, \"condition\": \"Partly cloudy\"},\n",
|
||||
" \"Paris\": {\"temperature\": 65, \"condition\": \"Rainy\"},\n",
|
||||
" \"Sydney\": {\"temperature\": 85, \"condition\": \"Clear\"},\n",
|
||||
" \"Berlin\": {\"temperature\": 60, \"condition\": \"Foggy\"},\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" return weather_data.get(location, {\"error\": f\"No weather data available for {location}\"})\n",
|
||||
" \n",
|
||||
" # Initial request with tool use and thinking\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" max_tokens=MAX_TOKENS,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": THINKING_BUDGET_TOKENS\n",
|
||||
" },\n",
|
||||
" tools=tools,\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"What's the weather like in Paris today?\"\n",
|
||||
" }]\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Detailed diagnostic output of initial response\n",
|
||||
" print(\"\\n=== INITIAL RESPONSE ===\")\n",
|
||||
" print(f\"Response ID: {response.id}\")\n",
|
||||
" print(f\"Stop reason: {response.stop_reason}\")\n",
|
||||
" print(f\"Model: {response.model}\")\n",
|
||||
" print(f\"Content blocks: {len(response.content)} blocks\")\n",
|
||||
" \n",
|
||||
" for i, block in enumerate(response.content):\n",
|
||||
" print(f\"\\nBlock {i+1}: Type = {block.type}\")\n",
|
||||
" if block.type == \"thinking\":\n",
|
||||
" print(f\"Thinking content: {block.thinking[:150]}...\")\n",
|
||||
" print(f\"Signature available: {bool(getattr(block, 'signature', None))}\")\n",
|
||||
" elif block.type == \"text\":\n",
|
||||
" print(f\"Text content: {block.text}\")\n",
|
||||
" elif block.type == \"tool_use\":\n",
|
||||
" print(f\"Tool: {block.name}\")\n",
|
||||
" print(f\"Tool input: {block.input}\")\n",
|
||||
" print(f\"Tool ID: {block.id}\")\n",
|
||||
" print(\"=== END INITIAL RESPONSE ===\\n\")\n",
|
||||
" \n",
|
||||
" # Extract thinking blocks to include in the conversation history\n",
|
||||
" assistant_blocks = []\n",
|
||||
" for block in response.content:\n",
|
||||
" if block.type in [\"thinking\", \"redacted_thinking\", \"tool_use\"]:\n",
|
||||
" assistant_blocks.append(block)\n",
|
||||
" \n",
|
||||
" # Handle tool use if required\n",
|
||||
" full_conversation = [{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"What's the weather like in Paris today?\"\n",
|
||||
" }]\n",
|
||||
" \n",
|
||||
" if response.stop_reason == \"tool_use\":\n",
|
||||
" # Add entire assistant response with thinking blocks and tool use\n",
|
||||
" full_conversation.append({\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": assistant_blocks\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" # Find the tool_use block\n",
|
||||
" tool_use_block = next((block for block in response.content if block.type == \"tool_use\"), None)\n",
|
||||
" if tool_use_block:\n",
|
||||
" # Execute the tool\n",
|
||||
" print(f\"\\n=== EXECUTING TOOL ===\")\n",
|
||||
" print(f\"Tool name: {tool_use_block.name}\")\n",
|
||||
" print(f\"Location to check: {tool_use_block.input['location']}\")\n",
|
||||
" tool_result = weather(tool_use_block.input[\"location\"])\n",
|
||||
" print(f\"Result: {tool_result}\")\n",
|
||||
" print(\"=== TOOL EXECUTION COMPLETE ===\\n\")\n",
|
||||
" \n",
|
||||
" # Add tool result to conversation\n",
|
||||
" full_conversation.append({\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": [{\n",
|
||||
" \"type\": \"tool_result\",\n",
|
||||
" \"tool_use_id\": tool_use_block.id,\n",
|
||||
" \"content\": json.dumps(tool_result)\n",
|
||||
" }]\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" # Continue the conversation with the same thinking configuration\n",
|
||||
" print(\"\\n=== SENDING FOLLOW-UP REQUEST WITH TOOL RESULT ===\")\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" max_tokens=MAX_TOKENS,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": THINKING_BUDGET_TOKENS\n",
|
||||
" },\n",
|
||||
" tools=tools,\n",
|
||||
" messages=full_conversation\n",
|
||||
" )\n",
|
||||
" print(f\"Follow-up response received. Stop reason: {response.stop_reason}\")\n",
|
||||
" \n",
|
||||
" print_thinking_response(response)\n",
|
||||
"\n",
|
||||
"# Run the example\n",
|
||||
"tool_use_with_thinking()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Multiple tool calls with thinking\n",
|
||||
"\n",
|
||||
"This example demonstrates how to handle multiple tool calls, such as a mock news and weather service, while observing the thinking process."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"=== INITIAL RESPONSE ===\n",
|
||||
"Response ID: msg_01VwqpBMARVoTP1H8Ytvmvsb\n",
|
||||
"Stop reason: tool_use\n",
|
||||
"Model: claude-3-7-sonnet-20250219\n",
|
||||
"Content blocks: 3 blocks\n",
|
||||
"\n",
|
||||
"Block 1: Type = thinking\n",
|
||||
"Thinking content: The user is asking for two pieces of information:\n",
|
||||
"1. The weather in London\n",
|
||||
"2. The latest news about technology\n",
|
||||
"\n",
|
||||
"Let me check what tools I have availab...\n",
|
||||
"Signature available: True\n",
|
||||
"\n",
|
||||
"Block 2: Type = text\n",
|
||||
"Text content: I'll get that information for you right away.\n",
|
||||
"\n",
|
||||
"Block 3: Type = tool_use\n",
|
||||
"Tool: weather\n",
|
||||
"Tool input: {'location': 'London'}\n",
|
||||
"Tool ID: toolu_016xHQWMR4JsKtWvH9nbsZyA\n",
|
||||
"=== END INITIAL RESPONSE ===\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"=== TOOL USE ITERATION 1 ===\n",
|
||||
"\n",
|
||||
"=== EXECUTING TOOL ===\n",
|
||||
"Tool name: weather\n",
|
||||
"Location to check: London\n",
|
||||
"Result: {'temperature': 62, 'condition': 'Cloudy'}\n",
|
||||
"=== TOOL EXECUTION COMPLETE ===\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"=== SENDING FOLLOW-UP REQUEST WITH TOOL RESULT ===\n",
|
||||
"\n",
|
||||
"=== FOLLOW-UP RESPONSE (ITERATION 1) ===\n",
|
||||
"Response ID: msg_01EhR96Z2Z2t5EDhuWeodUod\n",
|
||||
"Stop reason: tool_use\n",
|
||||
"Content blocks: 1 blocks\n",
|
||||
"\n",
|
||||
"Block 1: Type = tool_use\n",
|
||||
"Tool: news\n",
|
||||
"Tool input preview: {'topic': 'technology'}\n",
|
||||
"=== END FOLLOW-UP RESPONSE (ITERATION 1) ===\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"=== TOOL USE ITERATION 2 ===\n",
|
||||
"\n",
|
||||
"=== EXECUTING TOOL ===\n",
|
||||
"Tool name: news\n",
|
||||
"Topic to check: technology\n",
|
||||
"Result: {'headlines': ['New AI breakthrough announced by research lab', 'Tech company releases latest smartphone model', 'Quantum computing reaches milestone achievement']}\n",
|
||||
"=== TOOL EXECUTION COMPLETE ===\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"=== SENDING FOLLOW-UP REQUEST WITH TOOL RESULT ===\n",
|
||||
"\n",
|
||||
"=== FOLLOW-UP RESPONSE (ITERATION 2) ===\n",
|
||||
"Response ID: msg_01WUEfC4UxPFaJaktjVDMJEN\n",
|
||||
"Stop reason: end_turn\n",
|
||||
"Content blocks: 1 blocks\n",
|
||||
"\n",
|
||||
"Block 1: Type = text\n",
|
||||
"Text content preview: Here's the information you requested:\n",
|
||||
"\n",
|
||||
"## Weather in London\n",
|
||||
"Currently, it's 62°F and cloudy in Londo...\n",
|
||||
"=== END FOLLOW-UP RESPONSE (ITERATION 2) ===\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"=== FINAL RESPONSE ===\n",
|
||||
"\n",
|
||||
"==== FULL RESPONSE ====\n",
|
||||
"\n",
|
||||
"✓ FINAL ANSWER:\n",
|
||||
"Here's the information you requested:\n",
|
||||
"\n",
|
||||
"## Weather in London\n",
|
||||
"Currently, it's 62°F and cloudy in London.\n",
|
||||
"\n",
|
||||
"## Latest Technology News Headlines\n",
|
||||
"- New AI breakthrough announced by research lab\n",
|
||||
"- Tech company releases latest smartphone model\n",
|
||||
"- Quantum computing reaches milestone achievement\n",
|
||||
"\n",
|
||||
"==== END RESPONSE ====\n",
|
||||
"=== END FINAL RESPONSE ===\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def multiple_tool_calls_with_thinking():\n",
|
||||
" # Define tools\n",
|
||||
" tools = [\n",
|
||||
" {\n",
|
||||
" \"name\": \"weather\",\n",
|
||||
" \"description\": \"Get current weather information for a location.\",\n",
|
||||
" \"input_schema\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"location\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The location to get weather for.\"\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\"location\"]\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"name\": \"news\",\n",
|
||||
" \"description\": \"Get latest news headlines for a topic.\",\n",
|
||||
" \"input_schema\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"topic\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The topic to get news about.\"\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\"topic\"]\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" \n",
|
||||
" def weather(location):\n",
|
||||
" # Mock weather data\n",
|
||||
" weather_data = {\n",
|
||||
" \"New York\": {\"temperature\": 72, \"condition\": \"Sunny\"},\n",
|
||||
" \"London\": {\"temperature\": 62, \"condition\": \"Cloudy\"},\n",
|
||||
" \"Tokyo\": {\"temperature\": 80, \"condition\": \"Partly cloudy\"},\n",
|
||||
" \"Paris\": {\"temperature\": 65, \"condition\": \"Rainy\"},\n",
|
||||
" \"Sydney\": {\"temperature\": 85, \"condition\": \"Clear\"},\n",
|
||||
" \"Berlin\": {\"temperature\": 60, \"condition\": \"Foggy\"},\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" return weather_data.get(location, {\"error\": f\"No weather data available for {location}\"})\n",
|
||||
" \n",
|
||||
" def news(topic):\n",
|
||||
" # Mock news data\n",
|
||||
" news_data = {\n",
|
||||
" \"technology\": [\n",
|
||||
" \"New AI breakthrough announced by research lab\",\n",
|
||||
" \"Tech company releases latest smartphone model\",\n",
|
||||
" \"Quantum computing reaches milestone achievement\"\n",
|
||||
" ],\n",
|
||||
" \"sports\": [\n",
|
||||
" \"Local team wins championship game\",\n",
|
||||
" \"Star player signs record-breaking contract\",\n",
|
||||
" \"Olympic committee announces host city for 2036\"\n",
|
||||
" ],\n",
|
||||
" \"weather\": [\n",
|
||||
" \"Storm system developing in the Atlantic\",\n",
|
||||
" \"Record temperatures recorded across Europe\",\n",
|
||||
" \"Climate scientists release new research findings\"\n",
|
||||
" ]\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" return {\"headlines\": news_data.get(topic.lower(), [\"No news available for this topic\"])}\n",
|
||||
" \n",
|
||||
" # Initial request\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" max_tokens=MAX_TOKENS,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": THINKING_BUDGET_TOKENS\n",
|
||||
" },\n",
|
||||
" tools=tools,\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"What's the weather in London, and can you also tell me the latest news about technology?\"\n",
|
||||
" }]\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Print detailed information about initial response\n",
|
||||
" print(\"\\n=== INITIAL RESPONSE ===\")\n",
|
||||
" print(f\"Response ID: {response.id}\")\n",
|
||||
" print(f\"Stop reason: {response.stop_reason}\")\n",
|
||||
" print(f\"Model: {response.model}\")\n",
|
||||
" print(f\"Content blocks: {len(response.content)} blocks\")\n",
|
||||
" \n",
|
||||
" # Print each content block\n",
|
||||
" for i, block in enumerate(response.content):\n",
|
||||
" print(f\"\\nBlock {i+1}: Type = {block.type}\")\n",
|
||||
" if block.type == \"thinking\":\n",
|
||||
" print(f\"Thinking content: {block.thinking[:150]}...\")\n",
|
||||
" print(f\"Signature available: {bool(getattr(block, 'signature', None))}\")\n",
|
||||
" elif block.type == \"text\":\n",
|
||||
" print(f\"Text content: {block.text}\")\n",
|
||||
" elif block.type == \"tool_use\":\n",
|
||||
" print(f\"Tool: {block.name}\")\n",
|
||||
" print(f\"Tool input: {block.input}\")\n",
|
||||
" print(f\"Tool ID: {block.id}\")\n",
|
||||
" print(\"=== END INITIAL RESPONSE ===\\n\")\n",
|
||||
" \n",
|
||||
" # Handle potentially multiple tool calls\n",
|
||||
" full_conversation = [{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"What's the weather in London, and can you also tell me the latest news about technology?\"\n",
|
||||
" }]\n",
|
||||
" \n",
|
||||
" # Track iteration count for multi-turn tool use\n",
|
||||
" iteration = 0\n",
|
||||
" \n",
|
||||
" while response.stop_reason == \"tool_use\":\n",
|
||||
" iteration += 1\n",
|
||||
" print(f\"\\n=== TOOL USE ITERATION {iteration} ===\")\n",
|
||||
" \n",
|
||||
" # Extract thinking blocks and tool use to include in conversation history\n",
|
||||
" assistant_blocks = []\n",
|
||||
" for block in response.content:\n",
|
||||
" if block.type in [\"thinking\", \"redacted_thinking\", \"tool_use\"]:\n",
|
||||
" assistant_blocks.append(block)\n",
|
||||
" \n",
|
||||
" # Add assistant response with thinking blocks and tool use\n",
|
||||
" full_conversation.append({\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": assistant_blocks\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" # Find the tool_use block\n",
|
||||
" tool_use_block = next((block for block in response.content if block.type == \"tool_use\"), None)\n",
|
||||
" if tool_use_block:\n",
|
||||
" print(f\"\\n=== EXECUTING TOOL ===\")\n",
|
||||
" print(f\"Tool name: {tool_use_block.name}\")\n",
|
||||
" \n",
|
||||
" # Execute the appropriate tool\n",
|
||||
" if tool_use_block.name == \"weather\":\n",
|
||||
" print(f\"Location to check: {tool_use_block.input['location']}\")\n",
|
||||
" tool_result = weather(tool_use_block.input[\"location\"])\n",
|
||||
" elif tool_use_block.name == \"news\":\n",
|
||||
" print(f\"Topic to check: {tool_use_block.input['topic']}\")\n",
|
||||
" tool_result = news(tool_use_block.input[\"topic\"])\n",
|
||||
" else:\n",
|
||||
" tool_result = {\"error\": \"Unknown tool\"}\n",
|
||||
" \n",
|
||||
" print(f\"Result: {tool_result}\")\n",
|
||||
" print(\"=== TOOL EXECUTION COMPLETE ===\\n\")\n",
|
||||
" \n",
|
||||
" # Add tool result to conversation\n",
|
||||
" full_conversation.append({\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": [{\n",
|
||||
" \"type\": \"tool_result\",\n",
|
||||
" \"tool_use_id\": tool_use_block.id,\n",
|
||||
" \"content\": json.dumps(tool_result)\n",
|
||||
" }]\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" # Continue the conversation\n",
|
||||
" print(\"\\n=== SENDING FOLLOW-UP REQUEST WITH TOOL RESULT ===\")\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" max_tokens=MAX_TOKENS,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": THINKING_BUDGET_TOKENS\n",
|
||||
" },\n",
|
||||
" tools=tools,\n",
|
||||
" messages=full_conversation\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Print follow-up response details\n",
|
||||
" print(f\"\\n=== FOLLOW-UP RESPONSE (ITERATION {iteration}) ===\")\n",
|
||||
" print(f\"Response ID: {response.id}\")\n",
|
||||
" print(f\"Stop reason: {response.stop_reason}\")\n",
|
||||
" print(f\"Content blocks: {len(response.content)} blocks\")\n",
|
||||
" \n",
|
||||
" for i, block in enumerate(response.content):\n",
|
||||
" print(f\"\\nBlock {i+1}: Type = {block.type}\")\n",
|
||||
" if block.type == \"thinking\":\n",
|
||||
" print(f\"Thinking content preview: {block.thinking[:100]}...\")\n",
|
||||
" elif block.type == \"text\":\n",
|
||||
" print(f\"Text content preview: {block.text[:100]}...\")\n",
|
||||
" elif block.type == \"tool_use\":\n",
|
||||
" print(f\"Tool: {block.name}\")\n",
|
||||
" print(f\"Tool input preview: {str(block.input)[:100]}\")\n",
|
||||
" print(f\"=== END FOLLOW-UP RESPONSE (ITERATION {iteration}) ===\\n\")\n",
|
||||
" \n",
|
||||
" if response.stop_reason != \"tool_use\":\n",
|
||||
" print(\"\\n=== FINAL RESPONSE ===\")\n",
|
||||
" print_thinking_response(response)\n",
|
||||
" print(\"=== END FINAL RESPONSE ===\")\n",
|
||||
" else:\n",
|
||||
" print(\"No tool_use block found in response.\")\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
"# Run the example\n",
|
||||
"multiple_tool_calls_with_thinking()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "bat"
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Preserving thinking blocks\n",
|
||||
"\n",
|
||||
"When working with extended thinking and tools, make sure to:\n",
|
||||
"\n",
|
||||
"1. **Preserve thinking block signatures**: Each thinking block contains a cryptographic signature that validates the conversation context. These signatures must be included when passing thinking blocks back to Claude.\n",
|
||||
"\n",
|
||||
"2. **Avoid modifying prior context**: The API will reject requests if any previous content (including thinking blocks) is modified when submitting a new request with tool results.\n",
|
||||
"\n",
|
||||
"3. **Handle both thinking and redacted_thinking blocks**: Both types of blocks must be preserved in the conversation history, even if the content of redacted blocks is not human readable.\n",
|
||||
"\n",
|
||||
"For more details on extended thinking without tools, see the main \"Extended Thinking\" notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "python"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"=== INITIAL RESPONSE ===\n",
|
||||
"Response contains:\n",
|
||||
"- 1 thinking blocks\n",
|
||||
"- 1 tool use blocks\n",
|
||||
"\n",
|
||||
"Tool called: weather\n",
|
||||
"Location to check: Berlin\n",
|
||||
"Tool result: {'temperature': 60, 'condition': 'Foggy'}\n",
|
||||
"\n",
|
||||
"=== TEST 1: WITHOUT thinking block ===\n",
|
||||
"ERROR: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'messages.1.content.0.type: Expected `thinking` or `redacted_thinking`, but found `tool_use`. When `thinking` is enabled, a final `assistant` message must start with a thinking block (preceeding the lastmost set of `tool_use` and `tool_result` blocks). We recommend you include thinking blocks from previous turns. To avoid this requirement, disable `thinking`. Please consult our documentation at https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking'}}\n",
|
||||
"This demonstrates that thinking blocks must be preserved\n",
|
||||
"\n",
|
||||
"=== TEST 2: WITH thinking block (correct approach) ===\n",
|
||||
"SUCCESS: Response received with thinking blocks included\n",
|
||||
"\n",
|
||||
"Second response contains:\n",
|
||||
"- 0 thinking blocks\n",
|
||||
"- 1 text blocks\n",
|
||||
"\n",
|
||||
"Final answer: Currently in Berlin, it's foggy with a temperature of 60°F (about 15.5°C).\n",
|
||||
"\n",
|
||||
"Note: The second response after tool use doesn't contain thinking blocks.\n",
|
||||
"This is expected behavior - thinking is shown before tool use but not after receiving tool results.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def thinking_block_preservation_example():\n",
|
||||
" # Define a simple weather tool\n",
|
||||
" tools = [\n",
|
||||
" {\n",
|
||||
" \"name\": \"weather\",\n",
|
||||
" \"description\": \"Get current weather information for a location.\",\n",
|
||||
" \"input_schema\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"location\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The location to get weather for.\"\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\"location\"]\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" \n",
|
||||
" def weather(location):\n",
|
||||
" # Mock weather data\n",
|
||||
" weather_data = {\n",
|
||||
" \"New York\": {\"temperature\": 72, \"condition\": \"Sunny\"},\n",
|
||||
" \"London\": {\"temperature\": 62, \"condition\": \"Cloudy\"},\n",
|
||||
" \"Tokyo\": {\"temperature\": 80, \"condition\": \"Partly cloudy\"},\n",
|
||||
" \"Paris\": {\"temperature\": 65, \"condition\": \"Rainy\"},\n",
|
||||
" \"Sydney\": {\"temperature\": 85, \"condition\": \"Clear\"},\n",
|
||||
" \"Berlin\": {\"temperature\": 60, \"condition\": \"Foggy\"},\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" return weather_data.get(location, {\"error\": f\"No weather data available for {location}\"})\n",
|
||||
" \n",
|
||||
" # Initial request with tool use and thinking\n",
|
||||
" response = client.messages.create(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" max_tokens=MAX_TOKENS,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": THINKING_BUDGET_TOKENS\n",
|
||||
" },\n",
|
||||
" tools=tools,\n",
|
||||
" messages=[{\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"What's the weather like in Berlin right now?\"\n",
|
||||
" }]\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Extract blocks from response\n",
|
||||
" thinking_blocks = [b for b in response.content if b.type == \"thinking\"]\n",
|
||||
" tool_use_blocks = [b for b in response.content if b.type == \"tool_use\"]\n",
|
||||
" \n",
|
||||
" print(\"\\n=== INITIAL RESPONSE ===\")\n",
|
||||
" print(f\"Response contains:\")\n",
|
||||
" print(f\"- {len(thinking_blocks)} thinking blocks\")\n",
|
||||
" print(f\"- {len(tool_use_blocks)} tool use blocks\")\n",
|
||||
" \n",
|
||||
" # Check if tool use was triggered\n",
|
||||
" if tool_use_blocks:\n",
|
||||
" tool_block = tool_use_blocks[0]\n",
|
||||
" print(f\"\\nTool called: {tool_block.name}\")\n",
|
||||
" print(f\"Location to check: {tool_block.input['location']}\")\n",
|
||||
" \n",
|
||||
" # Execute the tool\n",
|
||||
" tool_result = weather(tool_block.input[\"location\"])\n",
|
||||
" print(f\"Tool result: {tool_result}\")\n",
|
||||
" \n",
|
||||
" # First, let's try WITHOUT including the thinking block\n",
|
||||
" print(\"\\n=== TEST 1: WITHOUT thinking block ===\")\n",
|
||||
" try:\n",
|
||||
" # Notice we're only including the tool_use block, not the thinking block\n",
|
||||
" partial_blocks = tool_use_blocks\n",
|
||||
" \n",
|
||||
" incomplete_response = client.messages.create(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" max_tokens=MAX_TOKENS,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": THINKING_BUDGET_TOKENS\n",
|
||||
" },\n",
|
||||
" tools=tools,\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"user\", \"content\": \"What's the weather like in Berlin right now?\"},\n",
|
||||
" {\"role\": \"assistant\", \"content\": partial_blocks},\n",
|
||||
" {\"role\": \"user\", \"content\": [{\n",
|
||||
" \"type\": \"tool_result\",\n",
|
||||
" \"tool_use_id\": tool_block.id,\n",
|
||||
" \"content\": json.dumps(tool_result)\n",
|
||||
" }]}\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
" print(\"SUCCESS: Response received without thinking block (not expected)\")\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"ERROR: {e}\")\n",
|
||||
" print(\"This demonstrates that thinking blocks must be preserved\")\n",
|
||||
" \n",
|
||||
" # Now try WITH the thinking block included (correct approach)\n",
|
||||
" print(\"\\n=== TEST 2: WITH thinking block (correct approach) ===\")\n",
|
||||
" try:\n",
|
||||
" # Include all blocks from the response\n",
|
||||
" complete_blocks = thinking_blocks + tool_use_blocks\n",
|
||||
" \n",
|
||||
" complete_response = client.messages.create(\n",
|
||||
" model=MODEL_NAME,\n",
|
||||
" max_tokens=MAX_TOKENS,\n",
|
||||
" thinking={\n",
|
||||
" \"type\": \"enabled\",\n",
|
||||
" \"budget_tokens\": THINKING_BUDGET_TOKENS\n",
|
||||
" },\n",
|
||||
" tools=tools,\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"user\", \"content\": \"What's the weather like in Berlin right now?\"},\n",
|
||||
" {\"role\": \"assistant\", \"content\": complete_blocks},\n",
|
||||
" {\"role\": \"user\", \"content\": [{\n",
|
||||
" \"type\": \"tool_result\",\n",
|
||||
" \"tool_use_id\": tool_block.id,\n",
|
||||
" \"content\": json.dumps(tool_result)\n",
|
||||
" }]}\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
" print(\"SUCCESS: Response received with thinking blocks included\")\n",
|
||||
" \n",
|
||||
" # Check if second response has thinking blocks\n",
|
||||
" second_thinking = [b for b in complete_response.content if b.type == \"thinking\"]\n",
|
||||
" second_text = [b for b in complete_response.content if b.type == \"text\"]\n",
|
||||
" \n",
|
||||
" print(f\"\\nSecond response contains:\")\n",
|
||||
" print(f\"- {len(second_thinking)} thinking blocks\")\n",
|
||||
" print(f\"- {len(second_text)} text blocks\")\n",
|
||||
" \n",
|
||||
" if second_text:\n",
|
||||
" print(f\"\\nFinal answer: {second_text[0].text}\")\n",
|
||||
" \n",
|
||||
" print(\"\\nNote: The second response after tool use doesn't contain thinking blocks.\")\n",
|
||||
" print(\"This is expected behavior - thinking is shown before tool use but not after receiving tool results.\")\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"ERROR: {e}\")\n",
|
||||
" \n",
|
||||
"# Uncomment to run the example\n",
|
||||
"thinking_block_preservation_example()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Conclusion\n",
|
||||
"\n",
|
||||
"This notebook shows how to combine Claude's extended thinking feature with tool use. Key benefits include:\n",
|
||||
"\n",
|
||||
"1. Transparency into Claude's thinking process when using tools\n",
|
||||
"2. Visibility into how Claude decides when to use tools versus internal knowledge\n",
|
||||
"3. Better understanding of multi-step tasks that involve multiple tool calls\n",
|
||||
"4. Insight into how Claude interprets tool results and incorporates them into responses\n",
|
||||
"\n",
|
||||
"When using extended thinking with tools, keep in mind:\n",
|
||||
"- Set appropriate thinking budgets for complex tasks (minimum 1,024 tokens)\n",
|
||||
"- Always preserve thinking blocks and their signatures when passing tool results\n",
|
||||
"- Include both normal and redacted thinking blocks in the conversation history\n",
|
||||
"- Ensure that system prompts, tools, and thinking configurations match between calls\n",
|
||||
"- Expect that tool result turns will not contain additional thinking blocks\n",
|
||||
"- Tool use and thinking together can increase token usage and response time"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Coconut",
|
||||
"language": "coconut",
|
||||
"name": "coconut"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "python",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".coco",
|
||||
"mimetype": "text/x-python3",
|
||||
"name": "coconut",
|
||||
"pygments_lexer": "coconut",
|
||||
"version": "3.0.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
Reference in New Issue
Block a user