LangChain

LangChain create_agent и цикл агента | Курс LangChain Agents урок 1

LangChain create_agent и цикл агента | Курс LangChain Agents урок 1
Mikhail
Автор
Mikhail
Опубликовано 23.03.2026
0,0
Views 6

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


Теория

Чем агент отличается от цепочки

В первом курсе вы строили цепочки: prompt | model | parser. Цепочка выполняется линейно, шаги заданы заранее и она не принимает решений. Запустили, получили результат.

Агент устроен иначе. У него есть цель и набор инструментов и он сам решает, что делать на каждом шаге.

Выглядит так:

Запрос → Модель думает → Вызвать инструмент? → Да → Инструмент выполняется
                                                ↑              ↓
                                          Результат ←←←←←←←←←←
                                                ↓
                                           Нет → Ответ пользователю

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

Это называется циклом ReAct: Reasoning (рассуждение) и Acting (действие).

Почему цепочки перестают справляться

Представьте задачу: "найди последние новости про компанию X и сделай краткое резюме". Цепочка не может её решить, потому что заранее неизвестно:

  • нужен ли поиск (может, информация уже в промпте)
  • сколько раз надо искать
  • какие именно запросы делать

Агент решает это, в этом его смысл.

create_agent

LangChain create_agent - это высокоуровневый API LangChain v1+, который строит агента поверх LangGraph. Внутри граф состояния с двумя основными узлами: model и tools. Подробнее граф разберём в уроке 2, сейчас нам важно уметь им пользоваться.

Минимальный вызов:

from langchain.agents import create_agent
from langchain_openai import ChatOpenAI

model = ChatOpenAI(model="gpt-4o-mini")

agent = create_agent(
    model=model,
    tools=[],
)

Три ключевых параметра:

  • model - экземпляр класса модели или строка в формате "provider:model"
  • tools - список инструментов, доступных агенту
  • prompt - системный промпт (опционально)

Почему ChatOpenAI, а не строка?

create_agent поддерживает строковые имена вида "openai:gpt-4o-mini", но только для стандартных провайдеров. Если вы используете кастомный эндпоинт через OPENAI_BASE_URL (прокси, локальная модель), строка не сработает. LangChain попытается определить провайдера по имени и произойдет ошибка. ChatOpenAI автоматически подхватывает OPENAI_API_KEY и OPENAI_BASE_URL из окружения и передаёт имя модели.

Инструменты

Инструмент - это функция Python с декоратором @tool. Описание в docstring важно, именно его читает модель, когда решает, какой инструмент вызвать.

from langchain.tools import tool

@tool
def get_weather(city: str) -> str:
    """Возвращает текущую погоду для указанного города."""
    return f"В городе {city} сейчас 18°C, облачно."

Хорошее описание отвечает на вопрос: "когда нужно вызвать этот инструмент?"

Как запустить агента

Агент принимает словарь с ключом messages:

response = agent.invoke({
    "messages": [{"role": "user", "content": "Какая погода в Москве?"}]
})

print(response["messages"][-1].content)

response["messages"] - это полная история: запрос пользователя, решение о вызове инструмента, результат инструмента, финальный ответ. Последний элемент это ответ агента.


Примеры кода

Минимальный агент

import os
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI

load_dotenv()

model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o-mini"))

agent = create_agent(
    model=model,
    tools=[],
)

response = agent.invoke({
    "messages": [{"role": "user", "content": "Сколько будет 17 умножить на 43?"}]
})

print(response["messages"][-1].content)
# Вывод: 17 × 43 = 731

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

Агент с инструментом

import os
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI

load_dotenv()

@tool
def multiply(a: int, b: int) -> int:
    """Умножает два числа. Используй, когда нужно посчитать произведение."""
    return a * b

model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o-mini"))

agent = create_agent(
    model=model,
    tools=[multiply],
)

response = agent.invoke({
    "messages": [{"role": "user", "content": "Сколько будет 17 умножить на 43?"}]
})

print(response["messages"][-1].content)
# Вывод: 17 × 43 = 731

На первый взгляд результат тот же. Разница видна в LangSmith: в первом случае один узел (model), во втором - model → tools → model.

Сквозной проект: базовый агент-аналитик

Создаём агента с двумя инструментами: поиск информации и чтение файла. Пока инструменты заглушки, в уроке 3 подключим реальные через MCP.

import os
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI

load_dotenv()

@tool
def search_web(query: str) -> str:
    """Ищет информацию в интернете по запросу. Используй для поиска актуальных
    данных, новостей, фактов о компаниях, событиях и людях."""
    # Заглушка. В уроке 3 заменим на реальный MCP.
    return f"[Результаты поиска по запросу '{query}']: найдено 5 источников."

@tool
def read_file(path: str) -> str:
    """Читает содержимое текстового файла по указанному пути. Используй, когда
    нужно проанализировать локальный документ."""
    try:
        with open(path, "r", encoding="utf-8") as f:
            return f.read()
    except FileNotFoundError:
        return f"Файл '{path}' не найден."

SYSTEM_PROMPT = """Ты аналитик-исследователь. Твоя задача - собирать информацию
по запросу пользователя, используя доступные инструменты, и давать структурированный
ответ с выводами.

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

model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o-mini"))

agent = create_agent(
    model=model,
    tools=[search_web, read_file],
    system_prompt=SYSTEM_PROMPT,
)

response = agent.invoke({
    "messages": [{
        "role": "user",
        "content": "Найди информацию о компании Anthropic: чем занимается и когда основана."
    }]
})

print(response["messages"][-1].content)

Запустите и откройте LangSmith. Вы увидите каждый шаг: какой инструмент агент решил вызвать, с какими аргументами, что получил в ответ.


Частые ошибки

Агент отвечает без вызова инструмента, хотя должен был его вызвать

# Плохое описание
@tool
def search(q: str) -> str:
    """Поиск."""  # слишком коротко
    ...

# Хорошее описание
@tool
def search(query: str) -> str:
    """Ищет актуальную информацию в интернете по текстовому запросу.
    Используй для получения фактов, новостей, данных о компаниях и событиях."""
    ...

Модель выбирает инструмент по описанию. Если описание слишком короткое или неясное, модель решит ответить из собственных знаний.


Ответ агента не там, где ищете

response = agent.invoke({"messages": [...]})

# Неправильно: response - это словарь, не строка
print(response)  # {'messages': [HumanMessage(...), AIMessage(...), ...]}

# Правильно: берём последнее сообщение
print(response["messages"][-1].content)

response - словарь с историей сообщений.
Финальный ответ всегда последний элемент в списке messages.


Модель не поддерживает tool calling

BadRequestError: This model does not support tool use.

Агенты требуют модели с поддержкой tool calling. Большинство современных моделей поддерживают это, но старые или маленькие модели могут не поддерживать. Используйте gpt-4o-mini, claude-haiku-4-5 или аналоги.


Агент зациклился

Если агент попал в петлю (вызывает инструменты снова и снова), в LangSmith вы увидите длинную цепочку повторяющихся вызовов. По умолчанию create_agent ограничивает количество итераций. Если лимит достигнут, агент вернёт последнее состояние с сообщением об ошибке.

Причина обычно в системном промпте или инструментах: агент не понимает, когда задача выполнена. Добавьте в промпт явное условие завершения.


Задание

Создайте агента для конвертации валют. У агента должны быть два инструмента:

  1. get_rate(from_currency: str, to_currency: str) -> float - возвращает курс обмена (можно использовать фиксированные значения как заглушку)
  2. convert(amount: float, rate: float) -> float - умножает сумму на курс

Задайте системный промпт, объясняющий агенту его роль.

Запросите агента: "Сколько рублей я получу за 100 долларов?".

Ожидаемое поведение: агент вызовет get_rate("USD", "RUB"), затем convert(100, <курс>), затем ответит конкретной суммой.

Откройте трейс в LangSmith и проверьте, что оба инструмента были вызваны в правильном порядке.

<< урок 0

урок 2 >>


Подписывайтесь на мой Telegram канал

Если вам нужен ментор и вы хотите научиться разрабатывать AI агентов, пишите, обсудим условия

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

Комментариев: 0

Нет комментариев.

Тут может быть ваша реклама

Пишите info@aisferaic.ru

Похожие туториалы