嵌入向量工具搜索

使用语义嵌入帮助 Claude 发现和选择工具

🌐 查看英文原文 | 源码 Notebook


基于 Embedding 的工具搜索:将 Claude 扩展到数千个工具

使用数十个专业工具构建 Claude 应用程序很快会遇到瓶颈:提前提供所有工具定义会消耗您的上下文窗口,增加延迟和成本,并使 Claude 更难找到正确的工具。超过 ~100 个工具时,这种方法变得不切实际。

语义工具搜索通过将工具视为可发现资源来解决此问题。您不必预先加载数百个定义,而是给 Claude 一个单一的 tool_search 工具,该工具可以按需返回相关功能,将上下文使用量减少 90% 以上,同时使应用程序能够扩展到数千个工具。

到本食谱结束时,您将能够: - 实现客户端工具搜索,将 Claude 应用程序从数十个扩展到数千个工具 - 使用语义嵌入根据任务上下文动态发现相关工具 - 将此模式应用于特定领域的工具库(API、数据库、内部系统)

此模式在生产中被管理大型工具生态系统的团队使用,其中上下文效率至关重要。虽然我们将使用一小套工具进行演示以保持清晰,但相同的方法可以无缝扩展到包含数百或数千个工具的库。

前置条件

在遵循此指南之前,请确保您具备以下条件:

所需知识 - Python 基础知识 - 熟悉函数、字典和基本数据结构 - 基本了解 Claude 工具使用 - 我们建议首先阅读工具使用指南

所需工具 - Python 3.11 或更高版本 - Anthropic API 密钥 (在此获取)

环境配置

首先,安装所需的依赖项:

# 示例代码
``````python
# Note: we use -q to avoid printing too much to stdout
# Use --only-binary to avoid build issues with pythran
%pip install --only-binary :all: -q anthropic sentence-transformers numpy python-dotenv
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using `tokenizers` before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
Note: you may need to restart the kernel to use updated packages.

Ensure your .env file contains:

ANTHROPIC_API_KEY=your_key_here

Load your environment variables and configure the client:

import json
import random
from datetime import datetime, timedelta
from typing import Any

import anthropic
import numpy as np
from dotenv import load_dotenv
from sentence_transformers import SentenceTransformer

# Load environment variables from .env file
load_dotenv()

# Define model constant for easy updates
MODEL = "claude-sonnet-4-6"

# Initialize Claude client (API key loaded from environment)
claude_client = anthropic.Anthropic()

# Load the SentenceTransformer model
# all-MiniLM-L6-v2 is a lightweight model with 384 dimensional embeddings
# It will be downloaded from HuggingFace on first use
print("Loading SentenceTransformer model...")
embedding_model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")

print("✓ Clients initialized successfully")
Loading SentenceTransformer model...
✓ Clients initialized successfully
```## 定义工具库

在我们实现语义搜索之前,我们需要一些工具来进行搜索。我们将创建一个包含8个工具的库,这些工具分为两个类别:天气和金融。

在生产应用中,您可能需要管理数百或数千个工具,这些工具分布在您的内部API、数据库操作或第三方集成中。语义搜索方法无需修改即可扩展到这些更大的库——我们在这里使用的是一个小型库,仅为了演示的清晰性。

In production applications, you might manage hundreds or thousands of tools across your internal APIs, database operations, or third-party integrations. The semantic search approach scales to these larger libraries without modification - we're using a small set here purely for demonstration clarity.

::: callout-notice
请注意,以下代码块中的技术术语将保持英文。
:::

```python
# 示例代码
def search_tool(query, tools):
    # 搜索逻辑
    pass
``````python
# Define our tool library with 2 domains
TOOL_LIBRARY = [
    # Weather Tools
    {
        "name": "get_weather",
        "description": "Get the current weather in a given location",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA",
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "The unit of temperature",
                },
            },
            "required": ["location"],
        },
    },
    {
        "name": "get_forecast",
        "description": "Get the weather forecast for multiple days ahead",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state",
                },
                "days": {
                    "type": "number",
                    "description": "Number of days to forecast (1-10)",
                },
            },
            "required": ["location", "days"],
        },
    },
    {
        "name": "get_timezone",
        "description": "Get the current timezone and time for a location",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City name or timezone identifier",
                }
            },
            "required": ["location"],
        },
    },
    {
        "name": "get_air_quality",
        "description": "Get current air quality index and pollutant levels for a location",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City name or coordinates",
                }
            },
            "required": ["location"],
        },
    },
    # Finance Tools
    {
        "name": "get_stock_price",
        "description": "Get the current stock price and market data for a given ticker symbol",
        "input_schema": {
            "type": "object",
            "properties": {
                "ticker": {
                    "type": "string",
                    "description": "Stock ticker symbol (e.g., AAPL, GOOGL)",
                },
                "include_history": {
                    "type": "boolean",
                    "description": "Include historical data",
                },
            },
            "required": ["ticker"],
        },
    },
    {
        "name": "convert_currency",
        "description": "Convert an amount from one currency to another using current exchange rates",
        "input_schema": {
            "type": "object",
            "properties": {
                "amount": {
                    "type": "number",
                    "description": "Amount to convert",
                },
                "from_currency": {
                    "type": "string",
                    "description": "Source currency code (e.g., USD)",
                },
                "to_currency": {
                    "type": "string",
                    "description": "Target currency code (e.g., EUR)",
                },
            },
            "required": ["amount", "from_currency", "to_currency"],
        },
    },
    {
        "name": "calculate_compound_interest",
        "description": "Calculate compound interest for investments over time",
        "input_schema": {
            "type": "object",
            "properties": {
                "principal": {
                    "type": "number",
                    "description": "Initial investment amount",
                },
                "rate": {
                    "type": "number",
                    "description": "Annual interest rate (as percentage)",
                },
                "years": {"type": "number", "description": "Number of years"},
                "frequency": {
                    "type": "string",
                    "enum": ["daily", "monthly", "quarterly", "annually"],
                    "description": "Compounding frequency",
                },
            },
            "required": ["principal", "rate", "years"],
        },
    },
    {
        "name": "get_market_news",
        "description": "Get recent financial news and market updates for a specific company or sector",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Company name, ticker symbol, or sector",
                },
                "limit": {
                    "type": "number",
                    "description": "Maximum number of news articles to return",
                },
            },
            "required": ["query"],
        },
    },
]

print(f"✓ Defined {len(TOOL_LIBRARY)} tools in the library")
✓ Defined 8 tools in the library
```## 创建工具 Embedding

语义搜索通过比较文本的 *意义* 来工作,而不仅仅是搜索关键词。为了实现这一点,我们需要将每个工具定义转换为捕获其语义意义的 **嵌入向量**。

由于我们的工具定义是具有名称、描述和参数的结构化JSON对象,我们首先将每个工具转换为可读的文本表示,然后使用SentenceTransformer的`all-MiniLM-L6-v2`模型生成嵌入向量。

我们选择这个模型是因为它:
- **轻量级且快速**(只有384个维度,而较大模型有768+个维度)
- **本地运行**,无需API调用
- **足以用于工具搜索**(您可以尝试使用较大模型以获得更高的准确性)

让我们先创建一个函数,将工具定义转换为可搜索的文本:

```python
def tool_to_searchable_text(tool):
    # 代码实现
    pass
``````python
def tool_to_text(tool: dict[str, Any]) -> str:
    """
    Convert a tool definition into a text representation for embedding.
    Combines the tool name, description, and parameter information.
    """
    text_parts = [
        f"Tool: {tool['name']}",
        f"Description: {tool['description']}",
    ]

    # Add parameter information
    if "input_schema" in tool and "properties" in tool["input_schema"]:
        params = tool["input_schema"]["properties"]
        param_descriptions = []
        for param_name, param_info in params.items():
            param_desc = param_info.get("description", "")
            param_type = param_info.get("type", "")
            param_descriptions.append(f"{param_name} ({param_type}): {param_desc}")

        if param_descriptions:
            text_parts.append("Parameters: " + ", ".join(param_descriptions))

    return "\n".join(text_parts)


# Test with one tool
sample_text = tool_to_text(TOOL_LIBRARY[0])
print("Sample tool text representation:")
print(sample_text)
Sample tool text representation:
Tool: get_weather
Description: Get the current weather in a given location
Parameters: location (string): The city and state, e.g. San Francisco, CA, unit (string): The unit of temperature

Now let’s create embeddings for all our tools:

# Create embeddings for all tools
print("Creating embeddings for all tools...")

tool_texts = [tool_to_text(tool) for tool in TOOL_LIBRARY]

# Embed all tools at once using SentenceTransformer
# The model returns normalized embeddings by default
tool_embeddings = embedding_model.encode(tool_texts, convert_to_numpy=True)

print(f"✓ Created embeddings with shape: {tool_embeddings.shape}")
print(f"  - {tool_embeddings.shape[0]} tools")
print(f"  - {tool_embeddings.shape[1]} dimensions per embedding")
Creating embeddings for all tools...
✓ Created embeddings with shape: (8, 384)
  - 8 tools
  - 384 dimensions per embedding
```## 实现工具搜索

通过将我们的工具嵌入为向量,我们现在可以实现语义搜索。如果两段文本具有相似的意义,它们的嵌入向量在向量空间中将彼此靠近。我们使用**余弦相似度**来衡量这种“接近度”。

搜索过程:
1. **嵌入查询**:将Claude的自然语言搜索请求转换为与我们的工具相同的向量空间
2. **计算相似度**:计算查询向量和每个工具向量之间的余弦相似度
3. **排序并返回**:根据相似度分数对工具进行排序并返回前N个匹配项

通过语义搜索,Claude可以使用自然语言进行搜索,例如“我需要查看天气”或“计算投资回报”,而不是精确的工具名称。

让我们实现搜索函数并使用一个示例查询进行测试:

```python
def search(query):
    # 将查询转换为向量
    query_vector = embed(query)
    # 计算查询向量与每个工具向量的相似度
    similarities = [cosine_similarity(query_vector, tool_vector) for tool_vector in tool_vectors]
    # 根据相似度分数排序工具并返回前N个匹配项
    return sorted(range(len(similarities)), key=lambda i: similarities[i], reverse=True)[:N]
{
  "query": "I need to check the weather",
  "results": [
    {
      "tool": "Weather API",
      "score": 0.95
    },
    {
      "tool": "Calendar",
      "score": 0.85
    }
  ]
}
SELECT tool, score
FROM search_results
WHERE query = 'I need to check the weather'
ORDER BY score DESC
LIMIT 2;

请注意,这里的代码示例仅供参考,实际实现可能需要根据具体情况进行调整。 :::```python def search_tools(query: str, top_k: int = 5) -> list[dict[str, Any]]: ““” Search for tools using semantic similarity.

Args:
    query: Natural language description of what tool is needed
    top_k: Number of top tools to return

Returns:
    List of tool definitions most relevant to the query
"""
# Embed the query using SentenceTransformer
query_embedding = embedding_model.encode(query, convert_to_numpy=True)

# Calculate cosine similarity using dot product
# SentenceTransformer returns normalized embeddings, so dot product = cosine similarity
similarities = np.dot(tool_embeddings, query_embedding)

# Get top k indices
top_indices = np.argsort(similarities)[-top_k:][::-1]

# Return the corresponding tools with their scores
results = []
for idx in top_indices:
    results.append({"tool": TOOL_LIBRARY[idx], "similarity_score": float(similarities[idx])})

return results

Test the search function

test_query = “I need to check the weather” test_results = search_tools(test_query, top_k=3)

print(f”Search query: ‘{test_query}’“) print(”Top 3 matching tools:“) for i, result in enumerate(test_results, 1): tool_name = result[”tool”][”name”] score = result[”similarity_score”] print(f”{i}. {tool_name} (similarity: {score:.3f})“)

Search query: ‘I need to check the weather’

Top 3 matching tools: 1. get_weather (similarity: 0.560) 2. get_forecast (similarity: 0.508) 3. get_air_quality (similarity: 0.401) ```## 定义 tool_search 工具

Now we’ll implement the meta-tool that allows Claude to discover other tools on demand. When Claude needs a capability it doesn’t have, it searches for it using this tool_search tool, receives the tool definitions in the result, and can use those newly discovered tools immediately.

This is the only tool we provide to Claude initially:

def tool_search(query):
    # Implementation goes here
    pass
{
    "tool_name": "example_tool",
    "description": "An example tool for demonstration purposes."
}
CREATE TABLE tools (
    tool_name VARCHAR(255),
    description TEXT
);
``````python
# The tool_search tool definition
TOOL_SEARCH_DEFINITION = {
    "name": "tool_search",
    "description": "Search for available tools that can help with a task. Returns tool definitions for matching tools. Use this when you need a tool but don't have it available yet.",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "Natural language description of what kind of tool you need (e.g., 'weather information', 'currency conversion', 'stock prices')",
            },
            "top_k": {
                "type": "number",
                "description": "Number of tools to return (default: 5)",
            },
        },
        "required": ["query"],
    },
}

print("✓ Tool search definition created")
✓ Tool search definition created
```现在让我们实现处理Claude发出的`tool_search`调用的处理器,并返回发现的工具:

```markdown
# 实现处理 `tool_search` 调用的处理器

Now let's implement the handler that processes `tool_search` calls from Claude and returns discovered tools:

```python
def handle_tool_search():
    # 处理工具搜索逻辑
    pass

```::: callout-注意 请注意,这里只是展示了处理器的框架,具体的实现细节需要根据实际需求来编写。

``````python def handle_tool_search(query: str, top_k: int = 5) -> list[dict[str, Any]]: ““” Handle a tool_search invocation and return tool references.

Returns a list of tool_reference content blocks for discovered tools.
"""
# Search for relevant tools
results = search_tools(query, top_k=top_k)

# Create tool_reference objects instead of full definitions
tool_references = [
    {"type": "tool_reference", "tool_name": result["tool"]["name"]} for result in results
]

print(f"\n🔍 Tool search: '{query}'")
print(f"   Found {len(tool_references)} tools:")
for i, result in enumerate(results, 1):
    print(f"   {i}. {result['tool']['name']} (similarity: {result['similarity_score']:.3f})")

return tool_references

Test the handler

test_result = handle_tool_search(“stock market data”, top_k=3) print(f” tool references:“) for ref in test_result: print(f” {ref}“)

🔍 Tool search: ‘stock market data’ Found 3 tools: 1. get_stock_price (similarity: 0.524) 2. get_market_news (similarity: 0.469) 3. calculate_compound_interest (similarity: 0.244)

Returned 3 tool references: {‘type’: ‘tool_reference’, ‘tool_name’: ‘get_stock_price’} {‘type’: ‘tool_reference’, ‘tool_name’: ‘get_market_news’} {‘type’: ‘tool_reference’, ‘tool_name’: ‘calculate_compound_interest’} ```## 模拟工具执行

For this demonstration, we’ll create mock responses for tool executions. In a real application, these would call actual APIs or services:

def mock_tool_execution():
    # 模拟工具执行逻辑
    pass
{
    "response": "执行成功"
}
SELECT * FROM tools WHERE id = 1;
``````python
def mock_tool_execution(tool_name: str, tool_input: dict[str, Any]) -> str:
    """
    Generate realistic mock responses for tool executions.

    Args:
        tool_name: Name of the tool being executed
        tool_input: Input parameters for the tool

    Returns:
        Mock response string appropriate for the tool
    """
    # Weather tools
    if tool_name == "get_weather":
        location = tool_input.get("location", "Unknown")
        unit = tool_input.get("unit", "fahrenheit")
        temp = random.randint(15, 30) if unit == "celsius" else random.randint(60, 85)
        conditions = random.choice(["sunny", "partly cloudy", "cloudy", "rainy"])
        return json.dumps(
            {
                "location": location,
                "temperature": temp,
                "unit": unit,
                "conditions": conditions,
                "humidity": random.randint(40, 80),
                "wind_speed": random.randint(5, 20),
            }
        )

    elif tool_name == "get_forecast":
        location = tool_input.get("location", "Unknown")
        days = int(tool_input.get("days", 5))
        forecast = []
        for i in range(days):
            date = (datetime.now() + timedelta(days=i)).strftime("%Y-%m-%d")
            forecast.append(
                {
                    "date": date,
                    "high": random.randint(20, 30),
                    "low": random.randint(10, 20),
                    "conditions": random.choice(["sunny", "cloudy", "rainy", "partly cloudy"]),
                }
            )
        return json.dumps({"location": location, "forecast": forecast})

    elif tool_name == "get_timezone":
        location = tool_input.get("location", "Unknown")
        return json.dumps(
            {
                "location": location,
                "timezone": "UTC+9",
                "current_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "utc_offset": "+09:00",
            }
        )

    elif tool_name == "get_air_quality":
        location = tool_input.get("location", "Unknown")
        aqi = random.randint(20, 150)
        categories = {
            (0, 50): "Good",
            (51, 100): "Moderate",
            (101, 150): "Unhealthy for Sensitive Groups",
        }
        category = next(cat for (low, high), cat in categories.items() if low <= aqi <= high)
        return json.dumps(
            {
                "location": location,
                "aqi": aqi,
                "category": category,
                "pollutants": {
                    "pm25": random.randint(5, 50),
                    "pm10": random.randint(10, 100),
                    "o3": random.randint(20, 80),
                },
            }
        )

    # Finance tools
    elif tool_name == "get_stock_price":
        ticker = tool_input.get("ticker", "UNKNOWN")
        return json.dumps(
            {
                "ticker": ticker,
                "price": round(random.uniform(100, 500), 2),
                "change": round(random.uniform(-5, 5), 2),
                "change_percent": round(random.uniform(-2, 2), 2),
                "volume": random.randint(1000000, 10000000),
                "market_cap": f"${random.randint(100, 1000)}B",
            }
        )

    elif tool_name == "convert_currency":
        amount = tool_input.get("amount", 0)
        from_currency = tool_input.get("from_currency", "USD")
        to_currency = tool_input.get("to_currency", "EUR")
        # Mock exchange rate
        rate = random.uniform(0.8, 1.2)
        converted = round(amount * rate, 2)
        return json.dumps(
            {
                "original_amount": amount,
                "from_currency": from_currency,
                "to_currency": to_currency,
                "exchange_rate": round(rate, 4),
                "converted_amount": converted,
            }
        )

    elif tool_name == "calculate_compound_interest":
        principal = tool_input.get("principal", 0)
        rate = tool_input.get("rate", 0)
        years = tool_input.get("years", 0)
        frequency = tool_input.get("frequency", "monthly")

        # Calculate compound interest
        n_map = {"daily": 365, "monthly": 12, "quarterly": 4, "annually": 1}
        n = n_map.get(frequency, 12)
        final_amount = principal * (1 + rate / 100 / n) ** (n * years)
        interest_earned = final_amount - principal

        return json.dumps(
            {
                "principal": principal,
                "rate": rate,
                "years": years,
                "compounding_frequency": frequency,
                "final_amount": round(final_amount, 2),
                "interest_earned": round(interest_earned, 2),
            }
        )

    elif tool_name == "get_market_news":
        query = tool_input.get("query", "")
        limit = tool_input.get("limit", 5)
        news = []
        for i in range(min(limit, 5)):
            news.append(
                {
                    "title": f"{query} - News Article {i + 1}",
                    "source": random.choice(
                        [
                            "Bloomberg",
                            "Reuters",
                            "Financial Times",
                            "Wall Street Journal",
                        ]
                    ),
                    "published": (datetime.now() - timedelta(hours=random.randint(1, 24))).strftime(
                        "%Y-%m-%d %H:%M"
                    ),
                    "summary": f"Latest developments regarding {query}...",
                }
            )
        return json.dumps({"query": query, "articles": news, "count": len(news)})

    # Default fallback
    else:
        return json.dumps(
            {
                "status": "executed",
                "tool": tool_name,
                "message": f"Tool {tool_name} executed successfully with input: {json.dumps(tool_input)}",
            }
        )


print("✓ Mock tool execution function created")
✓ Mock tool execution function created
```## 实现对话循环

现在让我们把它们全部整合起来!我们将创建一个处理完整工具搜索工作流程的对话循环。

**对话流程:**
1. Claude开始时只有`tool_search`工具可用
2. 当Claude调用`tool_search`时,我们运行语义搜索并返回匹配的工具定义
3. Claude然后可以立即使用发现的工具
4. 当Claude调用一个发现的工具时,我们执行它(在这个演示中使用模拟响应)
5. 循环继续,直到Claude得到最终答案```python
def run_tool_search_conversation(user_message: str, max_turns: int = 5) -> None:
    """
    Run a conversation with Claude using the tool search pattern.

    Args:
        user_message: The initial user message
        max_turns: Maximum number of conversation turns
    """
    print(f"\n{'=' * 80}")
    print(f"USER: {user_message}")
    print(f"{'=' * 80}\n")

    # Initialize conversation with only tool_search available
    messages = [{"role": "user", "content": user_message}]

    for turn in range(max_turns):
        print(f"\n--- Turn {turn + 1} ---")

        # Call Claude with current message history
        response = claude_client.messages.create(
            model=MODEL,
            max_tokens=1024,
            tools=TOOL_LIBRARY + [TOOL_SEARCH_DEFINITION],
            messages=messages,
            # IMPORTANT: This beta header enables tool definitions in tool results
            extra_headers={"anthropic-beta": "advanced-tool-use-2025-11-20"},
        )

        # Add assistant's response to messages
        messages.append({"role": "assistant", "content": response.content})

        # Check if we're done
        if response.stop_reason == "end_turn":
            print("\n✓ Conversation complete\n")
            # Print final response
            for block in response.content:
                if block.type == "text":
                    print(f"ASSISTANT: {block.text}")
            break

        # Handle tool uses
        if response.stop_reason == "tool_use":
            tool_results = []

            for block in response.content:
                if block.type == "text":
                    print(f"\nASSISTANT: {block.text}")

                elif block.type == "tool_use":
                    tool_name = block.name
                    tool_input = block.input
                    tool_use_id = block.id

                    print(f"\n🔧 Tool invocation: {tool_name}")
                    print(f"   Input: {json.dumps(tool_input, indent=2)}")

                    if tool_name == "tool_search":
                        # Handle tool search
                        query = tool_input["query"]
                        top_k = tool_input.get("top_k", 5)

                        # Get tool references
                        tool_references = handle_tool_search(query, top_k)

                        # Create tool result with tool_reference content blocks
                        tool_results.append(
                            {
                                "type": "tool_result",
                                "tool_use_id": tool_use_id,
                                "content": tool_references,
                            }
                        )
                    else:
                        # Execute the discovered tool with mock data
                        mock_result = mock_tool_execution(tool_name, tool_input)

                        # Print a preview of the result
                        if len(mock_result) > 150:
                            print(f"   ✅ Mock result: {mock_result[:150]}...")
                        else:
                            print(f"   ✅ Mock result: {mock_result}")

                        tool_results.append(
                            {
                                "type": "tool_result",
                                "tool_use_id": tool_use_id,
                                "content": mock_result,
                            }
                        )

            # Add tool results to messages
            if tool_results:
                messages.append({"role": "user", "content": tool_results})
        else:
            print(f"\nUnexpected stop reason: {response.stop_reason}")
            break

    print(f"\n{'=' * 80}\n")


print("✓ Conversation loop implemented")
✓ Conversation loop implemented
```## 示例 1:天气查询

让我们用一个简单的天气问题来测试。Claude 应该:
1. 调用 `tool_search` 来查找天气工具
2. 接收结果中的天气工具定义
3. 使用发现的工具之一```python
run_tool_search_conversation("What's the weather like in Tokyo?")

================================================================================
USER: What's the weather like in Tokyo?
================================================================================


--- Turn 1 ---

🔧 Tool invocation: get_weather
   Input: {
  "location": "Tokyo"
}
   ✅ Mock result: {"location": "Tokyo", "temperature": 75, "unit": "fahrenheit", "conditions": "partly cloudy", "humidity": 61, "wind_speed": 9}

--- Turn 2 ---

✓ Conversation complete

ASSISTANT: The weather in Tokyo is currently:
- **Temperature:** 75°F (about 24°C)
- **Conditions:** Partly cloudy
- **Humidity:** 61%
- **Wind Speed:** 9 mph

It's a pleasant day with comfortable temperatures and some cloud cover!

================================================================================
```## 示例 2:财务查询

让我们尝试一个需要发现和使用财务工具的财务计算查询:

```html
::: callout-attention
请注意,以下示例中的代码仅供参考,实际应用时可能需要根据具体情况进行调整。
:::

```python
# 导入必要的库
import RAG
import json

# 定义查询
query = "计算2023年1月到3月的总销售额"

# 创建Agent
agent = RAG.Agent()

# 使用API获取数据
data = agent.get_data_from_API(query)

# 解析JSON数据
parsed_data = json.loads(data)

# 计算总销售额
total_sales = sum(parsed_data['sales'])

# 输出结果
print("2023年1月到3月的总销售额为:", total_sales)
``````python
run_tool_search_conversation(
    "If I invest $10,000 at 5% annual interest for 10 years with monthly compounding, how much will I have?"
)

```

================================================================================ USER: If I invest $10,000 at 5% annual interest for 10 years with monthly compounding, how much will I have? ================================================================================

— Turn 1 —

🔧 Tool invocation: calculate_compound_interest Input: { “principal”: 10000, “rate”: 5, “years”: 10, “frequency”: “monthly” } ✅ Mock result: {“principal”: 10000, “rate”: 5, “years”: 10, “compounding_frequency”: “monthly”, “final_amount”: 16470.09, “interest_earned”: 6470.09}

— Turn 2 —

✓ Conversation complete

ASSISTANT: If you invest $10,000 at 5% annual interest for 10 years with monthly compounding, you will have:

Final Amount: $16,470.09

This means you’ll earn $6,470.09 in interest over the 10-year period.

The monthly compounding means that interest is calculated and added to your principal every month, which allows your investment to grow faster than with annual compounding due to the effect of earning “interest on interest” more frequently.

================================================================================ ```## 结论

在本指南中,我们实现了一个客户端工具搜索系统,使Claude能够高效地与大型工具库协同工作。我们涵盖了以下内容:

  • 语义工具发现:使用嵌入技术将自然语言查询与相关工具匹配,使Claude能够在不查看所有可用工具的情况下找到正确的功能
  • 动态工具加载:利用Claude的工具搜索功能返回工具定义,允许Claude在对话中即时发现并使用新工具
  • 上下文优化:将初始上下文从数千个标记(19+工具定义)减少到单个tool_search定义,上下文使用量减少90%+

将此应用到你的项目中

考虑以下情况时使用工具搜索: - 你拥有>20个专业工具,上下文使用成为关注点 - 你的工具库随着时间的推移而增长,手动管理变得不切实际 - 你需要支持具有数百个端点的特定领域API(数据库操作、内部微服务、第三方集成) - 成本和延迟优化是你的应用程序的重点

后续步骤

为了进一步实现此方案:

  1. 持久化嵌入:将嵌入缓存到磁盘以避免每次会话重新计算,减少启动时间
  2. 提高搜索质量:尝试不同的嵌入模型(例如,更大的模型如all-mpnet-base-v2)或实现结合语义和关键字匹配的混合搜索(BM25)
  3. 扩展到更大的库:使用数百或数千个工具进行测试,以查看该模式在生产规模下的表现
  4. 添加工具元数据:在搜索排名中包含使用统计信息、成本信息或可靠性评分
  5. 实现缓存:缓存常用工具定义以减少重复搜索

延伸阅读


Tip

中文读者提示:本章节的代码和输出为英文原文。如需理解具体实现细节,可参考上方中文导读和代码注释。如有疑问,欢迎在 GitHub Issues 讨论。