From 7f7223f76b861205247f726d1e35119e8ee5e51f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D1=80=D0=B8=D1=8F=20=D0=91=D0=B5=D1=80=D0=B4?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=D0=B0?= Date: Wed, 27 May 2026 08:36:16 +0000 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20solutions/69a474cdc46fd26feae69896=5F=D0=9F=D1=80?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=5F?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=5F=5F3=5F=5F=D0=9F?= =?UTF-8?q?=D0=B0=D0=BC=D1=8F=D1=82=D1=8C=5F=D0=B8=5F=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=D1=82=D0=B2=D0=B5/agent=5Fwith=5Fmemory=5Fand=5Fhuman=5Fin=5Ft?= =?UTF-8?q?he=5Floop.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent_with_memory_and_human_in_the_loop.py | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/agent_with_memory_and_human_in_the_loop.py 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()