选择工具
工具使用支持一个名为 tool_choice
的参数,该参数允许您指定希望 Claude 如何调用工具。在本笔记本中,我们将探讨它的工作原理以及何时使用它。在继续之前,请确保您已熟悉 Claude 工具使用的基础知识。
在使用 tool_choice
参数时,我们有三种可能的选项:
auto
允许 Claude 决定是否调用任何提供的工具tool
允许我们强制 Claude 始终使用特定工具any
告诉 Claude 必须使用提供的工具之一,但不强制使用特定工具
让我们详细了解每个选项。我们将从导入 Anthropic SDK 开始:
from anthropic import Anthropic
client = Anthropic()
MODEL_NAME = "claude-3-sonnet-20240229"
自动
将 tool_choice
设置为 auto
允许模型自动决定是否使用工具。这是使用工具时的默认行为。
为了演示这一点,我们将为 Claude 提供一个假的网络搜索工具。我们将向 Claude 提问,其中一些问题需要调用网络搜索工具,而另一些问题 Claude 应该能够自行回答。
让我们从定义一个名为 web_search
的工具开始。请注意,为了使此演示保持简单,我们实际上并没有在这里搜索网络:
def web_search(topic):
print(f"pretending to search the web for {topic}")
web_search_tool = {
"name": "web_search",
"description": "A tool to retrieve up to date information on a given topic by searching the web",
"input_schema": {
"type": "object",
"properties": {
"topic": {
"type": "string",
"description": "The topic to search the web for"
},
},
"required": ["topic"]
}
}
接下来,我们编写一个接受 user_query
并将其传递给 Claude 以及 web_search_tool
的函数。
我们还将 tool_choice
设置为 auto
:
tool_choice={"type": "auto"}
这是完整的函数:
from datetime import date
def chat_with_web_search(user_query):
messages = [{"role": "user", "content": user_query}]
system_prompt=f"""
Answer as many questions as you can using your existing knowledge.
Only search the web for queries that you can not confidently answer.
Today's date is {date.today().strftime("%B %d %Y")}
If you think a user's question involves something in the future that hasn't happened yet, use the search tool.
"""
response = client.messages.create(
system=system_prompt,
model=MODEL_NAME,
messages=messages,
max_tokens=1000,
tool_choice={"type": "auto"},
tools=[web_search_tool]
)
last_content_block = response.content[-1]
if last_content_block.type == "text":
print("Claude did NOT call a tool")
print(f"Assistant: {last_content_block.text}")
elif last_content_block.type == "tool_use":
print("Claude wants to use a tool")
print(last_content_block)
让我们从一个 Claude 应该能够回答而无需使用工具的问题开始:
chat_with_web_search("What color is the sky?")
Claude did NOT call a tool
Assistant: The sky appears blue during the day. This is because the Earth's atmosphere scatters more blue light from the sun than other colors, making the sky look blue.
当我们问“天空是什么颜色?”时,Claude 不会使用该工具。让我们尝试问一些 Claude 应该使用网络搜索工具来回答的问题:
chat_with_web_search("Who won the 2024 Miami Grand Prix?")
Claude wants to use a tool
ToolUseBlock(id='toolu_staging_018nwaaRebX33pHqoZZXDaSw', input={'topic': '2024 Miami Grand Prix winner'}, name='web_search', type='tool_use')
当我们问“谁赢得了 2024 年迈阿密大奖赛?”时,Claude 使用了网络搜索工具!
让我们再尝试几个例子:
# Claude 不应该需要为此使用该工具:
chat_with_web_search("Who won the superbowl in 2022?")
Claude did NOT call a tool
Assistant: The Los Angeles Rams won Super Bowl LVI in 2022, defeating the Cincinnati Bengals by a score of 23-20. The game was played on February 13, 2022 at SoFi Stadium in Inglewood, California.
# Claude 应该使用该工具来回答这个问题:
chat_with_web_search("Who won the superbowl in 2024?")
Claude wants to use a tool
ToolUseBlock(id='toolu_staging_016XPwcprHAgYJBtN7A3jLhb', input={'topic': '2024 Super Bowl winner'}, name='web_search', type='tool_use')
您的提示很重要!
在使用 auto
的 tool_choice
时,花时间编写详细的提示非常重要。通常,Claude 可能会过于热衷于调用工具。编写详细的提示有助于 Claude 确定何时调用工具以及何时不调用。在上面的示例中,我们在系统提示中包含了一些具体说明:
system_prompt=f"""
Answer as many questions as you can using your existing knowledge.
Only search the web for queries that you can not confidently answer.
Today's date is {date.today().strftime("%B %d %Y")}
If you think a user's question involves something in the future that hasn't happened yet, use the search tool.
"""
强制使用特定工具
我们可以使用 tool_choice
强制 Claude 使用特定工具。在下面的示例中,我们定义了两个简单的工具:
print_sentiment_scores
- 一个“欺骗” Claude 生成包含情感分析数据的结构化 JSON 输出的工具。有关此方法的更多信息,请参阅 使用 Claude 和工具使用提取结构化 JSONcalculator
- 一个非常简单的计算器工具,它接受两个数字并将它们相加
tools = [
{
"name": "print_sentiment_scores",
"description": "Prints the sentiment scores of a given tweet or piece of text.",
"input_schema": {
"type": "object",
"properties": {
"positive_score": {"type": "number", "description": "The positive sentiment score, ranging from 0.0 to 1.0."},
"negative_score": {"type": "number", "description": "The negative sentiment score, ranging from 0.0 to 1.0."},
"neutral_score": {"type": "number", "description": "The neutral sentiment score, ranging from 0.0 to 1.0."}
},
"required": ["positive_score", "negative_score", "neutral_score"]
}
},
{
"name": "calculator",
"description": "Adds two number",
"input_schema": {
"type": "object",
"properties": {
"num1": {"type": "number", "description": "first number to add"},
"num2": {"type": "number", "description": "second number to add"},
},
"required": ["num1", "num2"]
}
}
]
我们的目标是编写一个名为 analyze_tweet_sentiment
的函数,该函数接受一条推文并打印该推文的基本情感分析。最终,我们将“强制” Claude 使用我们的情感分析工具,但我们首先展示当不强制使用工具时会发生什么。
在 analyze_tweet_sentiment
函数的第一个“错误”版本中,我们为 Claude 提供了两个工具。为了进行比较,我们将首先将 tool_choice
设置为“auto”:
tool_choice={"type": "auto"}
请注意,我们故意没有为 Claude 提供编写良好的提示,以便更容易看到强制使用特定工具的影响。
def analyze_tweet_sentiment(query):
response = client.messages.create(
model=MODEL_NAME,
max_tokens=4096,
tools=tools,
tool_choice={"type": "auto"},
messages=[{"role": "user", "content": query}]
)
print(response)
让我们看看当使用推文“Holy cow, I just made the most incredible meal!”调用该函数时会发生什么。
analyze_tweet_sentiment("Holy cow, I just made the most incredible meal!")
ToolsBetaMessage(id='msg_staging_01ApgXx7W7qsDugdaRWh6p21', content=[TextBlock(text="That's great to hear! I don't actually have the capability to assess sentiment from text, but it sounds like you're really excited and proud of the incredible meal you made. Cooking something delicious that you're proud of can definitely give a sense of accomplishment and happiness. Well done on creating such an amazing dish!", type='text')], model='claude-3-sonnet-20240229', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=429, output_tokens=69))
Claude 没有调用我们的情感分析工具:
“That's great to hear! I don't actually have the capability to assess sentiment from text, but it sounds like you're really excited and proud of the incredible meal you made”
接下来,让我们想象有人发了这样的推文:“I love my cats! I had four and just adopted 2 more! Guess how many I have now?”
analyze_tweet_sentiment("I love my cats! I had four and just adopted 2 more! Guess how many I have now?")
ToolsBetaMessage(id='msg_staging_018gTrwrx6YwBR2jjhdPooVg', content=[TextBlock(text="That's wonderful that you love your cats and adopted two more! To figure out how many cats you have now, I can use the calculator tool:", type='text'), ToolUseBlock(id='toolu_staging_01RFker5oMQoY6jErz5prmZg', input={'num1': 4, 'num2': 2}, name='calculator', type='tool_use')], model='claude-3-sonnet-20240229', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=442, output_tokens=101))
Claude 想要调用计算器工具:
ToolUseBlock(id='toolu_staging_01RFker5oMQoY6jErz5prmZg', input={'num1': 4, 'num2': 2}, name='calculator', type='tool_use')
显然,当前实现 не 达到我们的预期(主要是因为我们将其设置为失败)。
接下来,让我们通过更新 tool_choice
来强制 Claude 始终使用 print_sentiment_scores
工具:
tool_choice={"type": "tool", "name": "print_sentiment_scores"}
除了将 type
设置为 tool
之外,我们还必须提供特定的工具名称。
def analyze_tweet_sentiment(query):
response = client.messages.create(
model=MODEL_NAME,
max_tokens=4096,
tools=tools,
tool_choice={"type": "tool", "name": "print_sentiment_scores"},
messages=[{"role": "user", "content": query}]
)
print(response)
现在,如果我们尝试使用相同的提示从早期提示 Claude,它将始终调用 print_sentiment_scores
工具:
analyze_tweet_sentiment("Holy cow, I just made the most incredible meal!")
ToolsBetaMessage(id='msg_staging_018GtYk8Xvee3w8Eeh6pbgoq', content=[ToolUseBlock(id='toolu_staging_01FMRQ9pZniZqFUGQwTcFU4N', input={'positive_score': 0.9, 'negative_score': 0.0, 'neutral_score': 0.1}, name='print_sentiment_scores', type='tool_use')], model='claude-3-sonnet-20240229', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=527, output_tokens=79))
Claude 调用我们的 print_sentiment_scores
工具:
ToolUseBlock(id='toolu_staging_01FMRQ9pZniZqFUGQwTcFU4N', input={'positive_score': 0.9, 'negative_score': 0.0, 'neutral_score': 0.1}, name='print_sentiment_scores', type='tool_use')
即使我们尝试用“数学味”的推文来迷惑 Claude,它仍然总是调用 print_sentiment_scores
工具:
analyze_tweet_sentiment("I love my cats! I had four and just adopted 2 more! Guess how many I have now?")
ToolsBetaMessage(id='msg_staging_01RACamfrHdpvLxWaNwDfZEF', content=[ToolUseBlock(id='toolu_staging_01Wb6ZKSwKvqVSKLDAte9cKU', input={'positive_score': 0.8, 'negative_score': 0.0, 'neutral_score': 0.2}, name='print_sentiment_scores', type='tool_use')], model='claude-3-sonnet-20240229', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=540, output_tokens=79))
即使我们强制 Claude 调用我们的 print_sentiment_scores
工具,我们仍然应该采用一些基本的提示工程:
def analyze_tweet_sentiment(query):
prompt = f"""
Analyze the sentiment in the following tweet:
<tweet>{query}</tweet>
"""
response = client.messages.create(
model=MODEL_NAME,
max_tokens=4096,
tools=tools,
tool_choice={"type": "auto"},
messages=[{"role": "user", "content": prompt}]
)
print(response)
Any
tool_choice
的最后一个选项是 any
,它允许我们告诉 Claude “你必须调用一个工具,但你可以选择哪一个”。想象一下我们想创建一个使用 Claude 的短信聊天机器人。这个聊天机器人实际与用户“交流”的唯一方法是通过短信。
在下面的示例中,我们创建了一个非常简单的文本消息助手,它可以使用两个工具:
send_text_to_user
向用户发送短信get_customer_info
根据用户名查找客户数据
目的是创建一个聊天机器人,该机器人始终调用其中一个工具,并且从不以非工具响应进行回复。在所有情况下,Claude 都应该通过尝试发送短信或调用 get_customer_info
来获取更多客户信息来响应。
最重要的是,我们将 tool_choice
设置为“any”:
tool_choice={"type": "any"}
def send_text_to_user(text):
# Sends a text to the user
# We'll just print out the text to keep things simple:
print(f"TEXT MESSAGE SENT: {text}")
def get_customer_info(username):
return {
"username": username,
"email": f"{username}@email.com",
"purchases": [
{"id": 1, "product": "computer mouse"},
{"id": 2, "product": "screen protector"},
{"id": 3, "product": "usb charging cable"},
]
}
tools = [
{
"name": "send_text_to_user",
"description": "Sends a text message to a user",
"input_schema": {
"type": "object",
"properties": {
"text": {"type": "string", "description": "The piece of text to be sent to the user via text message"},
},
"required": ["text"]
}
},
{
"name": "get_customer_info",
"description": "gets information on a customer based on the customer's username. Response includes email, username, and previous purchases. Only call this tool once a user has provided you with their username",
"input_schema": {
"type": "object",
"properties": {
"username": {"type": "string", "description": "The username of the user in question. "},
},
"required": ["username"]
}
},
]
system_prompt = """
All your communication with a user is done via text message.
Only call tools when you have enough information to accurately call them.
Do not call the get_customer_info tool until a user has provided you with their username. This is important.
If you do not know a user's username, simply ask a user for their username.
"""
def sms_chatbot(user_message):
messages = [{"role": "user", "content":user_message}]
response = client.messages.create(
system=system_prompt,
model=MODEL_NAME,
max_tokens=4096,
tools=tools,
tool_choice={"type": "any"},
messages=messages
)
if response.stop_reason == "tool_use":
last_content_block = response.content[-1]
if last_content_block.type == 'tool_use':
tool_name = last_content_block.name
tool_inputs = last_content_block.input
print(f"=======Claude Wants To Call The {tool_name} Tool=======")
if tool_name == "send_text_to_user":
send_text_to_user(tool_inputs["text"])
elif tool_name == "get_customer_info":
print(get_customer_info(tool_inputs["username"]))
else:
print("Oh dear, that tool doesn't exist!")
else:
print("No tool was called. This shouldn't happen!")
让我们从简单的开始:
sms_chatbot("Hey there! How are you?")
=======Claude Wants To Call The send_text_to_user Tool=======
TEXT MESSAGE SENT: Hello! I'm doing well, thanks for asking. How can I assist you today?
Claude 回复调用 send_text_to_user
工具。
接下来,我们将问 Claude 一个更棘手的问题:
sms_chatbot("I need help looking up an order")
=======Claude Wants To Call The send_text_to_user Tool=======
TEXT MESSAGE SENT: Hi there, to look up your order details I'll need your username first. Can you please provide me with your username?
Claude 想发送一条短信,要求用户提供用户名。
现在,让我们看看当我们向 Claude 提供用户名时会发生什么:
sms_chatbot("I need help looking up an order. My username is jenny76")
=======Claude Wants To Call The get_customer_info Tool=======
{'username': 'jenny76', 'email': 'jenny76@email.com', 'purchases': [{'id': 1, 'product': 'computer mouse'}, {'id': 2, 'product': 'screen protector'}, {'id': 3, 'product': 'usb charging cable'}]}
正如我们所希望的,Claude 调用了 get_customer_info
工具!
即使我们向 Claude 发送乱码消息,它仍然会调用我们的一个工具:
sms_chatbot("askdj aksjdh asjkdbhas kjdhas 1+1 ajsdh")
=======Claude Wants To Call The send_text_to_user Tool=======
TEXT MESSAGE SENT: I'm afraid I didn't understand your query. Could you please rephrase what you need help with?