16 — Écrire un agent Python
Le script Python de votre agent hérite de gagent_py.Agent. Cette
classe gère le protocole de communication avec le C++ (lecture JSON sur
stdin, écriture JSON sur stdout) — vous ne vous occupez que de
la logique.
La bibliothèque gagent_py
gagent_py est incluse dans le dépôt gAgent, dans le répertoire
python/.
import sys, os
sys.path.insert(0, "/chemin/vers/dist/python")
import gagent_py
Structure d’un agent Python
class MonAgent(gagent_py.Agent):
def on_start(self):
"""Appelé une fois au démarrage."""
# self.system_prompt — prompt système configuré côté C++
# self.model — modèle LLM ("gpt-4o-mini", etc.)
# self.max_tokens — longueur max de réponse
# self.max_history — nombre de tours mémorisés
pass
def on_message(self, msg: gagent_py.ACLMessage):
"""Appelé à chaque message ACL reçu."""
return self.noop()
def on_tick(self):
"""Appelé périodiquement quand aucun message n'arrive."""
return self.noop()
def on_stop(self):
"""Appelé avant l'arrêt propre."""
pass
if __name__ == "__main__":
MonAgent().run()
Les méthodes d’action
Chaque méthode de rappel doit retourner une action :
Méthode |
Effet |
|---|---|
|
Ne rien faire |
|
Envoyer un message ACL à |
|
Répondre à |
|
Supprimer l’agent |
|
Suspendre l’agent |
|
Réveiller l’agent |
La classe ACLMessage
Attribut |
Description |
|---|---|
|
|
|
Nom de l’agent émetteur |
|
Contenu textuel du message |
|
Identifiant de la conversation |
|
Ontologie du message |
Exemple — agent avec OpenAI
import os, sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "python"))
import gagent_py
try:
from openai import OpenAI
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", ""))
except ImportError:
client = None
class AgentConseil(gagent_py.Agent):
def on_start(self):
self._history = []
mode = f"OpenAI {self.model}" if client else "mode echo"
sys.stderr.write(f"[agent] démarré en {mode}\n")
def on_message(self, msg):
if msg.performative == "cancel":
return self.delete()
if msg.performative != "request":
return self.noop()
reponse = self._demander(msg.content)
return self.reply(msg, "inform", reponse)
def on_tick(self):
return self.noop()
def _demander(self, question):
if not client:
return f"[echo] {question}"
self._history.append({"role": "user", "content": question})
# Fenêtre glissante : garder les max_history derniers tours
if len(self._history) > self.max_history * 2:
self._history = self._history[-(self.max_history * 2):]
try:
rep = client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": self.system_prompt}
] + self._history,
max_tokens=self.max_tokens,
)
texte = rep.choices[0].message.content.strip()
self._history.append({"role": "assistant", "content": texte})
return texte
except Exception as e:
self._history.pop()
return f"[erreur] {e}"
if __name__ == "__main__":
AgentConseil().run()
Exemple — agent avec Claude (Anthropic)
import anthropic, gagent_py
client = anthropic.Anthropic() # lit ANTHROPIC_API_KEY
class AgentClaude(gagent_py.Agent):
def on_message(self, msg):
if msg.performative == "cancel":
return self.delete()
if msg.performative != "request":
return self.noop()
rep = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=self.max_tokens,
system=self.system_prompt,
messages=[{"role": "user", "content": msg.content}],
)
return self.reply(msg, "inform", rep.content[0].text)
if __name__ == "__main__":
AgentClaude().run()
Exemple — agent avec Ollama (local, sans clé API)
Ollama fait tourner un LLM localement. Aucune clé API, aucun envoi de données à l’extérieur.
# Installer Ollama puis télécharger un modèle
ollama run llama3
import requests, gagent_py
def ask_ollama(prompt, model="llama3"):
r = requests.post(
"http://localhost:11434/api/generate",
json={"model": model, "prompt": prompt, "stream": False}
)
return r.json()["response"]
class AgentOllama(gagent_py.Agent):
def on_message(self, msg):
if msg.performative == "cancel":
return self.delete()
if msg.performative != "request":
return self.noop()
return self.reply(msg, "inform", ask_ollama(msg.content, self.model))
if __name__ == "__main__":
AgentOllama().run()
Astuce
Configurez model="llama3" côté C++ dans le PythonBehaviour
pour sélectionner le modèle Ollama à utiliser.
Gestion des erreurs
Quelques bonnes pratiques :
Encapsulez l’appel LLM dans un
try/exceptpour éviter qu’une erreur réseau ne tue le script.Écrivez les logs de debug sur
sys.stderr(passtdout) —stdoutest réservé au protocole JSON avec le C++.Retournez toujours une action, même en cas d’erreur (
noop()ou un message d’erreur viareply()).
def on_message(self, msg):
try:
reponse = self._appeler_llm(msg.content)
return self.reply(msg, "inform", reponse)
except Exception as e:
sys.stderr.write(f"[erreur] {e}\n")
return self.reply(msg, "failure", str(e))