26 KiB
26 KiB
In [ ]:
%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].textIn [ ]:
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 "<function_calls>" block like the following as part of your
reply to the user:
<function_calls>
<invoke name="$FUNCTION_NAME">
<antml:parameter name="$PARAMETER_NAME">$PARAMETER_VALUE</parameter>
...
</invoke>
<nvoke name="$FUNCTION_NAME2">
...
</invoke>
</function_calls>
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 "<function_results>" 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 "<function_results>" does NOT appear after your function calls, then they are likely malformatted and not
recognized as a call."""In [ ]:
system_prompt_tools_specific_tools = """Here are the functions available in JSONSchema format:
<tools>
<tool_description>
<tool_name>calculator</tool_name>
<description>
Calculator function for doing basic arithmetic.
Supports addition, subtraction, multiplication
</description>
<parameters>
<parameter>
<name>first_operand</name>
<type>int</type>
<description>First operand (before the operator)</description>
</parameter>
<parameter>
<name>second_operand</name>
<type>int</type>
<description>Second operand (after the operator)</description>
</parameter>
<parameter>
<name>operator</name>
<type>str</type>
<description>The operation to perform. Must be either +, -, *, or /</description>
</parameter>
</parameters>
</tool_description>
</tools>
"""
system_prompt = system_prompt_tools_general_explanation + system_prompt_tools_specific_toolsIn [ ]:
multiplication_message = {
"role": "user",
"content": "Multiply 1,984,135 by 9,343,116"
}
stop_sequences = ["</function_calls>"]
# Get Claude's response
function_calling_response = get_completion([multiplication_message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(function_calling_response)In [ ]:
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."In [ ]:
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:,}")In [ ]:
def construct_successful_function_run_injection_prompt(invoke_results):
constructed_prompt = (
"<function_results>\n"
+ '\n'.join(
f"<result>\n<tool_name>{res['tool_name']}</tool_name>\n<stdout>\n{res['tool_result']}\n</stdout>\n</result>"
for res in invoke_results
) + "\n</function_results>"
)
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)In [ ]:
full_first_response = function_calling_response + "</function_calls>"
# 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)In [ ]:
non_multiplication_message = {
"role": "user",
"content": "Tell me the capital of France."
}
stop_sequences = ["</function_calls>"]
# Get Claude's response
function_calling_response = get_completion([non_multiplication_message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(function_calling_response)In [ ]:
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}
]
}In [ ]:
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 productIn [ ]:
system_prompt_tools_specific_tools_sql = """
"""
system_prompt = system_prompt_tools_general_explanation + system_prompt_tools_specific_tools_sqlIn [ ]:
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")In [ ]:
from hints import exercise_10_2_1_solution; print(exercise_10_2_1_solution)In [ ]:
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 "<function_calls>" block like the following as part of your
reply to the user:
<function_calls>
<invoke name="$FUNCTION_NAME">
<antml:parameter name="$PARAMETER_NAME">$PARAMETER_VALUE</parameter>
...
</invoke>
<nvoke name="$FUNCTION_NAME2">
...
</invoke>
</function_calls>
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 "<function_results>" 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 "<function_results>" does NOT appear after your function calls, then they are likely malformatted and not
recognized as a call."""In [ ]:
system_prompt_tools_specific_tools = """Here are the functions available in JSONSchema format:
<tools>
<tool_description>
<tool_name>calculator</tool_name>
<description>
Calculator function for doing basic arithmetic.
Supports addition, subtraction, multiplication
</description>
<parameters>
<parameter>
<name>first_operand</name>
<type>int</type>
<description>First operand (before the operator)</description>
</parameter>
<parameter>
<name>second_operand</name>
<type>int</type>
<description>Second operand (after the operator)</description>
</parameter>
<parameter>
<name>operator</name>
<type>str</type>
<description>The operation to perform. Must be either +, -, *, or /</description>
</parameter>
</parameters>
</tool_description>
</tools>
"""
system_prompt = system_prompt_tools_general_explanation + system_prompt_tools_specific_toolsIn [ ]:
multiplication_message = {
"role": "user",
"content": "Multiply 1,984,135 by 9,343,116"
}
stop_sequences = ["</function_calls>"]
# Get Claude's response
function_calling_response = get_completion([multiplication_message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(function_calling_response)In [ ]:
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."In [ ]:
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:,}")In [ ]:
def construct_successful_function_run_injection_prompt(invoke_results):
constructed_prompt = (
"<function_results>\n"
+ '\n'.join(
f"<result>\n<tool_name>{res['tool_name']}</tool_name>\n<stdout>\n{res['tool_result']}\n</stdout>\n</result>"
for res in invoke_results
) + "\n</function_results>"
)
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)In [ ]:
full_first_response = function_calling_response + "</function_calls>"
# 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)In [ ]:
non_multiplication_message = {
"role": "user",
"content": "Tell me the capital of France."
}
stop_sequences = ["</function_calls>"]
# Get Claude's response
function_calling_response = get_completion([non_multiplication_message], system_prompt=system_prompt, stop_sequences=stop_sequences)
print(function_calling_response)