From b6803c4957accedca7c1dc0279ded9ce1494be59 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 10 Apr 2026 22:16:27 +0000 Subject: [PATCH] interuption --- agent_interupt.py | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 agent_interupt.py diff --git a/agent_interupt.py b/agent_interupt.py new file mode 100644 index 0000000..531d9ef --- /dev/null +++ b/agent_interupt.py @@ -0,0 +1,86 @@ +import sys +import asyncio +from dotenv import load_dotenv +from langchain_google_genai import ChatGoogleGenerativeAI +from langchain_mcp_adapters.client import MultiServerMCPClient + +# ✅ LE BON IMPORT POUR LANGGRAPH V1.0+ (et futur v2.0) +from langgraph.prebuilt import create_react_agent +from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver +from langchain_core.messages import HumanMessage +from langchain_core.messages import ToolMessage + +load_dotenv() + +async def lancer_agent_moderne(): + llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash") + + # Notre configuration MCP habituelle + client = MultiServerMCPClient({ + "serveur_taches": { + "command": sys.executable, + "args": ["serveur_mcp.py"], + "transport": "stdio" + } + }) + outils = await client.get_tools() + + async with AsyncSqliteSaver.from_conn_string("memoire_agent.db") as checkpointer: + + # 🚀 L'approche moderne : + # On définit l'agent directement via le constructeur de LangGraph. + # Plus besoin de 'AgentExecutor', LangGraph gère la boucle lui-même. + agent = create_react_agent( + llm, + tools=outils, + checkpointer=checkpointer, + interrupt_before=["tools"] # Notre garde-fou + ) + + + config = {"configurable": {"thread_id": "session_moderne_001"}} + + # --- ÉTAPE 1 : L'Agent propose une action --- + consigne = "Ajoute la tâche 'Apprendre LangGraph' à ma liste." + print(f"\n🗣️ ORDRE : {consigne}") + + async for etape in agent.astream( + {"messages": [HumanMessage(content=consigne)]}, + config=config, + stream_mode="values" + ): + etape["messages"][-1].pretty_print() + + # --- ÉTAPE 2 : Vérification de l'état --- + etat_actuel = await agent.aget_state(config) + + if etat_actuel.next: + print(f"\n⚠️ ACTION EN ATTENTE : L'agent veut utiliser l'outil : {etat_actuel.next}") + confirmation = input("👉 Voulez-vous autoriser cette action ? (oui/non) : ") + + if confirmation.lower() == "oui": + # --- ÉTAPE 3 : On relance l'agent là où il s'est arrêté --- + print("🚀 Validation reçue. Exécution en cours...") + async for etape in agent.astream(None, config=config, stream_mode="values"): + etape["messages"][-1].pretty_print() + else: + print("❌ Action annulée par l'utilisateur.") + # On crée un message de résultat d'outil manuel pour clore la boucle + outil_id = etape["messages"][-1].tool_calls[0]["id"] + message_annulation = ToolMessage( + tool_call_id=outil_id, + content="L'utilisateur a refusé cette action. Propose une alternative ou arrête-toi là." + ) + + # On met à jour l'état de l'agent avec ce message de "faux" résultat + await agent.aupdate_state(config, {"messages": [message_annulation]}) + + # On relance pour que l'IA réagisse à l'annulation + async for etape in agent.astream(None, config=config, stream_mode="values"): + etape["messages"][-1].pretty_print() + + else: + print("\n✅ L'agent a terminé sans avoir besoin de validation.") + +if __name__ == "__main__": + asyncio.run(lancer_agent_moderne()) \ No newline at end of file