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

114 lines
4.3 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
# -*- coding: utf-8 -*-
"""
Пример агента с Human-in-the-Loop через HumanInTheLoopMiddleware.
При каждом вызове инструмента агент останавливается,
выводит информацию и ожидает решения пользователя: approve / reject.
"""
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import Command
# 1. Модель LLM
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0,
)
# 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(
model=llm,
tools=[get_weather],
system_prompt="Ты полезный ассистент, отвечай точно.",
checkpointer=memory,
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={
"get_weather": True, # прерывать на каждом вызове get_weather
},
description_prefix="Подтвердите вызов инструмента",
),
],
)
def main() -> None:
"""
Запускает чат-цикл с human-in-the-loop:
пользователь вводит запрос, агент останавливается перед вызовом
инструмента и ждёт подтверждения (approve / reject).
"""
config = {"configurable": {"thread_id": "session-1"}}
print("Привет! Введите запрос или 'выход' для завершения.")
while True:
user_input = input("\nВы: ").strip()
if user_input.lower() in {"выход", "exit", "quit"}:
print("Завершение работы.")
break
# Первый вызов агента
result = agent.invoke(
{"messages": [{"role": "human", "content": user_input}]},
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", {})
description = req.get("description", "")
print("\n--- Подтверждение ---")
print(f"Инструмент: {name}")
if description:
print(f"Описание: {description}")
print(f"Аргументы: {args}")
# Показываем разрешённые решения из review_configs
allowed = review_configs.get(name, {}).get(
"allowed_decisions", ["approve", "reject"]
)
print(f"Разрешённые решения: {allowed}")
choice = input("a=approve, r=reject: ").strip().lower()
if choice == "r":
reason = input("Причина отказа: ").strip()
decisions.append({"type": "reject", "message": reason})
else:
decisions.append({"type": "approve"})
# Возобновляем выполнение агента с решениями
result = agent.invoke(
Command(resume={"decisions": decisions}),
config=config,
)
# Финальный ответ агента
final_answer = result["messages"][-1].content
print(f"\nАгент: {final_answer}")
if __name__ == "__main__":
main()