0Pricing
AI Agents · درس

تسجيل الإجراءات غير القابل للتغيير للوكلاء

سجلات بنمط الإضافة فقط، وتوقيعات مشفَّرة، ومسارات تدقيق تكشف العبث.

تسجيل الإجراءات غير القابل للتغيير للوكلاء درس مجاني في AI Agents على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Agents، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Agents 4 دروس في المجموع.

لماذا يحتاج الوكلاء إلى سجلات غير قابلة للتغيير؟

يتخذ الوكلاء إجراءات بصورة مستقلة — وغالبًا من دون أن يراجع شخص كل خطوة. وعندما يحدث خطأ ما، تحتاج إلى الإجابة عن الأسئلة التالية: ماذا فعل الوكيل، ومتى، ولصالح من، ولماذا؟

يجعل السجل غير القابل للتغيير والكاشف للتلاعب تغيير مسار التدقيق بأثر رجعي أمرًا مستحيلًا.

مخطط إدخال السجل

يحتوي كل إدخال في السجل على: الطابع الزمني، وهوية الوكيل، وهوية المستخدم، ونوع الإجراء، والمعلمات، والنتيجة، وتجزئة تربطه بالإدخال السابق. ويدعم هذا الهيكل البحث واكتشاف التلاعب معًا.

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)

تسلسل التجزئة لاكتشاف التلاعب

تُحسب تجزئة كل إدخال من محتواه بالإضافة إلى تجزئة الإدخال السابق. وإذا عُدّل أي إدخال بعد إنشائه، تتغير تجزئته، مما يبطل جميع التجزئات اللاحقة. وبذلك يصبح التلاعب قابلًا للاكتشاف.

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'])

سجل للإلحاق فقط داخل العملية

أبسط تطبيق هو قائمة موجودة في الذاكرة مع أسلوب لا يسمح إلا بالإضافة. ولا يتم توفير أساليب للحذف أو التحديث. وفي بيئة التشغيل الفعلية، تدعم ذلك قاعدة بيانات أو مخزن كائنات.

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

الكتابة إلى PostgreSQL بدلالات الإلحاق

في بيئة التشغيل الفعلية، اكتب السجلات في جدول قاعدة بيانات لا يمتلك مستخدم التطبيق صلاحيات UPDATE أو DELETE عليه. اسحب هذه الامتيازات على مستوى قاعدة البيانات لتحقيق سلوك الإلحاق فقط فعليًا.

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()

الكتابة إلى S3 باستخدام Object Lock (WORM)

يمنع AWS S3 Object Lock في وضع COMPLIANCE أي مستخدم — بما في ذلك جذر الحساب — من حذف الكائنات أو استبدالها طوال فترة الاحتفاظ. وهذا هو التخزين الحقيقي للكتابة مرة واحدة لسجلات التدقيق.

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

الاستعلام عن سجل التدقيق

لا يقتصر سجل التدقيق على الامتثال — فهو مفيد أيضًا من الناحية التشغيلية. استعلم منه لتصحيح سلوك الوكيل، أو إعادة بناء جلسة، أو الإجابة عن سؤال «لماذا نفّذ الوكيل الإجراء 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))

تنقية البيانات الحساسة قبل التسجيل

ينبغي أن تسجل سجلات التدقيق ما فعله الوكيل، لا أن تكشف البيانات الحساسة. أزِل بيانات PII (كلمات المرور، ومفاتيح API، وأرقام بطاقات الائتمان) من المعلمات والنتائج قبل الكتابة.

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))

تدوير السجلات وأرشفتها

تزداد السجلات النشطة بلا نهاية. دوّرها بنقل الإدخالات الأقدم من 90 يومًا إلى تخزين بارد (مثل S3 Glacier) وضغطها. حافظ على تسلسل التجزئة سليمًا من خلال الاحتفاظ بآخر تجزئة لكل مقطع تم تدويره.

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}

التنبيه عند الأنماط المشبوهة

راقب سجل التدقيق في الوقت الفعلي بحثًا عن أنماط غير معتادة: دفعة من الإجراءات عالية المخاطر، أو إجراءات خارج ساعات العمل، أو تكرار الإجراء نفسه أكثر من N مرة خلال فترة قصيرة.

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)

التحقق من سلامة السجل عند الطلب

أجرِ فحصًا للسلامة كجزء من مسار المراقبة. أطلق تنبيهًا فورًا عند اكتشاف أي فشل في التحقق — فهذا يشير إما إلى خطأ في كود التسجيل أو إلى محاولة تلاعب نشطة.

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)

ما الذي يكشفه تسلسل التجزئة في سجل التدقيق؟

تسلسل التجزئة هو الآلية التشفيرية التي تمنح سجل التدقيق خاصية كشف التلاعب. ويُعد فهم ما يكشفه أمرًا أساسيًا لتصميم سجلات التدقيق.

مراجعة تسجيل إجراءات الوكيل غير القابل للتغيير

تستخدم سجلات الوكلاء غير القابلة للتغيير ما يلي: تسلسل التجزئة لاكتشاف التلاعب، والتخزين للإلحاق فقط (سحب صلاحيات UPDATE/DELETE على مستوى قاعدة البيانات)، وتخزين الكائنات WORM (S3 Object Lock) للاحتفاظ المطلوب لأغراض تنظيمية، وتنقية بيانات PII قبل الكتابة، وإطلاق تنبيهات فورية عن الحالات الشاذة عند اكتشاف أنماط إجراءات مشبوهة.

الأسئلة الشائعة

هل درس «تسجيل الإجراءات غير القابل للتغيير للوكلاء» مجاني؟

نعم — نص درس «تسجيل الإجراءات غير القابل للتغيير للوكلاء» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Agents، انتقل إلى CoddyKit PRO. تتضمن دورة AI Agents 4 دروس في المجموع.

ماذا ستتعلم في «تسجيل الإجراءات غير القابل للتغيير للوكلاء»؟

سجلات بنمط الإضافة فقط، وتوقيعات مشفَّرة، ومسارات تدقيق تكشف العبث. تتمرن على AI Agents مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ AI Agents؟

لا تُشترط خبرة سابقة. AI Agents على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «تسجيل الإجراءات غير القابل للتغيير للوكلاء»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس AI Agents هذا؟

نعم. كل درس في AI Agents يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تسجيل الإجراءات غير القابل للتغيير للوكلاء
  2. فرض السياسات على إجراءات الوكيل
  3. الامتثال التنظيمي: GDPR وSOC2
  4. بوابات الموافقة بوجود الإنسان ضمن الحلقة
← العودة إلى AI Agents