Per iniziare

Guida rapida

Passa dall'accesso a decisioni registrate allow, warn, require_approval e block, a un'approvazione nel Decision Desk e a un pacchetto di prove sigillato in circa 30 minuti.

10 min di lettura2298 parole

Questa guida porta una postazione di sviluppo dall'accesso a una decisione registrata per ogni esito di policy: allow, warn, require_approval e block. Registri un agente di onboarding KYC e il suo strumento, pubblichi una policy KYC iniziale, la chiami dall'SDK di governance con una chiave API del tenant, fai decidere il caso require_approval a una seconda persona nel Decision Desk e trovi ogni decisione nell'Audit Trail. I comandi puntano all'ambiente di sviluppo KLA all'indirizzo https://api.dev.kla.digital. Prevedi circa 30 minuti.

Servono due persone nel tenant: tu, amministratore del tenant che crea le policy, e un revisore con il ruolo kla:approver. KLA rifiuta una revisione o un'approvazione fatta dalla persona che ha presentato la richiesta.


Eseguire la CLI KLA

La CLI KLA viene eseguita al momento da questo monorepo. Dalla radice del repository, compilala con Node.js 20.x:

pnpm install --frozen-lockfile
pnpm kla --help
alias kla='pnpm --silent kla'

Il wrapper del repository ricompila la CLI quando un file sorgente è più recente del suo output generato. I passi seguenti usano anche jq.


Passo 1: accedi

Crea un contesto CLI per il tuo tenant, poi accedi dal browser. La CLI usa il client pubblico kla-cli con PKCE e non memorizza alcun client secret.

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 mostra il tenant a cui corrisponde la tua credenziale. Il revisore esegue gli stessi comandi nel proprio terminale e accede con il proprio account. Vedi Autenticazione.


Passo 2: registra l'agente e il suo strumento

Scrivi un manifest dell'agente. governanceIntent.environment dichiara dove l'agente è governato: development, staging o 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)

Una policy limitata a questo agente (scope.agentIds) può entrare in revisione solo se il suo scope.environments indica lo stesso ambiente. Imposta governanceIntent alla registrazione: un Draft successivo non può modificarlo.

KLA decide solo sugli strumenti presenti nel Tool Catalog del tenant. Registra lo strumento che l'agente chiama, con i campi che la policy legge:

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

Se il tenant ha già lo strumento, tools.create risponde CONFLICT e la voce esistente resta invariata.


Passo 3: scrivi la policy e simula quattro esiti

La policy KYC iniziale rifiuta una corrispondenza con le liste sanzioni, invia una persona politicamente esposta (PEP) o un paese ad alto rischio a una seconda persona, segnala il rischio medio e apre i conti a basso rischio. Qualsiasi altra richiesta, ad esempio un punteggio di rischio superiore a 70 o un campo mancante, porta alla decisione predefinita 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"
      ] }
    }
  ]
}
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 è true. lint.issues include un avviso POLICY_BAND_GAP per i punteggi superiori a 70. Esamina i risultati del lint e la decisione predefinita block della policy prima di inviare la bozza alla revisione. POLICY_ID deve essere nuovo nel tenant; se kla policy registry get "$POLICY_ID" trova già una policy, scegli un altro ID.

Simula una richiesta per esito. Una Simulazione valuta la bozza senza registrare una decisione reale, e la revisione richiede una Simulazione memorizzata della bozza esatta:

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"}

Invia la bozza in revisione:

kla policy registry submit-review "$POLICY_ID" 1.0.0 --comment 'Starter KYC onboarding gate.'

Passo 4: rivedi e pubblica la policy

Il revisore approva la versione dal proprio terminale:

kla policy registry review kyc-onboarding-gate 1.0.0 \
  --decision approve --comment 'Four outcomes match the KYC procedure.' --yes

Tu la pubblichi. La pubblicazione parte subito, a meno che tu non passi --effective-from, e apre una Decision Request per il revisore:

kla policy registry publish "$POLICY_ID" 1.0.0 --reason 'First KYC onboarding gate.' --yes

Il revisore apre Decision Desk nella Console all'indirizzo https://kla.digital, apre Publish KYC onboarding gate 1.0.0?, apre Policy version diff e Lint report, sceglie Approve publish, indica un motivo e seleziona Record decision. La versione risulta quindi pubblicata.


Passo 5: crea una chiave API per l'agente

L'agente si autentica con una chiave API del tenant. Una chiave con l'ambito policy:evaluate può richiedere decisioni e nient'altro:

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 restituisce la chiave una sola volta. Conservala nel tuo secret manager, tieni apiKey.id per la revoca ed elimina api-key.json. Un amministratore del tenant può creare la chiave anche in Settings > API keys.


Passo 6: richiedi decisioni dall'SDK di governance

L'SDK di governance chiede a KLA una decisione prima che l'agente chiami uno strumento. KLA registra ogni decisione con i tuoi identificativi di esecuzione e di punto di controllo e non esegue nulla; l'agente agisce in base al risultato. Una decisione require_approval apre una Decision Request nel Decision Desk.

Create una volta un file di identità per questo gruppo di azioni e conservatelo per i tentativi successivi. Entrambi gli esempi leggono il timestamp originale e usano identificativi di run distinti per Python e Node.js. Usate un nuovo file per un nuovo gruppo.

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,
    agentId: process.env.AGENT_ID,
    environment: 'development',
    policyId: process.env.POLICY_ID,
  });
  console.log(caseId, result.decision, result.ruleId, result.approval?.approvalId ?? '');
}

Ciascuno dei due programmi stampa una decisione per richiesta:

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

Un nuovo tentativo della stessa azione richiede i valori originali di runId, gateId, occurredAt, argomenti dello strumento, Agent, policy e ambiente. Il SDK conserva il corpo durante i tentativi automatici. Una chiamata SDK separata senza timestamp esplicito genera un nuovo occurredAt; riutilizzare lo stesso run e gate restituisce quindi 409. Conservate kyc-action-identity.env e gli input originali per rieseguire questi esempi. Assegnate un nuovo gateId a ogni azione distinta in un run. Un corpo identico restituisce la decisione registrata. Un 403 con api_key_scope_denied indica che alla chiave manca l’ambito policy:evaluate.


Passo 7: decidi la revisione nel Decision Desk

Il revisore apre Decision Desk, apre la richiesta di case-1003, sceglie Approve decision o Reject decision, indica un motivo, conferma e seleziona Record decision. KLA registra la decisione nella richiesta e nell'Audit Trail.

In questa guida l'agente si ferma a require_approval e non apre il conto. Inviare di nuovo la stessa richiesta restituisce la decisione registrata require_approval. Per un agente che attende il revisore e poi prosegue, usa l'action ingress descritto nell'ultima sezione di questa pagina.


Passo 8: trova le decisioni nell'Audit Trail

Apri Audit Trail nella Console e cerca case-1003. La sequenza registrata mostra la decisione di policy con la chiave API come attore, la Decision Request e la decisione del revisore, ciascuna con il checksum della voce e la radice della prova.


Passo 9: esporta un pacchetto di prove sigillato

Esporta decisioni governate, approvazioni e voci di audit con la CLI kla. Il pacchetto è firmato ed elenca l'hash di ogni file che contiene.

# 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

Verifica il pacchetto offline prima di consegnarlo a un revisore contabile:

kla evidence verify ./evidence/<bundle file>.zip

Prossimo passo: lascia che KLA esegua lo strumento

In questa guida l'agente esegue il proprio strumento dopo ogni decisione. Perché sia KLA a eseguire lo strumento tramite un connettore registrato, l'agente invia ogni azione a POST /v1/agent-actions con KLAClient e una chiave API con l'ambito actions:propose. Questo percorso richiede una Release approvata e distribuita e un mandato attivo, che stabilisce gli strumenti, le operazioni, le destinazioni e le finalità su cui l'agente può agire:

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>

Questa guida rapida ha registrato un Agent di sviluppo. Una Release di produzione richiede un Agent separato registrato con governanceIntent.environment: production e una policy pubblicata limitata a production che consenta quell’Agent. Governare un Agent end-to-end mostra la registrazione e la Release. Mantieni la registrazione di sviluppo. Usa l’ID dell’Agent di produzione per il mandato; un altro revisore approva la versione esatta restituita da get. Guida rapida all’SDK di governance (Python) tratta il file del mandato e la prima azione governata.

Guida rapida | Developer Docs | KLA Control Plane