Заставить ChatGPT отвечать на вопросы только о данных вашей компании

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

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

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

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

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

Что мы собираемся строить?

Какие библиотеки мы будем использовать?

Ленгчейн

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

Langchain использует цепочки, которые позволяют разработчикам объединять несколько компонентов для создания единого согласованного приложения в последовательной цепочке событий, поддерживающих обратные вызовы, например цепочка, которая принимает пользовательский ввод, форматирует его с помощью PromptTemplate, а затем передает отформатированный ответ в Большая языковая модель (LLM) для обработки или другой цепочки.

Лама-Индекс

Это простой и гибкий интерфейс между вашими внешними данными и LLM. Liama-Index также предоставляет возможность запрашивать ваши данные с помощью ChatGPT. Комбинация Langchain и ChatGPT позволяет решать множество задач, подобных ChatGPT Retrieval Plugin.

В этой статье мы рассмотрим загрузку ChatGPT с конкретным содержимым веб-сайта, используя плагин Llama-index под названием ReadabilityWebPageReader, который работает путем извлечения соответствующей информации из полностью обработанной веб-страницы.

Читаемость.js

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

Драматург

Драматург похож на фреймворк Selenium в нашем боте службы поддержки клиентов. Он будет объединен с Readability.js для загрузки веб-страниц в безголовом режиме Firefox.

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

proxy (Optional[str], optional): Proxy server. Defaults to None.

wait_until (Optional[Literal["commit", "domcontentloaded", "load", "networkidle"]], optional): Wait until the page is loaded. Defaults to "domcontentloaded".

text_splitter (TextSplitter, optional): Text splitter. Defaults to None.

normalizer (Optional[Callable[[str], str]], optional): Text normalizer. Defaults to nfkc_normalize.

Мы будем использовать бесплатную версию ChatGPT-3, абстрагированную библиотекой Langchain. Вы можете подумать, что GPT-4 уже изначально поддерживает подключение к внешнему миру; почему я должен испытывать головную боль при создании внешних экстракторов данных?

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

Каким образом наш персонализированный агент службы поддержки клиентов ChatGPT Bot обнаруживает полученные данные?

Мы используем Llama-Index, который предлагает эффективный метод индексации или векторизации данных для хранения и поиска вложений. Чтобы получить наиболее семантически релевантные результаты — в нашем случае контент, относящийся к запросу о компании, — Llama index выполняет поиск сходства в многомерных пространствах. Система семантического поиска может быстро определить наиболее релевантные совпадения для заданного запроса, сохраняя вложения документов или текстовых отрывков в векторной базе данных.

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

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

Какие инструменты доступны для больших данных?

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

loader = TextLoader(source)
documents = loader.load()
text_splitter = text_splitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)
db = ElasticVectorSearch.from_documents(docs, embeddings, elasticsearch_url=elasticsearch_url)
index = db.index_name

LlamaIndex и Langchain предлагают множество вариантов хранения данных, каждый из которых имеет свои преимущества и недостатки. Что использовать, зависит от варианта использования. В некоторых хранилищах данных хранятся как вложения, так и исходные данные и запросы с поддержкой контроля версий. Это может упростить ваш дизайн, не добавляя дополнительные хранилища данных, такие как корзины S3 и GCP.

Ниже приведен список вариантов хранения векторной БД и соответствующий llama_index загрузчик:

  • Pinecone (PineconeReader): облачное решение с уровнем бесплатного пользования, кредитная карта не требуется.
  • Chroma (ChromaReader): с открытым исходным кодом под лицензией Apache 2.0
  • DeepLake (DeepLakeReader): с открытым исходным кодом под лицензией Apache 2.0.
  • Qdrant (QdrantReader) Предоставляет два варианта управляемых служб или бесплатный открытый исходный код под лицензией Apache 2.0.
  • Weaviate (WeaviateReader) Предоставляет два варианта управляемых услуг: гибридный или бесплатный с открытым исходным кодом под лицензией BSD.
  • Faiss FaissReader): с открытым исходным кодом под лицензией типа MIT.
  • Milvus (MilvusReader): Opensource под лицензией Apache 2.0.
  • Zilliz (MilvusReader): Предоставляет два варианта управляемых услуг или бесплатный открытый исходный код под лицензией Apache 2.0.

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

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

Начиная

Предпосылка

  • Убедитесь, что у вас Python ≥ 3.8
  • Создайте новую виртуальную среду или среду conda. Вы можете пропустить требования к установке и создать свою среду на основе этого environment.yml файла.

Шаг 1. Создайте проект и установите зависимости

$ cd {YOUR DEV DIRECTORY}

$ mkdir chatassistantbot

$ pip install langchain

$ pip install llama-index

$ pip install beautifulsoup4

$ playwright install

Шаг 2. Динамически сканируйте любой веб-сайт по вашему выбору с данными компании.

Давайте создадим функцию Python, collect_urls, которая принимает три параметра: root_url, visited (набор, содержащий ранее посещенные URL-адреса) и max_urls (максимальное количество URL-адресов для сбора).

Целью этой функции является сбор URL-адресов с веб-страницы (начиная с root_url) и ее дочерних страниц, которые находятся в том же домене, что и root_url. Мы хотим убедиться, что мы проиндексировали как можно больше информации о компании, чтобы у ChatGPT было достаточно контекста. Если вы укажете корневой URL-адрес, он пройдет по нему. Если вы предоставляете URL-адреса, мы указываем функции использовать их только явно. Никакого обхода по корню не будет.

Сначала функция извлекает доменное имя из root_url, используя метод urlparse из библиотеки urllib.parse. Затем он использует библиотеку requests для запроса к root_url и анализа содержимого HTML с помощью библиотеки BeautifulSoup.

Затем функция находит все ссылки на корневой странице и проверяет, имеет ли дочерний URL-адрес тот же домен, что и корневой URL-адрес. Если это так, URL-адрес добавляется к набору собранных URL-адресов.

Если количество собранных URL-адресов меньше максимального, функция рекурсивно просматривает все дочерние URL-адреса и добавляет их в набор URL-адресов, если они не посещались ранее. Процесс продолжает рекурсивно перемещаться по дочерним URL-адресам до тех пор, пока не будет собрано максимальное количество URL-адресов или не останется дочерних URL-адресов для обхода.

Наконец, функция возвращает набор собранных URL-адресов.

import requests

from bs4 import BeautifulSoup
from urllib.parse import urlparse


def collect_urls(self, root_url: str, visited: Set = None, max_urls: int = 20) -> Set:
    # Create an empty set to store the collected URLs
    if visited is None:
        visited = set()
    urls = set()
    # Extract the domain name from the root URL
    domain = urlparse(root_url).netloc

    # Make a request to the root URL and parse the HTML content
    try:
        response = requests.get(root_url)
    except requests.exceptions.RequestException:
        # If the request raises an exception, just return an empty set
        return urls

    soup = BeautifulSoup(response.content, "html.parser")
    # Find all the links on the root page and add them to the URL set
    for link in soup.find_all("a"):
        url = link.get("href")
        if url:
            parsed_url = urlparse(url)

            # Check if the child URL has the same domain as the root URL
            if parsed_url.netloc == domain:
                urls.add(url)

                # Check if we have reached the maximum number of URLs
                if len(urls) >= max_urls:
                    return urls

    # Recursively traverse all child URLs and add them to the URL set
    for url in urls.copy():
        if url not in visited:
            print(url)
            visited.add(url)
            urls |= self.__collect_urls(url, visited, max_urls)
            # Check if we have reached the maximum number of URLs
            if len(urls) >= max_urls:
                break

    return urls

Мы создадим файл config.py для хранения конфигураций нашего помощника чат-бота. Обратите внимание, pydantic — это еще одна зависимость, которая будет автоматически доступна после установки основного пакета Langchain.

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

Шаг 3. Создайте конфигурацию модели и бота

import os

from pydantic import BaseSettings, __version__, BaseModel


class OpenAIModel(BaseModel):
    name: str = "gpt-3.5-turbo"
    temperature: int = 0


class Settings(BaseSettings):
    # Application
    PROJECT_NAME: str = "chatassistantbot"
    PROJECT_DESCRIPTION: str = "Reldyn Buddy powered by OpenAI."
    COMPANY_NAME: str = "Reldyn"
    COMPANY_EMAIL: str = "[email protected]"
    PROJECT_VERSION: str = __version__
    KNOWLEDGE_INDEX_CACHE_DURATION: int = 1
    TIME_ZONE = "Asia/Jakarta"
    PROMPT_MAX_INPUT_SIZE = 4096
    PROMPT_NUM_OUTPUTS = 512
    PROMPT_MAX_CHUNK_OVERLAP = 20
    PROMPT_CHUNK_SIZE_LIMIT = 600
    OPENAI_MODEL = OpenAIModel()

    class Config:
        # Place your .env file under this path
        env_file = ".env"
        env_prefix = "RELDYN_"
        case_sensitive = True


assert (
    os.getenv("OPENAI_API_KEY") is not None
), "Please set the OPENAI_API_KEY environment variable. " \
   "see https://platform.openai.com/account/api-keys"


try:
    import playwright
except ImportError:
    raise ValueError(
        "playwright not installed, which is needed Playwright module provides "
        "a method to launch a browser instance."
        "Please install it with `pip install playwright==1.30.0`."
    )


settings = Settings()

Мы не будем явно определять OPENAI_API_KEY, поскольку это делается неявно в пакете Langchain PyPI, поэтому убедитесь, что вы определили его через свой терминал, bash_profile или в конфигурациях запуска IDE.

Шаг 4. Определите агента ленгчейна

Определим агента. Langchain предлагает несколько реализаций агентов. Мы используем conversational-react-description

«Этот агент предназначен для использования в диалоговых настройках. Подсказка предназначена для того, чтобы агент был полезным и разговорчивым. Он использует структуру ReAct, чтобы решить, какой инструмент использовать, и использует память для запоминания предыдущих взаимодействий в беседе». — Документация по ленгчейну

import logging 

from langchain import PromptTemplate
from langchain.agents import initialize_agent, Tool, AgentType, AgentExecutor
from langchain.chains.conversation.memory import ConversationBufferMemory
from llama_index import QueryMode
from langchain.llms import OpenAI


logging.basicConfig(level=logging.DEBUG)

tools = [
    Tool(
        name="Website Index",
        func=lambda q: str(self.index.query(q, mode=QueryMode.EMBEDDING)),
        description="""
        useful for when you want to answer questions from Website Index. Always, 
        you must try the index first, only answer based this Website Index
        """,
    )
]

# wrapper around ChatMessageHistory that extracts the messages in a variable, 
# memory_key key within a dict so we can retrive same history been user you and agent
memory = ConversationBufferMemory(memory_key="chat_history")

# initialize langchain agent orchestrator
agent_chain = initialize_agent(
            tools,
            llm,
            agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
            memory=self.memory,
            verbose=True,
            max_iterations=10
        )

Параметр verbose=True позволяет нам наблюдать за «процессом мышления» агента в логах сервера. max_iterations ограничивает использование OpenAI API, если агент зависает. Без него агент будет продолжать попытки, пока не превысит лимит токенов в одном запросе OpenAI или не израсходует весь бюджет вашей учетной записи OpenAI.

Установка уровня ведения журнала для отладки помогает вам видеть все вызовы API, сделанные агентом Langchain к конечной точке REST API завершений OpenAI.

ConversationBufferMemory обеспечивает связный диалог между агентом и пользователем. Без него каждый запрос рассматривался бы как совершенно независимый вход без учета прошлых взаимодействий, что было бы не очень приятно с точки зрения пользователя, если только вы не разрабатываете self-ask-with-search agent, который дает немедленные ответы.

Это другие типы памяти, которые можно использовать с агентами Langchain. Они следующие:

  • ConversationBufferWindowMemory
  • ConversationSummaryBufferMemory
  • ConversationKnowledgeGraphMemory
  • ConversationEntityMemory

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

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

Шаг 5. Определите и оцените подсказку

Ключом являются ограждения вокруг модели. Пользователь должен проверять все, что выводит модель. Мы также должны установить границы того, что модель должна знать и как она должна реагировать. Подсказка — полдела. Данные — это другая битва. Интеграция - утомительная часть.

# Schema to represent a prompt for an LLM
prompt = PromptTemplate(template="""
            You are a personal assistant for {company_name} company your job is to answer questions. 
            Use only context Website Index to provide answers.
            Do not provide any answers that deviate from your tools documents.
            If you don't know the answer, just say "Hmm, Im not sure please contact customer support at {company_email} 
            for further assistance." Don't try to make up an answer.:
            --------
            Question: {query}
            """,
            input_variables=["query", "company_name", "company_email"],
        )

Учитывая приведенное выше приглашение templatequery — это наш входной вопрос, а company_name=’Reldyn’ и адрес электронной почты компании — [email protected]., вы можете заменить название компании и адрес электронной почты на свои собственные.

Крайне важно, чтобы галлюцинации были сведены к минимуму. Обратите внимание на то, как структурировано приглашение и насколько оно ясно. Ключ к тому, чтобы ChatGPT делал именно то, что вам нужно — следовательно, большая часть ваших усилий — будет потрачена на получение идеального системного приглашения.

Это важно, потому что сейчас мы устанавливаем границу или ограждение. Думайте об этом как о предоставлении персоны агенту только для Релдина и Релдина.

Если вы попытаетесь задать какие-либо вопросы, которые ему не известны, он не ответит. Поскольку мы хотим использовать этого бота в бизнес-среде, последнее, что нам нужно, — это предоставить неверную информацию потенциальным клиентам.

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

Рабочий процесс агента

Вот упрощенная версия логического потока:

ReadabilityWebPageReader = download_loader("ReadabilityWebPageReader")
loader = ReadabilityWebPageReader()

root_url = "https://reldyn.co"
urls = collect_urls(root_url)
urls = urls + [
    "https://buy.experian.com.my/index.php/search/Malaysia-Company/1359116/RELDYN-TECH-SDN.-BHD.",
    "https://newday.jobs/job-in-myanmar/junior-senior-ios-guru-developer/70949",
    "https://www.tofler.in/reldyn-tech-private-limited/company/U72900PN2022FTC217062"
]

# Iterate all urls and scrap text content load into Llama index documents
documents = []
for url in urls:
    _documents = loader.load_data(url=url)
    documents.extend(_documents)

# Define the default paramters for the model
max_input_size = 4096
num_outputs = 512
max_chunk_overlap = 20
chunk_size_limit = 600
prompt_helper = PromptHelper(max_input_size, num_outputs, max_chunk_overlap, chunk_size_limit=chunk_size_limit)

llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="gpt-3.5-turbo", max_tokens=num_outputs))
context = ServiceContext.from_defaults(llm_predictor=llm_predictor)

# load documents into a querable index
index = GPTListIndex.from_documents(documents, service_context=context)

# save a copy to disk
index.save_to_disk("reldyn.json")

llm = OpenAI(temperature=0, model_name="gpt-3.5-turbo")

# link indexed documents into Langchain tool so ChatGPT can query
tools = [Tool(
    name="Website Index",
    func=lambda q: str(index.query(q, mode=QueryMode.EMBEDDING)),
    description=f"useful for when you want to answer questions from Website Index. Always, you must try the index first, only answer based this Website Index",
)]

# define memory type for the agent
memory = ConversationBufferMemory(memory_key="chat_history")
agent_chain = initialize_agent(
    tools,
    llm,
    agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
    memory=memory,
    verbose=True,
    max_iterations=10
)
# execute agent with prompt template
agent_chain.run(YOUR_PROMPT_TEMPLATE)

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

Полный исходный код и зависимости PyPI см. в репозитории GitHub. Обратите внимание, что структура репозитория может незначительно отличаться, так как примерный код будет периодически обновляться, чтобы отражать состояние этих оболочек ChatGPT с открытым исходным кодом. Вы можете увидеть историю коммитов для предыдущих снимков.

Таким образом, мы создали класс WebDataExtractor, который будет инкапсулировать процесс извлечения данных. WebChatAssistant имеет источник конструктора типа WebDataExtractor. Класс помощника по чату абстрагирует агента и инструмент, используемый для запроса ваших проиндексированных данных. Созданный ранее шаблон подсказки используется для запуска агента Langchain.

if __name__ == "__main__":

    source = WebDataExtractor(
        root_url="https://reldyn.co",
        urls=[
            "https://buy.experian.com.my/index.php/search/Malaysia-Company/1359116/RELDYN-TECH-SDN.-BHD.",
            "https://newday.jobs/job-in-myanmar/junior-senior-ios-guru-developer/70949",
            "https://www.tofler.in/reldyn-tech-private-limited/company/U72900PN2022FTC217062"
        ]
    )
    assistant = WebChatAssistant(source)
    init_conversation = False

    while True:
        try:
            if not init_conversation:
                user_input = input("Hi Am {} Buddy, Ask your question...".format(
                    settings.COMPANY_NAME))
                init_conversation = True
            else:
                user_input = input("Please ask your question...")
            agent_prompt = assistant.prompt_persona.format(
                query=user_input,
                company_name=settings.COMPANY_NAME,
                company_email=settings.COMPANY_EMAIL)
            response = str(assistant.agent.run(agent_prompt)).strip()
            output_response(response)
        except ValueError as e:
            response = str(e)
            response_prefix = "Could not parse LLM output: `"
            if not response.startswith(response_prefix):
                raise e
            response_suffix = "`"
            if response.startswith(response_prefix):
                response = response[len(response_prefix):]
            if response.endswith(response_suffix):
                response = response[:-len(response_suffix)]
            output_response(response)
        except KeyboardInterrupt:
            break

Дополнительный ValueError в предложении exclude — это хак для обработки известной ошибки в Langchain, которая до сих пор не исправлена ​​на момент написания этой статьи; по этому вопросу есть открытый PR, который вы можете найти здесь.

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

Выполнение кода

Когда мы запускаем код в первый раз, определенные URL-адреса будут удалены для контента.

После завершения очистки мы можем просмотреть необработанные данные, проиндексированные классом GPTListIndex, и то, что ChatGPT будет использовать в качестве контекста.

Теперь мы можем протестировать наш чат-бот, настроенный на данные компании, с помощью приведенной ниже подсказки.

Данные о Релдине ограничены; если мы попросим агента ChatGPT расшириться еще больше, учитывая предыдущую подсказку, которую мы определили, мыслительный процесс бота выводит следующее, что интересно, поскольку он предлагает места для поиска:

Ниже приведена еще одна подсказка о WFH относительно того, откуда был взят контент.

We\u2019re an all-remote company where you can work from anywhere with good internet

Выходные данные ChatBot настолько хороши, насколько хороши данные, используемые для предоставления контекста. Мусор на входе, мусор на выходе.

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

INFO:openai:error_code=None error_message='You exceeded your current quota, please check your plan and billing details.' error_param=None error_type=insufficient_quota message='OpenAI API error received' stream_error=False

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

Мы можем сделать гораздо больше, чтобы перейти в автоматизированное состояние, комбинируя фреймворки, такие как Celery, для создания задания cron, то есть периодических заданий, которые мы можем запланировать для переиндексации контента друзей агента Reldyn на основе установленной частоты или когда есть данные изменены.

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

Обратите внимание: это пространство развивается ежедневно. Пакеты Python для Langchain и LlamaIndex обновляются ежедневно. К тому времени, когда вы что-то кодируете, высока вероятность того, что на следующей неделе выйдет новый релиз, который потенциально может сломать то, что вы реализовали сегодня.

Следовательно, всегда важно заблокировать версии PyPI в environment.yml или requirements.txt, чтобы не столкнуться с загадочными исключениями, которые потенциально могут привести вас в кроличью нору отладки.

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

Полный исходный код см. в этом репозитории Git.