Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save markomanninen/7c6e1e96faf0ec4382249e2131a9f1d0 to your computer and use it in GitHub Desktop.
Save markomanninen/7c6e1e96faf0ec4382249e2131a9f1d0 to your computer and use it in GitHub Desktop.
Anthropic Claude Function Calling.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyO7EM6S9BTu651k7fvM2Ahr",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/markomanninen/7c6e1e96faf0ec4382249e2131a9f1d0/anthropic-claude-function-calling.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"# Anthropic Claude Function Calling\n",
"\n",
"This document extends the notebook document: https://github.com/anthropics/anthropic-cookbook/blob/main/function_calling/function_calling.ipynb\n",
"\n",
"Marko T. Manninen &copy; 03/2024\n",
"\n",
"---\n",
"\n",
"In this document, we delve into the logic of function calls within the realm of Large Language Models (LLMs), demonstrating a framework that's designed to be adaptable across various models, though specifically tailored for use with Anthropic's Claude Sonnet language model in this instance. Claude Sonnet, being more cost-effective than its counterpart, Opus, has been found to perform satisfactorily for the use case at hand. The task of writing function documentation and Colab notebook texts was assigned to ChatGPT, aimed at creating a cohesive and comprehensible guide.\n",
"\n",
"To fully utilize this notebook, you'll need to provide an Anthropic API key. By the document's conclusion, you'll have the opportunity to test out your own prompts, exploring the capabilities and functionalities firsthand. For those interested in customizing the system further by adding new callable functions, the process involves defining these functions in Python, specifying their reference names/types `<xs:complexType name=\"OwnType\">` and the arguments they accept within the schema file `function_call_schema`.\n",
"\n",
"The initial learning curve might be steep as you navigate through understanding how language models can be integrated with native programming functions, translating both the given prompts and the results. However, mastering this concept opens up new avenues of knowledge on the symbiosis between language models and native applications, enhancing your grasp on how to leverage AI in interpreting and executing complex tasks."
],
"metadata": {
"id": "V1n0wV8G0l0J"
}
},
{
"cell_type": "markdown",
"source": [
"## Install\n",
"\n",
"To prepare for utilizing Anthropic's tools or libraries in this Colab notebook, the first step involves setting up the necessary software environment. This setup includes installing the `anthropic` Python package, which provides access to functionalities or models developed by Anthropic. This package installation is crucial for leveraging the cutting-edge AI technologies or tools that Anthropic offers, ensuring that the notebook has the required dependencies to run any subsequent code involving Anthropic's AI models or utilities efficiently. The following command installs the `anthropic` package using pip, Python's package installer, guaranteeing that the latest version of the package is added to the notebook's environment."
],
"metadata": {
"id": "7OrMvXGlyoDL"
}
},
{
"cell_type": "code",
"source": [
"!pip install anthropic"
],
"metadata": {
"id": "mbjpnx3xA9gI"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Import and Configure\n",
"\n",
"This block of code is designed for integrating various Python libraries and functionalities into your Colab notebook, focusing particularly on XML parsing, secure random string generation, regular expressions, interaction with Anthropic's AI models, and accessing user data stored in Google Colab. Here's a breakdown of its components:\n",
"\n",
"1. **XML Parsing**: `xml.etree.ElementTree` and `lxml.etree` are imported for parsing and manipulating XML data. While `xml.etree.ElementTree` is a built-in Python library suitable for most XML tasks, `lxml.etree` offers more advanced features and better performance for complex XML processing.\n",
"\n",
"2. **Utilities**:\n",
" - The `operator` module provides a set of efficient functions corresponding to the intrinsic operators of Python. For example, you can use these functions for mathematical operations, string comparisons, and logical operations in a functional programming style.\n",
" - The `secrets` module is used for generating cryptographically strong random numbers and strings, which is crucial for security-sensitive applications.\n",
" - The `string` module includes a collection of string constants, such as ASCII letters and digits, which can be used in conjunction with `secrets` for generating secure random strings.\n",
" - The `re` module allows for regular expression operations, enabling you to perform complex pattern matching and manipulation of text data.\n",
"\n",
"3. **Anatropic AI Integration**:\n",
" - The `anthropic` library is imported to enable interaction with Anthropic's AI models. The client is initialized with an API key retrieved from the Colab's user data, allowing secure access to Anthropic's AI services.\n",
" - The `MODEL_NAME` variable specifies the name of the Anthropic AI model you intend to use. It's set to `\"claude-3-sonnet-20240229\"` as an example, indicating a specific version of the Claude-3 model designed for generating sonnets. Note that the comment suggests an alternative model, `\"claude-3-opus-20240229\"`, which you might use for different AI tasks.\n",
"\n",
"4. **Google Colab User Data**:\n",
" - The code snippet `from google.colab import userdata` imports the `userdata` module from Google Colab's library, which provides a method to access user-specific data, such as API keys stored in the Colab environment. This feature is utilized to securely fetch the `ANTHROPIC_API_KEY`, demonstrating a way to integrate secure API usage within a Colab notebook.\n",
" \n",
"\n",
"This setup ensures a versatile environment in your Colab notebook, ready to handle a wide range of tasks from XML processing to secure interaction with AI models provided by Anthropic, all while maintaining best practices for security and performance."
],
"metadata": {
"id": "CrkQ9ePzyxqT"
}
},
{
"cell_type": "markdown",
"source": [
"### API Key from Colab Document Secrets\n",
"\n",
"![colabsecrets.png](data:image/png;base64,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sTwU56E5Z4iWJEmSJOWWLURjlo+4MkRLkiRJksa1rCH6qF1/tfraMT9taf7mu/9JtXnP/tboLj6wz8/WATtu2yCGaEmSJEnSuJY1RJfue6ar9vqXw3WpfMl7dv/l6quFLt+DGKIlSZIkSeNa1hCN0n3PhOKz3/qL1atf9fvFefCql216y9IA/bfr/1n1zl1+OkBZkofkQSHa0CxJkiRJGmTZQzROX/NL1TeO+SdLlv/04T9TnbHmF6vdXvfKXtk3vvZ3qo27/0r1yB//8yVduFkGy4rLTgzRkiRJkqRJm0mIplV5lPuaIwL0JW/7+XpZpXXkIXkQQ7QkSZIkaZCZhGgQft+/1yuqr4eBxrriGdMn7/HLjQEahmhJkiRJ0qTNLEQne+30W9X2g/9lp1Zpynz85bLMU1pWZIiWJEmSJE3azEN0svfLwfjKt/9c9czhP9PXOs3fT677meqyt/1c373Sgxiix3P00UdXF198cf1vfH3HHXeszjjjjOr888+v1q5d2zcNN9xwQ/Xcc8/18P+8TJNjjz22+sxnPlPP97nPfa465ZRTiuXa7LDDDtU111xTPfvss9UXv/jF6v7776/22WefYllJkiRJGtbchGj9VB5Eh/XpT3+62muvvYrL7uL444+vQyzL+sIXvlBt3ry5N23btm11OGXaY489Vu233359844Tou+6666+eR988MFq5513LpZtwsn82c9+trcMtvW8884rlpUkSZKkYRmi59CsQzShMwVlXH311b1p9957b+/1p59+ujruuOP65p1kiP7oRz9atyyXyjbJQzQ/Apx99tnFspIkSZI0LEP0HJp1iKb7M92gCaCf/OQnqwMPPLA37aSTTqqeeOKJetqNN964pKV4nBB92GGHVQ899FAd4OnWnQf0rvgRgIDPNt5+++3VLrvsUiwnSZIkScMyRM8hwiP3I+fuu+++voBK4CyVo/v1sN2gJ2WcEC1JkiRJ884QvUDygPqxj32sWG6WDNGSJEmSVjJD9ALpGqI/+MEP9pX7xCc+Ue222259ZY466qjqySef7JX5/Oc/3zcaNvcin3jiiXW37meeeaYuQzdr5rnjjjuqgw8+uG95yaAQfdFFF9XdrNN0/r7kkkvq9bXNy7axjWka+842PPDAA/Uy2Ea6mtONne7sqVy+X8mGDRuqe+65p96fdP83y3n44Yfr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)"
],
"metadata": {
"id": "ubCdZsaD9r0u"
}
},
{
"cell_type": "code",
"source": [
"import xml.etree.ElementTree as ET\n",
"from lxml import etree\n",
"import operator\n",
"import secrets\n",
"import string\n",
"import re\n",
"import anthropic\n",
"\n",
"# User your preferred way of setting up api key\n",
"from google.colab import userdata\n",
"\n",
"client = anthropic.Anthropic(\n",
" # defaults to os.environ.get(\"ANTHROPIC_API_KEY\")\n",
" #api_key=\"sk-...\",\n",
" api_key=userdata.get('ANTHROPIC_API_KEY'),\n",
")\n",
"\n",
"MODEL_NAME = \"claude-3-sonnet-20240229\" # claude-3-opus-20240229"
],
"metadata": {
"id": "Dkp6LN2myyB5"
},
"execution_count": 2,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Functions\n",
"\n",
"This code snippet encompasses a comprehensive suite of functions designed to validate XML documents against XML Schemas (XSD), evaluate arithmetic expressions represented in XML, generate secure random strings, and construct prompts for the Claude model by Anthropic. Each function is crafted to perform a specific task within this ecosystem, working together to process and evaluate XML-based input, generate dynamic responses based on predefined operations, and interact with AI models through the Anthropic API. The structure allows for flexible manipulation of XML data, secure randomization, and dynamic interaction with AI models, showcasing an advanced integration of XML processing, secure programming practices, and AI-driven automation within a Python environment.\n",
"\n",
"1. **validate_xml_against_xsd**: Validates an XML document against an XML Schema, returning `True` if the document adheres to the schema or `False` if it doesn't, while also printing any errors encountered during the validation process.\n",
"\n",
"2. **construct_format_tool_for_claude_prompt**: Creates a formatted XML-like string that describes a tool or function, including its title, name, description, and parameters, intended to structure information for the Claude model.\n",
"\n",
"3. **construct_tool_use_system_prompt**: Compiles a system prompt for Claude, integrating instructions, available tools, and parameter schemas into a cohesive prompt that outlines how tools can be invoked within the system.\n",
"\n",
"4. **construct_successful_function_run_injection_prompt**: Generates an XML-like summary of the results from invoking various tools, formatting the output of each tool's operation for clear presentation.\n",
"\n",
"5. **parse_function_call**: Extracts the `<function_calls>` section from a provided XML or text input, facilitating the isolation of specific sections for further processing or validation.\n",
"\n",
"6. **validate_and_create_assistant_message**: Validates a given XML input against an XSD schema and, if valid, processes the specified operations in the XML, constructing a summary message of the results.\n",
"\n",
"7. **prompt_claude**: Interfaces with the Anthropic API to send a prompt to the Claude model, retrieving and returning the generated response based on given parameters and context.\n",
"\n",
"8. **function_calling**: Orchestrates the creation and processing of function calls based on user prompts, generating XML for function invocation, validating it, and constructing a final message using responses from the Claude model."
],
"metadata": {
"id": "aBX8qluT1ePL"
}
},
{
"cell_type": "code",
"source": [
"def validate_xml_against_xsd(xml_input, xsd_input):\n",
" \"\"\"\n",
" Validates an XML document against an XML Schema.\n",
"\n",
" Args:\n",
" xml_input (str): A string representation of the XML document.\n",
" xsd_input (str): A string representation of the XML Schema (XSD).\n",
"\n",
" Returns:\n",
" bool: True if the XML document is valid against the schema, False otherwise.\n",
" \"\"\"\n",
" try:\n",
" schema_root = etree.XML(xsd_input)\n",
" schema = etree.XMLSchema(schema_root)\n",
" xml_parser = etree.XMLParser(schema=schema)\n",
" etree.fromstring(xml_input, xml_parser)\n",
" return True\n",
" except etree.XMLSchemaError as e:\n",
" print(f\"XML schema validation error: {e}\")\n",
" return False\n",
" except etree.XMLSyntaxError as e:\n",
" print(f\"XML syntax error: {e}\")\n",
" print(xml_input)\n",
" return False\n",
"\n",
"def construct_format_tool_for_claude_prompt(title, name, description, operation_type):\n",
" \"\"\"\n",
" Constructs a formatted tool description for Claude's prompt, encapsulating essential information about the tool within an XML-like structure.\n",
"\n",
" Args:\n",
" title (str): The title of the tool, providing a brief headline or identifier.\n",
" name (str): The name of the tool, typically used as a unique identifier.\n",
" description (str): A description of the tool, detailing its purpose and use.\n",
" operation_type (str): A string containing a reference to a parameter schema that details the inputs the tool accepts.\n",
"\n",
" Returns:\n",
" str: The formatted tool description in an XML-like string, ready for inclusion in Claude's prompts or documentation.\n",
" \"\"\"\n",
" return (\n",
" \"<tool_description>\\n\"\n",
" f\"<tool_title>{title}</tool_title>\\n\"\n",
" f\"<tool_name>{name}</tool_name>\\n\"\n",
" \"<description>\\n\"\n",
" f\"{description}\\n\"\n",
" \"</description>\\n\"\n",
" f\"<operation type=\\\"{operation_type}\\\"/>\\n\"\n",
" \"</tool_description>\"\n",
" )\n",
"\n",
"def construct_tool_use_system_prompt(tools, schemas):\n",
" \"\"\"\n",
" Constructs the system prompt for Claude, highlighting the available tools and how to invoke them.\n",
" This prompt includes a detailed list of tools, showcasing how each can be called, followed by the schemas\n",
" detailing the structure and parameters for each tool's invocation.\n",
"\n",
" Args:\n",
" tools (list): A list of formatted tool descriptions, each providing detailed information about a tool,\n",
" including its title, name, description, and parameterization.\n",
" schemas (str): A string containing XML-formatted descriptions of the parameters schemas for all tools,\n",
" providing details on how to correctly format function calls for each tool.\n",
"\n",
" Returns:\n",
" str: The system prompt for Claude, integrating instructions for tool invocation, a list of available tools,\n",
" and detailed schemas for tool use.\n",
" \"\"\"\n",
" return ''.join((\n",
" \"In this environment you have access to a set of tools you can use to answer the user's question.\\n\",\n",
" \"\\n\",\n",
" \"You may call them like this:\\n\",\n",
" \"<function_calls>\\n\",\n",
" \"<invoke>\\n\",\n",
" \"<tool_name>$TOOL_NAME</tool_name>\\n\",\n",
" \"$PARAMETERS\\n\",\n",
" \"</invoke>\\n\",\n",
" \"</function_calls>\\n\",\n",
" \"\\n\",\n",
" \"Here are the tools available:\\n\",\n",
" \"<tools>\\n\"\n",
" + '\\n'.join([tool for tool in tools]) +\n",
" \"\\n</tools>\\n\",\n",
" \"\\n<!-- Schemas -->\\n\",\n",
" schemas\n",
" ))\n",
"\n",
"def construct_successful_function_run_injection_prompt(invoke_results):\n",
" \"\"\"\n",
" Constructs an XML-like string that summarizes the results of tool invocations.\n",
"\n",
" Args:\n",
" invoke_results (list of dict): A list where each dictionary contains 'tool_name'\n",
" and 'tool_result' keys representing the name of the\n",
" tool invoked and its output, respectively.\n",
"\n",
" Returns:\n",
" str: A structured XML-like string that encapsulates each tool's invocation results.\n",
" \"\"\"\n",
" # Formatting each tool's results into XML structure\n",
" constructed_prompt = \"<function_results>\\n\" + '\\n'.join(\n",
" f\"<result>\\n<tool_name>{res['tool_name']}</tool_name>\\n<stdout>\\n{res['tool_result']}\\n</stdout>\\n</result>\"\n",
" for res in invoke_results\n",
" ) + \"\\n</function_results>\"\n",
"\n",
" return constructed_prompt\n",
"\n",
"def parse_function_call(xml_input):\n",
" \"\"\"\n",
" Extracts the <function_calls> section from a provided XML or text input.\n",
"\n",
" Args:\n",
" xml_input (str): The XML or string input from which to extract the <function_calls> section.\n",
"\n",
" Returns:\n",
" str: The extracted <function_calls> section, if found. Otherwise, returns None.\n",
" \"\"\"\n",
" # Using regex to find the <function_calls> XML content\n",
" xml_content_match = re.search(r\"(<function_calls>.+?</function_calls>)\", xml_input, re.DOTALL)\n",
"\n",
" if xml_content_match:\n",
" # Returning the matched <function_calls> content\n",
" return xml_content_match.group(1)\n",
" else:\n",
" # Handling cases where no <function_calls> content is found\n",
" print(\"No XML content found.\")\n",
" return None\n",
"\n",
"def validate_and_create_assistant_message(xml_input, xsd_input):\n",
" \"\"\"\n",
" Validates given XML input against an XSD schema, and if valid, evaluates the operations\n",
" specified in the XML and constructs a message summarizing the results.\n",
"\n",
" Args:\n",
" xml_input (str): XML input string containing function calls.\n",
" xsd_input (str): XSD schema string against which to validate the XML input.\n",
"\n",
" Returns:\n",
" str/bool: Constructed message summarizing the invocation results if the XML is valid;\n",
" otherwise, returns False.\n",
" \"\"\"\n",
" # Extracting <function_calls> section from the input\n",
" xml_content = parse_function_call(xml_input)\n",
"\n",
" # Validate the extracted XML content against the XSD schema\n",
" if xml_content and validate_xml_against_xsd(xml_content, xsd_input):\n",
" # If valid, construct and return the summary message\n",
" return xml_content + \"\\n\" + construct_successful_function_run_injection_prompt(\n",
" extract_and_evaluate_operations(xml_content)\n",
" )\n",
" else:\n",
" # Return False if the XML is invalid or no <function_calls> section is found\n",
" return False\n",
"\n",
"def prompt_claude(client, messages=[], system_prompt=\"\", model=\"claude-3-sonnet-20240229\", temperature=0, max_tokens=255):\n",
" \"\"\"\n",
" Sends a prompt to Claude API and retrieves the generated response.\n",
"\n",
" Args:\n",
" client: The API client configured to interact with Claude.\n",
" messages (list): List of previous messages and responses to provide context.\n",
" system_prompt (str): The system prompt or instructions for Claude.\n",
" model (str): The model version to use for generating responses.\n",
" temperature (float): Controls randomness in the response generation.\n",
" max_tokens (int): The maximum length of the model's response.\n",
"\n",
" Returns:\n",
" str: The text of the first response from Claude, or a message indicating\n",
" an issue with obtaining a valid response.\n",
" \"\"\"\n",
" try:\n",
" # Making an API call to the Claude model with the provided parameters\n",
" result = client.messages.create(\n",
" model=model,\n",
" temperature=temperature,\n",
" max_tokens=max_tokens,\n",
" messages=messages,\n",
" system=system_prompt\n",
" )\n",
"\n",
" # Check if the response has the expected structure and content\n",
" if result and result.content and len(result.content) > 0:\n",
" return result.content[0].text\n",
" else:\n",
" # Return a placeholder or error message if the expected content isn't found\n",
" return \"Unable to retrieve a valid response from the model.\"\n",
" except Exception as e:\n",
" # Handle any errors that may occur during the API call or response processing\n",
" return f\"An error occurred while fetching the response: {str(e)}\"\n",
"\n",
"def function_calling(client, user_prompt, tools, function_call_schema, debug_xmls = False):\n",
" \"\"\"\n",
" Orchestrates the creation and processing of function calls within a system that utilizes Claude\n",
" for generating and validating function call XML based on user prompts.\n",
"\n",
" Args:\n",
" client: The API client configured to interact with Claude.\n",
" user_prompt (str): The initial prompt from the user, describing their request.\n",
" tools (list): A list of formatted tool descriptions for use in the system prompt.\n",
" function_call_schema (str): The XML Schema Definition (XSD) as a string, against which generated function call XML will be validated.\n",
"\n",
" Returns:\n",
" str: The final message from Claude after processing the function call, or an error message if validation fails.\n",
" \"\"\"\n",
" # Construct the system prompt from the tools and schema\n",
" system_prompt = construct_tool_use_system_prompt(tools, function_call_schema)\n",
"\n",
" if debug_xmls:\n",
" print(f\"\\n\\nSystem prompt: {system_prompt}\")\n",
"\n",
" # Create the user message to send to Claude\n",
" user_message = {\n",
" \"role\": \"user\",\n",
" \"content\": user_prompt\n",
" }\n",
"\n",
" # Generate the XML for the function call based on the user's prompt\n",
" function_call_input = prompt_claude(\n",
" client,\n",
" [user_message],\n",
" system_prompt + \"\\nGenerate only XML.\"\n",
" )\n",
"\n",
" # Validate the generated XML against the schema and create the assistant message\n",
" assistant_message_content = validate_and_create_assistant_message(function_call_input, function_call_schema)\n",
"\n",
" if assistant_message_content:\n",
"\n",
" if debug_xmls:\n",
" print(f\"\\n\\nAssistant message: {assistant_message_content}\")\n",
"\n",
" # If validation succeeds, prepare the assistant message for final processing\n",
" assistant_message = {\n",
" \"role\": \"assistant\",\n",
" \"content\": assistant_message_content\n",
" }\n",
"\n",
" # Generate the final message using both the user and assistant messages\n",
" return prompt_claude(\n",
" client,\n",
" [user_message, assistant_message],\n",
" system_prompt\n",
" )\n",
" else:\n",
" # Handle cases where validation fails or no valid function call XML is generated\n",
" return \"Final message could not be retrieved due to validation failure or empty function call XML.\""
],
"metadata": {
"id": "ykOLyJoXK3Zy"
},
"execution_count": 3,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"### Custom functions for Tools\n",
"\n",
"Native Python functions are defined here to be referenced in the function calling tool chain by Claude: `do_pairwise_arithmetic` and `do_random_string`.\n",
"\n",
"1. **evaluate_expression**: Recursively evaluates an arithmetic expression represented in XML, supporting basic operations like addition, subtraction, multiplication, and division. It navigates through nested XML elements to compute the final result of the expression.\n",
"\n",
"2. **generate_random_string**: Generates a secure random string of specified length, combining uppercase and lowercase letters with digits, suitable for use cases requiring random identifiers or tokens.\n",
"\n",
"3. **handle_arithmetic_operation**: Extracts and evaluates an arithmetic operation defined within an XML `<invoke>` element, using the `evaluate_expression` function to compute its result.\n",
"\n",
"4. **handle_random_string**: Generates a random string based on a specified length found within an XML `<invoke>` element, ensuring the length falls within a valid range before generating the string.\n",
"\n",
"5. **extract_and_evaluate_operations**: Parses an XML string to extract `<invoke>` elements, determining the type of operation (arithmetic or random string generation) to perform, evaluates these operations, and collects their results."
],
"metadata": {
"id": "0-tYHfonCGNp"
}
},
{
"cell_type": "code",
"source": [
"#####################################\n",
"# Mapping for arithmetic operations #\n",
"#####################################\n",
"\n",
"operation_mapping = {\n",
" '+': operator.add,\n",
" '-': operator.sub,\n",
" '*': operator.mul,\n",
" '/': operator.truediv,\n",
"}\n",
"\n",
"def evaluate_expression(element):\n",
" \"\"\"\n",
" Recursively evaluates an arithmetic expression represented as XML elements.\n",
"\n",
" This function supports 'operation' elements, which specify an arithmetic operation\n",
" (addition, subtraction, multiplication, division) and 'number' elements as operands.\n",
" Each 'operation' must have exactly two operands, which can be 'number' elements or\n",
" nested 'operation' elements.\n",
"\n",
" Args:\n",
" element (xml.etree.ElementTree.Element): An XML element representing either an\n",
" 'operation' or a 'number'.\n",
"\n",
" Returns:\n",
" int: The result of the evaluated expression.\n",
"\n",
" Raises:\n",
" ValueError: If an unsupported operation is encountered or if the operation does not\n",
" have exactly two operands.\n",
" \"\"\"\n",
" # Check if the current element is an 'operation'\n",
" if element.tag == 'operation':\n",
" # Retrieve the operation type (e.g., '+', '-', '*', '/')\n",
" operation = element.attrib['operator']\n",
" # Map the operation type to a corresponding Python function\n",
" operation_func = operation_mapping.get(operation)\n",
"\n",
" # Raise an error if the operation is not supported\n",
" if not operation_func:\n",
" raise ValueError(f\"Operation {operation} not supported.\")\n",
"\n",
" # Recursively evaluate the operands of the operation\n",
" operands = [evaluate_expression(child) for child in element]\n",
"\n",
" # Ensure that each operation has exactly two operands\n",
" if len(operands) != 2:\n",
" raise ValueError(\"Each operation must have exactly two operands.\")\n",
"\n",
" # Perform the operation with the evaluated operands and return the result\n",
" return operation_func(*operands)\n",
"\n",
" # Check if the current element is a 'number'\n",
" elif element.tag == 'number':\n",
" # Return the integer value of the number element\n",
" return int(element.text)\n",
"\n",
" # Raise an error if the element is neither an 'operation' nor a 'number'\n",
" else:\n",
" raise ValueError(f\"Unsupported element: {element.tag}\")\n",
"\n",
"def generate_random_string(length):\n",
" \"\"\"\n",
" Generates a secure random string of the specified length, using a combination\n",
" of uppercase and lowercase letters, along with digits.\n",
"\n",
" Args:\n",
" length (int): The length of the random string to be generated. Must be\n",
" a non-negative integer.\n",
"\n",
" Returns:\n",
" str: A random string of the specified length.\n",
" \"\"\"\n",
" # Define the alphabet: includes both uppercase and lowercase letters, and digits\n",
" alphabet = string.ascii_letters + string.digits\n",
"\n",
" # Generate a random string of the given length\n",
" return ''.join(secrets.choice(alphabet) for _ in range(length))\n",
"\n",
"def handle_arithmetic_operation(invoke):\n",
" \"\"\"\n",
" Handles the arithmetic operation for a given invoke element.\n",
" \"\"\"\n",
" operation_element = invoke.find('.//operation')\n",
" if operation_element is not None:\n",
" try:\n",
" return evaluate_expression(operation_element)\n",
" except ValueError as e:\n",
" print(f\"Invalid value: {e}\")\n",
" else:\n",
" print(\"No operation found within <invoke>.\")\n",
" return None\n",
"\n",
"def handle_random_string(invoke):\n",
" \"\"\"\n",
" Handles the generation of a random string for a given invoke element.\n",
" \"\"\"\n",
" random_string_element = invoke.find('.//randomstring')\n",
" if random_string_element is not None and 'length' in random_string_element.attrib:\n",
" try:\n",
" length = int(random_string_element.attrib['length'])\n",
" if 1 <= length <= 1000:\n",
" return generate_random_string(length)\n",
" else:\n",
" print(\"Random string length must be between 1 and 1000.\")\n",
" except ValueError as e:\n",
" print(f\"Invalid length value: {e}\")\n",
" else:\n",
" print(\"Random string length attribute is missing or invalid.\")\n",
" return None\n",
"\n",
"def extract_and_evaluate_operations(xml_string):\n",
" \"\"\"\n",
" Extracts and evaluates operations from an XML string, handling arithmetic and random string generation.\n",
" \"\"\"\n",
" root = ET.fromstring(xml_string)\n",
" invoke_results = []\n",
"\n",
" for invoke in root.findall('.//invoke'):\n",
" tool_name = invoke.find('tool_name').text\n",
" result = None\n",
"\n",
" if tool_name == 'do_pairwise_arithmetic':\n",
" result = handle_arithmetic_operation(invoke)\n",
" elif tool_name == 'do_random_string':\n",
" result = handle_random_string(invoke)\n",
" else:\n",
" print(f\"Unsupported tool name: {tool_name}\")\n",
"\n",
" if result is not None:\n",
" invoke_results.append({'tool_name': tool_name, 'tool_result': result})\n",
"\n",
" return invoke_results"
],
"metadata": {
"id": "Xfp8URvYCDYh"
},
"execution_count": 4,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Function Call Schema\n",
"\n",
"This XML Schema Definition (XSD) specifies the structure for an XML document that represents function calls, particularly focusing on arithmetic operations and the generation of random strings. The schema is designed to validate XML documents that describe these operations in a structured and type-safe manner. Here's a brief overview of each component within the schema:\n",
"\n",
"- **OperatorType**: A simple type definition that restricts the operator attribute to one of four arithmetic operations: addition (`+`), subtraction (`-`), multiplication (`*`), or division (`/`).\n",
"\n",
"- **NumberType**: A complex type that extends `xs:decimal` to include both integers and floating-point numbers, used for representing numeric operands in operations.\n",
"\n",
"- **OperationType**: A recursive complex type for the `operation` element, allowing it to contain either `number` elements or nested `operation` elements, each of which requires an `operator` attribute of the `OperatorType`.\n",
"\n",
"- **RandomType**: A complex type designed for elements that specify the generation of random strings, potentially including a `length` attribute to define the length of the generated string.\n",
"\n",
"- **InvokeType**: A complex type for the `invoke` element, which acts as a container for specifying a function call. It includes a mandatory `tool_name` element and optional `operation` and `randomstring` elements, allowing for the definition of both arithmetic operations and random string generation requests.\n",
"\n",
"- **FunctionCallsType**: Defines the structure for the `function_calls` element, which can contain one or more `invoke` elements. This setup enables the representation of multiple function invocations within a single XML document.\n",
"\n",
"- **Root Element**: The schema establishes `function_calls` as the root element, expecting it to conform to the `FunctionCallsType`.\n",
"\n",
"This schema effectively outlines the expected format for XML documents used in this system, ensuring that any XML document used for invoking functions like arithmetic operations or random string generation is both structured and validated against these definitions."
],
"metadata": {
"id": "GYvQOrQM18RW"
}
},
{
"cell_type": "code",
"source": [
"function_call_schema = \"\"\"\n",
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n",
"\n",
" <!-- Simple type for the operator attribute -->\n",
" <xs:simpleType name=\"OperatorType\">\n",
" <xs:restriction base=\"xs:string\">\n",
" <xs:enumeration value=\"+\"/>\n",
" <xs:enumeration value=\"-\"/>\n",
" <xs:enumeration value=\"*\"/>\n",
" <xs:enumeration value=\"/\"/>\n",
" </xs:restriction>\n",
" </xs:simpleType>\n",
"\n",
" <!-- Complex type for the number element to include both integers and floats -->\n",
" <xs:complexType name=\"NumberType\">\n",
" <xs:simpleContent>\n",
" <xs:extension base=\"xs:decimal\"/>\n",
" </xs:simpleContent>\n",
" </xs:complexType>\n",
"\n",
" <!-- Recursive complex type for the operation element -->\n",
" <xs:complexType name=\"OperationType\">\n",
" <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n",
" <xs:element name=\"number\" type=\"NumberType\"/>\n",
" <xs:element name=\"operation\" type=\"OperationType\"/>\n",
" </xs:choice>\n",
" <xs:attribute name=\"operator\" type=\"OperatorType\" use=\"required\"/>\n",
" </xs:complexType>\n",
"\n",
" <!-- Definition for RandomType -->\n",
" <xs:complexType name=\"RandomType\">\n",
" <xs:simpleContent>\n",
" <xs:extension base=\"xs:string\">\n",
" <xs:attribute name=\"length\" type=\"xs:integer\" use=\"optional\"/>\n",
" </xs:extension>\n",
" </xs:simpleContent>\n",
" </xs:complexType>\n",
"\n",
" <!-- Add more types to here -->\n",
"\n",
" <!-- Complex type for the invoke element with optional operation and randomstring -->\n",
" <xs:complexType name=\"InvokeType\">\n",
" <xs:sequence>\n",
" <xs:element name=\"tool_name\" type=\"xs:string\"/>\n",
" <xs:element name=\"operation\" type=\"OperationType\" minOccurs=\"0\"/>\n",
" <xs:element name=\"randomstring\" type=\"RandomType\" minOccurs=\"0\"/>\n",
" <!-- Add more type references to here -->\n",
" </xs:sequence>\n",
" </xs:complexType>\n",
"\n",
" <!-- Complex type for the function_calls element -->\n",
" <xs:complexType name=\"FunctionCallsType\">\n",
" <xs:sequence>\n",
" <xs:element name=\"invoke\" type=\"InvokeType\" minOccurs=\"1\" maxOccurs=\"unbounded\"/>\n",
" </xs:sequence>\n",
" </xs:complexType>\n",
"\n",
" <!-- Root element -->\n",
" <xs:element name=\"function_calls\" type=\"FunctionCallsType\"/>\n",
"\n",
"</xs:schema>\n",
"\"\"\"\n"
],
"metadata": {
"id": "vzYR_G3rOC95"
},
"execution_count": 5,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Defining Tools for Function Calls\n",
"\n",
"With the provided titles, names, descriptions, and corresponding XML Schema types for each tool, you have created two structured tool descriptions designed for inclusion in a system prompt for Claude. These descriptions encapsulate the functionality and expected inputs for an Arithmetic Operation Tool and a Random String Generator, respectively. By formatting these descriptions according to the specified schema, you ensure that the tools are clearly defined and understandable within the context of interacting with Claude. The `tools` list now contains these formatted descriptions, ready to be integrated into a system prompt or documentation that instructs on how to utilize these tools within an AI-assisted environment."
],
"metadata": {
"id": "w2QfPChz3tqX"
}
},
{
"cell_type": "markdown",
"source": [
"### Tool 1: Arithmetic Operation Tool"
],
"metadata": {
"id": "fQkPXhbXDlZg"
}
},
{
"cell_type": "code",
"source": [
"# Create tools for the system prompt\n",
"title = \"Arithmetic Operation Tool\"\n",
"# This name must be defined in extract_and_evaluate_operations function\n",
"name = \"do_pairwise_arithmetic\"\n",
"description = \"Performs arithmetic operations that can be nested, each defined by an operator and consisting of operands that are either numbers or further operations.\"\n",
"# OperationType refers to schema\n",
"tool1 = construct_format_tool_for_claude_prompt(title, name, description, 'OperationType')"
],
"metadata": {
"id": "2beK8PkIyUxg"
},
"execution_count": 6,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"### Tool 2: Random String Generator"
],
"metadata": {
"id": "HaugqhVFDqd1"
}
},
{
"cell_type": "code",
"source": [
"title = \"Random String Generator\"\n",
"# This name must be defined in extract_and_evaluate_operations function\n",
"name = \"do_random_string\"\n",
"description = \"Generates a random string of a specified length, providing a tool for creating unique identifiers, test data, or any scenario where random string generation is needed. The length of the string can be adjusted to range from 1 to 1024 characters.\"\n",
"# RandomType refers to schema\n",
"tool2 = construct_format_tool_for_claude_prompt(title, name, description, 'RandomType')"
],
"metadata": {
"id": "x8-_jFxVDq28"
},
"execution_count": 7,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"### Add Tools"
],
"metadata": {
"id": "s5F50WfaD3Hc"
}
},
{
"cell_type": "code",
"source": [
"# Add tools to the list\n",
"tools = [tool1, tool2]"
],
"metadata": {
"id": "745AcfoND3cc"
},
"execution_count": 8,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Final Results\n",
"\n",
"In this scenario, the `function_calling` function is designed to process the `user_prompt`, which requests the multiplication of two large numbers and the generation of a random string of 32 characters. The function utilizes the `tools` list, which contains descriptions of available tools (an Arithmetic Operation Tool and a Random String Generator), along with the `function_call_schema` for XML validation. Here's an outline of what happens within `function_calling`:\n",
"\n",
"1. **Construct System Prompt**: The function first constructs a comprehensive system prompt for Claude using the `tools` descriptions. This prompt outlines how to invoke the available tools and the parameters they accept, based on the schema.\n",
"\n",
"2. **Generate XML for Function Calls**: Claude is then prompted to generate XML representing the function calls specified by the `user_prompt`. This step involves Claude interpreting the user's request, translating it into structured XML that conforms to the `function_call_schema`.\n",
"\n",
"3. **Validate Generated XML**: The generated XML is validated against the `function_call_schema` to ensure it accurately represents the requested operations and adheres to the defined structure and data types.\n",
"\n",
"4. **Process Valid XML**: If the XML is valid, the `function_calling` function processes it to perform the specified arithmetic operation and random string generation. This involves evaluating the XML to compute the result of the multiplication and generate the requested random string.\n",
"\n",
"5. **Construct Final Message**: Finally, the function constructs a message summarizing the results of the processed operations, which might include the outcome of the multiplication and the generated random string, formatted for clear communication back to the user.\n",
"\n",
"This process demonstrates an advanced integration of natural language processing, XML manipulation, schema validation, and AI-driven interaction to dynamically respond to user prompts with precise computational operations and custom data generation."
],
"metadata": {
"id": "WpFMOhVA4Xr9"
}
},
{
"cell_type": "code",
"source": [
"user_prompt = \"Multiply 1,984,135 by 9,343,116 and make a random string of 32 characters\"\n",
"\n",
"message = function_calling(client, user_prompt, tools, function_call_schema)\n",
"\n",
"print(message)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "cT8tIhXeI4Px",
"outputId": "5d359791-7fdb-46e5-8bf9-db29325f53d4"
},
"execution_count": 9,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n",
"\n",
"The first invoke calls the arithmetic operation tool to multiply 1,984,135 by 9,343,116, which gives the result 18538003464660.\n",
"\n",
"The second invoke calls the random string generator tool to generate a random string of length 32, which produces the string \"0wgTEIjJdnnq4C2D29rL5YrH2jSgAyIc\".\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"### Arithmetic Operation in Sequence"
],
"metadata": {
"id": "x1ykTdrK584H"
}
},
{
"cell_type": "code",
"source": [
"user_prompt = \"Multiply 123 by 234 and divide by 2*3\"\n",
"\n",
"message = function_calling(client, user_prompt, tools, function_call_schema)\n",
"\n",
"print(message)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "THWu77jQTIPN",
"outputId": "5b9c3e32-6719-47fe-cdd7-a8486476b4eb"
},
"execution_count": 10,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n",
"\n",
"To break this down:\n",
"1. Multiply 123 by 234 to get 28782\n",
"2. Multiply 2 by 3 to get 6 \n",
"3. Divide 28782 by 6 to get 4797\n",
"\n",
"So the final result of multiplying 123 by 234 and then dividing by 2*3 is 4797.\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"### Nested Calculation with XML Message Debug\n",
"\n",
"**Debugging Output Option**: Since debug_xmls is set to True, the function would print the system prompt constructed from the provided tools and the XML generated in response to the user_prompt. This output would give insight into how the system interprets and plans to execute the calculation."
],
"metadata": {
"id": "SEVRf6_Z5iri"
}
},
{
"cell_type": "code",
"source": [
"# Let system decide the precedence order of aritchmetic operations\n",
"#user_prompt = \"Calculate 3*1+2-1/2\"\n",
"\n",
"# Define the precedence order of aritchmetic operations\n",
"user_prompt = \"Calculate (3*(1+2)-1)/2\"\n",
"\n",
"message = function_calling(client, user_prompt, tools, function_call_schema, True)\n",
"\n",
"print(message)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "IsqM-tEDTpsW",
"outputId": "e666c3b2-a04b-4886-8542-b7b141fd1769"
},
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n",
"\n",
"System prompt: In this environment you have access to a set of tools you can use to answer the user's question.\n",
"\n",
"You may call them like this:\n",
"<function_calls>\n",
"<invoke>\n",
"<tool_name>$TOOL_NAME</tool_name>\n",
"$PARAMETERS\n",
"</invoke>\n",
"</function_calls>\n",
"\n",
"Here are the tools available:\n",
"<tools>\n",
"<tool_description>\n",
"<tool_title>Arithmetic Operation Tool</tool_title>\n",
"<tool_name>do_pairwise_arithmetic</tool_name>\n",
"<description>\n",
"Performs arithmetic operations that can be nested, each defined by an operator and consisting of operands that are either numbers or further operations.\n",
"</description>\n",
"<operation type=\"OperationType\"/>\n",
"</tool_description>\n",
"<tool_description>\n",
"<tool_title>Random String Generator</tool_title>\n",
"<tool_name>do_random_string</tool_name>\n",
"<description>\n",
"Generates a random string of a specified length, providing a tool for creating unique identifiers, test data, or any scenario where random string generation is needed. The length of the string can be adjusted to range from 1 to 1024 characters.\n",
"</description>\n",
"<operation type=\"RandomType\"/>\n",
"</tool_description>\n",
"</tools>\n",
"\n",
"<!-- Schemas -->\n",
"\n",
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n",
"\n",
" <!-- Simple type for the operator attribute -->\n",
" <xs:simpleType name=\"OperatorType\">\n",
" <xs:restriction base=\"xs:string\">\n",
" <xs:enumeration value=\"+\"/>\n",
" <xs:enumeration value=\"-\"/>\n",
" <xs:enumeration value=\"*\"/>\n",
" <xs:enumeration value=\"/\"/>\n",
" </xs:restriction>\n",
" </xs:simpleType>\n",
"\n",
" <!-- Complex type for the number element to include both integers and floats -->\n",
" <xs:complexType name=\"NumberType\">\n",
" <xs:simpleContent>\n",
" <xs:extension base=\"xs:decimal\"/>\n",
" </xs:simpleContent>\n",
" </xs:complexType>\n",
"\n",
" <!-- Recursive complex type for the operation element -->\n",
" <xs:complexType name=\"OperationType\">\n",
" <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n",
" <xs:element name=\"number\" type=\"NumberType\"/>\n",
" <xs:element name=\"operation\" type=\"OperationType\"/>\n",
" </xs:choice>\n",
" <xs:attribute name=\"operator\" type=\"OperatorType\" use=\"required\"/>\n",
" </xs:complexType>\n",
"\n",
" <!-- Definition for RandomType -->\n",
" <xs:complexType name=\"RandomType\">\n",
" <xs:simpleContent>\n",
" <xs:extension base=\"xs:string\">\n",
" <xs:attribute name=\"length\" type=\"xs:integer\" use=\"optional\"/>\n",
" </xs:extension>\n",
" </xs:simpleContent>\n",
" </xs:complexType>\n",
"\n",
" <!-- Add more types to here -->\n",
"\n",
" <!-- Complex type for the invoke element with optional operation and randomstring -->\n",
" <xs:complexType name=\"InvokeType\">\n",
" <xs:sequence>\n",
" <xs:element name=\"tool_name\" type=\"xs:string\"/>\n",
" <xs:element name=\"operation\" type=\"OperationType\" minOccurs=\"0\"/>\n",
" <xs:element name=\"randomstring\" type=\"RandomType\" minOccurs=\"0\"/>\n",
" <!-- Add more type references to here -->\n",
" </xs:sequence>\n",
" </xs:complexType>\n",
"\n",
" <!-- Complex type for the function_calls element -->\n",
" <xs:complexType name=\"FunctionCallsType\">\n",
" <xs:sequence>\n",
" <xs:element name=\"invoke\" type=\"InvokeType\" minOccurs=\"1\" maxOccurs=\"unbounded\"/>\n",
" </xs:sequence>\n",
" </xs:complexType>\n",
"\n",
" <!-- Root element -->\n",
" <xs:element name=\"function_calls\" type=\"FunctionCallsType\"/>\n",
"\n",
"</xs:schema>\n",
"\n",
"\n",
"\n",
"Assistant message: <function_calls>\n",
"<invoke>\n",
"<tool_name>do_pairwise_arithmetic</tool_name>\n",
"<operation operator=\"/\">\n",
" <operation operator=\"-\">\n",
" <operation operator=\"*\">\n",
" <number>3</number>\n",
" <operation operator=\"+\">\n",
" <number>1</number>\n",
" <number>2</number>\n",
" </operation>\n",
" </operation>\n",
" <number>1</number>\n",
" </operation>\n",
" <number>2</number>\n",
"</operation>\n",
"</invoke>\n",
"</function_calls>\n",
"<function_results>\n",
"<result>\n",
"<tool_name>do_pairwise_arithmetic</tool_name>\n",
"<stdout>\n",
"4.0\n",
"</stdout>\n",
"</result>\n",
"</function_results>\n",
"\n",
"\n",
"The calculation proceeds as follows:\n",
"1. (1 + 2) = 3\n",
"2. (3 * 3) = 9 \n",
"3. (9 - 1) = 8\n",
"4. (8 / 2) = 4.0\n",
"\n",
"So the result of the expression (3*(1+2)-1)/2 is 4.0.\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"## Conclusion\n",
"\n",
"In this Colab notebook, we've demonstrated a comprehensive approach to integrating and automating XML-based function calls within a Python environment, leveraging the Anthropic Claude model for processing natural language inputs into structured XML commands. The notebook includes a detailed XML Schema Definition (XSD) that outlines the structure for arithmetic operations and random string generation requests, ensuring precise and validated XML document generation that aligns with specified functionalities.\n",
"\n",
"Through a series of meticulously designed functions, the notebook showcases the process of parsing user prompts into XML format, validating these XML documents against the defined schema, and executing the described operations. This includes arithmetic calculations and the generation of secure random strings, embodying a flexible and dynamic system capable of handling complex user requests with accuracy and efficiency.\n",
"\n",
"Furthermore, the notebook illustrates the power of AI-driven automation in parsing and responding to natural language queries, translating them into executable actions that adhere to strict data type and structure requirements. By integrating tools like the Anthropic Claude model, the system not only enhances the precision of operations but also extends its applicability to a wide range of computational and data generation tasks."
],
"metadata": {
"id": "W06R7m-r8eTT"
}
}
]
}
@wd021
Copy link

wd021 commented Jul 10, 2025

👀 amazing.. share your prompt knowledge 🧠 with God Tier Prompts!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment