Guide de démarrage rapide
Passez de la connexion à des décisions enregistrées allow, warn, require_approval et block, à une approbation dans le Decision Desk et à un lot de preuves scellé en 30 minutes environ.
Ce guide mène un poste de développement de la connexion à une décision enregistrée pour chaque résultat de politique : allow, warn, require_approval et block. Vous enregistrez un agent d'entrée en relation KYC et son outil, publiez une politique KYC de départ, l'appelez depuis le SDK de gouvernance avec une clé API du tenant, faites décider le cas require_approval par une deuxième personne dans le Decision Desk et retrouvez chaque décision dans l'Audit Trail. Les commandes visent l'environnement de développement KLA à l'adresse https://api.dev.kla.digital. Prévoyez environ 30 minutes.
Il vous faut deux personnes dans le tenant : vous, administrateur du tenant qui crée les politiques, et un réviseur disposant du rôle kla:approver. KLA refuse une revue ou une approbation faite par la personne à l'origine de la demande.
Exécuter la CLI KLA
La CLI KLA s'exécute actuellement depuis ce monorepo. À la racine du dépôt, compilez-la avec Node.js 20.x :
pnpm install --frozen-lockfile
pnpm kla --help
alias kla='pnpm --silent kla'
Le wrapper du dépôt recompile la CLI lorsqu'un fichier source est plus récent que sa sortie générée. Les étapes suivantes utilisent aussi jq.
Étape 1 : se connecter
Créez un contexte CLI pour votre tenant, puis connectez-vous dans le navigateur. La CLI utilise le client public kla-cli avec PKCE et ne stocke aucun secret client.
export KLA_TENANT_ID='<tenant external ID>'
kla context set "$KLA_TENANT_ID" \
--api-url https://api.dev.kla.digital \
--tenant "$KLA_TENANT_ID" \
--issuer "https://auth.dev.kla.digital/realms/$KLA_TENANT_ID" \
--client-id kla-cli \
--use
kla auth login
kla auth test
kla auth test affiche le tenant auquel correspond votre identifiant. Le réviseur exécute les mêmes commandes dans son propre terminal et se connecte avec son compte. Voir Authentification.
Étape 2 : enregistrer l'agent et son outil
Rédigez un manifeste d'agent. governanceIntent.environment déclare où l'agent est gouverné : development, staging ou production.
cat > kyc-agent.yaml <<'YAML'
name: KYC Onboarding Agent
description: Screens retail account applications and recommends an onboarding decision.
model:
provider: openai
name: gpt-4o
pipeline:
- type: llm_generate
name: screen_application
config:
template: Review the application and recommend approve, review, or decline with reasons.
owners:
- email: [email protected]
tools:
- open_customer_account
humanOversightRequired: true
governanceIntent:
environment: development
YAML
kla agent create --file kyc-agent.yaml > agent.json
export AGENT_ID=$(jq -r '.id' agent.json)
Une politique limitée à cet agent (scope.agentIds) ne peut passer en revue que si son scope.environments nomme le même environnement. Définissez governanceIntent à l'enregistrement : un Draft ultérieur ne peut pas le modifier.
KLA ne décide que pour les outils inscrits au Tool Catalog du tenant. Enregistrez l'outil que l'agent appelle, avec les champs que lit la politique :
cat > open-customer-account.tool.json <<'JSON'
{
"key": "open_customer_account",
"name": "Open customer account",
"description": "Opens a retail account after KYC screening.",
"risk_class": "high",
"input_schema": {
"type": "object",
"properties": {
"sanctions_hit": { "type": "boolean" },
"pep_match": { "type": "boolean" },
"country_risk": { "type": "string", "enum": ["standard", "high"] },
"risk_score": { "type": "number", "minimum": 0, "maximum": 100 }
},
"required": ["sanctions_hit", "pep_match", "country_risk", "risk_score"]
}
}
JSON
kla api tools create --input @open-customer-account.tool.json
Si le tenant possède déjà l'outil, tools.create répond CONFLICT et l'entrée existante est conservée.
Étape 3 : rédiger la politique et simuler quatre résultats
Les règles de la politique lisent les arguments de l'outil à context.action.toolArgs.<field>. L'entrée de l'agent expose action.parameters au même chemin. Les entrées validées prennent aussi en charge tool.<toolId>.input.<field>.
La politique KYC de départ refuse un résultat positif au filtrage des sanctions, envoie une personne politiquement exposée (PPE) ou un pays à haut risque à une deuxième personne, signale un risque moyen et ouvre les comptes à faible risque. Toute autre demande, par exemple un score de risque supérieur à 70 ou un champ manquant, aboutit à la décision par défaut block.
cat > kyc-onboarding-gate.template.json <<'JSON'
{
"policyId": "kyc-onboarding-gate",
"name": "KYC onboarding gate",
"description": "Opens low-risk accounts, flags medium risk, sends PEP and high-risk countries to a second person, and refuses sanctions hits.",
"version": "1.0.0",
"scope": { "tools": [], "environments": ["development"], "agentIds": ["AGENT_ID"] },
"defaultDecision": "block",
"rules": [
{
"ruleId": "block-sanctions-hit",
"name": "Refuse a sanctions hit",
"interceptionPoint": "tool_call",
"when": { "expression": { "and": [
{ "==": [{ "var": "context.action.toolName" }, "open_customer_account"] },
{ "==": [{ "var": "context.action.toolArgs.sanctions_hit" }, true] }
] } },
"then": {
"decision": "block",
"reason": "Sanctions screening returned a hit.",
"reasonCodes": ["sanctions_hit"],
"remediation": { "summary": "Refuse the account and follow the sanctions procedure.", "steps": [] }
},
"execution": { "mode": "deterministic" },
"evidence": { "artifactRefs": [], "evaluatedFields": [
"context.action.toolName", "context.action.toolArgs.sanctions_hit"
] }
},
{
"ruleId": "review-pep-or-high-risk-country",
"name": "Second review for a PEP or a high-risk country",
"interceptionPoint": "tool_call",
"when": { "expression": { "and": [
{ "==": [{ "var": "context.action.toolName" }, "open_customer_account"] },
{ "==": [{ "var": "context.action.toolArgs.sanctions_hit" }, false] },
{ "or": [
{ "==": [{ "var": "context.action.toolArgs.pep_match" }, true] },
{ "==": [{ "var": "context.action.toolArgs.country_risk" }, "high"] }
] }
] } },
"then": {
"decision": "require_approval",
"reason": "A PEP match or a high-risk country needs a second person.",
"reasonCodes": ["pep_or_high_risk_country"],
"approverGroup": "kla:approver",
"remediation": { "summary": "Approve or reject the account in the Decision Desk.", "steps": [] }
},
"execution": { "mode": "deterministic" },
"evidence": { "artifactRefs": [], "evaluatedFields": [
"context.action.toolName", "context.action.toolArgs.sanctions_hit",
"context.action.toolArgs.pep_match", "context.action.toolArgs.country_risk"
] }
},
{
"ruleId": "warn-medium-risk",
"name": "Flag medium risk",
"interceptionPoint": "tool_call",
"when": { "expression": { "and": [
{ "==": [{ "var": "context.action.toolName" }, "open_customer_account"] },
{ "==": [{ "var": "context.action.toolArgs.sanctions_hit" }, false] },
{ "==": [{ "var": "context.action.toolArgs.pep_match" }, false] },
{ "==": [{ "var": "context.action.toolArgs.country_risk" }, "standard"] },
{ ">": [{ "var": "context.action.toolArgs.risk_score" }, 30] },
{ "<=": [{ "var": "context.action.toolArgs.risk_score" }, 70] }
] } },
"then": {
"decision": "warn",
"reason": "Medium customer risk: open the account and schedule enhanced monitoring.",
"reasonCodes": ["medium_risk"],
"remediation": { "summary": "Schedule enhanced monitoring for this customer.", "steps": [] }
},
"execution": { "mode": "deterministic" },
"evidence": { "artifactRefs": [], "evaluatedFields": [
"context.action.toolName", "context.action.toolArgs.sanctions_hit",
"context.action.toolArgs.pep_match", "context.action.toolArgs.country_risk",
"context.action.toolArgs.risk_score"
] }
},
{
"ruleId": "allow-low-risk",
"name": "Open a low-risk account",
"interceptionPoint": "tool_call",
"when": { "expression": { "and": [
{ "==": [{ "var": "context.action.toolName" }, "open_customer_account"] },
{ "==": [{ "var": "context.action.toolArgs.sanctions_hit" }, false] },
{ "==": [{ "var": "context.action.toolArgs.pep_match" }, false] },
{ "==": [{ "var": "context.action.toolArgs.country_risk" }, "standard"] },
{ ">=": [{ "var": "context.action.toolArgs.risk_score" }, 0] },
{ "<=": [{ "var": "context.action.toolArgs.risk_score" }, 30] }
] } },
"then": {
"decision": "allow",
"reason": "Low customer risk with clear screening.",
"reasonCodes": ["low_risk"]
},
"execution": { "mode": "deterministic" },
"evidence": { "artifactRefs": [], "evaluatedFields": [
"context.action.toolName", "context.action.toolArgs.sanctions_hit",
"context.action.toolArgs.pep_match", "context.action.toolArgs.country_risk",
"context.action.toolArgs.risk_score"
] }
},
{
"ruleId": "allow-account-opening-result",
"name": "Return the authorized account-opening result",
"interceptionPoint": "tool_result",
"appliesTo": { "toolKeys": ["open_customer_account"] },
"when": { "expression": { "==": [{ "var": "context.action.toolName" }, "open_customer_account"] } },
"then": { "decision": "allow", "reason": "The authorized account-opening result can be returned.", "reasonCodes": ["account_opening_result"] },
"execution": { "mode": "deterministic" }
}
]
}
JSON
export POLICY_ID='kyc-onboarding-gate'
jq --arg agent "$AGENT_ID" --arg policy "$POLICY_ID" \
'.policyId = $policy | .scope.agentIds = [$agent]' \
kyc-onboarding-gate.template.json > kyc-onboarding-gate.json
kla policy registry create --file kyc-onboarding-gate.json | jq '.lint'
lint.ok vaut true. lint.issues contient un avertissement POLICY_BAND_GAP pour les scores supérieurs à 70. Examinez les résultats du lint et la décision par défaut block de la politique avant de soumettre le brouillon. POLICY_ID doit être nouveau dans le tenant ; si kla policy registry get "$POLICY_ID" trouve déjà une politique, choisissez un autre identifiant.
Simulez une demande par résultat. Une Simulation évalue le brouillon sans enregistrer de décision réelle, et la revue exige une Simulation enregistrée du brouillon exact :
simulate() {
jq -n --arg tenant "$KLA_TENANT_ID" --arg agent "$AGENT_ID" --argjson args "$2" '{
context: {
occurredAt: (now | todate),
identifiers: { workspaceId: $tenant, agentId: $agent, environment: "development" },
action: { actionType: "tool_call", toolName: "open_customer_account", toolArgs: $args }
}
}' > "simulate-$1.json"
kla policy registry simulate "$POLICY_ID" 1.0.0 --input "@simulate-$1.json" \
| jq -c '{case: "'"$1"'", decision: .decision.decision, rule: .decision.primaryBasis.ruleId}'
}
simulate low '{"sanctions_hit":false,"pep_match":false,"country_risk":"standard","risk_score":15}'
simulate medium '{"sanctions_hit":false,"pep_match":false,"country_risk":"standard","risk_score":55}'
simulate pep '{"sanctions_hit":false,"pep_match":true,"country_risk":"standard","risk_score":20}'
simulate sanctions '{"sanctions_hit":true,"pep_match":false,"country_risk":"standard","risk_score":10}'
{"case":"low","decision":"allow","rule":"allow-low-risk"}
{"case":"medium","decision":"warn","rule":"warn-medium-risk"}
{"case":"pep","decision":"require_approval","rule":"review-pep-or-high-risk-country"}
{"case":"sanctions","decision":"block","rule":"block-sanctions-hit"}
Soumettez le brouillon à la revue :
kla policy registry submit-review "$POLICY_ID" 1.0.0 --comment 'Starter KYC onboarding gate.'
Étape 4 : réviser et publier la politique
Le réviseur approuve la version depuis son propre terminal :
kla policy registry review kyc-onboarding-gate 1.0.0 \
--decision approve --comment 'Four outcomes match the KYC procedure.' --yes
Vous la publiez. La publication prend effet maintenant sauf si vous passez --effective-from, et elle ouvre une Decision Request pour le réviseur :
kla policy registry publish "$POLICY_ID" 1.0.0 --reason 'First KYC onboarding gate.' --yes
Le réviseur ouvre Decision Desk dans la Console à l'adresse https://kla.digital, ouvre Publish KYC onboarding gate 1.0.0?, ouvre Policy version diff et Lint report, choisit Approve publish, indique un motif et sélectionne Record decision. La version est alors publiée.
Étape 5 : créer une clé API pour l'agent
L'agent s'authentifie avec une clé API du tenant. Une clé dotée de la portée policy:evaluate peut demander des décisions et rien d'autre :
kla api secrets createApiKey \
--input '{"name":"kyc-onboarding-agent","permissions":["policy:evaluate"],"expiresAt":"2027-01-08"}' \
> api-key.json
export KLA_API_KEY=$(jq -r '.key' api-key.json)
jq '.effectiveAccess' api-key.json
KLA renvoie la clé une seule fois. Rangez-la dans votre gestionnaire de secrets, conservez apiKey.id pour la révocation et supprimez api-key.json. Un administrateur du tenant peut aussi créer la clé dans Settings > API keys.
Étape 6 : demander des décisions depuis le SDK de gouvernance
Le SDK de gouvernance demande une décision à KLA avant que l'agent n'appelle un outil. KLA enregistre chaque décision sous vos identifiants d'exécution et de point de contrôle et n'exécute rien ; l'agent agit selon le résultat. Une décision require_approval ouvre une Decision Request dans le Decision Desk.
Créez une fois un fichier d’identité pour ce lot d’actions et conservez-le pour les nouvelles tentatives. Les deux exemples lisent son horodatage initial et utilisent des identifiants de run distincts pour Python et Node.js. Utilisez un nouveau fichier pour un nouveau lot.
if [ ! -f kyc-action-identity.env ]; then
printf 'export KLA_ACTION_BATCH_ID=%s\nexport KLA_ACTION_OCCURRED_AT=%s\n' \
"$(node -e "console.log(require('node:crypto').randomUUID())")" \
"$(date -u +'%Y-%m-%dT%H:%M:%SZ')" > kyc-action-identity.env
fi
. ./kyc-action-identity.env
Python
pip install kla-governance
import os
from kla_governance import PolicyClient
client = PolicyClient(
base_url="https://api.dev.kla.digital",
workspace_id=os.environ["KLA_TENANT_ID"],
api_key=os.environ["KLA_API_KEY"],
)
applications = {
"case-1001": {"sanctions_hit": False, "pep_match": False, "country_risk": "standard", "risk_score": 15},
"case-1002": {"sanctions_hit": False, "pep_match": False, "country_risk": "standard", "risk_score": 55},
"case-1003": {"sanctions_hit": False, "pep_match": True, "country_risk": "standard", "risk_score": 20},
"case-1004": {"sanctions_hit": True, "pep_match": False, "country_risk": "standard", "risk_score": 10},
}
for case_id, fields in applications.items():
result = client.evaluate_tool_call(
run_id=f"{case_id}-python-{os.environ['KLA_ACTION_BATCH_ID']}",
occurred_at=os.environ["KLA_ACTION_OCCURRED_AT"],
gate_id="open-account",
tool_name="open_customer_account",
tool_args=fields,
agent_id=os.environ["AGENT_ID"],
environment="development",
policy_id=os.environ["POLICY_ID"],
)
print(case_id, result.decision, result.rule_id, result.approval.approval_id if result.approval else "")
Node.js
npm install @kla-digital/governance
import { KLAPolicyClient } from '@kla-digital/governance';
const client = new KLAPolicyClient({
baseUrl: 'https://api.dev.kla.digital',
workspaceId: process.env.KLA_TENANT_ID!,
apiKey: process.env.KLA_API_KEY!,
});
const applications = {
'case-1001': { sanctions_hit: false, pep_match: false, country_risk: 'standard', risk_score: 15 },
'case-1002': { sanctions_hit: false, pep_match: false, country_risk: 'standard', risk_score: 55 },
'case-1003': { sanctions_hit: false, pep_match: true, country_risk: 'standard', risk_score: 20 },
'case-1004': { sanctions_hit: true, pep_match: false, country_risk: 'standard', risk_score: 10 },
};
for (const [caseId, toolArgs] of Object.entries(applications)) {
const result = await client.evaluateToolCall({
runId: `${caseId}-node-${process.env.KLA_ACTION_BATCH_ID}`,
occurredAt: process.env.KLA_ACTION_OCCURRED_AT!,
gateId: 'open-account',
toolName: 'open_customer_account',
toolArgs,
caseReference: caseId,
agentId: process.env.AGENT_ID,
environment: 'development',
policyId: process.env.POLICY_ID,
});
console.log(caseId, result.decision, result.ruleId, result.approval?.approvalId ?? '');
}
Chacun des deux programmes affiche une décision par demande :
case-1001 allow allow-low-risk
case-1002 warn warn-medium-risk
case-1003 require_approval review-pep-or-high-risk-country <Decision Request ID>
case-1004 block block-sanctions-hit
Une nouvelle tentative de la même action exige les valeurs initiales de runId, gateId, occurredAt, des arguments de l’outil, de l’Agent, de la politique et de l’environnement. Le SDK conserve le corps pendant ses nouvelles tentatives automatiques. Un appel SDK séparé sans horodatage explicite génère un nouvel occurredAt ; réutiliser le même run et la même gate renvoie alors 409. Conservez kyc-action-identity.env et les entrées initiales pour relancer ces exemples. Attribuez un nouveau gateId à chaque action distincte d’un run. Un corps identique renvoie la décision enregistrée. Un 403 avec api_key_scope_denied signifie que la clé n’a pas la portée policy:evaluate.
Étape 7 : trancher la revue dans le Decision Desk
Le réviseur ouvre Decision Desk, ouvre la demande de case-1003, choisit Approve decision ou Reject decision, indique un motif, confirme et sélectionne Record decision. KLA enregistre la décision avec la demande et dans l'Audit Trail.
Dans ce guide, l'agent s'arrête à require_approval et n'ouvre pas le compte. Renvoyer la même demande renvoie la décision enregistrée require_approval. Pour un agent qui attend le réviseur puis continue, utilisez l'action ingress décrit dans la dernière section de cette page.
Étape 8 : retrouver les décisions dans l'Audit Trail
Ouvrez Audit Trail dans la Console et recherchez case-1003. La séquence enregistrée montre la décision de politique avec la clé API comme acteur, la Decision Request et la décision du réviseur, chacune avec sa somme de contrôle d'entrée et sa racine de preuve.
Étape 9 : exporter un lot de preuves scellé
Exportez les décisions gouvernées, les approbations et les entrées d'audit avec la CLI kla. Le lot est signé et liste l'empreinte de chaque fichier qu'il contient.
# Export the last 30 days to a local directory
kla evidence export --tenant $KLA_TENANT_ID --days 30 --out ./evidence
# Export an exact time window
kla evidence export --tenant $KLA_TENANT_ID \
--start-date 2026-07-01 \
--end-date 2026-07-31 \
--out ./evidence
Vérifiez le lot hors ligne avant de le remettre à un auditeur :
kla evidence verify ./evidence/<bundle file>.zip
Ensuite : laisser KLA exécuter l'outil
Dans ce guide, l'agent exécute son propre outil après chaque décision. Pour que KLA exécute l'outil au moyen d'un connecteur enregistré, l'agent envoie chaque action à POST /v1/agent-actions avec KLAClient et une clé API dotée de la portée actions:propose. Cette voie nécessite une Release approuvée et déployée ainsi qu'un mandat actif, qui fixe les outils, opérations, destinations et finalités sur lesquels l'agent peut agir :
PRODUCTION_AGENT_ID='<registered production Agent UUID>'
kla agent mandate create "$PRODUCTION_AGENT_ID" --file mandate.yaml
kla agent mandate submit <mandate ID>
kla agent mandate get <mandate ID>
kla agent mandate approve <mandate ID> --version <version> --reason 'Scope matches the onboarding procedure.'
kla agent mandate activate <mandate ID>
Ce démarrage rapide a enregistré un Agent de développement. Une Release de production nécessite un Agent distinct enregistré avec governanceIntent.environment: production et une politique publiée limitée à production qui autorise cet Agent. Gouverner un Agent de bout en bout présente cet enregistrement et la Release. Conservez l’enregistrement de développement. Utilisez l’identifiant de l’Agent de production pour son mandat ; une autre personne approuve la version exacte renvoyée par get. Démarrage rapide du SDK de gouvernance (Python) couvre le fichier de mandat et la première action gouvernée.
