Guides

Gobernar un agente existente con una política

Simule, revise, publique y vincule una política de pagos de cierre por defecto a un Agente que ya existe en el KLA Control Plane.

12 min de lectura2599 palabras

Esta guía añade una política revisada a un Agente que ya existe en el KLA Control Plane. La política permite llamadas payments.create de 10.000 EUR o menos, envía los pagos mayores al Decision Desk y bloquea toda solicitud de pago que no coincida con ninguna de las dos reglas. La versión publicada se limita a este único Agente, de modo que los demás Agentes de producción del tenant no se ven afectados.

Un borrador limitado a un Agente solo puede enviarse a revisión cuando ese Agente ya tiene un entorno gobernado, y un Agente recibe un entorno gobernado únicamente mediante una vinculación de política desplegada. Por eso la secuencia tiene dos versiones de política: la versión 1.0.0 se revisa y se vincula para dar su entorno al Agente y nunca se publica; la versión 1.1.0 nombra al Agente en su alcance, se publica y se vincula como política aplicada.

Cada comando está listo para copiar. Los comandos crean registros en el tenant seleccionado, así que use un tenant que controle.

Antes de empezar

Necesita:

  • la CLI kla compilada desde el repositorio como se describe en la guía de instalación;
  • jq;
  • un Agente existente cuya herramienta de pago se llame payments.create y reciba un amount numérico y una currency de tipo cadena;
  • un token de acceso maker de un miembro que pueda crear, simular y publicar políticas, y configurar y desplegar Agentes;
  • un token de acceso checker de un miembro distinto que tenga el rol kla:approver, que revisa políticas, decide Decision Requests y aprueba Releases de Agente.

La CLI lee KLA_ACCESS_TOKEN. El token maker permanece exportado durante toda la guía. Cada comando checker recibe el token checker solo para ese comando. Mantenga ambos tokens fuera de archivos y argumentos de comando.

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 muestra las reclamaciones no secretas de cada token. Los dos sujetos deben ser distintos: la API rechaza una revisión o aprobación realizada por la identidad que hizo la solicitud. El modo de publicación del tenant debe ser two_person; en modo single_actor el paso de publicación registra su propia aprobación y el paso 7 no tiene ninguna Decision Request que decidir.

Paso 1: Identificar el Agente

Busque en el Agent Registry, copie el UUID del resultado y guarde el registro del Agente. El Agente no debe tener ningún borrador pendiente: una vinculación de política se fusiona en un borrador existente y el checker aprobaría entonces cambios no relacionados junto con la vinculación.

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 existe un borrador pendiente, su manifest aparece en esa salida del historial. Haga que el checker lo apruebe o que el maker lo descarte antes de continuar.

La política siguiente nombra la herramienta payments.create y los argumentos amount y currency. Si su Agente usa otro nombre de herramienta o de argumento, cambie esos valores en la política y en ambas entradas de Simulación.

Paso 2: Escribir la política de cierre por defecto

Guarde este archivo como policy.template.json. La versión 1.0.0 se limita al entorno gobernado production y es la versión de arranque. El paso 6 restringe el alcance al Agente para la versión 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 primera regla permite la puerta de ejecución. Una política vinculada se convierte en la primera policy_gate del Process gestionado del Agente, y esa puerta se evalúa con el actionType policy_gate y el ID de la política como toolName; sin esta regla, el valor predeterminado de cierre detendría cada ejecución antes de cualquier llamada a herramienta. Las dos reglas de pago se aplican a las solicitudes de llamada a herramienta, cuyo actionType es tool_input o tool_call, y son mutuamente excluyentes. Un importe ausente, cero o negativo, otra moneda u otra herramienta no coincide con ninguna regla y se resuelve con la defaultDecision block. La condición > 0 importa: un importe ausente se evalúa como 0 en las expresiones de regla y, de otro modo, cumpliría <= 10000. approverGroup se convierte en el rol requerido de la Decision Request, por lo que nombra el rol configurado kla:approver. El lint de políticas rechaza una defaultDecision allow con POLICY_DEFAULT_DECISION_FAIL_OPEN.

Inserte el ID externo del tenant y cree el borrador de arranque:

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

Paso 3: Ejecutar dos Simulaciones durables

Una Simulación envía al borrador un GateContext, el sobre de solicitud que evalúa el KLA Policy Engine. El Policy Registry guarda cada Simulación completada con una referencia de evidencia, y la preparación para revisión exige una Simulación guardada del contenido exacto del borrador.

Guarde el GateContext de valor bajo como 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"
    }
  }
}

Guarde el GateContext de valor alto como 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"
    }
  }
}

Inserte los identificadores del tenant y del Agente y ejecute ambas Simulaciones. kla policy registry simulate envía la cabecera Idempotency-Key que exige la ruta.

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

Ambas comprobaciones deben superarse antes de enviar el borrador. Cualquier edición posterior del borrador cambia su hash de contenido y exige una nueva Simulación.

Paso 4: Enviar y aprobar la versión de arranque

El maker envía la versión 1.0.0 y el checker la aprueba. La versión permanece approved y nunca se publica, por lo que nunca entra en vigor para ninguna puerta.

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

Una respuesta 409 con readiness enumera la comprobación bloqueante. required_simulation_missing significa que la Simulación guardada no coincide con el contenido actual del borrador; repita el paso 3.

Paso 5: Dar al Agente su entorno gobernado

kla agent policy bind acepta una política aprobada, publicada o activa. Escribe la vinculación en un nuevo borrador del Agente, crea el Process gestionado del entorno gobernado y devuelve el UUID del borrador. El checker aprueba el borrador, lo que lo publica como Release inmutable, y el maker despliega la Release. agent release deploy exige una vinculación production y mueve el manifiesto activo del Agente, que es donde se lee el entorno gobernado durante la revisión de políticas.

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

Desde este punto y hasta completar el paso 8, el Process gestionado del Agente hace referencia a una versión de política no publicada y sus ejecuciones fallan de forma cerrada.

Paso 6: Crear, simular y revisar la versión limitada al Agente

La versión 1.1.0 tiene las mismas reglas y nombra al Agente en scope.agentIds. Se reutilizan las entradas de Simulación del paso 3.

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 comprobación de preparación para revisión resuelve cada Agente de scope.agentIds en el Agent Registry y exige que cada uno tenga un entorno gobernado que coincida con scope.environments. El paso 5 lo garantizó para este Agente.

Paso 7: Publicar

El maker solicita la publicación. En modo two_person, la solicitud devuelve pendingApproval: true y un identificador 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)"

El checker decide la Decision Request mediante 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 publicación se ejecuta después de registrar la decisión. Espere a que la versión alcance published o active y luego lea el paquete de política compilado.

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 es PASS cuando el tenant firma los paquetes de política y SKIPPED cuando la firma está desactivada en ese entorno. Deténgase si la versión sigue sin publicarse o si manifestHashVerified es false.

Paso 8: Vincular la versión publicada

La vinculación selecciona la versión activa o publicada de la política, así que el mismo comando de vinculación fija ahora la versión 1.1.0 y actualiza el Process gestionado. El checker aprueba el nuevo borrador y el maker lo despliega.

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 comprobación del historial muestra al checker que el borrador contiene la vinculación de la versión 1.1.0 antes de la aprobación.

Paso 9: Verificar

Lea el Agente y su historial 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

Ejecute ambas Simulaciones contra la versión publicada y conserve los resultados junto con el registro del cambio.

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

Ahora tiene una política publicada y limitada al Agente cuyas Simulaciones resuelven 500 EUR en allow y 15.000 EUR en require_approval, y una Release de Agente desplegada que la vincula. Una llamada real payments.create por encima de 10.000 EUR desde este Agente abre una Decision Request en el Decision Desk para el rol kla:approver. La guía Gobernar un agente de extremo a extremo cubre la ejecución, la aprobación y la exportación de evidencia posteriores.

Gobernar un agente existente con una política | Developer Docs | KLA Control Plane