Aller au contenu
Tous les articles

Askolia pour Convex : recherche, réponses avec sources et synchronisation

Appik Studio

La première version de @appik-studio/askolia-convex est disponible sur npm. La version 0.1.0 relie une application Convex à Askolia pour la recherche, les réponses IA avec sources et la synchronisation de documents publics. Le package est sous licence MIT.

Votre mutation enregistre une intention de synchronisation en même temps que la modification des données. Le composant la transmet à Askolia après la validation de la transaction, suit son état et relance les tentatives lorsque l'erreur le permet. La recherche et les questions passent par vos actions Convex.

Installer et enregistrer le composant

Il vous faut Convex 1.46 ou plus récent et un backend Askolia sur lequel l'API d'intégration correspondante est déployée. Installez le package dans votre application Convex :

bun add @appik-studio/askolia-convex@0.1.0 convex@^1.46.0

Enregistrez le composant et transmettez ses variables d'environnement serveur :

// convex/convex.config.ts
import { defineApp } from "convex/server";
import { v } from "convex/values";
import askolia from "@appik-studio/askolia-convex/convex.config.js";

const app = defineApp({
  env: {
    ASKOLIA_URL: v.optional(v.string()),
    ASKOLIA_API_KEY: v.optional(v.string()),
  },
});
app.use(askolia, {
  env: {
    ASKOLIA_URL: app.env.ASKOLIA_URL,
    ASKOLIA_API_KEY: app.env.ASKOLIA_API_KEY,
  },
});
export default app;

Pour Askolia hébergé, définissez ASKOLIA_URL avec https://tacit-goose-489.eu-west-1.convex.site. Si vous exploitez votre propre backend Askolia, utilisez l'origine HTTPS de ses actions HTTP. L'adresse du tableau de bord et l'adresse en .convex.cloud ne conviennent pas.

Pour obtenir une clé d'intégration hébergée, contactez Appik Studio en indiquant le nom de votre projet Askolia. La création des clés est actuellement accompagnée par l'équipe ; le tableau de bord n'a pas encore de commandes pour les gérer. Définissez la clé ASKOLIA_API_KEY reçue dans les variables d'environnement du déploiement Convex et gardez-la côté serveur. Les opérateurs de leur propre backend Askolia peuvent créer un jeton avec la mutation authentifiée integrationApi:createKey, en lui passant projectId et label. La mutation ne retourne le jeton qu'une seule fois. Cette fonction de gestion appartient à Askolia, pas à votre application. Une clé publique de widget, une clé administrateur Convex ou un cookie de session ne la remplace pas.

Utilisez un projet Askolia et une clé distincts pour chaque déploiement de développement, de preview et de production. Ne placez jamais un jeton de production dans les valeurs par défaut des previews. Sans identifiants, l'installation réussit mais les requêtes échouent.

Askolia facture par projet : ajouter des projets pour d'autres environnements peut augmenter l'abonnement. Laissez les identifiants non configurés dans les previews qui n'utilisent pas l'intégration. Pour les tests automatisés isolés, utilisez le helper de test du package avec un transport simulé.

Créez un client après la génération des types du composant par Convex :

// convex/askolia.ts
import { Askolia } from "@appik-studio/askolia-convex";
import { components } from "./_generated/api";

export const askolia = new Askolia(components.askolia);

Rechercher et poser une question depuis une action

Dans une action Convex, passez le contexte de l'action au client :

const matches = await askolia.search(ctx, {
  query: "How long does delivery take?",
  locale: "en",
  limit: 5,
});

const result = await askolia.ask(ctx, {
  query: "How long does delivery take?",
  locale: "en",
  clientId: "visitor_12345678",
});

if (result.kind === "answer") {
  console.log(result.answer, result.sources);
}

search retourne des résultats avec _id, question, answer et, éventuellement, group et url. ask retourne answer, abstain ou unavailable. Affichez les sources avec la réponse et gérez les autres cas sans inventer de réponse. Le composant prend en charge en, fr, de, es et it. Remplacez locale: "en" par locale: "fr" pour une requête en français.

Les requêtes contiennent 2 à 200 caractères. La recherche accepte une limite de 1 à 20 résultats. Utilisez un clientId stable de 8 à 200 caractères composé de lettres, chiffres, points, tirets bas, deux-points ou traits d'union. L'identifiant de l'exemple est illustratif. Fournissez celui que votre application attribue à l'appelant.

Contrôlez les autorisations de vos fonctions avant de les exposer aux clients. Les réponses IA restent soumises au forfait du projet Askolia, au quota mensuel et aux limites de débit.

Synchroniser dans vos mutations

Dans une mutation qui crée un article, enregistrez le document et l'intention de synchronisation ensemble. Ici, title et content sont les champs reçus par votre mutation :

const id = await ctx.db.insert("articles", { title, content });
const revision = await askolia.syncDocument(ctx, {
  key: id,
  title,
  content,
});

Réutilisez cette clé stable pour les mises à jour. Si la mutation échoue, l'écriture, l'intention et le traitement planifié sont annulés ensemble. La transmission à Askolia intervient après la validation de la transaction. Les deux systèmes ne sont donc pas mis à jour instantanément.

Dans la mutation qui supprime l'article, enregistrez aussi sa suppression :

await ctx.db.delete(articleId);
await askolia.removeDocument(ctx, { key: articleId });

Synchronisez uniquement des contenus destinés à la recherche publique. Les documents rejoignent les connaissances publiées du projet Askolia et peuvent apparaître dans la recherche publique et les réponses IA. La V1 ne synchronise ni les permissions ni les données privées des utilisateurs.

Suivre la transmission et relancer les échecs

Exposez une query réactive de votre application, avec contrôle d'accès, qui appelle :

const state = await askolia.getSyncState(ctx, { key: articleId });

Elle retourne null avant la première intention. Ensuite, examinez revision et status ensemble. L'état est pending, syncing, synced ou failed. Le résultat comprend aussi les tentatives, la date de reprise planifiée et une erreur nettoyée des données sensibles si nécessaire. synced signifie qu'Askolia a accusé réception du changement. Cela ne garantit pas que les embeddings ou les réponses IA sont prêts. Les métadonnées d'un ancien succès peuvent subsister pendant l'attente d'une nouvelle révision.

Les erreurs réseau et les réponses HTTP 408 ou 5xx déclenchent jusqu'à huit tentatives. Le délai commence à cinq secondes et double jusqu'à cinq minutes. Une réponse HTTP 429 entraîne une attente de 60 à 75 secondes sans consommer ce budget. Les autres erreurs HTTP arrêtent immédiatement les tentatives. Un verrou temporaire et une surveillance planifiée récupèrent les traitements interrompus.

Après avoir corrigé les identifiants ou le problème de transmission, appelez askolia.retrySync(ctx, { key: articleId }) depuis une mutation. La même révision repart avec un nouveau budget de tentatives. Pour corriger le contenu, appelez plutôt syncDocument. Une query autorisée peut utiliser listFailed avec paginationOpts pour parcourir les échecs, par pages de dix états au maximum.

Namespaces et limites de la V1

Chaque instance du composant possède un namespace persistant. Une même clé dans un autre namespace désigne un autre document. Les révisions croissantes, les reprises idempotentes et les marqueurs de suppression empêchent une ancienne écriture retardée de rétablir un document supprimé. Réinstaller le composant avec un nouveau namespace ne reprend ni ne supprime les documents de l'ancienne instance.

Les namespaces séparent les identités des documents. Ils ne créent pas des collections de recherche publique distinctes au sein d'un même projet Askolia. Tous les namespaces qui utilisent ses clés contribuent aux connaissances publiques et à la limite de correspondances de ce projet. Supprimer une preview ou révoquer sa clé n'efface pas les documents et ne libère pas les correspondances.

Voici les limites à prévoir avant l'intégration :

  • Les documents sont du texte ou du Markdown, avec un titre de 1 à 200 caractères et un contenu de 1 à 100 000 octets UTF-8. Une clé ne peut pas être vide et compte au maximum 200 caractères. La V1 n'extrait pas les PDF et ne téléverse pas de fichiers arbitraires.
  • Les documents synchronisés sont attribués par leur titre, sans URL source dans cette version.
  • ask traite une question par appel. Il ne crée pas de conversation de support et ne conserve pas de fil de discussion.
  • La synchronisation va de votre application vers Askolia. Les modifications du tableau de bord ne reviennent pas dans votre application. Une nouvelle révision peut republier le contenu de l'application ; une reprise exacte conserve son accusé de réception initial.
  • Chaque instance accepte 2 000 clés distinctes, marqueurs de suppression compris. Une suppression ne libère pas d'emplacement. Il n'existe ni nettoyage automatique ni réinitialisation du namespace.
  • Le serveur accepte 2 000 correspondances de documents et dix clés API actives par projet. La limite partagée distincte de 500 documents s'applique toujours. La recherche et ask partagent une limite de 120 requêtes par minute ; les écritures ont leur propre limite de 120 requêtes par minute.

Commencez avec un projet de développement isolé et quelques documents publics. Vérifiez l'état de transmission, les résultats et les sources avant de connecter des contenus de production. Le README du package détaille la configuration, la validation, la reprise après erreur et l'utilitaire de test. La documentation des composants Convex explique leur intégration dans une application Convex.