В его ядре, Лангчейн — это инновационная среда, предназначенная для создания приложений, использующих возможности языковых моделей. Это набор инструментов, предназначенный для разработчиков, позволяющих создавать контекстно-зависимые приложения, способные выполнять сложные рассуждения.
Это означает, что приложения LangChain могут понимать контекст, например, быстрые инструкции или ответы на обоснование контента, и использовать языковые модели для сложных логических задач, таких как принятие решения о том, как реагировать или какие действия предпринять. LangChain представляет собой единый подход к разработке интеллектуальных приложений, упрощающий путь от концепции до реализации благодаря разнообразным компонентам.
Понимание LangChain
LangChain — это гораздо больше, чем просто фреймворк; это полноценная экосистема, состоящая из нескольких неотъемлемых частей.
- Во-первых, существуют библиотеки LangChain, доступные как на Python, так и на JavaScript. Эти библиотеки являются основой LangChain, предлагая интерфейсы и интеграцию для различных компонентов. Они предоставляют базовую среду выполнения для объединения этих компонентов в связные цепочки и агенты, а также готовые реализации для немедленного использования.
- Далее у нас есть шаблоны LangChain. Это коллекция развертываемых эталонных архитектур, предназначенных для решения широкого круга задач. Независимо от того, создаете ли вы чат-бота или сложный аналитический инструмент, эти шаблоны станут надежной отправной точкой.
- LangServe выступает в роли универсальной библиотеки для развертывания цепочек LangChain в качестве REST API. Этот инструмент необходим для превращения ваших проектов LangChain в доступные и масштабируемые веб-сервисы.
- Наконец, LangSmith служит платформой для разработчиков. Он предназначен для отладки, тестирования, оценки и мониторинга цепочек, построенных на любой платформе LLM. Полная интеграция с LangChain делает его незаменимым инструментом для разработчиков, стремящихся усовершенствовать и усовершенствовать свои приложения.
Вместе эти компоненты позволяют с легкостью разрабатывать, создавать и развертывать приложения. С LangChain вы начинаете с написания своих приложений с использованием библиотек, ссылаясь на шаблоны в качестве руководства. Затем LangSmith помогает вам проверять, тестировать и отслеживать ваши цепочки, гарантируя, что ваши приложения постоянно улучшаются и готовы к развертыванию. Наконец, с помощью LangServe вы можете легко преобразовать любую цепочку в API, упрощая развертывание.
В следующих разделах мы углубимся в настройку LangChain и начнем ваш путь к созданию интеллектуальных приложений на основе языковых моделей.
Автоматизируйте ручные задачи и рабочие процессы с помощью нашего конструктора рабочих процессов на основе искусственного интеллекта, разработанного Nanonets для вас и ваших команд.
Установка и настройка
Готовы ли вы погрузиться в мир LangChain? Настроить его очень просто, и это руководство шаг за шагом проведет вас через весь процесс.
Первым шагом в вашем путешествии по LangChain является его установка. Вы можете легко сделать это, используя pip или conda. Запустите следующую команду в своем терминале:
pip install langchain
Для тех, кто предпочитает новейшие функции и предпочитает больше приключений, вы можете установить LangChain непосредственно из исходного кода. Клонируйте репозиторий и перейдите в папку langchain/libs/langchain
каталог. Затем запустите:
pip install -e .
Для экспериментальных функций рассмотрите возможность установки langchain-experimental
. Это пакет, содержащий передовой код и предназначенный для исследовательских и экспериментальных целей. Установите его, используя:
pip install langchain-experimental
LangChain CLI — удобный инструмент для работы с шаблонами LangChain и проектами LangServe. Чтобы установить LangChain CLI, используйте:
pip install langchain-cli
LangServe необходим для развертывания ваших цепочек LangChain в качестве REST API. Он устанавливается вместе с CLI LangChain.
LangChain часто требует интеграции с поставщиками моделей, хранилищами данных, API и т. д. В этом примере мы будем использовать API моделей OpenAI. Установите пакет OpenAI Python, используя:
pip install openai
Чтобы получить доступ к API, установите свой ключ API OpenAI в качестве переменной среды:
export OPENAI_API_KEY="your_api_key"
Альтернативно, передайте ключ непосредственно в вашей среде Python:
import os
os.environ['OPENAI_API_KEY'] = 'your_api_key'
LangChain позволяет создавать приложения языковой модели с помощью модулей. Эти модули могут быть автономными или составлены для сложных случаев использования. Эти модули –
- Модель ввода / вывода: Облегчает взаимодействие с различными языковыми моделями, эффективно обрабатывая их входные и выходные данные.
- поиск: обеспечивает доступ к данным конкретного приложения и взаимодействие с ними, что имеет решающее значение для динамического использования данных.
- Риэлторы: Предоставьте приложениям возможность выбирать подходящие инструменты на основе директив высокого уровня, расширяя возможности принятия решений.
- Цепи: предлагает заранее определенные композиции многократного использования, которые служат строительными блоками для разработки приложений.
- Память: поддерживает состояние приложения во время выполнения нескольких цепочек, что важно для контекстно-зависимого взаимодействия.
Каждый модуль ориентирован на конкретные потребности разработки, что делает LangChain комплексным набором инструментов для создания приложений с расширенными языковыми моделями.
Помимо вышеперечисленных компонентов, у нас также есть Язык выражений LangChain (LCEL), который представляет собой декларативный способ легкого объединения модулей и позволяет объединять компоненты в цепочку с использованием универсального интерфейса Runnable.
LCEL выглядит примерно так:
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema import BaseOutputParser # Example chain
chain = ChatPromptTemplate() | ChatOpenAI() | CustomOutputParser()
Теперь, когда мы рассмотрели основы, мы продолжим:
- Подробно изучите каждый модуль Langchain.
- Узнайте, как использовать язык выражений LangChain.
- Изучите распространенные варианты использования и реализуйте их.
- Разверните комплексное приложение с помощью LangServe.
- Посетите LangSmith для отладки, тестирования и мониторинга.
Давайте начнем!
Модуль I: Модель ввода/вывода
В LangChain основной элемент любого приложения вращается вокруг языковой модели. Этот модуль предоставляет необходимые строительные блоки для эффективного взаимодействия с любой языковой моделью, обеспечивая плавную интеграцию и общение.
Ключевые компоненты модели ввода-вывода
- LLM и модели чата (используются взаимозаменяемо):
- LLM:
- Определение: Модели завершения текста.
- Ввод, вывод: принять текстовую строку в качестве входных данных и вернуть текстовую строку в качестве выходных данных.
- Модели чата
- LLM:
- Определение: модели, которые используют языковую модель в качестве основы, но различаются форматами ввода и вывода.
- Ввод, вывод: принять список сообщений чата в качестве входных данных и вернуть сообщение чата.
- Запрашивает: создание шаблонов, динамический выбор и управление входными данными модели. Позволяет создавать гибкие и контекстно-зависимые подсказки, которые определяют ответы языковой модели.
- Выходные парсеры: Извлечение и форматирование информации из выходных данных модели. Полезно для преобразования необработанного вывода языковых моделей в структурированные данные или определенные форматы, необходимые приложению.
LLM
Интеграция LangChain с моделями больших языков (LLM), такими как OpenAI, Cohere и Hugging Face, является фундаментальным аспектом его функциональности. LangChain сам по себе не размещает LLM, но предлагает единый интерфейс для взаимодействия с различными LLM.
В этом разделе представлен обзор использования оболочки OpenAI LLM в LangChain, применимой и к другим типам LLM. Мы уже установили это в разделе «Начало работы». Давайте инициализируем LLM.
from langchain.llms import OpenAI
llm = OpenAI()
- LLM реализуют Запускаемый интерфейс, основной строительный блок Язык выражений LangChain (LCEL). Это означает, что они поддерживают
invoke
,ainvoke
,stream
,astream
,batch
,abatch
,astream_log
звонки. - LLM принимают струны в качестве входных данных или объектов, которые можно привести к строковым подсказкам, в том числе
List[BaseMessage]
иPromptValue
. (подробнее об этом позже)
Давайте посмотрим на некоторые примеры.
response = llm.invoke("List the seven wonders of the world.")
print(response)
Альтернативно вы можете вызвать метод потока для потоковой передачи текстового ответа.
for chunk in llm.stream("Where were the 2012 Olympics held?"): print(chunk, end="", flush=True)
Модели чата
Интеграция LangChain с моделями чата, специализированной разновидностью языковых моделей, необходима для создания приложений интерактивного чата. Хотя модели чата используют внутренние языковые модели, они представляют собой отдельный интерфейс, ориентированный на сообщения чата в качестве входных и выходных данных. В этом разделе представлен подробный обзор использования модели чата OpenAI в LangChain.
from langchain.chat_models import ChatOpenAI
chat = ChatOpenAI()
Модели чата в LangChain работают с разными типами сообщений, такими как AIMessage
, HumanMessage
, SystemMessage
, FunctionMessage
и ChatMessage
(с произвольным параметром роли). В целом, HumanMessage
, AIMessage
и SystemMessage
являются наиболее часто используемыми.
Модели чата в первую очередь принимают List[BaseMessage]
в качестве входов. Строки могут быть преобразованы в HumanMessage
и PromptValue
также поддерживается.
from langchain.schema.messages import HumanMessage, SystemMessage
messages = [ SystemMessage(content="You are Micheal Jordan."), HumanMessage(content="Which shoe manufacturer are you associated with?"),
]
response = chat.invoke(messages)
print(response.content)
Запрашивает
Подсказки необходимы для управления языковыми моделями для получения релевантных и последовательных результатов. Они могут варьироваться от простых инструкций до сложных примеров из нескольких кадров. В LangChain обработка подсказок может быть очень упрощенным процессом благодаря нескольким выделенным классам и функциям.
LangChain's PromptTemplate
class — универсальный инструмент для создания строковых подсказок. Он использует Python str.format
синтаксис, позволяющий генерировать динамические подсказки. Вы можете определить шаблон с заполнителями и при необходимости заполнить их конкретными значениями.
from langchain.prompts import PromptTemplate # Simple prompt with placeholders
prompt_template = PromptTemplate.from_template( "Tell me a {adjective} joke about {content}."
) # Filling placeholders to create a prompt
filled_prompt = prompt_template.format(adjective="funny", content="robots")
print(filled_prompt)
В моделях чата подсказки более структурированы и включают сообщения с определенными ролями. Лангчейн предлагает ChatPromptTemplate
.
from langchain.prompts import ChatPromptTemplate # Defining a chat prompt with various roles
chat_template = ChatPromptTemplate.from_messages( [ ("system", "You are a helpful AI bot. Your name is {name}."), ("human", "Hello, how are you doing?"), ("ai", "I'm doing well, thanks!"), ("human", "{user_input}"), ]
) # Formatting the chat prompt
formatted_messages = chat_template.format_messages(name="Bob", user_input="What is your name?")
for message in formatted_messages: print(message)
Этот подход позволяет создавать интерактивных, привлекательных чат-ботов с динамическими реакциями.
Оба формата PromptTemplate
и ChatPromptTemplate
легко интегрироваться с языком выражений LangChain (LCEL), что позволяет им быть частью более крупных и сложных рабочих процессов. Мы обсудим это позже.
Пользовательские шаблоны подсказок иногда необходимы для задач, требующих уникального форматирования или конкретных инструкций. Создание пользовательского шаблона приглашения включает определение входных переменных и пользовательского метода форматирования. Такая гибкость позволяет LangChain удовлетворять широкий спектр требований конкретных приложений. Подробнее читайте здесь.
LangChain также поддерживает подсказки, состоящие из нескольких шагов, что позволяет модели учиться на примерах. Эта функция жизненно важна для задач, требующих контекстного понимания или определенных шаблонов. Шаблоны подсказок, состоящие из нескольких фрагментов, можно создать на основе набора примеров или с помощью объекта «Выбор примера». Подробнее читайте здесь.
Выходные парсеры
Парсеры вывода играют решающую роль в Langchain, позволяя пользователям структурировать ответы, генерируемые языковыми моделями. В этом разделе мы рассмотрим концепцию синтаксических анализаторов вывода и предоставим примеры кода с использованием Langchain PydanticOutputParser, SimpleJsonOutputParser, CommaSeparatedListOutputParser, DatetimeOutputParser и XMLOutputParser.
PydanticOutputParser
Langchain предоставляет PydanticOutputParser для анализа ответов в структуры данных Pydantic. Ниже приведен пошаговый пример того, как его использовать:
from typing import List
from langchain.llms import OpenAI
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain.pydantic_v1 import BaseModel, Field, validator # Initialize the language model
model = OpenAI(model_name="text-davinci-003", temperature=0.0) # Define your desired data structure using Pydantic
class Joke(BaseModel): setup: str = Field(description="question to set up a joke") punchline: str = Field(description="answer to resolve the joke") @validator("setup") def question_ends_with_question_mark(cls, field): if field[-1] != "?": raise ValueError("Badly formed question!") return field # Set up a PydanticOutputParser
parser = PydanticOutputParser(pydantic_object=Joke) # Create a prompt with format instructions
prompt = PromptTemplate( template="Answer the user query.n{format_instructions}n{query}n", input_variables=["query"], partial_variables={"format_instructions": parser.get_format_instructions()},
) # Define a query to prompt the language model
query = "Tell me a joke." # Combine prompt, model, and parser to get structured output
prompt_and_model = prompt | model
output = prompt_and_model.invoke({"query": query}) # Parse the output using the parser
parsed_result = parser.invoke(output) # The result is a structured object
print(parsed_result)
Выходной сигнал будет:
SimpleJsonOutputParser
SimpleJsonOutputParser от Langchain используется, когда вы хотите проанализировать выходные данные, подобные JSON. Вот пример:
from langchain.output_parsers.json import SimpleJsonOutputParser # Create a JSON prompt
json_prompt = PromptTemplate.from_template( "Return a JSON object with `birthdate` and `birthplace` key that answers the following question: {question}"
) # Initialize the JSON parser
json_parser = SimpleJsonOutputParser() # Create a chain with the prompt, model, and parser
json_chain = json_prompt | model | json_parser # Stream through the results
result_list = list(json_chain.stream({"question": "When and where was Elon Musk born?"})) # The result is a list of JSON-like dictionaries
print(result_list)
CommaSeparatedListOutputParser
CommaSeparatedListOutputParser удобен, если вы хотите извлечь списки, разделенные запятыми, из ответов модели. Вот пример:
from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI # Initialize the parser
output_parser = CommaSeparatedListOutputParser() # Create format instructions
format_instructions = output_parser.get_format_instructions() # Create a prompt to request a list
prompt = PromptTemplate( template="List five {subject}.n{format_instructions}", input_variables=["subject"], partial_variables={"format_instructions": format_instructions}
) # Define a query to prompt the model
query = "English Premier League Teams" # Generate the output
output = model(prompt.format(subject=query)) # Parse the output using the parser
parsed_result = output_parser.parse(output) # The result is a list of items
print(parsed_result)
DatetimeOutputParser
DatetimeOutputParser от Langchain предназначен для анализа информации о дате и времени. Вот как его использовать:
from langchain.prompts import PromptTemplate
from langchain.output_parsers import DatetimeOutputParser
from langchain.chains import LLMChain
from langchain.llms import OpenAI # Initialize the DatetimeOutputParser
output_parser = DatetimeOutputParser() # Create a prompt with format instructions
template = """
Answer the user's question:
{question}
{format_instructions} """ prompt = PromptTemplate.from_template( template, partial_variables={"format_instructions": output_parser.get_format_instructions()},
) # Create a chain with the prompt and language model
chain = LLMChain(prompt=prompt, llm=OpenAI()) # Define a query to prompt the model
query = "when did Neil Armstrong land on the moon in terms of GMT?" # Run the chain
output = chain.run(query) # Parse the output using the datetime parser
parsed_result = output_parser.parse(output) # The result is a datetime object
print(parsed_result)
Эти примеры демонстрируют, как синтаксические анализаторы вывода Langchain можно использовать для структурирования различных типов ответов модели, что делает их подходящими для разных приложений и форматов. Анализаторы вывода — ценный инструмент для повышения удобства использования и интерпретируемости результатов языковой модели в Langchain.
Автоматизируйте ручные задачи и рабочие процессы с помощью нашего конструктора рабочих процессов на основе искусственного интеллекта, разработанного Nanonets для вас и ваших команд.
Модуль II: Поиск
Извлечение в LangChain играет решающую роль в приложениях, которым требуются специфичные для пользователя данные, не включенные в обучающий набор модели. Этот процесс, известный как поисковая расширенная генерация (RAG), включает в себя выборку внешних данных и интеграцию их в процесс генерации языковой модели. LangChain предоставляет полный набор инструментов и функций для облегчения этого процесса, обслуживая как простые, так и сложные приложения.
LangChain осуществляет поиск с помощью ряда компонентов, которые мы обсудим один за другим.
Загрузчики документов
Загрузчики документов в LangChain позволяют извлекать данные из различных источников. Имея более 100 доступных загрузчиков, они поддерживают различные типы документов, приложений и источников (частные корзины s3, общедоступные веб-сайты, базы данных).
Вы можете выбрать загрузчик документов в соответствии с вашими требованиями. здесь.
Все эти загрузчики загружают данные в Документ занятия. Позже мы научимся использовать данные, полученные в классах Document.
Загрузчик текстовых файлов: Загрузите простой .txt
файл в документ.
from langchain.document_loaders import TextLoader loader = TextLoader("./sample.txt")
document = loader.load()
CSV-загрузчик: Загрузите файл CSV в документ.
from langchain.document_loaders.csv_loader import CSVLoader loader = CSVLoader(file_path='./example_data/sample.csv')
documents = loader.load()
Мы можем настроить синтаксический анализ, указав имена полей –
loader = CSVLoader(file_path='./example_data/mlb_teams_2012.csv', csv_args={ 'delimiter': ',', 'quotechar': '"', 'fieldnames': ['MLB Team', 'Payroll in millions', 'Wins']
})
documents = loader.load()
PDF-загрузчики: PDF-загрузчики в LangChain предлагают различные методы анализа и извлечения контента из PDF-файлов. Каждый загрузчик удовлетворяет различным требованиям и использует разные базовые библиотеки. Ниже приведены подробные примеры для каждого загрузчика.
PyPDFLoader используется для базового анализа PDF-файлов.
from langchain.document_loaders import PyPDFLoader loader = PyPDFLoader("example_data/layout-parser-paper.pdf")
pages = loader.load_and_split()
MathPixLoader идеально подходит для извлечения математического контента и диаграмм.
from langchain.document_loaders import MathpixPDFLoader loader = MathpixPDFLoader("example_data/math-content.pdf")
data = loader.load()
PyMuPDFLoader работает быстро и включает подробное извлечение метаданных.
from langchain.document_loaders import PyMuPDFLoader loader = PyMuPDFLoader("example_data/layout-parser-paper.pdf")
data = loader.load() # Optionally pass additional arguments for PyMuPDF's get_text() call
data = loader.load(option="text")
PDFMiner Loader используется для более детального контроля над извлечением текста.
from langchain.document_loaders import PDFMinerLoader loader = PDFMinerLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
AmazonTextractPDFParser использует AWS Textract для оптического распознавания символов и других расширенных функций анализа PDF-файлов.
from langchain.document_loaders import AmazonTextractPDFLoader # Requires AWS account and configuration
loader = AmazonTextractPDFLoader("example_data/complex-layout.pdf")
documents = loader.load()
PDFMinerPDFasHTMLLoader генерирует HTML из PDF для семантического анализа.
from langchain.document_loaders import PDFMinerPDFasHTMLLoader loader = PDFMinerPDFasHTMLLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
PDFPlumberLoader предоставляет подробные метаданные и поддерживает один документ на страницу.
from langchain.document_loaders import PDFPlumberLoader loader = PDFPlumberLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
Интегрированные погрузчики: LangChain предлагает широкий выбор пользовательских загрузчиков для прямой загрузки данных из ваших приложений (таких как Slack, Sigma, Notion, Confluence, Google Drive и многих других) и баз данных и использования их в приложениях LLM.
Полный список здесь.
Ниже приведены несколько примеров, иллюстрирующих это:
Пример I – Slack
Slack, широко используемая платформа обмена мгновенными сообщениями, может быть интегрирована в рабочие процессы и приложения LLM.
- Перейдите на страницу управления рабочим пространством Slack.
- Перейдите в
{your_slack_domain}.slack.com/services/export
. - Выберите нужный диапазон дат и начните экспорт.
- Slack уведомляет по электронной почте и в личных сообщениях, как только экспорт будет готов.
- Результатом экспорта является
.zip
файл, расположенный в папке «Загрузки» или по указанному вами пути загрузки. - Укажите путь к скачанному
.zip
файл вLOCAL_ZIPFILE
. - Использовать
SlackDirectoryLoader
изlangchain.document_loaders
пакет.
from langchain.document_loaders import SlackDirectoryLoader SLACK_WORKSPACE_URL = "https://xxx.slack.com" # Replace with your Slack URL
LOCAL_ZIPFILE = "" # Path to the Slack zip file loader = SlackDirectoryLoader(LOCAL_ZIPFILE, SLACK_WORKSPACE_URL)
docs = loader.load()
print(docs)
Пример II – Фигма
Figma, популярный инструмент для проектирования интерфейсов, предлагает REST API для интеграции данных.
- Получите ключ файла Figma из формата URL:
https://www.figma.com/file/{filekey}/sampleFilename
. - Идентификаторы узлов находятся в параметре URL.
?node-id={node_id}
. - Сгенерируйте токен доступа, следуя инструкциям на странице Справочный центр Фигмы.
- Ассоциация
FigmaFileLoader
класс отlangchain.document_loaders.figma
используется для загрузки данных Figma. - Различные модули LangChain, такие как
CharacterTextSplitter
,ChatOpenAI
и т. д., используются для обработки.
import os
from langchain.document_loaders.figma import FigmaFileLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.chat_models import ChatOpenAI
from langchain.indexes import VectorstoreIndexCreator
from langchain.chains import ConversationChain, LLMChain
from langchain.memory import ConversationBufferWindowMemory
from langchain.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate figma_loader = FigmaFileLoader( os.environ.get("ACCESS_TOKEN"), os.environ.get("NODE_IDS"), os.environ.get("FILE_KEY"),
) index = VectorstoreIndexCreator().from_loaders([figma_loader])
figma_doc_retriever = index.vectorstore.as_retriever()
- Ассоциация
generate_code
Функция использует данные Figma для создания кода HTML/CSS. - Он использует шаблонный диалог с моделью на основе GPT.
def generate_code(human_input): # Template for system and human prompts system_prompt_template = "Your coding instructions..." human_prompt_template = "Code the {text}. Ensure it's mobile responsive" # Creating prompt templates system_message_prompt = SystemMessagePromptTemplate.from_template(system_prompt_template) human_message_prompt = HumanMessagePromptTemplate.from_template(human_prompt_template) # Setting up the AI model gpt_4 = ChatOpenAI(temperature=0.02, model_name="gpt-4") # Retrieving relevant documents relevant_nodes = figma_doc_retriever.get_relevant_documents(human_input) # Generating and formatting the prompt conversation = [system_message_prompt, human_message_prompt] chat_prompt = ChatPromptTemplate.from_messages(conversation) response = gpt_4(chat_prompt.format_prompt(context=relevant_nodes, text=human_input).to_messages()) return response # Example usage
response = generate_code("page top header")
print(response.content)
- Ассоциация
generate_code
Функция при выполнении возвращает код HTML/CSS на основе входных данных дизайна Figma.
Давайте теперь воспользуемся нашими знаниями, чтобы создать несколько наборов документов.
Сначала мы загружаем PDF-файл — годовой отчет BCG об устойчивом развитии.
Для этого мы используем PyPDFLoader.
from langchain.document_loaders import PyPDFLoader loader = PyPDFLoader("bcg-2022-annual-sustainability-report-apr-2023.pdf")
pdfpages = loader.load_and_split()
Сейчас мы будем принимать данные из Airtable. У нас есть Airtable, содержащий информацию о различных моделях оптического распознавания символов и извлечения данных –
Давайте воспользуемся для этого AirtableLoader, который находится в списке встроенных загрузчиков.
from langchain.document_loaders import AirtableLoader api_key = "XXXXX"
base_id = "XXXXX"
table_id = "XXXXX" loader = AirtableLoader(api_key, table_id, base_id)
airtabledocs = loader.load()
Давайте теперь продолжим и научимся использовать эти классы документов.
Трансформеры документов
Преобразователи документов в LangChain — это важные инструменты, предназначенные для управления документами, которые мы создали в предыдущем подразделе.
Они используются для таких задач, как разделение длинных документов на более мелкие фрагменты, объединение и фильтрация, которые имеют решающее значение для адаптации документов к контекстному окну модели или удовлетворения конкретных потребностей приложения.
Одним из таких инструментов является RecursiveCharacterTextSplitter, универсальный разделитель текста, использующий для разделения список символов. Он допускает такие параметры, как размер фрагмента, перекрытие и начальный индекс. Вот пример того, как он используется в Python:
from langchain.text_splitter import RecursiveCharacterTextSplitter state_of_the_union = "Your long text here..." text_splitter = RecursiveCharacterTextSplitter( chunk_size=100, chunk_overlap=20, length_function=len, add_start_index=True,
) texts = text_splitter.create_documents([state_of_the_union])
print(texts[0])
print(texts[1])
Еще один инструмент — это FeatureTextSplitter, который разбивает текст на основе указанного символа и включает элементы управления размером фрагмента и перекрытием:
from langchain.text_splitter import CharacterTextSplitter text_splitter = CharacterTextSplitter( separator="nn", chunk_size=1000, chunk_overlap=200, length_function=len, is_separator_regex=False,
) texts = text_splitter.create_documents([state_of_the_union])
print(texts[0])
HTMLHeaderTextSplitter предназначен для разделения содержимого HTML на основе тегов заголовков, сохраняя семантическую структуру:
from langchain.text_splitter import HTMLHeaderTextSplitter html_string = "Your HTML content here..."
headers_to_split_on = [("h1", "Header 1"), ("h2", "Header 2")] html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
html_header_splits = html_splitter.split_text(html_string)
print(html_header_splits[0])
Более сложную манипуляцию можно выполнить, объединив HTMLHeaderTextSplitter с другим разделителем, например конвейерным разделителем:
from langchain.text_splitter import HTMLHeaderTextSplitter, RecursiveCharacterTextSplitter url = "https://example.com"
headers_to_split_on = [("h1", "Header 1"), ("h2", "Header 2")]
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
html_header_splits = html_splitter.split_text_from_url(url) chunk_size = 500
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size)
splits = text_splitter.split_documents(html_header_splits)
print(splits[0])
LangChain также предлагает специальные разделители для разных языков программирования, такие как Python Code Splitter и JavaScript Code Splitter:
from langchain.text_splitter import RecursiveCharacterTextSplitter, Language python_code = """
def hello_world(): print("Hello, World!")
hello_world() """ python_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.PYTHON, chunk_size=50
)
python_docs = python_splitter.create_documents([python_code])
print(python_docs[0]) js_code = """
function helloWorld() { console.log("Hello, World!");
}
helloWorld(); """ js_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.JS, chunk_size=60
)
js_docs = js_splitter.create_documents([js_code])
print(js_docs[0])
Для разделения текста на основе количества токенов, что полезно для языковых моделей с ограничениями на токен, используется TokenTextSplitter:
from langchain.text_splitter import TokenTextSplitter text_splitter = TokenTextSplitter(chunk_size=10)
texts = text_splitter.split_text(state_of_the_union)
print(texts[0])
Наконец, LongContextReorder изменяет порядок документов, чтобы предотвратить снижение производительности моделей из-за длинных контекстов:
from langchain.document_transformers import LongContextReorder reordering = LongContextReorder()
reordered_docs = reordering.transform_documents(docs)
print(reordered_docs[0])
Эти инструменты демонстрируют различные способы преобразования документов в LangChain: от простого разделения текста до сложного изменения порядка и разделения для конкретного языка. Для получения более подробных и конкретных вариантов использования следует обратиться к разделу документации и интеграции LangChain.
В наших примерах загрузчики уже создали для нас фрагментированные документы, и эта часть уже обработана.
Модели внедрения текста
Модели внедрения текста в LangChain предоставляют стандартизированный интерфейс для различных поставщиков моделей внедрения, таких как OpenAI, Cohere и Hugging Face. Эти модели преобразуют текст в векторные представления, позволяя выполнять такие операции, как семантический поиск по сходству текста в векторном пространстве.
Чтобы начать работу с моделями внедрения текста, вам обычно необходимо установить определенные пакеты и настроить ключи API. Мы уже сделали это для OpenAI.
В LangChain embed_documents
Метод используется для встраивания нескольких текстов, предоставляя список векторных представлений. Например:
from langchain.embeddings import OpenAIEmbeddings # Initialize the model
embeddings_model = OpenAIEmbeddings() # Embed a list of texts
embeddings = embeddings_model.embed_documents( ["Hi there!", "Oh, hello!", "What's your name?", "My friends call me World", "Hello World!"]
)
print("Number of documents embedded:", len(embeddings))
print("Dimension of each embedding:", len(embeddings[0]))
Для встраивания одного текста, например поискового запроса, embed_query
используется метод. Это полезно для сравнения запроса с набором внедрений документов. Например:
from langchain.embeddings import OpenAIEmbeddings # Initialize the model
embeddings_model = OpenAIEmbeddings() # Embed a single query
embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?")
print("First five dimensions of the embedded query:", embedded_query[:5])
Понимание этих вложений имеет решающее значение. Каждый фрагмент текста преобразуется в вектор, размерность которого зависит от используемой модели. Например, модели OpenAI обычно создают 1536-мерные векторы. Эти внедрения затем используются для извлечения соответствующей информации.
Функциональность внедрения LangChain не ограничивается OpenAI, но предназначена для работы с различными провайдерами. Настройка и использование могут немного отличаться в зависимости от провайдера, но основная концепция встраивания текстов в векторное пространство остается той же. Для подробного использования, включая расширенные настройки и интеграцию с различными поставщиками моделей внедрения, ценным ресурсом является документация LangChain в разделе «Интеграции».
Векторные магазины
Хранилища векторов в LangChain поддерживают эффективное хранение и поиск вложений текста. LangChain интегрируется с более чем 50 векторными магазинами, обеспечивая стандартизированный интерфейс для простоты использования.
Пример: хранение и поиск вложений
После встраивания текстов мы можем сохранить их в векторном хранилище, например Chroma
и выполнить поиск по сходству:
from langchain.vectorstores import Chroma db = Chroma.from_texts(embedded_texts)
similar_texts = db.similarity_search("search query")
Давайте в качестве альтернативы воспользуемся векторным хранилищем FAISS для создания индексов для наших документов.
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS pdfstore = FAISS.from_documents(pdfpages, embedding=OpenAIEmbeddings()) airtablestore = FAISS.from_documents(airtabledocs, embedding=OpenAIEmbeddings())
Ретриверы
Ретриверы в LangChain — это интерфейсы, которые возвращают документы в ответ на неструктурированный запрос. Они более общие, чем векторные хранилища, и ориентированы на поиск, а не на хранение. Хотя векторные хранилища могут использоваться в качестве основы ретривера, существуют и другие типы ретриверов.
Чтобы настроить ретривер Chroma, вы сначала устанавливаете его, используя pip install chromadb
. Затем вы загружаете, разделяете, встраиваете и извлекаете документы с помощью ряда команд Python. Вот пример кода для настройки средства извлечения Chroma:
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma full_text = open("state_of_the_union.txt", "r").read()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
texts = text_splitter.split_text(full_text) embeddings = OpenAIEmbeddings()
db = Chroma.from_texts(texts, embeddings)
retriever = db.as_retriever() retrieved_docs = retriever.invoke("What did the president say about Ketanji Brown Jackson?")
print(retrieved_docs[0].page_content)
MultiQueryRetriever автоматизирует настройку подсказок, генерируя несколько запросов для запроса, вводимого пользователем, и объединяет результаты. Вот пример его простого использования:
from langchain.chat_models import ChatOpenAI
from langchain.retrievers.multi_query import MultiQueryRetriever question = "What are the approaches to Task Decomposition?"
llm = ChatOpenAI(temperature=0)
retriever_from_llm = MultiQueryRetriever.from_llm( retriever=db.as_retriever(), llm=llm
) unique_docs = retriever_from_llm.get_relevant_documents(query=question)
print("Number of unique documents:", len(unique_docs))
Контекстное сжатие в LangChain сжимает полученные документы, используя контекст запроса, гарантируя возврат только актуальной информации. Это предполагает сокращение содержания и фильтрацию менее релевантных документов. В следующем примере кода показано, как использовать средство извлечения контекстного сжатия:
from langchain.llms import OpenAI
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor llm = OpenAI(temperature=0)
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(base_compressor=compressor, base_retriever=retriever) compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
print(compressed_docs[0].page_content)
EnsembleRetriever сочетает в себе различные алгоритмы поиска для достижения лучшей производительности. Пример объединения ретриверов BM25 и FAISS показан в следующем коде:
from langchain.retrievers import BM25Retriever, EnsembleRetriever
from langchain.vectorstores import FAISS bm25_retriever = BM25Retriever.from_texts(doc_list).set_k(2)
faiss_vectorstore = FAISS.from_texts(doc_list, OpenAIEmbeddings())
faiss_retriever = faiss_vectorstore.as_retriever(search_kwargs={"k": 2}) ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, faiss_retriever], weights=[0.5, 0.5]
) docs = ensemble_retriever.get_relevant_documents("apples")
print(docs[0].page_content)
MultiVector Retriever в LangChain позволяет запрашивать документы с несколькими векторами в каждом документе, что полезно для сбора различных семантических аспектов в документе. Методы создания нескольких векторов включают разделение на более мелкие фрагменты, суммирование или создание гипотетических вопросов. Для разделения документов на более мелкие фрагменты можно использовать следующий код Python:
python
from langchain.retrievers.multi_vector import MultiVectorRetriever
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.storage import InMemoryStore
from langchain.document_loaders from TextLoader
import uuid loaders = [TextLoader("file1.txt"), TextLoader("file2.txt")]
docs = [doc for loader in loaders for doc in loader.load()]
text_splitter = RecursiveCharacterTextSplitter(chunk_size=10000)
docs = text_splitter.split_documents(docs) vectorstore = Chroma(collection_name="full_documents", embedding_function=OpenAIEmbeddings())
store = InMemoryStore()
id_key = "doc_id"
retriever = MultiVectorRetriever(vectorstore=vectorstore, docstore=store, id_key=id_key) doc_ids = [str(uuid.uuid4()) for _ in docs]
child_text_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
sub_docs = [sub_doc for doc in docs for sub_doc in child_text_splitter.split_documents([doc])]
for sub_doc in sub_docs: sub_doc.metadata[id_key] = doc_ids[sub_docs.index(sub_doc)] retriever.vectorstore.add_documents(sub_docs)
retriever.docstore.mset(list(zip(doc_ids, docs)))
Еще одним методом является создание сводок для лучшего поиска благодаря более целенаправленному представлению контента. Вот пример создания сводок:
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.document import Document chain = (lambda x: x.page_content) | ChatPromptTemplate.from_template("Summarize the following document:nn{doc}") | ChatOpenAI(max_retries=0) | StrOutputParser()
summaries = chain.batch(docs, {"max_concurrency": 5}) summary_docs = [Document(page_content=s, metadata={id_key: doc_ids[i]}) for i, s in enumerate(summaries)]
retriever.vectorstore.add_documents(summary_docs)
retriever.docstore.mset(list(zip(doc_ids, docs)))
Еще одним подходом является создание гипотетических вопросов, относящихся к каждому документу, с использованием LLM. Это можно сделать с помощью следующего кода:
functions = [{"name": "hypothetical_questions", "parameters": {"questions": {"type": "array", "items": {"type": "string"}}}}]
from langchain.output_parsers.openai_functions import JsonKeyOutputFunctionsParser chain = (lambda x: x.page_content) | ChatPromptTemplate.from_template("Generate 3 hypothetical questions:nn{doc}") | ChatOpenAI(max_retries=0).bind(functions=functions, function_call={"name": "hypothetical_questions"}) | JsonKeyOutputFunctionsParser(key_name="questions")
hypothetical_questions = chain.batch(docs, {"max_concurrency": 5}) question_docs = [Document(page_content=q, metadata={id_key: doc_ids[i]}) for i, questions in enumerate(hypothetical_questions) for q in questions]
retriever.vectorstore.add_documents(question_docs)
retriever.docstore.mset(list(zip(doc_ids, docs)))
Parent Document Retriever — это еще один инструмент извлечения, который обеспечивает баланс между точностью внедрения и сохранением контекста, сохраняя небольшие фрагменты и извлекая их более крупные родительские документы. Его реализация заключается в следующем:
from langchain.retrievers import ParentDocumentRetriever loaders = [TextLoader("file1.txt"), TextLoader("file2.txt")]
docs = [doc for loader in loaders for doc in loader.load()] child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
vectorstore = Chroma(collection_name="full_documents", embedding_function=OpenAIEmbeddings())
store = InMemoryStore()
retriever = ParentDocumentRetriever(vectorstore=vectorstore, docstore=store, child_splitter=child_splitter) retriever.add_documents(docs, ids=None) retrieved_docs = retriever.get_relevant_documents("query")
Средство извлечения с самозапросами создает структурированные запросы на основе входных данных естественного языка и применяет их к своему базовому векторному хранилищу. Его реализация показана в следующем коде:
from langchain.chat_models from ChatOpenAI
from langchain.chains.query_constructor.base from AttributeInfo
from langchain.retrievers.self_query.base from SelfQueryRetriever metadata_field_info = [AttributeInfo(name="genre", description="...", type="string"), ...]
document_content_description = "Brief summary of a movie"
llm = ChatOpenAI(temperature=0) retriever = SelfQueryRetriever.from_llm(llm, vectorstore, document_content_description, metadata_field_info) retrieved_docs = retriever.invoke("query")
WebResearchRetriever выполняет веб-исследования на основе заданного запроса.
from langchain.retrievers.web_research import WebResearchRetriever # Initialize components
llm = ChatOpenAI(temperature=0)
search = GoogleSearchAPIWrapper()
vectorstore = Chroma(embedding_function=OpenAIEmbeddings()) # Instantiate WebResearchRetriever
web_research_retriever = WebResearchRetriever.from_llm(vectorstore=vectorstore, llm=llm, search=search) # Retrieve documents
docs = web_research_retriever.get_relevant_documents("query")
Для наших примеров мы также можем использовать стандартный ретривер, уже реализованный как часть нашего объекта векторного хранилища, следующим образом:
Теперь мы можем запросить ретриверы. Результатом нашего запроса будут объекты документа, соответствующие запросу. В конечном итоге они будут использованы для создания соответствующих ответов в дальнейших разделах.
Автоматизируйте ручные задачи и рабочие процессы с помощью нашего конструктора рабочих процессов на основе искусственного интеллекта, разработанного Nanonets для вас и ваших команд.
Модуль III: Агенты
LangChain представляет мощную концепцию под названием «Агенты», которая выводит идею цепочек на совершенно новый уровень. Агенты используют языковые модели для динамического определения последовательности действий, что делает их невероятно универсальными и адаптивными. В отличие от традиционных цепочек, где действия жестко запрограммированы в коде, агенты используют языковые модели в качестве механизмов рассуждения, чтобы решить, какие действия предпринять и в каком порядке.
Агент является основным компонентом, ответственным за принятие решений. Он использует возможности языковой модели и подсказки для определения следующих шагов для достижения конкретной цели. Входные данные для агента обычно включают в себя:
- Инструменты: Описания доступных инструментов (подробнее об этом позже).
- Пользовательский ввод: Цель высокого уровня или запрос пользователя.
- Промежуточные шаги: История пар (действие, вывод инструмента), выполняемых для достижения текущего пользовательского ввода.
Выход агента может быть следующим действие принять меры (Действия агента) или финал ответ отправить пользователю (АгентФиниш). действие указывает инструментом и вход для этого инструмента.
Инструменты
Инструменты — это интерфейсы, которые агент может использовать для взаимодействия с миром. Они позволяют агентам выполнять различные задачи, такие как поиск в Интернете, выполнение команд оболочки или доступ к внешним API. В LangChain инструменты необходимы для расширения возможностей агентов и предоставления им возможности выполнять разнообразные задачи.
Чтобы использовать инструменты в LangChain, вы можете загрузить их, используя следующий фрагмент:
from langchain.agents import load_tools tool_names = [...]
tools = load_tools(tool_names)
Некоторым инструментам для инициализации может потребоваться базовая языковая модель (LLM). В таких случаях вы также можете пройти LLM:
from langchain.agents import load_tools tool_names = [...]
llm = ...
tools = load_tools(tool_names, llm=llm)
Эта настройка позволяет вам получить доступ к множеству инструментов и интегрировать их в рабочие процессы вашего агента. Полный список инструментов с документацией по использованию см. здесь.
Давайте посмотрим на некоторые примеры инструментов.
DuckDuckGo
Инструмент DuckDuckGo позволяет выполнять поиск в Интернете с помощью его поисковой системы. Вот как его использовать:
from langchain.tools import DuckDuckGoSearchRun
search = DuckDuckGoSearchRun()
search.run("manchester united vs luton town match summary")
ДанныеФорСео
Инструментарий DataForSeo позволяет получать результаты поисковых систем с помощью API DataForSeo. Чтобы использовать этот набор инструментов, вам необходимо настроить учетные данные API. Вот как настроить учетные данные:
import os os.environ["DATAFORSEO_LOGIN"] = "<your_api_access_username>"
os.environ["DATAFORSEO_PASSWORD"] = "<your_api_access_password>"
После того как ваши учетные данные установлены, вы можете создать DataForSeoAPIWrapper
инструмент для доступа к API:
from langchain.utilities.dataforseo_api_search import DataForSeoAPIWrapper wrapper = DataForSeoAPIWrapper() result = wrapper.run("Weather in Los Angeles")
Ассоциация DataForSeoAPIWrapper
Инструмент извлекает результаты поисковых систем из различных источников.
Вы можете настроить тип результатов и полей, возвращаемых в ответе JSON. Например, вы можете указать типы результатов, поля и установить максимальное количество возвращаемых лучших результатов:
json_wrapper = DataForSeoAPIWrapper( json_result_types=["organic", "knowledge_graph", "answer_box"], json_result_fields=["type", "title", "description", "text"], top_count=3,
) json_result = json_wrapper.results("Bill Gates")
В этом примере настраивается ответ JSON, указывая типы результатов, поля и ограничивая количество результатов.
Вы также можете указать местоположение и язык результатов поиска, передав дополнительные параметры в оболочку API:
customized_wrapper = DataForSeoAPIWrapper( top_count=10, json_result_types=["organic", "local_pack"], json_result_fields=["title", "description", "type"], params={"location_name": "Germany", "language_code": "en"},
) customized_result = customized_wrapper.results("coffee near me")
Указав параметры местоположения и языка, вы можете адаптировать результаты поиска к конкретным регионам и языкам.
У вас есть возможность выбрать поисковую систему, которую вы хотите использовать. Просто укажите желаемую поисковую систему:
customized_wrapper = DataForSeoAPIWrapper( top_count=10, json_result_types=["organic", "local_pack"], json_result_fields=["title", "description", "type"], params={"location_name": "Germany", "language_code": "en", "se_name": "bing"},
) customized_result = customized_wrapper.results("coffee near me")
В этом примере поиск настроен на использование Bing в качестве поисковой системы.
Оболочка API также позволяет вам указать тип поиска, который вы хотите выполнить. Например, вы можете выполнить поиск по картам:
maps_search = DataForSeoAPIWrapper( top_count=10, json_result_fields=["title", "value", "address", "rating", "type"], params={ "location_coordinate": "52.512,13.36,12z", "language_code": "en", "se_type": "maps", },
) maps_search_result = maps_search.results("coffee near me")
Это настраивает поиск для получения информации, связанной с картами.
Ракушка (баш)
Инструментарий Shell предоставляет агентам доступ к среде оболочки, позволяя им выполнять команды оболочки. Эта функция является мощной, но ее следует использовать с осторожностью, особенно в изолированных средах. Вот как вы можете использовать инструмент Shell:
from langchain.tools import ShellTool shell_tool = ShellTool() result = shell_tool.run({"commands": ["echo 'Hello World!'", "time"]})
В этом примере инструмент Shell выполняет две команды оболочки: повторяя «Hello World!» и отображение текущего времени.
Вы можете предоставить агенту инструмент Shell для выполнения более сложных задач. Вот пример агента, получающего ссылки с веб-страницы с помощью инструмента Shell:
from langchain.agents import AgentType, initialize_agent
from langchain.chat_models import ChatOpenAI llm = ChatOpenAI(temperature=0.1) shell_tool.description = shell_tool.description + f"args {shell_tool.args}".replace( "{", "{{"
).replace("}", "}}")
self_ask_with_search = initialize_agent( [shell_tool], llm, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True
)
self_ask_with_search.run( "Download the langchain.com webpage and grep for all urls. Return only a sorted list of them. Be sure to use double quotes."
)
В этом сценарии агент использует инструмент Shell для выполнения последовательности команд для получения, фильтрации и сортировки URL-адресов с веб-страницы.
Приведенные примеры демонстрируют некоторые инструменты, доступные в LangChain. Эти инструменты в конечном итоге расширяют возможности агентов (рассматриваемые в следующем подразделе) и позволяют им эффективно выполнять различные задачи. В зависимости от ваших требований вы можете выбрать инструменты и наборы инструментов, которые лучше всего соответствуют потребностям вашего проекта, и интегрировать их в рабочие процессы вашего агента.
Вернуться к Агентам
Теперь перейдем к агентам.
AgentExecutor — это среда выполнения агента. Он отвечает за вызов агента, выполнение выбранных им действий, передачу результатов действия обратно агенту и повторение процесса до тех пор, пока агент не завершит работу. В псевдокоде AgentExecutor может выглядеть примерно так:
next_action = agent.get_action(...)
while next_action != AgentFinish: observation = run(next_action) next_action = agent.get_action(..., next_action, observation)
return next_action
AgentExecutor справляется с различными сложностями, такими как обработка случаев, когда агент выбирает несуществующий инструмент, обработка ошибок инструмента, управление выходными данными, созданными агентом, а также обеспечение регистрации и наблюдения на всех уровнях.
Хотя класс AgentExecutor является основной средой выполнения агента в LangChain, поддерживаются и другие, более экспериментальные среды выполнения, в том числе:
- Агент планирования и реализации
- Детский АГИ
- Авто GPT
Чтобы лучше понять структуру агента, давайте создадим базовый агент с нуля, а затем перейдем к изучению уже созданных агентов.
Прежде чем мы углубимся в создание агента, важно вернуться к некоторой ключевой терминологии и схеме:
- АгентДействие: Это класс данных, представляющий действие, которое должен предпринять агент. Он состоит из
tool
свойство (имя вызываемого инструмента) иtool_input
свойство (входные данные для этого инструмента). - АгентФиниш: Этот класс данных указывает, что агент завершил свою задачу и должен вернуть ответ пользователю. Обычно он включает в себя словарь возвращаемых значений, часто с ключевым «выводом», содержащим текст ответа.
- Промежуточные шаги: Это записи предыдущих действий агента и соответствующие результаты. Они имеют решающее значение для передачи контекста будущим итерациям агента.
В нашем примере мы будем использовать вызов функций OpenAI для создания нашего агента. Этот подход надежен для создания агентов. Мы начнем с создания простого инструмента, который вычисляет длину слова. Этот инструмент полезен, поскольку языковые модели иногда могут допускать ошибки из-за токенизации при подсчете длины слов.
Сначала давайте загрузим языковую модель, которую мы будем использовать для управления агентом:
from langchain.chat_models import ChatOpenAI llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
Давайте проверим модель с вычислением длины слова:
llm.invoke("how many letters in the word educa?")
В ответе должно быть указано количество букв в слове «educa».
Далее мы определим простую функцию Python для вычисления длины слова:
from langchain.agents import tool @tool
def get_word_length(word: str) -> int: """Returns the length of a word.""" return len(word)
Мы создали инструмент под названием get_word_length
который принимает слово в качестве входных данных и возвращает его длину.
Теперь давайте создадим приглашение для агента. Подсказка инструктирует агента о том, как обосновать и отформатировать выходные данные. В нашем случае мы используем вызов функций OpenAI, который требует минимального количества инструкций. Мы определим подсказку с заполнителями для пользовательского ввода и блокнотом агента:
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder prompt = ChatPromptTemplate.from_messages( [ ( "system", "You are a very powerful assistant but not great at calculating word lengths.", ), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ]
)
Итак, как агент узнает, какие инструменты он может использовать? Мы полагаемся на языковые модели вызова функций OpenAI, которые требуют, чтобы функции передавались отдельно. Чтобы предоставить наши инструменты агенту, мы отформатируем их как вызовы функций OpenAI:
from langchain.tools.render import format_tool_to_openai_function llm_with_tools = llm.bind(functions=[format_tool_to_openai_function(t) for t in tools])
Теперь мы можем создать агент, определив входные сопоставления и соединив компоненты:
Это язык LCEL. Мы обсудим это позже подробно.
from langchain.agents.format_scratchpad import format_to_openai_function_messages
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser agent = ( { "input": lambda x: x["input"], "agent_scratchpad": lambda x: format_to_openai _function_messages( x["intermediate_steps"] ), } | prompt | llm_with_tools | OpenAIFunctionsAgentOutputParser()
)
Мы создали наш агент, который понимает ввод пользователя, использует доступные инструменты и форматирует вывод. Теперь давайте с ним взаимодействовать:
agent.invoke({"input": "how many letters in the word educa?", "intermediate_steps": []})
Агент должен ответить AgentAction, указывая следующее действие, которое необходимо предпринять.
Мы создали агент, но теперь нам нужно написать для него среду выполнения. Самая простая среда выполнения — это та, которая непрерывно вызывает агент, выполняет действия и повторяет действия до тех пор, пока агент не завершит работу. Вот пример:
from langchain.schema.agent import AgentFinish user_input = "how many letters in the word educa?"
intermediate_steps = [] while True: output = agent.invoke( { "input": user_input, "intermediate_steps": intermediate_steps, } ) if isinstance(output, AgentFinish): final_result = output.return_values["output"] break else: print(f"TOOL NAME: {output.tool}") print(f"TOOL INPUT: {output.tool_input}") tool = {"get_word_length": get_word_length}[output.tool] observation = tool.run(output.tool_input) intermediate_steps.append((output, observation)) print(final_result)
В этом цикле мы неоднократно вызываем агент, выполняем действия и обновляем промежуточные шаги, пока агент не завершит работу. Мы также обрабатываем взаимодействие инструментов внутри цикла.
Чтобы упростить этот процесс, LangChain предоставляет класс AgentExecutor, который инкапсулирует выполнение агента и предлагает обработку ошибок, раннюю остановку, отслеживание и другие улучшения. Давайте воспользуемся AgentExecutor для взаимодействия с агентом:
from langchain.agents import AgentExecutor agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) agent_executor.invoke({"input": "how many letters in the word educa?"})
AgentExecutor упрощает процесс выполнения и предоставляет удобный способ взаимодействия с агентом.
Память также подробно обсуждается позже.
Агент, который мы создали до сих пор, не имеет состояния, то есть он не помнит предыдущие взаимодействия. Чтобы включить дополнительные вопросы и беседы, нам нужно добавить агенту память. Это включает в себя два шага:
- Добавьте переменную памяти в приглашение для хранения истории чата.
- Следите за историей чата во время взаимодействия.
Начнем с добавления заполнителя памяти в командную строку:
from langchain.prompts import MessagesPlaceholder MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages( [ ( "system", "You are a very powerful assistant but not great at calculating word lengths.", ), MessagesPlaceholder(variable_name=MEMORY_KEY), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ]
)
Теперь создайте список для отслеживания истории чата:
from langchain.schema.messages import HumanMessage, AIMessage chat_history = []
На этапе создания агента мы также добавим память:
agent = ( { "input": lambda x: x["input"], "agent_scratchpad": lambda x: format_to_openai_function_messages( x["intermediate_steps"] ), "chat_history": lambda x: x["chat_history"], } | prompt | llm_with_tools | OpenAIFunctionsAgentOutputParser()
)
Теперь при запуске агента обязательно обновите историю чата:
input1 = "how many letters in the word educa?"
result = agent_executor.invoke({"input": input1, "chat_history": chat_history})
chat_history.extend([ HumanMessage(content=input1), AIMessage(content=result["output"]),
])
agent_executor.invoke({"input": "is that a real word?", "chat_history": chat_history})
Это позволяет агенту вести историю разговоров и отвечать на дополнительные вопросы на основе предыдущих взаимодействий.
Поздравляем! Вы успешно создали и запустили свой первый сквозной агент в LangChain. Чтобы глубже изучить возможности LangChain, вы можете изучить:
- Поддерживаются различные типы агентов.
- Готовые агенты
- Как работать с инструментами и интеграциями инструментов.
Типы агентов
LangChain предлагает различные типы агентов, каждый из которых подходит для конкретных случаев использования. Вот некоторые из доступных агентов:
- Реакт с нулевым выстрелом: Этот агент использует структуру ReAct для выбора инструментов исключительно на основе их описаний. Он требует описания каждого инструмента и очень универсален.
- Структурированный ввод ReAct: Этот агент обрабатывает инструменты с несколькими входами и подходит для сложных задач, таких как навигация по веб-браузеру. Он использует схему аргументов инструментов для структурированного ввода.
- Функции OpenAI: Этот агент, специально разработанный для моделей, настроенных для вызова функций, совместим с такими моделями, как gpt-3.5-turbo-0613 и gpt-4-0613. Мы использовали это для создания нашего первого агента выше.
- Разговорный: Этот агент, предназначенный для диалоговых настроек, использует ReAct для выбора инструментов и использует память для запоминания предыдущих взаимодействий.
- Задайте себе вопрос с помощью поиска: Этот агент использует единственный инструмент «Промежуточный ответ», который ищет фактические ответы на вопросы. Это эквивалентно первоначальному самозапросу с поисковым документом.
- Хранилище документов ReAct: Этот агент взаимодействует с хранилищем документов с помощью платформы ReAct. Для этого требуются инструменты «Поиск» и «Поиск», и он аналогичен примеру исходной статьи ReAct в Википедии.
Изучите эти типы агентов, чтобы найти тот, который лучше всего соответствует вашим потребностям в LangChain. Эти агенты позволяют вам связывать внутри себя набор инструментов для обработки действий и генерации ответов. Узнайте больше на как создать свой собственный агент с помощью инструментов здесь.
Готовые агенты
Давайте продолжим исследование агентов, сосредоточив внимание на готовых агентах, доступных в LangChain.
Gmail
LangChain предлагает набор инструментов Gmail, который позволяет вам подключить электронную почту LangChain к API Gmail. Чтобы начать работу, вам необходимо настроить свои учетные данные, которые описаны в документации Gmail API. После того, как вы загрузили credentials.json
файл, вы можете продолжить использование Gmail API. Кроме того, вам потребуется установить некоторые необходимые библиотеки, используя следующие команды:
pip install --upgrade google-api-python-client > /dev/null
pip install --upgrade google-auth-oauthlib > /dev/null
pip install --upgrade google-auth-httplib2 > /dev/null
pip install beautifulsoup4 > /dev/null # Optional for parsing HTML messages
Вы можете создать набор инструментов Gmail следующим образом:
from langchain.agents.agent_toolkits import GmailToolkit toolkit = GmailToolkit()
Вы также можете настроить аутентификацию в соответствии со своими потребностями. За кулисами ресурс googleapi создается с использованием следующих методов:
from langchain.tools.gmail.utils import build_resource_service, get_gmail_credentials credentials = get_gmail_credentials( token_file="token.json", scopes=["https://mail.google.com/"], client_secrets_file="credentials.json",
)
api_resource = build_resource_service(credentials=credentials)
toolkit = GmailToolkit(api_resource=api_resource)
Набор инструментов предлагает различные инструменты, которые можно использовать внутри агента, в том числе:
GmailCreateDraft
: создать черновик электронного письма с указанными полями сообщения.GmailSendMessage
: Отправка сообщений электронной почты.GmailSearch
: поиск сообщений электронной почты или цепочек.GmailGetMessage
: Получить электронное письмо по идентификатору сообщения.GmailGetThread
: поиск сообщений электронной почты.
Чтобы использовать эти инструменты в агенте, вы можете инициализировать агент следующим образом:
from langchain.llms import OpenAI
from langchain.agents import initialize_agent, AgentType llm = OpenAI(temperature=0)
agent = initialize_agent( tools=toolkit.get_tools(), llm=llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
)
Вот несколько примеров того, как можно использовать эти инструменты:
- Создайте черновик Gmail для редактирования:
agent.run( "Create a gmail draft for me to edit of a letter from the perspective of a sentient parrot " "who is looking to collaborate on some research with her estranged friend, a cat. " "Under no circumstances may you send the message, however."
)
- Найдите последнее письмо в черновиках:
agent.run("Could you search in my drafts for the latest email?")
Эти примеры демонстрируют возможности набора инструментов Gmail от LangChain внутри агента, позволяющего программно взаимодействовать с Gmail.
Агент базы данных SQL
В этом разделе представлен обзор агента, предназначенного для взаимодействия с базами данных SQL, в частности с базой данных Chinook. Этот агент может отвечать на общие вопросы о базе данных и устранять ошибки. Обратите внимание, что он все еще находится в активной разработке и не все ответы могут быть правильными. Будьте осторожны при запуске его с конфиденциальными данными, так как он может выполнять операторы DML в вашей базе данных.
Чтобы использовать этот агент, вы можете инициализировать его следующим образом:
from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.sql_database import SQLDatabase
from langchain.llms.openai import OpenAI
from langchain.agents import AgentExecutor
from langchain.agents.agent_types import AgentType
from langchain.chat_models import ChatOpenAI db = SQLDatabase.from_uri("sqlite:///../../../../../notebooks/Chinook.db")
toolkit = SQLDatabaseToolkit(db=db, llm=OpenAI(temperature=0)) agent_executor = create_sql_agent( llm=OpenAI(temperature=0), toolkit=toolkit, verbose=True, agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
)
Этот агент можно инициализировать с помощью ZERO_SHOT_REACT_DESCRIPTION
тип агента. Он предназначен для ответов на вопросы и предоставления описаний. Альтернативно, вы можете инициализировать агент, используя команду OPENAI_FUNCTIONS
тип агента с моделью OpenAI GPT-3.5-turbo, которую мы использовали в нашем предыдущем клиенте.
Отказ от ответственности
- Цепочка запросов может генерировать запросы на вставку/обновление/удаление. Будьте осторожны и при необходимости используйте собственный запрос или создайте пользователя SQL без прав на запись.
- Имейте в виду, что выполнение определенных запросов, например «выполнить максимально возможный запрос», может перегрузить вашу базу данных SQL, особенно если она содержит миллионы строк.
- Базы данных, ориентированные на хранилища данных, часто поддерживают квоты на уровне пользователя для ограничения использования ресурсов.
Вы можете попросить агента описать таблицу, например таблицу «playlisttrack». Вот пример того, как это сделать:
agent_executor.run("Describe the playlisttrack table")
Агент предоставит информацию о схеме таблицы и примерах строк.
Если вы по ошибке спросите о несуществующей таблице, агент сможет восстановить ее и предоставить информацию о ближайшей совпадающей таблице. Например:
agent_executor.run("Describe the playlistsong table")
Агент найдет ближайшую таблицу соответствия и предоставит информацию о ней.
Вы также можете попросить агента выполнить запросы к базе данных. Например:
agent_executor.run("List the total sales per country. Which country's customers spent the most?")
Агент выполнит запрос и предоставит результат, например страну с самым высоким общим объемом продаж.
Чтобы получить общее количество треков в каждом плейлисте, вы можете использовать следующий запрос:
agent_executor.run("Show the total number of tracks in each playlist. The Playlist name should be included in the result.")
Агент вернет названия плейлистов вместе с соответствующим общим количеством треков.
В случаях, когда агент сталкивается с ошибками, он может восстановить их и предоставить точные ответы. Например:
agent_executor.run("Who are the top 3 best selling artists?")
Даже после обнаружения первоначальной ошибки агент внесет коррективы и предоставит правильный ответ, которым в данном случае является тройка самых продаваемых исполнителей.
Агент DataFrame Pandas
В этом разделе представлен агент, предназначенный для взаимодействия с кадрами данных Pandas для целей ответа на вопросы. Обратите внимание, что этот агент использует «под капотом» агента Python для выполнения кода Python, сгенерированного языковой моделью (LLM). Соблюдайте осторожность при использовании этого агента, чтобы предотвратить потенциальный вред от вредоносного кода Python, созданного LLM.
Вы можете инициализировать агент Pandas DataFrame следующим образом:
from langchain_experimental.agents.agent_toolkits 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 df = pd.read_csv("titanic.csv") # Using ZERO_SHOT_REACT_DESCRIPTION agent type
agent = create_pandas_dataframe_agent(OpenAI(temperature=0), df, verbose=True) # Alternatively, using OPENAI_FUNCTIONS agent type
# agent = create_pandas_dataframe_agent(
# ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0613"),
# df,
# verbose=True,
# agent_type=AgentType.OPENAI_FUNCTIONS,
# )
Вы можете попросить агента посчитать количество строк в DataFrame:
agent.run("how many rows are there?")
Агент выполнит код df.shape[0]
и дайте ответ, например: «В кадре данных 891 строка».
Вы также можете попросить агента отфильтровать строки на основе определенных критериев, например найти количество людей, у которых более трех братьев и сестер:
agent.run("how many people have more than 3 siblings")
Агент выполнит код df[df['SibSp'] > 3].shape[0]
и дайте ответ, например: «У 30 человек более трех братьев и сестер».
Если вы хотите вычислить квадратный корень из среднего возраста, вы можете попросить агента:
agent.run("whats the square root of the average age?")
Агент рассчитает средний возраст, используя df['Age'].mean()
а затем вычислите квадратный корень, используя math.sqrt()
. Он даст ответ, например: «Квадратный корень из среднего возраста равен 5.449689683556195».
Давайте создадим копию DataFrame, а отсутствующие значения возраста заполнятся средним возрастом:
df1 = df.copy()
df1["Age"] = df1["Age"].fillna(df1["Age"].mean())
Затем вы можете инициализировать агент с помощью обоих DataFrames и задать ему вопрос:
agent = create_pandas_dataframe_agent(OpenAI(temperature=0), [df, df1], verbose=True)
agent.run("how many rows in the age column are different?")
Агент сравнит столбцы возраста в обоих DataFrames и предоставит ответ, например: «177 строк в столбце возраста различаются».
Набор инструментов Джира
В этом разделе объясняется, как использовать набор инструментов Jira, который позволяет агентам взаимодействовать с экземпляром Jira. С помощью этого набора инструментов вы можете выполнять различные действия, такие как поиск и создание проблем. Он использует библиотеку atlassian-python-api. Чтобы использовать этот набор инструментов, вам необходимо установить переменные среды для вашего экземпляра Jira, включая JIRA_API_TOKEN, JIRA_USERNAME и JIRA_INSTANCE_URL. Кроме того, вам может потребоваться установить ключ API OpenAI в качестве переменной среды.
Для начала установите библиотеку atlassian-python-api и установите необходимые переменные среды:
%pip install atlassian-python-api import os
from langchain.agents import AgentType
from langchain.agents import initialize_agent
from langchain.agents.agent_toolkits.jira.toolkit import JiraToolkit
from langchain.llms import OpenAI
from langchain.utilities.jira import JiraAPIWrapper os.environ["JIRA_API_TOKEN"] = "abc"
os.environ["JIRA_USERNAME"] = "123"
os.environ["JIRA_INSTANCE_URL"] = "https://jira.atlassian.com"
os.environ["OPENAI_API_KEY"] = "xyz" llm = OpenAI(temperature=0)
jira = JiraAPIWrapper()
toolkit = JiraToolkit.from_jira_api_wrapper(jira)
agent = initialize_agent( toolkit.get_tools(), llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True
)
Вы можете поручить агенту создать новую задачу в конкретном проекте с кратким описанием и описанием:
agent.run("make a new issue in project PW to remind me to make more fried rice")
Агент выполнит необходимые действия для создания проблемы и предоставит ответ, например: «В проекте PW создана новая проблема с краткой информацией «Приготовьте еще жареного риса» и описанием «Напоминание приготовить еще жареного риса».
Это позволяет вам взаимодействовать с вашим экземпляром Jira, используя инструкции на естественном языке и набор инструментов Jira.
Автоматизируйте ручные задачи и рабочие процессы с помощью нашего конструктора рабочих процессов на основе искусственного интеллекта, разработанного Nanonets для вас и ваших команд.
Модуль IV: Цепи
LangChain — это инструмент, предназначенный для использования больших языковых моделей (LLM) в сложных приложениях. Он предоставляет основу для создания цепочек компонентов, включая LLM и другие типы компонентов. Две основные структуры
- Язык выражений LangChain (LCEL)
- Интерфейс устаревшей цепочки
Язык выражений LangChain (LCEL) — это синтаксис, который позволяет интуитивно составлять цепочки. Он поддерживает расширенные функции, такие как потоковая передача, асинхронные вызовы, пакетная обработка, распараллеливание, повторные попытки, резервные варианты и трассировка. Например, вы можете составить приглашение, модель и анализатор вывода в LCEL, как показано в следующем коде:
from langchain.prompts import ChatPromptTemplate
from langchain.schema import StrOutputParser model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
prompt = ChatPromptTemplate.from_messages([ ("system", "You're a very knowledgeable historian who provides accurate and eloquent answers to historical questions."), ("human", "{question}")
])
runnable = prompt | model | StrOutputParser() for chunk in runnable.stream({"question": "What are the seven wonders of the world"}): print(chunk, end="", flush=True)
Альтернативно, LLMChain — это вариант, аналогичный LCEL, для составления компонентов. Пример LLMChain выглядит следующим образом:
from langchain.chains import LLMChain chain = LLMChain(llm=model, prompt=prompt, output_parser=StrOutputParser())
chain.run(question="What are the seven wonders of the world")
Цепочки в LangChain также могут иметь состояние путем включения объекта Memory. Это обеспечивает сохранение данных между вызовами, как показано в этом примере:
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory conversation = ConversationChain(llm=chat, memory=ConversationBufferMemory())
conversation.run("Answer briefly. What are the first 3 colors of a rainbow?")
conversation.run("And the next 4?")
LangChain также поддерживает интеграцию с API-интерфейсами вызова функций OpenAI, что полезно для получения структурированных выходных данных и выполнения функций внутри цепочки. Чтобы получить структурированные выходные данные, вы можете указать их с помощью классов Pydantic или JsonSchema, как показано ниже:
from langchain.pydantic_v1 import BaseModel, Field
from langchain.chains.openai_functions import create_structured_output_runnable
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate class Person(BaseModel): name: str = Field(..., description="The person's name") age: int = Field(..., description="The person's age") fav_food: Optional[str] = Field(None, description="The person's favorite food") llm = ChatOpenAI(model="gpt-4", temperature=0)
prompt = ChatPromptTemplate.from_messages([ # Prompt messages here
]) runnable = create_structured_output_runnable(Person, llm, prompt)
runnable.invoke({"input": "Sally is 13"})
Для структурированных результатов также доступен устаревший подход с использованием LLMChain:
from langchain.chains.openai_functions import create_structured_output_chain class Person(BaseModel): name: str = Field(..., description="The person's name") age: int = Field(..., description="The person's age") chain = create_structured_output_chain(Person, llm, prompt, verbose=True)
chain.run("Sally is 13")
LangChain использует функции OpenAI для создания различных цепочек для разных целей. К ним относятся цепочки для извлечения, тегирования, OpenAPI и QA с цитатами.
В контексте извлечения этот процесс аналогичен структурированной цепочке вывода, но фокусируется на извлечении информации или объектов. Идея тегирования состоит в том, чтобы пометить документ такими классами, как настроения, язык, стиль, освещаемые темы или политические тенденции.
Пример того, как работает тегирование в LangChain, можно продемонстрировать с помощью кода Python. Процесс начинается с установки необходимых пакетов и настройки среды:
pip install langchain openai
# Set env var OPENAI_API_KEY or load from a .env file:
# import dotenv
# dotenv.load_dotenv() from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.chains import create_tagging_chain, create_tagging_chain_pydantic
Определяется схема для маркировки с указанием свойств и их ожидаемых типов:
schema = { "properties": { "sentiment": {"type": "string"}, "aggressiveness": {"type": "integer"}, "language": {"type": "string"}, }
} llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0613")
chain = create_tagging_chain(schema, llm)
Примеры запуска цепочки тегов с различными входными данными показывают способность модели интерпретировать настроения, языки и агрессивность:
inp = "Estoy increiblemente contento de haberte conocido! Creo que seremos muy buenos amigos!"
chain.run(inp)
# {'sentiment': 'positive', 'language': 'Spanish'} inp = "Estoy muy enojado con vos! Te voy a dar tu merecido!"
chain.run(inp)
# {'sentiment': 'enojado', 'aggressiveness': 1, 'language': 'es'}
Для более точного управления схема может быть определена более конкретно, включая возможные значения, описания и необходимые свойства. Пример этого расширенного элемента управления показан ниже:
schema = { "properties": { # Schema definitions here }, "required": ["language", "sentiment", "aggressiveness"],
} chain = create_tagging_chain(schema, llm)
Схемы Pydantic также можно использовать для определения критериев тегирования, предоставляя Pythonic-способ указания необходимых свойств и типов:
from enum import Enum
from pydantic import BaseModel, Field class Tags(BaseModel): # Class fields here chain = create_tagging_chain_pydantic(Tags, llm)
Кроме того, преобразователь документов тегов метаданных LangChain можно использовать для извлечения метаданных из документов LangChain, предлагая функциональность, аналогичную цепочке тегов, но применяемую к документу LangChain.
Цитирование поисковых источников — еще одна особенность LangChain, использующая функции OpenAI для извлечения цитат из текста. Это продемонстрировано в следующем коде:
from langchain.chains import create_citation_fuzzy_match_chain
from langchain.chat_models import ChatOpenAI llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0613")
chain = create_citation_fuzzy_match_chain(llm)
# Further code for running the chain and displaying results
В LangChain цепочка в приложениях большой языковой модели (LLM) обычно включает в себя объединение шаблона приглашения с LLM и, при необходимости, синтаксическим анализатором вывода. Рекомендуемый способ сделать это — использовать язык выражений LangChain (LCEL), хотя устаревший подход LLMChain также поддерживается.
Используя LCEL, BasePromptTemplate, BaseLanguageModel и BaseOutputParser реализуют интерфейс Runnable и могут быть легко перенаправлены друг в друга. Вот пример, демонстрирующий это:
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema import StrOutputParser prompt = PromptTemplate.from_template( "What is a good name for a company that makes {product}?"
)
runnable = prompt | ChatOpenAI() | StrOutputParser()
runnable.invoke({"product": "colorful socks"})
# Output: 'VibrantSocks'
Маршрутизация в LangChain позволяет создавать недетерминированные цепочки, в которых результат предыдущего шага определяет следующий шаг. Это помогает структурировать и поддерживать последовательность во взаимодействии с LLM. Например, если у вас есть два шаблона, оптимизированных для разных типов вопросов, вы можете выбрать шаблон на основе пользовательского ввода.
Вот как этого можно добиться, используя LCEL с RunnableBranch, который инициализируется списком пар (условие, исполняемый файл) и исполняемым объектом по умолчанию:
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnableBranch
# Code for defining physics_prompt and math_prompt general_prompt = PromptTemplate.from_template( "You are a helpful assistant. Answer the question as accurately as you can.nn{input}"
)
prompt_branch = RunnableBranch( (lambda x: x["topic"] == "math", math_prompt), (lambda x: x["topic"] == "physics", physics_prompt), general_prompt,
) # More code for setting up the classifier and final chain
Затем конструируется окончательная цепочка с использованием различных компонентов, таких как классификатор тем, ветвь приглашения и анализатор выходных данных, чтобы определить поток на основе темы входных данных:
from operator import itemgetter
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough final_chain = ( RunnablePassthrough.assign(topic=itemgetter("input") | classifier_chain) | prompt_branch | ChatOpenAI() | StrOutputParser()
) final_chain.invoke( { "input": "What is the first prime number greater than 40 such that one plus the prime number is divisible by 3?" }
)
# Output: Detailed answer to the math question
Этот подход иллюстрирует гибкость и мощь LangChain в обработке сложных запросов и их правильной маршрутизации на основе входных данных.
В области языковых моделей общепринятой практикой является сопровождение первоначального вызова серией последующих вызовов, используя выходные данные одного вызова в качестве входных данных для следующего. Этот последовательный подход особенно полезен, если вы хотите использовать информацию, полученную в ходе предыдущих взаимодействий. Хотя язык выражений LangChain (LCEL) является рекомендуемым методом создания этих последовательностей, метод SequentialChain по-прежнему документирован на предмет его обратной совместимости.
Чтобы проиллюстрировать это, давайте рассмотрим сценарий, в котором мы сначала создаем краткий обзор пьесы, а затем на основе этого обзора создаем обзор. Использование Python langchain.prompts
, мы создаем два PromptTemplate
экземпляры: один для краткого описания, другой для обзора. Вот код для настройки этих шаблонов:
from langchain.prompts import PromptTemplate synopsis_prompt = PromptTemplate.from_template( "You are a playwright. Given the title of play, it is your job to write a synopsis for that title.nnTitle: {title}nPlaywright: This is a synopsis for the above play:"
) review_prompt = PromptTemplate.from_template( "You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.nnPlay Synopsis:n{synopsis}nReview from a New York Times play critic of the above play:"
)
В подходе LCEL мы связываем эти подсказки с ChatOpenAI
и StrOutputParser
создать последовательность, которая сначала создает краткий обзор, а затем обзор. Фрагмент кода выглядит следующим образом:
from langchain.chat_models import ChatOpenAI
from langchain.schema import StrOutputParser llm = ChatOpenAI()
chain = ( {"synopsis": synopsis_prompt | llm | StrOutputParser()} | review_prompt | llm | StrOutputParser()
)
chain.invoke({"title": "Tragedy at sunset on the beach"})
Если нам нужны и краткий обзор, и обзор, мы можем использовать RunnablePassthrough
создать для каждого отдельную цепочку, а затем объединить их:
from langchain.schema.runnable import RunnablePassthrough synopsis_chain = synopsis_prompt | llm | StrOutputParser()
review_chain = review_prompt | llm | StrOutputParser()
chain = {"synopsis": synopsis_chain} | RunnablePassthrough.assign(review=review_chain)
chain.invoke({"title": "Tragedy at sunset on the beach"})
Для сценариев, включающих более сложные последовательности, SequentialChain
в дело вступает метод. Это позволяет использовать несколько входов и выходов. Рассмотрим случай, когда нам нужен синопсис, основанный на названии пьесы и эпохе. Вот как мы можем это настроить:
from langchain.llms import OpenAI
from langchain.chains import LLMChain, SequentialChain
from langchain.prompts import PromptTemplate llm = OpenAI(temperature=0.7) synopsis_template = "You are a playwright. Given the title of play and the era it is set in, it is your job to write a synopsis for that title.nnTitle: {title}nEra: {era}nPlaywright: This is a synopsis for the above play:"
synopsis_prompt_template = PromptTemplate(input_variables=["title", "era"], template=synopsis_template)
synopsis_chain = LLMChain(llm=llm, prompt=synopsis_prompt_template, output_key="synopsis") review_template = "You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.nnPlay Synopsis:n{synopsis}nReview from a New York Times play critic of the above play:"
prompt_template = PromptTemplate(input_variables=["synopsis"], template=review_template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review") overall_chain = SequentialChain( chains=[synopsis_chain, review_chain], input_variables=["era", "title"], output_variables=["synopsis", "review"], verbose=True,
) overall_chain({"title": "Tragedy at sunset on the beach", "era": "Victorian England"})
В сценариях, где вы хотите сохранить контекст на протяжении всей цепочки или для более поздней части цепочки, SimpleMemory
может быть использован. Это особенно полезно для управления сложными отношениями ввода/вывода. Например, в сценарии, где мы хотим создавать публикации в социальных сетях на основе названия пьесы, эпохи, краткого описания и рецензии, SimpleMemory
может помочь управлять этими переменными:
from langchain.memory import SimpleMemory
from langchain.chains import SequentialChain template = "You are a social media manager for a theater company. Given the title of play, the era it is set in, the date, time and location, the synopsis of the play, and the review of the play, it is your job to write a social media post for that play.nnHere is some context about the time and location of the play:nDate and Time: {time}nLocation: {location}nnPlay Synopsis:n{synopsis}nReview from a New York Times play critic of the above play:n{review}nnSocial Media Post:"
prompt_template = PromptTemplate(input_variables=["synopsis", "review", "time", "location"], template=template)
social_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="social_post_text") overall_chain = SequentialChain( memory=SimpleMemory(memories={"time": "December 25th, 8pm PST", "location": "Theater in the Park"}), chains=[synopsis_chain, review_chain, social_chain], input_variables=["era", "title"], output_variables=["social_post_text"], verbose=True,
) overall_chain({"title": "Tragedy at sunset on the beach", "era": "Victorian England"})
Помимо последовательных цепочек, существуют специализированные цепочки для работы с документами. Каждая из этих цепочек служит разным целям: от объединения документов до уточнения ответов на основе итеративного анализа документов, до сопоставления и сокращения содержания документа для обобщения или повторного ранжирования на основе оцененных ответов. Эти цепочки можно воссоздать с помощью LCEL для дополнительной гибкости и настройки.
-
StuffDocumentsChain
объединяет список документов в одно приглашение, передаваемое LLM. -
RefineDocumentsChain
обновляет свой ответ итеративно для каждого документа, что подходит для задач, в которых документы превышают емкость контекста модели. -
MapReduceDocumentsChain
применяет цепочку к каждому документу индивидуально, а затем объединяет результаты. -
MapRerankDocumentsChain
оценивает каждый ответ на основе документа и выбирает ответ с наивысшим баллом.
Вот пример того, как можно настроить MapReduceDocumentsChain
используя LCEL:
from functools import partial
from langchain.chains.combine_documents import collapse_docs, split_list_of_docs
from langchain.schema import Document, StrOutputParser
from langchain.schema.prompt_template import format_document
from langchain.schema.runnable import RunnableParallel, RunnablePassthrough llm = ChatAnthropic()
document_prompt = PromptTemplate.from_template("{page_content}")
partial_format_document = partial(format_document, prompt=document_prompt) map_chain = ( {"context": partial_format_document} | PromptTemplate.from_template("Summarize this content:nn{context}") | llm | StrOutputParser()
) map_as_doc_chain = ( RunnableParallel({"doc": RunnablePassthrough(), "content": map_chain}) | (lambda x: Document(page_content=x["content"], metadata=x["doc"].metadata))
).with_config(run_name="Summarize (return doc)") def format_docs(docs): return "nn".join(partial_format_document(doc) for doc in docs) collapse_chain = ( {"context": format_docs} | PromptTemplate.from_template("Collapse this content:nn{context}") | llm | StrOutputParser()
) reduce_chain = ( {"context": format_docs} | PromptTemplate.from_template("Combine these summaries:nn{context}") | llm | StrOutputParser()
).with_config(run_name="Reduce") map_reduce = (map_as_doc_chain.map() | collapse | reduce_chain).with_config(run_name="Map reduce")
Эта конфигурация позволяет проводить детальный и всесторонний анализ содержимого документа, используя сильные стороны LCEL и базовой языковой модели.
Автоматизируйте ручные задачи и рабочие процессы с помощью нашего конструктора рабочих процессов на основе искусственного интеллекта, разработанного Nanonets для вас и ваших команд.
Модуль V: Память
В LangChain память является фундаментальным аспектом диалоговых интерфейсов, позволяющим системам ссылаться на прошлые взаимодействия. Это достигается за счет хранения и запроса информации с двумя основными действиями: чтение и запись. Система памяти дважды взаимодействует с цепочкой во время запуска, дополняя вводимые пользователем данные и сохраняя входные и выходные данные для дальнейшего использования.
Встраивание памяти в систему
- Хранение сообщений чата: Модуль памяти LangChain объединяет различные методы хранения сообщений чата, от списков в памяти до баз данных. Это гарантирует, что все взаимодействия в чате записываются для дальнейшего использования.
- Запрос сообщений чата: Помимо хранения сообщений чата, LangChain использует структуры данных и алгоритмы для создания полезного представления этих сообщений. Простые системы памяти могут возвращать недавние сообщения, тогда как более продвинутые системы могут суммировать прошлые взаимодействия или фокусироваться на сущностях, упомянутых в текущем взаимодействии.
Чтобы продемонстрировать использование памяти в LangChain, рассмотрим ConversationBufferMemory
класс, простая форма памяти, которая хранит сообщения чата в буфере. Вот пример:
from langchain.memory import ConversationBufferMemory memory = ConversationBufferMemory()
memory.chat_memory.add_user_message("Hello!")
memory.chat_memory.add_ai_message("How can I assist you?")
При интеграции памяти в цепочку крайне важно понимать переменные, возвращаемые из памяти, и то, как они используются в цепочке. Например, load_memory_variables
Метод помогает согласовать переменные, считанные из памяти, с ожиданиями цепочки.
Комплексный пример с LangChain
Рассмотреть возможность использования ConversationBufferMemory
в LLMChain
. Цепочка в сочетании с соответствующим шаблоном подсказок и памятью обеспечивает беспрепятственный диалог. Вот упрощенный пример:
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory llm = OpenAI(temperature=0)
template = "Your conversation template here..."
prompt = PromptTemplate.from_template(template)
memory = ConversationBufferMemory(memory_key="chat_history")
conversation = LLMChain(llm=llm, prompt=prompt, memory=memory) response = conversation({"question": "What's the weather like?"})
Этот пример иллюстрирует, как система памяти LangChain интегрируется с ее цепочками, чтобы обеспечить последовательный и контекстно-зависимый диалог.
Типы памяти в Langchain
Langchain предлагает различные типы памяти, которые можно использовать для улучшения взаимодействия с моделями ИИ. Каждый тип памяти имеет свои собственные параметры и типы возвращаемых значений, что делает их подходящими для разных сценариев. Давайте рассмотрим некоторые типы памяти, доступные в Langchain, вместе с примерами кода.
1. Буферная память разговора
Этот тип памяти позволяет хранить и извлекать сообщения из разговоров. Вы можете извлечь историю в виде строки или списка сообщений.
from langchain.memory import ConversationBufferMemory memory = ConversationBufferMemory()
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.load_memory_variables({}) # Extract history as a string
{'history': 'Human: hinAI: whats up'} # Extract history as a list of messages
{'history': [HumanMessage(content='hi', additional_kwargs={}), AIMessage(content='whats up', additional_kwargs={})]}
Вы также можете использовать буферную память разговора в цепочке для взаимодействия в формате чата.
2. Память окна буфера разговора
Этот тип памяти хранит список последних взаимодействий и использует K последних взаимодействий, предотвращая слишком большой размер буфера.
from langchain.memory import ConversationBufferWindowMemory memory = ConversationBufferWindowMemory(k=1)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
memory.load_memory_variables({}) {'history': 'Human: not much younAI: not much'}
Как и буферная память разговора, вы также можете использовать этот тип памяти в цепочке для взаимодействий, подобных чату.
3. Память объекта разговора
Этот тип памяти запоминает факты о конкретных объектах в разговоре и извлекает информацию с помощью LLM.
from langchain.memory import ConversationEntityMemory
from langchain.llms import OpenAI llm = OpenAI(temperature=0)
memory = ConversationEntityMemory(llm=llm)
_input = {"input": "Deven & Sam are working on a hackathon project"}
memory.load_memory_variables(_input)
memory.save_context( _input, {"output": " That sounds like a great project! What kind of project are they working on?"}
)
memory.load_memory_variables({"input": 'who is Sam'}) {'history': 'Human: Deven & Sam are working on a hackathon projectnAI: That sounds like a great project! What kind of project are they working on?', 'entities': {'Sam': 'Sam is working on a hackathon project with Deven.'}}
4. Память диаграммы знаний разговора
Этот тип памяти использует граф знаний для воссоздания памяти. Из сообщений можно извлекать текущие сущности и тройки знаний.
from langchain.memory import ConversationKGMemory
from langchain.llms import OpenAI llm = OpenAI(temperature=0)
memory = ConversationKGMemory(llm=llm)
memory.save_context({"input": "say hi to sam"}, {"output": "who is sam"})
memory.save_context({"input": "sam is a friend"}, {"output": "okay"})
memory.load_memory_variables({"input": "who is sam"}) {'history': 'On Sam: Sam is friend.'}
Вы также можете использовать этот тип памяти в цепочке для извлечения знаний на основе разговора.
5. Память сводки разговоров
Этот тип памяти создает сводку разговора с течением времени, что полезно для сжатия информации из более длительных разговоров.
from langchain.memory import ConversationSummaryMemory
from langchain.llms import OpenAI llm = OpenAI(temperature=0)
memory = ConversationSummaryMemory(llm=llm)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.load_memory_variables({}) {'history': 'nThe human greets the AI, to which the AI responds.'}
6. Буферная память сводки разговоров
Этот тип памяти объединяет сводку разговора и буфер, поддерживая баланс между недавними взаимодействиями и сводкой. Он использует длину токена, чтобы определить, когда сбрасывать взаимодействия.
from langchain.memory import ConversationSummaryBufferMemory
from langchain.llms import OpenAI llm = OpenAI()
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=10)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
memory.load_memory_variables({}) {'history': 'System: nThe human says "hi", and the AI responds with "whats up".nHuman: not much younAI: not much'}
Вы можете использовать эти типы памяти для улучшения взаимодействия с моделями ИИ в Langchain. Каждый тип памяти служит определенной цели и может быть выбран в соответствии с вашими требованиями.
7. Буферная память токена разговора
ConversationTokenBufferMemory — еще один тип памяти, который хранит в памяти буфер недавних взаимодействий. В отличие от предыдущих типов памяти, которые фокусируются на количестве взаимодействий, этот использует длину токена, чтобы определить, когда сбрасывать взаимодействия.
Использование памяти с LLM:
from langchain.memory import ConversationTokenBufferMemory
from langchain.llms import OpenAI llm = OpenAI() memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=10)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"}) memory.load_memory_variables({}) {'history': 'Human: not much younAI: not much'}
В этом примере память настроена на ограничение взаимодействий на основе длины токена, а не количества взаимодействий.
При использовании этого типа памяти вы также можете получить историю в виде списка сообщений.
memory = ConversationTokenBufferMemory( llm=llm, max_token_limit=10, return_messages=True
)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
Использование в цепочке:
Вы можете использовать ConversationTokenBufferMemory в цепочке для улучшения взаимодействия с моделью ИИ.
from langchain.chains import ConversationChain conversation_with_summary = ConversationChain( llm=llm, # We set a very low max_token_limit for the purposes of testing. memory=ConversationTokenBufferMemory(llm=OpenAI(), max_token_limit=60), verbose=True,
)
conversation_with_summary.predict(input="Hi, what's up?")
В этом примере ConversationTokenBufferMemory используется в ConversationChain для управления диалогом и ограничения взаимодействий на основе длины токена.
8. VectorStoreRetrieverMemory
VectorStoreRetrieverMemory хранит воспоминания в векторном хранилище и запрашивает топ-K наиболее «значимых» документов при каждом вызове. Этот тип памяти не отслеживает явно порядок взаимодействий, а использует векторный поиск для извлечения соответствующих воспоминаний.
from datetime import datetime
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.llms import OpenAI
from langchain.memory import VectorStoreRetrieverMemory
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate # Initialize your vector store (specifics depend on the chosen vector store)
import faiss
from langchain.docstore import InMemoryDocstore
from langchain.vectorstores import FAISS embedding_size = 1536 # Dimensions of the OpenAIEmbeddings
index = faiss.IndexFlatL2(embedding_size)
embedding_fn = OpenAIEmbeddings().embed_query
vectorstore = FAISS(embedding_fn, index, InMemoryDocstore({}), {}) # Create your VectorStoreRetrieverMemory
retriever = vectorstore.as_retriever(search_kwargs=dict(k=1))
memory = VectorStoreRetrieverMemory(retriever=retriever) # Save context and relevant information to the memory
memory.save_context({"input": "My favorite food is pizza"}, {"output": "that's good to know"})
memory.save_context({"input": "My favorite sport is soccer"}, {"output": "..."})
memory.save_context({"input": "I don't like the Celtics"}, {"output": "ok"}) # Retrieve relevant information from memory based on a query
print(memory.load_memory_variables({"prompt": "what sport should i watch?"})["history"])
В этом примере VectorStoreRetrieverMemory используется для хранения и извлечения соответствующей информации из диалога на основе векторного поиска.
Вы также можете использовать VectorStoreRetrieverMemory в цепочке для извлечения знаний на основе диалога, как показано в предыдущих примерах.
Эти различные типы памяти в Langchain предоставляют различные способы управления и извлечения информации из разговоров, расширяя возможности моделей ИИ в понимании и реагировании на пользовательские запросы и контекст. Каждый тип памяти можно выбрать в зависимости от конкретных требований вашего приложения.
Теперь мы научимся использовать память с LLMChain. Память в LLMChain позволяет модели запоминать предыдущие взаимодействия и контекст, чтобы обеспечить более последовательные и контекстно-зависимые ответы.
Чтобы настроить память в LLMChain, вам необходимо создать класс памяти, например ConversationBufferMemory. Вот как это можно настроить:
from langchain.chains import LLMChain
from langchain.llms import OpenAI
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate template = """You are a chatbot having a conversation with a human. {chat_history}
Human: {human_input}
Chatbot:""" prompt = PromptTemplate( input_variables=["chat_history", "human_input"], template=template
)
memory = ConversationBufferMemory(memory_key="chat_history") llm = OpenAI()
llm_chain = LLMChain( llm=llm, prompt=prompt, verbose=True, memory=memory,
) llm_chain.predict(human_input="Hi there my friend")
В этом примере ConversationBufferMemory используется для хранения истории разговоров. memory_key
Параметр указывает ключ, используемый для хранения истории разговоров.
Если вы используете модель чата вместо модели завершения, вы можете структурировать свои подсказки по-другому, чтобы лучше использовать память. Вот пример того, как настроить LLMChain на основе модели чата с памятью:
from langchain.chat_models import ChatOpenAI
from langchain.schema import SystemMessage
from langchain.prompts import ( ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder,
) # Create a ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages( [ SystemMessage( content="You are a chatbot having a conversation with a human." ), # The persistent system prompt MessagesPlaceholder( variable_name="chat_history" ), # Where the memory will be stored. HumanMessagePromptTemplate.from_template( "{human_input}" ), # Where the human input will be injected ]
) memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) llm = ChatOpenAI() chat_llm_chain = LLMChain( llm=llm, prompt=prompt, verbose=True, memory=memory,
) chat_llm_chain.predict(human_input="Hi there my friend")
В этом примере ChatPromptTemplate используется для структурирования приглашения, а ConversationBufferMemory используется для хранения и получения истории разговоров. Этот подход особенно полезен для разговоров в стиле чата, где контекст и история играют решающую роль.
Память также можно добавить в цепочку с несколькими входами, например цепочку вопросов/ответов. Вот пример того, как настроить память в цепочке вопросов/ответов:
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.embeddings.cohere import CohereEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores.elastic_vector_search import ElasticVectorSearch
from langchain.vectorstores import Chroma
from langchain.docstore.document import Document
from langchain.chains.question_answering import load_qa_chain
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory # Split a long document into smaller chunks
with open("../../state_of_the_union.txt") as f: state_of_the_union = f.read()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_text(state_of_the_union) # Create an ElasticVectorSearch instance to index and search the document chunks
embeddings = OpenAIEmbeddings()
docsearch = Chroma.from_texts( texts, embeddings, metadatas=[{"source": i} for i in range(len(texts))]
) # Perform a question about the document
query = "What did the president say about Justice Breyer"
docs = docsearch.similarity_search(query) # Set up a prompt for the question-answering chain with memory
template = """You are a chatbot having a conversation with a human. Given the following extracted parts of a long document and a question, create a final answer. {context} {chat_history}
Human: {human_input}
Chatbot:""" prompt = PromptTemplate( input_variables=["chat_history", "human_input", "context"], template=template
)
memory = ConversationBufferMemory(memory_key="chat_history", input_key="human_input")
chain = load_qa_chain( OpenAI(temperature=0), chain_type="stuff", memory=memory, prompt=prompt
) # Ask the question and retrieve the answer
query = "What did the president say about Justice Breyer"
result = chain({"input_documents": docs, "human_input": query}, return_only_outputs=True) print(result)
print(chain.memory.buffer)
В этом примере ответ на вопрос осуществляется с использованием документа, разделенного на более мелкие части. ConversationBufferMemory используется для хранения и извлечения истории разговоров, что позволяет модели предоставлять контекстно-зависимые ответы.
Добавление памяти агенту позволяет ему запоминать и использовать предыдущие взаимодействия для ответа на вопросы и предоставления контекстно-зависимых ответов. Вот как можно настроить память в агенте:
from langchain.agents import ZeroShotAgent, Tool, AgentExecutor
from langchain.memory import ConversationBufferMemory
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.utilities import GoogleSearchAPIWrapper # Create a tool for searching
search = GoogleSearchAPIWrapper()
tools = [ Tool( name="Search", func=search.run, description="useful for when you need to answer questions about current events", )
] # Create a prompt with memory
prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!" {chat_history}
Question: {input}
{agent_scratchpad}""" prompt = ZeroShotAgent.create_prompt( tools, prefix=prefix, suffix=suffix, input_variables=["input", "chat_history", "agent_scratchpad"],
)
memory = ConversationBufferMemory(memory_key="chat_history") # Create an LLMChain with memory
llm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt)
agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True)
agent_chain = AgentExecutor.from_agent_and_tools( agent=agent, tools=tools, verbose=True, memory=memory
) # Ask a question and retrieve the answer
response = agent_chain.run(input="How many people live in Canada?")
print(response) # Ask a follow-up question
response = agent_chain.run(input="What is their national anthem called?")
print(response)
В этом примере агенту добавляется память, позволяющая ему запоминать предыдущую историю разговоров и предоставлять контекстно-зависимые ответы. Это позволяет агенту точно отвечать на дополнительные вопросы на основе информации, хранящейся в памяти.
Язык выражений LangChain
В мире обработки естественного языка и машинного обучения составление сложных цепочек операций может оказаться непростой задачей. К счастью, на помощь приходит язык выражений LangChain (LCEL), предоставляющий декларативный и эффективный способ создания и развертывания сложных конвейеров языковой обработки. LCEL предназначен для упрощения процесса составления цепочек, позволяя легко перейти от прототипирования к производству. В этом блоге мы рассмотрим, что такое LCEL и почему вы можете его использовать, а также приведем практические примеры кода, иллюстрирующие его возможности.
LCEL, или LangChain Expression Language, — мощный инструмент для составления цепочек языковой обработки. Он был специально создан для обеспечения плавного перехода от прототипирования к производству, не требуя значительных изменений кода. Независимо от того, строите ли вы простую цепочку «подсказка + LLM» или сложный конвейер с сотнями шагов, LCEL поможет вам.
Вот несколько причин использовать LCEL в ваших проектах обработки языков:
- Быстрая потоковая передача токенов: LCEL доставляет токены из языковой модели в выходной анализатор в режиме реального времени, повышая оперативность и эффективность.
- Универсальные API: LCEL поддерживает как синхронные, так и асинхронные API для прототипирования и производственного использования, эффективно обрабатывая многочисленные запросы.
- Автоматическое распараллеливание: LCEL оптимизирует параллельное выполнение, когда это возможно, уменьшая задержку как в синхронных, так и в асинхронных интерфейсах.
- Надежные конфигурации: настройте повторные попытки и резервные варианты для повышения надежности цепочки в масштабе, а поддержка потоковой передачи находится в разработке.
- Потоковая передача промежуточных результатов: доступ к промежуточным результатам во время обработки для пользовательских обновлений или целей отладки.
- Генерация схемы: LCEL генерирует схемы Pydantic и JSONSchema для проверки входных и выходных данных.
- Комплексная трассировка: LangSmith автоматически отслеживает все шаги в сложных цепочках для обеспечения возможности наблюдения и отладки.
- Простое развертывание: легко развертывайте цепочки, созданные LCEL, с помощью LangServe.
Теперь давайте углубимся в практические примеры кода, демонстрирующие возможности LCEL. Мы рассмотрим типичные задачи и сценарии, в которых LCEL сияет.
Подскажите + LLM
Самая фундаментальная композиция включает в себя объединение подсказки и языковой модели для создания цепочки, которая принимает вводимые пользователем данные, добавляет их в подсказку, передает их в модель и возвращает необработанные выходные данные модели. Вот пример:
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI prompt = ChatPromptTemplate.from_template("tell me a joke about {foo}")
model = ChatOpenAI()
chain = prompt | model result = chain.invoke({"foo": "bears"})
print(result)
В этом примере цепочка генерирует шутку про медведей.
Вы можете прикрепить стоп-последовательности к своей цепочке, чтобы контролировать обработку текста. Например:
chain = prompt | model.bind(stop=["n"])
result = chain.invoke({"foo": "bears"})
print(result)
Эта конфигурация останавливает генерацию текста при обнаружении символа новой строки.
LCEL поддерживает прикрепление информации о вызове функции к вашей цепочке. Вот пример:
functions = [ { "name": "joke", "description": "A joke", "parameters": { "type": "object", "properties": { "setup": {"type": "string", "description": "The setup for the joke"}, "punchline": { "type": "string", "description": "The punchline for the joke", }, }, "required": ["setup", "punchline"], }, }
]
chain = prompt | model.bind(function_call={"name": "joke"}, functions=functions)
result = chain.invoke({"foo": "bears"}, config={})
print(result)
В этом примере прикрепляется информация о вызове функции для создания шутки.
Подсказка + LLM + OutputParser
Вы можете добавить анализатор выходных данных, чтобы преобразовать необработанные выходные данные модели в более удобный формат. Вот как вы можете это сделать:
from langchain.schema.output_parser import StrOutputParser chain = prompt | model | StrOutputParser()
result = chain.invoke({"foo": "bears"})
print(result)
Вывод теперь имеет строковый формат, что более удобно для последующих задач.
Указывая возвращаемую функцию, вы можете проанализировать ее напрямую с помощью LCEL. Например:
from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser chain = ( prompt | model.bind(function_call={"name": "joke"}, functions=functions) | JsonOutputFunctionsParser()
)
result = chain.invoke({"foo": "bears"})
print(result)
В этом примере напрямую анализируется вывод функции «шутка».
Это всего лишь несколько примеров того, как LCEL упрощает сложные задачи языковой обработки. Независимо от того, создаете ли вы чат-ботов, генерируете контент или выполняете сложные преобразования текста, LCEL может оптимизировать ваш рабочий процесс и сделать ваш код более удобным в сопровождении.
RAG (генерация с расширенным поиском)
LCEL можно использовать для создания цепочек генерации с расширенным поиском, которые сочетают этапы поиска и генерации языка. Вот пример:
from operator import itemgetter from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough, RunnableLambda
from langchain.vectorstores import FAISS # Create a vector store and retriever
vectorstore = FAISS.from_texts( ["harrison worked at kensho"], embedding=OpenAIEmbeddings()
)
retriever = vectorstore.as_retriever() # Define templates for prompts
template = """Answer the question based only on the following context:
{context} Question: {question} """
prompt = ChatPromptTemplate.from_template(template) model = ChatOpenAI() # Create a retrieval-augmented generation chain
chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | model | StrOutputParser()
) result = chain.invoke("where did harrison work?")
print(result)
В этом примере цепочка извлекает соответствующую информацию из контекста и генерирует ответ на вопрос.
Диалоговая поисковая цепочка
Вы можете легко добавить историю разговоров в свои цепочки. Вот пример диалоговой поисковой цепочки:
from langchain.schema.runnable import RunnableMap
from langchain.schema import format_document from langchain.prompts.prompt import PromptTemplate # Define templates for prompts
_template = """Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question, in its original language. Chat History:
{chat_history}
Follow Up Input: {question}
Standalone question:"""
CONDENSE_QUESTION_PROMPT = PromptTemplate.from_template(_template) template = """Answer the question based only on the following context:
{context} Question: {question} """
ANSWER_PROMPT = ChatPromptTemplate.from_template(template) # Define input map and context
_inputs = RunnableMap( standalone_question=RunnablePassthrough.assign( chat_history=lambda x: _format_chat_history(x["chat_history"]) ) | CONDENSE_QUESTION_PROMPT | ChatOpenAI(temperature=0) | StrOutputParser(),
)
_context = { "context": itemgetter("standalone_question") | retriever | _combine_documents, "question": lambda x: x["standalone_question"],
}
conversational_qa_chain = _inputs | _context | ANSWER_PROMPT | ChatOpenAI() result = conversational_qa_chain.invoke( { "question": "where did harrison work?", "chat_history": [], }
)
print(result)
В этом примере цепочка обрабатывает дополнительный вопрос в контексте разговора.
С памятью и возвратом исходных документов
LCEL также поддерживает память и возврат исходных документов. Вот как вы можете использовать память в цепочке:
from operator import itemgetter
from langchain.memory import ConversationBufferMemory # Create a memory instance
memory = ConversationBufferMemory( return_messages=True, output_key="answer", input_key="question"
) # Define steps for the chain
loaded_memory = RunnablePassthrough.assign( chat_history=RunnableLambda(memory.load_memory_variables) | itemgetter("history"),
) standalone_question = { "standalone_question": { "question": lambda x: x["question"], "chat_history": lambda x: _format_chat_history(x["chat_history"]), } | CONDENSE_QUESTION_PROMPT | ChatOpenAI(temperature=0) | StrOutputParser(),
} retrieved_documents = { "docs": itemgetter("standalone_question") | retriever, "question": lambda x: x["standalone_question"],
} final_inputs = { "context": lambda x: _combine_documents(x["docs"]), "question": itemgetter("question"),
} answer = { "answer": final_inputs | ANSWER_PROMPT | ChatOpenAI(), "docs": itemgetter("docs"),
} # Create the final chain by combining the steps
final_chain = loaded_memory | standalone_question | retrieved_documents | answer inputs = {"question": "where did harrison work?"}
result = final_chain.invoke(inputs)
print(result)
В этом примере память используется для хранения и извлечения истории разговоров и исходных документов.
Несколько цепочек
Вы можете объединить несколько цепочек с помощью Runnables. Вот пример:
from operator import itemgetter from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema import StrOutputParser prompt1 = ChatPromptTemplate.from_template("what is the city {person} is from?")
prompt2 = ChatPromptTemplate.from_template( "what country is the city {city} in? respond in {language}"
) model = ChatOpenAI() chain1 = prompt1 | model | StrOutputParser() chain2 = ( {"city": chain1, "language": itemgetter("language")} | prompt2 | model | StrOutputParser()
) result = chain2.invoke({"person": "obama", "language": "spanish"})
print(result)
В этом примере две цепочки объединяются для генерации информации о городе и его стране на указанном языке.
Ветвление и слияние
LCEL позволяет разделять и объединять цепочки с помощью RunnableMaps. Вот пример ветвления и слияния:
from operator import itemgetter from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema import StrOutputParser planner = ( ChatPromptTemplate.from_template("Generate an argument about: {input}") | ChatOpenAI() | StrOutputParser() | {"base_response": RunnablePassthrough()}
) arguments_for = ( ChatPromptTemplate.from_template( "List the pros or positive aspects of {base_response}" ) | ChatOpenAI() | StrOutputParser()
)
arguments_against = ( ChatPromptTemplate.from_template( "List the cons or negative aspects of {base_response}" ) | ChatOpenAI() | StrOutputParser()
) final_responder = ( ChatPromptTemplate.from_messages( [ ("ai", "{original_response}"), ("human", "Pros:n{results_1}nnCons:n{results_2}"), ("system", "Generate a final response given the critique"), ] ) | ChatOpenAI() | StrOutputParser()
) chain = ( planner | { "results_1": arguments_for, "results_2": arguments_against, "original_response": itemgetter("base_response"), } | final_responder
) result = chain.invoke({"input": "scrum"})
print(result)
В этом примере цепочка ветвления и слияния используется для генерации аргумента и оценки его плюсов и минусов перед генерированием окончательного ответа.
Написание кода Python с помощью LCEL
Одним из мощных приложений языка выражений LangChain (LCEL) является написание кода Python для решения проблем пользователей. Ниже приведен пример использования LCEL для написания кода Python:
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain_experimental.utilities import PythonREPL template = """Write some python code to solve the user's problem. Return only python code in Markdown format, e.g.: ```python
....
```"""
prompt = ChatPromptTemplate.from_messages([("system", template), ("human", "{input}")]) model = ChatOpenAI() def _sanitize_output(text: str): _, after = text.split("```python") return after.split("```")[0] chain = prompt | model | StrOutputParser() | _sanitize_output | PythonREPL().run result = chain.invoke({"input": "what's 2 plus 2"})
print(result)
В этом примере пользователь вводит данные, а LCEL генерирует код Python для решения проблемы. Затем код выполняется с использованием Python REPL, и полученный код Python возвращается в формате Markdown.
Обратите внимание, что использование Python REPL может привести к выполнению произвольного кода, поэтому используйте его с осторожностью.
Добавление памяти в цепочку
Память необходима во многих диалоговых приложениях искусственного интеллекта. Вот как добавить память в произвольную цепочку:
from operator import itemgetter
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.schema.runnable import RunnablePassthrough, RunnableLambda
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder model = ChatOpenAI()
prompt = ChatPromptTemplate.from_messages( [ ("system", "You are a helpful chatbot"), MessagesPlaceholder(variable_name="history"), ("human", "{input}"), ]
) memory = ConversationBufferMemory(return_messages=True) # Initialize memory
memory.load_memory_variables({}) chain = ( RunnablePassthrough.assign( history=RunnableLambda(memory.load_memory_variables) | itemgetter("history") ) | prompt | model
) inputs = {"input": "hi, I'm Bob"}
response = chain.invoke(inputs)
response # Save the conversation in memory
memory.save_context(inputs, {"output": response.content}) # Load memory to see the conversation history
memory.load_memory_variables({})
В этом примере память используется для хранения и извлечения истории разговоров, что позволяет чат-боту сохранять контекст и реагировать соответствующим образом.
Использование внешних инструментов с Runnables
LCEL позволяет легко интегрировать внешние инструменты с Runnables. Вот пример использования инструмента поиска DuckDuckGo:
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.tools import DuckDuckGoSearchRun search = DuckDuckGoSearchRun() template = """Turn the following user input into a search query for a search engine: {input}"""
prompt = ChatPromptTemplate.from_template(template) model = ChatOpenAI() chain = prompt | model | StrOutputParser() | search search_result = chain.invoke({"input": "I'd like to figure out what games are tonight"})
print(search_result)
В этом примере LCEL интегрирует в цепочку инструмент поиска DuckDuckGo, что позволяет ему генерировать поисковый запрос на основе пользовательского ввода и получать результаты поиска.
Гибкость LCEL позволяет легко включать различные внешние инструменты и службы в ваши конвейеры языковой обработки, расширяя их возможности и функциональность.
Добавление модерации к заявке на LLM
Чтобы гарантировать, что ваша заявка на получение LLM соответствует политике в отношении контента и включает меры безопасности модерации, вы можете интегрировать проверки модерации в свою цепочку. Вот как добавить модерацию с помощью LangChain:
from langchain.chains import OpenAIModerationChain
from langchain.llms import OpenAI
from langchain.prompts import ChatPromptTemplate moderate = OpenAIModerationChain() model = OpenAI()
prompt = ChatPromptTemplate.from_messages([("system", "repeat after me: {input}")]) chain = prompt | model # Original response without moderation
response_without_moderation = chain.invoke({"input": "you are stupid"})
print(response_without_moderation) moderated_chain = chain | moderate # Response after moderation
response_after_moderation = moderated_chain.invoke({"input": "you are stupid"})
print(response_after_moderation)
В этом примере OpenAIModerationChain
используется для добавления модерации к ответу, генерируемому LLM. Цепочка модерации проверяет ответ на наличие контента, который нарушает политику содержания OpenAI. Если будут обнаружены какие-либо нарушения, он пометит ответ соответствующим образом.
Маршрутизация по семантическому сходству
LCEL позволяет реализовать собственную логику маршрутизации на основе семантического сходства пользовательского ввода. Вот пример того, как динамически определять логику цепочки на основе пользовательского ввода:
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import PromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnableLambda, RunnablePassthrough
from langchain.utils.math import cosine_similarity physics_template = """You are a very smart physics professor. You are great at answering questions about physics in a concise and easy to understand manner. When you don't know the answer to a question you admit that you don't know. Here is a question:
{query}""" math_template = """You are a very good mathematician. You are great at answering math questions. You are so good because you are able to break down hard problems into their component parts, answer the component parts, and then put them together to answer the broader question. Here is a question:
{query}""" embeddings = OpenAIEmbeddings()
prompt_templates = [physics_template, math_template]
prompt_embeddings = embeddings.embed_documents(prompt_templates) def prompt_router(input): query_embedding = embeddings.embed_query(input["query"]) similarity = cosine_similarity([query_embedding], prompt_embeddings)[0] most_similar = prompt_templates[similarity.argmax()] print("Using MATH" if most_similar == math_template else "Using PHYSICS") return PromptTemplate.from_template(most_similar) chain = ( {"query": RunnablePassthrough()} | RunnableLambda(prompt_router) | ChatOpenAI() | StrOutputParser()
) print(chain.invoke({"query": "What's a black hole"}))
print(chain.invoke({"query": "What's a path integral"}))
В этом примере prompt_router
Функция вычисляет косинусное сходство между вводом пользователя и предопределенными шаблонами подсказок для вопросов по физике и математике. На основе показателя сходства цепочка динамически выбирает наиболее подходящий шаблон подсказки, гарантируя, что чат-бот адекватно ответит на вопрос пользователя.
Использование агентов и Runnables
LangChain позволяет создавать агентов, комбинируя Runnables, подсказки, модели и инструменты. Вот пример создания агента и его использования:
from langchain.agents import XMLAgent, tool, AgentExecutor
from langchain.chat_models import ChatAnthropic model = ChatAnthropic(model="claude-2") @tool
def search(query: str) -> str: """Search things about current events.""" return "32 degrees" tool_list = [search] # Get prompt to use
prompt = XMLAgent.get_default_prompt() # Logic for going from intermediate steps to a string to pass into the model
def convert_intermediate_steps(intermediate_steps): log = "" for action, observation in intermediate_steps: log += ( f"<tool>{action.tool}</tool><tool_input>{action.tool_input}" f"</tool_input><observation>{observation}</observation>" ) return log # Logic for converting tools to a string to go in the prompt
def convert_tools(tools): return "n".join([f"{tool.name}: {tool.description}" for tool in tools]) agent = ( { "question": lambda x: x["question"], "intermediate_steps": lambda x: convert_intermediate_steps( x["intermediate_steps"] ), } | prompt.partial(tools=convert_tools(tool_list)) | model.bind(stop=["</tool_input>", "</final_answer>"]) | XMLAgent.get_default_output_parser()
) agent_executor = AgentExecutor(agent=agent, tools=tool_list, verbose=True) result = agent_executor.invoke({"question": "What's the weather in New York?"})
print(result)
В этом примере агент создается путем объединения модели, инструментов, подсказки и пользовательской логики для промежуточных шагов и преобразования инструментов. Затем запускается агент, предоставляющий ответ на запрос пользователя.
Запрос к базе данных SQL
Вы можете использовать LangChain для запроса базы данных SQL и создания SQL-запросов на основе вопросов пользователей. Вот пример:
from langchain.prompts import ChatPromptTemplate template = """Based on the table schema below, write a SQL query that would answer the user's question:
{schema} Question: {question}
SQL Query:"""
prompt = ChatPromptTemplate.from_template(template) from langchain.utilities import SQLDatabase # Initialize the database (you'll need the Chinook sample DB for this example)
db = SQLDatabase.from_uri("sqlite:///./Chinook.db") def get_schema(_): return db.get_table_info() def run_query(query): return db.run(query) from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough model = ChatOpenAI() sql_response = ( RunnablePassthrough.assign(schema=get_schema) | prompt | model.bind(stop=["nSQLResult:"]) | StrOutputParser()
) result = sql_response.invoke({"question": "How many employees are there?"})
print(result) template = """Based on the table schema below, question, SQL query, and SQL response, write a natural language response:
{schema} Question: {question}
SQL Query: {query}
SQL Response: {response}"""
prompt_response = ChatPromptTemplate.from_template(template) full_chain = ( RunnablePassthrough.assign(query=sql_response) | RunnablePassthrough.assign( schema=get_schema, response=lambda x: db.run(x["query"]), ) | prompt_response | model
) response = full_chain.invoke({"question": "How many employees are there?"})
print(response)
В этом примере LangChain используется для генерации SQL-запросов на основе вопросов пользователя и получения ответов из базы данных SQL. Подсказки и ответы отформатированы таким образом, чтобы обеспечить взаимодействие с базой данных на естественном языке.
Автоматизируйте ручные задачи и рабочие процессы с помощью нашего конструктора рабочих процессов на основе искусственного интеллекта, разработанного Nanonets для вас и ваших команд.
ЛангСерв и ЛангСмит
LangServe помогает разработчикам развертывать исполняемые файлы и цепочки LangChain в качестве REST API. Эта библиотека интегрирована с FastAPI и использует pydantic для проверки данных. Кроме того, он предоставляет клиент, который можно использовать для вызова исполняемых модулей, развернутых на сервере, а клиент JavaScript доступен в LangChainJS.
Особенности
- Схемы ввода и вывода автоматически выводятся из вашего объекта LangChain и применяются при каждом вызове API с подробными сообщениями об ошибках.
- Доступна страница документации API с JSONSchema и Swagger.
- Эффективные конечные точки /invoke, /batch и /stream с поддержкой множества одновременных запросов на одном сервере.
- Конечная точка /stream_log для потоковой передачи всех (или некоторых) промежуточных шагов из вашей цепочки/агента.
- Страница игровой площадки в /playground с потоковым выводом и промежуточными шагами.
- Встроенная (дополнительная) трассировка до LangSmith; просто добавьте свой ключ API (см. Инструкции).
- Все они созданы с использованием проверенных библиотек Python с открытым исходным кодом, таких как FastAPI, Pydantic, uvloop и asyncio.
Ограничения
- Обратные вызовы клиента пока не поддерживаются для событий, происходящих на сервере.
- Документы OpenAPI не будут создаваться при использовании Pydantic V2. FastAPI не поддерживает смешивание пространств имен pydantic v1 и v2. Более подробную информацию смотрите в разделе ниже.
Используйте интерфейс командной строки LangChain для быстрой загрузки проекта LangServe. Чтобы использовать CLI langchain, убедитесь, что у вас установлена последняя версия langchain-cli. Вы можете установить его с помощью pip install -U langchain-cli.
langchain app new ../path/to/directory
Быстро запустите свой экземпляр LangServe с помощью шаблонов LangChain. Дополнительные примеры см. в указателе шаблонов или в каталоге примеров.
Вот сервер, который развертывает модель чата OpenAI, модель чата Anthropic и цепочку, которая использует модель Anthropic, чтобы рассказать шутку по определенной теме.
#!/usr/bin/env python
from fastapi import FastAPI
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatAnthropic, ChatOpenAI
from langserve import add_routes app = FastAPI( title="LangChain Server", version="1.0", description="A simple api server using Langchain's Runnable interfaces",
) add_routes( app, ChatOpenAI(), path="/openai",
) add_routes( app, ChatAnthropic(), path="/anthropic",
) model = ChatAnthropic()
prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
add_routes( app, prompt | model, path="/chain",
) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000)
После развертывания указанного выше сервера вы можете просмотреть сгенерированные документы OpenAPI, используя:
curl localhost:8000/docs
Обязательно добавьте суффикс /docs.
from langchain.schema import SystemMessage, HumanMessage
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnableMap
from langserve import RemoteRunnable openai = RemoteRunnable("http://localhost:8000/openai/")
anthropic = RemoteRunnable("http://localhost:8000/anthropic/")
joke_chain = RemoteRunnable("http://localhost:8000/chain/") joke_chain.invoke({"topic": "parrots"}) # or async
await joke_chain.ainvoke({"topic": "parrots"}) prompt = [ SystemMessage(content='Act like either a cat or a parrot.'), HumanMessage(content='Hello!')
] # Supports astream
async for msg in anthropic.astream(prompt): print(msg, end="", flush=True) prompt = ChatPromptTemplate.from_messages( [("system", "Tell me a long story about {topic}")]
) # Can define custom chains
chain = prompt | RunnableMap({ "openai": openai, "anthropic": anthropic,
}) chain.batch([{ "topic": "parrots" }, { "topic": "cats" }])
В TypeScript (требуется LangChain.js версии 0.0.166 или новее):
import { RemoteRunnable } from "langchain/runnables/remote"; const chain = new RemoteRunnable({ url: `http://localhost:8000/chain/invoke/`,
});
const result = await chain.invoke({ topic: "cats",
});
Python с использованием запросов:
import requests
response = requests.post( "http://localhost:8000/chain/invoke/", json={'input': {'topic': 'cats'}}
)
response.json()
Вы также можете использовать завиток:
curl --location --request POST 'http://localhost:8000/chain/invoke/' --header 'Content-Type: application/json' --data-raw '{ "input": { "topic": "cats" } }'
Следующий код:
...
add_routes( app, runnable, path="/my_runnable",
)
добавляет эти конечные точки на сервер:
- POST /my_runnable/invoke – вызвать исполняемый файл на одном входе
- POST /my_runnable/batch – вызвать исполняемый файл для пакета входных данных.
- POST /my_runnable/stream – вызвать один вход и передать поток вывода
- POST /my_runnable/stream_log – вызывать один вход и передавать выходные данные, включая выходные данные промежуточных шагов по мере их создания.
- GET /my_runnable/input_schema — схема json для ввода в исполняемый файл
- GET /my_runnable/output_schema — схема json для вывода исполняемого файла
- GET /my_runnable/config_schema — схема json для конфигурации исполняемого файла.
Вы можете найти страницу игровой площадки для вашего запуска в /my_runnable/playground. Это предоставляет простой пользовательский интерфейс для настройки и вызова вашего исполняемого файла с потоковым выводом и промежуточными шагами.
И для клиента, и для сервера:
pip install "langserve[all]"
или pip install «langserve[client]» для клиентского кода и pip install «langserve[server]» для серверного кода.
Если вам нужно добавить аутентификацию на свой сервер, обратитесь к документации по безопасности FastAPI и документации по промежуточному программному обеспечению.
Вы можете выполнить развертывание в GCP Cloud Run с помощью следующей команды:
gcloud run deploy [your-service-name] --source . --port 8001 --allow-unauthenticated --region us-central1 --set-env-vars=OPENAI_API_KEY=your_key
LangServe обеспечивает поддержку Pydantic 2 с некоторыми ограничениями. Документы OpenAPI не будут создаваться для вызова/пакета/потока/stream_log при использовании Pydantic V2. Fast API не поддерживает смешивание пространств имен pydantic v1 и v2. LangChain использует пространство имен v1 в Pydantic v2. Пожалуйста, прочтите следующие рекомендации, чтобы обеспечить совместимость с LangChain. За исключением этих ограничений, мы ожидаем, что конечные точки API, игровая площадка и любые другие функции будут работать должным образом.
Приложения LLM часто работают с файлами. Существуют различные архитектуры, которые можно реализовать для реализации обработки файлов; на высоком уровне:
- Файл можно загрузить на сервер через выделенную конечную точку и обработать с использованием отдельной конечной точки.
- Файл можно загрузить либо по значению (байты файла), либо по ссылке (например, URL-адрес s3 на содержимое файла).
- Конечная точка обработки может быть блокирующей или неблокирующей.
- Если требуется значительная обработка, ее можно выгрузить в выделенный пул процессов.
Вы должны определить, какая архитектура подходит для вашего приложения. В настоящее время для загрузки файлов по значению в исполняемый файл используйте кодировку base64 (multipart/form-data пока не поддерживается).
Вот пример это показывает, как использовать кодировку base64 для отправки файла на удаленный исполняемый файл. Помните, что вы всегда можете загружать файлы по ссылке (например, URL-адрес s3) или загружать их как multipart/form-data в выделенную конечную точку.
Типы ввода и вывода определены для всех исполняемых файлов. Вы можете получить к ним доступ через свойства input_schema и output_schema. LangServe использует эти типы для проверки и документирования. Если вы хотите переопределить выводимые по умолчанию типы, вы можете использовать метод with_types.
Вот игрушечный пример, иллюстрирующий эту идею:
from typing import Any
from fastapi import FastAPI
from langchain.schema.runnable import RunnableLambda app = FastAPI() def func(x: Any) -> int: """Mistyped function that should accept an int but accepts anything.""" return x + 1 runnable = RunnableLambda(func).with_types( input_schema=int,
) add_routes(app, runnable)
Наследуйте от CustomUserType, если вы хотите, чтобы данные десериализовались в модель pydantic, а не в эквивалентное представление dict. На данный момент этот тип работает только на стороне сервера и используется для указания желаемого поведения декодирования. При наследовании от этого типа сервер сохранит декодированный тип как pydantic модель вместо того, чтобы преобразовывать его в dict.
from fastapi import FastAPI
from langchain.schema.runnable import RunnableLambda
from langserve import add_routes
from langserve.schema import CustomUserType app = FastAPI() class Foo(CustomUserType): bar: int def func(foo: Foo) -> int: """Sample function that expects a Foo type which is a pydantic model""" assert isinstance(foo, Foo) return foo.bar add_routes(app, RunnableLambda(func), path="/foo")
Игровая площадка позволяет вам определять собственные виджеты для вашего запускаемого приложения из бэкэнда. Виджет указывается на уровне поля и поставляется как часть схемы JSON входного типа. Виджет должен содержать ключ с именем type, значение которого должно быть одним из известного списка виджетов. Другие ключи виджета будут связаны со значениями, описывающими пути в объекте JSON.
Общая схема:
type JsonPath = number | string | (number | string)[];
type NameSpacedPath = { title: string; path: JsonPath }; // Using title to mimic json schema, but can use namespace
type OneOfPath = { oneOf: JsonPath[] }; type Widget = { type: string // Some well-known type (e.g., base64file, chat, etc.) [key: string]: JsonPath | NameSpacedPath | OneOfPath;
};
Позволяет создавать входные данные для загрузки файлов на игровой площадке пользовательского интерфейса для файлов, которые загружаются в виде строк в кодировке Base64. Вот полный пример.
try: from pydantic.v1 import Field
except ImportError: from pydantic import Field from langserve import CustomUserType # ATTENTION: Inherit from CustomUserType instead of BaseModel otherwise
# the server will decode it into a dict instead of a pydantic model.
class FileProcessingRequest(CustomUserType): """Request including a base64 encoded file.""" # The extra field is used to specify a widget for the playground UI. file: str = Field(..., extra={"widget": {"type": "base64file"}}) num_chars: int = 100
Автоматизируйте ручные задачи и рабочие процессы с помощью нашего конструктора рабочих процессов на основе искусственного интеллекта, разработанного Nanonets для вас и ваших команд.
Введение в ЛэнгСмит
LangChain упрощает создание прототипов приложений и агентов LLM. Однако доставка приложений LLM в производство может оказаться обманчиво сложной. Вам, вероятно, придется тщательно настраивать и перерабатывать ваши подсказки, цепочки и другие компоненты, чтобы создать высококачественный продукт.
Чтобы помочь в этом процессе, была представлена LangSmith — унифицированная платформа для отладки, тестирования и мониторинга ваших приложений LLM.
Когда это может пригодиться? Это может оказаться полезным, если вы хотите быстро отладить новую цепочку, агент или набор инструментов, визуализировать, как компоненты (цепочки, llms, ретриверы и т. д.) связаны и используются, оценить различные подсказки и LLM для одного компонента. запустите заданную цепочку несколько раз над набором данных, чтобы убедиться, что она постоянно соответствует планке качества, или фиксируйте следы использования и используйте LLM или конвейеры аналитики для получения ценной информации.
Необходимые условия:
- Создайте учетную запись LangSmith и создайте ключ API (см. нижний левый угол).
- Ознакомьтесь с платформой, просмотрев документацию.
Теперь давайте начнем!
Сначала настройте переменные среды, чтобы указать LangChain регистрировать трассировки. Это делается путем установки переменной среды LANGCHAIN_TRACING_V2 в значение true. Вы можете указать LangChain, в какой проект выполнять вход, установив переменную среды LANGCHAIN_PROJECT (если она не установлена, запуски будут записываться в проект по умолчанию). Это автоматически создаст для вас проект, если он не существует. Вы также должны установить переменные среды LANGCHAIN_ENDPOINT и LANGCHAIN_API_KEY.
ПРИМЕЧАНИЕ. Вы также можете использовать диспетчер контекста в Python для регистрации трассировок, используя:
from langchain.callbacks.manager import tracing_v2_enabled with tracing_v2_enabled(project_name="My Project"): agent.run("How many people live in canada as of 2023?")
Однако в этом примере мы будем использовать переменные среды.
%pip install openai tiktoken pandas duckduckgo-search --quiet import os
from uuid import uuid4 unique_id = uuid4().hex[0:8]
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = f"Tracing Walkthrough - {unique_id}"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGCHAIN_API_KEY"] = "<YOUR-API-KEY>" # Update to your API key # Used by the agent in this tutorial
os.environ["OPENAI_API_KEY"] = "<YOUR-OPENAI-API-KEY>"
Создайте клиент LangSmith для взаимодействия с API:
from langsmith import Client client = Client()
Создайте компонент LangChain и записывайте его на платформу. В этом примере мы создадим агент в стиле ReAct с доступом к общему инструменту поиска (DuckDuckGo). Подсказку агента можно просмотреть в хабе здесь:
from langchain import hub
from langchain.agents import AgentExecutor
from langchain.agents.format_scratchpad import format_to_openai_function_messages
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
from langchain.chat_models import ChatOpenAI
from langchain.tools import DuckDuckGoSearchResults
from langchain.tools.render import format_tool_to_openai_function # Fetches the latest version of this prompt
prompt = hub.pull("wfh/langsmith-agent-prompt:latest") llm = ChatOpenAI( model="gpt-3.5-turbo-16k", temperature=0,
) tools = [ DuckDuckGoSearchResults( name="duck_duck_go" ), # General internet search using DuckDuckGo
] llm_with_tools = llm.bind(functions=[format_tool_to_openai_function(t) for t in tools]) runnable_agent = ( { "input": lambda x: x["input"], "agent_scratchpad": lambda x: format_to_openai_function_messages( x["intermediate_steps"] ), } | prompt | llm_with_tools | OpenAIFunctionsAgentOutputParser()
) agent_executor = AgentExecutor( agent=runnable_agent, tools=tools, handle_parsing_errors=True
)
Мы запускаем агент одновременно на нескольких входах, чтобы уменьшить задержку. Запуски регистрируются в LangSmith в фоновом режиме, поэтому задержка выполнения не изменяется:
inputs = [ "What is LangChain?", "What's LangSmith?", "When was Llama-v2 released?", "What is the langsmith cookbook?", "When did langchain first announce the hub?",
] results = agent_executor.batch([{"input": x} for x in inputs], return_exceptions=True) results[:2]
Если вы успешно настроили свою среду, трассировки вашего агента должны появиться в разделе «Проекты» приложения. Поздравляю!
Однако похоже, что агент неэффективно использует эти инструменты. Давайте оценим это, чтобы у нас была базовая линия.
Помимо ведения журналов, LangSmith также позволяет вам тестировать и оценивать ваши приложения LLM.
В этом разделе вы будете использовать LangSmith для создания эталонного набора данных и запуска оценщиков с помощью искусственного интеллекта на агенте. Вы сделаете это в несколько шагов:
- Создайте набор данных LangSmith:
Ниже мы используем клиент LangSmith для создания набора данных из введенных выше вопросов и меток списка. Вы будете использовать их позже для измерения производительности нового агента. Набор данных — это набор примеров, которые представляют собой не что иное, как пары ввода-вывода, которые вы можете использовать в качестве тестовых примеров для своего приложения:
outputs = [ "LangChain is an open-source framework for building applications using large language models. It is also the name of the company building LangSmith.", "LangSmith is a unified platform for debugging, testing, and monitoring language model applications and agents powered by LangChain", "July 18, 2023", "The langsmith cookbook is a github repository containing detailed examples of how to use LangSmith to debug, evaluate, and monitor large language model-powered applications.", "September 5, 2023",
] dataset_name = f"agent-qa-{unique_id}" dataset = client.create_dataset( dataset_name, description="An example dataset of questions over the LangSmith documentation.",
) for query, answer in zip(inputs, outputs): client.create_example( inputs={"input": query}, outputs={"output": answer}, dataset_id=dataset.id )
- Инициализируйте новый агент для тестирования:
LangSmith позволяет оценить любую LLM, цепочку, агента или даже пользовательскую функцию. Диалоговые агенты имеют состояние (у них есть память); чтобы гарантировать, что это состояние не передается между запусками набора данных, мы передадим Chain_factory (
она же конструктор) функция для инициализации для каждого вызова:
# Since chains can be stateful (e.g. they can have memory), we provide
# a way to initialize a new chain for each row in the dataset. This is done
# by passing in a factory function that returns a new chain for each row.
def agent_factory(prompt): llm_with_tools = llm.bind( functions=[format_tool_to_openai_function(t) for t in tools] ) runnable_agent = ( { "input": lambda x: x["input"], "agent_scratchpad": lambda x: format_to_openai_function_messages( x["intermediate_steps"] ), } | prompt | llm_with_tools | OpenAIFunctionsAgentOutputParser() ) return AgentExecutor(agent=runnable_agent, tools=tools, handle_parsing_errors=True)
- Настроить оценку:
Сравнение результатов цепочек в пользовательском интерфейсе вручную эффективно, но может занять много времени. Для оценки производительности вашего компонента может быть полезно использовать автоматические метрики и обратную связь с помощью искусственного интеллекта:
from langchain.evaluation import EvaluatorType
from langchain.smith import RunEvalConfig evaluation_config = RunEvalConfig( evaluators=[ EvaluatorType.QA, EvaluatorType.EMBEDDING_DISTANCE, RunEvalConfig.LabeledCriteria("helpfulness"), RunEvalConfig.LabeledScoreString( { "accuracy": """
Score 1: The answer is completely unrelated to the reference.
Score 3: The answer has minor relevance but does not align with the reference.
Score 5: The answer has moderate relevance but contains inaccuracies.
Score 7: The answer aligns with the reference but has minor errors or omissions.
Score 10: The answer is completely accurate and aligns perfectly with the reference.""" }, normalize_by=10, ), ], custom_evaluators=[],
)
- Запустите агент и оценщики:
Используйте функцию run_on_dataset (или асинхронную функцию arun_on_dataset) для оценки вашей модели. Это будет:
- Получите строки примера из указанного набора данных.
- Запустите свой агент (или любую пользовательскую функцию) в каждом примере.
- Примените оценщиков к полученным трассировкам выполнения и соответствующим справочным примерам для создания автоматической обратной связи.
Результаты будут видны в приложении LangSmith:
chain_results = run_on_dataset( dataset_name=dataset_name, llm_or_chain_factory=functools.partial(agent_factory, prompt=prompt), evaluation=evaluation_config, verbose=True, client=client, project_name=f"runnable-agent-test-5d466cbc-{unique_id}", tags=[ "testing-notebook", "prompt:5d466cbc", ],
)
Теперь, когда у нас есть результаты тестового запуска, мы можем внести изменения в наш агент и протестировать их. Давайте попробуем это еще раз с другим приглашением и посмотрим результаты:
candidate_prompt = hub.pull("wfh/langsmith-agent-prompt:39f3bbd0") chain_results = run_on_dataset( dataset_name=dataset_name, llm_or_chain_factory=functools.partial(agent_factory, prompt=candidate_prompt), evaluation=evaluation_config, verbose=True, client=client, project_name=f"runnable-agent-test-39f3bbd0-{unique_id}", tags=[ "testing-notebook", "prompt:39f3bbd0", ],
)
LangSmith позволяет экспортировать данные в распространенные форматы, такие как CSV или JSONL, непосредственно в веб-приложении. Вы также можете использовать клиент для получения результатов для дальнейшего анализа, для хранения в собственной базе данных или для обмена с другими. Давайте получим трассировки выполнения из оценочного прогона:
runs = client.list_runs(project_name=chain_results["project_name"], execution_order=1) # After some time, these will be populated.
client.read_project(project_name=chain_results["project_name"]).feedback_stats
Это было краткое руководство для начала работы, но существует множество других способов использовать LangSmith для ускорения процесса разработки и достижения лучших результатов.
Для получения дополнительной информации о том, как максимально эффективно использовать LangSmith, ознакомьтесь с документацией LangSmith.
Повышайте уровень с помощью Nanonets
Хотя LangChain является ценным инструментом для интеграции языковых моделей (LLM) с вашими приложениями, он может столкнуться с ограничениями, когда дело доходит до корпоративных вариантов использования. Давайте рассмотрим, как Nanonets выходит за рамки LangChain для решения этих проблем:
1. Комплексное подключение данных:
LangChain предлагает коннекторы, но может не охватывать все рабочие приложения и форматы данных, на которые полагаются предприятия. Nanonets предоставляет соединители данных для более чем 100 широко используемых рабочих приложений, включая Slack, Notion, Google Suite, Salesforce, Zendesk и многих других. Он также поддерживает все неструктурированные типы данных, такие как PDF-файлы, TXT, изображения, аудиофайлы и видеофайлы, а также структурированные типы данных, такие как CSV, электронные таблицы, базы данных MongoDB и SQL.
2. Автоматизация задач для приложений Workspace:
Хотя генерация текста/ответов работает отлично, возможности LangChain ограничены, когда дело доходит до использования естественного языка для выполнения задач в различных приложениях. Nanonets предлагает агенты триггеров/действий для самых популярных приложений рабочей области, что позволяет вам настраивать рабочие процессы, которые прослушивают события и выполняют действия. Например, вы можете автоматизировать ответы по электронной почте, записи CRM, запросы SQL и многое другое с помощью команд на естественном языке.
3. Синхронизация данных в реальном времени:
LangChain извлекает статические данные с помощью коннекторов данных, которые могут не успевать за изменениями данных в исходной базе данных. Напротив, Nanonets обеспечивает синхронизацию в реальном времени с источниками данных, гарантируя, что вы всегда работаете с самой последней информацией.
3. Упрощенная конфигурация:
Настройка элементов конвейера LangChain, таких как ретриверы и синтезаторы, может оказаться сложным и трудоемким процессом. Nanonets упрощает эту задачу, обеспечивая оптимизированный прием и индексирование данных для каждого типа данных, причем все это обрабатывается в фоновом режиме с помощью AI Assistant. Это снижает нагрузку на тонкую настройку и упрощает настройку и использование.
4. Единое решение:
В отличие от LangChain, для каждой задачи которого могут потребоваться уникальные реализации, Nanonets служит универсальным решением для подключения ваших данных к LLM. Если вам нужно создать приложения LLM или рабочие процессы искусственного интеллекта, Nanonets предлагает единую платформу для ваших разнообразных потребностей.
Рабочие процессы Nanonets AI
Nanonets Workflows — это безопасный многоцелевой AI-помощник, который упрощает интеграцию ваших знаний и данных с LLM и облегчает создание приложений и рабочих процессов без кода. Он предлагает простой в использовании пользовательский интерфейс, что делает его доступным как для частных лиц, так и для организаций.
Чтобы начать работу, вы можете запланировать звонок с одним из наших экспертов по искусственному интеллекту, который предоставит индивидуальную демонстрацию и пробную версию рабочих процессов Nanonets, адаптированных к вашему конкретному сценарию использования.
После настройки вы можете использовать естественный язык для проектирования и выполнения сложных приложений и рабочих процессов на основе LLM, легко интегрируясь с вашими приложениями и данными.
Дайте вашим командам возможность создавать приложения и интегрировать свои данные с приложениями и рабочими процессами на базе искусственного интеллекта, позволяя вашим командам сосредоточиться на том, что действительно важно.
Автоматизируйте ручные задачи и рабочие процессы с помощью нашего конструктора рабочих процессов на основе искусственного интеллекта, разработанного Nanonets для вас и ваших команд.
- SEO-контент и PR-распределение. Получите усиление сегодня.
- PlatoData.Network Вертикальный генеративный ИИ. Расширьте возможности себя. Доступ здесь.
- ПлатонАйСтрим. Интеллект Web3. Расширение знаний. Доступ здесь.
- ПлатонЭСГ. Углерод, чистые технологии, Энергия, Окружающая среда, Солнечная, Управление отходами. Доступ здесь.
- ПлатонЗдоровье. Биотехнологии и клинические исследования. Доступ здесь.
- Источник: https://nanonets.com/blog/langchain/
- :имеет
- :является
- :нет
- :куда
- $UP
- 1
- 10
- 100
- 114
- 13
- 15%
- 2000
- 2012
- 2023
- 25
- 30
- 32
- 36
- 40
- 400
- 408
- 50
- 500
- 7
- 8
- a
- азбука
- способность
- в состоянии
- О нас
- об этом
- выше
- Принять
- Принимает
- доступ
- доступной
- доступа
- выполнять
- соответственно
- Учетная запись
- точность
- точный
- точно
- Достигать
- достигнутый
- Достигает
- через
- Действие (Act):
- Действие
- действия
- активный
- адаптация
- адаптивный
- Добавить
- добавленный
- добавить
- дополнение
- дополнительный
- Дополнительно
- адрес
- Добавляет
- признавать
- продвинутый
- Приключение.
- После
- снова
- возраст
- Агент
- агенты
- AI
- Помощник АИ
- AI модели
- помощь
- Стремясь
- алгоритмы
- выравнивать
- Выравнивает
- Все
- позволять
- Позволяющий
- позволяет
- в одиночестве
- вдоль
- рядом
- уже
- причислены
- Несмотря на то, что
- всегда
- an
- анализ
- Аналитические фармацевтические услуги
- аналитика
- и
- -Анджелесе
- анонсировать
- годовой
- Другой
- ответ
- ответы
- гимн
- Антропный
- любой
- все
- API
- КЛЮЧИ API
- API
- приложение
- отношение
- Применение
- Разработка приложения
- Приложения
- прикладной
- применяется
- подхода
- подходы
- соответствующий
- надлежащим образом
- Программы
- архитектура
- МЫ
- аргумент
- Аргументы
- Армстронг
- около
- массив
- Специалисты ELAN
- AS
- спросить
- внешний вид
- аспекты
- помощь
- помощник
- связанный
- At
- прикреплять
- внимание
- аудио
- дополненная
- Аутентификация
- автоматизировать
- Автоматизированный
- автоматы
- автоматически
- автоматизация
- доступен
- в среднем
- Ждите
- знать
- AWS
- назад
- Позвоночник
- Backend
- фон
- плохо
- Баланс
- бар
- Использование темпера с изогнутым основанием
- основанный
- Базовая линия
- колотить
- основной
- Основы
- BCG
- BE
- Beach
- Медведи
- , так как:
- было
- до
- начинать
- поведение
- за
- за кулисами
- не являетесь
- ниже
- эталонный тест
- полезный
- ЛУЧШЕЕ
- Лучшая
- между
- Beyond
- Крупнейшая
- Билл
- Билл Гейтс
- связывать
- Bing
- Немного
- Черный
- Черная дыра
- Заблокировать
- блокирование
- Блоки
- Блог
- зерно
- Начальная загрузка
- рожденный
- Бот
- изоферменты печени
- Дно
- Филиал
- Ломать
- ветер
- кратко
- шире
- коричневый
- браузер
- буфер
- строить
- строитель
- Строительство
- построенный
- бремя
- бизнес
- но
- by
- вычислять
- исчисляет
- расчет
- расчет
- призывают
- обратные вызовы
- под названием
- вызова
- Объявления
- CAN
- Может получить
- Канада
- возможности
- способный
- Пропускная способность
- захватить
- Захват
- случаев
- случаев
- КПП
- обслуживать
- общественное питание
- обслуживает
- Кошки
- осторожность
- осторожный
- центр
- определенный
- цепь
- цепи
- проблемы
- изменения
- персонаж
- Chatbot
- chatbots
- проверка
- Проверки
- Выберите
- выбранный
- обстоятельства
- Город
- класс
- классов
- клиент
- облако
- код
- Кодирование
- Кофе
- ПОСЛЕДОВАТЕЛЬНЫЙ
- сплоченной
- сотрудничать
- коллапс
- лыжных шлемов
- красочный
- Column
- Колонки
- COM
- объединять
- сочетании
- комбинаты
- комбинируя
- как
- выходит
- удобный
- Общий
- Связь
- Компания
- сравнить
- сравнив
- совместимость
- совместим
- полный
- полностью
- завершение
- комплекс
- сложности
- компонент
- компоненты
- состоящие
- композиция
- комплексный
- содержащий
- сама концепция
- краткий
- параллельный
- состояние
- Конфигурация
- слияние
- Свяжитесь
- Соединительный
- связь
- Минусы
- Рассматривать
- последовательно
- состоит
- Консоли
- постоянно
- конструкции
- содержать
- содержит
- содержание
- контекст
- контексты
- контекстной
- продолжать
- непрерывно
- контраст
- контроль
- контрольная
- Удобно
- Разговор
- диалоговый
- разговорный ИИ
- Беседы
- Конверсия
- переделанный
- преобразование
- Основные
- Corner
- исправить
- соответствующий
- может
- подсчет
- страна
- Пара
- чехол для варгана
- покрытый
- Создайте
- создали
- создает
- Создающий
- создание
- Полномочия
- Критерии
- Критик
- CRM
- решающее значение
- Текущий
- В настоящее время
- изготовленный на заказ
- Клиенты
- настройка
- настроить
- подгонянный
- передовой
- данным
- Структура данных
- База данных
- базы данных
- Время
- Дата и время
- сделка
- занимавшийся
- Декабрь
- решать
- Решение
- Принятие решений
- Декодирование
- преданный
- более глубокий
- По умолчанию
- определять
- определенный
- определяющий
- Определения
- доставки
- обеспечивает
- копаться
- демонстрация
- демонстрировать
- убивают
- демонстрирующий
- зависеть
- в зависимости
- зависит
- развертывание
- развернуть
- развертывание
- развертывание
- развертывает
- описывать
- описание
- Проект
- назначенный
- предназначенный
- желанный
- подробность
- подробный
- подробнее
- Определять
- определяет
- развивать
- Застройщик
- застройщиков
- развивающийся
- Развитие
- диаграммы
- ДИКТ
- DID
- отличаться
- различный
- по-разному
- трудный
- Размеры
- размеры
- Директивы
- непосредственно
- обсуждать
- обсуждается
- отображать
- отчетливый
- погружение
- Разное
- DM
- do
- документ
- документации
- Документация
- приносит
- Безразлично
- дело
- Дон
- сделанный
- двойной
- вниз
- скачать
- загрузок
- проект
- управлять
- два
- в течение
- динамический
- динамично
- e
- каждый
- Ранее
- Рано
- простота
- простота в использовании
- легче
- легко
- легко
- Простой в использовании
- эхо
- экосистема
- Эффективный
- фактически
- затрат
- эффективный
- эффективно
- легко
- или
- элемент
- элементы
- Элон
- Elon Musk
- еще
- вставлять
- встроенный
- вложения
- занятых
- сотрудников
- работает
- расширение прав и возможностей
- включить
- позволяет
- позволяет
- инкапсулирует
- встреча
- впритык
- Конечная точка
- привлечение
- Двигатель
- Двигатели
- Англия
- Английский
- Английская премьер-лига
- повышать
- расширение
- повышение
- обеспечивать
- обеспечивает
- обеспечение
- Предприятие
- лиц
- организация
- Окружающая среда
- средах
- Эквивалент
- Эпоха
- ошибка
- ошибки
- особенно
- существенный
- раздельно проживающий
- и т.д
- оценивать
- оценка
- Даже
- События
- Каждая
- пример
- Примеры
- превышать
- Кроме
- выполнять
- выполненный
- Выполняет
- проведение
- выполнение
- экземплифицирует
- Упражнение
- существовать
- ожидать
- ожидания
- ожидаемый
- надеется
- опыт
- экспериментальный
- эксперты
- объяснены
- Объясняет
- эксплицитно
- исследование
- Больше
- Разведанный
- экспорт
- выражение
- продлить
- простирающийся
- обширный
- и, что лучший способ
- дополнительно
- извлечение
- добыча
- Экстракты
- Face
- содействовал
- облегчает
- завод
- Факты
- далеко
- БЫСТРО
- Избранное
- Особенность
- Особенности
- Обратная связь
- несколько
- поле
- Поля
- figma
- фигура
- Файл
- Файлы
- заполнять
- заполненный
- заполнение
- фильтр
- фильтрация
- окончательный
- в заключение
- Найдите
- обнаружение
- Во-первых,
- 5
- Трансформируемость
- гибкого
- поток
- Фокус
- внимание
- фокусируется
- фокусировка
- следовать
- после
- следующим образом
- питание
- Что касается
- форма
- формат
- сформированный
- К счастью
- найденный
- Рамки
- каркасы
- часто
- друг
- друзья
- от
- полный
- полноценный
- функция
- функциональные возможности
- функциональность
- Функции
- фундаментальный
- веселая
- далее
- будущее
- Gain
- Игры
- ворота
- Общие
- в общем
- порождать
- генерируется
- генерирует
- порождающий
- поколение
- жанре
- Germany
- получить
- получающий
- GIF
- GitHub
- данный
- GMT
- Go
- идет
- будет
- хорошо
- зернистый
- график
- большой
- большой
- руководство
- инструкция
- методические рекомендации
- Hackathon
- обрабатывать
- Ручки
- Управляемость
- удобный
- Жесткий
- вред
- жгуты
- Есть
- имеющий
- сильно
- Герой
- помощь
- полезный
- помогает
- ее
- здесь
- hi
- High
- на высшем уровне
- высококачественный
- наивысший
- очень
- исторический
- история
- Отверстие
- капот
- кашель
- Как
- How To
- Однако
- HTML
- HTTP
- HTTPS
- хаб
- человек
- Сотни
- i
- ID
- идея
- идеальный
- идентификаторы
- if
- ii
- III
- иллюстрировать
- иллюстрирует
- изображений
- немедленная
- осуществлять
- реализация
- реализации
- в XNUMX году
- Импортировать
- улучшение
- улучшение
- in
- углубленный
- включают
- включены
- включает в себя
- В том числе
- включать
- включения
- невероятно
- индекс
- Индексы
- указывать
- указывает
- в отдельности
- лиц
- информация
- начальный
- инициировать
- инновационный
- вход
- затраты
- размышления
- устанавливать
- установлен
- Установка
- пример
- мгновение
- вместо
- инструкции
- рефлексологии
- интегрировать
- интегрированный
- Интегрируется
- Интегрируя
- интеграции.
- интеграций
- Умный
- предназначенных
- взаимодействовать
- взаимодействие
- взаимодействие
- интерактивный
- взаимодействует
- Интерфейс
- интерфейсы
- внутренне
- Интернет
- в
- выпустили
- Представляет
- интуитивный
- с участием
- мобильной
- вопрос
- вопросы
- IT
- пункты
- итерации
- ЕГО
- саму трезвость
- Джексон
- JavaScript
- работа
- Джордан
- путешествие
- JSON
- июль
- всего
- Юстиция
- Сохранить
- держит
- Основные
- ключи
- Вид
- Знать
- знания
- График знаний
- известный
- этикетка
- Этикетки
- Земля
- язык
- Языки
- большой
- больше
- Фамилия
- Задержка
- новее
- последний
- Лига
- УЧИТЬСЯ
- изучение
- оставил
- Наследие
- Длина
- Меньше
- позволять
- Lets
- письмо
- уровень
- уровни
- Кредитное плечо
- рычаги
- Используя
- библиотеки
- Библиотека
- такое как
- Вероятно
- ОГРАНИЧЕНИЯ
- недостатки
- Ограниченный
- ограничивающий
- рамки
- связи
- Список
- Слушать
- Списки
- жить
- ll
- LLM
- загрузка
- загрузчик
- расположенный
- расположение
- журнал
- Войти
- каротаж
- логика
- Длинное
- дольше
- посмотреть
- искать
- ВЗГЛЯДЫ
- поиск
- лос
- Лос-Анджелес
- Низкий
- машина
- обучение с помощью машины
- сделанный
- поддерживать
- Ремонтопригодный
- Сохранение
- поддерживает
- сделать
- ДЕЛАЕТ
- Создание
- управлять
- управление
- менеджер
- управления
- Манчестер
- Манчестер Юнайтед
- Манипуляция
- способ
- руководство
- ПРОИЗВОДИТЕЛЬ
- многих
- много людей
- карта
- отображение
- Карты
- Совпадение
- согласование
- математике
- математический
- Вопросы
- максимальный
- Май..
- me
- значить
- смысл
- означает
- проводить измерение
- Медиа
- заседания
- Соответствует
- памяти
- Память
- упомянутый
- идти
- объединение
- сообщение
- Сообщения
- обмен сообщениями
- Метаданные
- метод
- методы
- Метрика
- может быть
- миллионы
- минимальный
- небольшая
- отсутствующий
- ошибки
- Смешивание
- MLB
- Мобильный телефон
- модель
- Модели
- умеренность
- Модули
- Модули
- момент
- MongoDB
- монитор
- Мониторинг
- луна
- БОЛЕЕ
- самых
- Самые популярные
- двигаться
- кино
- много
- с разными
- несколько цепочек
- Маск:
- должен
- my
- имя
- Названный
- имена
- национальный
- натуральный
- Обработка естественного языка
- Откройте
- навигационный
- Возле
- необходимо
- Необходимость
- необходимый
- потребности
- отрицательный
- Новые
- New York
- New York Times
- следующий
- нет
- Ничто
- ничего
- понятие
- сейчас
- номер
- Обама
- объект
- цель
- объекты
- наблюдение
- получать
- получение
- OCR
- of
- предлагают
- предлагающий
- Предложения
- .
- oh
- Хорошо
- Олимпийские игры
- on
- консолидировать
- ONE
- только
- с открытым исходным кодом
- OpenAI
- Операционный отдел
- оператор
- оптимизированный
- оптимизирует
- Опция
- or
- заказ
- органический
- организации
- оригинал
- OS
- Другое
- Другое
- в противном случае
- наши
- внешний
- выходной
- выходы
- за
- переопределение
- обзор
- собственный
- пакет
- пакеты
- страница
- страниц
- пар
- панд
- бумага & картон
- Параллельные
- параметр
- параметры
- Парк
- часть
- особенно
- части
- pass
- Прошло
- проходит
- Прохождение
- мимо
- путь
- пути
- паттеранами
- Заработная плата
- Люди
- для
- ИДЕАЛЬНОЕ
- в совершенстве
- Выполнять
- производительность
- выполнения
- выполняет
- Разрешения
- настойчивость
- человек
- Персонализированные
- перспектива
- Физика
- кусок
- трубопровод
- пицца
- заполнитель
- Платформа
- Платон
- Платон Интеллектуальные данные
- ПлатонДанные
- Играть
- детская площадка
- играет
- пожалуйста
- плюс
- Точка
- сборах
- политика
- политический
- бассейн
- Популярное
- населенный
- положительный
- возможное
- После
- Блог
- потенциал
- мощностью
- Питание
- мощный
- практическое
- практика
- предпочитать
- премьер-министр
- представить
- президент
- предотвращать
- предупреждение
- предыдущий
- в первую очередь
- первичный
- Простое число
- частная
- Проблема
- проблемам
- продолжить
- процесс
- Обработанный
- Процессы
- обработка
- производит
- Продукт
- Производство
- Профессор
- Программирование
- языки программирования
- Проект
- проектов
- свойства
- собственность
- PROS
- прототип
- макетирования
- обеспечивать
- при условии
- Недвижимости
- поставщики
- приводит
- обеспечение
- что такое варган?
- цель
- целей
- положил
- Питон
- Вопросы и ответы
- Запросы
- вопрос
- Вопросы
- САЙТ
- быстро
- кавычки
- R
- повышение
- ассортимент
- ранжирование
- скорее
- рейтинг
- Сырье
- RE
- достигать
- реагировать
- Читать
- Reading
- готовый
- реальные
- реального времени
- данные в реальном времени
- область
- причина
- причины
- последний
- Управление по борьбе с наркотиками (DEA)
- записанный
- учет
- Recover
- уменьшить
- снижает
- снижение
- снижение
- ссылка
- привязка
- совершенствовать
- рафинирование
- районы
- Отношения
- выпустил
- актуальность
- соответствующие
- надежность
- складская
- полагаться
- опираясь
- остатки
- помнить
- напоминание
- удаленные
- оказывать
- повторять
- НЕОДНОКРАТНО
- перефразировать
- замещать
- отчету
- хранилище
- представление
- представляющий
- представляет
- запросить
- Запросы
- требовать
- обязательный
- Требования
- требуется
- спасать
- исследованиям
- решение
- ресурс
- Реагируйте
- ответ
- ответ
- ответы
- ответственный
- отзывчивый
- ОТДЫХ
- результат
- в результате
- Итоги
- удерживающий
- сохранение
- возвращают
- возвращение
- Возвращает
- многоразовый
- обзоре
- вращается
- Рис
- Богатые
- Роботы
- Роли
- роли
- корень
- маршрутизация
- РЯД
- Run
- Бег
- работает
- время выполнения
- s
- защитные меры
- главная
- Salesforce
- Сэм
- то же
- Сохранить
- сообщили
- говорит
- масштабируемые
- Шкала
- сценарий
- Сценарии
- Сцены
- график
- Гол
- поцарапать
- бесшовные
- легко
- Поиск
- Поисковая система
- поиск
- поиск
- Раздел
- разделах
- безопасный
- безопасность
- посмотреть
- выбранный
- выбор
- продажа
- Отправить
- чувствительный
- настроение
- чувства
- отдельный
- сентябрь
- Последовательность
- Серии
- служить
- сервер
- служит
- Услуги
- набор
- Наборы
- установка
- настройки
- установка
- семь
- несколько
- Поделиться
- общие
- Оболочка
- блестит
- поставляется
- должен
- показывать
- демонстрации
- показанный
- Шоу
- Сигма
- значительный
- аналогичный
- просто
- упрощенный
- упростить
- упрощение
- просто
- с
- одинарной
- Размер
- слабина
- небольшой
- меньше
- умный
- отрывок
- So
- уже
- Футбольный
- Соцсети
- социальные сети
- Сообщения в социальных сетях
- только
- твердый
- Решение
- РЕШАТЬ
- некоторые
- удалось
- иногда
- сложный
- звуки
- Источник
- Источники
- Space
- Испанский
- специализированный
- конкретный
- конкретно
- конкретика
- указанный
- скорость
- потраченный
- раскол
- расколы
- Спорт
- площадь
- стоять
- автономные
- стандарт
- Начало
- и политические лидеры
- Начало
- Область
- отчетность
- статический
- Шаг
- Шаги
- По-прежнему
- Stop
- остановка
- Останавливает
- диск
- магазин
- хранить
- магазины
- хранение
- История
- простой
- поток
- потоковый
- упорядочить
- обтекаемый
- сильные
- Забастовки
- строка
- Структура
- структурированный
- структур
- структурирующий
- стиль
- предмет
- последующее
- Успешно
- такие
- Костюм
- подходящее
- suite
- суммировать
- РЕЗЮМЕ
- Закат
- поддержка
- Поддержанный
- Поддержка
- Убедитесь
- Стабильность
- синхронизация
- конспект
- синтаксис
- система
- системы
- ТАБЛИЦЫ
- портной
- с учетом
- взять
- принимает
- направлена против
- Сложность задачи
- задачи
- команда
- команды
- сказать
- шаблон
- шаблоны
- Терминал
- терминология
- terms
- тестXNUMX
- Тестирование
- текст
- чем
- Спасибо
- который
- Ассоциация
- Основы
- концентратор
- информация
- The New York Times
- Проекты
- Источник
- мир
- их
- Их
- тогда
- Там.
- Эти
- они
- вещи
- этой
- те
- хоть?
- Через
- по всему
- время
- кропотливый
- раз
- Название
- в
- вместе
- знак
- лексемизацию
- Лексемы
- слишком
- инструментом
- Инструментарий
- инструменты
- топ
- тема
- Темы
- Всего
- город
- трассировка
- трек
- традиционный
- Обучение
- Transform
- преобразований
- трансформатор
- трансформеры
- переход
- суд
- правда
- по-настоящему
- стараться
- настройка
- ОЧЕРЕДЬ
- Поворот
- учебник
- Дважды
- два
- напишите
- Типы
- Машинопись
- типично
- ui
- В конечном счете
- незатронутый
- под
- лежащий в основе
- понимать
- понимание
- понимает
- унифицированный
- созданного
- Объединенный
- Universal
- В отличие от
- до
- Обновление ПО
- Updates
- загружено
- URL
- us
- юзабилити
- Применение
- использование
- прецедент
- используемый
- Информация о пользователе
- Пользовательский интерфейс
- пользователей
- использования
- через
- коммунальные услуги
- использовать
- использовать
- использует
- Использующий
- v1
- Проверка
- Validator
- ценный
- ценностное
- Наши ценности
- переменная
- разнообразие
- различный
- Ve
- разносторонний
- версия
- очень
- с помощью
- Видео
- Вид
- Нарушения
- видимый
- визуализации
- жизненный
- vs
- от
- прохождение
- хотеть
- законопроект
- Смотреть
- Путь..
- способы
- we
- Погода
- Web
- веб-браузер
- веб-сервисы
- веб-сайты
- ЧТО Ж
- известный
- были
- Что
- Что такое
- Что
- когда
- будь то
- который
- в то время как
- КТО
- все
- зачем
- широкий
- широко
- виджет
- Википедия.
- будете
- окно
- Победы
- в
- без
- Word
- Работа
- работавший
- рабочий
- Рабочие процессы
- работает
- работает
- Мир
- бы
- записывать
- письмо
- X
- еще
- йорк
- Ты
- ВАШЕ
- себя
- YouTube
- Zendesk
- зефирнет
- ZIP