الدرس 5 من 10
أدوات حقيقية وآمنة
الأدوات هي اللي بتفرق agent لعبة عن agent مفيد.
١. آلة حاسبة آمنة
الموديلات مش دقيقة في الحسابات الكبيرة، فادّيله آلة حاسبة. بس أوعى تستخدم eval() على نص جاي من الموديل: ده بيسمح بتنفيذ أي كود بايثون! الحل إنك تحلل التعبير وتسمح بالعمليات الحسابية بس:
import ast import operator as op ALLOWED = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.USub: op.neg} def calculate(expression: str) -> str: """Safely evaluate a math expression like '1850 * 0.14 + 75'.""" def ev(node): if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): return node.value if isinstance(node, ast.BinOp) and type(node.op) in ALLOWED: return ALLOWED[type(node.op)](ev(node.left), ev(node.right)) if isinstance(node, ast.UnaryOp) and type(node.op) in ALLOWED: return ALLOWED[type(node.op)](ev(node.operand)) raise ValueError("Only numbers and + - * / ** are allowed") return str(round(ev(ast.parse(expression, mode="eval").body), 2)) print(calculate("1850 * 0.14 + 75")) print(calculate("(640 - 64) / 3")) try: calculate("__import__('os').system('rm -rf /')") # an attack attempt except ValueError as e: print("blocked:", e)
٢. قراية ملفات في فولدر محدد
لو اديت الـ agent أداة تقرا ملفات، لازم تحبسه في فولدر معين. من غير كده ممكن يطلب ../../.env ويقرا الأسرار. الهجوم ده اسمه path traversal:
from pathlib import Path DOCS_DIR = Path("store_docs").resolve() DOCS_DIR.mkdir(exist_ok=True) (DOCS_DIR / "refund_policy.txt").write_text("Returns accepted within 14 days.", encoding="utf-8") def read_doc(filename: str, max_chars: int = 2000) -> str: """Read a document from the store's docs folder.""" path = (DOCS_DIR / filename).resolve() if not path.is_relative_to(DOCS_DIR): # escaped the allowed folder? raise PermissionError("Access outside store_docs is not allowed") if not path.exists(): available = ", ".join(p.name for p in DOCS_DIR.iterdir()) raise FileNotFoundError(f"Not found. Available files: {available}") return path.read_text(encoding="utf-8")[:max_chars] # cap the size print(read_doc("refund_policy.txt")) for bad in ["../.env", "shipping.txt"]: try: read_doc(bad) except Exception as e: print(f"{bad!r}: {e}")
لاحظ رسالة «Not found» فيها أسماء الملفات المتاحة: كده الموديل يقدر يصلح طلبه لوحده في الخطوة الجاية. ولاحظ max_chars: نتايج الأدوات بتدخل الـ context وبتتحاسب tokens، فمتبعتش ملف ٥٠٠ صفحة.
٣. قاعدة بيانات: قراية بس
أداة بتنفذ SQL من الموديل خطيرة لو تقدر تمسح أو تعدل. افتح الاتصال read-only، وده بيمنع أي تعديل على مستوى قاعدة البيانات نفسها مش على مستوى الكود بس:
import sqlite3 # setup: a small orders database (normally this already exists) setup = sqlite3.connect("store.db") setup.executescript(""" DROP TABLE IF EXISTS orders; CREATE TABLE orders (id TEXT, city TEXT, total REAL, status TEXT); INSERT INTO orders VALUES ('1024','Cairo',1850,'shipping'), ('1031','Giza',640,'delivered'), ('1040','Aswan',2300,'delivered'), ('1043','Cairo',420,'cancelled'); """) setup.commit(); setup.close() def query_orders(sql: str) -> str: """Run a read-only SQL query on the orders table (id, city, total, status).""" conn = sqlite3.connect("file:store.db?mode=ro", uri=True) # READ-ONLY connection try: rows = conn.execute(sql).fetchmany(50) # cap the rows return str(rows) finally: conn.close() print(query_orders("SELECT city, SUM(total) FROM orders WHERE status='delivered' GROUP BY city")) try: query_orders("DELETE FROM orders") except sqlite3.OperationalError as e: print("blocked:", e)
٤. API خارجي
import requests def get_exchange_rate(currency: str) -> str: """Get today's rate of a currency (e.g. USD) against the Egyptian pound.""" try: r = requests.get(f"https://api.example-rates.com/latest/{currency.upper()}", timeout=5) # ALWAYS set a timeout r.raise_for_status() except requests.RequestException as e: raise RuntimeError(f"Rate service unavailable, try again later ({type(e).__name__})") return str(r.json()["rates"]["EGP"])
💡 قواعد لأي أداةtimeout لأي طلب شبكة. حد أقصى لحجم النتيجة. رسايل خطأ مفيدة للموديل. أقل صلاحيات ممكنة (read-only لو القراية كفاية). والأدوات اللي بتغيّر حاجات ليها درس لوحدها في الأمان.