设置 #
运行以下设置单元以加载您的 API 密钥并建立 get_completion 辅助函数。
!pip install anthropic
# Import python's built-in regular expression library
import re
import anthropic
# Retrieve the API_KEY variable from the IPython store
%store -r API_KEY
client = anthropic.Anthropic(api_key=API_KEY)
# Rewrittten to call Claude 3 Sonnet, which is generally better at tool use, and include stop_sequences
def get_completion(messages, system_prompt="", prefill="",stop_sequences=None):
message = client.messages.create(
model="claude-3-sonnet-20240229",
max_tokens=2000,
temperature=0.0,
system=system_prompt,
messages=messages,
stop_sequences=stop_sequences
)
return message.content[0].text
课程 #
虽然乍一看工具使用(又称函数调用)在概念上似乎很复杂,但实际上它非常简单!您已经了解了实现工具使用所需的所有技能,这实际上只是替换和提示链的组合。
在之前的替换练习中,我们将文本替换为提示。使用工具时,我们将工具或函数结果替换为提示。Claude 无法真正调用或访问工具和函数。相反,我们有 Claude:
- 输出它想要调用的工具名称和参数
- 在调用工具时停止任何进一步的响应生成
- 然后我们使用附加的工具结果重新提示
函数调用很有用,因为它扩展了 Claude 的功能并使 Claude 能够处理更复杂、多步骤的任务。以下是您可以为 Claude 提供的一些功能示例:
- 计算器
- 字数统计器
- SQL 数据库查询和数据检索
- 天气 API
您可以通过结合以下两个元素让 Claude 使用工具:
- 系统提示,其中我们向 Claude 解释工具使用的概念以及它可以访问的工具的详细描述列表
- 用于编排和执行 Claude 的工具使用请求的控制逻辑
工具/函数使用路线图 #
本课讲授我们当前的工具使用格式。但是,我们将在不久的将来更新和改进工具使用功能,包括:
- 更简化的函数定义和调用格式
- 更强大的错误处理和边缘情况覆盖
- 与我们 API 的其余部分更紧密地集成
- 更好的可靠性和性能,尤其是对于更复杂的工具使用任务
示例 #
为了在 Claude 中启用工具使用,我们从系统提示开始。在这个特殊的工具使用系统提示中,我们告诉 Claude:
- 工具使用的基本前提及其含义
- Claude 如何调用和使用已获得的工具
- 在此特定场景中可以访问的工具的详细列表
这是系统提示的第一部分,向 Claude 解释工具的使用。系统提示的这一部分适用于所有提示 Claude 使用工具的实例。我们为 Claude 提供的函数调用结构 (<function_calls> [...] </function_calls>
是 Claude 经过专门训练使用的结构,因此我们建议您坚持使用。
system_prompt_tools_general_explanation = """You have access to a set of functions you can use to answer the user's question. This includes access to a
sandboxed computing environment. You do NOT currently have the ability to inspect files or interact with external
resources, except by invoking the below functions.
You can invoke one or more functions by writing a "" block like the following as part of your
reply to the user:
$PARAMETER_VALUE
...
...
String and scalar parameters should be specified as is, while lists and objects should use JSON format. Note that
spaces for string values are not stripped. The output is not expected to be valid XML and is parsed with regular
expressions.
The output and/or any errors will appear in a subsequent "" block, and remain there as part of
your reply to the user.
You may then continue composing the rest of your reply to the user, respond to any errors, or make further function
calls as appropriate.
If a "" does NOT appear after your function calls, then they are likely malformatted and not
recognized as a call."""
这是系统提示的第二部分,它定义了 Claude 在这种特定情况下可以使用的具体工具。在这个例子中,我们将为 Claude 提供一个计算器工具,它有三个参数:两个操作数和一个运算符。
然后我们将系统提示的两个部分结合起来。
system_prompt_tools_specific_tools = """Here are the functions available in JSONSchema format:
calculator
Calculator function for doing basic arithmetic.
Supports addition, subtraction, multiplication
first_operand
int
First operand (before the operator)
second_operand
int
Second operand (after the operator)
operator
str
The operation to perform. Must be either +, -, *, or /
"""
system_prompt = system_prompt_tools_general_explanation + system_prompt_tools_specific_tools
现在我们可以给 Claude 出一道需要使用计算器工具的问题。我们将使用 stop_sequences 中的 <function_calls> 来检测 Claude 是否以及何时调用该函数。
multiplication_message = {
"role": "user",
"content": "Multiply 1,984,135 by 9,343,116"
}
stop_sequences = [""]
# Get Claude's response
function_calling_response = get_completion([multiplication_message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(function_calling_response)
现在,我们可以从 Claude 的函数调用中提取参数,并代表 Claude 实际运行该函数。
首先,我们将定义函数的代码。
def do_pairwise_arithmetic(num1, num2, operation):
if operation == '+':
return num1 + num2
elif operation == "-":
return num1 - num2
elif operation == "*":
return num1 * num2
elif operation == "/":
return num1 / num2
else:
return "Error: Operation not supported."
然后我们从 Claude 的函数调用响应中提取参数。如果所有参数都存在,我们就运行计算器工具。
def find_parameter(message, parameter_name):
parameter_start_string = f"name=\"{parameter_name}\">"
start = message.index(parameter_start_string)
if start == -1:
return None
if start > 0:
start = start + len(parameter_start_string)
end = start
while message[end] != "<":
end += 1
return message[start:end]
first_operand = find_parameter(function_calling_response, "first_operand")
second_operand = find_parameter(function_calling_response, "second_operand")
operator = find_parameter(function_calling_response, "operator")
if first_operand and second_operand and operator:
result = do_pairwise_arithmetic(int(first_operand), int(second_operand), operator)
print("---------------- RESULT ----------------")
print(f"{result:,}")
现在我们有了结果,我们必须正确格式化该结果,以便当我们将其传回 Claude 时,Claude 能够理解该结果与哪种工具相关。Claude 已经训练过一套格式来识别它:
<function_results>
<result>
<tool_name>{TOOL_NAME}</tool_name>
<stdout>
{TOOL_RESULT}
</stdout>
</result>
</function_results>
运行下面的单元格,将上述工具结果格式化为这种结构。
def construct_successful_function_run_injection_prompt(invoke_results):
constructed_prompt = (
"\n"
+ '\n'.join(
f"\n{res['tool_name']}\n\n{res['tool_result']}\n\n"
for res in invoke_results
) + "\n"
)
return constructed_prompt
formatted_results = [{
'tool_name': 'do_pairwise_arithmetic',
'tool_result': result
}]
function_results = construct_successful_function_run_injection_prompt(formatted_results)
print(function_results)
现在我们要做的就是将这个结果附加到与之前相同的消息链中,并将其发送回 Claude,这样就完成了!
full_first_response = function_calling_response + ""
# Construct the full conversation
messages = [multiplication_message,
{
"role": "assistant",
"content": full_first_response
},
{
"role": "user",
"content": function_results
}]
# Print Claude's response
final_response = get_completion(messages, system_prompt=system_prompt, stop_sequences=stop_sequences)
print("------------- FINAL RESULT -------------")
print(final_response)
恭喜您从头到尾运行了整个工具使用链!
现在,如果我们给 Claude 一个根本不需要使用给定工具的问题,会怎么样?
non_multiplication_message = {
"role": "user",
"content": "Tell me the capital of France."
}
stop_sequences = [""]
# Get Claude's response
function_calling_response = get_completion([non_multiplication_message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(function_calling_response)
成功!如您所见,Claude 知道在不需要时不要调用该函数。
如果您想在不更改上述任何内容的情况下试验课程提示,请一直滚动到课程笔记本的底部以访问示例操场。
练习 #
练习 10.2.1 – SQL #
在本练习中,您将编写一个工具使用提示,用于查询和写入世界上最小的“数据库”。这是初始化的数据库,它实际上只是一个字典。
db = {
"users": [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"},
{"id": 3, "name": "Charlie", "email": "charlie@example.com"}
],
"products": [
{"id": 1, "name": "Widget", "price": 9.99},
{"id": 2, "name": "Gadget", "price": 14.99},
{"id": 3, "name": "Doohickey", "price": 19.99}
]
}
以下是写入和从数据库写入数据的函数代码。
def get_user(user_id):
for user in db["users"]:
if user["id"] == user_id:
return user
return None
def get_product(product_id):
for product in db["products"]:
if product["id"] == product_id:
return product
return None
def add_user(name, email):
user_id = len(db["users"]) + 1
user = {"id": user_id, "name": name, "email": email}
db["users"].append(user)
return user
def add_product(name, price):
product_id = len(db["products"]) + 1
product = {"id": product_id, "name": name, "price": price}
db["products"].append(product)
return product
要解决此练习,请先定义一个系统提示,如上面的 system_prompt_tools_specific_tools。确保包含每个工具的名称和说明,以及每个函数的每个参数的名称、类型和说明。我们在下面为您提供了一些起始框架。
system_prompt_tools_specific_tools_sql = """
"""
system_prompt = system_prompt_tools_general_explanation + system_prompt_tools_specific_tools_sql
准备就绪后,您可以在以下示例中尝试工具定义系统提示。只需运行以下单元格即可!
examples = [
"Add a user to the database named Deborah.",
"Add a product to the database named Thingo",
"Tell me the name of User 2",
"Tell me the name of Product 3"
]
for example in examples:
message = {
"role": "user",
"content": example
}
# Get & print Claude's response
function_calling_response = get_completion([message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(example, "\n----------\n\n", function_calling_response, "\n*********\n*********\n*********\n\n")
如果您操作正确,函数调用消息应该正确调用 add_user、add_product、get_user 和 get_product 函数。
为了获得额外积分,请添加一些代码单元并编写参数解析代码。然后使用 Claude 给您的参数调用函数,以查看调用后“数据库”的状态。
恭喜! #
恭喜您学习了工具的使用和函数调用!如果您想了解有关搜索和 RAG 的更多信息,请转到最后的附录部分。
示例操场 #
这是一个区域,您可以自由地尝试本课中显示的提示示例,并调整提示以查看它如何影响 Claude 的响应。
system_prompt_tools_general_explanation = """You have access to a set of functions you can use to answer the user's question. This includes access to a
sandboxed computing environment. You do NOT currently have the ability to inspect files or interact with external
resources, except by invoking the below functions.
You can invoke one or more functions by writing a "" block like the following as part of your
reply to the user:
$PARAMETER_VALUE
...
...
String and scalar parameters should be specified as is, while lists and objects should use JSON format. Note that
spaces for string values are not stripped. The output is not expected to be valid XML and is parsed with regular
expressions.
The output and/or any errors will appear in a subsequent "" block, and remain there as part of
your reply to the user.
You may then continue composing the rest of your reply to the user, respond to any errors, or make further function
calls as appropriate.
If a "" does NOT appear after your function calls, then they are likely malformatted and not
recognized as a call."""
system_prompt_tools_specific_tools = """Here are the functions available in JSONSchema format:
calculator
Calculator function for doing basic arithmetic.
Supports addition, subtraction, multiplication
first_operand
int
First operand (before the operator)
second_operand
int
Second operand (after the operator)
operator
str
The operation to perform. Must be either +, -, *, or /
"""
system_prompt = system_prompt_tools_general_explanation + system_prompt_tools_specific_tools
multiplication_message = {
"role": "user",
"content": "Multiply 1,984,135 by 9,343,116"
}
stop_sequences = [""]
# Get Claude's response
function_calling_response = get_completion([multiplication_message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(function_calling_response)
def do_pairwise_arithmetic(num1, num2, operation):
if operation == '+':
return num1 + num2
elif operation == "-":
return num1 - num2
elif operation == "*":
return num1 * num2
elif operation == "/":
return num1 / num2
else:
return "Error: Operation not supported."
def find_parameter(message, parameter_name):
parameter_start_string = f"name=\"{parameter_name}\">"
start = message.index(parameter_start_string)
if start == -1:
return None
if start > 0:
start = start + len(parameter_start_string)
end = start
while message[end] != "<":
end += 1
return message[start:end]
first_operand = find_parameter(function_calling_response, "first_operand")
second_operand = find_parameter(function_calling_response, "second_operand")
operator = find_parameter(function_calling_response, "operator")
if first_operand and second_operand and operator:
result = do_pairwise_arithmetic(int(first_operand), int(second_operand), operator)
print("---------------- RESULT ----------------")
print(f"{result:,}")
def construct_successful_function_run_injection_prompt(invoke_results):
constructed_prompt = (
"\n"
+ '\n'.join(
f"\n{res['tool_name']}\n\n{res['tool_result']}\n\n"
for res in invoke_results
) + "\n"
)
return constructed_prompt
formatted_results = [{
'tool_name': 'do_pairwise_arithmetic',
'tool_result': result
}]
function_results = construct_successful_function_run_injection_prompt(formatted_results)
print(function_results)
full_first_response = function_calling_response + ""
# Construct the full conversation
messages = [multiplication_message,
{
"role": "assistant",
"content": full_first_response
},
{
"role": "user",
"content": function_results
}]
# Print Claude's response
final_response = get_completion(messages, system_prompt=system_prompt, stop_sequences=stop_sequences)
print("------------- FINAL RESULT -------------")
print(final_response)
non_multiplication_message = {
"role": "user",
"content": "Tell me the capital of France."
}
stop_sequences = [""]
# Get Claude's response
function_calling_response = get_completion([non_multiplication_message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(function_calling_response)