Введение

Это моя первая статья среднего размера, надеюсь, она окажется для вас полезной, это новая горячая тема, и я изо всех сил стараюсь обеспечить вам качество.

Функции OpenAI воплощают высокий уровень обобщения, которого достигли крупные языковые модели, такие как GPT-3.5 и GPT-4, и демонстрируют свою мощь элегантным и меняющим правила игры способом.

Это будет практическое руководство, так что будьте готовы.

Предварительные требования

  • Хорошее знание Python.
  • Знание OpenAI API (необязательно).

Что такое функции OpenAI?

Функции OpenAI — это функция openai API, которая позволяет моделям gpt-3.5 и gpt-4 получать дополнительную роль сообщения `function`, чтобы расширить ее возможности и раздвинуть границы возможностей языковых моделей.

Конкретно, у него очень строгие правила: мы должны передать объект сообщения внутри ключа сообщений тела запроса с ролью `function`, а содержимое должно соответствовать `json_schema` это означает, что мы должны предоставить следующую структуру данных в виде строки json:

{
  "name": "string",
  "description": "string",
  "parameters": "object"
}

Мы можем передать массив этих сообщений, позволяя модели из нескольких вариантов вывести значения свойств объекта на основе предоставленных метаданных и приглашения пользователя, тем самым возвращая строку json, представляющую объект выбранной функции с выведенными значениями, в противном случае он просто вернет обычный ответ о завершении чата.

Итак, как мы с ними работаем?

Давайте сами попрактикуемся в обобщении. Ключевой частью здесь является создание `json_schema`, и, конечно, мы не собираемся делать это вручную, поэтому давайте воспользуемся преимуществами библиотеки `pydantic` для сделай это для нас.

# schema.py
from typing import Any, List, Type, TypeVar
from pydantic import BaseModel, Field
from abc import ABC, abstractmethod

F = TypeVar("F", bound="OpenAIFunction")

class FunctionCall(BaseModel):
    name: str
    data: Any


class OpenAIFunction(BaseModel, ABC):
    class Metadata:
        subclasses: List[Type[F]] = []

    @classmethod
    def __init_subclass__(cls, **kwargs: Any):
        super().__init_subclass__(**kwargs)
        _schema = cls.schema()
        if cls.__doc__ is None:
            raise ValueError(
    f"OpenAIFunction subclass {cls.__name__} must have a docstring"
   )
        cls.openaischema = {
            "name": cls.__name__,
            "description": cls.__doc__,
            "parameters": {
                "type": "object",
                "properties": {
                    k: v for k, v in _schema["properties"].items() if k != "self"
                },
                "required": [
                    k
                    for k, v in _schema["properties"].items()
                    if v.get("required", False)
                ],
            },
        }
        cls.Metadata.subclasses.append(cls)

    async def __call__(self, **kwargs: Any) -> FunctionCall:
        response = await self.run(**kwargs)
  return FunctionCall(name=self.__class__.__name__, data=response)

    @abstractmethod
    async def run(self, **kwargs: Any) -> Any:
        ...

Здесь мы автоматизировали создание документа `json_schema` с помощью библиотеки `pydantic`, мы также создали базовый класс для наших функций, который будет использоваться для их регистрации в атрибут класса `Metadata` и объявил абстрактный метод `run`, который будет фактической реализацией нашей функции. Имея автоматизированный, централизованный уникальный источник достоверной информации о наших функциях, теперь нам нужен способ их регистрации и вызова.

# service.py
import openai
from .schema import *


async def parse_openai_function(
    response: dict,
    functions: List[Type[F]] = OpenAIFunction.Metadata.subclasses,
    **kwargs: Any,
) -> FunctionCall:
    choice = response["choices"][0]["message"]
    if "function_call" in choice:
        function_call_ = choice["function_call"]
        name = function_call_["name"]
        arguments = function_call_["arguments"]
        for i in functions:
            if i.__name__ == name:
                result = await i(**json.loads(arguments))(**kwargs)
                break
        else:
            raise ValueError(f"Function {name} not found")
        return result
    return FunctionCall(name="chat", data=choice["content"])


async def function_call(
    text: str,
    model: str = "gpt-3.5-turbo-16k-0613",
    functions: List[Type[F]] = OpenAIFunction.Metadata.subclasses,
    **kwargs,
) -> FunctionCall:
    messages = [
        {"role": "user", "content": text},
        {"role": "system", "content": "You are a function Orchestrator"},
    ]
    response = await openai.ChatCompletion.acreate(
        model=model,
        messages=messages,
        functions=[i.openaischema for i in functions],
    )
    return await parse_openai_function(response, functions=functions, **kwargs)

Теперь, когда все готово, мы можем создать пару функций и протестировать их.

# functions.py
from typing import Any, List, Optional
from .schema import *

async def chat_completion(text: str, context: Optional[str] = None):
 if context is not None:
  messages = [
   {"role": "user", "content": text},
   {"role": "system", "content": context},
  ]
 else:
  messages = [{"role": "user", "content": text}]
 response = await openai.ChatCompletion.acreate(
  model="gpt-3.5-turbo-16k-0613", messages=messages
 )
 return response["choices"][0]["message"]["content"]


class Quiz(OpenAIFunction):
 """Generates a set of questions of a given topic."""
 topic: str = Field(description="Topic to generate questions about.")
 quantity: int = Field(default=5, gt=0, lt=11, description="Number of questions to generate.")
 questions:Optional[List[str]] = Field(default=None, description="List of questions to generate answers for.")

 async def ask(self, **kwargs: Any) -> str:
  context = f"You are an expert on {self.topic}."
  text = f"Please formulate a non trivial question about {self.topic} to asses candidate knowledge about the subject. These questions were already asked: {self.questions}, ask a different one."
  response = await chat_completion(text, context=context)
  if self.questions is None:
   self.questions = [response]
  else:
   self.questions.append(response)
  return response

 async def run(self, **kwargs: Any) ->List[str]:
  for _ in range(self.quantity):
   await self.ask(**kwargs)
  return self.questions

class Song(OpenAIFunction):
 """Generates a song of a given genre."""
 title: str = Field(description="Title of the song.")
 genre: str = Field(default="pop", description="Genre of the song.")
 lyrics: Optional[str] = Field(default=None, description="Lyrics of the song.")

 async def run(self, **kwargs: Any) -> str:
  context = f"You are a songwriter. You are writing a {self.genre} song called {self.title}."
  text = f"Generate lyrics for the song {self.title}."
  response = await chat_completion(text, context=context)
  self.lyrics = response
  return self

class Blog(OpenAIFunction):
 """Generates a blog post of a given topic."""
 topic: str = Field(description="Topic of the blog post.")
 title: str = Field(description="Title of the blog post.")
 content: Optional[str] = Field(default=None, description="Content of the blog post.")

 async def run(self, **kwargs: Any) -> str:
  context = f"You are a blogger. You are writing a blog post about {self.topic}."
  text = f"Generate content for the blog post {self.title}."
  response = await chat_completion(text, context=context)
  self.content = response
  return self

import asyncio
from .service import function_call

async def main():
 quiz = await function_call("Generate a quiz about Python")
 print(quiz.data)
 #['Can you explain the difference between deep copying and shallow copying in Python, and provide an example scenario where each would be useful?', 'Can you explain the concept of a generator in Python, and provide an example of how it can be used to optimize memory usage in a program?', "Sure, here's a non-trivial question about Python:\n\nQuestion: Can you explain the concept of *garbage collection* in Python and how it differs from manual memory management? Provide an example of when garbage collection is beneficial and discuss any potential drawbacks.\n\nThis question assesses the candidate's understanding of memory management in Python and their knowledge of garbage collection mechanisms. It also tests their ability to discuss the advantages and disadvantages of different memory management approaches.", 'Can you explain the concept of closures in Python and provide an example of when they would be useful in programming?', 'Can you explain the concept of inheritance in Python and provide an example of when it would be beneficial in programming?']
 
 song = await function_call("Generate a song about love")
 print(song.data)
 # title='Love Song' genre='pop' lyrics="(Verse 1)\nEvery time I close my eyes\nI feel your love deep inside\nYou're the reason for my smile\nIn your arms, I feel alive\n\n(Pre-Chorus)\nEvery touch is like a fire\nBurning brighter, taking me higher\nYou're the melody in my heart\nI knew it from the very start \n\n(Chorus)\nThis is our love song, playing on repeat\nA symphony of emotions, oh so sweet\nTogether, we're dancing in perfect harmony\nYou and me, forever, baby, can't you see?\n\n(Verse 2)\nIn your eyes, I found my home\nA love so pure, I've never known\nWith each word, you steal my soul\nIn this love, we're in control\n\n(Bridge)\nEvery word, every note\nWe're writing our own love story, don't you know?\nWe're the rhythm that beats as one\nThis love song, forever sung\n\n(Chorus)\nThis is our love song, playing on repeat\nA symphony of emotions, oh so sweet\nTogether, we're dancing in perfect harmony\nYou and me, forever, baby, can't you see?\n\n(Verse 3)\nThrough the highs and lows we'll go\nOur love's a journey, ebb and flow\nWe'll conquer any storm that comes our way\nCause darling, our love is here to stay\n\n(Chorus)\nThis is our love song, playing on repeat\nA symphony of emotions, oh so sweet\nTogether, we're dancing in perfect harmony\nYou and me, forever, baby, can't you see?\n\n(Outro)\nIn this love song, our hearts entwine\nYou're the melody that forever shines\nYou and me, forever, eternally\nIn this love song, we'll always be."
 blog = await function_call("Generate a blog post about OpenAI Functions")
 print(blog.data)
 #topic='OpenAI Functions' title='Introduction to OpenAI Functions' content='Welcome to our blog post on "Introduction to OpenAI Functions"! In today\'s digital age, artificial intelligence has become an integral part of our lives, revolutionizing the way we interact with technology. OpenAI, a leading organization in AI research and development, has been at the forefront of creating groundbreaking solutions, and OpenAI Functions is one such innovation.\n\nOpenAI Functions is a powerful tool that allows developers to create and deploy specialized AI models quickly and easily. Whether you need a model for natural language processing, image recognition, or even chatbots, OpenAI Functions provides a user-friendly interface to develop custom AI models tailored to your specific needs.\n\nGone are the days when building sophisticated AI models required extensive knowledge of AI algorithms, complex code, and massive computational resources. With OpenAI Functions, developers can now leverage these advanced AI capabilities without being AI experts themselves.\n\nMoreover, OpenAI Functions simplifies the development and deployment process by abstracting away the underlying complexities. It provides a high-level interface that enables developers to interact with the model using simple API calls. This streamlined approach not only saves valuable time but also makes AI accessible to a wider range of developers and entrepreneurs.\n\nWhat makes OpenAI Functions unique is its ability to adapt and learn from real-world data. This means that as you use the model, it continuously improves and becomes more accurate over time. This iterative learning process ensures that your AI model is always up-to-date and optimized for better performance.\n\nAnother notable feature of OpenAI Functions is its scalability. Whether you are building a personal project or launching a large-scale application, OpenAI Functions can seamlessly handle heavy workloads and cater to a growing number of users. This scalability makes it an ideal choice for startups and enterprises alike, as it can accommodate fluctuating demands without compromising on performance.\n\nAdditionally, OpenAI Functions is designed with a focus on ethical AI practices. OpenAI has made significant strides in ensuring transparency, fairness, and accountability in AI development. By using OpenAI Functions, you can rest assured that your AI models adhere to these ethical standards, minimizing biases and promoting inclusivity.\n\nIn conclusion, OpenAI Functions opens up a world of possibilities for developers and businesses looking to harness the power of AI. Its ease of use, scalability, and focus on ethics make it a standout choice in the AI landscape. Stay tuned as we delve deeper into the functionalities, use cases, and best practices of OpenAI Functions in our upcoming blog posts. Get ready to unlock the true potential of AI with OpenAI Functions!'

 # LOL, totally made up.

if __name__ == "__main__":
 asyncio.run(main())

Ограничения и обходные пути

Хотя функции OpenAI предлагают мощный способ расширения возможностей языковых моделей, они имеют свой собственный набор ограничений:

Ограничение скорости. API OpenAI имеет ограничения по скорости, поэтому вам необходимо тщательно управлять вызовами API. Рассмотрите возможность использования библиотеки ограничения скорости или создайте свой собственный механизм.

Стоимость: API OpenAI не бесплатен. Всегда следите за использованием, чтобы избежать непредвиденных расходов.

Обработка ошибок. API может выдавать ошибки по разным причинам, например недопустимая схема или проблемы с сетью. Убедитесь, что ваш код достаточно надежен, чтобы справиться с этими сценариями.

Расширенные варианты использования

Совместная работа в реальном времени. Вы можете использовать функции OpenAI для создания инструментов для совместной работы в реальном времени. Например, редактор кода, предлагающий улучшения в режиме реального времени.

Автоматическое создание контента. Думайте не только о чат-ботах. Вы можете создавать автоматические публикации в блогах, обновления в социальных сетях или даже сценарии для видео.

Что дальше?

OpenAI постоянно развивается, и в будущем мы можем ожидать появления новых функций и возможностей. Следите за официальной документацией OpenAI и форумами сообщества, чтобы быть в курсе последних обновлений.

В этой статье мы рассмотрели множество вопросов: от понимания того, что такое функции OpenAI, до их фактической реализации на Python. Мы также рассмотрели, как автоматизировать этот процесс с помощью библиотеки Pydantic, и обсудили некоторые сложные варианты использования. Обладая этими знаниями, вы хорошо подготовлены к созданию более интеллектуальных и динамичных приложений.

И вот оно! На этом мы завершаем наше полное руководство по функциям OpenAI. Если эта статья оказалась для вас полезной, поделитесь ею и не забудьте подписаться на меня, чтобы получать больше подобного контента. Приятного кодирования! 🚀