Добавить solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/agent_with_memory_and_human_in_the_loop.py

This commit is contained in:
2026-05-27 08:36:16 +00:00
parent b038b62cc2
commit 7f7223f76b
@@ -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()