Brak opisu

main.py 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. """Point d'entrée principal — CLI interactif et visualisation du graphe.
  2. Ce fichier est FOURNI COMPLET — ce n'est pas un exercice.
  3. Il fonctionnera dès que react_agent.py sera implémenté.
  4. Usage :
  5. uv run python main.py # mode interactif
  6. uv run python main.py --verbose # avec traces des outils
  7. uv run python main.py --ask "Prix d'un laptop ?"
  8. uv run python main.py --visualize # diagramme Mermaid
  9. """
  10. import argparse
  11. import asyncio
  12. import os
  13. import sys
  14. import uuid
  15. from pathlib import Path
  16. from dotenv import load_dotenv
  17. from langchain_core.messages import HumanMessage
  18. load_dotenv()
  19. async def chat(question: str, thread_id: str, verbose: bool = False) -> str:
  20. """Envoie une question à l'agent et retourne la réponse finale."""
  21. # Import ici pour éviter l'import au démarrage si --visualize
  22. from src.agents.react_agent import agent
  23. config = {"configurable": {"thread_id": thread_id}}
  24. initial_state = {"messages": [HumanMessage(content=question)], "iterations": 0}
  25. if verbose:
  26. step_num = 0
  27. async for event in agent.astream_events(initial_state, config=config, version="v2"):
  28. kind = event.get("event", "")
  29. if kind == "on_tool_start":
  30. step_num += 1
  31. print(f"\n 🔧 [{step_num}] {event.get('name', '?')} "
  32. f"← {event.get('data', {}).get('input', {})}")
  33. elif kind == "on_tool_end":
  34. out = str(event.get("data", {}).get("output", ""))
  35. print(f" → {out[:100]}{'...' if len(out) > 100 else ''}")
  36. final = await agent.aget_state(config)
  37. msgs = final.values.get("messages", [])
  38. return str(msgs[-1].content) if msgs else "(aucune réponse)"
  39. else:
  40. result = await agent.ainvoke(initial_state, config=config)
  41. msgs = result.get("messages", [])
  42. return str(msgs[-1].content) if msgs else "(aucune réponse)"
  43. async def interactive_loop(verbose: bool = False) -> None:
  44. """Boucle interactive. Commandes : exit | reset | verbose"""
  45. thread_id = f"session-{uuid.uuid4().hex[:8]}"
  46. show_verbose = verbose
  47. print(f"\n{'═'*55}")
  48. print(" Agent Catalogue — Formation Agents IA S1")
  49. print(f"{'═'*55}")
  50. print(f" Modèle : {os.getenv('MODEL_NAME', 'llama3.1:8b')}")
  51. print(f" Session : {thread_id}")
  52. print(f" Aide : exit | reset | verbose")
  53. print(f"{'═'*55}\n")
  54. while True:
  55. try:
  56. question = input("Vous : ").strip()
  57. except (KeyboardInterrupt, EOFError):
  58. print("\nAu revoir !")
  59. break
  60. if not question:
  61. continue
  62. if question.lower() in ("exit", "quit", "q"):
  63. print("Au revoir !")
  64. break
  65. if question.lower() == "reset":
  66. thread_id = f"session-{uuid.uuid4().hex[:8]}"
  67. print(f"Nouvelle session : {thread_id}\n")
  68. continue
  69. if question.lower() == "verbose":
  70. show_verbose = not show_verbose
  71. print(f"Verbose {'activé' if show_verbose else 'désactivé'}\n")
  72. continue
  73. print("\nAgent : ", end="", flush=True)
  74. try:
  75. response = await chat(question, thread_id, verbose=show_verbose)
  76. print(response)
  77. except NotImplementedError as e:
  78. print(f"\n[TODO] {e}")
  79. print("→ Implémentez les fonctions marquées TODO dans src/")
  80. except Exception as e:
  81. print(f"\n[Erreur] {e}")
  82. print("→ Vérifiez qu'Ollama tourne : ollama serve")
  83. print()
  84. async def ask_once(question: str, verbose: bool = False) -> None:
  85. """Pose une question unique."""
  86. thread_id = f"cli-{uuid.uuid4().hex[:8]}"
  87. print(f"\nQuestion : {question}\n{'─'*50}")
  88. try:
  89. response = await chat(question, thread_id, verbose=verbose)
  90. print(f"\n{response}\n")
  91. except NotImplementedError as e:
  92. print(f"\n[TODO] {e}")
  93. sys.exit(1)
  94. except Exception as e:
  95. print(f"\n[Erreur] {e}")
  96. sys.exit(1)
  97. def visualize_graph() -> None:
  98. """Génère le diagramme Mermaid et le sauvegarde dans docs/graphs/."""
  99. from src.agents.react_agent import agent
  100. mermaid = agent.get_graph().draw_mermaid()
  101. print(f"\n{'═'*50}\n Graphe LangGraph — Mermaid\n{'═'*50}")
  102. print(mermaid)
  103. path = Path("docs/graphs/agent_react.md")
  104. path.parent.mkdir(parents=True, exist_ok=True)
  105. path.write_text(f"# Graphe Agent ReAct\n\n```mermaid\n{mermaid}```\n", encoding="utf-8")
  106. print(f"\n✅ Sauvegardé dans {path}")
  107. print(" → Visualiser sur https://mermaid.live\n")
  108. def main() -> None:
  109. parser = argparse.ArgumentParser(description="Agent Catalogue — S1")
  110. parser.add_argument("--visualize", action="store_true")
  111. parser.add_argument("--ask", type=str, metavar="QUESTION")
  112. parser.add_argument("--verbose", action="store_true")
  113. args = parser.parse_args()
  114. if args.visualize:
  115. visualize_graph()
  116. elif args.ask:
  117. asyncio.run(ask_once(args.ask, verbose=args.verbose))
  118. else:
  119. asyncio.run(interactive_loop(verbose=args.verbose))
  120. if __name__ == "__main__":
  121. main()

Powered by TurnKey Linux.