Полное руководство по 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
набор данных. На изображении ниже показан наш тест.
Как показано выше, его логика заключается в поиске в 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?")
Если мы запустим его, мы увидим что-то вроде результатов, показанных ниже.
Это всего лишь несколько примеров. Мы можем использовать практически любой 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 для своих начинаний в области НЛП, независимо от того, являетесь ли вы исследователем, разработчиком или любителем.