AI-agenter · Lektion

Oföränderlig åtgärdsloggning för agenter

Loggar som endast kan utökas, kryptografiska signaturer och manipulationssäkra revisionsspår.

Lektion 1 av 413 steg

Oföränderlig åtgärdsloggning för agenter är en gratis lektion i AI-agenter på CoddyKit. Detta är lektion 1 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för AI-agenter, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i AI-agenter innehåller totalt 4 lektioner.

Varför agenter behöver oföränderliga loggar

Agenter utför åtgärder autonomt – ofta utan att en människa granskar varje steg. När något går fel måste ni kunna svara på frågorna: vad gjorde agenten, när, för vems räkning och varför?

En oföränderlig logg med manipuleringsdetektering gör det omöjligt att i efterhand ändra revisionsspåret.

Schema för loggposter

Varje loggpost innehåller: tidsstämpel, agentidentitet, användaridentitet, åtgärdstyp, parametrar, resultat och en hash som länkar till föregående post. Denna struktur stöder både sökning och upptäckt av manipulation.

from dataclasses import dataclass
from typing import Any

@dataclass
class AuditEntry:
    timestamp:   float       # Unix time (UTC)
    agent_id:    str
    user_id:     str
    action_type: str
    parameters:  dict
    result:      dict
    session_id:  str
    prev_hash:   str         # hash of previous entry
    entry_hash:  str = ''    # computed after construction

if __name__ == '__main__':
    entry = AuditEntry(
        timestamp=1717000000.0, agent_id='agent-1', user_id='user-42',
        action_type='send_email', parameters={'to': 'a@b.com'},
        result={'status': 'sent'}, session_id='sess-1', prev_hash='0' * 64
    )
    print('Audit entry created:')
    print(' agent_id:', entry.agent_id)
    print(' action_type:', entry.action_type)
    print(' prev_hash:', entry.prev_hash)

Hashkedjning för att upptäcka manipulation

Varje posts hash beräknas utifrån postens innehåll plus föregående posts hash. Om en post ändras i efterhand ändras dess hash, vilket ogiltigförklarar alla efterföljande hashvärden. På så sätt kan manipulation upptäckas.

import hashlib, json, time

def compute_entry_hash(entry: dict, prev_hash: str) -> str:
    content = json.dumps(entry, sort_keys=True, default=str)
    payload = f'{prev_hash}:{content}'
    return hashlib.sha256(payload.encode()).hexdigest()

def create_log_entry(agent_id: str, user_id: str, action_type: str,
                     parameters: dict, result: dict,
                     session_id: str, prev_hash: str) -> dict:
    entry = {
        'timestamp':   time.time(),
        'agent_id':    agent_id,
        'user_id':     user_id,
        'action_type': action_type,
        'parameters':  parameters,
        'result':      result,
        'session_id':  session_id,
        'prev_hash':   prev_hash
    }
    entry['entry_hash'] = compute_entry_hash(
        {k: v for k, v in entry.items() if k != 'entry_hash'},
        prev_hash
    )
    return entry

if __name__ == '__main__':
    entry = create_log_entry('agent-1', 'user-42', 'send_email',
                              {'to': 'a@b.com'}, {'status': 'sent'},
                              'sess-1', prev_hash='0' * 64)
    print('New log entry hash:', entry['entry_hash'])
    print('Chained from prev_hash:', entry['prev_hash'])

Processintern append-only-logg

Den enklaste implementeringen är en lista i minnet med en metod som endast tillåter tillägg. Inga metoder för radering eller uppdatering exponeras. I produktion stöds detta av en databas eller ett objektlager.

class AppendOnlyLog:
    def __init__(self):
        self._entries: list[dict] = []
        self._last_hash = 'GENESIS'

    def append(self, agent_id: str, user_id: str, action_type: str,
               parameters: dict, result: dict, session_id: str) -> dict:
        entry = create_log_entry(
            agent_id, user_id, action_type,
            parameters, result, session_id,
            self._last_hash
        )
        self._entries.append(entry)
        self._last_hash = entry['entry_hash']
        return entry

    def verify_integrity(self) -> bool:
        running_hash = 'GENESIS'
        for e in self._entries:
            expected = compute_entry_hash(
                {k: v for k, v in e.items() if k != 'entry_hash'},
                running_hash
            )
            if expected != e['entry_hash']:
                return False
            running_hash = e['entry_hash']
        return True

    @property
    def entries(self):
        return list(self._entries)  # return copy, not reference

Skriva till PostgreSQL med append-semantik

I produktion skriver ni loggar till en databastabell där programanvändaren inte har tilldelats några UPDATE- eller DELETE-behörigheter. Återkalla dessa privilegier på databasnivå för att uppnå verkligt append-only-beteende.

import psycopg2, json

# Table DDL (run once):
# CREATE TABLE agent_audit_log (
#   id          BIGSERIAL PRIMARY KEY,
#   timestamp   DOUBLE PRECISION NOT NULL,
#   agent_id    TEXT NOT NULL,
#   user_id     TEXT NOT NULL,
#   action_type TEXT NOT NULL,
#   parameters  JSONB NOT NULL,
#   result      JSONB NOT NULL,
#   session_id  TEXT NOT NULL,
#   prev_hash   TEXT NOT NULL,
#   entry_hash  TEXT NOT NULL UNIQUE
# );
# REVOKE UPDATE, DELETE ON agent_audit_log FROM app_user;

def write_to_db(conn, entry: dict):
    with conn.cursor() as cur:
        cur.execute(
            'INSERT INTO agent_audit_log '
            '(timestamp,agent_id,user_id,action_type,parameters,result,session_id,prev_hash,entry_hash) '
            'VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)',
            (entry['timestamp'], entry['agent_id'], entry['user_id'],
             entry['action_type'], json.dumps(entry['parameters']),
             json.dumps(entry['result']), entry['session_id'],
             entry['prev_hash'], entry['entry_hash'])
        )
    conn.commit()

Skriva till S3 med Object Lock (WORM)

AWS S3 Object Lock i COMPLIANCE-läge förhindrar alla användare – inklusive kontots root-användare – från att radera eller skriva över objekt under bevarandetiden. Detta är verklig write-once-lagring för revisionsloggar.

import boto3, json, time

s3 = boto3.client('s3', region_name='us-east-1')
BUCKET = 'your-audit-log-bucket-worm'

def write_to_s3_worm(entry: dict):
    key = f'audit/{entry["agent_id"]}/{int(entry["timestamp"])}/{entry["entry_hash"][:16]}.json'
    s3.put_object(
        Bucket=BUCKET,
        Key=key,
        Body=json.dumps(entry).encode(),
        ContentType='application/json',
        ObjectLockMode='COMPLIANCE',
        ObjectLockRetainUntilDate='2030-01-01T00:00:00Z'  # 4+ year retention
    )
    return key

Fråga revisionsloggen

Revisionsloggen är inte bara till för regelefterlevnad – den är också operativt användbar. Använd den för att felsöka agentens beteende, återskapa en session eller besvara frågan 'varför gjorde agenten X?'

def query_session(log: AppendOnlyLog, session_id: str) -> list[dict]:
    return [
        e for e in log.entries
        if e['session_id'] == session_id
    ]

def query_user_actions(log: AppendOnlyLog, user_id: str,
                       action_type: str = None) -> list[dict]:
    entries = [e for e in log.entries if e['user_id'] == user_id]
    if action_type:
        entries = [e for e in entries if e['action_type'] == action_type]
    return sorted(entries, key=lambda e: e['timestamp'])

def count_actions_by_type(log: AppendOnlyLog) -> dict:
    from collections import Counter
    return dict(Counter(e['action_type'] for e in log.entries))

Rensa känsliga data före loggning

Revisionsloggar bör dokumentera vad agenten gjorde, inte exponera känsliga data. Ta bort PII (lösenord, API-nycklar och kreditkortsnummer) från parametrar och resultat innan de skrivs.

import re

SENSITIVE_KEYS = {'password', 'api_key', 'secret', 'token', 'credit_card', 'ssn'}

def sanitize(obj, depth: int = 0) -> dict | list | str:
    if depth > 5:
        return '[MAX_DEPTH]'
    if isinstance(obj, dict):
        return {
            k: '[REDACTED]' if k.lower() in SENSITIVE_KEYS
               else sanitize(v, depth + 1)
            for k, v in obj.items()
        }
    elif isinstance(obj, list):
        return [sanitize(i, depth + 1) for i in obj]
    elif isinstance(obj, str):
        # Redact anything that looks like an API key
        return re.sub(r'(sk-|Bearer\s)[A-Za-z0-9_-]{16,}', '[REDACTED]', obj)
    return obj

if __name__ == '__main__':
    record = {'user': 'alice', 'password': 'hunter2', 'note': 'call me at sk-abcdefghijklmnopqrstuv'}
    print('Sanitized record:', sanitize(record))

Loggrotation och arkivering

Aktiva loggar växer utan gräns. Rotera dem: flytta poster äldre än 90 dagar till kall lagring (t.ex. S3 Glacier) och komprimera dem. Bevara kedjan genom att spara det sista hashvärdet i varje roterat segment.

import time, json, gzip

def rotate_log(log: AppendOnlyLog, max_age_days: int = 90) -> dict:
    cutoff = time.time() - max_age_days * 86400
    archive = [e for e in log.entries if e['timestamp'] < cutoff]
    remaining = [e for e in log.entries if e['timestamp'] >= cutoff]

    if not archive:
        return {'archived': 0, 'remaining': len(remaining)}

    # Compress archive
    archive_bytes = gzip.compress(json.dumps(archive).encode())
    archive_file  = f'/tmp/audit_archive_{int(time.time())}.json.gz'
    with open(archive_file, 'wb') as f:
        f.write(archive_bytes)

    # Update in-memory log
    log._entries = remaining

    return {'archived': len(archive), 'remaining': len(remaining), 'file': archive_file}

Aviseringar om misstänkta mönster

Övervaka revisionsloggen efter ovanliga mönster i realtid: en plötslig mängd högriskåtgärder, åtgärder utanför arbetstid eller samma åtgärd som upprepas fler än N gånger under ett kort tidsintervall.

import time
from collections import defaultdict

HIGH_RISK_ACTIONS = {'delete_user', 'send_mass_email',
                     'transfer_funds', 'export_all_data'}
BURST_LIMIT = 5
BURST_WINDOW = 60  # seconds

action_timestamps: dict[str, list] = defaultdict(list)

def check_suspicious(entry: dict) -> list[str]:
    alerts = []
    action = entry['action_type']

    if action in HIGH_RISK_ACTIONS:
        alerts.append(f'HIGH_RISK_ACTION: {action} by {entry["agent_id"]}')

    now = time.time()
    action_timestamps[action].append(now)
    recent = [t for t in action_timestamps[action] if now - t < BURST_WINDOW]
    action_timestamps[action] = recent

    if len(recent) > BURST_LIMIT:
        alerts.append(f'ACTION_BURST: {action} called {len(recent)}x in {BURST_WINDOW}s')

    return alerts

if __name__ == '__main__':
    entry = {'action_type': 'delete_user', 'agent_id': 'agent-9'}
    for _ in range(6):
        alerts = check_suspicious(entry)
    print('Alerts on 6th call:', alerts)

Verifiera loggens integritet på begäran

Kör en integritetskontroll som en del av övervakningspipelinen. Avisera omedelbart om ett verifieringsfel upptäcks – det tyder antingen på ett fel i loggningskoden eller på ett aktivt manipuleringsförsök.

def verify_and_alert(log: AppendOnlyLog) -> dict:
    is_valid = log.verify_integrity()
    total    = len(log.entries)
    result   = {
        'total_entries': total,
        'integrity_ok':  is_valid
    }
    if not is_valid:
        import logging
        alert_logger = logging.getLogger('agent.integrity_alert')
        alert_logger.critical(
            'AUDIT LOG INTEGRITY FAILURE: tampered or corrupted entries detected. '
            'Total entries: %d. Initiating incident response.', total
        )
        result['alert_sent'] = True
    return result

# Run periodically via cron or monitoring hook
# verify_and_alert(global_audit_log)

Vad upptäcker hashkedjning i en revisionslogg?

Hashkedjning är den kryptografiska mekanism som ger revisionsloggen dess manipuleringsdetekterande egenskap. Att förstå vad den upptäcker är grundläggande vid utformning av revisionsloggar.

Sammanfattning av oföränderlig åtgärdsloggning

Oföränderliga agentloggar använder: hashkedjning för att upptäcka manipulation, append-only-lagring (återkalla UPDATE/DELETE på databasnivå), WORM-objektlagring (S3 Object Lock) för lagring enligt regulatoriska bevarandekrav, sanering av PII före skrivning och realtidsaviseringar om avvikelser vid misstänkta åtgärdsmönster.

Gratis att börja

Lär dig AI-agenter med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
60
Lektioner
239

Vanliga frågor

Är lektionen ”Oföränderlig åtgärdsloggning för agenter” gratis?

Ja – hela texten till ”Oföränderlig åtgärdsloggning för agenter” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i AI-agenter, kan Ni uppgradera till CoddyKit PRO. Kursen i AI-agenter innehåller totalt 4 lektioner.

Vad lär jag mig i ”Oföränderlig åtgärdsloggning för agenter”?

Loggar som endast kan utökas, kryptografiska signaturer och manipulationssäkra revisionsspår. Ni övar på AI-agenter med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig AI-agenter?

Du behöver inga förkunskaper. Utbildningen i AI-agenter på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.

Hur lång tid tar lektionen ”Oföränderlig åtgärdsloggning för agenter”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här AI-agenter-lektionen?

Ja. Varje AI-agenter-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Oföränderlig åtgärdsloggning för agenter
  2. Policytillämpning för agentåtgärder
  3. Regelefterlevnad: GDPR och SOC2
  4. Godkännandegrindar med människa i loopen
← Tillbaka till AI-agenter