Guides

Gouverner un agent existant avec une politique

Simulez, examinez, publiez et liez une politique de paiement à échec fermé à un Agent qui existe déjà dans le KLA Control Plane.

12 min de lecture2590 mots

Ce guide ajoute une politique examinée à un Agent qui existe déjà dans le KLA Control Plane. La politique autorise les appels payments.create de 10 000 EUR ou moins, envoie les paiements plus élevés au Decision Desk et bloque toute requête de paiement qui ne correspond à aucune des deux règles. La version publiée est limitée à ce seul Agent : les autres Agents de production du tenant ne sont pas concernés.

Un brouillon limité à un Agent ne peut être soumis à l'examen qu'après que cet Agent porte un environnement gouverné, et un Agent ne reçoit un environnement gouverné que par une liaison de politique déployée. La séquence comporte donc deux versions de politique : la version 1.0.0 est examinée et liée pour donner son environnement à l'Agent et n'est jamais publiée ; la version 1.1.0 nomme l'Agent dans sa portée, est publiée et est liée comme politique appliquée.

Chaque commande est prête à copier. Les commandes créent des enregistrements dans le tenant sélectionné : utilisez un tenant que vous contrôlez.

Avant de commencer

Vous avez besoin de :

  • la CLI kla compilée depuis le dépôt comme décrit dans le guide d'installation ;
  • jq ;
  • un Agent existant dont l'outil de paiement s'appelle payments.create et reçoit un amount numérique et une currency sous forme de chaîne ;
  • un jeton d'accès maker pour un membre qui peut créer, simuler et publier des politiques, et configurer et déployer des Agents ;
  • un jeton d'accès checker pour un autre membre qui détient le rôle kla:approver, lequel examine les politiques, décide des Decision Requests et approuve les Releases d'Agent.

La CLI lit KLA_ACCESS_TOKEN. Le jeton maker reste exporté pendant tout le guide. Chaque commande checker reçoit le jeton checker pour cette seule commande. Gardez les deux jetons hors des fichiers et des arguments de commande.

export KLA_API_URL='https://api.dev.kla.digital'
export KLA_TENANT_ID='<tenant external ID>'
export KLA_ACCESS_TOKEN='<maker short-lived access token>'
export KLA_CHECKER_ACCESS_TOKEN='<checker short-lived access token>'

KLA_REPO_ROOT="$(git rev-parse --show-toplevel)"
kla() { node "$KLA_REPO_ROOT/cli/dist/index.js" "$@"; }

kla auth status > maker-auth.json
KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" kla auth status > checker-auth.json
jq -e '.token.subject | type == "string"' maker-auth.json
jq -e '.token.subject | type == "string"' checker-auth.json
jq -en --slurpfile maker maker-auth.json --slurpfile checker checker-auth.json \
  '$maker[0].token.subject != $checker[0].token.subject'
jq -e '.token.roles | index("kla:approver") != null' checker-auth.json

kla api tenants getSettings | jq -e '.governance.publishApprovalMode == "two_person"'

kla auth status affiche les revendications non secrètes de chaque jeton. Les deux sujets doivent différer : l'API rejette un examen ou une approbation par l'identité qui a fait la demande. Le mode de publication du tenant doit être two_person ; en mode single_actor, l'étape de publication enregistre sa propre approbation et l'étape 7 n'a aucune Decision Request à décider.

Étape 1 : Identifier l'Agent

Recherchez dans l'Agent Registry, copiez l'UUID du résultat et enregistrez la fiche de l'Agent. L'Agent ne doit avoir aucun brouillon en attente : une liaison de politique est fusionnée dans un brouillon existant, et le checker approuverait alors des changements sans rapport en même temps que la liaison.

kla agent list --search 'payment'
export AGENT_UUID='<agent UUID>'
kla agent get "$AGENT_UUID" > agent-before.json
jq -e --arg agent "$AGENT_UUID" '.id == $agent' agent-before.json

kla agent release history "$AGENT_UUID" > agent-history-before.json
jq -e '[.versions[] | select(.is_draft == true)] | length == 0' agent-history-before.json

Si un brouillon en attente existe, son manifest figure dans cette sortie d'historique. Faites-le approuver par le checker ou abandonner par le maker avant de continuer.

La politique ci-dessous nomme l'outil payments.create et les arguments amount et currency. Si votre Agent utilise un autre nom d'outil ou d'argument, modifiez ces valeurs dans la politique et dans les deux entrées de Simulation.

Étape 2 : Écrire la politique à échec fermé

Enregistrez ce fichier sous policy.template.json. La version 1.0.0 est limitée à l'environnement gouverné production et sert de version d'amorçage. L'étape 6 restreint la portée à l'Agent pour la version 1.1.0.

{
  "schemaVersion": "1.0.0",
  "policyId": "existing-agent-payment-controls",
  "workspaceId": "TENANT_EXTERNAL_ID",
  "name": "Existing agent payment controls",
  "description": "Allows EUR payments of 10,000 or less and routes larger EUR payments to review.",
  "status": "draft",
  "version": "1.0.0",
  "scope": {
    "environments": ["production"]
  },
  "defaultDecision": "block",
  "rules": [
    {
      "ruleId": "allow-managed-execution-gate",
      "name": "Allow the managed Process execution gate",
      "interceptionPoint": "input",
      "when": {
        "expression": {
          "==": [{ "var": "context.action.actionType" }, "policy_gate"]
        }
      },
      "then": {
        "decision": "allow",
        "reason": "The execution may start; each payment tool call is evaluated separately.",
        "reasonCodes": ["execution_gate_allowed"]
      },
      "execution": { "mode": "deterministic" },
      "evidence": {
        "evaluatedFields": ["context.action.actionType"],
        "artifactRefs": []
      }
    },
    {
      "ruleId": "allow-payment-up-to-10000",
      "name": "Allow positive payment up to EUR 10,000",
      "interceptionPoint": "tool_call",
      "when": {
        "expression": {
          "and": [
            { "in": [{ "var": "context.action.actionType" }, ["tool_input", "tool_call"]] },
            { "==": [{ "var": "context.action.toolName" }, "payments.create"] },
            { "==": [{ "var": "context.action.toolArgs.currency" }, "EUR"] },
            { ">": [{ "var": "context.action.toolArgs.amount" }, 0] },
            { "<=": [{ "var": "context.action.toolArgs.amount" }, 10000] }
          ]
        }
      },
      "then": {
        "decision": "allow",
        "reason": "Payment is within the approved automatic limit.",
        "reasonCodes": ["payment_within_automatic_limit"]
      },
      "execution": { "mode": "deterministic" },
      "evidence": {
        "evaluatedFields": [
          "context.action.actionType",
          "context.action.toolName",
          "context.action.toolArgs.currency",
          "context.action.toolArgs.amount"
        ],
        "artifactRefs": []
      }
    },
    {
      "ruleId": "review-payment-over-10000",
      "name": "Review payment over EUR 10,000",
      "interceptionPoint": "tool_call",
      "when": {
        "expression": {
          "and": [
            { "in": [{ "var": "context.action.actionType" }, ["tool_input", "tool_call"]] },
            { "==": [{ "var": "context.action.toolName" }, "payments.create"] },
            { "==": [{ "var": "context.action.toolArgs.currency" }, "EUR"] },
            { ">": [{ "var": "context.action.toolArgs.amount" }, 10000] }
          ]
        }
      },
      "then": {
        "decision": "require_approval",
        "reason": "Payment exceeds the approved automatic limit.",
        "reasonCodes": ["payment_over_automatic_limit"],
        "approverGroup": "kla:approver",
        "remediation": {
          "summary": "Obtain a payment approval before execution.",
          "steps": []
        }
      },
      "execution": { "mode": "deterministic" },
      "evidence": {
        "evaluatedFields": [
          "context.action.actionType",
          "context.action.toolName",
          "context.action.toolArgs.currency",
          "context.action.toolArgs.amount"
        ],
        "artifactRefs": []
      }
    }
  ]
}

La première règle autorise la porte d'exécution. Une politique liée devient la première policy_gate du Process géré de l'Agent, et cette porte est évaluée avec l'actionType policy_gate et l'identifiant de la politique comme toolName ; sans cette règle, la valeur par défaut à échec fermé arrêterait chaque exécution avant tout appel d'outil. Les deux règles de paiement s'appliquent aux requêtes d'appel d'outil, dont l'actionType est tool_input ou tool_call, et s'excluent mutuellement. Un montant absent, nul ou négatif, une autre devise ou un autre outil ne correspond à aucune règle et aboutit à la defaultDecision block. La garde > 0 est importante : un montant absent vaut 0 dans les expressions de règle et satisferait sinon <= 10000. approverGroup devient le rôle requis de la Decision Request : il nomme donc le rôle configuré kla:approver. Le lint de politique rejette une defaultDecision allow avec POLICY_DEFAULT_DECISION_FAIL_OPEN.

Insérez l'identifiant externe du tenant et créez le brouillon d'amorçage :

jq --arg workspace "$KLA_TENANT_ID" '.workspaceId = $workspace' \
  policy.template.json > policy.json

export POLICY_ID='existing-agent-payment-controls'
export BOOTSTRAP_VERSION='1.0.0'
export POLICY_VERSION='1.1.0'

kla policy registry create --file policy.json
kla policy registry get "$POLICY_ID" --version "$BOOTSTRAP_VERSION" \
  | jq -e '.versions[0].status == "draft"'

Étape 3 : Exécuter deux Simulations durables

Une Simulation envoie au brouillon un GateContext, l'enveloppe de requête évaluée par le KLA Policy Engine. Le Policy Registry stocke chaque Simulation terminée avec une référence de preuve, et la préparation à l'examen exige une Simulation stockée du contenu exact du brouillon.

Enregistrez le GateContext de faible valeur sous simulate-500.template.json :

{
  "context": {
    "schemaVersion": "1.0.0",
    "occurredAt": "2026-09-06T12:00:00Z",
    "identifiers": {
      "workspaceId": "TENANT_EXTERNAL_ID",
      "environment": "production"
    },
    "agentId": "AGENT_UUID",
    "action": {
      "actionType": "tool_input",
      "toolName": "payments.create",
      "toolArgs": { "amount": 500, "currency": "EUR" },
      "environment": "production"
    }
  }
}

Enregistrez le GateContext de valeur élevée sous simulate-15000.template.json :

{
  "context": {
    "schemaVersion": "1.0.0",
    "occurredAt": "2026-09-06T12:00:00Z",
    "identifiers": {
      "workspaceId": "TENANT_EXTERNAL_ID",
      "environment": "production"
    },
    "agentId": "AGENT_UUID",
    "action": {
      "actionType": "tool_input",
      "toolName": "payments.create",
      "toolArgs": { "amount": 15000, "currency": "EUR" },
      "environment": "production"
    }
  }
}

Insérez les identifiants du tenant et de l'Agent, puis exécutez les deux Simulations. kla policy registry simulate envoie l'en-tête Idempotency-Key exigé par la route.

for amount in 500 15000; do
  jq --arg workspace "$KLA_TENANT_ID" --arg agent "$AGENT_UUID" \
    '.context.identifiers.workspaceId = $workspace | .context.agentId = $agent' \
    "simulate-${amount}.template.json" > "simulate-${amount}.json"
done

kla policy registry simulate "$POLICY_ID" "$BOOTSTRAP_VERSION" \
  --input @simulate-500.json > simulation-500.json
jq -e '
  .decision.decision == "allow" and
  .decision.primaryBasis.ruleId == "allow-payment-up-to-10000" and
  (.simulation.simulationId | type == "string") and
  (.simulation.evidenceId | type == "string")
' simulation-500.json

kla policy registry simulate "$POLICY_ID" "$BOOTSTRAP_VERSION" \
  --input @simulate-15000.json > simulation-15000.json
jq -e '
  .decision.decision == "require_approval" and
  .decision.primaryBasis.ruleId == "review-payment-over-10000" and
  .decision.approval.assigneeGroup == "kla:approver" and
  (.simulation.simulationId | type == "string") and
  (.simulation.evidenceId | type == "string")
' simulation-15000.json

Les deux vérifications doivent réussir avant la soumission du brouillon. Toute modification ultérieure du brouillon change son empreinte de contenu et exige une nouvelle Simulation.

Étape 4 : Soumettre et approuver la version d'amorçage

Le maker soumet la version 1.0.0 et le checker l'approuve. La version reste approved et n'est jamais publiée : elle ne prend donc jamais effet pour aucune porte.

kla policy registry submit-review "$POLICY_ID" "$BOOTSTRAP_VERSION" \
  --comment 'Threshold outcomes and fail-closed default verified by Simulation.'

KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
  kla policy registry review "$POLICY_ID" "$BOOTSTRAP_VERSION" \
  --decision approve \
  --comment 'Rule scope, outcomes, and Simulation evidence reviewed.' \
  --yes

kla policy registry get "$POLICY_ID" --version "$BOOTSTRAP_VERSION" \
  | jq -e '.versions[0].status == "approved"'

Une réponse 409 avec readiness liste la vérification bloquante. required_simulation_missing signifie que la Simulation stockée ne correspond pas au contenu actuel du brouillon ; relancez l'étape 3.

Étape 5 : Donner à l'Agent son environnement gouverné

kla agent policy bind accepte une politique approuvée, publiée ou active. La commande écrit la liaison dans un nouveau brouillon d'Agent, crée le Process géré pour l'environnement gouverné et renvoie l'UUID du brouillon. Le checker approuve le brouillon, ce qui le publie comme Release immuable, et le maker déploie la Release. agent release deploy exige une liaison production et déplace le manifeste actif de l'Agent, où l'environnement gouverné est lu lors de l'examen de politique.

kla agent policy bind "$AGENT_UUID" \
  --policy "$POLICY_ID" \
  --environment production \
  --description 'Bind the reviewed payment control to establish the production environment.' \
  > agent-binding-bootstrap.json

jq -e --arg agent "$AGENT_UUID" '.success == true and .agentId == $agent' \
  agent-binding-bootstrap.json
export BOOTSTRAP_DRAFT_UUID="$(jq -er '.draftId' agent-binding-bootstrap.json)"

KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
  kla agent release approve "$BOOTSTRAP_DRAFT_UUID" \
  --notes 'Bootstrap binding for the production governed environment reviewed.' \
  --yes | jq -e '.success == true'

kla agent release deploy "$BOOTSTRAP_DRAFT_UUID" --yes > agent-rollout-bootstrap.json

kla agent get "$AGENT_UUID" \
  | jq -e '.manifest.governedExecution.environment == "production"'

À partir de ce point et jusqu'à la fin de l'étape 8, le Process géré de l'Agent référence une version de politique non publiée et ses exécutions échouent de manière fermée.

Étape 6 : Créer, simuler et examiner la version limitée à l'Agent

La version 1.1.0 a les mêmes règles et nomme l'Agent dans scope.agentIds. Les entrées de Simulation de l'étape 3 sont réutilisées.

jq --arg workspace "$KLA_TENANT_ID" --arg agent "$AGENT_UUID" --arg version "$POLICY_VERSION" \
  '.workspaceId = $workspace | .version = $version | .scope.agentIds = [$agent]' \
  policy.template.json > policy-agent-scoped.json

kla policy registry create --file policy-agent-scoped.json

kla policy registry simulate "$POLICY_ID" "$POLICY_VERSION" \
  --input @simulate-500.json | jq -e '.decision.decision == "allow"'
kla policy registry simulate "$POLICY_ID" "$POLICY_VERSION" \
  --input @simulate-15000.json | jq -e '.decision.decision == "require_approval"'

kla policy registry submit-review "$POLICY_ID" "$POLICY_VERSION" \
  --comment 'Agent-scoped version; outcomes verified by Simulation.'

KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
  kla policy registry review "$POLICY_ID" "$POLICY_VERSION" \
  --decision approve \
  --comment 'Agent scope, outcomes, and Simulation evidence reviewed.' \
  --yes

kla policy registry get "$POLICY_ID" --version "$POLICY_VERSION" \
  | jq -e '.versions[0].status == "approved"'

La vérification de préparation à l'examen résout chaque Agent de scope.agentIds dans l'Agent Registry et exige que chacun porte un environnement gouverné correspondant à scope.environments. L'étape 5 l'a garanti pour cet Agent.

Étape 7 : Publier

Le maker demande la publication. En mode two_person, la demande renvoie pendingApproval: true et un identifiant de Decision Request.

export KLA_EFFECTIVE_FROM="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

kla policy registry publish "$POLICY_ID" "$POLICY_VERSION" \
  --effective-from "$KLA_EFFECTIVE_FROM" \
  --reason 'Activate the reviewed payment control.' \
  --yes > publication-request.json

jq -e '.success == true and .pendingApproval == true and .status == "PENDING"' \
  publication-request.json
export APPROVAL_UUID="$(jq -er '.approvalId' publication-request.json)"

Le checker décide de la Decision Request via approvals.decide :

jq -n --arg approvalId "$APPROVAL_UUID" '{
  approvalId: $approvalId,
  decision: "approve",
  decisionAcknowledged: true,
  reason: "Reviewed policy version approved for publication."
}' > approval-decision.json

KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
  kla api approvals decide --input @approval-decision.json > publication-decision.json
jq -e '.decision == "approve" and .status == "APPROVED"' publication-decision.json

La publication s'exécute après l'enregistrement de la décision. Attendez que la version atteigne published ou active, puis lisez le pack de politique compilé.

for attempt in $(seq 1 30); do
  kla policy registry get "$POLICY_ID" --version "$POLICY_VERSION" > published-policy.json
  jq -e '.versions[0].status == "published" or .versions[0].status == "active"' \
    published-policy.json > /dev/null && break
  sleep 2
done
jq -e '.versions[0].status == "published" or .versions[0].status == "active"' \
  published-policy.json

kla policy registry compiled "$POLICY_ID" "$POLICY_VERSION" > compiled-policy.json
jq -e --arg policy "$POLICY_ID" --arg version "$POLICY_VERSION" '
  .integrity.policyId == $policy and
  .integrity.policyVersion == $version and
  .integrity.manifestHashVerified == true
' compiled-policy.json

integrity.verification.status vaut PASS lorsque le tenant signe les packs de politique et SKIPPED lorsque la signature est désactivée dans cet environnement. Arrêtez-vous si la version reste non publiée ou si manifestHashVerified vaut false.

Étape 8 : Lier la version publiée

La liaison sélectionne la version active ou publiée de la politique : la même commande de liaison épingle donc maintenant la version 1.1.0 et met à jour le Process géré. Le checker approuve le nouveau brouillon et le maker le déploie.

kla agent policy bind "$AGENT_UUID" \
  --policy "$POLICY_ID" \
  --environment production \
  --description 'Bind the published Agent-scoped payment control.' \
  > agent-binding.json

jq -e --arg agent "$AGENT_UUID" '.success == true and .agentId == $agent' agent-binding.json
export DRAFT_UUID="$(jq -er '.draftId' agent-binding.json)"

kla agent release history "$AGENT_UUID" > agent-history-draft.json
jq -e --arg release "$DRAFT_UUID" --arg policy "$POLICY_ID" --arg version "$POLICY_VERSION" '
  any(.versions[];
    .id == $release and
    .is_draft == true and
    .manifest.governedExecution.policyId == $policy and
    .manifest.governedExecution.policyVersion == $version)
' agent-history-draft.json

KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
  kla agent release approve "$DRAFT_UUID" \
  --notes 'Published policy binding and managed Process reviewed.' \
  --yes | jq -e '.success == true'

kla agent release deploy "$DRAFT_UUID" --yes > agent-rollout.json

La vérification de l'historique montre au checker que le brouillon porte la liaison de la version 1.1.0 avant l'approbation.

Étape 9 : Vérifier

Lisez l'Agent et son historique de Releases.

kla agent get "$AGENT_UUID" > agent-after.json
jq -e --arg policy "$POLICY_ID" --arg version "$POLICY_VERSION" '
  .manifest.governedExecution.policyId == $policy and
  .manifest.governedExecution.policyVersion == $version and
  .manifest.governedExecution.environment == "production" and
  (.manifest.governedExecution.workflowId | type == "string") and
  any(.passport.activePolicies[]; .policyId == $policy and .version == $version)
' agent-after.json

kla agent release history "$AGENT_UUID" > agent-history.json
jq -e --arg release "$DRAFT_UUID" '
  any(.versions[];
    .id == $release and
    .is_draft == false and
    .is_current_version == true and
    .approved_at != null and
    .deployed_at != null)
' agent-history.json

Exécutez les deux Simulations contre la version publiée et conservez les résultats avec le dossier de changement.

kla policy registry simulate "$POLICY_ID" "$POLICY_VERSION" \
  --input @simulate-500.json | jq -e '.decision.decision == "allow"'

kla policy registry simulate "$POLICY_ID" "$POLICY_VERSION" \
  --input @simulate-15000.json | jq -e '.decision.decision == "require_approval"'

Vous disposez maintenant d'une politique publiée limitée à l'Agent, dont les Simulations résolvent 500 EUR en allow et 15 000 EUR en require_approval, et d'une Release d'Agent déployée qui la lie. Un appel payments.create réel supérieur à 10 000 EUR provenant de cet Agent ouvre une Decision Request sur le Decision Desk pour le rôle kla:approver. Le guide Gouverner un agent de bout en bout couvre l'exécution, l'approbation et l'export de preuves qui suivent.

Gouverner un agent existant avec une politique | Developer Docs | KLA Control Plane