86 lines
3.5 KiB
Python
86 lines
3.5 KiB
Python
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()) |