Files
dz/solutions/69a86305c46fd26feae6bcaa_Human-in-the-Loop_через_middleware/solution.py
T

157 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
solution.py
Пример использования HumanInTheLoopMiddleware в LangChain Agent.
При каждом вызове инструмента агент останавливается и запрашивает у пользователя подтверждение
(approve / reject). После принятия решения выполнение возобновляется через Command.
Требования:
pip install langchain langgraph openai rich
"""
import json
from typing import Any, Dict, List
# --------------------------------------------------------------------------- #
# 1. Подключаем необходимые модули LangChain и LangGraph
# --------------------------------------------------------------------------- #
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import Command
from langchain.tools import tool
# --------------------------------------------------------------------------- #
# 2. Определяем простой инструмент get_weather
# --------------------------------------------------------------------------- #
@tool("get_weather", "Получить погоду в указанном городе и дате")
def get_weather(city: str, date: str) -> str:
"""
Возвращает фиктивную информацию о погоде.
В реальном проекте здесь можно подключиться к API погоды.
"""
return f"Погода в {city} на {date}: солнечно, 25°C."
# --------------------------------------------------------------------------- #
# 3. Создаём LLM (используем OpenAI GPT-4o-mini как пример)
# --------------------------------------------------------------------------- #
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0,
)
# --------------------------------------------------------------------------- #
# 4. Настраиваем агент с HumanInTheLoopMiddleware
# --------------------------------------------------------------------------- #
memory = MemorySaver()
agent = create_agent(
model=llm,
tools=[get_weather],
system_prompt="Ты полезный ассистент, помогающий пользователю.",
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={
"get_weather": True, # разрешаем все решения
},
description_prefix="Подтвердите вызов инструмента",
),
],
checkpointer=memory,
)
# --------------------------------------------------------------------------- #
# 5. Функция для получения решений от пользователя
# --------------------------------------------------------------------------- #
def ask_decisions(action_requests: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Для каждого запроса к инструменту выводим информацию и запрашиваем у пользователя решение.
Возвращаем список словарей с решениями в том же порядке, что и action_requests.
"""
decisions = []
print("\n--- Подтверждение вызова инструмента ---")
for idx, req in enumerate(action_requests):
name = req.get("name", "неизвестный инструмент")
args = req.get("args", {})
description = req.get("description", "")
print(f"\n{idx + 1}. Инструмент: {name}")
if description:
print(f" Описание: {description}")
print(f" Аргументы: {json.dumps(args, ensure_ascii=False)}")
while True:
choice = input("a = approve, r = reject: ").strip().lower()
if choice == "a":
decisions.append({"type": "approve"})
break
elif choice == "r":
msg = input(
"Введите причину отказа (можно оставить пустой): "
).strip()
decisions.append({"type": "reject", "message": msg})
break
else:
print("Неверный ввод. Пожалуйста, введите 'a' или 'r'.")
return decisions
# --------------------------------------------------------------------------- #
# 6. Основной цикл взаимодействия с агентом
# --------------------------------------------------------------------------- #
def main() -> None:
"""
Запускает чат-цикл: пользователь вводит сообщение,
агент обрабатывает его, при необходимости запрашивает подтверждение.
После завершения выводится финальный ответ агента.
"""
thread_id = "session-1"
config = {"configurable": {"thread_id": thread_id}}
print("Привет! Я ассистент. Введите ваш запрос (или 'выход' для завершения).")
while True:
user_msg = input("\nВы: ").strip()
if user_msg.lower() in ("выход", "quit", "exit"):
print("До свидания!")
break
# Первый вызов агента
result = agent.invoke(
{"messages": [{"role": "human", "content": user_msg}]},
config=config,
)
# Цикл подтверждений
while "__interrupt__" in result:
interrupt_value = result["__interrupt__"][0].value
action_requests = interrupt_value.get("action_requests", [])
# review_configs не используется в этом примере, но можно вывести при желании
decisions = ask_decisions(action_requests)
# Возобновляем выполнение агента с решениями
result = agent.invoke(
Command(resume={"decisions": decisions}),
config=config,
)
# После завершения выводим ответ агента
if "messages" in result and result["messages"]:
last_msg = result["messages"][-1]
print(f"\nАгент: {last_msg.get('content', '')}")
else:
print("\nАгент не вернул ответа.")
if __name__ == "__main__":
main()