ai-agents-for-beginners

Погледајте видео лекцију: Осигуравање AI агената криптографским потврдама

(Видео лекције и сличица ће бити додати од стране Microsoft тима за садржај након спајања, у складу са обрасцем лекција 14 / 15.)

Осигуравање AI агената криптографским потврдама

Увод

У овој лекцији ће бити обрађено:

Циљеви учења

Након завршене ове лекције, знаћете како да:

Проблем: Ревизијски траг вашег агента

Замислите да сте поставили AI агента за Contoso Travel. Агент чита захтеве корисника, позива API за летове да нађе опције и резервише седишта у име корисника. Прошлог квартала, агент је обрадио 50.000 резервација.

Данас стиже ревидент. Поставља једноставно питање: “Покажи ми шта је твој агент радио.”

Предајете датотеке дневника. Ревидент их погледа и поставља тежје питање: “Како знам да ови записи нису били уређивани?”

Ово је проблем ревизијског трага. Већина данашњих агената се ослања на:

Ниједна од ових опција не може одговорити ревиденту без даље вере у неко лице (вас, вашег провајдера облака, добављача базе података). За интерну употребу то је често прихватљиво. За регулисане радне оптерећења (финансије, здравство, све подложно EU AI акту) то није.

Криптографске потврде сужавају овај проблем тако што праве потписану изјаву која је независно проверљива. Ревиденту је ипак потребан спољни разлог да верује потписном кључу и компоненту која је произвела изјаву. Уграђени кључ доказује само унутрашњу конзистентност, не идентитет издаваоца или место примене.

Шта је Криптографска Потврда?

Потврда је JSON објекат који бележи шта је агент урадио, потписан дигиталним потписом.

flowchart LR
    A[Agent invokes a tool] --> B[Build receipt payload]
    B --> C[Canonicalize JSON RFC 8785]
    C --> E[Ed25519 sign canonical bytes]
    E --> F[Receipt with signature]
    F --> G[Auditor verifies offline]
    G --> H{Signature valid?}
    H -- yes --> I[Signed statement is intact]
    H -- no --> J[Receipt rejected]

Минимална потврда изгледа овако:

{
  "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..."
  }
}

Три својства обављају посао:

  1. Потпис. Потврда је потписана на улазној тачки агента користећи Ed25519 приватни кључ. Сваки ко има одговарајући јавни кључ може да верификује потпис офлајн. Корисник који се ослања мора да закључа тај кључ или да га добије из поузданог регистра ван потврде. Измена било ког потписаног поља поништава потпис.

  2. Канонско кодирање. Пре потписивања, потврда се серијализује коришћењем JSON канонизационе шеме (JCS, RFC 8785). Ово осигурава да две имплементације које производе исти логички садржај дају потпуно идентичан бајт излаз. Без канонизације, различити JSON серијализатори дали би различите потписе за исти садржај.

  3. Повезивање хешем. Поље previous_receipt_hash повезује сваку потврду са претходном. Уклањање или преуређивање потврде разбија представљени низ. Детекција прескакања или преписивања које контролише потписивач такође захтева закључану тренутну главу ланца, независно сведочени контролни скуп или аутентификовану обавезу за затворену популацију.

Заједно ова својства пружају три гаранције:

Прављење Потврде у Питону

Не треба вам посебна библиотека да направите потврду. Криптографски примитиви су широко доступни, а логика је само неколико десетина линија Python кода.

Практични задаци у code_samples/18-signed-receipts.ipynb воде вас кроз цео процес. Изворно контролисани code_samples/18-signed-receipts.execution-receipt.json бележи тачан хеш бележнице, Python окружење, број ћелија и провере из последњег чистог извршења без промене сачуваних резултата лекције. Сажетак:

import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize  # RFC 8785 canonical 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()}"

# Generate or load a signing key (in production, store in a key vault)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
TRUSTED_PUBLIC_KEYS = {b64url_nopad(bytes(verify_key))}  # pinned out of band by the verifier

# Build the receipt payload (no signature yet)
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,
}

# Canonicalize and sign the JCS bytes directly. PureEdDSA hashes internally.
canonical_bytes = canonicalize(payload)
signature_bytes = signing_key.sign(canonical_bytes).signature

# Attach a structured signature object.
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 CryptoError
from jcs import canonicalize

def b64url_decode(s: str) -> bytes:
    padding = "=" * ((4 - len(s) % 4) % 4)
    return base64.b64decode(s + padding, altchars=b"-_", validate=True)

def verify_receipt(receipt: dict, trusted_public_keys: set[str]) -> bool:
    # The signature is a structured object: {"alg", "sig", "public_key"}.
    if not isinstance(receipt, dict):
        return False
    sig_obj = receipt.get("signature")
    if not isinstance(sig_obj, dict) or sig_obj.get("alg") != "EdDSA":
        return False
    try:
        # An inline key proves only self-consistency. Trust is configured out of band.
        public_key = sig_obj.get("public_key")
        signature = sig_obj.get("sig")
        if not isinstance(public_key, str) or not isinstance(signature, str):
            return False
        if public_key not in trusted_public_keys:
            return False
        payload = {k: v for k, v in receipt.items() if k != "signature"}
        canonical_bytes = canonicalize(payload)
        verify_key = signing.VerifyKey(b64url_decode(public_key))
        verify_key.verify(canonical_bytes, b64url_decode(signature))
        return True
    except (CryptoError, TypeError, ValueError, KeyError, OverflowError):
        return False

Овај метод прихвата потврду и скуп закључаних кључева корисника који се ослања и враћа True само ако је потпис важећи под прихваћеним кључем. Проверa не захтева позив мрежи, али поверење у кључ је и даље спољна конфигурациона одлука.

Да бисте видели детекцију измене у акцији, бележница вас води кроз:

  1. Производњу ваљане потврде и потврду да се верификује.
  2. Измену једног бајта у пољу tool_args_hash.
  3. Поновно извођење провере и уочавање да не успева.

Ово је практична демонстрација да су потврде доказиво непромењиве: свака измена, ма колико мала била, разбија потпис.

Повезивање Потврда за Више-Корачне Агенте

Једна потписана потврда штити једну радњу. Ланац потврда штити низ радњи.

flowchart LR
    R0[Receipt 0<br/>genesis] --> R1[Receipt 1]
    R1 --> R2[Receipt 2]
    R2 --> R3[Receipt 3]
    R1 -. previous_receipt_hash .-> R0
    R2 -. previous_receipt_hash .-> R1
    R3 -. previous_receipt_hash .-> R2

Свaka потврда бележи хеш претходне. Да би нападач тихо уклонио потврду 2, морао би да:

Ако је приватни кључ заштићен и верификатор закључава независно сведочену главу ланца, ови напади су откривљиви. Објављивање кључа унутар сваке потврде није довољно: потписивач или нападач може представити други кључ, преписати наставак или прескочити ланац ако корисник није обезбедио спољни ослонац поверења и потпуности.

Бележница води корисника кроз:

  1. Изградњу ланца од три потврде.
  2. Верификацију да previous_receipt_hash сваке потврде одговара стварном хешу претходне.
  3. Менјање једне потврде у средини и уочавање прекида ланца управо на том месту.

Овако правите ревизијски траг који спољни ревидент може проверити својим закључаним издавачким кључевима и контролним тачкама ланца, без поверења у вашу изменљиву продавницу дневника.

Шта Потврде Доказују (а шта Не Доказују)

Ово је најважнији део ове лекције. Потврде су моћне али њихова моћ је ограничена.

Потврде доказују три ствари:

  1. Атрибуцију: одређени кључ је потписао одређени садржај.
  2. Интегритет: садржај није промењен од времена потписивања.
  3. Редослед у представљеном, верификованом низу: ова потврда повезује претходну.

Потврде НЕ доказују:

  1. Тачност: да је радња агента била исправна. Потврда може бити потписана за погрешан одговор подједнако добро као за исправан.
  2. Усаглашеност са политиком: да је политика наведена у policy_id заиста процењена, или да би дозволила ову радњу ако би била проверена. Потврда бележи шта је тврђено, а не шта је спроводљено.
  3. Идентитет изван кључа: потврда каже “овaj кључ је потписао овај садржај.” Не каже “овај човек је одобрио ово.” Повезивање кључа са особом или организацијом захтева посебну инфраструктуру идентитета (књижицу, регистар јавних кључева, итд.).
  4. Истинитост уноса: ако агент добије измењен упит и делова на основу њега, потврда верно бележи радњу. Потврде зависе од валидности улаза, нису њена замена.
  5. Извршење или стварни светски ефекат: потврда након позива бележи шта издаваоц тврди да се десило. Доказивање пријема или усаглашавање са спољним ефектом захтева доказе са стварног извршиоца или границе пружаоца.
  6. Потпуност: одсутна потврда може значити блокиране, изостављене, изгубљене или непроцењене радње. Одсуство постаје доказ само унутар аутентификоване затворене популације.
  7. Поверење у време: потписани временски жиг доказује да је потписивач имао ту вредност, не да је вредност била тачна. Поверење у време захтева посебног временског ауторитетa или сведочену контролу.

Ова граница је важна из два разлога:

Честа грешка је претпоставка да “имамо потврде” значи “имамо управљање.” Не значи. Потврде су основа. Управљање је систем који се на њој гради.

Повезивање Потписа Одобраваоца Са Тачном Радњом

Према ставци 3 горе, потврда радње каже “овaj кључ је потписао овај садржај,” а никада “човек је одобрио ово.” За радње високог ризика (повраћаји новца, брисања, електронски трансфери) посебан потпис одобраваоца може повезати тачну радњу. Повезивање тог кључа са човеком и провера људског присуства остају посебне одговорности.

Наставна бележница code_samples/human-authorization-receipts.ipynb додаје други тип потврде, human.approval.v1, у истом формату коверте као потврде из лекције (типизовани објекат потписан Ed25519 преко његових JCS бајтова, са signature објектом изван потписаних бајтова). Кључ одобраваоца потписује целу канонску радњу и њен дигест; кључ агента потписује тврдњу која носи иста дигест радње и parent_approval_ref, receipt_hash одобрења, исти конвенцијски приступ као previous_receipt_hash у ланцу који сте направили горе. Један verify_chain обрађује оба артефакта под одвојеним закључаним регистрима кључева (кључеви одобраваоца насупрот кључевима агента), тако да је код подељен али власти никад нису.

Својство које ово даје, пажљиво формулисано: закључани кључ одобраваоца потписао је ову тачну радњу, а агентски кључ касније је потписао изјаву са истим дигестом радње. То је корисан доказ, али није доказ да је радња извршена. Да контролишете извршење, тачно одобрење мора бити резервисано и искоришћено једном на стварној граници извршиоца; непознати исходи пружаоца морају остати непознати уместо да се слепо поново покушавају. Ревидентске функције бележнице тестирају слој ланца потврда:

Сваки неуспех одбија са јасним разлогом, тако да ревидент може разликовати да ли је овлашћење застарело или је тврђена радња промењена. Провера је само за читање и понављање: не троши одобрење нити позива пружаоца услуге. Потребан је одвојен извршилац са трајном, атомском заштитом од репродукције и експлицитним статусима успеха, неуспеха и неодређености. Потврда о људском одобрењу је образовна композиција дефинисана овом лекцијом, није тип потврде дефинисан од стране draft-farley-acta-signed-receipts.

Референце за Производњу

Python код у овој лекцији је намерно минималан да бисте могли прочитати сваку линију и тачно разумети шта се догађа. У продукцији имате две опције:

  1. Градите директно на криптографским примитивима. Тих 50 линија које сте видели горе су довољне за многе случајеве употребе. PyNaCl (Ed25519) и jcs пакет (канонски JSON) су добро одржаване и ревидиране библиотеке.

  2. Користите продукциону библиотеку за потврде. Неколико open-source пројеката имплементира исти образац са додатним функцијама (ротација кључева, групна верификација, дистрибуција JWK сета, интеграција са политикама):

    • Поступак потписивања користи JCS и конвенцију опсега потписа из независног IETF Internet-Draft-а (draft-farley-acta-signed-receipts, ревизија 02). Образовна равна потврда ове лекције разликује се од коверте из {payload, signature} нацрта и није приказана као пример усклађености. Нацрт објављује заједнички скуп тестова (agent-governance-testvectors) за имплементације које циљају тај формат.
    • Microsoft Agent Governance Toolkit комбинује потврде са одлукама политике заснованим на Cedar-у; видите Туторијал 33 у том репозиторијуму за пример од почетка до краја.
    • protect-mcp (npm) и @veritasacta/verify (npm) пакети обезбеђују Node-ову имплементацију потписивања потврда и офлајн провере, намењену за обавијање било ког MCP сервера са доказиво непромењивим ревизијским трагом, укључујући процес задржавања за заједничко потписивање у којем паузирана радња емитује потврду одобрења везану за дигест радње (WebAuthn подржано у desktop процесу), исти образац одобрења као у горе поменутој бележници за људску ауторизацију.
    • nobulex Python SDK (pip install nobulex) пружа исти Ed25519 + JCS образац потписивања у Python-у са LangChain и CrewAI интеграцијама, укључујући објављене тест векторе за крос-верификацију и мапирање усаглашености допринето преко OWASP PR #2210.
    • EMILIA Protocol пружа комплементарни слој за спровођење тачне акције: одобрења која се могу верификовати офлајн, закључана WebAuthn или кврум ауторитет, једнократна потрошња на ивицама извршиоца и експлицитан пут неодређеног исхода. Њени адаптери оквира третирају признаницу као неопходан доказ уместо као доказ да је спољни ефекат заиста настао.

Одлука између писања сопствене имплементације и коришћења библиотеке слична је одлуци између писања сопствене JWT библиотеке и коришћења тестиране: обоје је разуман избор; библиотека штеди време и смањује површину ревизије; приступ од нуле приморава вас да разумете сваки примитив. Овај лекција учи пут од нуле тако да имате темеље за било који избор.

Провера знања

Тестирајте своје разумевање пре него што пређете на вежбу.

1. Признаница је потписана приватним Ed25519 кључем агента. Ревизор има само јавни кључ. Може ли ревизор проверити признаницу офлајн?

Одговор Да, криптографска верификација захтева само јавни кључ и потписане бајтове. Ревизор ипак мора независно добити и закључати прихваћени јавни кључ без обзира на признаницу. Након постављања тог поверења, није потребан мрежни позив.

2. Нападач мења поље policy_id признања како би тврдио да је било под управом либералније политике. Потпис је направљен преко оригиналног садржаја. Шта се дешава током верификације?

Одговор Верификација не успева. Потпис је израчунат преко канонских бајтова оригиналног садржаја; промена било ког поља мења те бајтове, што чини потпис неважећим. Нападачу је потребан приватни кључ да би произвео нови важећи потпис, којег нема.

3. Зашто признаница садржи tool_args_hash и result_hash уместо сирових аргумената и резултата?

Одговор Два разлога. Прво, признаница можда треба да буде архивирана или послата у окружењима где цурење сировог садржаја (ПИИ, пословни подаци) представља проблем. Хешање држи признаницу компактном и садржај приватним; ревизор проверaва да хеш одговара копији стварног садржаја сачуваној посебно. Друго, хешеви имају фиксну величину; признаница са хешевима има ограничену величину без обзира колико су улази и излази били велики.

4. Поље previous_receipt_hash повезује сваку признаницу са претходником. Ако нападач тихо избрише једну признаницу из средине ланца, шта постаје неважеће?

Одговор Следећа представљена признаница више није повезана са очекиваним претходником, тако да верификација те претстављене секвенце не успева од те празнине надаље. Ако потписник контролише приватни кључ, може преписати или скратити суфикс. Детекција тога захтева закључану тренутну главу, спољашњу проверу тачке или аутентификовану обавезу популације.

5. Признаница се чисто верификује. Да ли то доказује да је акција агента исправна, робусна или у складу са политиком?

Одговор Не. Под закључаним кључем и контекстом ланца, важећа признаница доказује атрибуцију потписа, интегритет садржаја и везу са представљеним претходником. НЕ доказује да је акција исправна или извршена, да је политика процењена, да је временска ознака поуздана или да је скуп записа комплетан. Ово је најважнија граница у лекцији.

Вежба

Отворите code_samples/18-signed-receipts.ipynb и завршите сва четири одељка:

  1. Одељак 1: Потпишите прву признаницу и проверите је.
  2. Одељак 2: Измамените признаницу и посматрајте пад верификације.
  3. Одељак 3: Направите ланац од три признанице и проверите интегритет ланца.
  4. Одељак 4: Примените образац на агента изграђеног са Microsoft Agent Framework: упакујте позив алата у потписивање признанице, а затим проверите признаницу независно.

Изазов 1: проширите шему признања додатним пољем по свом избору (на пример, ID захтева за праћење), ажурирајте логику канонског потписивања да га укључи и потврдите да признаница и даље успешно пролази верификацију. Затим промените то поље након потписивања и потврдите да верификација пада. Ово вас приморава да разумете како сваки бајт канонског кодирања доприноси потпису.

Изазов 2: SHA-256 рашчланите две своје признанице заједно (конкатенацијом њихових канонских бајтова у детерминистичком редоследу) и уградите добијени сажећ на трећој признаници пре потписивања. Проверите да ли све три признанице и даље пролазе круг верификације. Управо сте направили доказ укључивања у једном кораку: било ко има трећу признаницу може доказати да су прве две постојале у тренутку потписа, без потребе да открива њихов садржај. Ово је образац који користе признанице селективног откривања на скали (Merkle обавезе, RFC 6962).

Закључак

Криптографске признанице дају AI агентима преглед за ревизију који је:

Нису замена за валидацију уноса, спровођење политика, пријем извршиоца, доказе исхода, контролу комплетности или инфраструктуру идентитета. Они су темељ за те слојеве. У регулисаним или мулти-организационим радним токовима, признанице чине одређене изјаве издавача независно проверљивим у оквиру поверења ревизора.

Најважнија поука: признанице доказују да је одређени кључ потписао одређени садржај. Пренета временска ознака је изјава потписника, осим ако није посебно оверена временски. Признаница не доказује да је њен садржај био истинит, да је акција извршена или да је скуп записа комплетан. Чувајте те разлике јасно.

Контрола за продукцију

Када будете спремни да завршите ову лекцију и имплементирате агенте који потписују признанице у правом окружењу:

Имате још питања о обезбеђивању AI агената?

Придружите се Microsoft Foundry Discord да се сретнете са другим ученицима, посетите радне сате и добијете одговоре на питања о AI агентима.

Више од ове лекције

Ова лекција покрива потписивање једне признанице и низове ланаца са хешевима. Исти примитиви се састављају у неколико напреднијих образаца на које можете наићи како ваш управљачки оквир сазрева:

Додатни ресурси

Претходна лекција

Креирање локалних AI агената


Изјава о одрицању одговорности: Овај документ је преведен коришћењем услуге за аутоматски превод Co-op Translator. Иако тежимо тачности, имајте у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на његовом изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални људски превод. Нисмо одговорни за било каква неспоразума или погрешна тумачења која произилазе из коришћења овог превода.