diff --git a/solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/agent_with_memory_and_human_in_the_loop.py b/solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/agent_with_memory_and_human_in_the_loop.py new file mode 100644 index 0000000..9809345 --- /dev/null +++ b/solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/agent_with_memory_and_human_in_the_loop.py @@ -0,0 +1,112 @@ + +""" +Практическое задание №3: +Память и подтверждение действий + +Сценарий: +- Агент использует LangChain + HumanInTheLoopMiddleware. +- При каждом вызове инструмента агент останавливается, + выводит запрос пользователю (approve/reject) через консоль. +- История сохраняется в MemorySaver, чтобы при повторном запуске + агент «помнил» предыдущие сообщения и решения. + +Требования: +1. Подключение к LLM (ChatOpenAI). +2. Создание простого инструмента – get_weather(city: str) -> str. +3. Middleware‑подтверждение с выводом в консоль. +4. Хранение истории в MemorySaver. +5. Чат‑цикл: пользователь вводит запрос, агент отвечает, + при необходимости запрашивает подтверждение. + +""" + +import os +from typing import Dict, Any + +# ---------- 1. Подключаем LLM ---------- +from langchain_openai import ChatOpenAI +from langchain.agents import create_agent +from langchain.tools import tool +from langchain.schema import HumanMessage, AIMessage, SystemMessage +from langgraph.checkpoint.memory import MemorySaver +from langchain.agents.middleware import HumanInTheLoopMiddleware + +# Для примера используем локальный LLM (замените на свой) +os.environ["OPENAI_API_KEY"] = "fake" # нужен только для совместимости +llm = ChatOpenAI( + model="gpt-4o-mini", + base_url="https://api.openai.com/v1", # замените, если используете локальный сервер + temperature=0.7, +) + +# ---------- 2. Инструмент ---------- +@tool +def get_weather(city: str) -> str: + """ + Возвращает погоду в указанном городе. + (В реальном проекте подключитесь к API погоды) + """ + # Здесь просто пример – возвращаем фиктивный ответ + return f"Погода в {city}: ☀️ 25°C, ясное небо." + +# ---------- 3. Создаём агент ---------- +memory = MemorySaver() # хранит историю + +agent = create_agent( + model=llm, + tools=[get_weather], + system_prompt="Ты полезный ассистент, отвечай на вопросы и при вызове инструментов спрашивай подтверждение.", + middleware=[ + HumanInTheLoopMiddleware( + interrupt_on={"get_weather": True}, # останавливаемся перед каждым вызовом + description_prefix="Подтвердите вызов инструмента", + ) + ], + checkpointer=memory, # сохраняем историю в памяти +) + +# ---------- 4. Чат‑цикл ---------- +def main(): + print("🤖 Агент готов! Введите запрос (или 'exit' для выхода).") + while True: + user_input = input("\nВы: ") + if user_input.lower() in {"exit", "quit"}: + break + + # Запускаем потоковый ответ + for chunk in agent.stream( + {"messages": [{"role": "human", "content": user_input}]}, + stream_mode=["messages", "updates"], + ): + chunk_type, data = chunk + + if chunk_type == "messages": + msg: AIMessage | HumanMessage | SystemMessage = data + # выводим только текст (без tool_calls) + if hasattr(msg, "content") and msg.content: + print(msg.content, end="", flush=True) + + elif chunk_type == "updates": + # Обрабатываем вызовы инструментов и подтверждения + for update in data: + if "tool_calls" in update: + # Выводим запрос к пользователю о подтверждении + tool_name = update["tool_calls"][0]["name"] + args = update["tool_calls"][0]["args"] + print(f"\n\n⚠️ Нужно вызвать инструмент: {tool_name}({args})") + decision = input("Разрешить? (y/n): ").strip().lower() + if decision == "y": + # Продолжаем выполнение, передавая решение + agent.invoke( + {"messages": [{"role": "human", "content": user_input}]}, + config={"configurable": {"thread_id": "chat-1"}}, + ) + else: + print("⚠️ Вызов инструмента отклонён.") + # Можно завершить цикл, если не нужно продолжать + break + + print("\n\n--- Конец ответа ---") + +if __name__ == "__main__": + main()