Полное руководство по LangChain в Python

LangChain — это универсальная библиотека Python , которая позволяет разработчикам и исследователям создавать, экспериментировать и анализировать языковые модели и агенты. Он предлагает богатый набор функций для энтузиастов обработки естественного языка (NLP), от создания пользовательских моделей до эффективного манипулирования текстовыми данными. В этом подробном руководстве мы углубимся в основные компоненты LangChain и продемонстрируем, как использовать его возможности в Python.

Настройка

Чтобы следовать этой статье, создайте новую папку и установите LangChain и OpenAI с помощью pip:

pip3 install langchain openai

Агенты

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

Создание агента LangChain

Агентов можно настроить на использование «инструментов» для сбора необходимых им данных и формулирования правильного ответа. Взгляните на пример ниже. Он использует Serp API (API поиска в Интернете) для поиска в Интернете информации, относящейся к вопросу или вводу, и использует ее для ответа. Он также использует этот llm-math инструмент для выполнения математических операций — например, для преобразования единиц измерения или поиска процентного изменения между двумя значениями:

from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.llms import OpenAI
import os

os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
os.environ["SERPAPI_API_KEY"] = "YOUR_SERP_API_KEY" # get your Serp API key here: https://serpapi.com/

OpenAI.api_key = "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"
llm = OpenAI(model="gpt-3.5-turbo", temperature=0)
tools = load_tools(["serpapi", "llm-math"], llm=llm)
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run("How much energy did wind turbines produce worldwide in 2022?")

Как видите, после выполнения всего базового импорта и инициализации нашего LLM ( llm = OpenAI(model="gpt-3.5-turbo", temperature=0)), код загружает инструменты, необходимые для работы нашего агента с использованием tools = load_tools(["serpapi", "llm-math"], llm=llm). Затем он создает агента, используя initialize_agent функцию, предоставляя ему указанные инструменты и дает ему описание ZERO_SHOT_REACT_DESCRIPTION, что означает, что он не будет помнить предыдущие вопросы.

Пример теста агента 1

Давайте проверим этот агент со следующими входными данными:

"How much energy did wind turbines produce worldwide in 2022?"

Агент тестирования

Как видите, он использует следующую логику:

  • выполните поиск по запросу «Производство энергии ветряными турбинами в мире в 2022 году» с помощью API интернет-поиска Serp.
  • проанализировать лучший результат
  • получить любые соответствующие цифры
  • конвертируйте 906 гигаватт в джоули с помощью этого llm-mathинструмента, поскольку мы просили энергию, а не мощность

Пример теста агента 2

Агенты LangChain не ограничиваются поиском в Интернете. Мы можем подключить практически любой источник данных (включая наш собственный) к агенту LangChain и задавать ему вопросы о данных. Давайте попробуем создать агента, обученного на наборе данных CSV.

Загрузите этот набор данных фильмов и телешоу Netflix из SHIVAM BANSAL на Kaggle и переместите его в свой каталог. Теперь добавьте этот код в новый файл Python:

from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
from langchain.agents.agent_types import AgentType
from langchain.agents import create_csv_agent
import os

os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"

agent = create_csv_agent(
    OpenAI(temperature=0),
    "netflix_titles.csv",
    verbose=True,
    agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
)

agent.run("In how many movies was Christian Bale casted")

Этот код вызывает create_csv_agent функцию и использует netflix_titles.csv набор данных. На изображении ниже показан наш тест.

Тестирование CSV-агента

Как показано выше, его логика заключается в поиске в cast столбце всех вхождений «Кристиан Бэйл».

Мы также можем создать агент Pandas Dataframe следующим образом:

from langchain.agents import create_pandas_dataframe_agent
from langchain.chat_models import ChatOpenAI
from langchain.agents.agent_types import AgentType
from langchain.llms import OpenAI
import pandas as pd
import os

os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"
df = pd.read_csv("netflix_titles.csv")

agent = create_pandas_dataframe_agent(OpenAI(temperature=0), df, verbose=True)

agent.run("In what year were the most comedy movies released?")

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

Тестирование логики агента Pandas Dataframe

Тестирование ответа Pandas Dataframe

Это всего лишь несколько примеров. Мы можем использовать практически любой API или набор данных с LangChain.

Модели

В LangChain существует три типа моделей: LLM, модели чата и модели внедрения текста. Давайте рассмотрим каждый тип модели на нескольких примерах.

Языковая модель

LangChain предоставляет возможность использовать языковые модели в Python для вывода текста на основе ввода текста. Она не так сложна, как модель чата, и лучше всего используется для решения простых языковых задач ввода-вывода. Вот пример использования OpenAI:

from langchain.llms import OpenAI
import os
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"

llm = OpenAI(model="gpt-3.5-turbo", temperature=0.9)
print(llm("Come up with a rap name for Matt Nikonorov"))

Как видно выше, он использует gpt-3.5-turboмодель для генерации выходных данных для предоставленных входных данных («Придумайте рэп-имя для Мэтта Никонорова»). В этом примере я установил температуру, чтобы 0.9сделать LLM по-настоящему креативным. В результате появился «MC MegaMatt». Я бы дал этому фильму твердую оценку 9/10.

Модель чата

Заставлять моделей LLM придумывать рэп-имена — это весело, но если мы хотим более сложных ответов и разговоров, нам нужно активизировать нашу игру, используя модель чата. Чем модели чата технически отличаются от языковых моделей? Ну, по словам документации LangChain :

Модели чата представляют собой разновидность языковых моделей. Хотя модели чата «под капотом» используют языковые модели, интерфейс, который они используют, немного отличается. Вместо использования API «ввод и вывод текста» они используют интерфейс, в котором «сообщения чата» являются входными и выходными данными.

Вот простой скрипт модели чата Python:

from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage
)
import os
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"

chat = ChatOpenAI()
messages = [
    SystemMessage(content="You are a friendly, informal assistant"),
    HumanMessage(content="Convince me that Djokovic is better than Federer")
]
print(chat(messages))

Как показано выше, код сначала отправляет сообщение SystemMessage и сообщает чат-боту вести себя дружелюбно и неформально, а затем отправляет сообщение HumanMessage чат-боту, чтобы убедить нас, что Джокович лучше Федерера.

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

Тест модели чат-бота

Вложения

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

from langchain.embeddings import OpenAIEmbeddings

embeddings_model = OpenAIEmbeddings()
embedded_query = embeddings_model.embed_query("Who created the world wide web?")
embedded_query[:5]

Это вернет список чисел с плавающей запятой: [0.022762885317206383, -0.01276398915797472, 0.004815981723368168, -0.009435392916202545, 0.010824492201209068]. Вот как выглядит встраивание.

Вариант использования внедрения моделей

Если мы хотим обучить чат-бота или LLM отвечать на вопросы, связанные с нашими данными или конкретным образцом текста, нам нужно использовать встраивания. Давайте создадим простой файл CSV ( embs.csv) с «текстовым» столбцом, содержащим три фрагмента информации:

text
"Robert Wadlow was the tallest human ever"
"The Burj Khalifa is the tallest skyscraper"
"Roses are red"

Вот скрипт, который будет отвечать на вопрос «Кто был самым высоким человеком на свете?» и найдите правильный ответ в CSV-файле, используя встраивания:

from langchain.embeddings import OpenAIEmbeddings
from openai.embeddings_utils import cosine_similarity
import os
import pandas

os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"
embeddings_model = OpenAIEmbeddings()

df = pandas.read_csv("embs.csv")

# Make embeddings for each piece of information
emb1 = embeddings_model.embed_query(df["text"][0])
emb2 = embeddings_model.embed_query(df["text"][1])
emb3 = embeddings_model.embed_query(df["text"][2])
emb_list = [emb1, emb2, emb3]
df["embedding"] = emb_list

embedded_question = embeddings_model.embed_query("Who was the tallest human ever?") # Make an embedding for the question
df["similarity"] = df.embedding.apply(lambda x: cosine_similarity(x, embedded_question)) # Finds the relevance of each piece of data in context to the question
df.to_csv("embs.csv")
df2 = df.sort_values("similarity", ascending=False) # Sorts the pieces of information by their relatedness to the question
print(df2["text"][0])

Если мы запустим этот код, мы увидим, что он выводит: «Роберт Уодлоу был самым высоким человеком на свете». Код находит правильный ответ, получая встраивание каждого фрагмента информации и находя тот, который больше всего связан с встраиванием вопроса «Кто был самым высоким человеком в истории?» Сила вложений!

Chunks

Модели LangChain не могут одновременно обрабатывать большие тексты и использовать их для ответов. Здесь на помощь приходят куски(chunks) и разделение текста. Давайте рассмотрим два простых способа разбить наши текстовые данные на куски перед подачей их в LangChain.

Разделение кусков по символам

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

from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(separators=["\n\n", "\n"], chunk_size=2000, chunk_overlap=250)
texts = text_splitter.split_text(your_text)

Рекурсивное разбиение кусков

Если мы хотим строго разделить наш текст на символы определенной длины, мы можем сделать это, используя RecursiveCharacterTextSplitter:

from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=2000,
    chunk_overlap=250,
    length_function=len,
    add_start_index=True,
)
texts = text_splitter.create_documents([your_text])

Размер чанка и перекрытие

Глядя на приведенные выше примеры, вы, возможно, задались вопросом, что именно означают размер фрагмента и параметры перекрытия и какое влияние они оказывают на производительность.

Это можно объяснить двумя моментами:

  • Размер фрагмента определяет количество символов, которые будут находиться в каждом фрагменте. Чем больше размер чанка, тем больше данных содержится в нем и тем больше времени потребуется LangChain для его обработки и получения выходных данных, и наоборот.
  • Перекрытие фрагментов — это то, что разделяет информацию между фрагментами, чтобы они разделяли некоторый контекст. Чем выше перекрытие фрагментов, тем более избыточными будут наши фрагменты, тем меньше перекрытие фрагментов и тем меньше контекста будет разделяться между фрагментами. Как правило, хорошее перекрытие фрагментов составляет от 10% до 20% от размера фрагмента, хотя идеальное перекрытие фрагментов варьируется в зависимости от типа текста и вариантов использования.

Цепочки

Цепочки — это, по сути, несколько функций LLM, связанных вместе для выполнения более сложных задач, которые иначе невозможно было бы выполнить с помощью простого input --> outputметода LLM. Давайте посмотрим на классный пример:

from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
import os
os.environ["OPENAI_API_KEY"] = "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"

llm = OpenAI(temperature=0.9)
prompt = PromptTemplate(
    input_variables=["media", "topic"],
    template="What is a good title for a {media} about {topic}",
)
chain = LLMChain(llm=llm, prompt=prompt)
print(chain.run({
    'media': "horror movie",
    'topic': "math"
}))

Этот код принимает в подсказку две переменные и формулирует творческий ответ ( temperature=0.9). В этом примере мы попросили его придумать хорошее название для фильма ужасов о математике.

Результатом запуска этого кода было «Рассчитывающее проклятие», но на самом деле это не показывает всю мощь цепочек.

Давайте посмотрим на более практический пример:

from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
from typing import Optional

from langchain.chains.openai_functions import (
    create_openai_fn_chain,
    create_structured_output_chain,
)
import os

os.environ["OPENAI_API_KEY"] = "YOUR_KEY"

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1)
template = """Use the given format to extract information from the following input: {input}. Make sure to answer in the correct format"""

prompt = PromptTemplate(template=template, input_variables=["input"])

json_schema = {
    "type": "object",
    "properties": {
        "name": {"title": "Name", "description": "The artist's name", "type": "string"},
        "genre": {"title": "Genre", "description": "The artist's music genre", "type": "string"},
        "debut": {"title": "Debut", "description": "The artist's debut album", "type": "string"},
        "debut_year": {"title": "Debut_year", "description": "Year of artist's debut album", "type": "integer"}
    },
    "required": ["name", "genre", "debut", "debut_year"],
}

chain = create_structured_output_chain(json_schema, llm, prompt, verbose=False)
f = open("Nas.txt", "r")
artist_info = str(f.read())
print(chain.run(artist_info))

Этот код может показаться запутанным, поэтому давайте пройдемся по нему.

Этот код читает краткую биографию Наса (хип-хоп исполнителя) , извлекает из текста следующие значения и форматирует их в объект JSON:

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

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

{'name': 'Nas', 'genre': 'Hip Hop', 'debut': 'Illmatic', 'debut_year': 1994}

Предоставив функции схему JSON create_structured_output_chain, мы заставили цепочку преобразовать выходные данные в формат JSON.

Выходя за рамки OpenAI

Несмотря на то, что я продолжаю использовать модели OpenAI в качестве примеров различных функциональных возможностей LangChain, это не ограничивается моделями OpenAI. Мы можем использовать LangChain со множеством других LLM и сервисов искусственного интеллекта. (Вот полный список LLM, интегрируемых с LangChain .)

Например, мы можем использовать Cohere с LangChain. Вот документация по интеграции LangChain Cohere , но просто чтобы дать практический пример: после установки Cohere с помощью pip3 install cohere мы можем сделать простой question --> answerкод с использованием LangChain и Cohere следующим образом:

from langchain.llms import Cohere
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

template = """Question: {question}

Answer: Let's think step by step."""

prompt = PromptTemplate(template=template, input_variables=["question"])
llm = Cohere(cohere_api_key="YOUR_COHERE_KEY")
llm_chain = LLMChain(prompt=prompt, llm=llm)

question = "When was Novak Djokovic born?"

print(llm_chain.run(question))

Приведенный выше код выдает следующий результат:

The answer is Novak Djokovic was born on May 22, 1987.

Novak Djokovic is a Serbian tennis player.

Заключение

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