From 26297d683d705d1c98ab13fcdaf843949201e864 Mon Sep 17 00:00:00 2001 From: Jeff Zeng Date: Mon, 24 Nov 2025 16:31:12 -0500 Subject: [PATCH] tool-search-tool --- .../getting_started_tool_search_tool.ipynb | 801 ++++++++++++++++++ 1 file changed, 801 insertions(+) create mode 100644 claude_4_5_tool_search_tool/getting_started_tool_search_tool.ipynb diff --git a/claude_4_5_tool_search_tool/getting_started_tool_search_tool.ipynb b/claude_4_5_tool_search_tool/getting_started_tool_search_tool.ipynb new file mode 100644 index 0000000..4480534 --- /dev/null +++ b/claude_4_5_tool_search_tool/getting_started_tool_search_tool.ipynb @@ -0,0 +1,801 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "intro-overview", + "metadata": {}, + "source": [ + "# Getting Started with the Tool Search Tool in Amazon Bedrock\n", + "\n", + "This notebook introduces the **Tool Search Tool (TST)**, a new advanced tool use capability that enables Claude to work with hundreds or even thousands of tools without loading all their definitions into the context window upfront.\n", + "\n", + "## The Problem\n", + "\n", + "When building AI systems with many tools, you face a trade-off:\n", + "- **Load all tools upfront**: Consumes significant context window space (potentially 40-80K tokens)\n", + "- **Limit your tools**: Restricts the capabilities of your AI system\n", + "\n", + "## The Solution: Tool Search Tool\n", + "\n", + "Instead of declaring all tools immediately, you mark them with `defer_loading: true`, and Claude discovers and loads only the tools it needs through the tool search mechanism. This improves both context efficiency and tool selection accuracy.\n", + "\n", + "### Key Benefits\n", + "\n", + "- **Scale to massive tool libraries**: Build AI systems with 200+ tools without consuming context window\n", + "- **Maintain tool selection accuracy**: Only 3-5 most relevant tools shown to Claude at a time, preventing schema confusion\n", + "- **Context window optimization**: Save 40-80K tokens that would otherwise be spent on tool definitions\n", + "\n", + "### Important Notes\n", + "\n", + "> **Beta Header**: This feature is gated behind the beta header `tool-search-tool-2025-10-19`\n", + "\n", + "> **API Availability**: At launch, this feature is only available via **Invoke APIs** (as demonstrated in this notebook). Converse APIs will be supported as a fast follow.\n", + "\n", + "By the end of this notebook, you'll understand how to:\n", + "1. Configure tools with deferred loading\n", + "2. Use the Tool Search Tool to discover relevant tools\n", + "3. Handle the response structure including `server_tool_use` and `tool_reference` blocks\n", + "4. Avoid common errors when using this feature" + ] + }, + { + "cell_type": "markdown", + "id": "how-it-works", + "metadata": {}, + "source": [ + "## How Tool Search Tool Works\n", + "\n", + "The Tool Search Tool introduces a new pattern for tool discovery:\n", + "\n", + "### Traditional Approach\n", + "```\n", + "Request: [All tool definitions loaded] + User message\n", + " ↓\n", + "Claude selects and uses appropriate tool\n", + "```\n", + "\n", + "### With Tool Search Tool\n", + "```\n", + "Request: [Tool Search Tool] + [Deferred tool definitions] + User message\n", + " ↓\n", + "Claude invokes Tool Search Tool with a query\n", + " ↓\n", + "Server returns matching tool_reference(s)\n", + " ↓\n", + "Claude uses the discovered tool\n", + "```\n", + "\n", + "### Key Concepts\n", + "\n", + "1. **`defer_loading: true`**: Mark tools that should not be loaded into context upfront\n", + "2. **`tool_search_tool_regex`**: A server-side tool that searches through deferred tools\n", + "3. **`server_tool_use`**: A content block type indicating Claude is using a server-side tool\n", + "4. **`tool_reference`**: The result type that references a discovered tool by name\n", + "\n", + "The server handles the tool search automatically - Claude queries it, receives matching tool references, and then can use those tools normally." + ] + }, + { + "cell_type": "markdown", + "id": "setup-header", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "Let's install the required dependencies and configure our Bedrock client." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "install-deps", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install boto3 botocore -qU --disable-pip-version-check" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "imports-setup", + "metadata": {}, + "outputs": [], + "source": [ + "import boto3\n", + "import json\n", + "from botocore.config import Config\n", + "\n", + "# Configure AWS region\n", + "REGION = 'us-west-2' # Change to your preferred region\n", + "\n", + "# Initialize Bedrock runtime client\n", + "bedrock_runtime = boto3.client(\n", + " service_name='bedrock-runtime',\n", + " region_name=REGION,\n", + " config=Config(read_timeout=300)\n", + ")\n", + "\n", + "# Model configuration\n", + "MODEL_ID = 'global.anthropic.claude-opus-4-5-20251101-v1:0'\n", + "\n", + "# Beta header for Tool Search Tool\n", + "TOOL_SEARCH_BETA_HEADER = 'tool-search-tool-2025-10-19'\n", + "\n", + "print(f\"Region: {REGION}\")\n", + "print(f\"Model ID: {MODEL_ID}\")\n", + "print(f\"Beta Header: {TOOL_SEARCH_BETA_HEADER}\")" + ] + }, + { + "cell_type": "markdown", + "id": "utility-functions-header", + "metadata": {}, + "source": [ + "## Utility Functions\n", + "\n", + "Let's create helper functions to invoke Claude with the Tool Search Tool and display the responses clearly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "utility-functions", + "metadata": {}, + "outputs": [], + "source": [ + "def invoke_with_tool_search(messages, tools, max_tokens=4096):\n", + " \"\"\"\n", + " Invoke Claude with Tool Search Tool enabled.\n", + " \n", + " Args:\n", + " messages: List of message objects\n", + " tools: List of tool definitions (including tool_search_tool_regex and deferred tools)\n", + " max_tokens: Maximum tokens for response\n", + " \n", + " Returns:\n", + " dict: The API response\n", + " \"\"\"\n", + " request_body = {\n", + " \"anthropic_version\": \"bedrock-2023-05-31\",\n", + " \"anthropic_beta\": [TOOL_SEARCH_BETA_HEADER],\n", + " \"max_tokens\": max_tokens,\n", + " \"tools\": tools,\n", + " \"messages\": messages\n", + " }\n", + " \n", + " response = bedrock_runtime.invoke_model(\n", + " modelId=MODEL_ID,\n", + " body=json.dumps(request_body)\n", + " )\n", + " \n", + " return json.loads(response['body'].read())\n", + "\n", + "\n", + "def display_response(response):\n", + " \"\"\"\n", + " Display the response content blocks in a readable format.\n", + " \"\"\"\n", + " print(\"=\" * 60)\n", + " print(\"RESPONSE\")\n", + " print(\"=\" * 60)\n", + " print(f\"Stop Reason: {response.get('stop_reason', 'N/A')}\")\n", + " print(f\"Usage: {response.get('usage', {})}\")\n", + " print(\"-\" * 60)\n", + " \n", + " for i, block in enumerate(response.get('content', [])):\n", + " block_type = block.get('type')\n", + " print(f\"\\n[Block {i + 1}] Type: {block_type}\")\n", + " \n", + " if block_type == 'text':\n", + " print(f\" Text: {block.get('text')}\")\n", + " \n", + " elif block_type == 'server_tool_use':\n", + " print(f\" ID: {block.get('id')}\")\n", + " print(f\" Name: {block.get('name')}\")\n", + " print(f\" Input: {json.dumps(block.get('input', {}), indent=4)}\")\n", + " \n", + " elif block_type == 'tool_search_tool_result':\n", + " print(f\" Content: {json.dumps(block, indent=4)}\")\n", + " \n", + " elif block_type == 'tool_result':\n", + " print(f\" Tool Use ID: {block.get('tool_use_id')}\")\n", + " print(f\" Content: {json.dumps(block.get('content', []), indent=4)}\")\n", + " \n", + " elif block_type == 'tool_use':\n", + " print(f\" ID: {block.get('id')}\")\n", + " print(f\" Name: {block.get('name')}\")\n", + " print(f\" Input: {json.dumps(block.get('input', {}), indent=4)}\")\n", + " \n", + " else:\n", + " print(f\" Content: {json.dumps(block, indent=4)}\")\n", + " \n", + " print(\"\\n\" + \"=\" * 60)" + ] + }, + { + "cell_type": "markdown", + "id": "basic-example-header", + "metadata": {}, + "source": [ + "## Basic Example: Weather Tool\n", + "\n", + "Let's start with a simple example that demonstrates the core functionality. We'll define:\n", + "1. The `tool_search_tool_regex` - the server-side search tool\n", + "2. A `get_weather` tool with `defer_loading: true`\n", + "\n", + "When we ask about the weather, Claude will:\n", + "1. Invoke the Tool Search Tool to find relevant tools\n", + "2. Receive a `tool_reference` pointing to `get_weather`\n", + "3. Use the `get_weather` tool with the appropriate parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "basic-example", + "metadata": {}, + "outputs": [], + "source": [ + "# Define our tools\n", + "tools = [\n", + " # The Tool Search Tool - this is a server-side tool\n", + " {\n", + " \"type\": \"tool_search_tool_regex\",\n", + " \"name\": \"tool_search_tool_regex\"\n", + " },\n", + " # A deferred tool - won't be loaded into context until discovered\n", + " {\n", + " \"name\": \"get_weather\",\n", + " \"description\": \"Get current weather for a location\",\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"location\": {\"type\": \"string\"},\n", + " \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]}\n", + " },\n", + " \"required\": [\"location\"]\n", + " },\n", + " \"defer_loading\": True\n", + " }\n", + "]\n", + "\n", + "# Create our message\n", + "messages = [\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"What's the weather in Seattle?\"\n", + " }\n", + "]\n", + "\n", + "print(\"Sending request with Tool Search Tool...\")\n", + "print(f\"User message: {messages[0]['content']}\")\n", + "print()\n", + "\n", + "# Invoke the model\n", + "response = invoke_with_tool_search(messages, tools)\n", + "\n", + "# Display the response\n", + "display_response(response)" + ] + }, + { + "cell_type": "markdown", + "id": "response-walkthrough", + "metadata": {}, + "source": [ + "### Understanding the Response\n", + "\n", + "Let's break down what happened in the response:\n", + "\n", + "1. **`text` block**: Claude explains what it's about to do.\n", + "\n", + "2. **`server_tool_use` block**: Claude invoked the `tool_search_tool_regex` with a search pattern (e.g., `\"pattern\": \"weather\"`). This is handled server-side.\n", + "\n", + "3. **`tool_search_tool_result` block**: The server returned a `tool_reference` pointing to `get_weather`. This tells Claude that this tool is now available for use. Note the nested structure with `tool_references` array.\n", + "\n", + "4. **`text` block**: Claude explains it found the tool and will use it.\n", + "\n", + "5. **`tool_use` block**: Claude then called `get_weather` with the appropriate parameters (location: \"Seattle\").\n", + "\n", + "6. **`stop_reason: tool_use`**: The response stopped because Claude wants to use a tool. In a real application, you would execute the tool and return the result.\n", + "\n", + "Notice that:\n", + "- The `server_tool_use` has an ID starting with `srvtoolu_bdrk_` (server tool use)\n", + "- The regular `tool_use` has an ID starting with `toolu_bdrk_`\n", + "- The tool search and result happen automatically within a single API call" + ] + }, + { + "cell_type": "markdown", + "id": "multiple-tools-header", + "metadata": {}, + "source": [ + "## Multiple Deferred Tools\n", + "\n", + "Now let's add more deferred tools to demonstrate that Claude selects only the relevant one based on the user's query." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "multiple-tools", + "metadata": {}, + "outputs": [], + "source": [ + "# Define multiple deferred tools\n", + "tools_expanded = [\n", + " # The Tool Search Tool\n", + " {\n", + " \"type\": \"tool_search_tool_regex\",\n", + " \"name\": \"tool_search_tool_regex\"\n", + " },\n", + " # Weather tool\n", + " {\n", + " \"name\": \"get_weather\",\n", + " \"description\": \"Get current weather for a location\",\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"location\": {\"type\": \"string\"},\n", + " \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]}\n", + " },\n", + " \"required\": [\"location\"]\n", + " },\n", + " \"defer_loading\": True\n", + " },\n", + " # File search tool\n", + " {\n", + " \"name\": \"search_files\",\n", + " \"description\": \"Search through files in the workspace\",\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"query\": {\"type\": \"string\"},\n", + " \"file_types\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}}\n", + " },\n", + " \"required\": [\"query\"]\n", + " },\n", + " \"defer_loading\": True\n", + " },\n", + " # Calculator tool\n", + " {\n", + " \"name\": \"calculator\",\n", + " \"description\": \"Perform mathematical calculations\",\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"expression\": {\"type\": \"string\", \"description\": \"Mathematical expression to evaluate\"}\n", + " },\n", + " \"required\": [\"expression\"]\n", + " },\n", + " \"defer_loading\": True\n", + " },\n", + " # Send email tool\n", + " {\n", + " \"name\": \"send_email\",\n", + " \"description\": \"Send an email to a recipient\",\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"to\": {\"type\": \"string\"},\n", + " \"subject\": {\"type\": \"string\"},\n", + " \"body\": {\"type\": \"string\"}\n", + " },\n", + " \"required\": [\"to\", \"subject\", \"body\"]\n", + " },\n", + " \"defer_loading\": True\n", + " }\n", + "]\n", + "\n", + "print(f\"Total tools defined: {len(tools_expanded)} (1 search tool + {len(tools_expanded) - 1} deferred tools)\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "test-file-search", + "metadata": {}, + "outputs": [], + "source": [ + "# Test with a file search query\n", + "messages_files = [\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"Find all Python files that contain the word 'database'\"\n", + " }\n", + "]\n", + "\n", + "print(\"Testing file search query...\")\n", + "print(f\"User message: {messages_files[0]['content']}\")\n", + "print()\n", + "\n", + "response_files = invoke_with_tool_search(messages_files, tools_expanded)\n", + "display_response(response_files)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "test-calculator", + "metadata": {}, + "outputs": [], + "source": [ + "# Test with a calculation query\n", + "messages_calc = [\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"What is 15% of 2500?\"\n", + " }\n", + "]\n", + "\n", + "print(\"Testing calculation query...\")\n", + "print(f\"User message: {messages_calc[0]['content']}\")\n", + "print()\n", + "\n", + "response_calc = invoke_with_tool_search(messages_calc, tools_expanded)\n", + "display_response(response_calc)" + ] + }, + { + "cell_type": "markdown", + "id": "response-structure-header", + "metadata": {}, + "source": [ + "## Understanding the Response Structure\n", + "\n", + "Let's examine the different content block types you'll encounter when using the Tool Search Tool.\n", + "\n", + "### Content Block Types\n", + "\n", + "| Type | Description | When it appears |\n", + "|------|-------------|----------------|\n", + "| `text` | Regular text output from Claude | Claude's explanations |\n", + "| `server_tool_use` | Claude invoking a server-side tool | When Claude searches for tools |\n", + "| `tool_search_tool_result` | Result from the Tool Search Tool | Contains nested `tool_references` array |\n", + "| `tool_use` | Claude invoking a user-defined tool | When Claude uses a discovered tool |\n", + "\n", + "### The `tool_search_tool_result` Structure\n", + "\n", + "When the Tool Search Tool finds matching tools, it returns a `tool_search_tool_result` block with a nested structure:\n", + "\n", + "```json\n", + "{\n", + " \"type\": \"tool_search_tool_result\",\n", + " \"tool_use_id\": \"srvtoolu_bdrk_xxx\",\n", + " \"content\": {\n", + " \"type\": \"tool_search_tool_search_result\",\n", + " \"tool_references\": [\n", + " {\n", + " \"type\": \"tool_reference\",\n", + " \"tool_name\": \"get_weather\"\n", + " }\n", + " ]\n", + " }\n", + "}\n", + "```\n", + "\n", + "This tells Claude that the referenced tool(s) are now available for use." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "parse-response", + "metadata": {}, + "outputs": [], + "source": [ + "def analyze_tool_search_flow(response):\n", + " \"\"\"\n", + " Analyze and display the tool search flow from a response.\n", + " \"\"\"\n", + " print(\"Tool Search Flow Analysis\")\n", + " print(\"=\" * 40)\n", + " \n", + " searched_tools = []\n", + " discovered_tools = []\n", + " used_tools = []\n", + " \n", + " for block in response.get('content', []):\n", + " block_type = block.get('type')\n", + " \n", + " if block_type == 'server_tool_use':\n", + " pattern = block.get('input', {}).get('pattern', 'N/A')\n", + " searched_tools.append({\n", + " 'tool': block.get('name'),\n", + " 'pattern': pattern\n", + " })\n", + " \n", + " elif block_type == 'tool_search_tool_result':\n", + " # Handle the nested structure\n", + " content = block.get('content', {})\n", + " tool_refs = content.get('tool_references', [])\n", + " for ref in tool_refs:\n", + " if ref.get('type') == 'tool_reference':\n", + " discovered_tools.append(ref.get('tool_name'))\n", + " \n", + " elif block_type == 'tool_use':\n", + " used_tools.append({\n", + " 'tool': block.get('name'),\n", + " 'input': block.get('input', {})\n", + " })\n", + " \n", + " print(\"\\n1. SEARCH PHASE\")\n", + " if searched_tools:\n", + " for search in searched_tools:\n", + " print(f\" Tool: {search['tool']}\")\n", + " print(f\" Pattern: {search['pattern']}\")\n", + " else:\n", + " print(\" No search performed\")\n", + " \n", + " print(\"\\n2. DISCOVERY PHASE\")\n", + " if discovered_tools:\n", + " for tool in discovered_tools:\n", + " print(f\" Found: {tool}\")\n", + " else:\n", + " print(\" No tools discovered\")\n", + " \n", + " print(\"\\n3. USAGE PHASE\")\n", + " if used_tools:\n", + " for tool in used_tools:\n", + " print(f\" Tool: {tool['tool']}\")\n", + " print(f\" Input: {json.dumps(tool['input'], indent=4)}\")\n", + " else:\n", + " print(\" No tools used yet (waiting for tool result)\")\n", + " \n", + " print(\"\\n\" + \"=\" * 40)\n", + "\n", + "# Analyze our previous response\n", + "print(\"Analyzing the weather query response:\\n\")\n", + "analyze_tool_search_flow(response)" + ] + }, + { + "cell_type": "markdown", + "id": "error-handling-header", + "metadata": {}, + "source": [ + "## Error Handling\n", + "\n", + "There are specific error conditions to be aware of when using the Tool Search Tool.\n", + "\n", + "### Error 1: All Tools Have `defer_loading: true`\n", + "\n", + "You must have at least one tool with `defer_loading: false` (or without the `defer_loading` property). The Tool Search Tool itself does not have `defer_loading` set, so it serves this purpose.\n", + "\n", + "If all tools (including the search tool) have `defer_loading: true`, you'll receive a 400 error.\n", + "\n", + "### Error 2: Invalid `tool_reference`\n", + "\n", + "If a `tool_reference` is returned that doesn't match any tool definition, you'll receive a 400 error." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "error-example-valid", + "metadata": {}, + "outputs": [], + "source": [ + "# CORRECT: Tool Search Tool without defer_loading (implicitly false)\n", + "valid_tools = [\n", + " {\n", + " \"type\": \"tool_search_tool_regex\",\n", + " \"name\": \"tool_search_tool_regex\"\n", + " # No defer_loading - this is correct!\n", + " },\n", + " {\n", + " \"name\": \"some_tool\",\n", + " \"description\": \"A deferred tool\",\n", + " \"input_schema\": {\"type\": \"object\", \"properties\": {}},\n", + " \"defer_loading\": True # This tool is deferred\n", + " }\n", + "]\n", + "\n", + "print(\"Valid configuration:\")\n", + "print(\"- tool_search_tool_regex: defer_loading not set (defaults to false)\")\n", + "print(\"- some_tool: defer_loading = true\")\n", + "print(\"\\nThis configuration will work correctly.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "error-example-invalid", + "metadata": {}, + "outputs": [], + "source": [ + "# INCORRECT: This would cause a 400 error (DO NOT RUN)\n", + "# Shown for educational purposes only\n", + "\n", + "invalid_tools_example = \"\"\"\n", + "# This configuration would fail with a 400 error:\n", + "[\n", + " {\n", + " \"type\": \"tool_search_tool_regex\",\n", + " \"name\": \"tool_search_tool_regex\",\n", + " \"defer_loading\": true # ERROR: Search tool cannot be deferred!\n", + " },\n", + " {\n", + " \"name\": \"some_tool\",\n", + " \"description\": \"A tool\",\n", + " \"input_schema\": {...},\n", + " \"defer_loading\": true\n", + " }\n", + "]\n", + "\n", + "Error: Setting defer_loading: true for all tools will throw a 400 error\n", + "\"\"\"\n", + "\n", + "print(\"Invalid configuration example (DO NOT USE):\")\n", + "print(invalid_tools_example)" + ] + }, + { + "cell_type": "markdown", + "id": "custom-search-header", + "metadata": {}, + "source": [ + "## Custom Tool Search Tools\n", + "\n", + "While `tool_search_tool_regex` provides built-in regex-based search, you can implement your own custom tool search mechanism (e.g., using embeddings for semantic search).\n", + "\n", + "### Key Requirements for Custom Search Tools\n", + "\n", + "1. **Your custom search tool must have `defer_loading: false`** (or omit the property)\n", + "2. **Other tools should have `defer_loading: true`**\n", + "3. **Your tool must return `tool_reference` blocks** to indicate which tools Claude can use\n", + "\n", + "### Conceptual Example\n", + "\n", + "```python\n", + "# Custom search tool definition\n", + "custom_search_tool = {\n", + " \"name\": \"semantic_tool_search\",\n", + " \"description\": \"Search for tools using semantic similarity\",\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"query\": {\"type\": \"string\"}\n", + " },\n", + " \"required\": [\"query\"]\n", + " }\n", + " # defer_loading is implicitly false\n", + "}\n", + "\n", + "# When Claude calls your custom search tool, return tool_reference blocks:\n", + "tool_result = {\n", + " \"type\": \"tool_result\",\n", + " \"tool_use_id\": \"toolu_xxx\",\n", + " \"content\": [\n", + " {\"type\": \"tool_reference\", \"tool_name\": \"matched_tool_1\"},\n", + " {\"type\": \"tool_reference\", \"tool_name\": \"matched_tool_2\"}\n", + " ]\n", + "}\n", + "```\n", + "\n", + "This allows you to implement sophisticated tool discovery logic while maintaining the same interface." + ] + }, + { + "cell_type": "markdown", + "id": "best-practices-header", + "metadata": {}, + "source": [ + "## Best Practices\n", + "\n", + "### When to Use Tool Search Tool\n", + "\n", + "**Good use cases:**\n", + "- Large tool libraries (10+ tools)\n", + "- Plugin systems where tools are dynamically added\n", + "- MCP (Model Context Protocol) servers with many capabilities\n", + "- Applications where context window optimization is important\n", + "\n", + "**When it may not be needed:**\n", + "- Small number of tools (< 5) that easily fit in context\n", + "- All tools are always relevant to every query\n", + "- Simple, single-purpose applications\n", + "\n", + "### Configuration Tips\n", + "\n", + "1. **Always include the Tool Search Tool** as your non-deferred tool\n", + "2. **Write clear tool descriptions** - the search relies on matching tool names and descriptions\n", + "3. **Test with representative queries** - ensure the right tools are discovered for your use cases\n", + "4. **Handle the tool_use response** - remember that `stop_reason: tool_use` means you need to execute the tool and continue the conversation\n", + "\n", + "### Response Handling Pattern\n", + "\n", + "```python\n", + "# Typical flow for handling Tool Search Tool responses\n", + "response = invoke_with_tool_search(messages, tools)\n", + "\n", + "while response.get('stop_reason') == 'tool_use':\n", + " # Find the tool_use block\n", + " for block in response['content']:\n", + " if block['type'] == 'tool_use':\n", + " # Execute the tool\n", + " result = execute_tool(block['name'], block['input'])\n", + " \n", + " # Add assistant response and tool result to messages\n", + " messages.append({\"role\": \"assistant\", \"content\": response['content']})\n", + " messages.append({\n", + " \"role\": \"user\",\n", + " \"content\": [{\n", + " \"type\": \"tool_result\",\n", + " \"tool_use_id\": block['id'],\n", + " \"content\": result\n", + " }]\n", + " })\n", + " \n", + " # Continue the conversation\n", + " response = invoke_with_tool_search(messages, tools)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "summary-header", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this notebook, we covered:\n", + "\n", + "1. **What the Tool Search Tool does**: Enables Claude to work with many tools without loading all definitions upfront\n", + "\n", + "2. **How it works**: Tools marked with `defer_loading: true` are discovered on-demand through the `tool_search_tool_regex`\n", + "\n", + "3. **Key response types**:\n", + " - `server_tool_use`: Claude invoking the search tool\n", + " - `tool_search_tool_result` with nested `tool_references`: The discovered tools\n", + " - `tool_use`: Claude using the discovered tool\n", + "\n", + "4. **Error conditions**: Ensure at least one tool has `defer_loading: false`\n", + "\n", + "5. **Custom search tools**: You can implement your own search mechanism that returns `tool_reference` blocks\n", + "\n", + "### Key Observations from the Examples\n", + "\n", + "- Claude may choose **not** to use tools if it can answer directly (as seen in the calculator example)\n", + "- Claude can perform **multiple searches** in a single response to find the right tool (as seen in the file search example)\n", + "- The search pattern is typically derived from keywords in the user's query\n", + "- Empty `tool_references` arrays indicate no matching tools were found for that search pattern\n", + "\n", + "### Next Steps\n", + "\n", + "- Experiment with larger tool libraries\n", + "- Implement a complete tool execution loop\n", + "- Consider building a custom semantic search tool for better matching\n", + "- Combine with other features like streaming for real-time responses" + ] + }, + { + "cell_type": "markdown", + "id": "c07f9aa2", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}