Anthropic Claude3: Function Calling Tools in Python
Function calling tools in Python allow extending Claude’s capabilities by integrating external functions or APIs. Define functions, create tool descriptions, construct system prompts, and process user messages to generate responses with function calls. This powerful technique enables Claude to perform tasks beyond its built-in knowledge.
llm
claude3
function-calling
python
Author
Arun Prakash
Published
March 22, 2024
Function calling is a powerful technique that allows you to extend the capabilities of a language model like Claude by integrating external functions or APIs. It enables the model to perform tasks or access information that it wouldn’t be able to do on its own.
Function Definition: First, you need to define the external function that you want Claude to call. This function can perform any task, such as string manipulation, mathematical calculations, API queries, or database operations. In the string reversal example, we defined the reverse_string function:
def reverse_string(string):return string[::-1]
Tool Description: Next, you create a description of the function (referred to as a “tool”) that Claude can understand. This description includes the tool’s name, a brief explanation of what it does, and the parameters it accepts. You use the construct_format_tool_for_claude_prompt function to format this information:
tool_name ="reverse_string"tool_description ="Reverses the provided string."parameters = [ {"name": "string","type": "str","description": "The string to be reversed." }]tool = construct_format_tool_for_claude_prompt(tool_name, tool_description, parameters)
System Prompt: The system prompt is a set of instructions that guide Claude on how to use the available tools. It’s created using the construct_tool_use_system_prompt function, which takes the tool as input:
User Message: The user message is the input from the user that Claude will respond to. In the example, it’s a request to reverse a specific string:
reverse_message = {"role": "user","content": "Reverse the string 'Hello, World!'"}
Partial Response: Claude processes the user message and the system prompt to generate a partial response. This response includes a function call in a specific format. The client.messages.create function is used to send the message to Claude and retrieve the partial response:
Parameter Extraction: The parameters for the function call are extracted from the partial response using the extract_between_tags function. This function uses regular expressions to find the values between specific XML-like tags:
Function Execution: The extracted parameters are passed to the actual function (reverse_string in this case), and the function is executed:
reversed_string = reverse_string(string)
Result Formatting: The result of the function execution is formatted into a structure that Claude can understand using the construct_successful_function_run_injection_prompt function:
Final Response: The formatted results are appended to the partial response, and this combined message is sent back to Claude to generate the final response:
In the final response, Claude incorporates the result of the function call into its output, allowing it to provide a response that it wouldn’t have been able to generate on its own.
Function calling opens up a wide range of possibilities for extending the capabilities of language models like Claude. By defining custom functions and providing clear descriptions of how to use them, you can enable Claude to perform complex tasks, access real-time data, and interact with external systems.
!pip install anthropic --upgrade
Requirement already satisfied: anthropic in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (0.16.0)
Collecting anthropic
Using cached anthropic-0.21.0-py3-none-any.whl (851 kB)
Requirement already satisfied: anyio<5,>=3.5.0 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from anthropic) (3.7.1)
Requirement already satisfied: distro<2,>=1.7.0 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from anthropic) (1.8.0)
Requirement already satisfied: httpx<1,>=0.23.0 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from anthropic) (0.25.0)
Requirement already satisfied: pydantic<3,>=1.9.0 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from anthropic) (2.4.2)
Requirement already satisfied: sniffio in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from anthropic) (1.3.0)
Requirement already satisfied: tokenizers>=0.13.0 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from anthropic) (0.14.1)
Requirement already satisfied: typing-extensions<5,>=4.7 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from anthropic) (4.8.0)
Requirement already satisfied: idna>=2.8 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from anyio<5,>=3.5.0->anthropic) (3.4)
Requirement already satisfied: certifi in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from httpx<1,>=0.23.0->anthropic) (2023.7.22)
Requirement already satisfied: httpcore<0.19.0,>=0.18.0 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from httpx<1,>=0.23.0->anthropic) (0.18.0)
Requirement already satisfied: annotated-types>=0.4.0 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->anthropic) (0.6.0)
Requirement already satisfied: pydantic-core==2.10.1 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->anthropic) (2.10.1)
Requirement already satisfied: huggingface_hub<0.18,>=0.16.4 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from tokenizers>=0.13.0->anthropic) (0.17.3)
Requirement already satisfied: h11<0.15,>=0.13 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from httpcore<0.19.0,>=0.18.0->httpx<1,>=0.23.0->anthropic) (0.14.0)
Requirement already satisfied: filelock in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from huggingface_hub<0.18,>=0.16.4->tokenizers>=0.13.0->anthropic) (3.12.4)
Requirement already satisfied: fsspec in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from huggingface_hub<0.18,>=0.16.4->tokenizers>=0.13.0->anthropic) (2023.9.2)
Requirement already satisfied: requests in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from huggingface_hub<0.18,>=0.16.4->tokenizers>=0.13.0->anthropic) (2.31.0)
Requirement already satisfied: tqdm>=4.42.1 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from huggingface_hub<0.18,>=0.16.4->tokenizers>=0.13.0->anthropic) (4.66.1)
Requirement already satisfied: pyyaml>=5.1 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from huggingface_hub<0.18,>=0.16.4->tokenizers>=0.13.0->anthropic) (6.0.1)
Requirement already satisfied: packaging>=20.9 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from huggingface_hub<0.18,>=0.16.4->tokenizers>=0.13.0->anthropic) (23.2)
Requirement already satisfied: charset-normalizer<4,>=2 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from requests->huggingface_hub<0.18,>=0.16.4->tokenizers>=0.13.0->anthropic) (3.3.0)
Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/arunprakash/opt/anaconda3/envs/gpt/lib/python3.11/site-packages (from requests->huggingface_hub<0.18,>=0.16.4->tokenizers>=0.13.0->anthropic) (1.26.18)
Installing collected packages: anthropic
Attempting uninstall: anthropic
Found existing installation: anthropic 0.16.0
Uninstalling anthropic-0.16.0:
Successfully uninstalled anthropic-0.16.0
Successfully installed anthropic-0.21.0
def construct_tool_use_system_prompt(tools): tool_use_system_prompt = ("In this environment you have access to a set of tools you can use to answer the user's question.\n""\n""You may call them like this:\n""<function_calls>\n""<invoke>\n""<tool_name>$TOOL_NAME</tool_name>\n""<parameters>\n""<$PARAMETER_NAME>$PARAMETER_VALUE</$PARAMETER_NAME>\n""...\n""</parameters>\n""</invoke>\n""</function_calls>\n""\n""Here are the tools available:\n""<tools>\n"+'\n'.join([tool for tool in tools]) +"\n</tools>" )return tool_use_system_prompt
def extract_between_tags(tag: str, string: str, strip: bool=False) ->list[str]: ext_list = re.findall(f"<{tag}>(.+?)</{tag}>", string, re.DOTALL)if strip: ext_list = [e.strip() for e in ext_list]return ext_list
Most large language models (LLMs) struggle with string reversal tasks because they are primarily trained on natural language data and lack explicit programming knowledge.
def reverse_string(string):return string[::-1]
tool_name ="reverse_string"tool_description ="Reverses the provided string."parameters = [ {"name": "string","type": "str","description": "The string to be reversed." }]tool = construct_format_tool_for_claude_prompt(tool_name, tool_description, parameters)system_prompt = construct_tool_use_system_prompt([tool])
print(tool)
<tool_description>
<tool_name>reverse_string</tool_name>
<description>
Reverses the provided string.
</description>
<parameters>
<parameter>
<name>string</name>
<type>str</type>
<description>The string to be reversed.</description>
</parameter>
</parameters>
</tool_description>
print(system_prompt)
In this environment you have access to a set of tools you can use to answer the user's question.
You may call them like this:
<function_calls>
<invoke>
<tool_name>$TOOL_NAME</tool_name>
<parameters>
<$PARAMETER_NAME>$PARAMETER_VALUE</$PARAMETER_NAME>
...
</parameters>
</invoke>
</function_calls>
Here are the tools available:
<tools>
<tool_description>
<tool_name>reverse_string</tool_name>
<description>
Reverses the provided string.
</description>
<parameters>
<parameter>
<name>string</name>
<type>str</type>
<description>The string to be reversed.</description>
</parameter>
</parameters>
</tool_description>
</tools>
reverse_message = {"role": "user","content": "Reverse the string encyclopediachatgpt"}
Okay, let's reverse the string "encyclopediachatgpt" using the reverse_string tool:
<function_calls>
<invoke>
<tool_name>reverse_string</tool_name>
<parameters>
<string>encyclopediachatgpt</string>
</parameters>
</invoke>
</function_calls><function_results>
<result>
<tool_name>reverse_string</tool_name>
<stdout>
tpgtahcaidepolcycne
</stdout>
</result>
</function_results>
"encyclopediachatgpt" reversed is "tpgtahcaidepolcycne".
print(final_message)
"encyclopediachatgpt" reversed is "tpgtahcaidepolcycne".
Another Approach
Tools
Anthropic Tools Installation
Function calling is a technique that allows you to extend the capabilities of a language model like Claude by integrating external functions or APIs. It enables the model to perform tasks or access information that it wouldn’t be able to do on its own.
The process involves defining a set of tools (functions) that Claude can use, and then providing a structured prompt format that guides Claude on how to use these tools. The prompt format includes a description of each tool, its parameters, and how to call it.
When Claude receives a user’s message, it analyzes the message and determines if it needs to use any of the available tools to provide a response. If it does, it generates a special message (called a “tool_inputs” message) that specifies which tool it wants to use and what arguments it wants to pass to that tool.
At this point, there are two modes of operation: automatic and manual.
Automatic Mode (execution_mode=‘automatic’):
In automatic mode, the tool use process is handled automatically by the ToolUser class.
When Claude generates a “tool_inputs” message, the ToolUser automatically extracts the specified tool and its arguments, calls the corresponding function, and generates a “tool_outputs” message with the result.
This process continues until Claude generates a response that doesn’t require any tool use (i.e., a regular “assistant” message).
Automatic mode is simpler to use but provides less control over the tool use process.
Manual Mode (execution_mode=‘manual’):
In manual mode, you have more control over the tool use process.
When Claude generates a “tool_inputs” message, the ToolUser stops and returns this message to you.
You are then responsible for extracting the specified tool and its arguments, calling the corresponding function, and generating a “tool_outputs” message with the result.
You pass this “tool_outputs” message back to the ToolUser, which sends it to Claude.
This process continues, with you manually handling each “tool_inputs” message, until Claude generates a regular “assistant” message.
Manual mode requires more code to handle the back-and-forth between Claude and the tools, but it allows you to add your own validation logic, custom error handling, and more.
Regardless of the mode, the key idea is that Claude can ask to use external tools when it needs them, and the ToolUser facilitates the communication between Claude and these tools.
This function calling technique allows you to leverage the language understanding and generation capabilities of Claude, while augmenting it with additional capabilities provided by external functions. It’s a powerful way to create more capable and versatile AI systems.
Some common use cases for function calling include: - Performing complex calculations or data transformations - Retrieving real-time data from APIs - Accessing databases or knowledge bases - Executing actions in external systems
class StringReversalTool(BaseTool):"""Reverses a given string."""def use_tool(self, string: str):"""Reverses the characters in the given string.""" reversed_string = string[::-1]return {"reversed_string": reversed_string}
tool_name ="reverse_string"tool_description ="""The reverse_string tool will return the reversed version of the given string."""tool_parameters = [ {"name": "string", "type": "str", "description": "The string to be reversed."}]
# Pass the tool instance into the ToolUsertool_user = ToolUser([string_reversal_tool])
# Call the tool_user with a prompt to get a version of Claude that can use your tools!messages = [{"role": "user", "content": "Reverse the string 'encyclopeida'"}]print(tool_user.use_tools(messages, execution_mode='automatic'))
So the reversed string is:
adiepolcycne
Tools with Execution Mode: Manual
# Define the function to handle manual Claude responsesdef handle_manual_claude_res(messages, claude_res, tool_user):""" - messages does not include claude_res - tool_user should be the ToolUser instance you have been using for previous messages """# Append Claude's response to messages. messages.append(claude_res)if claude_res['role'] =="assistant":# If the message is not trying to use a tool we should not automatically respond to Claude, and instead we should ask the user for input.return {"next_action": "user_input", "messages": messages}elif claude_res['role'] =="tool_inputs":# If the message is trying to use a tool we should parse the tool and arguments, use the tool, create the tool_outputs message with the results, and append that message to messages. tool_outputs = []for tool_input in claude_res['tool_inputs']: tool =next((t for t in tool_user.tools if t.name == tool_input['tool_name']), None)if tool isNone: messages.append({"role": "tool_outputs", "tool_outputs": None, "tool_error": f"No tool named <tool_name>{tool_input['tool_name']}</tool_name> available."})return {"next_action": "auto_respond", "messages": messages} tool_result = tool.use_tool(**tool_input['tool_arguments']) tool_outputs.append({"tool_name": tool_input['tool_name'], "tool_result": tool_result}) messages.append({"role": "tool_outputs", "tool_outputs": tool_outputs, "tool_error": None})return {"next_action": "auto_respond", "messages": messages}else:raiseValueError(f"Provided role should be assistant or tool_inputs, got {claude_res['role']}")
# Call the tool_user with a prompt to get a version of Claude that can use your tools!user_message = {"role": "user", "content": "Reverse the string 'encyclopeida'"}messages = [user_message]
So the reversed string is "adiepolcycne".
Therefore, the string "encyclopeida" reversed is "adiepolcycne".
In this updated code:
We define the handle_manual_claude_res function to process Claude’s responses in manual mode. This function checks the role of Claude’s response and determines the next action accordingly.
We start a loop that continues until Claude returns a response with the role ‘assistant’, indicating that it has completed the task.
Inside the loop, we call tool_user.use_tools with execution_mode='manual' to get Claude’s response.
We pass Claude’s response to the handle_manual_claude_res function, which processes the response and returns the next action and updated messages.
If the next action is ‘user_input’, we print Claude’s response and break the loop, as the task is complete.
If the next action is ‘auto_respond’, we continue the loop to process the next response from Claude.