Žiūrėkite pamokos vaizdo įrašą: AI agentų apsauga su kriptografiniais kvitais
(Pamokos vaizdo įrašą ir miniatiūrą po sujungimo pridės Microsoft turinio komanda, atitinkant pamokų 14 / 15 modelį.)
Šioje pamokoje aptarsime:
Baigę šią pamoką žinosite, kaip:
Įsivaizduokite, kad įdiegėte AI agentą Contoso Travel. Agentas skaito kliento užklausas, iškviečia skrydžių API, kad surastų variantus, ir rezervuoja vietas kliento vardu. Praėjusį ketvirtį agentas apdorodavo 50 000 rezervacijų.
Šiandien atvyksta auditorius. Jis užduoda paprastą klausimą: „Parodykite, ką darė jūsų agentas.“
Jūs perduodate žurnalo failus. Auditoriui tikrinant, jis užduoda sunkesnį klausimą: „Kaip žinau, kad šie žurnalai nebuvo redaguoti?“
Tai yra audito takelio problema. Šiandien dauguma agentų diegimų pasikliauja:
Joks iš jų negali atsakyti į auditoriaus klausimą be būtinybės pasitikėti kažkuo (jumis, jūsų debesų tiekėju ar duomenų bazės pardavėju). Vidaus naudojimui toks pasitikėjimas dažnai priimtinas. Tačiau reglamentuojamoms veikloms (finansai, sveikatos priežiūra, viskas, kas yra dėl ES AI reglamento) jis nepriimtinas.
Kriptografiniai kvitai išsprendžia šią problemą leisdami nepriklausomai patikrinti kiekvieną agento veiksmą. Auditorius neprivalo jums pasitikėti. Jam reikia tik jūsų viešo rakto ir paties kvito.
Kvitas yra JSON objektas, kuris registruoja, ką agentas padarė, pasirašytas skaitmeniniu parašu.
flowchart LR
A[Agentas iškviečia įrankį] --> B[Sukurkite kvito paketą]
B --> C[Kanonizuokite JSON RFC 8785]
C --> D[SHA-256 maišos funkcija]
D --> E[Ed25519 parašas]
E --> F[Kvitas su parašu]
F --> G[Auditorius tikrina offline režimu]
G --> H{Ar parašas galiojantis?}
H -- yes --> I[Įrodymas be klastojimo pėdsakų]
H -- no --> J[Kvitas atmestas]
Minimalus kvitas atrodo taip:
{
"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..."
}
}
Trys savybės atlieka darbą:
Parašas. Kvitas pasirašomas agento vartų naudotu Ed25519 privačiu raktu. Bet kas, turintis atitinkamą viešąjį raktą, gali neprisijungęs patikrinti parašą. Bet koks lauko pakeitimas anuliuoja parašą.
Kanoniškas kodavimas. Prieš pasirašant kvitas seralizuojamas JSON kanoniškumo schemos (JCS, RFC 8785) būdu. Tai užtikrina, kad dvi skirtingos įgyvendinimo versijos, generuojančios tą pačią logišką kvito reikšmę, gaus vienodus baitų rezultatus. Be kanoniškumo, skirtingi JSON seralizatoriai būtų sugeneravę skirtingus parašus tam pačiam turiniui.
Hash grandinimas. Laukas previous_receipt_hash susieja kiekvieną kvitą su ankstesniu. Kvito pašalinimas ar permaina sulaužo visus vėliau einančius kvitus. Klastojimas tampa matomas grandinės lygyje net jei atskiri parašai pralaužiami.
Kartu šios savybės suteikia tris garantijas:
Kvito kūrimui nereikia specialios bibliotekos. Kriptografiniai primityvai plačiai prieinami, o logika – vos keliasdešimt Python eilučių.
Praktinės užduotys faile code_samples/18-signed-receipts.ipynb išsamiai paaiškina visą procesą. Santrauka:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 kanoninis 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()}"
# Generuoti arba įkelti pasirašymo raktą (gamyboje saugoti raktų saugykloje)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Sudaryti kvito duomenis (dar be parašo)
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,
}
# Kanonizuoti, išmaišyti, pasirašyti.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Prisegti struktūrizuotą parašo objektą.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Tai visas pasirašymo srautas. Užduotys knygoje išsamiai aprašo kiekvieną žingsnį.
Tikrinimas yra priešingas procesas:
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:
# Parašas yra struktūruotas objektas: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Atkurkite naudotą užrašyti krovinių duomenis (viską išskyrus parašą).
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
Ši funkcija paima kvitą ir grąžina True, jei parašas galioja, arba False kitu atveju. Nereikia jokių tinklo kvietimų, paslaugų priklausomybių ar trečiųjų šalių pasitikėjimo.
Norint pamatyti klastojimo aptikimą praktikoje, knyga aprašo:
tool_args_hash.Tai praktinis įrodymas, kad kvitai rodo klastojimą: bet koks pakeitimas, net mažas, sulaužo parašą.
Vienas pasirašytas kvitas saugo vieną veiksmą. Kvito grandinė saugo veiksmų seką.
flowchart LR
R0[Gavimas 0<br/>pradine] --> R1[Gavimas 1]
R1 --> R2[Gavimas 2]
R2 --> R3[Gavimas 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Kiekvienas kvitas įrašo prieš tai buvusio kvito hash reikšmę. Norint tyliai pašalinti 2 kvitą, užpuolėjas turėtų:
previous_receipt_hash lauką (sulaužytų 3 kvito parašą), ARBAJei privatus raktas saugomas aparatinėje saugykloje, o viešasis raktas skelbiamas kartu su kiekvienu kvitu, nei viena iš šių atakų negalima nepastebėta.
Knyga parodo, kaip:
previous_receipt_hash atitinka tikrą ankstesnio kvito hash.Tai leidžia sukurti audito takelį, kurį išorinis auditas gali patikrinti be pasitikėjimo jumis.
Tai svarbiausia pamokos dalis. Kvitas yra galingas, bet jo galia yra ribota.
Kvitai įrodo tris dalykus:
Kvitai neįrodo:
policy_id nurodyta politika buvo tikrai įvertinta arba kad ji būtų leista, jei būtų tikrinta. Kvitas registruoja tai, kas buvo teigiama, ne tai, kas buvo vykdoma.Ši riba svarbi dėl dviejų priežasčių:
Dažna klaida yra manyti, kad „turėdami kvitus“ jau „valdome procesą“. Ne. Kvitai yra pamatas. Valdymas – tai sistema, kurią jūs statote ant jų.
Šios pamokos Python kodas sąmoningai minimalus, kad galėtumėte perskaityti kiekvieną eilutę ir suprasti, kas vyksta. Gamyboje turite dvi galimybes:
Dirbti tiesiogiai su kriptografiniais primityvais. Minėtos 50 eilučių pakanka daugeliui panėkų. PyNaCl (Ed25519) ir jcs paketas (kanoninis JSON) yra gerai palaikomos ir patikrintos bibliotekos.
Naudoti gamybos kvitų biblioteką. Keli atviro kodo projektai įgyvendina tą patį modelį su papildomomis savybėmis (raktų rotacija, masinė patikra, JWK rinkinio platinimas, integracija su politikos varikliais):
draft-farley-acta-signed-receipts), kuris yra standartų procese.protect-mcp (npm) ir @veritasacta/verify (npm) pateikia Node pagrindo kvitų pasirašymo ir neprisijungus vykdomo tikrinimo įgyvendinimą, skirtą bet kokiam MCP serveriui aprišti nepriekaištingu audito takeliu.Pasirinkimas tarp savo sprendimo ir bibliotekos primena sprendimą tarp savo JWT bibliotekos rašymo ir išbandytos bibliotekos naudojimo: abi prasmingos; biblioteka taupo laiką ir mažina audito paviršių; savarankiškas kūrimas priverčia suprasti kiekvieną primityvą. Ši pamoka moko savarankiško kelio, kad turėtumėte pagrindą bet kuriam pasirinkimui.
Patikrinkite savo supratimą prieš pradedant praktikos užduotį.
1. Kvitas pasirašytas agento privačiu Ed25519 raktu. Auditorius turi tik viešąjį raktą. Ar auditorius gali neprisijungęs patikrinti kvitą?
2. Užpuolėjas pakeičia kvito policy_id lauką, kad tvirtintų, jog buvo taikoma lepesnė politika. Parašas buvo sudarytas originaliam turiniui. Kas nutinka tikrinimo metu?
3. Kodėl kvitas įrašo tool_args_hash ir result_hash, o ne žalius argumentus ir rezultatus?
4. Laukas previous_receipt_hash sujungia kiekvieną kvitą su ankstesniu. Jei užpuolėjas tyliai ištrina vieną kvitą grandinės viduryje, kas tampa negaliojančiu?
5. Kvitas patikrinamas sėkmingai. Ar tai įrodo, kad agento veiksmas buvo teisingas, patikimas ar atitiko politiką?
Atidarykite code_samples/18-signed-receipts.ipynb ir atlikite visas keturias dalis:
Iššūkis 1: išplėskite kvito schemą pridėdami papildomą pasirinktą lauką (pavyzdžiui, užklausos ID sekimui), atnaujinkite kanoninį pasirašymo logiką, kad jį įtrauktumėte ir įsitikinkite, jog kvitas vis dar teisingai patikrinamas. Tada pakeiskite tą lauką po pasirašymo ir patikrinkite, kad patikrinimas nepavyktų. Tai verčia suprasti, kaip kiekvienas kanoninės koduotės baitas prisideda prie parašo. Iššūkis 2: SHA-256 maišos du iš jūsų kvitų kartu (sujunkite jų kanoninę baitų seką nulemtoje tvarkoje) ir įterpkite gautą maišo reikšmę kaip naują lauką trečiajame kvite prieš pasirašant jį. Patikrinkite, ar visi trys kvitai vis dar grįžta į pradinę būseną. Jūs ką tik sukūrėte vieno žingsnio įtraukimo įrodymą: bet kas turintis trečiąjį kvitą gali įrodyti, kad pirmieji du egzistavo tuo metu, kai buvo pasirašytas, nereikalaujant atskleisti jų turinio. Tai yra modelis, kurį naudoja selektyvaus atskleidimo kvitai dideliu mastu (Merkle įsipareigojimai, RFC 6962).
Kryptografiniai kvitai suteikia AI agentams audito takelį, kuris yra:
Jie nėra įvesties validacijos, politikos vykdymo ar tapatybės infrastruktūros pakaitalas. Jie yra pagrindas toms sluoksniams. Kai diegiate agentus reguliuojamose darbo aplinkose, daugiainstituciniuose darbo procesuose arba bet kokioje situacijoje, kuriai būsimam auditorui negalima aklai pasitikėti, kvitai užtikrina sąžiningą audito takelį.
Svarbiausia išvada: kvitai įrodo, kas ką pasakė, kada. Jie neįrodo, kad pasakyta buvo teisinga ar teisinga. Laikykite šią skirtį griežtą. Tai skirtumas tarp sąžiningos kilmės sistemos ir klaidinančios.
Kai būsite pasiruošę pereiti nuo šios pamokos prie kvitu pasirašytų agentų diegimo tikroje aplinkoje:
https://your-org.example.com/.well-known/agent-keys.json.Prisijunkite prie Microsoft Foundry Discord, susitikite su kitais besimokančiais, dalyvaukite darbo valandose ir gaukite atsakymus į AI agentų klausimus.
Ši pamoka apima vieno kvito pasirašymą ir maišos grandinės sekas. Tie patys primityvai sudaro keletą pažangesnių modeliavimo būdų, su kuriais galite susidurti stiprinant savo valdymo politiką:
authorization_*) ir povykdymo (result_*) puses su nepriklausomais parašais, naudingomis, kai autorizavimo sprendimą ir stebėtą rezultatą kuria skirtingi asmenys ar skirtingu laiku. Tai papildomai suderinama su šios pamokos kvito formatu.result_hash. Tikrojo pasaulio turinys dažnai yra turtingesnis nei vienas įrankio kvietimo rezultatas: prieš sprendimą pagrindimas (modelio prognozė, svarstytos galimybės, įrodymai ir jų pilnumas, rizikos padėtis, atsakomybės grandinė, sprendimo rezultatas) gali gyventi viduje, užantspauduotas vienu kvitu. Tai padeda išlaikyti kvito formatą minimalų, leidžiant turinio schemoms evoliucionuoti pagal sritis.signature.alg laukas gali turėti ML-DSA-65 (NIST postkvantinis parašo standartas), kai reikalinga migracija. Planuokite pereinamąjį laikotarpį, kai kvitai yra dvigubai pasirašyti.Kompiuterio naudojimo agentų kūrimas (CUA)
(Nustatys mokymosi programos administracija)
Atsakomybės apribojimas: Šis dokumentas buvo išverstas naudojant dirbtinio intelekto vertimo paslaugą Co-op Translator. Nors siekiame tikslumo, prašome atkreipti dėmesį, kad automatiniai vertimai gali turėti klaidų ar netikslumų. Originalus dokumentas jo gimtąja kalba laikomas autoritetingu šaltiniu. Svarbiai informacijai rekomenduojama naudoti profesionalų žmogiškąjį vertimą. Mes neatsakome už jokius nesusipratimus ar neteisingą interpretaciją, kilusią naudojantis šiuo vertimu.