File size: 6,241 Bytes
13c0411 c19d9d0 13c0411 c19d9d0 13c0411 c19d9d0 13c0411 c19d9d0 13c0411 c19d9d0 13c0411 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | """REPL minimal de Q&A NL → JeuxDeMots.
Usage :
python -m jdm_agent.apps.qa_cli
python -m jdm_agent.apps.qa_cli --question "synonymes de voiture"
python -m jdm_agent.apps.qa_cli --provider ollama --model llama3.1:8b
Variables d'environnement (alternatives aux flags) :
LLM_PROVIDER, LLM_MODEL, LLM_TEMPERATURE
ANTHROPIC_API_KEY, OPENAI_API_KEY (selon provider)
"""
from __future__ import annotations
from jdm_agent.apps import _console # noqa: F401 — force stdout UTF-8 (Windows)
import argparse
import os
import sys
from typing import Optional
from jdm_agent.client import JDMClient
from jdm_agent.tools.jdm_agent import ask, build_jdm_agent, stream
from jdm_agent.tools.llm_factory import get_llm
BANNER = """
╔══════════════════════════════════════════════════════════════╗
║ JDM Q&A — réponses ancrées dans le graphe JeuxDeMots ║
║ Tape une question (en français) ou 'exit' pour quitter. ║
║ '/help' pour l'aide, '/tools' pour lister les outils. ║
╚══════════════════════════════════════════════════════════════╝
"""
HELP = """
Commandes :
/help cette aide
/tools liste les outils JDM disponibles
/verbose on|off active/désactive la trace des appels d'outils
/clear efface l'écran
exit | quit quitter
Exemples de questions :
- Qu'est-ce qu'un chat ?
- Donne-moi des synonymes de voiture.
- Quel est le rapport entre chat et internet ?
- Le mot "avocat" a-t-il plusieurs sens ?
- Quelle est la couleur du sang d'après JDM ?
"""
def _print_tool_calls(tool_calls: list[dict]) -> None:
if not tool_calls:
return
print("\n[outils appelés]")
for tc in tool_calls:
args = tc.get("args") or {}
args_str = ", ".join(f"{k}={v!r}" for k, v in args.items())
print(f" • {tc['name']}({args_str})")
def _stream_printer(verbose: bool):
"""Imprime un événement par étape de l'agent — montre que ça avance."""
import time
t0 = [time.time()]
def on_event(ev: dict) -> None:
dt = time.time() - t0[0]
kind = ev["kind"]
if kind == "AIMessage":
tcs = ev.get("tool_calls") or []
if tcs:
for tc in tcs:
args = ", ".join(f"{k}={v!r}" for k, v in (tc.get("args") or {}).items())
print(f" ⏱ {dt:5.1f}s → appel {tc['name']}({args})", flush=True)
else:
# Réponse finale du modèle.
preview = (ev.get("content") or "").strip().replace("\n", " ")[:80]
if preview:
print(f" ⏱ {dt:5.1f}s ← réponse du modèle ({len(ev['content'])} chars)", flush=True)
elif kind == "ToolMessage":
content = ev.get("content") or ""
preview = content[:100].replace("\n", " ")
print(f" ⏱ {dt:5.1f}s ← outil {ev.get('name')} renvoie {len(content)} chars : {preview}…", flush=True)
t0[0] = time.time()
return on_event if verbose else None
def run_repl(provider: Optional[str], model: Optional[str], verbose: bool) -> int:
print(BANNER)
print(f"Provider : {provider or os.environ.get('LLM_PROVIDER', 'anthropic')}")
print(f"Modèle : {model or os.environ.get('LLM_MODEL', 'claude-sonnet-4-5')}")
print("Chargement du client JDM et du modèle…")
client = JDMClient()
try:
llm = get_llm(provider=provider, model=model)
agent = build_jdm_agent(client=client, llm=llm)
except Exception as e:
print(f"\n[erreur] impossible d'initialiser le modèle: {e}", file=sys.stderr)
return 2
print("Prêt.\n")
show_tools = verbose
while True:
try:
q = input("> ").strip()
except (EOFError, KeyboardInterrupt):
print()
break
if not q:
continue
if q.lower() in {"exit", "quit"}:
break
if q == "/help":
print(HELP)
continue
if q == "/clear":
os.system("cls" if os.name == "nt" else "clear")
continue
if q.startswith("/verbose"):
show_tools = "on" in q
print(f"verbose = {show_tools}")
continue
if q == "/tools":
from jdm_agent.tools.jdm_tools import ALL_TOOLS
for t in ALL_TOOLS:
print(f" - {t.name}: {t.description.splitlines()[0]}")
continue
try:
print("(réflexion en cours…)", flush=True)
on_event = _stream_printer(show_tools)
out = stream(agent, q, on_event=on_event)
except Exception as e:
print(f"[erreur] {e}", file=sys.stderr)
continue
print()
print(out["answer"])
print()
client.close()
return 0
def main() -> int:
p = argparse.ArgumentParser(description="REPL Q&A JeuxDeMots (LangChain agent).")
p.add_argument("--provider", default=None, help="LLM provider (anthropic, openai, ollama, ...)")
p.add_argument("--model", default=None, help="Nom du modèle (claude-sonnet-4-5, gpt-4o, llama3.1:8b, ...)")
p.add_argument("--question", "-q", default=None, help="Pose une seule question puis quitte.")
p.add_argument("--verbose", action="store_true", help="Affiche les appels d'outils.")
args = p.parse_args()
if args.question:
client = JDMClient()
llm = get_llm(provider=args.provider, model=args.model)
agent = build_jdm_agent(client=client, llm=llm)
if args.verbose:
print("(réflexion en cours…)", flush=True)
out = stream(agent, args.question, on_event=_stream_printer(True))
else:
out = ask(agent, args.question)
print()
print(out["answer"])
client.close()
return 0
return run_repl(args.provider, args.model, args.verbose)
if __name__ == "__main__":
sys.exit(main())
|