Azure 函数示例

注意:OpenAI 库有新版本可用。请参阅 https://github.com/openai/openai-python/discussions/742

本笔记本展示了如何将函数调用功能与 Azure OpenAI 服务结合使用。函数允许聊天补全的调用者定义模型可以用来扩展其功能到外部工具和数据源的功能。

您可以在 OpenAI 的博客上阅读更多关于聊天函数的信息:https://openai.com/blog/function-calling-and-other-api-updates

注意:聊天函数需要模型版本以 gpt-4 和 gpt-35-turbo 的 -0613 标签开头。旧版本模型不支持它们。

设置

首先,我们安装必要的依赖项。

! pip install "openai>=0.28.1,<1.0.0"
# (可选) 如果您想使用 Microsoft Active Directory
! pip install azure-identity
import os
import openai

此外,为了正确访问 Azure OpenAI 服务,我们需要在 Azure 门户 上创建相应的资源(您可以在 Microsoft Docs 中查看详细的操作指南)。

创建资源后,我们需要使用的第一项是其终结点。您可以从“资源管理”下的“密钥和终结点”部分找到终结点。有了这个,我们将使用此信息设置 SDK:

openai.api_base = "" # 在此处添加您的终结点

# functions 仅支持 2023-07-01-preview API 版本
openai.api_version = "2023-07-01-preview"

身份验证

Azure OpenAI 服务支持多种身份验证机制,包括 API 密钥和 Azure 凭据。

use_azure_active_directory = False

使用 API 密钥进行身份验证

要设置 OpenAI SDK 以使用 Azure API 密钥,我们需要将 api_type 设置为 azure,并将 api_key 设置为与您的终结点关联的密钥(您可以在 Azure 门户 的“资源管理”下的“密钥和终结点”中找到此密钥)。

if not use_azure_active_directory:
    openai.api_type = "azure"
    openai.api_key = os.environ["OPENAI_API_KEY"]

注意:在此示例中,我们通过在代码中设置变量来配置库以使用 Azure API。对于开发,请考虑改用环境变量:

OPENAI_API_BASE
OPENAI_API_KEY
OPENAI_API_TYPE
OPENAI_API_VERSION

使用 Microsoft Active Directory 进行身份验证

现在让我们看看如何通过 Microsoft Active Directory 身份验证获取密钥。

from azure.identity import DefaultAzureCredential

if use_azure_active_directory:
    default_credential = DefaultAzureCredential()
    token = default_credential.get_token("https://cognitiveservices.azure.com/.default")

    openai.api_type = "azure_ad"
    openai.api_key = token.token

令牌的有效期是有限的,之后会过期。为了确保每次请求都发送有效的令牌,您可以通过挂钩到 requests.auth 来刷新即将过期的令牌:

import typing
import time
import requests

if typing.TYPE_CHECKING:
    from azure.core.credentials import TokenCredential

class TokenRefresh(requests.auth.AuthBase):

    def __init__(self, credential: "TokenCredential", scopes: typing.List[str]) -> None:
        self.credential = credential
        self.scopes = scopes
        self.cached_token: typing.Optional[str] = None

    def __call__(self, req):
        if not self.cached_token or self.cached_token.expires_on - time.time() < 300:
            self.cached_token = self.credential.get_token(*self.scopes)
        req.headers["Authorization"] = f"Bearer {self.cached_token.token}"
        return req

if use_azure_active_directory:
    session = requests.Session()
    session.auth = TokenRefresh(default_credential, ["https://cognitiveservices.azure.com/.default"])

    openai.requestssession = session

函数

设置和身份验证完成后,您现在可以与 Azure OpenAI 服务一起使用函数了。这分为几个步骤:

  1. 定义函数
  2. 将函数定义传递给聊天补全 API
  3. 使用响应中的参数调用函数
  4. 将函数响应反馈给聊天补全 API

1. 定义函数

可以定义一个函数列表,每个函数包含函数名称、可选描述以及函数接受的参数(描述为 JSON schema)。

functions = [
    {
        "name": "get_current_weather",
        "description": "获取当前天气",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "城市和州,例如旧金山,加利福尼亚州",
                },
                "format": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "要使用的温度单位。请根据用户的地点推断此信息。",
                },
            },
            "required": ["location"],
        },
    }
]

2. 将函数定义传递给聊天补全 API

现在我们可以将函数传递给聊天补全 API。如果模型确定应调用该函数,则 choices 中的 finish_reason 将被填充为“function_call”,并且 message 中将包含有关调用哪个函数及其参数的详细信息。可选地,您可以将 function_call 关键字参数设置为 {"name": get_current_weather} 来强制模型调用特定函数。默认情况下,此设置为 auto,允许模型自行决定是否调用该函数。

messages = [
    {"role": "system", "content": "不要假设要将哪些值填入函数。如果用户请求不明确,请寻求澄清。"},
    {"role": "user", "content": "今天西雅图的天气怎么样?"}
]

chat_completion = openai.ChatCompletion.create(
    deployment_id="gpt-35-turbo-0613",
    messages=messages,
    functions=functions,
)
print(chat_completion)
{
  "choices": [
    {
      "content_filter_results": {},
      "finish_reason": "function_call",
      "index": 0,
      "message": {
        "function_call": {
          "arguments": "{\n  \"location\": \"Seattle, WA\"\n}",
          "name": "get_current_weather"
        },
        "role": "assistant"
      }
    }
  ],
  "created": 1689702512,
  "id": "chatcmpl-7dj6GkYdM7Vw9eGn02bc2qqjN70Ps",
  "model": "gpt-4",
  "object": "chat.completion",
  "prompt_annotations": [
    {
      "content_filter_results": {
        "hate": {
          "filtered": False,
          "severity": "safe"
        },
        "self_harm": {
          "filtered": False,
          "severity": "safe"
        },
        "sexual": {
          "filtered": False,
          "severity": "safe"
        },
        "violence": {
          "filtered": False,
          "severity": "safe"
        }
      },
      "prompt_index": 0
    }
  ],
  "usage": {
    "completion_tokens": 18,
    "prompt_tokens": 115,
    "total_tokens": 133
  }
}

3. 使用响应中的参数调用函数

函数调用的名称将是最初提供的名称之一,参数将包括与函数定义中的模式匹配的 JSON。

import json

def get_current_weather(request):
    """
    此函数仅用于说明目的。
    地点和单位应根据天气情况确定,而不是返回硬编码的响应。
    """
    location = request.get("location")
    unit = request.get("unit")
    return {"temperature": "22", "unit": "celsius", "description": "晴朗"}

function_call =  chat_completion.choices[0].message.function_call
print(function_call.name)
print(function_call.arguments)

if function_call.name == "get_current_weather":
    response = get_current_weather(json.loads(function_call.arguments))
get_current_weather
{
  "location": "Seattle, WA"
}

4. 将函数响应反馈给聊天补全 API

函数响应应序列化为一条新消息,角色设置为“function”。现在模型将使用响应数据来制定其答案。

messages.append(
    {
        "role": "function",
        "name": "get_current_weather",
        "content": json.dumps(response)
    }
)

function_completion = openai.ChatCompletion.create(
    deployment_id="gpt-35-turbo-0613",
    messages=messages,
    functions=functions,
)

print(function_completion.choices[0].message.content.strip())
今天西雅图天气晴朗,气温为 22 摄氏度。