Agenten auf Guard umstellen

Ein schrittweiser Weg von „der Agent ruft Stripe selbst auf“ zu „der Agent schlägt vor und COLVO führt aus“: zuerst beobachten, dann von Hand freigeben, dann kleine, sichere Aktionen automatisch durchlassen.

Direkt aus dem Code geschrieben · aktualisiert am 28 Sept. 2026 · Fehlt etwas oder ist etwas falsch? Schreib uns

Checkliste

  1. Die Suite ist grün. Die aktuelle Version Ihres Agenten besteht die Erstattungs- und Kündigungs-Suite in COLVO Test, mit drei Versuchen pro Szenario.
  2. Stripe ist mit einem eingeschränkten Schlüssel verbunden. Schreibrechte für Erstattungen und Abonnements, alles andere nur lesend oder gar nicht. Nur der Executor von COLVO bekommt ihn je zu sehen.
  3. Ihr Backend stellt pro Ticket ein Mandat aus. Kunde, Zahlungen, Abonnements, Aktionen, Limits und ein Ablaufdatum – entschieden von Ihrem Code, nie vom Modell.
  4. Eine Woche Beobachtungsmodus. Der Agent handelt weiter wie bisher und meldet jede Aktion; COLVO protokolliert, was es entschieden hätte, und prüft das Ergebnis in Stripe.
  5. Mit Freigaben durchsetzen. Entfernen Sie den Stripe-Schlüssel aus dem Agenten, leiten Sie jede Aktion über POST /v1/operations und setzen Sie die automatische Freigabe auf null, sodass ein Mensch jede einzelne freigibt.
  6. Automatische Freigabe anheben. Sobald die Prüfenden jeden Tag dieselben kleinen Erstattungen freigeben, lassen Sie diese automatisch durch. Behalten Sie den Kill-Switch einen Klick entfernt.

1. Stripe mit einem eingeschränkten Schlüssel verbinden

Erstellen Sie in Stripe einen eingeschränkten Schlüssel mit Refunds: write, Subscriptions: write, Customers, Charges, PaymentIntents, Invoices: read und allem anderen auf none. Verbinden Sie ihn auf der Projektseite (Connections → Connect Stripe) zusammen mit dem Webhook-Signatur-Secret; COLVO prüft die Berechtigungen nur lesend (keine Test-Erstattung) und zeigt die Webhook-URL an, die Sie in Stripe mit den Events refund.created, refund.updated, charge.refunded, customer.subscription.updated, customer.subscription.deleted eintragen. Live-Schlüssel müssen eingeschränkt sein (rk_live_…); ein sk_live_-Schlüssel mit Vollzugriff wird abgelehnt. Der Schlüssel wird pro Organisation verschlüsselt und nie von der API zurückgegeben, in Logs angezeigt oder an ein Modell übergeben.

Noch kein Stripe-Konto? POST /v1/projects/{id}/connections/sandbox stellt Ihnen ein simuliertes bereit, mit dem Sie den gesamten Weg durchspielen können.

2. Ein Mandat aus Ihrem Backend ausstellen

Das Mandat ist die Befugnis, unter der der Agent arbeitet. Ihr Backend erstellt es, wenn ein Ticket oder Gespräch eröffnet wird – auf Grundlage von Fakten, denen es bereits vertraut (der angemeldete Kunde, seine Bestellungen), nicht auf Grundlage dessen, was das Modell sagt. Vollständige Feldreferenz: Mandate und Verbindungen.

// backend — when ticket #4711 opens (backend key, never shipped to the agent)
const mandate = await fetch('https://colvo.app/v1/mandates', {
  method: 'POST',
  headers: { authorization: `Bearer ${process.env.COLVO_BACKEND_KEY}`, 'content-type': 'application/json' },
  body: JSON.stringify({
    project_id: process.env.COLVO_PROJECT_ID,
    source_request_id: ticket.id,                         // 'ticket_4711'
    subject: { customer_id: customer.stripeId, payment_ids: orders.map((o) => o.paymentIntent), subscription_ids: [customer.subscriptionId] },
    actions: ['refund.create', 'subscription.cancel'],
    limits: { currency: 'eur', max_amount_minor: 5000, auto_allow_up_to_minor: 0, max_operations: 2,
              cancel_modes_allowed: ['period_end'], cancel_modes_review: ['immediate'] },
    expires_at: new Date(Date.now() + 24 * 3600_000).toISOString(),
  }),
}).then((r) => r.json());
// hand only mandate.mandate_id and an agent key (cak_…) to the agent

3. Zuerst beobachten (für Kunden ändert sich nichts)

Im Beobachtungsmodus behält der Agent seinen bisherigen Codepfad. Nachdem er gehandelt hat – oder entschieden hat, nicht zu handeln –, meldet er, was er getan hat. COLVO wertet dieselbe Richtlinie aus, die es durchsetzen würde, speichert die hypothetische Entscheidung und liest den Provider zurück, um zu prüfen, ob die Wirkung zur Antwort passt. Es blockiert nie, führt nie aus und benötigt nie Schreibzugriff.

POST /v1/observations – Agenten- oder Backend-Schlüssel.

{ "mandate_id": "…",                       // optional: without one the would-be decision is DENY (no authority)
  "source_request_id": "ticket_4711",
  "action": "refund.create",
  "parameters": { "payment_id": "pi_01", "amount_minor": 5000, "currency": "eur" },
  "provider_id": "re_3Nx…",                  // the refund / subscription the agent touched, if any
  "context": { "user_message": "…", "agent_reply": "Refunded €50 to your card." } }
{ "observation_id": "…", "would_decide": "REVIEW",
  "reasons": ["amount 5000 above auto-allow 0 (within max 5000)"],
  "verify_state": "PENDING",                 // → VERIFIED | MISMATCH | UNVERIFIABLE
  "flags": [] }                              // e.g. "would_deny", "state_mismatch", "no_mandate"

Senden Sie "performed": false, wenn der Agent entschieden hat, nicht zu handeln – die hypothetische Entscheidung wird trotzdem protokolliert, es gibt nur nichts zu verifizieren. Beobachtete Aktionen unter einem Mandat werden genau wie Operationen auf dessen Limits angerechnet, sodass eine zweite Erstattung, die die Gesamtsumme überschreiten würde, als DENY erscheint. Eine fehlgeschlagene Verifizierung eröffnet einen Vorfall und löst Ihre Alerts aus, auch wenn nichts blockiert wurde.

Die Seite Observations in der Konsole zeigt jede Beobachtung mit ihrer hypothetischen Entscheidung und Verifizierung sowie eine wöchentliche Zusammenfassung: wie viele Aktionen erlaubt, zur Prüfung geschickt oder abgelehnt worden wären und wie viele Antworten nicht zum Provider-Zustand passten. Wenn diese Liste eine Woche lang langweilig ist, gehen Sie zum nächsten Schritt.

4. Durchsetzen: ein Aufruf ersetzt den Stripe-Aufruf

Entfernen Sie den Stripe-Schlüssel aus der Umgebung des Agenten. Wo er bisher Stripe aufgerufen hat, schlägt er jetzt vor. Beginnen Sie mit auto_allow_up_to_minor: 0, sodass jede Aktion REVIEW ist und ein Mensch sie auf der Seite Approvals der Konsole freigibt. COLVO schickt allen Ownern und Editoren eine E-Mail, wenn eine Aktion wartet (und postet in Alert-Kanäle, die Freigaben abonniert haben), erinnert sie bei 75 % des Fensters und storniert die Operation, wenn niemand rechtzeitig entscheidet, bevor etwas gesendet wird – und informiert sie darüber.

Node / TypeScript

npm install @colvo/sdk – keine Abhängigkeiten, Node 18+. Wiederholungsversuche sind sicher: Das SDK sendet einen deterministischen Idempotenzschlüssel, sodass ein Wiederholungsversuch die ursprüngliche Operation zurückgibt, statt sie zu verdoppeln.

// before
await stripe.refunds.create({ payment_intent: 'pi_01', amount: 5000 });
return reply('Refunded €50!');

// with Guard
import { Colvo, messageFor } from '@colvo/sdk';
const colvo = new Colvo({ apiKey: process.env.COLVO_AGENT_KEY }); // cak_… — can only propose and read

const op = await colvo.operations.propose({
  mandate_id, source_request_id: ticket.id, action: 'refund.create',
  parameters: { payment_id: 'pi_01', amount_minor: 5000, currency: 'eur' },
  context: { user_message: lastUserMessage, agent_reply: draftReply },
});
return reply(messageFor(op)); // honest for ALLOW / REVIEW / HOLD / DENY

Python

pip install colvo – nur Standardbibliothek, Python 3.9+.

import os
from colvo import Colvo, ColvoError, message_for

colvo = Colvo(os.environ["COLVO_AGENT_KEY"])

def propose_refund(mandate_id, ticket_id, payment_id, amount_minor, user_message):
    op = colvo.operations.propose({
        "mandate_id": mandate_id, "source_request_id": ticket_id,
        "action": "refund.create",
        "parameters": {"payment_id": payment_id, "amount_minor": amount_minor, "currency": "eur"},
        "context": {"user_message": user_message},
    })
    return message_for(op)   # ColvoError(409, "business_key_conflict") if a different effect was already proposed

Kein SDK für Ihren Stack? Es ist ein einziger HTTP-Aufruf: POST /v1/operations mit Authorization: Bearer cak_…, einem Idempotency-Key-Header und dem obigen JSON-Body – siehe die Guard-API.

n8n

  1. Ersetzen Sie den Stripe-Node durch einen HTTP Request-Node: POST https://colvo.app/v1/operations, Header Authorization: Bearer {{ $env.COLVO_AGENT_KEY }}, Header Idempotency-Key: {{ $json.ticket_id }}:refund, JSON-Body wie oben.
  2. Fügen Sie einen Switch-Node auf {{ $json.decision }} mit vier Ausgängen hinzu: ALLOW, REVIEW, HOLD, DENY.
  3. Jeder Ausgang setzt den Antworttext (Tabelle unten) vor Respond to Webhook. Löschen Sie die Stripe-Zugangsdaten aus dem Workflow.

5. Dem Kunden die Wahrheit sagen

Die Entscheidung sagt dem Agenten, was tatsächlich passiert ist. Ordnen Sie sie im Code der Antwort zu, nicht im Prompt, damit das Modell keine Erstattung versprechen kann, die nie gesendet wurde.

EntscheidungWas passiert istAntwort etwa so
ALLOWEingereiht; einmal ausgeführt, dann in Stripe verifiziert„Ihre Erstattung über 50 € ist auf dem Weg zurück auf Ihre Karte.“
REVIEWWartet darauf, dass ein Mensch genau diesen Digest freigibt„Ich habe eine Kollegin gebeten, Ihre Erstattung freizugeben – Sie erhalten eine E-Mail, sobald sie erledigt ist.“
HOLDGerade nicht sicher (pausiert, Limit erreicht, Provider nicht lesbar); nichts gesendet„Ich habe Ihre Anfrage erfasst; sie wird in Kürze bearbeitet.“
DENYAußerhalb des Mandats; es wird nichts gesendet, niemals„Diesen Betrag kann ich für diese Bestellung nicht erstatten – ich kann höchstens 50 € erstatten.“
// @colvo/sdk and the Python package ship this as messageFor / message_for (with overrides); the logic, to adapt:
function messageFor(op: { decision: string; parameters: { amount_minor?: number } }) {
  const eur = ((op.parameters.amount_minor ?? 0) / 100).toFixed(2);
  switch (op.decision) {
    case 'ALLOW':  return `Your €${eur} refund is on its way back to your card.`;
    case 'REVIEW': return `I've asked a colleague to approve your €${eur} refund — you'll get an email when it's done.`;
    case 'HOLD':   return "I've logged your request; it will be processed shortly.";
    default:       return "I can't do that on this order, but I've passed your request to the team.";
  }
}

6. Automatische Freigabe anheben

Schauen Sie sich nach ein bis zwei Wochen Freigaben an, was die Prüfenden jedes Mal freigeben – typischerweise kleine Erstattungen auf eine aktuelle Zahlung des Kunden selbst. Setzen Sie auto_allow_up_to_minor in den Mandaten, die Ihr Backend ausstellt, auf diese Höhe (z. B. 2000). Alles darüber geht weiterhin an einen Menschen; alles außerhalb des Mandats bleibt DENY.

  • Kill-Switch: POST /v1/projects/{id}/pause – jeder neue Vorschlag wird zu HOLD, eingereihte Ausführungen warten.
  • Alerts: MISMATCH und UNVERIFIABLE eröffnen einen Vorfall und lösen Ihre Alert-Webhooks aus.
  • Weiter testen: Das CI-Gate bleibt bei jedem Agenten-Release aktiv.

Optional: Der Zugang liegt in Ihrem eigenen System

Viele Produkte vergeben den Zugang aus ihrer eigenen Datenbank, nicht aus Stripe. Bei Kündigungen kann COLVO den Zugang aus Ihrem Dienst lesen, sodass „behält den Zugang bis zum 31.“ durchgängig verifiziert wird. Fügen Sie eine Service-Verbindung mit einer URL und einem Signatur-Secret hinzu; COLVO ruft sie nur lesend auf:

GET https://api.yourco.com/colvo/access?customer_id=cus_01
X-Colvo-Signature: t=1790000000,v1=<hex hmac-sha256 of "t.customer_id">

200 { "customer_id": "cus_01", "access_enabled": true, "access_until": "2026-10-31T23:59:59Z" }

Nach einer period_end-Kündigung erwartet die Verifizierung access_enabled: true und access_until am Ende des bezahlten Zeitraums; nach einer immediate-Kündigung access_enabled: false. Eine Abweichung ist ein MISMATCH. Die URL unterliegt denselben Egress-Regeln wie Agenten-Endpunkte.

Möchten Sie es sehen, bevor Sie etwas anbinden? Probieren Sie die interaktive Demo aus oder buchen Sie ein 30-minütiges Gespräch, und wir schreiben das erste Mandat gemeinsam mit Ihnen.

Agenten auf Guard umstellen · Docs · COLVO