Construire

Créer un agent

Recevez des messages, appelez votre propre modèle, répondez au bon endroit et gérez les tâches longues.

Tout agent Pacerelle suit la même boucle : recevoir un message, faire le travail, répondre. Le SDK s'occupe de la connexion et du chiffrement ; vous n'écrivez que la fonction de traitement.

client.onMessage(async (message, agent) => {
  const answer = await doTheWork(message.text);
  await agent.reply(message, { text: answer });
});

Si vous n'avez pas encore connecté d'agent, commencez par le démarrage rapide.

L'objet message

Votre fonction reçoit le message déchiffré et un client pour répondre.

JavaScriptPythonDescription
ididIdentifiant unique du message. Sert à répondre et comme clé d'idempotence.
conversationIdconversation_idLa conversation du message.
fromfrom_idL'expéditeur — la personne (ou l'agent) à qui répondre.
texttextLe texte du message.
attachmentsattachmentsFichiers joints. Voir Fichiers et médias.
widgetResponsewidget_responsePrésent quand le message répond à un widget.
replyToMessageIdreply_to_message_idRenseigné quand l'expéditeur répond à un message précis.

Répondre au bon endroit

Répondez toujours avec la conversation et l'expéditeur du message reçu, et indiquez son identifiant comme message auquel vous répondez. Le SDK envoie alors la réponse vers l'appareil exact qui a posé la question, même si un autre message est arrivé entre-temps.

// reply() renseigne la conversation, le destinataire et le message d'origine.
await agent.reply(message, { text: "C'est fait." });

// Équivalent, entièrement explicite :
await agent.sendMessage({
  conversationId: message.conversationId,
  to: message.from,
  replyToMessageId: message.id,
  text: "C'est fait.",
});

Vous pouvez envoyer plusieurs messages pour une même demande — un accusé rapide, puis le résultat.

Brancher un modèle

Pacerelle est indifférent au modèle utilisé. Cet exemple utilise le SDK OpenAI, qui fonctionne aussi avec tout point d'accès compatible OpenAI — y compris un serveur Ollama local. Il garde un court historique par conversation pour que le modèle garde le contexte.

agent.mjs
import OpenAI from "openai";
import {
  createNodeAgentGatewayClient,
  createNodeSignalSnapshotStore,
} from "@pacerelle/sdk/node";

// Lit OPENAI_API_KEY. Pour Ollama : new OpenAI({ baseURL: "http://localhost:11434/v1", apiKey: "ollama" })
const openai = new OpenAI();
const histories = new Map(); // conversationId → messages récents

const agentId = process.env.PACERELLE_AGENT_ID;
const state = createNodeSignalSnapshotStore(agentId);
const client = createNodeAgentGatewayClient({
  agentId,
  token: process.env.PACERELLE_AGENT_TOKEN,
  e2ee: true,
  signalSnapshot: state.signalSnapshot,
  onSignalSnapshot: state.onSignalSnapshot,
});

client.onMessage(async (message, agent) => {
  if (message.widgetResponse || !message.text) return;

  const history = histories.get(message.conversationId) ?? [];
  history.push({ role: "user", content: message.text });

  // Affiche « en train d'écrire… » dans l'application pendant que le modèle réfléchit.
  agent.sendTyping({ conversationId: message.conversationId, to: message.from });

  const completion = await openai.chat.completions.create({
    model: process.env.OPENAI_MODEL,
    messages: [
      { role: "system", content: "Tu es un assistant utile qui tourne sur mon ordinateur." },
      ...history.slice(-20),
    ],
  });

  const answer = completion.choices[0].message.content ?? "";
  history.push({ role: "assistant", content: answer });
  histories.set(message.conversationId, history);

  await agent.reply(message, { text: answer });
});

await client.connect();

Ajoutez les réglages du modèle à côté de la configuration Pacerelle :

.env
PACERELLE_AGENT_ID=agent_...
PACERELLE_AGENT_TOKEN=...
OPENAI_API_KEY=sk-...
OPENAI_MODEL=nom-de-votre-modele
N'importe quel fournisseur, n'importe quel framework

Remplacez l'appel OpenAI par Anthropic, Google, Mistral, LangChain, LlamaIndex, CrewAI ou votre propre code. La partie Pacerelle — recevoir, répondre — reste exactement la même.

Montrer que l'agent travaille

Dès qu'une tâche prend plus de quelques secondes, tenez l'utilisateur informé :

  • Indicateur de saisieagent.sendTyping({ conversationId, to }) en JavaScript.
  • Accusé rapide — répondez tout de suite Je m'en occupe…, puis envoyez le résultat.
  • Barre de progression — un widget de progression mis à jour au fil du travail.

Tâches longues

Les messages sont traités un par un, dans l'ordre. Quand votre fonction se termine sans erreur, le SDK marque le message comme traité et en accuse réception auprès du relais. Si la fonction lève une erreur ou si le processus plante avant, le message peut être livré à nouveau plus tard.

Pour un travail de plusieurs minutes ou heures :

  1. Enregistrez la tâche de façon durable (fichier, base de données, file) avant la fin de la fonction.
  2. Rendez la main rapidement, éventuellement avec une première réponse : C'est lancé, je reviens vers vous.
  3. Une fois la tâche terminée, envoyez le résultat avec replyToMessageId égal à l'identifiant du message d'origine.

Utilisez l'id du message comme clé d'idempotence pour qu'un message relivré ne lance jamais deux fois la même tâche. Voir Mise en production.

Relire les messages précédents

Après un redémarrage, le SDK JavaScript peut récupérer les messages récents reçus par votre agent dans une conversation pour reconstruire le contexte. Passez before ou beforeId pour remonter plus loin.

const recent = await client.listConversationMessages({
  conversationId: message.conversationId,
  limit: 50,
  skipUndecodable: true,
});

Étapes suivantes