[アップデート] Amazon Nova の Tool Use で利用する ToolChoice オプションに Any と Tool モードが追加されました

[アップデート] Amazon Nova の Tool Use で利用する ToolChoice オプションに Any と Tool モードが追加されました

Clock Icon2025.03.20

こんにちは!クラウド事業本部コンサルティング部のたかくに(@takakuni_)です。

Amazon Nova の Tool Use で利用する ToolChoice オプションに Any と Tool モードが追加されました。

https://aws.amazon.com/about-aws/whats-new/2025/03/amazon-nova-expands-tool-converse-api/

ToolChoice

ToolChoice は Tool Use で利用可能なオプションの 1 つです。

Tool Use は、「選択肢に機能A, 機能B があり、ユーザーからは XX と指示が来ています。どの機能をどういったパラメーターで利用すればいいですか?」といった質問に、「機能A を YY, ZZ で呼び出してください。」といった回答が期待できる機能です。Tool Use は Function Calling といった呼び方もされます。

ToolChoice は、Tool Use の回答をどのように回答させるか変化させるオプションです。モードは以下の 3 つが提供されています。

  • Auto
    • デフォルトの設定値
    • Tool Use の回答が可能であれば、関数名、パラメータを回答
    • Tool Use の回答が難しい場合は、ユーザーに追加情報を求めるプロンプトを生成
  • Any
    • Tool Use 内で定義した関数の中のどれかで、関数名、パラメータを回答
    • ユーザーに追加情報を求めるプロンプトを生成しない
  • Tool
    • 開発者が指定した特定のツールのパラメータを回答させる

https://docs.aws.amazon.com/ja_jp/bedrock/latest/APIReference/API_runtime_ToolChoice.html

アップデート内容

今まで Amazon Nova では Tool use は使えたものの、ToolChoice オプションは Auto モードのみサポートしていました。

システム間でアプリケーション連携している場合、Auto モードで利用していると、ユーザーに追加情報を求めるプロンプトを生成されてもスキーマが合わず、エラーになるケースが考えられます。

今回のアップデートではこのようなニーズに応じて、Any と Tool モードもサポートされました。というものです。

やってみた

それでは実際に ToolChoice オプションを利用し、挙動の違いを確認してみます。

Auto

まずは、 Auto から試します。

input_text = "I want to all products" と定義し、get_all_products を呼び出すような書き方にします。

import json
import boto3

client = boto3.client("bedrock-runtime", region_name="us-west-2")

tool_config = {
    "toolChoice": {
        "auto": {}
    },
    "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"]
                    }
                }
            }
        }
    ]
}

input_text = "I want to all products"

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

inf_params = {"maxTokens": 1000, "topP": 1, "temperature": 1}

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

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

for block in response["output"]["message"]["content"]:
    if "toolUse" in block:
        print("\n[Tool Use]")
        print(json.dumps(block["toolUse"], indent=2))

予想通り、get_all_products を呼ぶ出すようレスポンスが返ってきていますね。

(nova-toolchoice-py3.12) takakuni@ nova-toolchoice % python main.py
[Full Response]
{
  "ResponseMetadata": {
    "RequestId": "8ee12e32-3273-4e30-a117-097376499274",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "date": "Thu, 20 Mar 2025 12:12:50 GMT",
      "content-type": "application/json",
      "content-length": "511",
      "connection": "keep-alive",
      "x-amzn-requestid": "8ee12e32-3273-4e30-a117-097376499274"
    },
    "RetryAttempts": 0
  },
  "output": {
    "message": {
      "role": "assistant",
      "content": [
        {
          "text": "<thinking>The User wants to retrieve all the products. I can use the `get_all_products` tool to retrieve the products. I will use the default sorting options for this request.</thinking>\n"
        },
        {
          "toolUse": {
            "toolUseId": "tooluse_s89BifZpTIm5Qv42EU9keQ",
            "name": "get_all_products",
            "input": {
              "sort_by": "created_date",
              "sort_order": "desc"
            }
          }
        }
      ]
    }
  },
  "stopReason": "tool_use",
  "usage": {
    "inputTokens": 524,
    "outputTokens": 98,
    "totalTokens": 622
  },
  "metrics": {
    "latencyMs": 765
  }
}

[Tool Use]
{
  "toolUseId": "tooluse_s89BifZpTIm5Qv42EU9keQ",
  "name": "get_all_products",
  "input": {
    "sort_by": "created_date",
    "sort_order": "desc"
  }
}

続いて、どちらの tool にも当てはまらないような文章にしてみます。

import json
import boto3

client = boto3.client("bedrock-runtime", region_name="us-west-2")

tool_config = {
    "toolChoice": {
        "auto": {}
    },
    "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"]
                    }
                }
            }
        }
    ]
}

+ input_text = "Hi, Nova! How do you do?"
- input_text = "I want to all products"

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

inf_params = {"maxTokens": 1000, "topP": 1, "temperature": 1}

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

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

for block in response["output"]["message"]["content"]:
    if "toolUse" in block:
        print("\n[Tool Use]")
        print(json.dumps(block["toolUse"], indent=2))

Tool Use のレスポンスは行われず、thinking タグの後に、追加の補足情報を求めてきています。

(nova-toolchoice-py3.12) takakuni@ nova-toolchoice % python main.py
[Full Response]
{
  "ResponseMetadata": {
    "RequestId": "69100d70-5c5d-438c-854c-3eecf45dd058",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "date": "Thu, 20 Mar 2025 12:14:56 GMT",
      "content-type": "application/json",
      "content-length": "384",
      "connection": "keep-alive",
      "x-amzn-requestid": "69100d70-5c5d-438c-854c-3eecf45dd058"
    },
    "RetryAttempts": 0
  },
  "output": {
    "message": {
      "role": "assistant",
      "content": [
        {
          "text": "<thinking>The User is greeting and asking how I am doing. I should respond politely and acknowledge the greeting.</thinking>\nHi there! I'm doing well, thank you for asking. How can I assist you today?"
        }
      ]
    }
  },
  "stopReason": "end_turn",
  "usage": {
    "inputTokens": 528,
    "outputTokens": 46,
    "totalTokens": 574
  },
  "metrics": {
    "latencyMs": 525
  }
}

Any

続いて、 Any モードに切り替えます。 Any は tools 内のどちらかで Tool Use を返すモードです。

import json
import boto3

client = boto3.client("bedrock-runtime", region_name="us-west-2")

tool_config = {
    "toolChoice": {
+        "any": {}
-        "auto": {}
    },
    "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"]
                    }
                }
            }
        }
    ]
}

input_text = "Hi, Nova! How do you do?"

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

inf_params = {"maxTokens": 1000, "topP": 1, "temperature": 1}

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

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

for block in response["output"]["message"]["content"]:
    if "toolUse" in block:
        print("\n[Tool Use]")
        print(json.dumps(block["toolUse"], indent=2))

ただ、返ってきた結果は auto 同様、Tool Use のレスポンスは行われないような形になりました。

(nova-toolchoice-py3.12) takakuni@ nova-toolchoice % python main.py
[Full Response]
{
  "ResponseMetadata": {
    "RequestId": "4b178dfd-e0f0-4cf2-a018-d372a0a49535",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "date": "Thu, 20 Mar 2025 12:23:01 GMT",
      "content-type": "application/json",
      "content-length": "317",
      "connection": "keep-alive",
      "x-amzn-requestid": "4b178dfd-e0f0-4cf2-a018-d372a0a49535"
    },
    "RetryAttempts": 0
  },
  "output": {
    "message": {
      "role": "assistant",
      "content": [
        {
          "text": "\nHello! I'm doing well, thank you for asking. How can I assist you today? If you have any questions about products, feel free to ask!"
        }
      ]
    }
  },
  "stopReason": "end_turn",
  "usage": {
    "inputTokens": 533,
    "outputTokens": 37,
    "totalTokens": 570
  },
  "metrics": {
    "latencyMs": 383
  }
}

時折、thinking タグも発生するため、今の所、アップデートされていない可能性がありそうです。

(nova-toolchoice-py3.12) takakuni@ nova-toolchoice % python main.py
[Full Response]
{
  "ResponseMetadata": {
    "RequestId": "fe806ea2-efc0-450c-9d87-889519370893",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "date": "Thu, 20 Mar 2025 12:24:56 GMT",
      "content-type": "application/json",
      "content-length": "501",
      "connection": "keep-alive",
      "x-amzn-requestid": "fe806ea2-efc0-450c-9d87-889519370893"
    },
    "RetryAttempts": 0
  },
  "output": {
    "message": {
      "role": "assistant",
      "content": [
        {
          "text": "\n<thinking> The user is greeting me, but there is no specific request or question. I should respond politely and ask if there's anything specific they need help with. </thinking>\nHi there! I'm doing well, thank you. How can I assist you today? If you have any questions or need help with something, feel free to ask!"
        }
      ]
    }
  },
  "stopReason": "end_turn",
  "usage": {
    "inputTokens": 533,
    "outputTokens": 77,
    "totalTokens": 610
  },
  "metrics": {
    "latencyMs": 745
  }
}

Tool

最後に、特定の Tool の利用を指定する Tool モードです。

"Hi, Nova! How do you do?" のテキストに対して、get_products_by_id を強制するよう指定しています。どのような結果が返ってくるのでしょう。

import json
import boto3

client = boto3.client("bedrock-runtime", region_name="us-west-2")

tool_config = {
    "toolChoice": {
+        "tool": { "name" : "get_products_by_id"}
-        "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"]
                    }
                }
            }
        }
    ]
}

input_text = "Hi, Nova! How do you do?"

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

inf_params = {"maxTokens": 1000, "topP": 1, "temperature": 1}

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

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

for block in response["output"]["message"]["content"]:
    if "toolUse" in block:
        print("\n[Tool Use]")
        print(json.dumps(block["toolUse"], indent=2))

Tool Use が発生し、架空の Product ID で指示がきていますね。

(nova-toolchoice-py3.12) takakuni@ nova-toolchoice % python main.py
[Full Response]
{
  "ResponseMetadata": {
    "RequestId": "ab1bb737-827e-4656-be5e-1e39ea83bf0a",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "date": "Thu, 20 Mar 2025 12:28:58 GMT",
      "content-type": "application/json",
      "content-length": "283",
      "connection": "keep-alive",
      "x-amzn-requestid": "ab1bb737-827e-4656-be5e-1e39ea83bf0a"
    },
    "RetryAttempts": 0
  },
  "output": {
    "message": {
      "role": "assistant",
      "content": [
        {
          "toolUse": {
            "toolUseId": "tooluse_rmXBBBAuQvyYhnAuzqBOEA",
            "name": "get_products_by_id",
            "input": {
              "product_id": 1
            }
          }
        }
      ]
    }
  },
  "stopReason": "tool_use",
  "usage": {
    "inputTokens": 541,
    "outputTokens": 18,
    "totalTokens": 559
  },
  "metrics": {
    "latencyMs": 311
  }
}

[Tool Use]
{
  "toolUseId": "tooluse_rmXBBBAuQvyYhnAuzqBOEA",
  "name": "get_products_by_id",
  "input": {
    "product_id": 1
  }
}

まとめ

以上、「Amazon Nova の Tool Use で利用する ToolChoice オプションに Any と Tool モードが追加されました。」でした。

システム間の連携でスキーマを強制させたいケースでは確かに必要な機能だと思いました。

このブログがどなたかの参考になれば幸いです。

クラウド事業本部コンサルティング部のたかくに(@takakuni_)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.