Основная идея LangChain Agent заключается в использовании LLM в качестве мозга, способного автоматически мыслить, принимать решения и выполнять различные действия для достижения поставленных задач.
С разработчиков это означает, что мы предварительно разрабатываем различные API, а затем даем Агенту задачу, позволяя LLM анализировать, какой API вызывать для завершения задачи.
Чтобы лучше понять проблему, которую ставит перед собой LangChain Agent, давайте рассмотрим пример.
Например:
Если мы хотим исследовать, можно ли использовать "Docker в качестве решения для развертывания в производстве", мы бы сначала искали "введение в Docker" на Baidu, просматривали результаты поиска, а затем дальше искали "преимущества и недостатки развертывания Docker" и просматривали результаты, и так далее, пока не пришли к выводу.
LangChain Agent стремится симулировать этот процесс. Я могу предварительно подготовить ряд инструментов (например, поиск в Baidu, инструменты для извлечения содержимого URL), а затем дать Агенту целевую задачу "Можно ли использовать Docker в качестве решения для развертывания в производстве?" Агент затем будет формулировать подсказки для вызова LLM. Для достижения этой задачи следующим шагом будет выполнение какого действия (т.е. вызов какого инструмента). ИИ вернет инструмент, который нужно вызвать, код выполнит этот инструмент, затем передаст результат выполнения инструмента обратно ИИ и спросит следующий шаг в выполнении какого инструмента. Повторяя этот процесс, мы завершим упомянутую задачу.
Примечание: С выпуском модели GPT эта функциональность стала взрывной, позволяя LLM действовать как мозг, активно мыслить, а затем вызывать различные API, которые мы разработали. Это значительно расширяет возможности LLM. В настоящее время эта функция все еще находится в экспериментальной стадии и будет многократно вызывать LLM, поэтому требует довольно много токенов. Завершение задачи может стоить десятки тысяч токенов за несколько минут. Если вы хотите сэкономить деньги, рекомендуется сначала позволить LLM выполнять простые логические задачи.
Основные концепции
Давайте теперь представим связанные компоненты и концепции.
Агент
Агент можно понимать как нашего помощника, действующего от нашего имени для принятия решений. В основной реализации LangChain Agent это происходит через LLM, который определяет следующее действие (или вызов API). Режим ReAct описывает процесс принятия решений ИИ. Те, кто заинтересованы, могут изучить этот вопрос более подробно.
LangChain предоставляет несколько типов Агентов для различных сценариев.
Инструменты
Я считаю, что Инструменты лучше понимать как API, предварительно упакованные с различными функциональными API, предназначенные для расширения возможностей LLM. LLM определяет, какой конкретный API вызвать для выполнения задачи.
Наборы инструментов
Наборы инструментов обычно предоставляют LLM не только один или два инструмента, а целый набор инструментов, чтобы дать LLM больше вариантов при выполнении задач.
AgentExecutor
Запускаемый исполнитель отвечает за выполнение выбранного LLM инструмента (API). Псевдокод для этого процесса выглядит следующим образом:
next_action = agent.get_action(...)
while next_action != AgentFinish:
observation = run(next_action)
next_action = agent.get_action(..., next_action, observation)
return next_action
Хотя процесс выполнения несложен, исполнитель обрабатывает множество деталей, в основном включая:
- Решение ситуации, когда агент выбирает несуществующий инструмент
- Обработка ситуаций ошибок инструментов
- Обработка ситуаций, когда агент создает вывод, который нельзя разрешить как вызов инструмента
- Отладка проблем.
Быстрый старт
Этот раздел вводит основное использование LangChain's Agent.
1. Загрузка LLM
Сначала давайте загрузим языковую модель (LLM), которую мы будем использовать для управления агентом.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
2. Определение Инструментов
Далее мы определим некоторые инструменты для вызова Агентом. Мы напишем очень простую функцию на Python для вычисления длины введенного слова.
from langchain.agents import tool
@tool
def get_word_length(word: str) -> int:
"""Возвращает длину слова."""
return len(word)
get_word_length.invoke("abc")
Примечание: Комментарии к функциям очень важны. Они сообщают LLM, какую проблему он может решить, вызывая их. Функция
get_word_length
говорит LLM, что он может вычислить длину слова.
3
Определить набор инструментов
tools = [get_word_length]
3. Создание приглашения
Теперь давайте создадим приглашение. Поскольку OpenAI Function Calling был оптимизирован для использования инструментов, нам практически не нужны инструкции о логике или формате вывода. У нас есть только две входные переменные: input
и agent_scratchpad
. input
представляет вопрос пользователя, а agent_scratchpad
является заполнителем для инструкций вызова агента, которые будут вставлены в шаблон приглашения при выполнении команды вызова инструмента.
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Ты очень мощный помощник, но не понимаешь текущую ситуацию.",
),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)
4. Привязка инструментов к LLM
Как агент узнает, какие инструменты он может использовать?
Это зависит от функции вызова инструмента OpenAI (многие модели поддерживают подобные функции). Нам просто нужно сообщить модели о формате вызова инструмента.
llm_with_tools = llm.bind_tools(tools)
5. Создание агента
Теперь, когда мы интегрировали предыдущий контент, мы можем перейти к созданию прокси-программы. Мы импортируем две последние практические утилиты: одну для форматирования промежуточных шагов (действий прокси, вывода инструмента) во входные сообщения, которые могут быть отправлены модели, и другую для преобразования выходных сообщений в действия прокси/завершения прокси.
from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser
agent = (
{
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),
}
| prompt
| llm_with_tools
| OpenAIToolsAgentOutputParser()
)
Определение исполнителя агента
from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
Давайте продемонстрируем работу прокси-программы на примере:
list(agent_executor.stream({"input": "Сколько букв в слове 'eudca'?"}))
Пример журнала вывода агента
> Входим в новую цепочку выполнения агента...
Вызов: Запуск `get_word_length` с параметром `{'word': 'educa'}`
В слове "educa" 5 букв.
> Завершено выполнение цепочки.
Через этот пример мы продемонстрировали полный процесс работы прокси-программы.
Добавление функции памяти в агент
Если мы хотим, чтобы агент помнил предыдущие разговоры, это довольно просто: нам просто нужно вставить содержимое, возвращенное ИИ, в приглашение и отправить его вместе с ИИ.
Изменение шаблона приглашения
Ниже мы изменяем шаблон приглашения, чтобы включить переменную шаблона истории разговора.
from langchain.prompts import MessagesPlaceholder
MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Ты отличный помощник, но не очень хорош в подсчете количества букв в словах.",
),
MessagesPlaceholder(variable_name=MEMORY_KEY),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)
Изменение определения процесса агента
Измените определение процесса агента, чтобы обеспечить данные истории разговора для шаблона приглашения, как показано в следующем коде, добавив обработку параметра chat_history
.
agent = (
{
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_to_openai_tool_messages(
x["intermediate_steps"]
),
"chat_history": lambda x: x["chat_history"],
}
| prompt
| llm_with_tools
| OpenAIToolsAgentOutputParser()
)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
chat_history = []
input1 = "Сколько букв в слове '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": "Действительно ли это слово существует?", "chat_history": chat_history})