

# Tool use (function calling) with Amazon Nova
<a name="tool-use"></a>

**Note**  
This documentation is for Amazon Nova Version 1. For information about using tools with Amazon Nova 2, visit [Using tools (function calling)](https://docs.aws.amazon.com/nova/latest/nova2-userguide/using-tools.html).

Tools are a way to provide external functionality to Amazon Nova such as an API call or a code function. This section will cover how you can define and integrate with tools when working with Amazon Nova models.

Tool use involves three high level steps:
+ **User query** - You define the tools that Amazon Nova can use by providing a JSON schema that describes each tool's functionality and input requirements.
+ **Tool Selection** - When a user sends a message, Amazon Nova will analyze it to determine if a tool is necessary to generate a response. This is referred to as `Auto` tool choice. See [Choosing a tool](https://docs.aws.amazon.com/nova/latest/userguide/tool-choice.html) for more information. If Amazon Nova identifies a suitable tool, it will "call the tool" and return the name of the tool and the parameters to use.

  You, as the developer, are responsible for executing the tool based on the model's request. This means you need to write the code that invokes the tool's functionality and processes the input parameters provided by the model.
**Note**  
Like all LLM responses, it is possible for Amazon Nova to hallucinate a tool call. It is the responsibility of you, the developer, to validate that the tool exists, inputs are formatted correctly, and the appropriate permissions are already in place.
+ **Return Results** - After executing the tool, you must send the results back to Amazon Nova in a structured format. Valid formats include JSON or a combination of text and images. This allows Amazon Nova to incorporate the tool's output into the final response to the user.

  If there are any errors during the tool's execution, you can denote this in the tool response to Amazon Nova, allowing Amazon Nova to adjust its response accordingly.

Consider a simple example of a calculator tool:

------
#### [ User query ]

The first step in the tool calling workflow is the user query to Amazon Nova for the result of a math equation - 10 times 5. This query is sent as the prompt to Amazon Nova along with a tool specification that represents the calculator.

```
user_query = "10*5"

messages = [{
    "role": "user",
    "content": [{"text": user_query}]
}]

tool_config = {
    "tools": [
        {
            "toolSpec": {
                "name": "calculator", # Name of the tool
                "description": "A calculator tool that can execute a math equation", # Concise description of the tool
                "inputSchema": {
                    "json": { 
                        "type": "object",
                        "properties": {
                            "equation": { # The name of the parameter
                                "type": "string", # parameter type: string/int/etc
                                "description": "The full equation to evaluate" # Helpful description of the parameter
                            }
                        },
                        "required": [ # List of all required parameters
                            "equation"
                        ]
                    }
                }
            }
        }
    ]
}
```

------
#### [ Tool selection ]

Amazon Nova uses the context of the tool along with the user prompt to determine the necessary tool to use and the required configuration. This is returned as a part of the API response.

```
{
    "toolUse": {
        "toolUseId": "tooluse_u7XTryCSReawd9lXwljzHQ", 
        "name": "calculator", 
        "input": {
            "equation": "10*5"
         }
    }
}
```

The application is responsible for executing the tool and storing the result.

```
def calculator(equation: str):
    return eval(equation)
    
tool_result = calculator("10*5")
```

------
#### [ Return results ]

To return the result of the tool to Amazon Nova, the tool result is included in a new API request. Note that the tool use ID is consistent with the one returned from Amazon Nova in the previous response.

```
{ 
    "toolResult": {
        "toolUseId": "tooluse_u7XTryCSReawd9lXwljzHQ",
        "content": [
            {
                "json": {
                    "result": "50"
                }
            }
        ],
        "status": "success"
    }
}
```
+ Amazon Nova will use the full context of the messages, including the initial user query, the tool use, and tool result to determine the final response to the user. In this case, Amazon Nova will respond to the user that "10 times 5 is 50".

------

Amazon Nova allows tool use in both the Invoke and Converse API however, for full feature breadth we recommend using the [Converse API](https://docs.aws.amazon.com/bedrock/latest/userguide/tool-use-inference-call.html) and will be using examples with this API moving forward.

**Topics**
+ [Defining a tool](tool-use-definition.md)
+ [Invoking a tool](tool-use-invocation.md)
+ [Choosing a tool](tool-choice.md)
+ [Returning tool results](tool-use-results.md)
+ [Using built-in tools](tool-built-in.md)
+ [Reporting an error](tool-use-error.md)
+ [Additional references](#tool-use-resources)

# Defining a tool
<a name="tool-use-definition"></a>

A critical step in the tool calling workflow is defining the tool. The tool definition must include all of the necessary context to guide the model on when it is appropriate to invoke the tool.

To define a tool, create a tool configuration and pass it with the user message to the API. The [tool configuration](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolConfiguration.html) schema expects an array of tools and optionally a tool choice parameter.

**Note**  
Amazon Nova supports the `auto`, `any`, and `tool` options for `toolChoice`. For more information, see [ToolChoice](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolChoice.html) in the Amazon Bedrock API documentation and [Use a tool to complete an Amazon Bedrock model response](https://docs.aws.amazon.com/bedrock/latest/userguide/tool-use.html).

Here is an example of how to define a tool:

```
tool_config = {
    "tools": [
        {
            "toolSpec": {
                "name": "top_song",
                "description": "Get the most popular song played on a radio station.",
                "inputSchema": {
                    "json": {
                        "type": "object",
                        "properties": {
                            "sign": {
                                "type": "string",
                                "description": "The call sign for the radio station for which you want the most popular song. Example calls signs are WZPZ, and WKRP."
                            }
                        },
                        "required": [
                            "sign"
                        ]
                    }
                }
            }
        }
    ],
}
```

The name, description, and the input schema must be explicit with the exact functionality of the tool. Ensure any key differentiators for when to use the tool are reflected in the tool configuration.

**Note**  
Amazon Nova understanding models currently support only a subset of JsonSchema functionality when used to define the [ToolInputSchema](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolInputSchema.html) in Converse API.  
The top level schema must be of type [Object](https://json-schema.org/understanding-json-schema/reference/object).
Only three fields are supported in the top-level Object - type (must be set to ‘object’), [https://json-schema.org/understanding-json-schema/reference/object#properties](https://json-schema.org/understanding-json-schema/reference/object#properties), and [https://json-schema.org/understanding-json-schema/reference/object#required](https://json-schema.org/understanding-json-schema/reference/object#required).

For tool calling, we recommend setting the temperature to 0 to enable greedy decoding.

Here is an example of calling a tool with the Converse API:

```
import json
import boto3

client = boto3.client("bedrock-runtime", region_name="us-east-1")

input_text = "What is the most popular song on WZPZ?"

messages = [{
    "role": "user",
    "content": [{"text": input_text}]
}]

inf_params = {"maxTokens": 1000, "temperature": 0}

response = client.converse(
    modelId="us.amazon.nova-lite-v1:0",
    messages=messages,
    toolConfig=tool_config,
    inferenceConfig=inf_params
)

messages.append(response["output"]["message"])

# Pretty print the response JSON.
print("[Full Response]")
print(json.dumps(response, indent=2))

# Print the tool content for easy readability.
tool = next(
    block["toolUse"]
    for block in response["output"]["message"]["content"]
    if "toolUse" in block
)
print("\n[Tool Response]")
print(tool)
```

# Invoking a tool
<a name="tool-use-invocation"></a>

If Amazon Nova decides to call a tool, a tool use block will be returned as a part of the assistant message and the stop reason will be "tool\$1use". The tool block will contain the name of the tool and it's inputs.

**Note**  
To improve the accuracy of tool calls, the default behavior of Amazon Nova models is to use chain-of-thought reasoning for tool calling. The thought process will be made available to you in the assistant message and will be contained in <thinking> tags. It is possible to have multiple tool calls and thinking blocks in a response so your application should take this into account.  
If tool choice is configured to `any` or `tool`, this will override the chain-of-thought behavior and the response will only contain the necessary tool calls.

```
{
   "toolUse": 
    {
        "toolUseId": "tooluse_20Z9zl0BQWSXjFuLKdTJcA", 
        "name": "top_song", 
        "input": {
            "sign": "WZPZ"
        }
    }
}
```

To actually call the tool, the tool name and arguments can be extracted from the message and the application can then invoke it.

Here is an example for how you can process a tool call.

```
def get_top_song(sign):
    print(f"Getting the top song at {sign}")
    return ("Espresso", "Sabrina Carpenter")

stop_reason = response["stopReason"]

tool, song, artist = None, None, None
if stop_reason == "tool_use":
    thought_process = next(
        block["text"]
        for block in response["output"]["message"]["content"]
        if "text" in block
    )

    print(thought_process)

    tool = next(
        block["toolUse"]
        for block in response["output"]["message"]["content"]
        if "toolUse" in block
    )

    if tool["name"] == "top_song":
        song, artist = get_top_song(tool["input"]["sign"])
```

It is important to keep security in mind when you are defining and invoking tools. LLMs like Amazon Nova do not have access to the session details so permissions should be validated when necessary before invoking a tool. Rely on user details from your session instead of augmenting the prompt and allowing Amazon Nova to inject it into the tool call.

# Choosing a tool
<a name="tool-choice"></a>

Amazon Nova models support the functionality of *tool choice*. Tool choice allows you, as the developer, to control the manner in which a tool is called. There are three supported parameter options for tool choice: `tool`, `any`, and `auto`.
+ **Tool** - The specified tool will be called once.
+ **Any** - One of the provided tools will be called at least once.
+ **Auto** - The model will decide whether to call a tool and multiple tools will be called if required.

------
#### [ Tool ]

Using `tool` as the tool choice allows you to control the specific tool that the model calls. The example below highlights this with a structured output use case where the response is required to be formatted in a consistent manner.

```
tool_config = {
    "toolChoice": {
        "tool": { "name" : "extract_recipe"}
    },
    "tools": [
        {
            "toolSpec": {
                "name": "extract_recipe",
                "description": "Extract recipe for cooking instructions",
                "inputSchema": {
                    "json": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string",
                                "description": "Name of the recipe"
                            },
                            "description": {
                                "type": "string",
                                "description": "Brief description of the dish"
                            },
                            "ingredients": {
                                "type": "array",
                                "items": {
                                    "type": "string",
                                    "description": "Name of ingredient"
                                }
                            }
                        },
                        "required": ["name", "description", "ingredients"]
                    }
                }
            }
        }
    ]
}
```

------
#### [ Any ]

Using `any` as the tool choice allows you to ensure that at least one tool is called each time. While the decision of which tool to call is left up to the model, there will always be a tool returned. The example below highlights using tool choice any for an API selection endpoint use case. This is one example of when it is helpful to require the model to return a specific tool.

```
tool_config = {
    "toolChoice": {
        "any": {}
    },
    "tools": [
         {
            "toolSpec": {
                "name": "get_all_products",
                "description": "API to retrieve multiple products with filtering and pagination options",
                "inputSchema": {
                    "json": {
                        "type": "object",
                        "properties": {
                            "sort_by": {
                                "type": "string",
                                "description": "Field to sort results by. One of: price, name, created_date, popularity",
                                "default": "created_date"
                            },
                            "sort_order": {
                                "type": "string",
                                "description": "Order of sorting (ascending or descending). One of: asc, desc",
                                "default": "desc"
                            },
                        },
                        "required": []
                    }
                }
            }
        },
        {
            "toolSpec": {
                "name": "get_products_by_id",
                "description": "API to retrieve retail products based on search criteria",
                "inputSchema": {
                    "json": {
                        "type": "object",
                        "properties": {
                            "product_id": {
                                "type": "string",
                                "description": "Unique identifier of the product"
                            },
                        },
                        "required": ["product_id"]
                    }
                }
            }
        }
    ]
}
```

------
#### [ Auto ]

Using `auto` as the tool choice is the default functionality of the tool support and allows the model to decide when to call a tool and how many tools to call. This is the behavior if you don’t include tool choice in your request.

**Note**  
The default behavior of Amazon Nova tool calling is to use chain-of-thought for tool selection. When using the default behavior or tool choice `auto`, there will also be the thought process output in <thinking> tags.

The following example highlights a chatbot use case where you might want to allow the model to search the internet for recent information or to respond directly to the user. This tool choice provides flexibility and will leave the reasoning to the model.

```
tool_config = {
    "toolChoice": {
        "auto": {}
    },
    "tools": [
         {
            "toolSpec": {
                "name": "search",
                "description": "API that provides access to the internet",
                "inputSchema": {
                    "json": {
                        "type": "object",
                        "properties": {
                            "query": {
                                "type": "string",
                                "description": "Query to search by",
                            },
                        },
                        "required": ["query"]
                    }
                }
            }
        }
    ]
}
```

------

**Note**  
When setting the tool choice parameter, you might still see the model output text or perform sequential tool calls after the original tool selection. We recommend that you set a stop sequence here to limit the output to just the tool:  

```
“stopSequences”: [“</tool>”]
```
For more information, see [InferenceConfiguration](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent_InferenceConfiguration.html) in the Amazon Bedrock API guide.

# Returning tool results
<a name="tool-use-results"></a>

Once the tool has been invoked by the application, the final step is to provide the tool result to the model. This is done by returning a tool result with the ID of the tool call and the response content. This content follows the [ToolResultBlock](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolResultBlock.html) schema:

```
{
    "toolResult": {
        "toolUseId": tool['toolUseId'],
        "content": [{"json": {"song": song, "artist": artist}}],
        "status": "success"
    }
}
```

The contents of the `ToolResultBlock` should be either a single JSON or a mix of text and images.

The status field can be used to indicate to the model the status of the tool execution. If the tool execution failed you can indicate the failure, and Amazon Nova will attempt the modify its original tool call.

Refer to the [ToolResultContentBlock](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolResultContentBlock.html) documentation for more details on the schema.

Here is an example of how to use the Converse API to return the tool results:

```
messages.append({
    "role": "user",
    "content": [
        {
            "toolResult": {
                "toolUseId": tool['toolUseId'],
                "content": [{"json": {"song": song, "artist": artist}}],
                "status": "success"
            }
        }
    ]
})

inf_params = {"maxTokens": 1000, "temperature": 0}

# Send the tool result to the model.
response = client.converse(
    modelId="us.amazon.nova-lite-v1:0",
    messages=messages,
    toolConfig=tool_config,
    inferenceConfig=inf_params
)

print(response['output']['message'])
```

For more details on how to leverage tools refer to [Amazon Bedrock Tool Use](https://docs.aws.amazon.com/bedrock/latest/userguide/tool-use.html) documentation or visit the [tool use samples](https://github.com/aws-samples/amazon-nova-samples/blob/main/multimodal-understanding/repeatable-patterns/10-tool-calling-with-converse/10_tool_calling_with_converse.ipynb) in the Amazon Nova samples repository.

# Using built-in tools
<a name="tool-built-in"></a>

Built-in tools are fully managed tools that are available out of the box, with no need for custom implementation. These can be enabled in the Converse API with a simple toggle. 

## Code interpreter
<a name="code-interpreter"></a>

Code Interpreter allows Nova to securely execute Python code in isolated sandbox environments. This enables writing and executing code, analyzing data, creating visualizations, and solving mathematical problems. For example, Code Interpreter can be used to:
+ Generate financial reports based on uploaded data
+ Complete statistical analysis or algorithm simulations
+ Execute database migration scripts in isolated environments
+ Run unit tests for new generated code

Here’s an example of how to enable Code Interpreter with the Converse API:

```
{
  "messages": [
    {
      "role": "user",
      "content": [{"text":  "What is the average of 10, 24, 2, 3, 43, 52, 13, 68, 6, 7, 902, 82")}]
    }
  ],

"toolConfig": {
    "tools": [
        {
            "systemTool": {
                "name": "nova_code_interpreter"
            }
        }
    ]
},
```

In this case, the model will determine that the request requires computation so it generates the required Python code and calls the code interpreter tool. 

```
{
    "toolUse": {
        "input": {
            "code": "'''Calculate the average of the given numbers.'''\nnumbers = [10, 24, 2, 3, 43, 52, 13, 68, 6, 7, 902, 82]\nsum_numbers = sum(numbers)\ncount = len(numbers)\naverage = sum_numbers / count\n(sum_numbers, count, average)"
        },
        "name": "nova_code_interpreter",
        "toolUseId": "tooluse_WytfF0g1S5qUeEPm0ptOdQ",
        "type": "server_tool_use"
    }
},
```

The interpreter runs this code in a sandbox and captures the result, output in a standard schema:

```
{
  "stdOut": String,
  "stdErr": String,
  "exitCode": int,
  "isError": boolean
}
```

In this case, you would receive back:

```
{
    "toolResult": {
        "content": [
            {
                "text": "{\"stdOut\":\"(1212, 12, 101.0)\",\"stdErr\":\"\",\"exitCode\":0,\"isError\":false}"
            }
        ],
        "status": "success",
        "toolUseId": "tooluse_WytfF0g1S5qUeEPm0ptOdQ",
        "type": "nova_code_interpreter_result"
    }
}
```

## Model Context Protocol
<a name="w2aac51c28b7"></a>

The Model Context Protocol (MCP) is an open standard that enables developers to build secure, two-way connections between their data sources and AI-powered tools. Instead of writing custom adapters for each API or service, you can run an MCP server and let Nova discover its tools automatically through a client bridge. Once connected, Nova treats these tools like any other external integration: it decides when to call them, sends the required parameters, and incorporates the results into its response. 

# Reporting an error
<a name="tool-use-error"></a>

There are some instances where the parameters selected by Amazon Nova can cause an external error. It can be beneficial then to communicate this back to Amazon Nova so the request can be modified and retried. To notify about errors, still return a tool result but modify the status to report the error and share the exception message.

Here is an example that reports an error status message:

```
tool_result_message = {
    "role": "user",
    "content": [
        { 
            "toolResult": {
                "toolUseId": tool["toolUseId"],
                "content": [{"text": "A validation exception occured on field: sample.field"}],
                "status": "error"
            }
        }
    ]
}
```

## Additional references
<a name="tool-use-resources"></a>

1. [Use a tool to complete a model response](https://docs.aws.amazon.com/bedrock/latest/userguide/tool-use.html)

1. [Building AI agents with Amazon Nova](agents.md)

1. [Text understanding prompting best practices](prompting-text-understanding.md)

1. [Troubleshooting tool calls](prompting-tool-troubleshooting.md)