AI Tool Audit Log: اعرف الوكيل نفّذ إيه قبل ما تلومه
لو وكيل AI عندك بينفّذ أدوات حقيقية، هتكسب من المقال ده طريقة عملية تعرف بها: مين طلب إيه، الأداة اشتغلت ليه، واتكلفت قد إيه.
مستوى القارئ: متوسط
المشكلة باختصار
الطريقة الشائعة بتفشل لما تعتمد على لوجات التطبيق العادية فقط. هتلاقي error في API، لكن مش هتعرف هل النموذج طلب الأداة، ولا البوابة رفضتها، ولا الأداة اشتغلت ومدخلاتها كانت غلط.
الافتراض إن عندك وكيل AI صغير في خدمة Node.js أو Python، وعنده 3 إلى 10 أدوات مثل: البحث في قاعدة البيانات، إنشاء تذكرة، إرسال بريد، أو قراءة ملف داخلي. في الحالة دي، خطأ واحد ممكن يأخذ 6 ساعات تحقيق لأن كل طبقة عندها جزء ناقص من القصة.
مثال بسيط قبل التعريف
ركز في السيناريو ده. عميل كتب: “ابعت ملخص الطلبات المتأخرة للمحاسبة”. النموذج طلب أداة send_email. الرسالة راحت لفريق غلط. لو عندك الرد النهائي فقط، هتدور في الظلام. لو عندك Audit Log، هتشوف request_id، واسم الأداة، وملخص المدخلات، وقرار البوابة، وزمن التنفيذ.
التعريف الدقيق: Audit Log لاستدعاءات أدوات AI هو سجل append-only لكل محاولة tool call، سواء اتنفذت أو اترفضت. السجل لا يخزن أسرار ولا نصوص كاملة غالبًا. يخزن بصمة أو ملخصًا آمنًا للمدخلات، مع القرار والنتيجة.
ما الذي يجب تسجيله بالظبط؟
لا تسجل كل شيء. تسجيل البرومبت كاملًا والمدخلات كاملةً ممكن يحوّل اللوج نفسه لمخزن بيانات حساسة. أفضل طريقة هي تسجيل حقول صغيرة تكفي للتحقيق.
request_id: يربط كل خطوة بنفس طلب المستخدم.user_id_hash: هاش للمستخدم بدل البريد أو الهاتف.model: اسم النموذج أو العائلة المستخدمة.tool: اسم الأداة المطلوبة.args_digest: SHA-256 مختصر لمدخلات الأداة بعد إزالة الحقول الحساسة.decision: allowed أو denied أو needs_review.latency_ms: زمن تنفيذ الأداة.cost_usd: تكلفة تقريبية للطلب لو متاحة.
تنفيذ Python بسيط بس قابل للاستخدام
الكود التالي يكتب JSONL. كل سطر حدث مستقل. لو عندك 50 ألف tool call في اليوم، الملف اليومي غالبًا هيكون عشرات الميجابايت فقط حسب حجم الحقول، وأسهل في الشحن إلى S3 أو Loki أو ClickHouse.
import hashlib
import hmac
import json
import time
from datetime import datetime, timezone
from pathlib import Path
LOG_PATH = Path("ai_tool_audit.jsonl")
SECRET = b"rotate-this-secret-monthly"
SENSITIVE_KEYS = {"password", "token", "api_key", "email", "phone"}
def scrub_args(args: dict) -> dict:
clean = {}
for key, value in args.items():
if key.lower() in SENSITIVE_KEYS:
clean[key] = "[redacted]"
else:
clean[key] = value
return clean
def digest(data: dict) -> str:
payload = json.dumps(data, sort_keys=True, ensure_ascii=False).encode("utf-8")
return hmac.new(SECRET, payload, hashlib.sha256).hexdigest()[:24]
def audit_tool_call(request_id, user_id, model, tool, args, decision, result_status, latency_ms, cost_usd=0):
clean_args = scrub_args(args)
event = {
"ts": datetime.now(timezone.utc).isoformat(),
"request_id": request_id,
"user_id_hash": digest({"user_id": user_id}),
"model": model,
"tool": tool,
"args_digest": digest(clean_args),
"decision": decision,
"result_status": result_status,
"latency_ms": latency_ms,
"cost_usd": round(cost_usd, 6),
}
with LOG_PATH.open("a", encoding="utf-8") as f:
f.write(json.dumps(event, ensure_ascii=False) + "\n")
start = time.perf_counter()
# هنا مكان تنفيذ الأداة الفعلية بعد بوابة الصلاحيات
time.sleep(0.08)
audit_tool_call(
request_id="req_20260426_001",
user_id="user_42",
model="gpt-5-mini",
tool="send_email",
args={"to":"finance@example.com", "subject":"late orders", "token":"secret"},
decision="needs_review",
result_status="blocked_before_send",
latency_ms=int((time.perf_counter() - start) * 1000),
cost_usd=0.0031,
)الـ trade-off هنا
المكسب واضح: التحقيق ينزل من ساعات إلى أقل من ساعة في فرق صغيرة، لأنك لا تعيد بناء القصة من logs متفرقة. في مثال داخلي افتراضي، زمن التحقيق نزل من 360 دقيقة إلى 45 دقيقة عندما صار كل tool call مربوطًا بـ request_id واحد.
الثمن إنك تضيف طبقة تشغيلية جديدة. لازم تدوّر secret المستخدم في HMAC، وتحدد retention واضحًا، وتمنع تسجيل PII. لو خزنت المدخلات كاملة، أنت نقلت المشكلة من وكيل AI إلى نظام logging. بدل ما تعمل كده، خزّن digest وقرار البوابة واسم الأداة.
OpenAI توضح أن tool calling يعني أن التطبيق هو الذي ينفّذ الكود بعد أن يطلبه النموذج، وليس النموذج نفسه. لذلك نقطة التسجيل الصحيحة تكون حول بوابة التنفيذ في تطبيقك. OWASP يصنّف Excessive Agency كخطر عندما يحصل نظام LLM على صلاحيات أو استقلالية أكثر من اللازم، ووجود سجل تدقيق لا يمنع الخطر وحده، لكنه يخليك تراه بسرعة.
متى لا تستخدم هذه الطريقة
لا تبدأ بها لو تطبيقك مجرد chat بدون أدوات ولا عمليات خارجية. اللوج العادي يكفي غالبًا. ولا تستخدمها كبديل عن permission checks. Audit Log يخبرك ماذا حدث، لكنه لا يمنع تنفيذ أمر خطر. لو الأداة ترسل أموالًا، تمسح بيانات، أو تغيّر صلاحيات، لازم بوابة موافقة قبل التنفيذ وليس بعده.
كمان لا تخزن هذا اللوج للأبد. لو عندك بيانات عملاء، ابدأ بـ 30 يوم retention، ثم راجع المتطلبات القانونية. NIST AI RMF يتعامل مع إدارة مخاطر AI كعملية مستمرة عبر دورة الحياة، والسجل هنا جزء صغير من القياس والمراقبة، وليس الحوكمة كلها.
مصادر
- OpenAI Function Calling Documentation
- OpenAI Tools Guide
- OWASP LLM06:2025 Excessive Agency
- NIST AI Risk Management Framework
- Python json documentation
الخطوة التالية
افتح نقطة تنفيذ الأدوات في وكيلك، وأضف سطر JSONL واحد قبل التنفيذ وسطرًا بعده. لو لم تستطع ربطهما بنفس request_id، ابدأ بإصلاح التتبّع قبل أي تحسين آخر.