Guides

Ajouter une porte d'approbation humaine

Mettre en pause un appel d'outil à haut risque pour obtenir la validation d'un humain grâce au modèle de checkpoint du SDK KLA, acheminé vers le Decision Desk.

5 min de lecture1065 mots

Certaines actions d'agent sont trop lourdes de conséquences pour s'exécuter sans supervision : supprimer un compte, traiter un paiement, publier un document. Ce guide montre comment encadrer l'un de ces appels avec une porte d'approbation humaine en utilisant le SDK KLA Control Plane. KLA Control Plane est une couche govern-in-place de sécurité d'exécution, d'audit et de gouvernance : vous instrumentez le code d'agent que vous exécutez déjà. Un seul checkpoint suffit pour qu'un appel d'outil se mette en pause en attendant un humain, soit acheminé vers un réviseur, et ne reprenne qu'après une validation explicite.

Fonctionnement d'une porte

Vous encadrez l'appel à risque dans un checkpoint. Le checkpoint soumet une Decision Request (l'action et son contexte) au moteur de politiques KLA, qui la résout en l'un de quatre résultats, par ordre de précédence : allow, warn, require_approval ou block. Quand la politique renvoie require_approval, l'exécution se met en pause. KLA ouvre une Escalation (une unité de travail en pause dans l'attente d'un humain) et l'achemine vers le Decision Desk, l'espace de travail où les réviseurs autorisés approuvent ou refusent les actions en attente. Par défaut, l'appel checkpoint du SDK interroge le proposal par sondage jusqu'à ce qu'un réviseur décide, puis fait valoir l'approbation et rend le contrôle à votre code ; le serveur exécute l'action approuvée exactement une fois.

sequenceDiagram
  participant App as Votre agent
  participant KLA as Checkpoint KLA
  participant Desk as Decision Desk
  App->>KLA: checkpoint("process_payment", context)
  KLA->>KLA: Évaluer la Decision Request
  Note over KLA: résultat = require_approval
  KLA->>Desk: Ouvrir une Escalation
  Desk-->>KLA: Le réviseur approuve ou refuse
  KLA-->>App: Décision résolue
  App->>App: Exécuter ou gérer le refus

Ajouter le checkpoint

Ci-dessous, un appel à haut risque process_payment est protégé avec le SDK de gouvernance (kla-governance pour Python, @kla-digital/governance pour Node.js). Le SDK sonde au checkpoint jusqu'à ce que l'Escalation soit résolue au Decision Desk. Les paramètres de l'action (compte, montant) sont liés à l'approbation, si bien que la politique décide sur les valeurs réelles et que le réviseur les voit.

Python

from kla_governance import ActionRequest, AgentIdentity, DecisionDenied, KLAClient

client = KLAClient(
    base_url="https://api.kla.digital",
    credential=get_access_token,
    agent=AgentIdentity(agent_id="agt_9f81a7", release_version="1.4.2"),
    mandate_id="mnd_payments",
)

def process_payment(account_id: str, amount: float):
    try:
        # Polls here if policy returns require_approval, until a
        # reviewer resolves the Escalation on the Decision Desk.
        decision = client.checkpoint(
            ActionRequest(
                tool="payments",
                operation="charge",
                destination="gateway:acct_main",
                parameters={"account_id": account_id, "amount": amount},
            ),
            proposal_id=stable_id_for(account_id, amount),
        )
    except DecisionDenied as denial:
        # block, a denied approval, or an expired approval.
        raise PaymentNotAuthorized(
            f"Payment not authorized: {denial.reason_codes} ({denial.remediation})"
        )

    receipt = gateway.charge(account_id, amount)
    decision.log_success(receipt_id=receipt.id)
    return receipt

TypeScript

import { KLAClient, DecisionDenied } from '@kla-digital/governance';

const client = new KLAClient({
  baseUrl: 'https://api.kla.digital',
  credential: () => getAccessToken(),
  agent: { agentId: 'agt_9f81a7', releaseVersion: '1.4.2' },
  mandateId: 'mnd_payments',
});

async function processPayment(accountId: string, amount: number) {
  let decision;
  try {
    // Polls here if policy returns require_approval, until a
    // reviewer resolves the Escalation on the Decision Desk.
    decision = await client.checkpoint(
      {
        tool: 'payments',
        operation: 'charge',
        destination: 'gateway:acct_main',
        parameters: { accountId, amount },
      },
      { proposalId: stableIdFor(accountId, amount) }
    );
  } catch (error) {
    if (error instanceof DecisionDenied) {
      // block, a denied approval, or an expired approval.
      throw new PaymentNotAuthorizedError(
        `Payment not authorized: ${error.reasonCodes} (${error.remediation})`
      );
    }
    throw error;
  }

  const receipt = await gateway.charge(accountId, amount);
  await decision.logSuccess({ receiptId: receipt.id });
  return receipt;
}

Configurez le client une seule fois, au démarrage, avec l'URL de base du control plane, un jeton d'accès client credentials et le rattachement de l'agent : l'identifiant de l'agent enregistré, sa version de release épinglée et un identifiant de mandat actif. L'identité du tenant provient du jeton vérifié ; le SDK n'envoie jamais de champ tenant dans le corps de la requête. Chaque appel de checkpoint envoie Authorization: Bearer <token> à https://api.kla.digital en votre nom.

Gérer un refus proprement

Un réviseur peut refuser l'Escalation, ou la politique peut renvoyer un block ferme. Les deux cas remontent de checkpoint sous la forme d'un DecisionDenied accompagné de la décision résolue ; le transport a réussi et le résultat est enregistré. Traitez l'exception comme une branche attendue :

  • Lisez les reason codes. Chaque décision autre que allow porte des reason_codes lisibles par machine (par exemple PAYMENT_OVER_THRESHOLD) et une remediation lisible par un humain. Branchez sur les codes ; n'analysez jamais la prose.
  • Rendez le refus visible. Renvoyez un message clair à l'utilisateur appelant ou à l'agent en amont (« Le paiement nécessite l'approbation d'un responsable et a été refusé »). Le refus est déjà enregistré comme Lineage Record, vous n'avez donc pas besoin de le journaliser séparément pour l'audit.
  • Ne réessayez pas à l'aveugle. Une action refusée ne doit pas revenir automatiquement dans le même checkpoint. Escaladez vers un parcours humain dans votre propre produit.
⚠️ Warning

Un checkpoint require_approval peut sonder aussi longtemps qu'un réviseur en a besoin. Exécutez les appels protégés dans un worker ou une tâche d'arrière-plan, afin qu'aucune connexion de requête côté utilisateur ne reste ouverte pendant toute l'attente. Passez timeout (timeoutMs en Node.js) pour borner l'attente : à son expiration, le SDK lève ApprovalTimeout, ce qui signifie que l'approbation reste en attente côté serveur. Rappelez checkpoint avec le même proposal id pour reprendre l'attente. Pour ne pas attendre du tout, passez wait_for_approval=False (waitForApproval: false) ; la décision en attente est renvoyée immédiatement et vous la faites valoir plus tard avec resume_action (resumeAction).

💡 Tip

Rendez l'appel protégé idempotent. Passez un proposal_id stable (ici, dérivé du compte et du montant) pour que, lorsque l'action reprend après approbation (ou si votre worker redémarre en cours de route), le débit sous-jacent s'exécute exactement une fois. KLA lie l'approbation à ce proposal, de sorte qu'une nouvelle exécution après validation se résout en la même décision approuvée au lieu d'ouvrir une seconde Escalation.

Avant la mise en production

Rédigez la politique de la porte dans le Policy Builder et lancez une Simulation : rejouez des Decision Requests représentatives contre le brouillon et confirmez qu'un paiement de montant élevé se résout en require_approval tandis qu'un paiement de faible montant se résout en allow. Une fois validée, la politique est compilée en un policy pack signé et entre en production. Dès lors, chaque appel protégé produit une trace défendable : la requête, le résultat, le verdict du réviseur et l'action résultante, le tout capturé dans un Lineage Record que vous pourrez exporter plus tard sous forme de Sealed Evidence Bundle.

Ajouter une porte d'approbation humaine | Developer Docs | KLA Control Plane