Типы сообщений LangChain

В LangChain все разговоры передаются через объекты Message. Понимание назначения различных типов сообщений — основа для написания агентов.


Четыре основных типа сообщений

LangChain определяет четыре основных типа сообщений, которые соответствуют различным ролям в разговоре:

TypeРольОписаниеТипичное содержимое
HumanMessageПользовательСообщение, отправленное пользователем"Какая сегодня погода?"
AIMessageИИ-ассистентОтвет модели, может включать tool_calls"Сегодня в Ханчжоу солнечно, 25°C"
SystemMessageСистемаСистемная инструкция, определяющая роль и правила поведения ИИ"Вы — профессиональный ассистент по погоде"
ToolMessageИнструментВозврат результата после выполнения инструмента"Солнечно, 25°C, влажность 60%"

HumanMessage — сообщение пользователя

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

Пример

from langchain.messages import HumanMessage
from langchain.chat_models import init_chat_model

# Создать сообщение пользователя
msg = HumanMessage(content="Что такое Python?")

print(f"Тип: {msg.type}")        # human
print(f"Содержимое: {msg.content}")      # Что такое Python?
print(f"Роль: {msg.role}")         # user

# Создание списка сообщений (представляющего историю многоходового диалога)
messages = [
    HumanMessage(content="Привет"),
    HumanMessage(content="Какие курсы предлагает Example?"),
    HumanMessage(content="Подходит ли курс Python для начинающих с нуля?"),
]

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
response = model.invoke(messages)
print(f"\n"Ответ модели: {response.content}")

Вывод:

Type: human
Content: What is Python?
Role: user

Model reply: Python is a high-level, interpreted programming language known for its clear and readable syntax, widely used across many fields.
Its rich ecosystem and beginner-friendly design make it an excellent choice for those starting from zero.

Быстрые способы создания HumanMessage

При создании списков сообщений можно использовать кортежи или словари в качестве сокращений:

Пример

from langchain.messages import HumanMessage

# Способ 1: Стандартное построение
msg1 = HumanMessage(content="Hello")

# Метод 2: Сокращение через кортеж (role, content)
msg2 = ("user", "Hello")
msg3 = ("human", "Hello")

# Метод 3: Сокращение через словарь
msg4 = {"role": "user", "content": "Hello"}

# Все четыре метода эквивалентны и будут преобразованы в HumanMessage внутри Agent.
print(type(msg1))  # <class 'langchain_core.messages.human.HumanMessage'>

AIMessage — ответ ИИ

AIMessage представляет собой ответ модели. В отличие от обычного текста, AIMessage может включатьtool_calls(запросы на вызов инструментов).

Пример

from langchain.messages import AIMessage

# Обычный ответ ИИ (без вызова инструмента)
ai_msg = AIMessage(content="Example is a programming learning platform")

# Ответ AI, содержащий вызов инструмента
ai_with_tools = AIMessage(
    content="",  # При вызове инструмента content обычно пуст
    tool_calls=[
        {
            "name": "get_weather",
            "args": {"city": "Hangzhou"},
            "id": "call_abc123",
            "type": "tool_call",
        }
    ]
)

print("=== Обычное сообщение AI ===")
print(f"содержимое: {ai_msg.content}")
print(f"tool_calls: {ai_msg.tool_calls}")   # []

print("\n"=== Сообщение AI с вызовом инструмента ===")
print(f"содержимое: {ai_with_tools.content}")
print(f"tool_calls: {ai_with_tools.tool_calls}")
# [{'name': 'get_weather', 'args': {'city': 'Hangzhou'}, ...}]

Дополнительная информация для AIMessage

Пример

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash")
response = model.invoke("Расскажи о Python")

# AIMessage содержит расширенные метаданные
print(f"Содержимое: {response.content}")
print(f"ID сообщения: {response.id}")
print(f"Имя модели: {response.response_metadata.get('model_name')}")
print(f"Причина завершения: {response.response_metadata.get('finish_reason')}")

# usage_metadata содержит информацию об использовании токенов
if response.usage_metadata:
    print(f"Входные токены: {response.usage_metadata.get('input_tokens')}")
    print(f"Выходные токены: {response.usage_metadata.get('output_tokens')}")
    print(f"Всего токенов: {response.usage_metadata.get('total_tokens')}")

Вывод:

Content: Example is a free programming learning platform for beginners...
Message ID: msg_abc123def456
Model name: deepseek-v4-flash
Finish reason: stop
Input tokens: 19
Output tokens: 47
Total tokens: 66

SystemMessage — системная инструкция

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

Пример

from langchain.messages import HumanMessage, SystemMessage
from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0.7)

# Ответ без системной инструкции
messages_no_system = [HumanMessage(content="Расскажи о Python")]
response = model.invoke(messages_no_system)
print(f"Без системной инструкции: {response.content[:80]}...")

# Ответ с системной инструкцией
messages_with_system = [
    SystemMessage(content="Ты блогер в стиле Xiaohongshu; ответы должны быть живыми, используй эмодзи и добавляй тематические теги."),
    HumanMessage(content="Расскажи о Python")
]
response = model.invoke(messages_with_system)
print(f"\n"С системной инструкцией: {response.content}")

Вывод:

Without system prompt: Example is an online programming learning platform for beginners, offering rich tutorials and examples...

With system prompt: Amazing site! Recommending Example to every beginner who wants to learn programming!
Comprehensive free tutorials covering everything from HTML to Python!
Plenty of examples and quick to get started - a perfect entry point for programming novices!
#ProgrammingLearning #FreeTutorials #ZeroBasics #Example

ToolMessage — Результат возврата инструмента

ToolMessage содержит результат, возвращённый после выполнения инструмента. Он должен быть связан с соответствующим tool_call.

Пример

from langchain.messages import HumanMessage, AIMessage, ToolMessage
from langchain.chat_models import init_chat_model

# Имитировать полный раунд диалога с вызовом инструментов
messages = [
    HumanMessage(content="Какая погода в Ханчжоу?"),

    # Модель запрашивает вызов инструмента
    AIMessage(
        content="",
        tool_calls=[
            {"name": "get_weather", "args": {"city": "Ханчжоу"},
             "id": "call_abc", "type": "tool_call"}
        ]
    ),

    # Результат возврата инструмента (должен включать tool_call_id, соответствующий id выше)
    ToolMessage(
        content="Солнечно, 25°C, влажность 60%",
        tool_call_id="call_abc",   # Соответствует id из tool_call
        name="get_weather",        # Имя инструмента
    ),
]

model = init_chat_model("deepseek:deepseek-v4-flash")
response = model.invoke(messages)
print(f"Ответ модели на основе результата инструмента: {response.content}")

Вывод:

Model reply based on tool result: Hangzhou is sunny today, 25 C, humidity 60%, great for outdoor activities.

tool_call_id в ToolMessage должен точно совпадать с id tool_call в AIMessage. Если они не совпадают, модель может проигнорировать результат инструмента или выдавать сбивающее с толку поведение.


AIMessageChunk — фрагмент сообщения потокового вывода

Когда вы используете stream() для потокового вывода, каждый приходящий фрагмент — это AIMessageChunk, а не полный AIMessage:

Пример

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash")

print("Процесс потокового вывода:")
# stream() возвращает итератор AIMessageChunk
for chunk in model.stream("Опиши Python в одном предложении"):
    # Каждый фрагмент — небольшой кусочек текста
    print(chunk.content, end="", flush=True)
print()  # Перевод строки

Вывод:

Streaming output:
Example is a free online learning platform for programming beginners, offering rich tutorials and examples.

Краткая справочная таблица типов сообщений

Тип сообщенияатрибут typeатрибут roleКлючевые поляКогда использовать
HumanMessagehumanusercontentВвод пользователя
AIMessageaiassistantcontent, tool_calls, usage_metadataОтвет модели
AIMessageChunkaiassistantcontent (delta)Фрагмент потокового вывода
SystemMessagesystemsystemcontentЗадать роль ИИ
ToolMessagetooltoolcontent, tool_call_id, nameРезультат выполнения инструмента

ContentBlock — структурированное содержимое сообщения

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

Три наиболее распространённых типа блоков содержимого:

TypeОписаниеНазначение
PlainTextContentBlockСодержимое в виде обычного текстаОбычное текстовое сообщение
ImageContentBlockСодержимое изображения (base64 или URL)Ввод изображения для мультимодальных моделей
ToolCallЗапрос на вызов инструментаИИ запрашивает вызов инструмента

Пример

from langchain.messages import HumanMessage
from langchain.messages import PlainTextContentBlock, ImageContentBlock

# Создание сообщений с использованием блоков содержимого
# содержимое может быть обычной строкой (простой сценарий)
simple_msg = HumanMessage(content=Привет)

# содержимое также может быть списком ContentBlock (сложный сценарий)
complex_msg = HumanMessage(content=[
    PlainTextContentBlock(text="Что на этой картинке?"),
    # Изображение может быть URL-адресом или в кодировке base64
    ImageContentBlock(
        url="https://example.com/photo.jpg"
    ),
])

print(f"Тип содержимого простого сообщения: {type(simple_msg.content)}")
# Вывод: <class 'str'>

print(f"Тип содержимого сложного сообщения: {type(complex_msg.content)}")
# Вывод: <class 'list'>

print(f"Количество блоков содержимого: {len(complex_msg.content)}")
# Вывод: 2

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


Мультимодальные сообщения — позволяем модели "видеть" изображения

Если ваша модель поддерживает мультимодальный ввод (например, GPT-4o, Claude 3+), вы можете позволить ей анализировать содержимое изображения:

Пример

import base64
from pathlib import Path
from langchain.messages import HumanMessage
from langchain.chat_models import init_chat_model

# Закодировать локальное изображение в base64
def encode_image(image_path: str) -> str:
    Прочитать файл изображения и преобразовать в кодировку base64
    with open(image_path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

# Создание мультимодальной модели (должна поддерживать ввод изображений)
model = init_chat_model("deepseek:deepseek-v4-flash")

# Формирование сообщения, содержащего изображение
# Примечание: content использует формат списка, содержащий текстовые блоки и блоки изображений
image_data = encode_image("screenshot.png")
messages = [
    HumanMessage(content=[
        {"type": "text", "text": Опишите содержимое этого скриншота официального сайта Python.},
        {
            "type": "image_url",
            "image_url": {
                "url": f"data:image/png;base64,{image_data}",
                "detail": "auto"  # Необязательно: low, high, auto
            }
        }
    ])
]

response = model.invoke(messages)
print(fРезультат анализа изображения: {response.content})

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

Пример

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash")

# Использование URL для прямой ссылки на изображение из интернета
messages = [
    {
        "role": "user",
        "content": [
            {"type": "text", "text": Что описывает это изображение?},
            {
                "type": "image_url",
                "image_url": {"url": "https://www.example.com/wp-content/uploads/2024/01/logo.png"}
            }
        ]
    }
]

response = model.invoke(messages)
print(response.content)

Не все модели поддерживают мультимодальный ввод. Серия GPT-4o от OpenAI, Claude 3+ от Anthropic и серия Gemini от Google поддерживают его. Если вы отправите изображение с использованием неподдерживаемой модели, вы получите ошибку.


ToolCall — сообщение вызова инструмента

Поле tool_calls в AIMessage — это список объектов ToolCall, где каждый ToolCall представляет запрос модели на вызов инструмента:

Пример

from langchain.messages import AIMessage
from langchain.messages.tool import ToolCall

# Создать ToolCall вручную
tool_call = ToolCall(
    name="get_weather",        # Имя инструмента
    args={"city": Hangzhou},     # Аргументы вызова
    id="call_abc123",         # Уникальный идентификатор
    type="tool_call",         # Фиксированное значение
)

# Создать AIMessage, содержащий tool_calls
ai_message = AIMessage(
    content="",               # Когда tool_calls присутствует, content обычно пустой
    tool_calls=[tool_call],
)

print(fИмя инструмента: {ai_message.tool_calls)
print(fПараметры вызова: {ai_message.tool_calls)
print(fID вызова: {ai_message.tool_calls)

Проверка, содержит ли AIMessage вызовы инструментов

Пример

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

# После привязки инструментов модель может вернуть tool_calls
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": Запрос погоды,
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": Название города}
            },
            "required": ["city"]
        }
    }
}]

model_with_tools = model.bind_tools(tools)

# Вопрос, инициирующий вызов инструмента
response = model_with_tools.invoke(Какая погода в Ханчжоу?)

# Два способа определить, есть ли вызов инструмента
# Способ 1: Проверить, что список tool_calls не пуст
if response.tool_calls:
    print(Модель запрашивает вызов инструмента)

# Способ 2: Проверить, пусто ли content
# (Для большинства моделей content пуст, когда присутствует tool_calls)
if not response.content:
    print(Содержимое модели пусто, что указывает на то, что она хочет вызвать инструмент, а не ответить напрямую.)

trim_messages() — Обрезка истории сообщений

Когда разговор становится длиннее, список сообщений может превысить контекстное окно модели.trim_messages()Функция помогает интеллектуально обрезать историю сообщений.

Пример

from langchain.messages import (
    HumanMessage, AIMessage, SystemMessage, trim_messages
)
from langchain.chat_models import init_chat_model

# Имитировать длинную историю разговора
messages = [
    SystemMessage(content=Вы — ИИ-ассистент учебника EXAMPLE.),
    HumanMessage(content=Как начать работу с Python?),
    AIMessage(content=Начало работы с Python может начинаться с базовых знаний...),
    HumanMessage(content=Есть ли рекомендуемые IDE?),
    AIMessage(content=Рекомендую VS Code или PyCharm...),
    HumanMessage(content=Как установить сторонние библиотеки?),
    AIMessage(content=Используйте команду pip install...),
    HumanMessage(content=Что такое NumPy?),
    AIMessage(content=NumPy — это библиотека для научных вычислений...),
    HumanMessage(content=В чём разница между pandas и NumPy?),
]

model = init_chat_model("deepseek:deepseek-v4-flash")

# Обрезать сообщения, чтобы уместить их в контекстное окно модели (не более 1000 токенов)
# strategy="last" сохраняет последнее системное сообщение и самый недавний диалог
trimmed = trim_messages(
    messages,
    max_tokens=1000,           # Хранить не более 1000 токенов
    strategy="last",           # Сохранять последнее системное сообщение + самые последние сообщения диалога
    token_counter=model,       # Использовать метод подсчёта токенов модели
    include_system=True,       # Всегда сохранять SystemMessage
    start_on="human",          # После обрезки начинать с сообщения человека
)

print(fПеред обрезкой: {len(messages)} сообщений)
print(fПосле обрезки: {len(trimmed)} сообщений)
for msg in trimmed:
    snippet = msg.content[:50] if isinstance(msg.content, str) else str(msg.content)[:50]
    print(f" [{msg.type}] {snippet}...")

Результат выполнения:

Before trimming: 10 messages
After trimming: 5 messages
  [system] You are the AI assistant of Example...
  [human] How do I install third-party libraries?...
  [ai] Use the pip install command...
  [human] What is NumPy?...
  [ai] NumPy is a scientific computing library...
СтратегияОписаниеПрименимые сценарии
strategy="last"Сохранить системное сообщение + последнюю часть диалогаВ длинных диалогах важен только последний контекст
strategy="first"Сохранить системное сообщение + начало диалогаУбедиться, что ключевой контекст не обрезан

start_on="human" гарантирует, что обрезанный список сообщений начинается с сообщения пользователя (а не с сообщения ИИ), не позволяя модели получить список, который начинается с отдельного ответа ИИ.


RemoveMessage — Удаление конкретных сообщений

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

Пример

from langchain.messages import HumanMessage, AIMessage, RemoveMessage

# Предположим, есть диалог
messages = [
    HumanMessage(content=Привет, id="msg_1"),
    AIMessage(content=Здравствуйте! Чем я могу вам помочь?, id="msg_2"),
    HumanMessage(content=Проверь погоду для меня, id="msg_3"),
]

# Используйте RemoveMessage для удаления конкретного сообщения (по ID)
# RemoveMessage используется с редьюсером add_messages
# При обновлении состояния Agent RemoveMessage удаляет сообщение с соответствующим ID из списка
removal = RemoveMessage(id="msg_3")

print(fID сообщения для удаления: {removal.id})
print(fТип: {removal.type})  # удалить

RemoveMessage обычно используется вместе с редьюсером add_messages из AgentState. Возврат RemoveMessage в middleware или хуках after_model позволяет динамически очищать историю сообщений.


Общие методы для свойств сообщения

Все типы сообщений наследуются от BaseMessage и имеют некоторые общие методы:

Пример

from langchain.messages import HumanMessage, AIMessage

msg = HumanMessage(content=Привет, Example)

# Базовые атрибуты
print(f"content: {msg.content}")      # Содержимое сообщения
print(f"type: {msg.type}")            # Тип сообщения (human/ai/system/tool)
print(f"id: {msg.id}")                # Автоматически сгенерированный уникальный ID

# Атрибут text: если это текстовое содержимое, вернуть текст; иначе вернуть ""
print(f"text: {msg.text}")

# pretty_repr(): форматированный вывод, подходящий для отладки
print(f"Форматированный вывод:\n{msg.pretty_repr()}")

Результат выполнения:

content: Hello, world
type: human
id: 00000000-0000-4000-8000-000000000000
text: Hello, world
Pretty output:
================================ Human Message =================================
Hello, world
Другие расширения