Гледайте видео урока: Осигуряване на AI агенти с криптографски разписки
(Видео урок и миниатюра ще бъдат добавени от екипа за съдържание на Microsoft след сливането, съответстващо на модела на урок 14 / 15.)
Този урок ще разгледа:
След приключване на този урок ще знаете как да:
Представете си, че сте разположили AI агент за Contoso Travel. Агентът чете заявки от клиенти, извиква API за полети, за да търси опции, и резервира места от името на клиента. През последното тримесечие агентът е обработил 50 000 резервации.
Днес пристига одитор. Той задава прост въпрос: „Покажете ми какво е направил вашият агент.“
Вие предавате лог файловете си. Одиторът ги преглежда и задава по-трудния въпрос: „Как да знам, че тези лога не са били редактирани?“
Това е проблемът със следата за одит. Повечето разгръщания на агенти днес разчитат на:
Нито един от тези не може да отговори на въпроса на одитора без да изисква той да се довери на някого (вас, вашия облачен доставчик, вашия доставчик на база данни). За вътрешна употреба това доверие често е приемливо. За регулирани товарове (финанси, здравеопазване, всичко подчинено на Законa за AI на ЕС), не е.
Криптографските разписки решават това, като правят всяко действие на агента независимо проверимо. Одиторът не трябва да ви се доверява. Той се нуждае само от вашия публичен ключ и самата разписка.
Разписката е JSON обект, който записва какво е направил агентът, подписан с цифров подпис.
flowchart LR
A[Агент извиква инструмент] --> B[Създаване на полезен товар за разписка]
B --> C[Канонизиране на JSON RFC 8785]
C --> D[SHA-256 хеш]
D --> E[Подписване с Ed25519]
E --> F[Разписка с подпис]
F --> G[Одитор проверява офлайн]
G --> H{Подписът валиден ли е?}
H -- yes --> I[Доказателство за защита от подправяне]
H -- no --> J[Разписката е отхвърлена]
Минималната разписка изглежда така:
{
"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..."
}
}
Три свойства извършват работата:
Подписът. Разписката е подписана от шлюза на агента с Ed25519 частен ключ. Всеки с отговарящия публичен ключ може да провери подписа офлайн. Манипулация с което и да е поле прави подписа невалиден.
Канонично кодиране. Преди подписване разписката се сериализира с JSON Canonicalization Scheme (JCS, RFC 8785). Това гарантира, че две имплементации, произвеждащи една и съща логическа разписка, произвеждат идентичен по байтове изход. Без канонизация, различните JSON сериализатори биха произвели различни подписи за едно и също съдържание.
Хеш вериги. Полето previous_receipt_hash свързва всяка разписка с предходната. Премахването или преподреждането на разписка нарушава всички разписки след нея. Манипулацията става видима на ниво верига, дори ако се заобиколят индивидуалните подписи.
Заедно тези свойства осигуряват три гаранции:
Не ви е нужна специална библиотека за създаване на разписка. Криптографските примитиви са широко достъпни, а логиката е няколко десетки реда Python.
Практическите упражнения в code_samples/18-signed-receipts.ipynb преминават през целия процес. Резюме:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 каноничен 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()}"
# Генерирайте или заредете подписващ ключ (на живо го съхранявайте в хранилище за ключове)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Изградете полезния товар на разписката (все още без подпис)
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,
}
# Канонизирайте, хеширайте, подпишете.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Прикрепете структурен обект за подпис.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Това е цялата подписваща последователност. Упражненията в тетрадката разглеждат всяка стъпка.
Проверката е обратната операция:
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:
# Подписът е структуриран обект: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Възстановете полезния товар, който всъщност е бил подписан (всичко освен подписа).
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
Тази функция приема разписка и връща True, ако подписът е валиден, False в противен случай. Без мрежови повиквания, без зависимост от услуга, без нужда от доверие в трети страни.
За да видите откриването на манипулации в действие, тетрадката разглежда:
tool_args_hash.Това е практическата демонстрация, че разписките са доказателство за непокътнатост: всяка промяна, колкото и малка, разваля подписа.
Една подписана разписка защитава едно действие. Верига от разписки защитава поредица.
flowchart LR
R0[Квитанция 0<br/>генезис] --> R1[Квитанция 1]
R1 --> R2[Квитанция 2]
R2 --> R3[Квитанция 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Всяка разписка записва хеша на предишната. За да се премахне разгледаната разписка 2 без следа, нападателят трябва или:
previous_receipt_hash на разписка 3 (нарушава подписа на разписка 3), ИЛИАко частният ключ е в хардуерен ключов склад и публикувате публичния ключ с всяка разписка, нито една от тези атаки не е възможна без да бъде открита.
Тетрадката преминава през:
previous_receipt_hash на всяка разписка съвпада с истинския хеш на предишната разписка.Това е начинът да произведете следа за одит, която външен одитор може да провери без да ви се доверява.
Това е най-важният раздел на този урок. Разписките са мощни, но тяхната сила е ограничена.
Разписките доказват три неща:
Разписките не доказват:
policy_id, действително е била оценена, или че би разрешила това действие, ако е била проверена. Разписката записва какво е било твърдено, не какво е било наложено.Тази граница е важна по две причини:
Честа грешка е да се предполага, че „имаме разписки“ означава „ние сме управлявани“. Не. Разписките са основа. Управлението е системата, която изграждате върху тях.
Точка 3 по-горе заслужава собствен раздел: разписката за действие казва „този ключ е подписал това съдържание“, но никога „човек е одобрил това.“ За действия с висок риск (връщания на пари, изтривания, банкови преводи) рамките за управление все повече изискват именно това липсващо изявление, което може да се произведе с помощта на същите примитиви, които вече сте изградили в този урок.
Следващата тетрадка code_samples/human-authorization-receipts.ipynb добавя втори тип разписка, human.approval.v1, със същата форма на конверта като разписките в урока (типизиран товар, подписан с Ed25519 върху каноничния му SHA-256, като обектът signature е извън подписаните байтове). Именуван одобряващ подписва пълното канонично действие и неговия хеш преди изпълнението; разписката на действието на агента носи същия хеш на действие и parent_approval_ref, receipt_hash на одобрението, същия конвенционален подход като previous_receipt_hash в горната верига. Една функция verify_chain проверява двата артефакта под отделни регистри на фиксирани ключове (ключове на одобряващите срещу ключове на агентите), така че пътят на кода е споделен, но властите никога не са.
Свойството, което това осигурява, е формулирано внимателно: човекът е одобрил точно това действие, и агентът е изпълнил точно това одобрено действие. Откривателите за отказ в тетрадката притежават това свойство като реално, а не просто декларация:
Всеки отказ връща различна причина, така че одитор, прочитащ отказа, може да разпознае дали властта е изтекла или изпълненото действие се е променило. Правилото, което тетрадката учи: подписаното одобрение само по себе си не е власт. Властта съществува само ако двете разписки все още са свързани с едно и също канонично действие към момента на изпълнение. Пътят с коподписване в същия Internet-Draft, върху който се базира този урок (draft-farley-acta-signed-receipts), е стандартизираната форма на този модел.
Python кодът в този урок е умишлено минимален, за да можете да четете всеки ред и да разберете точно какво се случва. В продукция имате два варианта:
Строите директно върху криптографските примитиви. 50-те реда, които видяхте по-горе, са достатъчни за много случаи. PyNaCl (Ed25519) и пакетът jcs (каноничен JSON) са добре поддържани и проверени библиотеки.
Използвате библиотека за разписки в продукция. Няколко проекта с отворен код прилагат същия модел с допълнителни функции (ротация на ключ, пакетна проверка, разпространение на JWK набор, интеграция с двигатели за политиките):
draft-farley-acta-signed-receipts, версия 02), в процес на стандартизация, с общ набор от тестове за съответствието (agent-governance-testvectors), който независими реализации кръстосано проверяват за идентичен байтов изход.protect-mcp (npm) и @veritasacta/verify (npm) предоставят Node-реализация на подписване и офлайн проверка на разписки, предназначена за обвиване на всеки MCP сървър с доказуема следа за одит, включително поток с държане за коподпис, в който спряно действие издава одобрителна разписка, обвързана с хеша на действието (подкрепена с WebAuthn в настолната версия), същият модел за одобрителна разписка като човешкия авторизационен пример по-горе.pip install nobulex) предоставя същия модел за подписване Ed25519 + JCS в Python с интеграции за LangChain и CrewAI, включително публикувани тестови вектори за кръстосана проверка и карта за съответствие, дарена чрез OWASP PR #2210.Изборът между създаване на собствено решение и използване на библиотека отразява избора между писане на собствен JWT софтуер и използване на тестван: и двете са разумни; библиотеката икономисва време и намалява повърхността за одит; написването от нулата ви принуждава да разберете всеки примитив. Този урок учи пътя от нулата, за да имате основата за всеки избор.
Тествайте разбирането си преди да преминете към практическото упражнение.
1. Разписката е подписана с частния Ed25519 ключ на агента. Одиторът разполага само с публичния ключ. Може ли одиторът да провери разписката офлайн?
2. Нападател модифицира полето policy_id в разписка, за да заяви, че е била управлявана от по-либерална политика. Подписът беше върху оригиналния товар. Какво се случва при проверката?
3. Защо разписката включва tool_args_hash и result_hash, а не суровите аргументи и резултата?
4. Полето previous_receipt_hash свързва всяка разписка с предшественика й. Ако нападател тихомълком изтрие една разписка от средата на веригата, какво става невалидно?
5. Разписката се проверява успешно. Дали това доказва, че действието на агента е било правилно, надеждно или съобразено с политиката?
Отворете code_samples/18-signed-receipts.ipynb и завършете всички четири секции:
Изискване за разширение 1: разширете схемата на разписката с допълнително избрано от вас поле (например, идентификатор на заявка за проследяване), актуализирайте каноничната логика за подписване, за да го включва, и потвърдете, че разписката все още преминава през проверката. След това променете полето след подписване и потвърдете, че проверката се проваля. Това ви научава как всеки байт от каноничното кодиране влияе на подписа.
Изискване за разширение 2: SHA-256 хеширайте два от вашите подписи заедно (конкатенирайте техните канонични байтове в детерминиран ред) и вградете получения дайджест като ново поле в трета разписка преди да я подпишете. Проверете, че всички три разписки все още преминават през проверката. Току-що сте създали доказателство за включване в една стъпка: всеки, който държи третата разписка, може да докаже, че първите две са съществували в момента на подписване, без да разкрива съдържанието им. Това е моделът, който селективните разписки за разкриване използват мащабно (Меркъл ангажименти, RFC 6962).
Криптографските разписки дават на AI агентите следа за одит, която е:
Те не заместват валидирането на входа, прилагането на политики или инфраструктурата за идентичност. Те са основа за тези слоеве. Когато пускате агенти в регламентирани среди, мултиорганизационни работни потоци или среди, където бъдещ одитор не може да ви се довери, разписките правят следата за одит честна.
Най-важният извод: разписките доказват кой е казал какво и кога. Те не доказват дали казаното е вярно или правилно. Дръжте тази разлика строго. Това е разликата между честна система за произход и подвеждаща такава.
Когато сте готови да преминете към внедряване на агенти с подписани разписки в реална среда:
https://your-org.example.com/.well-known/agent-keys.json.Присъединете се към Microsoft Foundry Discord, за да се срещнете с други учащи, да посетите офис часове и да получите отговори на въпросите си за AI агенти.
Този урок покрива подписване на единична разписка и хеш-верижни последователности. Същите примитиви се комбинират в няколко по-напреднали модела, които може да срещнете с напредване на управленската рамка:
authorization_*) и след изпълнение (result_*) с независими подписи, полезно когато решението за оторизация и наблюдаваният резултат се произвеждат от различни актьори или в различно време. Това се наслагва върху формата на разписката, обучен в този урок.result_hash. Реалните натоварвания често са по-богати от един резултат на инструмент: предварително обмисляне (прогноза на модел, разглеждани опции, доказателства и тяхната пълнота, рискова позиция, верига на отговорност, резултат на врата) могат всички да живеят в натоварването, опечатани само от една разписка. Това държи формата на разписката минимален, позволявайки схеми на натоварването да се развиват домейн по домейн.signature.alg може да носи ML-DSA-65 (стандарта на NIST за пост-квантови подписи), когато е нужно мигриране. Планирайте преходен период с двойно подписване на разписките.Създаване на локални AI агенти
Отказ от отговорност: Този документ е преведен с помощта на AI преводачески услуга Co-op Translator. Въпреки че се стремим към точност, моля имайте предвид, че автоматизираните преводи могат да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за каквито и да е недоразумения или неправилни тълкувания, произтичащи от използването на този превод.