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

91 lines
3.5 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.
# solution.py
"""
Пример агента с Human-in-the-Loop через middleware.
При каждом вызове инструмента агент останавливается,
выводит информацию и ожидает решения пользователя:
approve / reject (с возможностью указать причину отказа).
"""
from langchain import ChatOpenAI
from langchain.tools import tool
from langgraph.checkpoint.memory import MemorySaver
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain.agents import create_react_agent
from langgraph.types import Command
# 1. Модель LLM (замените на нужную модель и укажите ключ API)
llm = ChatOpenAI(
model="gpt-4o-mini", # пример модели, можно заменить
temperature=0.7,
)
# 2. Простой инструмент
@tool
def get_weather(city: str, date: str) -> str:
"""Возвращает погоду в городе на указанную дату."""
return f"Погода в {city} на {date}: солнечно 25°C."
# 3. Хранилище памяти для чекпоинтера
memory = MemorySaver()
# 4. Создание агента с HumanInTheLoopMiddleware
agent = create_react_agent(
tools=[get_weather],
llm=llm,
system_prompt="Ты полезный ассистент, отвечай точно.",
checkpointer=memory,
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={"get_weather": True}, # прерывать на каждом вызове
description_prefix="Подтвердите вызов инструмента",
),
],
)
def run_agent():
"""
Запускает агента, обрабатывает прерывания и выводит финальный ответ.
"""
config = {"configurable": {"thread_id": "session-1"}}
# Инициализируем запрос
result = agent.invoke(
{"messages": [{"role": "human", "content": "Какая погода в Казани сегодня?"}]},
config=config,
)
# Обрабатываем прерывания до тех пор, пока они не исчезнут
while "__interrupt__" in result:
interrupt_payload = result["__interrupt__"][0].value
action_requests = interrupt_payload.get("action_requests", [])
review_configs = interrupt_payload.get("review_configs", {})
decisions = []
for req in action_requests:
name = req.get("name")
args = req.get("args", {})
print("\nИнструмент:", name)
print("Аргументы:", args)
allowed = review_configs.get(name, {}).get(
"allowed_decisions", ["approve", "reject"]
)
print("Разрешённые решения:", allowed)
choice = input("a=approve, r=reject: ").strip().lower()
if choice == "r":
msg = input("Причина отказа: ")
decisions.append({"type": "reject", "message": msg})
else:
decisions.append({"type": "approve"})
# Возобновляем выполнение агента
result = agent.invoke(Command(resume={"decisions": decisions}), config=config)
# Финальный ответ агента
final_answer = result["messages"][-1].content
print("\nОтвет агента:\n", final_answer)
if __name__ == "__main__":
run_agent()