Files

111 lines
3.9 KiB
Python
Raw Permalink 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 -*-
"""
Агент с HumanInTheLoopMiddleware: при каждом вызове инструмента
агент останавливается, пользователь подтверждает (approve/reject),
после чего выполнение возобновляется через Command.
"""
import json
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import Command
# 1. Инструмент
@tool
def get_weather(city: str, date: str = "сегодня") -> str:
"""Получить погоду в городе на указанную дату."""
return f"В городе {city} на {date}: солнечно, 25°C."
# 2. LLM
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0,
)
# 3. Память
memory = MemorySaver()
# 4. Агент с HumanInTheLoopMiddleware
agent = create_agent(
model=llm,
tools=[get_weather],
system_prompt="Ты полезный ассистент.",
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={
"get_weather": True,
},
description_prefix="Подтвердите вызов инструмента",
),
],
checkpointer=memory,
)
# 5. Сбор решений от пользователя
def get_user_decisions(action_requests: list[dict], review_configs: list[dict]) -> list[dict]:
decisions = []
for action, review_cfg in zip(action_requests, review_configs):
name = action.get("name", "unknown")
args = action.get("args", {})
description = action.get("description", "")
allowed = review_cfg.get("allowed_decisions", ["approve", "reject"])
print(f"\n--- Подтверждение ---")
print(f"Инструмент: {name}")
print(f"Аргументы: {json.dumps(args, ensure_ascii=False)}")
if description:
print(f"Описание: {description}")
print(f"Разрешённые решения: {', '.join(allowed)}")
while True:
choice = input("a=approve, r=reject: ").strip().lower()
if choice in ("a", "approve"):
decisions.append({"type": "approve"})
break
elif choice in ("r", "reject"):
msg = input("Причина отказа: ").strip()
decisions.append({
"type": "reject",
"message": msg or "Запрос отклонён пользователем",
})
break
else:
print("Неверный ввод. Введите 'a' или 'r'.")
return decisions
# 6. Основной цикл
def main() -> None:
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_value = result["__interrupt__"][0].value
action_requests = interrupt_value.get("action_requests", [])
review_configs = interrupt_value.get("review_configs", [])
decisions = get_user_decisions(action_requests, review_configs)
result = agent.invoke(
Command(resume={"decisions": decisions}),
config=config,
)
print(f"\nАгент: {result['messages'][-1].content}")
if __name__ == "__main__":
main()