{"id":2084,"date":"2024-03-11T12:31:21","date_gmt":"2024-03-11T19:31:21","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/semantic-kernel\/?p=2084"},"modified":"2024-05-16T03:54:49","modified_gmt":"2024-05-16T10:54:49","slug":"python-auto-tool-calling-openai-models","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/agent-framework\/python-auto-tool-calling-openai-models\/","title":{"rendered":"Enhanced Automation in Python: Auto Tool Calling for OpenAI Models in the Semantic Kernel SDK"},"content":{"rendered":"<p class=\"code-line\" dir=\"auto\" data-line=\"2\">Greetings, Semantic Kernel Python developers and enthusiasts! We&#8217;re happy to share a significant update to the Semantic Kernel Python SDK now available in 0.9.1b1 &#8212; a leap towards more efficient and streamlined OpenAI model integration. Your feedback, the need to align with the .NET Semantic Kernel SDK, and our commitment to enhancing developer experiences have guided this feature. Let\u2019s dive into what\u2019s new and how it can transform your workflow.<\/p>\n<h2 id=\"the-initial-challenge\" class=\"code-line\" dir=\"auto\" style=\"margin-top: 2rem;\" data-line=\"4\">The Initial Challenge: Manual Tool Integration<\/h2>\n<p>Previous to this feature, integrating OpenAI models and using tool calls in your projects has traditionally required a hands-on approach. Developers needed to manually manage these tool calls, process results, and ensure seamless communication back to the models. Effective, yet time-consuming, this method often diverted attention from more strategic tasks. A separate utility method, which may not have been very apparent, also handled this. There was a lot of hand-holding involved to get a result from the prompt function and its function result.<\/p>\n<h2 id=\"our-solution\" class=\"code-line\" dir=\"auto\" style=\"margin-top: 2rem;\" data-line=\"4\">Our Solution: Streamlined Auto Tool Calling<\/h2>\n<p>Aiming to refine this process, we\u2019ve introduced an innovative feature: automatic tool calling. This development is born from our aspiration to simplify interactions with OpenAI models, making them more intuitive and less labor-intensive. By integrating the kernel directly with our <code>OpenAIChatCompletionBase<\/code>, we&#8217;ve enabled the SDK to automatically manage tool calls initiated by OpenAI models. This integration not only reduces manual overhead but also enriches the flexibility of our SDK, catering to both automated and manual preferences. Note that the OpenAI model requires forming the <code>tool_call<\/code> responses in a particular order: you must respond to all tool call IDs returned from the model with the <code>tool_call_id<\/code>, the function name, and a result. Additionally, if auto invoke tool calling is enabled and <code>num_of_responses<\/code>\u00a0is greater than one, we provide a warning message and re-configure the <code>num_of_responses<\/code> to be one.<\/p>\n<ol class=\"code-line\" dir=\"auto\" data-line=\"6\">\n<li class=\"code-line\" dir=\"auto\" data-line=\"6\">Define the AzureOpenAI\/OpenAI Chat Service.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"7\">While configuring the prompt execution settings, specify that tools are available by utilizing the utility function <code>get_tool_call_object<\/code> that is part of <code>semantic_kernel.connectors.ai.open_ai.utils<\/code>. You can also specify <code>tool_choice<\/code> on the settings to be <code>auto<\/code>\u00a0or to be a specific tool only.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"8\">To enable automatic invocation of tool calls, configure the prompt execution settings by setting <code>auto_invoke_kernel_functions=True<\/code> and specifying your desired number of attempts in <code>max_auto_invoke_attempts<\/code>.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"9\">Add any required plugins to the kernel that the LLM may utilize to complete the prompt\/query.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"10\">Use kernel invoke to invoke the prompt function.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"11\">The LLM will respond with a <code>tools_call<\/code> finish reason. This means the chat completion contains tool calls that we need to handle. We construct the ToolCall object with the required data. These are attributes like: the tool call ID, the function name, and its arguments.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"12\">When we enable auto tool calling, we loop through the required tool calls and use the kernel to invoke the functions with the required arguments. The result of the function calls are added to the chat history with the role <code>tool_call<\/code>.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"13\">If you disable auto tool calling, then you must manually handle the tool calls that the model returns, and send back the response in the correct order, as the referenced kernel example shows.<\/li>\n<\/ol>\n<h2 id=\"example-usage\" class=\"code-line\" dir=\"auto\" style=\"margin-top: 2rem;\" data-line=\"4\">Example Usage<\/h2>\n<p>We&#8217;ve included a kernel example that showcases using this new functionality. It handles streaming and non-streaming auto tool calls. It also has some helper code to show how to handle tool calls if auto invoke tool calls is disabled (via prompt execution settings). By default, the prompt execution settings have the auto invoke tool calls as disabled, so please note that when configuring your OpenAIChatPromptExecutionSettings\/AzureOpenAIChatPromptExecutionSettings. The following code is a slimmed down version of the following <a href=\"https:\/\/github.com\/microsoft\/semantic-kernel\/blob\/main\/python\/samples\/concepts\/auto_function_calling\/chat_gpt_api_function_calling.py\">kernel example<\/a>.<\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">&lt;required imports here&gt;\r\n\r\nkernel = sk.Kernel()\r\n\r\n# Note: the underlying gpt-35\/gpt-4 model version needs to be at least version 0613 to support tools.\r\napi_key, org_id = sk.openai_settings_from_dot_env()\r\nkernel.add_service(\r\n    sk_oai.OpenAIChatCompletion(\r\n        service_id=\"chat\",\r\n        ai_model_id=\"gpt-3.5-turbo-1106\",\r\n        api_key=api_key,\r\n    ),\r\n)\r\n\r\nplugins_directory = os.path.join(__file__, \"..\/..\/..\/..\/samples\/plugins\")\r\nkernel.import_plugin_from_object(MathPlugin(), plugin_name=\"math\")\r\nkernel.import_plugin_from_object(TimePlugin(), plugin_name=\"time\")\r\n\r\n# Note: the number of responses for auto invoking tool calls is limited to 1.\r\n# If configured to be greater than one, this value will be overridden to 1.\r\nexecution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\r\n    service_id=\"chat\",\r\n    max_tokens=2000,\r\n    temperature=0.7,\r\n    top_p=0.8,\r\n    tool_choice=\"auto\",\r\n    tools=get_tool_call_object(kernel, {\"exclude_plugin\": [\"ChatBot\"]}),\r\n    auto_invoke_kernel_functions=True,\r\n    max_auto_invoke_attempts=3,\r\n)\r\n\r\nprompt_template_config = sk.PromptTemplateConfig(\r\n    template=\"{{$chat_history}}{{$user_input}}\",\r\n    name=\"chat\",\r\n    template_format=\"semantic-kernel\",\r\n    input_variables=[\r\n        InputVariable(name=\"user_input\", description=\"The user input\", is_required=True),\r\n        InputVariable(name=\"chat_history\", description=\"The history of the conversation\", is_required=True),\r\n    ],\r\n    execution_settings={\"chat\": execution_settings},\r\n)\r\n\r\nhistory = ChatHistory()\r\n\r\nhistory.add_system_message(system_message)\r\nhistory.add_user_message(\"Hi there, who are you?\")\r\nhistory.add_assistant_message(\"I am Mosscap, a chat bot. I'm trying to figure out what people need.\")\r\n\r\narguments = KernelArguments()\r\n\r\nchat_function = kernel.create_function_from_prompt(\r\n    prompt_template_config=prompt_template_config,\r\n    plugin_name=\"ChatBot\",\r\n    function_name=\"Chat\",\r\n)\r\n\r\n\r\nasync def chat() -&gt; bool:\r\n    user_input = input(\"User:&gt; \")\r\n\r\n    result = await kernel.invoke(chat_function, user_input=user_input, chat_history=history)\r\n\r\n    # If tools are used, and auto invoke tool calls is False, the response will be of type\r\n    # OpenAIChatMessageContent with information about the tool calls, which need to be sent\r\n    # back to the model to get the final response.\r\n    if not execution_settings.auto_invoke_kernel_functions and isinstance(\r\n        result.value[0], OpenAIChatMessageContent\r\n    ):\r\n        # print_tool_calls(result.value[0])\r\n        # return True\r\n\r\n    print(f\"Mosscap:&gt; {result}\")\r\n    return True\r\n\r\n\r\nasync def main() -&gt; None:\r\n    chatting = True\r\n    print(\r\n        \"Welcome to the chat bot!\\\r\n        \\n  Type 'exit' to exit.\\\r\n        \\n  Try a math question to see the function calling in action (i.e. what is 3+3?).\"\r\n    )\r\n    while chatting:\r\n        chatting = await chat()\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    asyncio.run(main())<\/code><\/pre>\n<h2 id=\"why-this-matters\" class=\"code-line\" dir=\"auto\" style=\"margin-top: 2rem;\" data-line=\"4\">Why This Matters<\/h2>\n<ol class=\"code-line\" dir=\"auto\" data-line=\"6\">\n<li class=\"code-line\" dir=\"auto\" data-line=\"6\">Enhanced Productivity: Automating routine tasks allows developers to concentrate on innovation and complex problem-solving.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"7\">Customizable Interactions: Whether you prefer the SDK to handle tool calls automatically or you opt for manual input, the choice is yours.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"8\">Universal Compatibility: Designed to support both streaming and non-streaming chat completions, this feature is versatile and adaptable to various use cases.<\/li>\n<\/ol>\n<h2 id=\"moving-forward\" class=\"code-line\" dir=\"auto\" style=\"margin-top: 2rem;\" data-line=\"4\">Moving Forward<\/h2>\n<p>We&#8217;re excited to introduce this update as it opens up the ability to use the OpenAI models in a more advanced way. It also allows us to move forward with our plans to support the FunctionCallingStepwise planner, which already exists in the .NET SDK.<\/p>\n<p>We value your input and encourage you to share your experiences, suggestions, and stories of how you&#8217;re using the Semantic Kernel SDK. Your insights are crucial as we continue to evolve and enhance our offerings. As always, you can find our Python public backlog <a href=\"https:\/\/github.com\/orgs\/microsoft\/projects\/866\/views\/3?sliceBy%5Bvalue%5D=python\">here<\/a>, as well as our Python roadmap blog <a href=\"https:\/\/devblogs.microsoft.com\/semantic-kernel\/road-to-v1-0-for-the-python-semantic-kernel-sdk\/\">here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Greetings, Semantic Kernel Python developers and enthusiasts! We&#8217;re happy to share a significant update to the Semantic Kernel Python SDK now available in 0.9.1b1 &#8212; a leap towards more efficient and streamlined OpenAI model integration. Your feedback, the need to align with the .NET Semantic Kernel SDK, and our commitment to enhancing developer experiences have [&hellip;]<\/p>\n","protected":false},"author":150043,"featured_media":2301,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[34,1],"tags":[],"class_list":["post-2084","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python-2","category-semantic-kernel"],"acf":[],"blog_post_summary":"<p>Greetings, Semantic Kernel Python developers and enthusiasts! We&#8217;re happy to share a significant update to the Semantic Kernel Python SDK now available in 0.9.1b1 &#8212; a leap towards more efficient and streamlined OpenAI model integration. Your feedback, the need to align with the .NET Semantic Kernel SDK, and our commitment to enhancing developer experiences have [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts\/2084","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/users\/150043"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/comments?post=2084"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts\/2084\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/media\/2301"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/media?parent=2084"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/categories?post=2084"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/tags?post=2084"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}