پرش به محتوای اصلی
پرش به محتوای مقاله

۱۴۹ دلار هزینهٔ审计 با Claude Fable برای نجات sqlite-utils از حذف داده‌ها

·۱۴ تیر ۱۴۰۵۱۰ دقیقه مطالعه
نسخه ۴.۰rc2 sqlite-utils، عمدتاً نوشته‌شده توسط Claude Fable با هزینه حدود ۱۴۹ دلار
نسخه ۴.۰rc2 sqlite-utils، عمدتاً نوشته‌شده توسط Claude Fable با هزینه حدود ۱۴۹ دلار
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

استفاده از یک خط لوله «بازبینی متقاطع» (Cross-review) با سه مدل مجزا برای审计 کد؛ این اولین بار است که هزینه دقیق دلاری یک بازبینی جامع توسط عامل‌های هوش مصنوعی برای یک کتابخانه متن‌باز افشا می‌شود.

۱۴۹ دلار؛ این مبلغ هزینه حسابرسی (Audit) فنی بود که کتابخانه sqlite-utils نسخه ۴.۰ را از یک باگ بحرانی حذف داده نجات داد. در ۵ جولای ۲۰۲۶، سایمون ویلسون (Simon Willison) از Claude Fable (که از طریق اشتراک‌های Max در دسترس است) استفاده کرد تا تغییرات شکسته‌ساز (breaking changes) را رصد کند. او متوجه شد متد delete_where() باعث مسموم شدن اتصالات پایگاه داده می‌شود و منجر به این می‌گردد که نوشته‌های بعدی به‌صورت بی‌صدا نادیده گرفته و ذخیره نشوند.

این تجربه گامی فراتر از تولید کد ساده است. در حالی که پوشش‌های قبلی ما توضیح داد که توسعه‌دهنده چگونه از ابزارهای جدید برای مدیریت پروژه‌ها بهره می‌گیرند، از جمله اینکه کاربران چگونه از چت‌های ساده به سمت مهندسی برنامه‌ریزی‌شده با Claude Code حرکت کرده‌اند، این گردش‌کار بر نقش «عامل» (Agent) به عنوان یک بازرس سخت‌گیر تضمین کیفیت (QA) در موقعیت‌های حساس تأکید دارد. رویکرد ویلسون از یک درخواست تک‌پرامپتی شروع شد: «بازبینی نهایی پیش از انتشار نسخه پایدار ۴.۰؛ شناسایی هر موردی که اگر بعداً اصلاح شود، باعث شکست کد کاربران شود بسیار حیاتی است». اما این مسیر به یک چرخه شدید ۳۷ مرحله‌ای تبدیل شد که شامل ۳۴ کامیت و بیش از ۱۳۲۱ تغییر مثبت و ۱۹۰ تغییر منفی در ۳۰ فایل مجزا بود.

کشف «سد راه انتشار»

طبق گزارش منتشر شده، شدیدترین یافته، شکست در مدیریت تراکنش‌ها بود. Claude Fable پنج مورد را شناسایی کرد که آن‌ها را به عنوان «سد راه انتشار» (release blockers) دسته‌بندی کرد. بدترین این موارد، باگی در Table.delete_where() بود (واقع در sqlite_utils/db.py:2948) که عملیات DELETE را از طریق یک self.db.execute() ساده و بدون پوشش atomic() اجرا می‌کرد. در مقابل، متد Table.delete() در خط db.py:2944 به درستی در یک Wrapper قرار گرفته بود.

این نقص باعث می‌شد اتصال در حالتی باقی بماند که in_transaction=True باشد. در نتیجه، هر فراخوانی بعدی atomic() به جای شروع تراکنش جدید، وارد شاخه savepoint می‌شد (db.py:430-440) و در نهایت هرگز commit نمی‌شد. ویلسون این مورد را به‌صورت کامل (end-to-end) بازتولید کرد:

۱. ایجاد یک پایگاه داده با دستور db = sqlite_utils.Database("dw.db") و درج سه سطر در جدول "t" با استفاده از db["t"].insert_all([{"id": i} for i in range(3)], pk="id").
۲. اجرای دستور db["t"].delete_where("id = ?", [0]). در این نقطه، مقدار conn.in_transaction برابر با True می‌شود.
۳. درج یک سطر جدید (db["t"].insert({"id": 50})) و ایجاد یک جدول جدید "u" به همراه داده (db["u"].insert({"a": 1})).
۴. بستن پایگاه داده و باز کردن مجدد آن.

نتیجه فاجعه‌بار بود: عملیات حذف، سطر ۵۰ و جدول "u" همگی ناپدید شده بودند. اگرچه این مورد در یک نسخه اصلاحی ۴.۰.۱ قابل رفع بود، اما جلوگیری از آن در مرحله انتشار، مانع از یک نقص طراحی شد که احتمالاً توسعه‌دهندگان را مجبور به جهش به نسخه ۵.۰ می‌کرد.

خط لوله بازبینی چندمدلی

ویلسون برای اطمینان بیشتر، استراتژی «بازبینی متقاطع» (cross-review) را اجرا کرد. او از GPT-5.5 xhigh و Codex Desktop خواست تا کارهای انجام شده توسط مدل Claude را بررسی کنند. ویلسون از آن‌ها خواست تغییرات اعمال شده از آخرین نسخه RC (Release Candidate) را بازبینی کرده و تأیید کنند که لیست تغییرات (changelog) به‌روز است. این لایه دوم نظارت، دو مورد لبه‌ای (edge case) دیگر را افشا کرد:

  • کامیت‌های زودهنگام: در sqlite_utils/db.py:663 متد db.query() دستورات غیر-row را تنها بعد از فراخوانی db.execute() رد می‌کرد. از آنجا که db.execute() (در db.py:705) ابتدا نوشته‌ها را به‌طور خودکار کامیت می‌کند، فراخوانی دستوری مثل db.query("update ...") باعث می‌شد آپدیت پیش از بروز خطای ValueError در دیتابیس ذخیره شود. این رفتار با مستندات متد که به عنوان ابزاری برای SQLهای بازگرداننده سطر تعریف شده بود، در تضاد بود.
  • نشت ژنراتورها: در sqlite_utils/db.py:672 دستورات INSERT ... RETURNING از طریق db.query() تنها زمانی کامیت می‌شدند که ژنراتور بازگشتی کاملاً تخلیه (exhausted) شود. اگر کاربر از next(db.query(...)) استفاده می‌کرد یا اصلاً روی نتایج پیمایش نمی‌کرد، تراکنش باز می‌ماند و با بستن دیتابیس، نوشته‌ها rollback می‌شدند. این مورد با مستندات در docs/changelog.rst:15 و docs/python-api.rst:232 ناسازگار بود.

مهندسی پایداری نسخه ۴.۰

عامل هوش مصنوعی علاوه بر رفع باگ‌ها، به بازسازی کامل مدل تراکنش کمک کرد. در نسخه فعلی پایدار، تضمین شده است که هر متد نوشتاری — شامل insert()، upsert()، update()، delete()، delete_where()، transform()، create_table()، create_index() و enable_fts() — در تراکنش خود اجرا شده و پیش از بازگشت نتیجه، کامیت شود.

این بدان معناست که فراخوانی دستوری مانند db.table("news").insert({"headline": "Dog wins award"}) سطر را بلافاصله روی دیسک ذخیره می‌کند بدون اینکه کاربر نیاز به فراخوانی دستی commit() یا بستن دیتابیس داشته باشد. همین منطق برای SQLهای خام اجرا شده با db.execute() نیز اعمال می‌شود.

اکنون دقیقاً دو سناریو وجود دارد که کاربر باید تراکنش‌ها را مدیریت کند:
۱. برای گروه‌بندی چندین عملیات نوشتاری تا همه با هم موفق یا شکست بخورند، با استفاده از db.atomic().
۲. مدیریت دستی تراکنش با db.begin()؛ در این حالت هیچ چیز تا فراخوانی commit() ذخیره نمی‌شود و کتابخانه هرگز تراکنشی که به این روش باز شده باشد را به‌طور خودکار کامیت نمی‌کند.

ویلسون همچنین با کمک مدل، ناسازگاری‌های تنظیمات autocommit در پایتون ۳.۱۲ را حل کرد. او کشف کرد که اتصالات ایجاد شده با sqlite3.connect(..., autocommit=True) یا autocommit=False پشتیبانی نمی‌شدند زیرا رفتار commit() و rollback() در آن‌ها متفاوت بود. این نقص باعث شکست تقریباً تمام مجموعه تست‌های کتابخانه می‌شد و نیاز به اصلاحات خاصی داشت تا کتابخانه برای کاربرانی که از تنظیمات جدید پایتون استفاده می‌کنند، متوقف نشود.

اصلاحات فنی دقیق

بهبودهای سیستمی دیگری نیز در نسخه ۴.۰ ادغام شدند:

اعتبارسنجی API و خطاها

  • جایگزینی AssertionError با ValueError برای خطاهای اعتبارسنجی. این کار مانع از آن می‌شود که خطاهایی (مانند create_table() بدون ستون، transform() روی جدولی که وجود ندارد، یا ارسال هم‌زمان ignore=True و replace=True) در هنگام اجرای پایتون با پرچم بهینه‌سازی -O به‌صورت بی‌صدا نادیده گرفته شوند.
  • متدهای table.upsert() و table.upsert_all() اکنون اگر رکورد فاقد مقدار کلید اصلی باشد یا مقدار آن None باشد، خطای PrimaryKeyRequired می‌دهند. پیش از این، این رکوردها یا به‌طور بی‌صدا به‌عنوان سطور کاملاً جدید درج می‌شدند یا پس از درج، یک خطای گیج‌کننده KeyError ایجاد می‌کردند.

ابزارهای خط فرمان و دیتابیس

  • متدهای db.enable_wal() و db.disable_wal() اکنون اگر در حین یک تراکنش باز فراخوانی شوند، خطای sqlite_utils.db.TransactionError می‌دهند. پیش از این، آن‌ها بی‌صدا تراکنش باز را کامیت می‌کردند تا حالت ژورنال را تغییر دهند، که این امر تضمین rollback در db.atomic() را می‌شکست.
  • در کلاس View متد enable_fts() حذف شد. این متد پیش از این خطای NotImplementedError می‌داد (چون FTS برای Viewها پشتیبانی نمی‌شود)؛ اکنون به درستی خطای AttributeError می‌دهد و دیگر در مرجع API ظاهر نمی‌شود.
  • دستورات drop-table و drop-view اکنون صراحتاً از حذف اشیاء با نوع نادرست (حتی اگر نام‌ها یکسان باشند) خودداری می‌کنند و با خطایی خارج شده و دستور صحیح را پیشنهاد می‌دهند.
  • پرچم -d/--detect-types از دستورات insert/upsert حذف شد. از نسخه 4.0a1 تشخیص نوع برای CSV/TSV پیش‌فرض شده بود و این پرچم عملاً بی‌اثر (no-op) بود. با این حال، --no-detect-types برای غیرفعال کردن این قابلیت باقی مانده است.

مهاجرت‌ها و کوئری‌ها

  • مهاجرت‌ها (Migrations) اکنون همراه با رکورد ثبت کاربردشان، درون یک تراکنش اجرا می‌شوند. هرگونه استثنا باعث rollback می‌شود تا مهاجرت‌ها به‌سادگی قابل بازاجرا باشند. کاربران می‌توانند با استفاده از @migrations(transactional=False) برای دستوراتی که نمی‌توانند در تراکنش اجرا شوند (مانند VACUUM)، این حالت را غیرفعال کنند.
  • دستور db.table(name).insert({}) اکنون از درج سطرهایی با مقادیر کاملاً پیش‌فرض از طریق INSERT INTO ... DEFAULT VALUES پشتیبانی می‌کند (مورد #759).
  • متدهای upsert اکنون به‌طور خودکار کلیدهای اصلی (یا کلیدهای ترکیبی) جدول موجود را شناسایی می‌کنند و آرگومان pk= را اختیاری می‌کنند.
  • در دستور migrate پرچم --stop-before اکنون مقادیر ناشناخته مهاجرت را به عنوان خطا تلقی می‌کند. همچنین این پرچم با فایل‌های قدیمی‌تر کلاس sqlite_migrate.Migrations سازگار شده و --list اکنون یک عملیات read-only است که منجر به ایجاد فایل دیتابیس یا جدول ردیابی نمی‌شود.
  • متد migrations.applied() اکنون مهاجرت‌ها را به ترتیب اعمال شده برمی‌گرداند.
  • متدهای جدید db.begin()، db.commit() و db.rollback() برای کنترل دستی تراکنش‌ها اضافه شدند.

اقتصاد بازبینی عامل‌محور

ویلسون برای تسهیل این حجم از کار، اشتراک خود را به Claude Max (۲۰۰ دلار در ماه) ارتقا داد تا سهمیه Fable خود را پیش از «Fablepocalypse» در ۷ جولای (که پس از آن مشترکین باید هزینه کامل API را پرداخت کنند) افزایش دهد. او با استفاده از ابزاری به نام AgentsView (uvx agentsview) محاسبه کرد که هزینه خالص API این جلسه ۱۴۹.۲۵ دلار بوده است.

تفکیک هزینه‌ها نشان می‌دهد بخش اعظم بار روی دوش عامل اصلی بوده است:

  • جلسه اصلی (claude-fable-5): ۱۴۱.۰۲ دلار
  • عامل بررسی سطح API: ۲.۴۰ دلار
  • عامل بازبینی تراکنش‌ها/atomic: ۲.۳۹ دلار
  • عامل بازبینی کامیت‌های پس از rc1: ۱.۷۲ دلار
  • عامل بازبینی مهاجرت‌ها: ۱.۴۰ دلار
  • عامل شمارش پرامپت‌ها (claude-opus-4-8): ۰.۳۲ دلار

این هزینه نشان‌دهنده قیمت بالای «عامل‌های استدلالی» (reasoning-heavy) است که قادرند حالت‌های پیچیده را در ده‌ها فایل مختلف مدیریت کنند. ویلسون اشاره کرد که برای بهینه‌سازی هزینه‌ها، باید از عامل‌های ارزان‌تر برای کارهای ساده‌تر استفاده می‌کرد. ویلسون در حالی که در رژه ۴ جولای در Half Moon Bay حضور داشت، عامل هوش مصنوعی در بازه‌های ۱۰ تا ۱۵ دقیقه‌ای روی تکالیف کار می‌کرد و او تنها از طریق آیفون خود گام‌های بعدی را پرامپت می‌کرد.

برای خواننده، این بدان معناست که هزینه یک «حسابرسی» AI اکنون قابل محاسبه است. پرداخت حدود ۱۵۰ دلار برای جلوگیری از یک باگ حذف داده در کتابخانه‌ای پرکاربرد، در مقایسه با بدهی فنی و ضربه به اعتبار ناشی از یک نسخه شکست‌خورده، معامله‌ای بسیار به‌صرفه و ناچیز است.

این تغییر نشان می‌دهد که آینده پایداری نرم‌افزار تنها در تست‌های بهتر نیست، بلکه در یک فاز «عامل خصمانه» (adversarial agent) است؛ جایی که یک مدل سعی می‌کند هر آنچه مدل دیگر ساخته را بشکند. نقش توسعه‌دهنده ارشد از «نویسنده کد» به «بازبین پیشنهادهای عامل» تغییر می‌کند. تاریخچه کامیت‌های لیست تغییرات که توسط Fable با هر تغییر به‌روز شده بود، اکنون به عنوان خلاصه‌ای دقیق از این نسخه عمل می‌کند. ویلسون اشاره کرد که این یادداشت‌ها — که باید خسته‌کننده، پیش‌بینی‌پذیر و دقیق باشند — در واقع بهتر از یادداشت‌های انتشاری بودند که خودش به‌صورت دستی می‌نوشت.

چرا این موضوع مهم است؟

این رویکرد با تکیه بر تجربه عملی ویلسون، ثابت می‌کند که مدل‌های استدلالی می‌توانند جایگزین سناریوهای تست دستی پیچیده شوند. اعتبار این روش در شناسایی باگ‌های «نامرئی» تراکنشی است که معمولاً از دید تست‌های واحد (Unit Tests) پنهان می‌مانند.

تأثیر برای ایران

توسعه‌دهندگان ایرانی می‌توانند با استفاده از مدل‌های ارزان‌تر یا وزن‌های باز (Open Weights) مشابه، این استراتژی بازبینی چندمرحله‌ای را برای پروژه‌های حساس خود پیاده کنند تا بدون هزینه بالای API، پایداری کد را افزایش دهند.

·نگاه ما
تحریریه دات‌هوش

این مورد نشان می‌دهد که ما از عصر «تولید کد» به عصر «تأیید کد» وارد شده‌ایم. نکته کلیدی این است که هزینه ۱۵۰ دلاری برای یک بازبینی دقیق، در واقع مدل اقتصادی جدید QA است؛ جایی که هزینه API جایگزین ساعت‌های گران‌بهای بررسی دستی توسط توسعه‌دهندگان ارشد می‌شود. انتقال نقش برنامه‌نویس به یک «ناظر بر عامل‌ها» تغییر بنیادینی در چرخه حیات توسعه نرم‌افزار (SDLC) ایجاد می‌کند.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.