Primeros pasos

Guía de inicio rápido

Pasa del inicio de sesión a decisiones registradas allow, warn, require_approval y block, una aprobación en el Decision Desk y un paquete de evidencia sellado en unos 30 minutos.

11 min de lectura2438 palabras

Esta guía lleva un puesto de desarrollo desde el inicio de sesión hasta una decisión registrada para cada resultado de política: allow, warn, require_approval y block. Registras un agente de alta de clientes KYC y su herramienta, publicas una política KYC inicial, la llamas desde el SDK de gobernanza con una clave API del tenant, una segunda persona decide el caso require_approval en el Decision Desk y encuentras cada decisión en el Audit Trail. Los comandos apuntan al entorno de desarrollo de KLA en https://api.dev.kla.digital. Calcula unos 30 minutos.

Necesitas dos personas en el tenant: tú, como administrador del tenant que crea políticas, y un revisor con el rol kla:approver. KLA rechaza una revisión o aprobación hecha por la persona que presentó la solicitud.


Ejecutar la CLI de KLA

La CLI de KLA se ejecuta actualmente desde este monorepo. Desde la raíz del repositorio, compílala con Node.js 20.x:

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

El wrapper del repositorio recompila la CLI cuando un archivo fuente es más reciente que su salida generada. Los pasos siguientes también usan jq.


Paso 1: Inicia sesión

Crea un contexto de CLI para tu tenant e inicia sesión en el navegador. La CLI usa el cliente público kla-cli con PKCE y no almacena ningún secreto de cliente.

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 muestra el tenant al que corresponde tu credencial. El revisor ejecuta los mismos comandos en su propio terminal e inicia sesión con su cuenta. Consulta Autenticación.


Paso 2: Registra el agente y su herramienta

Escribe un manifiesto de agente. governanceIntent.environment declara dónde se gobierna el agente: 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 política limitada a este agente (scope.agentIds) solo puede pasar a revisión cuando su scope.environments nombra el mismo entorno. Define governanceIntent en el registro: un Draft posterior no puede cambiarlo.

KLA solo decide sobre herramientas que figuran en el Tool Catalog del tenant. Registra la herramienta que llama el agente, con los campos que lee la política:

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 el tenant ya tiene la herramienta, tools.create responde CONFLICT y la entrada existente se mantiene.


Paso 3: Escribe la política y simula cuatro resultados

Las reglas de la política leen los argumentos de la herramienta en context.action.toolArgs.<field>. La entrada del agente expone action.parameters en esa misma ruta. Las entradas validadas también admiten tool.<toolId>.input.<field>.

La política KYC inicial rechaza una coincidencia de sanciones, envía a una persona políticamente expuesta (PEP) o un país de alto riesgo a una segunda persona, marca el riesgo medio y abre las cuentas de riesgo bajo. Cualquier otra solicitud, como una puntuación de riesgo superior a 70 o un campo ausente, se resuelve con la decisión por defecto 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 es true. lint.issues incluye una advertencia POLICY_BAND_GAP para puntuaciones superiores a 70. Revisa los resultados del lint y la decisión predeterminada block de la política antes de enviar el borrador a revisión. POLICY_ID debe ser nuevo en el tenant; si kla policy registry get "$POLICY_ID" ya encuentra una política, elige otro ID.

Simula una solicitud por resultado. Una Simulación evalúa el borrador sin registrar una decisión en vivo, y la revisión exige una Simulación almacenada del borrador exacto:

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

Envía el borrador a revisión:

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

Paso 4: Revisa y publica la política

El revisor aprueba la versión desde su propio terminal:

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

Tú la publicas. La publicación empieza ahora salvo que indiques --effective-from, y abre una Decision Request para el revisor:

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

El revisor abre Decision Desk en la Console en https://kla.digital, abre Publish KYC onboarding gate 1.0.0?, abre Policy version diff y Lint report, elige Approve publish, indica un motivo y selecciona Record decision. La versión queda publicada.


Paso 5: Crea una clave API para el agente

El agente se autentica con una clave API del tenant. Una clave con el alcance policy:evaluate puede solicitar decisiones y nada más:

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 devuelve la clave una sola vez. Guárdala en tu gestor de secretos, conserva apiKey.id para revocarla y elimina api-key.json. Un administrador del tenant también puede crear la clave en Settings > API keys.


Paso 6: Solicita decisiones desde el SDK de gobernanza

El SDK de gobernanza pide a KLA una decisión antes de que el agente llame a una herramienta. KLA registra cada decisión con tus identificadores de ejecución y de punto de control y no ejecuta nada; el agente actúa según el resultado. Una decisión require_approval abre una Decision Request en el Decision Desk.

Cree un archivo de identidad una vez para este lote de acciones y consérvelo para los reintentos. Ambos ejemplos leen su marca de tiempo original y usan identificadores de run separados para Python y Node.js. Use un archivo nuevo para un lote nuevo.

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 ?? '');
}

Cualquiera de los dos programas imprime una decisión por solicitud:

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

Reintentar la misma acción requiere los valores originales de runId, gateId, occurredAt, argumentos de la herramienta, Agent, política y entorno. El SDK conserva el cuerpo durante sus reintentos automáticos. Una llamada SDK separada sin una marca de tiempo explícita genera un nuevo occurredAt; reutilizar el mismo run y gate devuelve entonces 409. Conserve kyc-action-identity.env y las entradas originales al volver a ejecutar estos ejemplos. Asigne un nuevo gateId a cada acción distinta de un run. Un cuerpo idéntico devuelve la decisión registrada. Un 403 con api_key_scope_denied indica que a la clave le falta el alcance policy:evaluate.


Paso 7: Decide la revisión en el Decision Desk

El revisor abre Decision Desk, abre la solicitud de case-1003, elige Approve decision o Reject decision, indica un motivo, confirma y selecciona Record decision. KLA registra la decisión en la solicitud y en el Audit Trail.

En esta guía el agente se detiene en require_approval y no abre la cuenta. Enviar de nuevo la misma solicitud devuelve la decisión registrada require_approval. Para un agente que espera al revisor y después continúa, usa el action ingress de la última sección de esta página.


Paso 8: Encuentra las decisiones en el Audit Trail

Abre Audit Trail en la Console y busca case-1003. La secuencia registrada muestra la decisión de política con la clave API como actor, la Decision Request y la decisión del revisor, cada una con su suma de verificación de entrada y su raíz de prueba.


Paso 9: Exporta un paquete de evidencia sellado

Exporta decisiones gobernadas, aprobaciones y entradas de auditoría con la CLI kla. El paquete está firmado e incluye el hash de cada archivo que 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 el paquete sin conexión antes de entregarlo a un auditor:

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

Siguiente: deja que KLA ejecute la herramienta

En esta guía el agente ejecuta su propia herramienta después de cada decisión. Para que KLA ejecute la herramienta mediante un conector registrado, el agente envía cada acción a POST /v1/agent-actions con KLAClient y una clave API con el alcance actions:propose. Esa vía requiere una Release aprobada y desplegada y un mandato activo, que indica las herramientas, operaciones, destinos y finalidades sobre los que el agente puede actuar:

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>

Este inicio rápido registró un Agente de desarrollo. Una Release de producción requiere otro Agente registrado con governanceIntent.environment: production y una política publicada limitada a production que permita a ese Agente. Gobernar un Agente de extremo a extremo muestra ese registro y la Release. Conserva el registro de desarrollo. Usa el ID del Agente de producción para su mandato; otro revisor aprueba la versión exacta que devuelve get. Inicio rápido del SDK de gobernanza (Python) cubre el archivo de mandato y la primera acción gobernada.

Guía de inicio rápido | Developer Docs | KLA Control Plane