Гайд · TNWS AI

Как поставить отправку сообщения LangChain-агента на одобрение человека

LangChain 1.xPythonopenai:gpt-5.5ChatGPT#LangChain#Python#AI-агенты
5 мин—

Пауза перед side-effect tool, checkpointer, thread_id и Command(resume) без автоматического одобрения отправки.

Задача и применимость

Русскоязычный запрос: «как настроить LangChain HumanInTheLoopMiddleware approve reject». Проверка официальной документации LangChain — 19 сентября 2026 года. LangChain 1.x Python с HumanInTheLoopMiddleware и LangGraph checkpointer; пример tool лишь имитирует отправку и не рассылает реальные сообщения.

HumanInTheLoopMiddleware приостанавливает вызовы инструментов согласно interrupt_on. Для возобновления нужен checkpointer и тот же thread_id. Решения approve, edit, reject, respond различаются: respond — синтетический успешный результат для ask-user, не способ отказать в side-effect вызове. Если остановлено несколько tool calls, решения подаются в том же порядке.

Порядок действий

  1. Разделите безопасные read-only операции и действие отправки в отдельные инструменты.
  2. Установите interrupt_on для инструмента отправки, разрешив approve/reject; настройте checkpointer.
  3. Запустите agent.invoke с устойчивым thread_id и сохраните информацию interrupt для человека.
  4. Покажите адресата, тему и текст до решения; при reject не выполняйте отправку.
  5. После подтверждения передайте Command(resume=...) с тем же config и проверьте журнал фактического вызова.

Пример ниже предназначен для демонстрационного окружения: установите соответствующий пакет интеграции, настройте ключ через секреты сервера и убедитесь, что модель доступна вашему аккаунту. Если доступ к указанному model ID отсутствует, выберите поддерживаемую модель со сходными возможностями и заново проверьте работу именно на ней. Ни один пример не содержит действующего API-ключа и не разрешает обход ограничений провайдера.

Готовый шаблон на Python

from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command

def send_notice(recipient: str, body: str) -> str:
    """Имитировать отправку уведомления адресату."""
    return f"Симуляция отправки для {recipient}: {body}"

agent = create_agent(model="openai:gpt-5.5", tools=[send_notice],
    checkpointer=InMemorySaver(), middleware=[
    HumanInTheLoopMiddleware(interrupt_on={"send_notice": {
        "allowed_decisions": ["approve", "reject"]}})])
config = {"configurable": {"thread_id": "review-demo-1"}}
pending = agent.invoke({"messages": [{"role": "user", "content":
    "Подготовь уведомление для test@example.com"}]}, config=config)
# После проверки pending человеком, НЕ автоматически:
# agent.invoke(Command(resume={"decisions": [{"type": "approve"}]}),
#              config=config, version="v2")

Конкретные входные и ожидаемые данные

Вход: просьба отправить сообщение на test@example.com. Ожидается пауза до вызова send_notice. При reject журнал инструмента пуст; при approve появляется строка «Симуляция отправки...». Адрес демонстрационный: письмо не отправляется. Для настоящей отправки необходимы согласованное действие пользователя, валидированный получатель и идемпотентность.

Оценивать стоит не красоту формулировки, а проверяемое выполнение задачи. Сохраните исходный текст, ID пользователя или теста и ожидаемое значение отдельно от ответа модели. Если пример выдаёт неверный факт, разберите данные на входе и состояние инструмента до изменения промпта: иначе можно замаскировать ошибку инфраструктуры удачной перефразировкой.

Диагностика и границы решения

Главная ошибка реализации — считать, что наличие middleware само по себе обеспечивает человека в контуре. Ваше приложение должно сохранить pending interrupt, показать осмысленные аргументы, получить решение от уполномоченного оператора и только затем передать Command. Если система автоматически отправляет approve при каждом interrupt, защиты фактически нет. Проверяйте, что thread_id совпадает при resume, а решение относится к конкретному ожидающему действию. Между остановкой и подтверждением данные могут измениться: для настоящей отправки показывайте окончательный адресат и текст; перед исполнением повторно проверяйте права и актуальность адреса. Не используйте respond для отрицания отправки: документация трактует его как успешный synthetic tool result; применяйте reject. В интерфейсе показывайте, как отличить «проект», «ожидает одобрения», «отклонён» и «выполнен». Дублирующий клик approve и повтор сети способны привести к повторной рассылке, поэтому side-effect tool должен иметь свой idempotency key или безопасную проверку статуса. InMemorySaver годится лишь для демо: при рестарте ожидающие решения не переживут процесс.

Контрольный эксперимент

Сценарий тестирования включает четыре ветви: approve, reject, попытка использовать чужой thread_id и повторно отправленное решение. В первом случае выполнится ровно одно действие; во втором не будет вызова; третий сервер отклонит до агента; четвёртый не создаст дубликат. На стороне модератора решение должно включать личность подтверждающего и время, но не раскрывать API credentials. Если агент предложил одновременно несколько действий, нельзя бездумно подтвердить весь пакет одним «да»: документация требует решение для каждого вызова в порядке interrupt. Для особо рискованных команд разрешайте только approve/reject, не edit, если изменение аргументов после модели невозможно проверить. Human-in-the-loop задаёт точку контроля, но полная безопасность состоит также из проверки права на конкретную операцию и самого tool.

Дополнительный проверочный сценарий

У одобрения должно быть конкретное содержание. Показывайте человеку точные аргументы side-effect tool, источник адресата и последствия исполнения; кнопка «разрешить агенту действовать» слишком широкая. При edit требуется повторная серверная валидация новых значений, иначе изменённый адресат может обойти ограничения, применённые до паузы. Если оператор отклоняет действие, передайте причину в безопасной форме, не открывая модели служебные секреты. Храните короткий журнал: ID ожидающего действия, ID оператора, решение, время, состояние исполнения. Различайте статус «одобрено» и статус «исполнено»: техническая ошибка отправки после approve требует обработки и не превращается в успешное сообщение. Проверяйте повторный approve после такой ошибки по idempotency key.

Критерии результата

  • До решения не исполняется send_notice.
  • reject оставляет side-effect журнал пустым.
  • resume использует тот же thread_id и проверенного оператора.
  • Дублирующее подтверждение не исполняет действие второй раз.

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

Типичные ошибки и что не делать

  • Автоматически подтверждать любой interrupt.
  • Использовать respond вместо reject для отказа.
  • Терять ожидающие решения при рестарте production процесса.
  • Считать middleware заменой авторизации и идемпотентности.

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

FAQ

Зачем checkpointer?

Чтобы сохранить состояние паузы для последующего resume.

Чем отказать отправке?

Решением reject.

Можно ли редактировать аргументы?

Да, если policy допускает edit, но изменения требуют отдельной проверки.

Официальные источники

Частые вопросы

Зачем checkpointer?

Чтобы сохранить состояние паузы для последующего resume.

Чем отказать отправке?

Решением reject.

Можно ли редактировать аргументы?

Да, если policy допускает edit, но изменения требуют отдельной проверки.

Читайте также

Комментарии

Пока тихо. Скажите первое слово