Пошаговое руководство по подключению диалоговых ботов модели LLM к любой схеме спецификации OpenAPI

Возможность предоставить определение вашего API-интерфейса прикладного программирования внешним пользователям жизненно важна как для ведения бизнеса, так и для передачи ваших API-интерфейсов потребителям, в то время как потребители являются другими технически подкованными пользователями. Успех ваших API зависит от повторяющихся, кратких и детерминированных действий с использованием словаря, который имеет отношение к потребителю API.

Язык спецификации OpenAPI предоставляет для этого стандартизированные средства. Ваши API могут быть описаны в агностических терминах, отделяя их от любого конкретного языка программирования. Эта спецификация схемы определяется либо через документ JSON, либо через документ YAML. Он предоставляет исчерпывающий словарь терминов, отражающий общепринятые концепции в мире API. В сочетании с вспомогательными инструментами он может обеспечить богатый опыт на основе простого документа.

В то время как ChatGPT хорошо понимает и пишет код. Возможно, вы не поняли, что это также хорошо для понимания определений спецификаций OpenAPI. На самом деле именно так плагины ChatGPT работают под капотом: вы просто предоставляете техническую спецификацию вашего API, которая ссылается на живые сервисы, доступные через Интернет, с конечными точками API, и ChatGPT сделает вывод, как его использовать.

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

Представьте, что вы можете связать различные модели LLM, такие как GPT, с любой из этих схем OpenAPI, просто с помощью быстрой разработки.

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

Зачем использовать обработку естественного языка с API?

Чтобы лучше понять, как обработка естественного языка может помочь в контексте интерфейсов прикладного программирования. Давайте углубимся в преимущества, которые поначалу могут быть не столь очевидны.

Благодаря обработке естественного языка у нас есть возможность общаться с нашими API. Теперь у нас есть более естественный и интуитивно понятный способ взаимодействия с приложениями напрямую с помощью низкоуровневых API, которые в основном были доступны разработчикам программного обеспечения.

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

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

Давайте взглянем прямо вперед, чтобы начать подключать любую спецификацию OpenAPI, которая документирует живые REST API, с ChatGPT, чтобы вы могли использовать естественный язык для общения с вашими API.

Начиная

Для начала давайте установим необходимые предварительные условия, и Langchain станет нашей основной библиотекой, поскольку она упрощает процесс.

pip install langchain 

pip install tiktoken

pip install faiss 

Если вы получаете сообщение об ошибке при установке faiss (Поиск подобия ИИ в Facebook), вы можете выбрать версию с процессором.

pip install faiss-cpu

Как Langchain и ChatGPT работают с OpenAPI?

Мы будем использовать встроенный естественный язык Langchain NLAToolkit , который эквивалентен AIPluginTool для создания оболочек естественного языка вокруг конечных точек схемы OpenAPI.

Концепция похожа на то, как работают плагины OpenAI под капотом, настоящая магия происходит, если у вас есть хорошо документированная схема OpenAPI. Использование NLAToolkit wrapper, поставляемого с инструментами агентов Langchain, которые используют настраиваемый агент с механизмом поиска для выбора не инструментов явно, а набора спецификаций OpenAPI для использования.

По умолчанию Langchain и ChatGPT помогут вам выполнить вызовы API, хотя они представляют собой случаи, когда вам нужно будет обрабатывать аутентификацию для служб за замком и ключом, поскольку каждая служба имеет свой собственный подход к тому, как вы аутентифицируетесь в API.

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

Обход происходит путем выбора сначала API или плагина, а затем конечных точек, а не непосредственно конечных точек, что более эффективно. Это связано с тем, что плагин может содержать больше полезной информации для выбора. Вы можете прочитать больше об инструментах агента Langchain здесь, так как это даст вам больше контекста и понимания.

Давайте создадим базовый класс, который возвращает инструменты для всех операций API для данной схемы OpenAI.

class BaseOpenAPI(ABC):
    
    @abstractmethod
    def get_tools(self) -> List[BaseTool]:
        """Get tool of type NLAToolkit"""

Затем создайте пользовательский класс, который расширяет BaseOpenAPI. В нашем примере мы назовем его SpooncularOpenAPI классом, который будет указывать на схему OpenAPI для Sppcular, которая предоставляет исчерпывающий API о еде, планах питания, рецептах и ​​ингредиентах.

Создание оболочки вокруг схемы OpenAPI дает нам более точный контроль с помощью объекта Python Requests над тем, как мы инициализируем API. Мы можем определить ключи API или токены-носители Oauth2 и сохранить наши ключи в секретном хранилище или переменных среды, чего LangChain и GPT не могут сделать за вас, поскольку у них нет контекста для аутентификации вашего целевого API.

class SpoonacularOpenAPI(BaseOpenAPI):

    def __init__(self, llm: BaseOpenAI):
        self.__spoonacular_api_key = os.environ.get("SPOONACULAR_API_KEY")
        if not self.__spoonacular_api_key:
            raise ImproperlyConfigured("api key required SPOONACULAR_API_KEY")
        requests = Requests(headers={"x-api-key": self.__spoonacular_api_key})
        self.spoonacular_toolkit = NLAToolkit.from_llm_and_spec(
            llm,
            spec=OpenAPISpec.from_file(
                "/Users/timothymugayi/Development/openai_llms_query_lang/spoonacular-openapi-3.json"
            ), # this is hard coded obviously you do not want to hard code the path
            requests=requests,
            max_text_length=1800,  # If you want to truncate the response text
        )

    def get_tools(self) -> List[BaseTool]:
        return self.spoonacular_toolkit.get_tools()[:30]

Обратите внимание, что это два подхода к загрузке файлов спецификаций OpenAPI.

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

Если вам нужны всегда актуальные схемы и вас не беспокоит необходимость настройки схемы OpenAPI для более оптимизации настройки и вывода GPT, мы можем загружать с действующего URL-адреса; как показано ниже, оба подхода имеют свои плюсы и минусы.

self.spoonacular_toolkit = NLAToolkit.from_llm_and_url(
    llm,
    "https://spoonacular.com/application/frontend/downloads/spoonacular-openapi-3.json",
    requests=requests,
    max_text_length=1800,
)

Давайте импортируем все необходимые нам классы и посмотрим, как можно использовать схемы Langchain, ChatGPT и OpenAI для выполнения вызовов API с использованием NLP. Обратите внимание, что этот пример может использовать ChatGPT 3.5, 4 и другие модели LLM, такие как Google PaLM, поддерживаемые платформой Langchain, в зависимости от ваших предпочтений.

import logging

from typing import List

from langchain.agents import AgentType, initialize_agent
from langchain.agents.agent_toolkits import NLAToolkit
from langchain.llms import OpenAI
from langchain.tools import OpenAPISpec, BaseTool
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.schema import Document

from spoonacular import SpoonacularOpenAPI


logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)

# Select the LLM to use. Here, we use text-davinci-003
# You can swap between different core LLM's here.
llm = OpenAI(temperature=0, max_tokens=700)

nl_api_tools = []
nl_api_tools.extend(SpoonacularOpenAPI(llm).get_tools())
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://www.transvribe.com/ai-plugin/openapi.yaml").get_tools())  # Undestands YouTube videos
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://chatgpt-plugin-ts.transitive-bullshit.workers.dev/openapi.json").get_tools()) # Generates ASII art
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://www.greenyroad.com/openapi.yaml").get_tools()) # Reads URLs and provides context
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://api.schooldigger.com/swagger/docs/v2.0").get_tools()) # Information about school districts
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://api.tasty.co/.well-known/openapi.yaml").get_tools()), # Recipes
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://www.wolframalpha.com/.well-known/apispec.json").get_tools()) # Wolfram Alpha
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://www.freetv-app.com/openapi.json").get_tools())  # Latest news (works)
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://chatgpt-plugin-dexa-lex-fridman.transitive-bullshit.workers.dev/openapi.json").get_tools()) # Searches Lex Friedman's podcast
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://websearch.plugsugar.com/api/openapi_yaml").get_tools())  # Search web (works)
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://server.shop.app/openai/v1/api.json").get_tools()) # Search for products
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://api.speak.com/openapi.yaml").get_tools())
nl_api_tools.extend(NLAToolkit.from_llm_and_url(llm, "https://www.klarna.com/us/shopping/public/openai/v0/api-docs/").get_tools()) # shop online
nl_api_tools.extend(NLAToolkit.from_llm_and_spec(llm, spec=OpenAPISpec.from_file("/Users/timothymugayi/Development/openai_llms_query_lang/openapi-directory/APIs/worldtimeapi.org/20210108/openapi.yaml")).get_tools()) # understands how to tell world time

Обратите внимание, что класс SpoonacularOpenAPI загружается иначе, чем остальные. NLAtookit можно вызывать напрямую, не передавая объект запросов Python, когда вы вызываете API, которые не требуют специальной обработки и обычно открыты для публики.

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

Затем мы можем инициализировать наш агент Langchain с помощью специальной системной подсказки; это важно, чтобы переопределить приглашение Langchains по умолчанию, поскольку мы хотим, чтобы любые ошибки, которые являются частью мыслительного процесса, распространялись на запрашивающую сторону, поскольку важно знать, какие ошибки возникают из ответа на вызов REST API.

# Slightly tweak the instructions from the default agent
openapi_format_instructions = """Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: what to instruct the AI Action representative.
Observation: The Agent's response
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer. User can't see any of my observations, API responses,
links, or tools.
Final Answer: the final answer to the original input question with the right amount of detail

When responding with your Final Answer, remember that the person you are responding to CANNOT
see any of your Thought/Action/Action Input/Observations, so if there is any relevant information
there you need to include it explicitly in your response."""

Вставляем последний кусок

print(f"{len(nl_api_tools)} tools loaded.")

# Create an agent with the new tools
agent = initialize_agent(
    nl_api_tools, llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
    agent_kwargs={
        "format_instructions": openapi_format_instructions
    }
)

# Create a complex query so GPT model figure 
# out out of the list of OpenAI schemas which best first to address the query
user_input = (
    "I'm learning Italian, and my language class is having an end of year party... "
    " Could you help me find an Italian outfit to wear and"
    " an appropriate recipe to prepare so I can present for the class in Italian?"
)

agent.run(user_input)

Это производит следующий вывод

Обратите внимание, что мы получаем ошибки, поскольку для начала исчерпали лимиты токенов ChatGPT. Давайте посмотрим, что было загружено, чтобы вы поняли, почему мы превышаем лимиты токенов OpenAI.

Чтобы проиллюстрировать, что может происходить, давайте взглянем только на одну спецификацию OpenAPI для spoonacular; сколько жетонов это занимает? Это поможет вам лучше понять, как создавать собственные приложения ChatGPT, работающие с большими объемами данных.

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

Это довольно серьезная проблема. Многие схемы OpenAPI для служб REST, как правило, слишком велики, чтобы соответствовать текущим контекстам GPT. Вот почему OpenAI рекомендует разделять ваш API при создании плагинов ChatGPT.

В приведенном выше примере мы используем схему JSON OpenAPI, есть случай перехода к схемам на основе YAML, поскольку размер токена будет меньше, но это фактически полностью устраняет проблему и минимизирует ее.

Другим подходом является модификация схемы YAML OpenAPI, которая может еще больше уменьшить размер токена, но не решает другую проблему в нашем примере. У нас есть несколько схем OpenAP, а не одна. В типичном приложении вы имеете дело не просто с данными из одного источника, а обычно из нескольких источников.

Как Langchain и GPT могут работать с большим репозиторием API

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

Чтобы свести к минимуму затраты на обращение к службе OpenAI, мы будем использовать векторизацию с векторным хранилищем и семантический поиск, чтобы найти ближайший соответствующий API, который ваши LLM могут использовать для вывода ответа API.

Мы делаем это, создавая вложения для каждого описания инструмента схемы OpenAPI, мы прокручиваем список определений NLAToolkit. Затем для входящего запроса мы можем создать вложения для этого запроса и выполнить поиск по сходству для соответствующих инструментов.

def add_to_vector_tools(nl_api_tools: List[BaseTool]):
    docs = [Document(page_content=t.description, metadata={"index": i}) for i, t in enumerate(nl_api_tools)]
    vector_store = FAISS.from_documents(docs, OpenAIEmbeddings())
    retriever = vector_store.as_retriever()
    return retriever

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

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

def get_tools(query):
    docs = retriever.get_relevant_documents(query)
    return [nl_api_tools[d.metadata["index"]] for d in docs]

Мы можем проверить нашу реализацию, выполнив прямой вызов функции get_tools. В нашем случае нам нужна первая релевантная лучшая схема OpenAPI, применимая к нашему запросу.

tool = get_tools("How is the best food and receipe, in Jakarta?")[0]

print(tool.name, tool.description)

Учитывая, что мы можем делать вызовы get_tools, мы можем использовать ответ как часть вызова функции инициализации нашего агента.

tool = get_tools("Pick the best Italian outfit for a candle light dinner")[0]

agent = initialize_agent(
    [tool], llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
    agent_kwargs={
        "format_instructions": openapi_format_instructions
    }
)

Далее вы можете взаимодействовать с агентом следующим образом

user_input = (
    "I'm learning Italian, and my language class is having an end of year party... "
    " Could you help me find an Italian outfit to wear and"
    " an appropriate recipe to prepare so I can present for the class in Italian?"
)

agent.run(user_input)

Последние мысли

NLP предлагает большой потенциал для запросов REST API, который является популярным методом предоставления данных и функций другим приложениям. Однако API REST могут быть сложными в использовании, особенно для нетехнических пользователей.

Используя NLP, процесс запросов к REST API может стать намного проще. Пользователи могут задавать вопросы на естественном языке, а система НЛП переводит эти запросы в соответствующие вызовы REST API.

Использование естественного языка для запроса REST API дает несколько преимуществ.

Его проще использовать по сравнению с синтаксисом REST API, что делает его более доступным для пользователей, не являющихся техническими специалистами, для взаимодействия с открытыми данными и функциями.

Естественный язык более интуитивно понятен, что облегчает пользователям запоминание того, как запрашивать REST API.

Естественный язык является более гибким, позволяя пользователям задавать сложные вопросы и получать подробные результаты.

Хотя GPT хорошо понимает структуры API, он не идеален и по-прежнему требует усилий, чтобы обеспечить легкость поиска и чтения схем OpenAPI с помощью GPT. Это означает, что когда вы создаете свое приложение, вместо использования онлайн-схемы OpenAI в реальном времени, размещенной у поставщика, вы можете делать их копии и корректировать текст, чтобы модель LLM работала хорошо.

Хорошо описанный API упрощает быстрое проектирование.

Та же концепция может быть применена к Google PaLM или любым другим моделям LLM. Такой прагматичный подход помогает свести к минимуму галлюцинации или неправильные предположения.

Фактор стоимости и высокая задержка не могут быть проигнорированы при переходе между LLM, API и базами данных, поскольку источники информации добавляют больше абстракции в цикл запрос-ответ. Однако по мере того, как вычислительная мощность процессора и скорость сети улучшаются и становятся более доступными, эти ограничения со временем будут уменьшаться, а преимущества NLP перевешивают недостатки.

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