Всем привет! Меня зовут Максимов Максим, я — NLP инженер в компании red_mad_robot. В этой статье хотел бы рассказать об определении AI-агента, а также об основных его компонентах. Также на практических примерах будет показано, как каждый из компонентов может быть реализован.

Содержание:

Что такое AI-агент?

Нет четкого и фиксированного определения AI‑агента, но многие крупные компании дают схожие описания, по которым мы можем понять что это такое. Давайте рассмотрим определения агента от Anthropic, Hugging Face и Сбера.

Anthropic (перевод):

«Агенты … представляют собой системы, в которых LLM динамически управляют собственными процессами и использованием инструментов, сохраняя контроль над тем, как они выполняют задачи.»

Hugging Face (перевод):

«Агент — это система, использующая модель искусственного интеллекта для взаимодействия с окружающей средой для достижения поставленной пользователем цели. Он сочетает в себе рассуждения, планирование и выполнение действий (часто с помощью внешних инструментов) для выполнения задач.»

Сбер:

«AI‑агент или агентная система — это автоматизированная система, соответствующая трем критериям:

  1. Планированию действий — способности понять потребности человека и построить план действий;

  2. Выполнению плана — способности самостоятельно совершить указанные действия, в том числе используя агентные инструменты для взаимодействия с внешней средой;

  3. Автономности — наличию полномочий исполнять план без необходимости предварительного согласования шагов с человеком.»

Рассмотрим пример простого агента. На вход он принимает запрос пользователя (например узнать текущий курс доллара). Этот запрос отправляется в LLM, которой доступны инструменты:

  1. Калькулятор;

  2. Поиск в интернете;

  3. Запуск кода.

Задачей LLM будет выбрать наиболее релевантный к запросу пользователя инструмент (в данном случае «Поиск в интернете»). 

После вызова этого инструмента агентом, используя запрос пользователя (курс доллара) и полученный ответ инструмента (результат поиска в интернете по запросу «текущий курс доллара) LLM сгенерирует ответ пользователю.

Схема базового AI-агента
Схема базового AI‑агента

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

Основные компоненты AI-агента

Далее мы рассмотрим основные компоненты AI‑агента, а также приведём их описания и практические примеры на Python.

LLM

Large Language Model (LLM) — это мозг нашего агента, который позволяет «интерпретировать» запросы на естественном языке. Тема LLM достаточно объемная, давайте вкратце рассмотрим, что это такое. 

Если простыми словами, то LLM — это математическая модель, которая на основе входного текста, предсказывает следующее слово (часть слова или фразу). 

Работает она следующим образом:

Генерация текста LLM
Генерация текста LLM

Это достаточно простое и обобщенное объяснение, но можно воспринимать LLM как черный ящик, который способен генерировать текст.

При выборе LLM для работы стоит обратить внимание на следующие вещи:

  • Язык данных и промптов. Языковые модели обучаются на разных корпусах текстов. Если вы планируете работать с русскоязычными данными, то необходимо обратить внимание да LLM, которые обучались на русскоязычных данных (например LLM от Vikhr). Либо же, если данные могут быть на разных языках, можно присмотреться к мультиязычным LLM;

  • Тип задачи, выполняемой агентом. Различные модели могут преуспевать в различных задачах. Например, модели Claude считаются лучшими в написании кода, поэтому их стоит рассмотреть с задачами на программирование;

  • Требуемая конфиденциальность данных. LLM может быть развернута в облаке (on‑cloud) и доступна вам по API, но тогда данные, с которыми работает агент, обрабатываются на серверах вашего провайдера LLM. Если ваши данные конфиденциальны, это недопустимо. В случае желания защитить свои данные, LLM разворачиваются в своем контуре (on‑premise) на собственном оборудовании. Это позволяет сохранить в безопасности данные, но накладывает дополнительные затраты на инфраструктуру;

  • Инфраструктурные требования. Если LLM планируется развертывать в собственном контуре, то для ее работы требуется мощное оборудование, на которое нужно выделять бюджет. Также для развертывания и инференса LLM используются различные инструменты, которые подбираются в зависимости от требований проекта. Например этими инструментами могут быть vLLM, Triton или так далее (про работу с фреймворками по запуску LLM я уже писал в своем прошлом посте).

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

Далее приведем пример использования LLM в программе на Python. 

Для этого можно использовать различные библиотеки. Если Ваша модель поддерживает OpenAI API, то можно воспользоваться библиотекой openai следующим образом:

# Предварительно установите библиотеку командой: pip install openai 
from openai import OpenAI

url = "localhost:1234" # url с моделью (если имеется)
api_key = "OPENAI_API_KEY" # API_KEY для доступа к модели
model_name = "model_name" # название модели 
 
# Настройка клиента 
client = OpenAI(
    base_url=f"http:{url}/v1",
    api_key=api_key
)

# Отправка запроса LLM
response = client.chat.completions.create(
    model=model_name,
    messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)

Также популярная библиотека LangChain предоставляет удобный интерфейс для взаимодействия с LLM. Рассмотрим простой пример подключения и использования LLM, используя эту библиотеку.

# Предварительно установите библиотеки командой: pip install openai langchain
from langchain.chat_models import init_chat_model

provider = "openai" # провайдер модели 
model_name = "gpt-5-nano" # название модели 

model = init_chat_model(f"{provider}:{model_name}")
response = model.invoke("Why do parrots talk?")

Инструменты

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

Почему это нужно LLM?

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

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

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

Схема работы AI-агента с инструментами
Схема работы AI‑агента с инструментами

Давайте чуть подробнее рассмотрим, как работает вызов инструментов агентом.

LLM — это математическая модель, которая генерирует текст. То есть, у нее нет возможность вызывать инструменты. Когда говорим о том, что мы предоставляем инструмент LLM, имеется ввиду то, что мы даем ей »информацию» об имеющихся у нее инструментах. Используя эту информацию, на основе запроса пользователя LLM генерирует специально форматированный текст (например, JSON), который парсится системой для вызова соответствующего инструмента с переданными моделью аргументами.

Чтобы LLM могла работать с инструментом, он должен иметь подробное текстовое описание, а именно:

  • Текстовое описание того, что делает функция;

  • Текстовое описание параметров функции, а также типизацию каждого из них;

Рассмотрим процесс работы LLM с инструментами на практике. 

LangChain предоставляет простой интерфейс для работы с Function calling. Реализуем его с использованием этой библиотеки и GigaChat.

# Предварительно установите библиотеку командой: langchain_gigachat
from langchain_gigachat.chat_models import GigaChat
from langchain_core.tools import tool
from langchain_community.document_loaders import PyPDFLoader

# Создаем клиент для взаимодействия с GigaChat
giga = GigaChat(
    credentials=GIGACHAT_API_KEY, # API_KEY для доступа к модели
    verify_ssl_certs=False,
    model = "GigaChat-Pro"
)

# Инициализация инструментов
@tool
def read_pdf(path_to_pdf: str) -> str:
    """Чтение файла из pdf
       param:
       path_to_pdf (str): Путь к файлу pdf
       return: Считанный текст из pdf файла
       """
    loader = PyPDFLoader(path_to_pdf)
    pages = []
    for page in loader.alazy_load():
        pages.append(page)
    return pages

@tool
def add(a: int, b: int) -> int:
    """Складывает числа a и b"""
    return a + b


@tool
def multiply(a: int, b: int) -> int:
    """Перемножает числа a и b"""
    return a * b


tools = [read_pdf, add, multiply]

# Оснащение модели инструментами
giga_with_tools = giga.bind_tools(tools)

После инициализации наших инструментов и «предоставления» их LLM, попробуем посмотреть какой будет ответ на разных запросах:

result = giga_with_tools.invoke("Прочитай файл по пути: ./files/file_1.pdf")
print(result.tool_calls)
Результат вызова LLM с инструментами #1
Результат вызова LLM с инструментами #1
result = giga_with_tools.invoke("Просуммируй числа 10 и -4")
print(result.tool_calls)
Результат вызова LLM с инструментами #2
Результат вызова LLM с инструментами #2
result = giga_with_tools.invoke("Перемножь числа 5 и 0")
print(result.tool_calls)
Результат вызова LLM с инструментами #3
Результат вызова LLM с инструментами #3

Как могли заметить, LLM не вызывает функцию, а генерирует ее название и аргументы. Далее, используя эту информацию, мы можем в программе определить вызов каждой из функций. Реализовать это можно следующим образом:

func_d = {"read_pdf": read_pdf.func, "add": add.func, "multiply": multiply.func}

result = giga_with_tools.invoke("Перемножь числа 5 и 20")

func_name = result.tool_calls[0]['name']
func_args = result.tool_calls[0]['args']

print(func_d[func_name](**func_args))
# 100

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

  1. Используйте понятное и конкретное название функций;

  2. Для каждой функции используйте подробное описание, а также описание каждого аргумента;

  3. Для специфичных функций в описании приводите пример аргументов, которые она может принимать, а также пример и описание результата выполнения функции;

  4. Производите качественную обработку ошибок при некорректных данных, а также при неудачных вызовах функции;

  5. Реализуйте методы проверки вызова функции на выполнения «опасных» операций. (удаление данных, отправка данных и так далее).

Промпты

Прелесть LLM в том, что мы можем общаться с ней на естественном языке. Соответственно, задачи, которые мы ставим LLM, тоже описываются естественным языком, то есть — текстом. 

Промпт (или подсказка) — это текстовый запрос, по которому LLM генерирует ответ. 

Как для человека качество выполненной задачи зависит от четкости поставленной цели, так и для LLM качество ответа определяется точностью и ясностью промпта.

Плохой промпт - плохой результат
Плохой промпт — плохой результат

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

Компонент

Описание

Пример

Цель 

Конкретная цель, которую вы хотите достичь с помощью модели

Твоя цель — выполнить поставленную задачу, используя имеющиеся инструменты

Инструкция

Инструкция по выполнению поставленной задачи

Выбери наиболее релевантный для задачи инструмент

С входными данными сделай А, B и С

Контекст 

Информация, необходимая для решения поставленной задачи

Текст из pdf документа 

Сформулированный запрос пользователя 

Роль

Кем или чем выступает LLM. Корректным будет сказать модели, чтобы она действовала как профессионал в предметной области

Вы Senior Python разработчик... Действуй как Project manager... 

Формат ответа

Формат, в котором LLM должна предоставить ответ. Это может быть список, таблица, JSON и так далее

Ответ предоставь в формате JSON

Ограничения

Ограничения, которых должна придерживаться LLM при генерации ответа

Если под задачу пользователя не подходит ни один инструмент, выдай ответ «Я не смогу вам помочь».

Также хорошей практикой является предоставление модели одного или нескольких примеров выполнения поставленной задачи. Например:

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

Пример:

You: Hello! How are you?

User: I good 

You: Correct answer: “I‘m good’. How is your day?

….”

Отлично, мы разобрались с промптами и поняли, как правильно их писать. Далее рассмотрим такое понятие как «шаблоны подсказок». 

Шаблоны подсказок — это заранее подготовленные шаблоны текстов для LLM, которые переиспользуются для различных входных данных. Шаблоны удобны тем, что снимают с пользователя необходимость писать полный (по всем правилам) промпт для LLM, чтобы решить свою задачу, а также стандартизируют формат работы агента.

Работа шаблонов подсказок для LLM
Работа шаблонов подсказок для LLM

По сути, текст (шаблон) имеет переменные, в которые можно подставлять значения — запросы пользователей, результат выполнения функции и так далее.

Давайте рассмотрим примеры работа с шаблонами в своей программе на Python:

def user_query_template(user_query):
    prompt = f"""
    Твоя цель - структурировать информацию о пользователе и выдать ее строго в формате JSON. 
    Данные могут содержать только следующие ключи: name, year_birth, city_birth.
    Информация о пользователе: {user_query}
    """
    return prompt
user_info = "Петя родился в 1999 году в городе Москва, у него есть собака"
print(user_query_template(user_info))
Шаблон заполняется нашими данными #1
Шаблон заполняется нашими данными #1

Таким нехитрым способом мы создали шаблон подсказки для нашей задачи.

Также библиотека LangChain предоставляет удобный интерфейс для создания шаблонов подсказок. Рассмотрим пример:

from langchain_core.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template("""
    Твоя цель - структурировать информацию о пользователе
    и выдать ее строго в формате JSON. 
    Данные могут содержать только следующие ключи: name, year_birth, city_birth.
    Информация о пользователе: {user_query}
""")

prompt = prompt_template.invoke({"user_query": "Петя родился в 1999 году в городе Москва, у него есть собака"})

print(prompt.text)
Шаблон заполняется нашими данными #2
Шаблон заполняется нашими данными #2

Помимо простых текстовых шаблонов, LangChain предоставляет шаблоны для промптов в формате чата. То есть, когда мы имеем последовательность сообщений от ассистента (LLM) и пользователя.

from langchain_core.prompts import ChatPromptTemplate

# Создание шаблона подсказки
prompt_template = ChatPromptTemplate([
    ("system", """Твоя цель - структурировать информацию о пользователе 
                  и выдать ее строго в формате JSON. 
                  Данные могут содержать только следующие ключи: 
                  name, year_birth, city_birth."""),
    ("user", "Информация о пользователе: {user_query}")
])

# Заполнения шаблона 
chat_prompt = prompt_template.invoke({"user_query": "Петя родился в 1999 году в городе Москва, у него есть собака"})

print(chat_prompt)
Шаблон заполняется нашими данными #3
Шаблон заполняется нашими данными #3

Современные LLM, обученные на данных в виде чата, лучше работают с промптами в виде диалога, поэтому в зависимости от модели выбирайте подходящий формат общения с ней.

Память

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

Сама по себе LLM не имеет способность запоминать информацию. Когда вы общаетесь в чате с ChatGPT, DeepSeek или другими LLM в web‑сервисах, могли заметить, что модель запоминает ваши предыдущие сообщения, которые вы ей отправляли в рамках диалога. Под капотом этого чата реализовано хранение вашего диалога с LLM. Если бы этого не было, общение с моделью стало бы сущим кошмаром: для каждого сообщения приходилось бы заново вписывать весь контекст. Давайте подробнее разберем тему памяти LLM и как это помогает при работе с агентами. 

Память для LLM - важная вещь
Память для LLM — важная вещь

Как и у человека, для LLM выделяется 2 основных вида памяти:

  • Кратковременная память

  • Долговременная память 

Рассмотрим каждую из них. 

Кратковременная память

Кратковременная память позволяет агенту запоминать предыдущие сообщения в рамках одной сессии приложения или в рамках одного диалога. В качестве примера можно привести диалог в мессенджере. Ваш собеседник (LLM), видит предыдущие сообщения, и остается в контексте. После нескольких сообщений вы можете вставить реплику или зададите вопрос на основе предыдущего общения. Изобразить это можно следующим образом:

Пример работы кратковременной памяти LLM
Пример работы кратковременной памяти LLM

Можно заметить, что с каждой итерацией диалог растет. А значит, на каждой итерации все больше текста поступает на вход LLM.

Проблема в том, что тот объем текста, который LLM может обрабатывать за раз ограничен. Соответственно, при достижении максимальной длины контекста необходимо производить очистку диалога с моделью. 

Существуют различные подходы, которыми происходит уменьшение контекста при работе с LLM. Рассмотрим некоторые из них. 

Самый простой алгоритм — это очистка первых N сообщений, который были в диалоге, сохраняя последние. Изобразить это можно следующим образом:

Очистка диалога "скользящим окном"
Очистка диалога «скользящим окном»

Технически это реализуется через «скользящее окно»: в контексте каждого нового запроса остается только последняя часть диалога, умещающаяся в лимит LLM.

Также известным подходом является обобщение той части диалога, которая не помещается в контекст LLM — этот процесс называется «суммаризацией текста». Суммаризация текста происходит при помощи LLM. Она формирует выжимку предыдущей части диалога. Этот процесс уменьшает начало диалога, и позволяет уместить эту часть в доступном контексте для модели. Изобразить это можно следующим образом:

Обобщение предыдущих сообщений диалога
Обобщение предыдущих сообщений диалога

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

Давай рассмотрим простой пример формирования и использования кратковременной памяти на Python. Реализуем программу для общения с GigaChat с функционал истории диалога.

from langchain_gigachat.chat_models import GigaChat
from langchain_core.tools import tool
from langchain_community.document_loaders import PyPDFLoader

def cut_or_summarize_messages(messages):
    """Функция для обрезания\суммаризации сообщений"""
    ...

# Фукнция для преобразования массива сообщений в текстовый запрос 
def messages_to_text(messages, max_len=1000):
    messages_one_str = ""
    
    for i, message in enumerate(reversed(messages), 1):
        curr_str = f"{message['role']}: {message['message']}"

        if len(messages_one_str) + len(curr_str) > max_len: # если контекст превышает лимит
            cut_or_summarize_messages(messages) # уменьшаем контекст
            return messages_one_str
            
        messages_one_str = curr_str + messages_one_str

    return messages_one_str

giga = GigaChat(
    credentials=GIGACHAT_API_KEY,
    verify_ssl_certs=False,
    model = "GigaChat-Pro")

messages = []

# Диалог с LLM
while True:
    # Ввод текстового сообщения 
    input_text = input("User: ")

    # Запоминаем сообщение пользователя
    messages.append({"role": "user", "message": input_text})
    
    if input_text == "end":
        break

    # Формируем текстовый запрос с историй прошлых сообщений
    message_text = messages_to_text(messages)

    # Вызов модели
    result = giga.invoke(message_text)
    
    output_text = result.content

    print()
    print(f"Assisten: {output_text}")
    print()

    # Сохраняем ответ LLM в память
    messages.append({"role": "assistant", "message": output_text})
Демонстрация работы кратковременной памяти у AI-агента #1
Демонстрация работы кратковременной памяти у AI‑агента #1

Также LangChain предоставляет простую реализацию по использование кратковременной памяти:

from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain_gigachat.chat_models import GigaChat
from langchain_core.prompts import PromptTemplate

# Инициализируем модель и память
giga = GigaChat(
    credentials=GIGACHAT_API_KEY,
    verify_ssl_certs=False,
    model = "GigaChat-Pro")

memory = ConversationBufferMemory()

# Создаем шаблон подсказки с переменными "история" и "текущее" сообщение пользователя
prompt_template = PromptTemplate(template='Ты полезный ассистент. Текущий разговор:\n{history}\nЧеловек: {input}\nAI:',
                                 input_variables=['history', 'input'])
                             
# Создаем цепочку с памятью
chain = ConversationChain(
    llm=giga,
    memory=memory,
    verbose=True,  # Чтобы видеть промпты в консоли
    prompt=prompt_template
)

# Пример диалога
print("--- Первый запрос ---")
response1 = chain.predict(input="Привет! Меня зовут Алексей.")
print(response1)

print("\n--- Второй запрос ---")
response2 = chain.predict(input="Как меня зовут?")
print(response2)
Демонстрация работы кратковременной памяти у AI-агента #2
Демонстрация работы кратковременной памяти у AI‑агента #2

Долговременная память

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

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

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

Получение эмбеддинга из объекта. Источник
Получение эмбеддинга из объекта. Источник

Одним из наиболее эффективных и популярных методов работы агента и векторной базы данных является RAG (Retrieval Augmented Generation), или «Генерация, дополненная поиском». Его работа состоит из нескольких шагов: запрос пользователя векторизуется → используя эмбеддинг запроса, система находит в векторной базе данных наиболее релевантные фрагменты информации → найденные фрагменты передаются LLM для генерации ответа на запрос пользователя.

Изобразить это можно следующим образом:

Визуализация работы простого RAG
Визуализация работы простого RAG

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

Давайте рассмотрим практический пример. Мы создадим агента, который будет иметь инструмент для обращения в RAG. В качестве векторной базы данных мы возьмем FAISS. В качестве LLM будет взят GigaChat.

Первым этапом мы подготовим векторную базу данных:

# Предварительно установите библиотеки: pip install faiss-cpu sentence-transformers
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np

# Функция для считывания текста с файла и разделения его на фрагменты 
def preprocess_data(file_path):

    loader = PyPDFLoader(file_path)
    pages = []
    for page in loader.load():
        pages.append(page)

    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
    all_splits = text_splitter.split_documents(pages)

    chunks = [chunk.page_content for chunk in all_splits]
    
    return chunks

# Функция для получения эмбеддингов фрагментов
def get_embeddings(model, chunks):
    
    embeddings = model.encode(chunks)

    return embeddings


# Инициализация модели для векторизации текста
model = SentenceTransformer("all-MiniLM-L6-v2")

files = ["./data/file_1.pdf", 
         "./data/file_2.pdf"]

# Подготовка текстовых фрагментов
chunks = []
for file_path in files:
    chunks.extend(preprocess_data(file_path))

# Векторизация текстовых фрагментов
embeddings = get_embeddings(model, chunks)

# Сбор полученных эмбеддингов в векторную базу FAISS
index = faiss.IndexFlatL2(embeddings.shape[1])
index.add(embeddings)

Далее подготовим агента. Создадим инструмент для обращения в векторную базу данных:

from langchain_core.tools import tool
from langchain_gigachat.chat_models import GigaChat

# Инициализируем GigaChat
giga = GigaChat(
    credentials=GIGACHAT_API_KEY,
    verify_ssl_certs=False,
    model = "GigaChat-Pro")

# Инструмент для поиска релевантного фрагмента к запросу пользователя
@tool
def get_relevant_chunk(query: str):
    """
    Функция для поиска информации в базе знаний. 
    Используй этот инструмент если пользователь хочет найти информацию.
    params:
    query (str): Запрос пользователя.
    """
    embs = model.encode([query])

    D, I = index.search(x=embs, k=1)
    relevant_chunk = chunks[I[0][0]]
    return relevant_chunk

# Оснощаем агента инструментом поиска 
tools = [get_relevant_chunk]
tools_d = {"get_relevant_chunk": get_relevant_chunk.func}
giga_with_tools = giga.bind_tools(tools)

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

query = """Я хочу найти информацию по следующему заголовку: Integrating generative AI into SDLC"""
response = giga_with_tools.invoke(query)

print(response.tool_calls[0])
Результат обращения к AI-агенту
Результат обращения к AI‑агенту

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

tool_chice = response.tool_calls[0]

# Вызов инструмента с аргументами, которые передала LLM
result_search = tools_d[tool_chice['name']](**tool_chice['args'])
print(result_search)

Теперь подготовим шаблон подсказки, которую в дальнейшем агент сможет использовать для формирования ответа:

prompt = PromptTemplate(template="""Ты полезный ассистент. 
                            Дай краткий ответ на запрос пользователя, используя найденную информацию:
                            Запрос: {query}
                            Найденная информация: {context}
                            """,
                        input_variables=["query", "context"]) # Переменные шаблона

Сгенерируем ответ по запросу пользователя и найденному контексту:

full_query = prompt.invoke({"query": query, "context": result_search})
response = giga.invoke(full_query)
print(response.content)
Ответ LLM на основе вопроса и найденного контекста
Ответ LLM на основе вопроса и найденного контекста

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

Планирование и рассуждение

До этого момента были рассмотрены сценарии работы агента в виде прямолинейного выполнения действий: запрос от пользователя → выбор и вызов инструмента → генерация ответа. 

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

Здесь мы и приходим к таким вещам как планирование и рассуждения агентов. 

Рассмотрим работу планирования и рассуждения на примере агента для решения математических задач. 

Логика такого агента может быть следующая: 

0. Агент получает на вход формулировку математический задачи;

1. Агент интерпретирует задачу и строит план ее решение в виде последовательности действий (происходит планирование);

2. Далее происходит последовательное выполнение каждого шага, с сохранением полученных результатов и передачи их на следующий шаг. На каждом шаге агент использует необходимые инструменты;

3. После выполнения всех действий, на основе задачи и выполненных шагов происходит саморефлексия. То есть агент анализирует проделанную работу и выдает решение, правильно ли была решена задача или нет. 

4. Если агент решил, что задача решена верно → возвращаем сгенерированный ответ пользователю. Если агент посчитал, что решение некорректно → возвращаемся к шагу 1.

Схема работы простого AI-агента для решения математических задач
Схема работы простого AI‑агента для решения математических задач

Кстати, кому интересно, в рамках pet‑проекта я делал похожего агента для решения математических задач. Его реализацию можно найти у меня на GitHub здесь.

Существуют различные фреймворки планирования и рассуждения LLM, которое может использоваться при разработке агента. Например — это цепочка мыслей (Chain of Thoughts), дерево мыслей (Tree of thoughts) или ReAct.

Давай кратко рассмотрим каждый из них.

Chain of Thoughts (CoT)

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

Схема Chain of Thoughts
Схема Chain of Thoughts

Наиболее простой способ использования CoT является добавления в промпт надписи:«Let„s think step by step“, или „Давайте думать шаг за шагом“.

Thoughts of Tree (ToT)

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

Схема Thoughts of Tree
Схема Thoughts of Tree

Reasoning and Acting (ReAct)

Данный подход основывается на двух операциях агента: «действие» и «рассуждение». Чем‑то этот подход может напоминать Chain of Thoughts, но он улучшен тем, что на каждом шаге обновляет свои знания, на основе взаимодействия с внешней средой (например, используя доступные инструменты), и уже на основе текущих знаний решает что нужно делать дальше.

Схема ReAct
Схема ReAct

Рассмотрим пример реализации ReAct агента, с использованием библиотеки LangGraph:

from langgraph.prebuilt import create_react_agent
from langchain_core.tools import tool
from langchain_gigachat.chat_models import GigaChat
from langgraph.checkpoint.memory import InMemorySaver
from langchain_community.tools import TavilySearchResults

# Память агента
checkpointer = InMemorySaver()

# Инициализируем GigaChat
giga = GigaChat(
    credentials=GIGACHAT_API_KEY,
    verify_ssl_certs=False,
    model = "GigaChat-Pro")

# Инструмент для поиска в интернете 
tavily_search = TavilySearchResults(
        tavily_api_key=TAVILY_API_KEY,
        max_results=2,
    )

# Инициализация ReAct агента
agent = create_react_agent(
    model=giga,  
    tools=[tavily_search],  
    prompt="Ты полезный асситент",
    checkpointer=checkpointer,
    debug=True, 
)

# Конфиг для поддержки работы памяти
config = {"configurable": {"thread_id": "1"}}


# Вызов агента
msc_response = agent.invoke(
    {"messages": [{"role": "user", "content": "Какая погода в Москве?"}]},
    config  
)

# Повторный вызов агента (агент хранит в памяти прошлый запрос)
sp_response = agent.invoke(
    {"messages": [{"role": "user", "content": "Что насчет Санкт-Петербурга?"}]},
    config
)

Заключение

В этой статье было рассмотрено понятие агента и его основные компоненты. Данная информация не является исчерпывающей, но на основе нее можно изучить те моменты, которые вам показались интересными, или обратить внимание на те вещи, которые наиболее критичны в вашем проекте. 

Подписывайтесь на мой телеграмм канал, в котором я рассказываю интересные вещи об IT и AI технологиях.

Комментарии (2)


  1. 010011011000101110101
    29.09.2025 03:19

    чем содержательнее статья, тем меньше под ней комментариев :-)))


    1. Paczuk
      29.09.2025 03:19

      А если и есть комментарии, то совсем бессодержательные