Se lektionens video: Sikring af AI-agenter med kryptografiske kvitteringer
(Lektionsvideo og thumbnail tilføjes af Microsoft-indholdsteamet efter sammenfletning, i overensstemmelse med lektion 14 / 15 mønsteret.)
Denne lektion vil dække:
Efter at have gennemført denne lektion vil du vide, hvordan du:
Forestil dig, at du har implementeret en AI-agent for Contoso Travel. Agenten læser kunders forespørgsler, kalder en fly-API for at finde muligheder og booker sæder på kundens vegne. I sidste kvartal behandlede agenten 50.000 bookinger.
I dag ankommer en revisor. De stiller et simpelt spørgsmål: “Vis mig, hvad din agent gjorde.”
Du overleverer dine logfiler. Revisoren ser på dem og stiller det sværere spørgsmål: “Hvordan ved jeg, at disse logs ikke er blevet redigeret?”
Dette er problemet med revisionssporet. De fleste agent-implementeringer i dag stoler på:
Ingen af disse kan besvare revisorens spørgsmål uden at kræve, at revisoren stoler på nogen (dig, din cloud-udbyder, din databaseleverandør). Til intern brug er den tillid ofte acceptabel. For regulerede arbejdsbelastninger (finans, sundhedsvæsen, alt underlagt EU’s AI-lov) er det ikke.
Kryptografiske kvitteringer løser dette ved at gøre hver agents handling uafhængigt verificerbar. Revisoren behøver ikke at stole på dig. De behøver kun din offentlige nøgle og selve kvitteringen.
En kvittering er et JSON-objekt, der registrerer, hvad en agent gjorde, signeret med en digital signatur.
flowchart LR
A[Agenten kalder et værktøj] --> B[Byg kvitteringsdata]
B --> C[Kanoniser JSON RFC 8785]
C --> D[SHA-256 hash]
D --> E[Ed25519 signér]
E --> F[Kvittering med signatur]
F --> G[Revisor verificerer offline]
G --> H{Signatur gyldig?}
H -- yes --> I[Manipulationssikker bevis]
H -- no --> J[Kvittering afvist]
En minimal kvittering ser sådan ud:
{
"type": "agent.tool_call.v1",
"agent_id": "contoso-travel-bot",
"tool_name": "lookup_flights",
"tool_args_hash": "sha256:a3f9c1...",
"result_hash": "sha256:7b2e1d...",
"policy_id": "contoso-travel-policy-v3",
"timestamp": "2026-04-25T14:30:00Z",
"sequence": 47,
"previous_receipt_hash": "sha256:9d4e6a...",
"signature": {
"alg": "EdDSA",
"sig": "c5af83...",
"public_key": "8f3b2c..."
}
}
Tre egenskaber udfører arbejdet:
Signaturen. Kvitteringen signeres af agentens gateway med en Ed25519-privatnøgle. Enhver med den tilhørende offentlige nøgle kan verificere signaturen offline. Manipulation af et hvilket som helst felt ugyldiggør signaturen.
Kanonisk kodning. Før underskrivelse serialiseres kvitteringen ved brug af JSON Canonicalization Scheme (JCS, RFC 8785). Det sikrer, at to implementeringer, der producerer den samme logiske kvittering, producerer byte-identisk output. Uden kanonisering ville forskellige JSON-serialisatorer producere forskellige signaturer for det samme indhold.
Hash-kædning. Feltet previous_receipt_hash linker hver kvittering til den forrige. Fjernelse eller omrokering af en kvittering bryder hver efterfølgende kvittering. Manipulation bliver synlig på kædeniveau, selv hvis enkelte signaturer omgås.
Sammen giver disse egenskaber tre garantier:
Du behøver ikke et specielt bibliotek for at producere en kvittering. De kryptografiske primitive er bredt tilgængelige, og logikken fylder få dusin linjer Python.
De praktiske øvelser i code_samples/18-signed-receipts.ipynb gennemgår hele flowet. Her er opsummeringen:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 kanonisk JSON
def b64url_nopad(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")
def sha256_canonical(obj) -> str:
"""SHA-256 of a Python object's JCS-canonical JSON form."""
return f"sha256:{hashlib.sha256(canonicalize(obj)).hexdigest()}"
# Generér eller indlæs en signeringsnøgle (i produktion, gem i en nøgleboks)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Byg kvitteringsindholdet (ingen signatur endnu)
tool_args = {"origin": "SYD", "destination": "LAX"}
tool_result = [{"flight": "QF11", "price": 1850, "stops": 0}]
payload = {
"type": "agent.tool_call.v1",
"agent_id": "contoso-travel-bot",
"tool_name": "lookup_flights",
"tool_args_hash": sha256_canonical(tool_args),
"result_hash": sha256_canonical(tool_result),
"policy_id": "contoso-travel-policy-v3",
"timestamp": "2026-04-25T14:30:00Z",
"sequence": 0,
"previous_receipt_hash": None,
}
# Kanoniser, hash, signer.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Vedhæft et struktureret signaturobjekt.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Det er hele underskrivningspipelinjen. Øvelserne i notebogen gennemgår hvert trin.
Verifikation er den inverse operation:
import base64
import hashlib
from nacl import signing
from nacl.exceptions import BadSignatureError
from jcs import canonicalize
def b64url_decode(s: str) -> bytes:
padding = "=" * ((4 - len(s) % 4) % 4)
return base64.urlsafe_b64decode(s + padding)
def verify_receipt(receipt: dict) -> bool:
# Signaturen er et struktureret objekt: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Genskab den nyttelast, der faktisk blev signeret (alt undtagen signaturen).
payload = {k: v for k, v in receipt.items() if k != "signature"}
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
try:
verify_key = signing.VerifyKey(b64url_decode(sig_obj["public_key"]))
verify_key.verify(message_hash, b64url_decode(sig_obj["sig"]))
return True
except BadSignatureError:
return False
Denne funktion tager en kvittering og returnerer True hvis signaturen er gyldig, False ellers. Ingen netværkskald, ingen serviceafhængighed, ingen tillid nødvendig til tredjepart.
For at se manipulation opdages i praksis gennemgår notebogen:
tool_args_hash.Dette er den praktiske demonstration af, at kvitteringer er manipulationssikre: enhver ændring, hvor lille den end er, bryder signaturen.
En enkelt signeret kvittering beskytter en handling. En kæde af kvitteringer beskytter en sekvens.
flowchart LR
R0[Kvittering 0<br/>genese] --> R1[Kvittering 1]
R1 --> R2[Kvittering 2]
R2 --> R3[Kvittering 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Hver kvittering registrerer hashen af den forrige kvittering. For at fjerne kvittering 2 stille og roligt, skulle en angriber enten:
previous_receipt_hash (ødelægger kvittering 3’s signatur), ELLERHvis den private nøgle er i en hardware key vault, og du offentliggør den offentlige nøgle med hver kvittering, er ingen af angrebene mulige uden opdagelse.
Notebogen gennemgår:
previous_receipt_hash matcher den faktiske hash af den forrige kvittering.Sådan producerer du et revisionsspor, som en ekstern revisor kan verificere uden at skulle stole på dig.
Dette er det vigtigste afsnit i denne lektion. Kvitteringer er kraftfulde, men deres magt er begrænset.
Kvitteringer beviser tre ting:
Kvitteringer beviser IKKE:
policy_id faktisk blev evalueret, eller at den ville have godkendt denne handling, hvis tjekket. Kvitteringen registrerer hvad der blev påstået, ikke hvad der blev håndhævet.Denne grænse er vigtig af to grunde:
En almindelig fejl er at antage, at “vi har kvitteringer” betyder “vi er styret.” Det gør det ikke. Kvitteringer er en grundsten. Styring er det system, du bygger ovenpå.
Punkt 3 ovenfor fortjener sit eget afsnit: en handlingskvittering siger “denne nøgle signerede dette indhold,” aldrig “et menneske godkendte dette.” For handlinger med høj risiko (refunderinger, sletninger, pengeoverførsler) kræver styringsrammer i stigende grad præcis denne manglende erklæring, og den kan produceres med de samme primitive værktøjer, du allerede byggede i denne lektion.
Følge-notebooken code_samples/human-authorization-receipts.ipynb tilføjer en anden type kvittering, human.approval.v1, i samme konvolutform som lektionens kvitteringer (en typet payload signeret med Ed25519 over dens kanoniske SHA-256, med signature objektet uden for de signerede bytes). En navngivet godkender signerer den fulde kanoniske handling og dens digest før udførelse; agentens handlingskvittering bærer den samme handle-digest og en parent_approval_ref, receipt_hash for godkendelsen, samme konvention som previous_receipt_hash i den kæde, du byggede ovenfor. Én verify_chain behandler begge artefakter under separate pinned nøgleregistre (godkendernøgler vs agentnøgler), så kodevejen er delt, men myndighederne aldrig er.
Den egenskab, dette sikrer, formuleret omhyggeligt: mennesket godkendte denne præcise handling, og agenten udførte nøjagtig den godkendte handling. Notebooks afvisningssinstitutioner er hvad der gør egenskaben virkelig snarere end påstået:
Hver fejl nægter med en distinct grund, så en revisor, der læser en afvisning, kan se, om myndighed blev forældet eller om den udførte handling ændrede sig. Reglen, notebooken lærer: en signeret godkendelse er ikke myndighed i sig selv. Myndighed eksisterer kun, hvis begge kvitteringer stadig binder til den samme kanoniske handling ved udførelsestidspunktet. Co-signaturvejen i det samme Internet-Draft, denne lektion følger (draft-farley-acta-signed-receipts), er den standardspor-form af dette mønster.
Python-koden i denne lektion er bevidst minimal, så du kan læse hver linje og forstå præcis, hvad der sker. I produktion har du to muligheder:
Byg direkte på de kryptografiske primitive. De 50 linjer, du så ovenfor, er tilstrækkelige til mange anvendelser. PyNaCl (Ed25519) og jcs-pakken (kanonisk JSON) er velvedligeholdte og reviderede biblioteker.
Brug et produktions-bibliotek til kvitteringer. Flere open source-projekter implementerer samme mønster med ekstra funktioner (nøgle-rotation, batch-verifikation, JWK Set-distribution, integration med politikmotorer):
draft-farley-acta-signed-receipts, revision 02) som aktuelt er i standardiseringsprocessen, med en delt konformitetssuite (agent-governance-testvectors) som uafhængige implementeringer krydsverificerer mod for byte-identisk kanonisk output.protect-mcp (npm) og @veritasacta/verify (npm) pakkerne giver en Node-baseret implementering af kvitteringssignering og offline verifikation, beregnet til indpakning af enhver MCP-server med et manipulationssikkert revisionsspor, inklusive et hold-for-co-sign flow, hvor en pauset handling udsender en godkendelses-kvittering bundet til handledigesten (WebAuthn-understøttet i desktopflowet), samme godkendelses-kvitteringsmønster som human-autorisation-notebooken ovenfor.pip install nobulex) leverer samme Ed25519 + JCS signaturmønster i Python med LangChain og CrewAI integrationer, inklusive offentliggjorte krydsvalideringstestvektorer og en overholdelseskortlægning bidraget via OWASP PR #2210.Valget mellem selv at bygge og at bruge et bibliotek svarer til valget mellem at skrive dit eget JWT-bibliotek eller bruge et testet: begge er rimelige; biblioteket sparer tid og reducerer audit-areal; den fra-grunden tilgang tvinger dig til at forstå hver primitive. Denne lektion lærer fra-grunden-vejen, så du har grundlaget for begge valg.
Test din forståelse, før du går videre til praksisøvelsen.
1. En kvittering er signeret med agentens private Ed25519-nøgle. Revisoren har kun den offentlige nøgle. Kan revisoren verificere kvitteringen offline?
2. En angriber ændrer feltet policy_id i en kvittering for at hævde, at den var underlagt en mere tilladende politik. Signaturen var over den oprindelige payload. Hvad sker der under verifikation?
3. Hvorfor inkluderer kvitteringen en tool_args_hash og result_hash i stedet for de rå argumenter og resultat?
4. Feltet previous_receipt_hash forbinder hver kvittering til dens forgænger. Hvis en angriber stille sletter en kvittering midt i en kæde, hvad bliver så ugyldigt?
5. En kvittering verificeres rent. Beviser det, at agentens handling var korrekt, valid eller i overensstemmelse med politikken?
Åbn code_samples/18-signed-receipts.ipynb og gennemfør alle fire sektioner:
Udvidelsesudfordring 1: udvid kvitteringsskemaet med et yderligere felt efter dit valg (for eksempel en anmodnings-ID til sporing), opdater den kanoniske signeringslogik til at inkludere det, og bekræft at kvitteringen stadig kan verifieres gennem hele processen. Ændr derefter feltet efter signering og bekræft at verifikationen fejler. Dette tvinger dig til at forstå, hvordan hver byte af den kanoniske kodning bidrager til signaturen.
Udvidelsesudfordring 2: SHA-256-hash to af dine kvitteringer sammen (sammenkæd deres kanoniske bytes i en deterministisk rækkefølge) og indlejre den resulterende digest som et nyt felt på en tredje kvittering før signering. Verificer at alle tre kvitteringer stadig kan rundrejses. Du har lige bygget et enkelt-trin inklusionsbevis: enhver, der har den tredje kvittering, kan bevise at de første to eksisterede på det tidspunkt den blev signeret, uden at skulle afsløre deres indhold. Dette er mønsteret som selective-disclosure kvitteringer bruger i stor skala (Merkle-forpligtelser, RFC 6962).
Kryptografiske kvitteringer giver AI-agenter et revisionsspor, der er:
De er ikke en erstatning for inputvalidering, håndhævelse af politik eller identitetsinfrastruktur. De er fundamentet for disse lag. Når du implementerer agenter i regulerede arbejdsbelastninger, tværorganisatoriske arbejdsgange eller enhver indstilling hvor en fremtidig revisor ikke kan antages at stole på dig, er kvitteringer måden hvorpå du gør revisionssporet ærligt.
Det vigtigste at tage med: kvitteringer beviser, hvem der sagde hvad og hvornår. De beviser ikke, at det sagte var sandt eller rigtigt. Hold det skel tæt. Det er forskellen mellem et ærligt provenienssystem og et vildledende.
Når du er klar til at rykke videre fra denne lektion til at implementere kvitteringssignerende agenter i et rigtigt miljø:
https://your-org.example.com/.well-known/agent-keys.json.Deltag i Microsoft Foundry Discord for at møde andre lærende, deltage i åben kontortid og få svar på dine spørgsmål om AI-agenter.
Denne lektion dækker enkelt-kvitteringssignering og hash-kædede sekvenser. De samme primitive byggekonstruktioner sammensætter flere mere avancerede mønstre, du kan støde på, efterhånden som din styringsholdning modnes:
authorization_*) og efter-eksekvering (result_*) halvdele med uafhængige signaturer, nyttigt når autorisationsbeslutningen og det observerede resultat produceres af forskellige aktører eller på forskellige tidspunkter. Dette bygger ovenpå kvitteringsformatet undervist i denne lektion.result_hash. Payloads i virkeligheden er ofte rigere end et enkelt værktøjsopkalds resultat: beslutningsforberedelse (modelprediktion, overvejede muligheder, beviser og deres fuldstændighed, risikoposition, ansvarskæde, gate udfald) kan alle være indeholdt, forseglet af en enkelt kvittering. Dette holder kvitteringsformatet minimalt samtidig med at payloadskemaer kan udvikle sig domæne-for-domæne.signature.alg feltet kan bære ML-DSA-65 (NIST post-kvantelig signaturstandard), når du har behov for migrering. Planlæg en overgangsperiode hvor kvitteringer er dualsignerede.Oprettelse af lokale AI-agenter
Ansvarsfraskrivelse: Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten Co-op Translator. Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.