how.wtf

Anthropic Claude with tools using Python SDK

· Thomas Taylor

On April 4th, 2024, Anthropic released official support for tool use through their API. This feature allows developers to define one or more tools that include parameters, descriptions, and schema definitions for Claude to process.

In this post, we’ll go over how to leverage these tools using the Python Anthropic SDK.

Claude with tools example in Python

Firstly, ensure that your Anthropic API key is available as an environment variable in your shell:

1export ANTHROPIC_API_KEY="sk-......"

Install the Anthropic SDK

Next, install the latest version of the Anthropic SDK using pip.

1pip install anthropic

Define the tools

Using the provided JSON schema example in the Anthropic documentation, let’s define a get_current_stock_price tool:

 1{
 2  "name": "get_current_stock_price",
 3  "description": "Retrieves the current stock price for a given ticker symbol. The ticker symbol must be a valid symbol for a publicly traded company on a major US stock exchange like NYSE or NASDAQ. The tool will return the latest trade price in USD. It should be used when the user asks about the current or most recent price of a specific stock. It will not provide any other information about the stock or company.",
 4  "input_schema": {
 5    "type": "object",
 6    "properties": {
 7      "ticker": {
 8        "type": "string",
 9        "description": "The stock ticker symbol, e.g. AAPL for Apple Inc."
10      }
11    },
12    "required": ["ticker"]
13  }
14}

In Python,

 1import anthropic
 2
 3tools = [
 4    {
 5        "name": "get_current_stock_price",
 6        "description": "Retrieves the current stock price for a given ticker symbol. The ticker symbol must be a valid symbol for a publicly traded company on a major US stock exchange like NYSE or NASDAQ. The tool will return the latest trade price in USD. It should be used when the user asks about the current or most recent price of a specific stock. It will not provide any other information about the stock or company.",
 7        "input_schema": {
 8            "type": "object",
 9            "properties": {
10                "symbol": {
11                    "type": "string",
12                    "description": "The stock ticker symbol, e.g. AAPL for Apple Inc."
13                }
14            },
15            "required": ["symbol"]
16        }
17    }
18]

Instantiate a client

To invoke Claude, we need to instantiate a client. The SDK uses the ANTHROPIC_API_KEY environment variable by default for authentication.

1import anthropic
2
3client = anthropic.Anthropic()

Invoke Claude with tools

Now, let’s create a new message using Claude and the tools:

 1import anthropic
 2
 3tools = [
 4    {
 5        "name": "get_current_stock_price",
 6        "description": "Retrieves the current stock price for a given ticker symbol. The ticker symbol must be a valid symbol for a publicly traded company on a major US stock exchange like NYSE or NASDAQ. The tool will return the latest trade price in USD. It should be used when the user asks about the current or most recent price of a specific stock. It will not provide any other information about the stock or company.",
 7        "input_schema": {
 8            "type": "object",
 9            "properties": {
10                "symbol": {
11                    "type": "string",
12                    "description": "The stock ticker symbol, e.g. AAPL for Apple Inc."
13                }
14            },
15            "required": ["symbol"]
16        }
17    }
18]
19
20client = anthropic.Anthropic()
21
22response = client.beta.tools.messages.create(
23    model="claude-3-haiku-20240307",
24    max_tokens=1024,
25    tools=tools,
26    messages=[
27        {
28            "role": "user",
29            "content": "What is the stock price of Apple?"
30        }
31    ]
32)
33
34print(response)

Output:

1ToolsBetaMessage(id='msg_01Bedxru94A4Pe1sHgWtymSJ', content=[ToolUseBlock(id='toolu_01CbGgyko9mdkKSDPw6LsTvV', input={'symbol': 'AAPL'}, name='get_current_stock_price', type='tool_use')], model='claude-3-haiku-20240307', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=433, output_tokens=60))

The output includes a ToolUseBlock with the input for the tool. We now need to process the tool and return the result to Claude.

Building a tool router

Let’s build a mock function that returns 150.0 for the stock price:

1def get_current_stock_price(symbol):
2    # Fake implementation for demonstration purposes
3    return 150.0

And then a “router” function that accepts the tool name and input:

1def process_tool(tool_name, input):
2    match tool_name:
3        case "get_current_stock_price":
4            return get_current_stock_price(input["symbol"])
5        case _:
6            raise ValueError(f"Unknown tool: {tool_name}")

Responding with a tool result example

Anthropic provides a Jupyter notebook that showcases a customer service example using tools. We’ll adapt some of the functionality to our script:

 1while response.stop_reason == "tool_use":
 2    tool_use = next(block for block in response.content if block.type == "tool_use")
 3    tool_name = tool_use.name
 4    tool_input = tool_use.input
 5    tool_result = process_tool(tool_name, tool_input)
 6
 7    messages = [
 8        {"role": "user", "content": user_message},
 9        {"role": "assistant", "content": response.content},
10        {
11            "role": "user",
12            "content": [
13                {
14                    "type": "tool_result",
15                    "tool_use_id": tool_use.id,
16                    "content": str(tool_result),
17                }
18            ],
19        },
20    ]
21
22    response = client.beta.tools.messages.create(
23        model=model,
24        max_tokens=4096,
25        tools=tools,
26        messages=messages
27    )
28
29final_response = next(
30    (block.text for block in response.content if isinstance(block, TextBlock)),
31    None,
32)

The code does the following:

  1. Loop while the response is tool_use
  2. Extracts the current tool’s name and input
  3. Processes the tool by calling the router function
  4. Creates a new Claude message using the prior messages and the tool result
  5. Retrieves the final response from Claude and returns the text

Putting it altogether

Here’s the resulting code:

 1import anthropic
 2from anthropic.types import TextBlock
 3
 4client = anthropic.Anthropic()
 5
 6
 7def talk(client, tools, model, user_message):
 8    response = client.beta.tools.messages.create(
 9        model=model,
10        max_tokens=1024,
11        tools=tools,
12        messages=[{"role": "user", "content": user_message}],
13    )
14
15    while response.stop_reason == "tool_use":
16        tool_use = next(block for block in response.content if block.type == "tool_use")
17        tool_name = tool_use.name
18        tool_input = tool_use.input
19        tool_result = process_tool(tool_name, tool_input)
20
21        messages = [
22            {"role": "user", "content": user_message},
23            {"role": "assistant", "content": response.content},
24            {
25                "role": "user",
26                "content": [
27                    {
28                        "type": "tool_result",
29                        "tool_use_id": tool_use.id,
30                        "content": str(tool_result),
31                    }
32                ],
33            },
34        ]
35
36        response = client.beta.tools.messages.create(
37            model=model, max_tokens=4096, tools=tools, messages=messages
38        )
39
40    final_response = next(
41        (block.text for block in response.content if isinstance(block, TextBlock)),
42        None,
43    )
44
45    return final_response
46
47
48def get_current_stock_price(symbol: str):
49    # Fake implementation for demonstration purposes
50    return 150.0
51
52
53def process_tool(tool_name: str, input):
54    match tool_name:
55        case "get_current_stock_price":
56            return get_current_stock_price(input["symbol"])
57        case _:
58            raise ValueError(f"Unknown tool: {tool_name}")
59
60
61if __name__ == "__main__":
62    client = anthropic.Anthropic()
63    tools = [
64        {
65            "name": "get_current_stock_price",
66            "description": "Retrieves the current stock price for a given ticker symbol. The ticker symbol must be a valid symbol for a publicly traded company on a major US stock exchange like NYSE or NASDAQ. The tool will return the latest trade price in USD. It should be used when the user asks about the current or most recent price of a specific stock. It will not provide any other information about the stock or company.",
67            "input_schema": {
68                "type": "object",
69                "properties": {
70                    "symbol": {
71                        "type": "string",
72                        "description": "The stock ticker symbol, e.g. AAPL for Apple Inc.",
73                    }
74                },
75                "required": ["symbol"],
76            },
77        }
78    ]
79    response = talk(
80        client, tools, "claude-3-haiku-20240307", "What is the price of Apple?"
81    )
82    print(response)

Here is the output:

1The current stock price for Apple (ticker symbol AAPL) is $150.00.

Conclusion

In this post, we covered:

  1. How to invoke Claude using tools
  2. How to process tool calls using a switch statement
  3. How to respond with a tool_result so that Claude can output a final answer

The purpose of this post was to get you started by providing a foundational understanding of Anthropic tool usage using the Python SDK. For more information about tool best practices or multi-tool chaining, please refer to Anthropic’s documentation.

#python   #generative-ai  

Reply to this post by email ↪