Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

+1 967новых вакансий за сутки·94 749вакансий открыто сейчас·Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Новые вакансии в TelegramРаботодателям
Talanto
Вакансии
Компании7 308
РезюмеAIПодготовкаСтатьи
BoostТарифыBoost
Войти

Не нашли подходящее?

Пусть нужная вакансия найдёт вас сама.

  1. Подборка в TelegramНовые вакансии по вашему фильтру — сразу в бота.Подключить
  2. Мэтчи по резюмеЗагрузите резюме — покажем, где вы подходите лучше всего.Загрузить
  3. Сохранить фильтрНастройте поиск один раз и возвращайтесь к новым вакансиям.Настроить
Что нового на TalantoTalanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Для работодателей

Опубликуйте вакансию — отклики придут в ваш кабинет.

  1. Вакансиябесплатно
  2. Откликиконтакты бесплатно
  3. Наймэтапы и интервью

40 компаний публикуют вакансии напрямую · 4 402 кандидата открыты к предложениям

Разместить вакансию

Популярные направления

Все подборки
  • Python17 098
  • Облачные технологии13 740
  • QA вакансии3 442
  • Backend вакансии2 113
  • Инженер данных1 769
  • Продакт-менеджер1 215

Разработка

  • Android1 857
  • iOS1 692
  • Fullstack вакансии980
  • AI вакансии956
  • Frontend вакансии750
  • Mobile вакансии44

По стеку

  • Go8 640
  • Java6 540
  • Node.js2 016
  • .NET1 705
  • Kotlin1 619
  • Rust1 458
  • C#701
  • PHP681
  • C++564

DevOps и инфраструктура

  • Kubernetes7 596
  • DevOps вакансии6 045
  • Linux-инфраструктура5 418
  • Terraform / IaC4 165
  • CI/CD3 212
  • Мониторинг и Observability1 759
  • SRE564
  • Кибербезопасность430

Аналитика и данные

  • Data Scientist1 093
  • Бизнес-аналитик914
  • Аналитик данных851
  • Системный аналитик150

Другие направления

  • Проджект-менеджер570
  • UI/UX дизайн144
  • Ручное тестирование14

По регионам

  • Вакансии в США45 435
  • Вакансии в Европе23 707
  • Вакансии в Канаде4 503
  • Работа в России3 905

По формату

  • Удалённые вакансии76 073
  • Релокация в США45 435
  • Гибридная работа12 406
  • Офисная работа7 080

По уровню

  • Senior вакансии42 979
  • Middle вакансии21 322
  • Junior вакансии6 799

Сейчас нанимают

Все компании
  • Wise192 вакансии
  • Rakuten189 вакансий
  • ASML165 вакансий
  • Grab139 вакансий
  • Celonis39 вакансий
  • Spotify36 вакансий
  • Adyen29 вакансий
  • Infobip28 вакансий
  • Monzo20 вакансий
  • Darktrace17 вакансий

Вакансии из 344 источников: карьерные страницы компаний и джоб-борды.

КомпанииО насНовостиПубличная офертаУсловия использованияПолитика конфиденциальностиПоддержка
←К теме LangChainВсе вопросы
LangChainMiddleКодинг

Как реализовать историю диалога с помощью MessagesPlaceholder в LangChain?

В закладки
Сначала попробуйте ответить сами30 секунд
Сравнить с ответом ↓

MessagesPlaceholder — специальный слот в ChatPromptTemplate, который вставляет список объектов BaseMessage (историю диалога) напрямую в промпт, сохраняя тип каждого сообщения (HumanMessage, AIMessage, SystemMessage).

Базовый пример с RunnableWithMessageHistory

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

prompt = ChatPromptTemplate.from_messages([
    ("system", "Ты эксперт по {domain}. Отвечай кратко."),
    MessagesPlaceholder(variable_name="chat_history"),  # история диалога
    ("human", "{input}"),                               # текущий вопрос
])

chain = prompt | llm

store: dict[str, ChatMessageHistory] = {}

def get_session_history(session_id: str) -> ChatMessageHistory:
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]

chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="chat_history",
)

cfg = {"configurable": {"session_id": "alice"}}

reply1 = chain_with_history.invoke(
    {"input": "Что такое контекстное окно?", "domain": "LLM"},
    config=cfg,
)
print(reply1.content)

reply2 = chain_with_history.invoke(
    {"input": "А как его увеличить?", "domain": "LLM"},
    config=cfg,
)
print(reply2.content)  # Бот понимает «его» из контекста

Зачем нужен MessagesPlaceholder

MessagesPlaceholder решает проблему встраивания динамического списка сообщений в шаблон промпта. В отличие от обычного {variable}, который подставляет строку, MessagesPlaceholder принимает список объектов BaseMessage и вставляет их «как есть», сохраняя роли (human, ai, system). Это критично для истории диалога: модель должна видеть чередование реплик пользователя и ассистента, а не их конкатенацию в строку.

Что происходит внутри промпта

После заполнения шаблон превращается в список сообщений:

from langchain_core.messages import HumanMessage, AIMessage

# MessagesPlaceholder подставляет реальные объекты сообщений
formatted = prompt.format_messages(
    domain="LLM",
    chat_history=[
        HumanMessage(content="Что такое контекстное окно?"),
        AIMessage(content="Контекстное окно — максимальное количество токенов..."),
    ],
    input="А как его увеличить?",
)
# Результат: [SystemMessage, HumanMessage, AIMessage, HumanMessage]
for msg in formatted:
    print(f"{msg.type}: {msg.content[:50]}")

optional=True для пустой истории

# При первом сообщении история пустая — без optional=True
# вылетит KeyError или ValidationError
prompt = ChatPromptTemplate.from_messages([
    ("system", "Ты ассистент."),
    MessagesPlaceholder(variable_name="chat_history", optional=True),
    ("human", "{input}"),
])

# Работает даже без ключа chat_history во входных данных
result = (prompt | llm).invoke({"input": "Привет!"})

Ограничение размера истории через trim_messages

from langchain_core.messages import trim_messages
from langchain_core.runnables import RunnableLambda

trimmer = trim_messages(
    max_tokens=1000,
    strategy="last",          # оставляем последние сообщения
    token_counter=llm,        # используем токенизатор модели
    include_system=True,      # системное сообщение не удалять
    allow_partial=False,
    start_on="human",         # начинаем с реплики пользователя
)

chain = (
    RunnableLambda(lambda x: {**x, "chat_history": trimmer.invoke(x["chat_history"])})
    | prompt
    | llm
)

Подводные камни

  • Имя переменной в MessagesPlaceholder(variable_name="chat_history") должно точно совпадать с history_messages_key в RunnableWithMessageHistory — несовпадение даёт KeyError в runtime
  • Без optional=True промпт падает при пустом или отсутствующем ключе истории — всегда добавляйте этот параметр
  • История хранится как объекты BaseMessage, но Redis-сериализатор по умолчанию хранит JSON — при десериализации тип сообщения (HumanMessage vs AIMessage) должен восстанавливаться корректно
  • При использовании нескольких MessagesPlaceholder в одном промпте (например, системный контекст + диалог) порядок имеет значение — LLM интерпретирует чередование ролей строго по порядку в списке
  • Без обрезки истории (trim_messages) длинные сессии превышают context limit модели — ошибка context_length_exceeded от API, часто без понятного сообщения
  • Изменение variable_name в MessagesPlaceholder требует синхронного изменения в RunnableWithMessageHistory — это не проверяется статически
  • В многопользовательском приложении при конкурентных запросах с одним session_id история может записываться в неправильном порядке — нужна блокировка на уровне store
  • Старый API ConversationChain автоматически управлял промптом с историей, но в LCEL это ответственность разработчика — легко забыть MessagesPlaceholder и получить бота без памяти

Частые вопросы на собеседовании

  • Как работают инструменты (tools) в LangChain и как создать кастомный инструмент?
  • Что такое RAG (Retrieval-Augmented Generation) и как реализовать его с помощью LangChain?
  • В чём разница между invoke(), stream() и batch() в интерфейсе Runnable в LangChain?
  • Что такое LCEL (LangChain Expression Language) и как он использует оператор | для компоновки цепочек?

Типичные ошибки

  • Объяснять messagesplaceholder history только синтаксисом без shape, dtype, состояния или режима выполнения.
  • Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
  • Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.

Что проверяет интервьюер

  • Может ли связать messagesplaceholder history с реальным контрактом входов и выходов.
  • Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
  • Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.

Как вы знаете этот вопрос?

Проверить себя по теме LangChain →

Источники

  • https://docs.langchain.com/oss/python/langchain/short-term-memory
  • https://docs.langchain.com/oss/python/langchain/models
←Предыдущий вопросКак LangChain интегрируется с различными провайдерами LLM (OpenAI, Anthropic, локальные модели через Ollama)?Следующий вопрос→Как работает система памяти в LangChain? Какие существуют типы памяти (ConversationBufferMemory, ConversationSummaryMemory и др.)?

Связанные темы

LangChaindata-aillm-orchestrationtool-calling