# گزارش جامع و دستورکار اجرایی: کرالر STT بله، متن‌باز، شخص ثالث و ترکیب بهینه > **نسخه:** 2.0 | **تاریخ:** ۱۶ مهر ۱۴۰۵ (8 Oct 2026) > **مخاطب اول:** مدیر پروژه (انسان) ← **بخش A** > **مخاطب دوم:** ایجنت هوش مصنوعی مجری ← **بخش B (دستورکار)** و **بخش C (دانش‌پایه)** > **ضمیمه‌ها:** بخش D > > **ورودی‌های بررسی‌شده:** گزارش A (متن‌باز و شخص ثالث)، گزارش B (`bale_stt_executive_roadmap_report3.md`)، و راستی‌آزمایی مستقل با منابع عمومی (PyPI، مستندات رسمی بله، arXiv، Hugging Face، GitHub). > **دامنه:** طبق درخواست، حریم خصوصی و امنیت در این سند تصمیم‌گیری نشده است. فقط «راه‌ها، ارزیابی، نظر من و ترکیب بهینه» آمده است. فقط جاهایی که ایجنت برای اجرای فنی لازم دارد (مثل مدیریت سکرت‌ها) ذکر شده است. > > **برچسب‌های وضعیت در کل سند:** > ✅ تأیید شد (با منبع عمومی) | 🟡 تأییدنشده یا استنباط من | ❌ با شواهد در تناقض است > > **محدودیت من:** شبکه sandbox من بسته بود؛ پکیجی را نصب نکردم و پروتو یا کد کتابخانه‌ها را نخواندم. هر جا «🟡» می‌بینید یعنی ایجنت باید خودش بررسی کند، نه اینکه لزوماً غلط باشد. ## راهنمای استفاده از این سند | اگر شما… | این بخش‌ها را بخوانید | |---|---| | مدیر پروژه هستید | **بخش A** (۵ دقیقه) و در صورت نیاز جدول ریسک و تصمیم‌ها | | ایجنت مجری هستید | **بخش B کامل** (دستورکار)، سپس **بخش C** برای زمینه و **ضمیمه D** برای دستورها | | می‌خواهید همه راه‌ها و نظرات را ببینید | **بخش C** | --- # بخش A: برای مدیر پروژه ## A1. مسئله و هدف بات تدارکات متن کاربر را می‌گیرد و عملیات روی آن انجام می‌دهد. می‌خواهیم کاربر بتواند **ویس** هم بدهد. بات اکنون چند ASR را موازی صدا می‌زند و با یک LLM اجماع‌گیری می‌کند. هدف این پروژه: **افزودن متن STT خود پیام‌رسان بله به‌عنوان یکی از کاندیدها** تا دقت (به‌خصوص برای نام کالا، برند، عدد و واحد) بالا برود. **مشکل پایه:** Bot API رسمی بله هیچ متد STT ندارد (✅ تأیید شد؛ در مستندات رسمی، شیء `Voice` فقط `file_id` و `file_unique_id` دارد). پس تنها راه گرفتن متن بله، **شبیه‌سازی یک کاربر عادی** (حساب کاربری) است. ## A2. خلاصه یافته‌ها 1. **هیچ پروژه عمومی‌ای که خروجی STT خود بله را استخراج کند پیدا نشد.** جامعه معمولاً STT را بیرون از بله انجام می‌دهد. ابزارهای مجاور (کلاینت‌های مهندسی‌معکوس‌شده، سلف‌بات‌ها با Selenium/Playwright) وجود دارند و نشان می‌دهند مسیر **شدنی** است، ولی برای این کار آزموده نشده است. 2. **دو ادعای کلیدی هر دو گزارش تأییدنشده‌اند** و پروژه به آن‌ها وابسته است: - متد `bale.turing.v1.AI / GetTranscript` در `bale-sdk` (🟡 نتوانستم ببینم). - وجود دکمه «تبدیل به متن» در **نسخه وب** بله (🟡 هیچ منبعی تأیید نکرده؛ فقط اپ اندروید/iOS در اخبار آمده است). 3. **کتابخانه‌ها:** `bale-sdk` (۶۰۷ RPC، نسخه 0.1.1) **مجوز اختصاصی** دارد؛ `aiobale` و فورک‌هایش `autobale` و `BaleClient` **MIT** هستند ولی کم‌نگهداری. پکیج `aiobale-py` که گزارش‌ها می‌گویند پیدا نشد (🟡). 4. **متن‌باز لزوماً دقیق‌تر نیست.** بنچمارک مستقل PSRB نشان می‌دهد ASR تجاری فارسی و Google Chirp 2 حدود **۱۴ امتیاز WER** از بهترین مدل متن‌باز جلوترند (۱۹٫۳ تا ۲۴٫۶ درصد در برابر ۳۳٫۹ درصد). نویسندگان مقاله از Part AI هستند و Avanegar ظاهراً محصول همان شرکت است؛ پس عدد آن را خوش‌بینانه بدانید. 5. **ایده معماری جدید و ساده‌تر:** به‌جای اینکه حساب کرالر ویس آپلود کند، یک **بات رله** (جدا از بات اصلی) ویس را با Bot API رسمی برای حساب کرالر بفرستد. کرالر فقط دریافت، ترنسکرایب و حذف می‌کند. (جزئیات در B5.) 6. **ارزش پروژه را فقط آزمون دقت تعیین می‌کند.** پیشنهاد می‌کنم **قبل از هر ساخت** روی ۳۰ تا ۱۰۰ ویس واقعی اندازه بگیریم (گام T2). ## A3. گزینه‌ها در یک نگاه | # | گزینه | چه می‌کند | تلاش (تخمین من) | ریسک | پتانسیل دقت | توصیه | |---|---|---|---|---|---|---| | 1 | **مسیر رسمی با بله** | درخواست API یا همکاری رسمی | ۱ ایمیل | کم | — | **حتماً انجام شود (موازی)** | | 2 | **کرالر RPC مستقیم (کلاینت کاربری)** | حساب اختصاصی ویس را ترنسکرایب می‌کند | ۳–۵ روز | متوسط | ⭐ نامعلوم تا آزمون | **مسیر اصلی کرالر** | | 3 | **کرالر Playwright روی وب بله** | مرورگر headless دکمه را می‌زند | ۱–۲ روز | متوسط | همان | **فالبک** (اگر دکمه وب باشد) | | 4 | شبیه‌ساز اندروید | اپ بله در امولاتور | ۵+ روز | بالا | همان | فقط آخرین چاره | | 5 | چت هوش مصنوعی بله | ویس به بات AI بله | ۱ روز | بالا (خروجی ناخالص) | پایین | **رد** | | 6 | پراکسی غیررسمی (MajidAPI و مشابه) | REST با لینک ویس | ساعتی | بالا (بدون SLA) | نامعلوم | **رد** | | 7 | ASR تجاری فارسی (Avanegar، Aipa، فارس‌آوا، IOType) | پرووایدر جدید در `stt.py` | ۱ روز | کم | ⭐⭐⭐ بالا | **توصیه قوی** | | 8 | Google Chirp 2 (و ابرهای بین‌المللی) | پرووایدر جدید | ۱ روز | متوسط (دسترسی و ارز) | ⭐⭐⭐ بالا | توصیه (اگر دسترسی هست) | | 9 | Faster-Whisper خودمیزبان + واژگان تدارکات | سرور داخلی | ۱–۲ روز | کم | ⭐⭐ متوسط | **توصیه** (مستقل) | | 10 | فاین‌تیون روی داده خودتان (LoRA) | مدل داخلی | هفته‌ها | کم | ⭐⭐⭐ در بلندمدت | **سرمایه‌گذاری بلندمدت** | | 11 | LLM اجماع‌گیر + کاتالوگ کالا + نرمال‌سازی | تقویت لایه اجماع | چند روز | کم | ⭐⭐⭐ بیشترین بازده | **حتماً** | ## A4. توصیه من **ترکیب هدف: «بله + ASR تجاری فارسی + مدل خودمیزبان + LLM اجماع‌گیر با کاتالوگ کالا»** - **کرالر بله یک کاندید است، نه ستون فقرات.** اگر خراب یا مسدود شد، بات باید بی‌وقفه با بقیه ادامه دهد (ساختار همین‌طور طراحی شده: خطا ⇒ ۵۰۳ ⇒ رد شدن پرووایدر). - حداقل **دو کاندید غیر بله** همیشه فعال باشد. - **ترتیب کار:** (۱) آزمون دقت و تأیید دو ادعای کلیدی ⇐ (۲) فقط اگر ارزش داشت، ساخت کرالر ⇐ (۳) به‌موازات، پرووایدرهای شخص ثالث و تقویت اجماع ⇐ (۴) سواک ⇐ (۵) جمع‌آوری داده برای فاین‌تیون. - **در صورتی که آزمون دقت نشان دهد بله بهتر نیست، کرالر ساخته نشود** و سرمایه روی گزینه‌های ۷ تا ۱۱ برود. ## A5. تصمیم‌ها و ورودی‌هایی که از شما لازم است > از آنجا که تمام اجرا توسط ایجنت AI انجام می‌شود، این‌ها تنها نقاطی‌اند که ایجنت **نمی‌تواند** خودش انجام دهد یا نباید تصمیمش را بگیرد. | ID | تصمیم / ورودی | چرا لازم است | پیش‌فرض پیشنهادی من | مسدودکننده برای | |---|---|---|---|---| | **H1** | **Go/No-Go حقوقی/ToS** برای خودکارسازی یک حساب کاربری بله | بله ممکن است آن را مجاز نداند؛ تصمیم مدیریتی است | تصمیم کتبی قبل از T4 | T4 به بعد | | **H2** | **شماره/سیم‌کارت اختصاصی شرکتی** و یک حساب بله برای کرالر | کرالر باید با یک هویت ثابت کار کند | شماره حقوقی، نه شخصی | T4 به بعد | | **H3** | **نمونه ویس‌ها** (حداقل ۳۰، بهتر ۱۰۰) با رونوشت انسانی و جدول کالا/تعداد/واحد | تنها راه معتبر برای سنجش دقت | از ویس‌های واقعی استعلام، با رضایت فرستنده | T2، T9، T10 | | **H4** | **ورود OTP**: وقتی ایجنت کد پیامکی لازم دارد، کد را می‌دهید | OTP را فقط انسان دریافت می‌کند | پاسخ ظرف چند دقیقه | T4 | | **H5** | **ساخت بات رله** با BotFather و تحویل توکن آن | معماری رله (B5) | یک بات جدا از بات اصلی | T6 | | **H6** | **دسترسی ایجنت به مخزن بات** (`bot/app/stt.py`, `config.py`, `.env` نمونه) | هماهنگی با قرارداد پرووایدرها | فقط خواندن + تغییر `.env` | T0، T11 | | **H7** | **کلیدها/اکانت آزمایشی** پرووایدرهای تجاری (Avanegar، Aipa، فارس‌آوا، Chirp 2…) و سقف بودجه | سنجش واقعی | سقف ماهانه مشخص | T9 | | **H8** | **سرور برای Faster-Whisper** (ترجیحاً GPU) یا اجازه استفاده از سرور فعلی | میزبانی مدل خودمیزبان | GPU با ۸GB VRAM یا بیشتر | T9 | | **H9** | ارسال **ایمیل به بله** برای مسیر رسمی (S1) و مالک پیگیری | ایجنت نمی‌تواند به‌جای شرکت مکاتبه کند | همین هفته | — | | **H10** | **مالک نگهداری** کرالر پس از استقرار | اگر پروتکل بله عوض شد | یک نفر مشخص | استقرار نهایی | | **H11** | **تأیید نهایی استقرار** و مرج | دروازه انسانی | پس از G5 | T11 | ## A6. زمان‌بندی و هزینه (تخمین من) | فاز | مدت | یادداشت | |---|---|---| | تأیید ادعاها + آزمون دقت (T0–T3 و مقایسه) | ۱–۲ روز | هزینه صفر؛ بیشترین ارزش اطلاعاتی | | کشف و پروتوتایپ کرالر (T4–T6) | ۲–۴ روز | فقط اگر G0 و G1 عبور کنند | | adapter سخت‌شده + اتصال (T8، T11) | ۲ روز | | | پرووایدرهای شخص ثالث و مدل خودمیزبان (T9) | ۲–۳ روز، **موازی** | هزینه ریالی/ارزی طبق قرارداد؛ استعلام رسمی بگیرید | | تقویت اجماع (T10) | ۲–۳ روز | بیشترین بازده | | سواک | ۷ روز | | | فاین‌تیون (T13) | ۲+ هفته، اختیاری | | > قیمت‌هایی که گزارش A آورده (مثلاً ۴۵–۶۰ هزار تومان در ساعت برای فارس‌آوا یا ۲۴۰–۵۰۰ تومان در دقیقه برای بات‌های واسط) 🟡 تأییدنشده‌اند؛ قبل از بودجه‌بندی استعلام بگیرید. ## A7. ریسک‌های اصلی | ریسک | احتمال | اثر | مقابله | |---|---|---|---| | RPC `GetTranscript` وجود ندارد یا پایدار نیست | نامعلوم | ساخت E1 ممکن نیست | تأیید در T1 و T5؛ فالبک Playwright | | دکمه در وب نیست | نامعلوم | Playwright ممکن نیست | تأیید در T4 | | بن یا محدودیت حساب | متوسط | از دست رفتن کاندید بله | حجم کم، فاصله تصادفی، سقف روزانه، ۵۰۳ و حذف از اجماع | | تغییر پروتکل/UI بله | متوسط | قطع کاندید بله | آزمون طلایی شبانه، هشدار، rollback با env | | انقضای نشست و نیاز به OTP | بالا (دوره‌ای) | توقف موقت | ولوم نشست، هشدار به انسان، تغییر مسیر به بقیه | | کتابخانه‌های MIT رها می‌شوند | متوسط | نگهداری سخت | pin نسخه، آینه داخلی، لایه‌بندی | | مجوز اختصاصی `bale-sdk` | بالا | ممنوعیت استفاده | از آن فقط به‌عنوان مرجع و با اجازه نویسنده؛ کد را کپی نکنید | | دقت بله کمتر از انتظار | متوسط | پروژه بی‌ارزش | **G0 پیش از ساخت** | | ToS بله | نامعلوم | توقف اجباری | **H1** (تصمیم مدیریت) | ## A8. معیارهای موفقیت و توقف (دروازه‌ها) | دروازه | شرط عبور (قابل‌تنظیم) | اگر رد شد | |---|---|---| | **G0** دقت | بله خطای استخراج اقلام اجماع را حداقل **۱۵٪ نسبی** کم می‌کند، یا حداقل ۲ مورد سخت را درست می‌کند که بقیه نمی‌کنند | **توقف کرالر**، تمرکز روی گزینه‌های ۷ تا ۱۱ | | **G1** وجود | RPC تأیید شد یا دکمه وب تأیید شد | شبیه‌ساز (فقط اگر G0 خیلی خوب بود) یا توقف | | **G2** کشف | درخواست/پاسخ قابل بازتولید و پروتو ساخته شد | فالبک Playwright | | **G3** پروتوتایپ | ۱ ویس ⇒ ۱ متن فارسی معتبر | بازبینی طراحی | | **G4** پایداری | ≥ ۹۵٪ موفقیت روی ۱۰۰ ویس ترتیبی، p95 ≤ ۸ ثانیه، بدون بن | کاهش نرخ؛ در بن، توقف | | **G5** ارزش | بهبود اجماع روی مجموعه طلایی تأیید شد | حذف بله از اجماع، ادامه با بقیه | | **G6** ToS | تصمیم کتبی مدیریت | توقف | | **G7** سواک | یک هفته بدون ابطال نشست | بازگشت به G4 | ## A9. نظر من درباره دو گزارش (خلاصه) **گزارش A (متن‌باز و شخص ثالث):** دسته‌بندی ۸ راهکار و پیشنهاد دومسیره درست است. ایرادها: چند ادعای تأییدنشده (`aiobale-py`، `GetTranscript`، قیمت‌ها)؛ «متن‌باز پایدارترین راهکار است» با داده PSRB سازگار نیست ❌؛ ارائه‌دهندگان مهم (Avanegar، Aipa، Chirp 2) غایب‌اند؛ مقایسه دقت فقط با WER خودگزارشی روی Common Voice. **گزارش B (نقشه راه عملیاتی):** دیاگرام‌ها و جریان تاب‌آوری خوب‌اند و اصلاحات قبلی اعمال شده (۵۰۳، `compare_digest`، قفل ترتیبی). ایرادها: - **کد مرجع شبه‌کد است.** `client.send_voice` و `client.raw_call` در README عمومی `bale-sdk` نیستند. مدل رسمی RPC خام آن `client..(...)` است و نام namespace از بخش آخر نام سرویس می‌آید؛ پس فراخوانی `bale.turing.v1.AI` احتمالاً به‌شکل `client.ai.GetTranscript(...)` است (🟡 استنباط). - پاسخ `GetTranscript` طبق خود سند `mustWait` و `DownloadSource` است؛ یعنی متن شاید باید دانلود شود، نه رشته مستقیم. - کلید پیش‌فرض در کد (`bale_internal_secret`) و شمارنده روزانه درون‌حافظه‌ای (با ریست کانتینر صفر می‌شود). - آپلود ویس به Saved Messages طراحی نشده؛ راه رله ساده‌تر است (B5). - ادعای «بدون حتی ۱ ثانیه قطعی» فقط وقتی درست است که `stt.py` پاسخ ۵۰۳ را بی‌خطا رد کند؛ باید در کد بات بررسی شود (T0). ## A10. آیا دیگران هم این مشکل را داشته‌اند؟ هیچ نمونه عمومی از استخراج STT خود بله پیدا نشد. الگوهای مجاور (همه در بخش C۲): بات رسمی + ASR مستقل (رایج‌ترین)، کلاینت کاربری مهندسی‌معکوس‌شده (`aiobale`، `bale-sdk`)، سلف‌بات Selenium (`baleself`)، اسکرپر Playwright روی Bale Web. نتیجه: **مسیر شدنی است ولی پیشگام‌شدن ریسک دارد**؛ برای همین کرالر را یک کاندید اختیاری و قابل‌حذف طراحی می‌کنیم. --- # بخش B: دستورکار ایجنت مجری > **تو یک ایجنت هوش مصنوعی هستی که این پروژه را اجرا می‌کنی.** این بخش دستورکار توست. بخش C دانش‌پایه است. بخش D دستورها و قالب‌ها را دارد. هر جا نوشته «انسان»، یعنی باید متوقف شوی و از انسان بخواهی (جدول H1…H11 در A5). ## B0. مأموریت افزودن **متن STT پیام‌رسان بله** به‌عنوان **پرووایدر سوم** خط‌لوله `bot/app/stt.py`، از طریق یک **میکروسرویس adapter با قرارداد OpenAI** (`POST /v1/audio/transcriptions`)، بدون تغییر کد بات (فقط `.env`). همزمان، پرووایدرهای شخص ثالث و مدل خودمیزبان را ارزیابی و اضافه می‌کنی و لایه اجماع را تقویت می‌کنی. هر مرحله با دروازه‌های A8 کنترل می‌شود. ## B1. قواعد اجرا (الزامی) 1. **صداقت و برچسب‌گذاری:** هر یافته را با یکی از ✅/🟡/❌ و منبع یا خروجی دستور ثبت کن. هرگز چیزی را که اجرا یا مشاهده نکرده‌ای «تأییدشده» ننویس. اگر نتوانستی بررسی کنی، بنویس «بررسی نشد» و دلیل را بگو. 2. **سند را حقیقت مطلق ندان.** مواردی که در این سند 🟡 هستند را اول بررسی کن (T0–T1). اگر با واقعیت نخواند، سند را اصلاح و به انسان گزارش بده. 3. **کد گزارش B و بخش‌های «شبه‌کد» اینجا را اجراشدنی فرض نکن.** نام متدها را از کتابخانه واقعی نصب‌شده بخوان (`help()`, `dir()`, خواندن سورس). 4. **مجوز:** کدی از `bale-sdk` کپی **نکن** (اختصاصی). مجاز: خواندن مستندات عمومی آن برای شناخت نام سرویس/متد، و تولید پروتوی خودت از کپچر ترافیک (clean-room). استفاده از سورس/پروتوهای آن فقط با تأیید کتبی انسان (H1). 5. **فقط حساب اختصاصی** (H2). هرگز از حساب شخصی استفاده نکن. 6. **نرخ در آزمایش:** حداکثر ۱ درخواست در هر ۳ ثانیه و ۱۰۰ درخواست در روز روی حساب بله؛ فاصله تصادفی؛ اجرای ترتیبی. در صورت خطای ۴۲۹/محدودیت یا چالش، فوراً متوقف شو و به انسان گزارش بده. 7. **سکرت‌ها:** توکن بات، JWT، کوکی، کلید API و کد OTP را هرگز در لاگ، commit، گزارش یا خروجی چاپ نکن. در کپچرها `access_token`، `Authorization`، کوکی‌ها و شماره تلفن را قبل از ذخیره redact کن. نشست فقط روی ولوم داکر. 8. **تغییر در مخزن بات:** فقط `.env` (و در صورت لزوم مستند). هیچ چیز را روی شاخه‌های دیگران push نکن. کار روی شاخه جدا (مثلاً `feat/bale-stt`) و PR به انسان. 9. **متوقف شو و بپرس** در این موارد: نیاز به OTP، نیاز به تصمیم حقوقی، هر خطای احتمال بن، هزینه جدید، تغییری خارج از محدوده، یا نتیجه‌ای که با فرض‌های این سند در تناقض باشد. 10. **بازتولیدپذیری:** هر آزمایش را با اسکریپت در `tools/` بنویس، نه دستور یک‌بارمصرف. خروجی‌ها در `docs/findings/`. 11. **قطع سریع (kill switch):** همیشه راه خاموش‌کردن کاندید بله با env (خالی کردن `PROCUREMENT_BOT_STT_PROVIDER_3_*`) موجود باشد. 12. **هیچ کاری که یک تصمیم برگشت‌ناپذیر است را بدون تأیید انسان انجام نده** (حذف داده، تغییر شماره، حذف حساب، مصرف بودجه). ## B2. ورودی‌های لازم از انسان همان جدول A5 (H1–H11). قبل از هر وظیفه، چک کن ورودی‌های مورد نیازش آمده است؛ اگر نه، **همان لحظه با قالب B9.2 از انسان بخواه** و تا رسیدن، وظایف مستقل دیگر را پیش ببر. ## B3. چیدمان پیشنهادی مخزن ``` bale-stt/ ├── adapter/ │ ├── main.py # FastAPI، قرارداد OpenAI │ ├── config.py # خواندن env (الزامی‌ها بدون پیش‌فرض) │ ├── counters.py # شمارنده روزانه پایدار (SQLite) │ ├── engines/ │ │ ├── base.py # رابط Engine │ │ ├── stub.py # موتور آزمایشی (بدون بله) │ │ ├── rpc.py # E1: RPC مستقیم + رله │ │ ├── playwright_engine.py # E2 │ │ └── relay_bot.py # کلاینت Bot API برای بات رله │ ├── tests/ │ ├── Dockerfile │ └── requirements.txt ├── tools/ │ ├── probe_bot_api.py # T3 │ ├── capture_ws.py # T4/T5 │ ├── decode_frames.py # T5 │ └── eval/ │ ├── normalize.py # نرمال‌سازی فارسی │ ├── metrics.py # CER/WER/F1 اقلام │ └── run_eval.py ├── docs/findings/ # خروجی هر وظیفه ├── docker-compose.stt.yml └── .env.example # بدون مقدار واقعی ``` ## B4. گراف وظایف (قابل‌خواندن توسط ماشین) ```yaml tasks: T0: {name: "بازبینی کد بات (stt.py/config.py)", needs: [H6], gate: null} T1: {name: "تأیید پکیج‌ها و جست‌وجوی GetTranscript", needs: [], gate: G1 (بخش الف)} T2: {name: "آزمون دقت دستی/نیمه‌خودکار (کیت ارزیابی)", needs: [H3], gate: G0} T3: {name: "پروب Bot API (فیلد مخفی؟)", needs: [H5 or existing_bot_token], gate: null} T4: {name: "بررسی وب بله + کپچر ترافیک", needs: [H1, H2, H4], gate: G1} T5: {name: "رمزگشایی و ساخت پروتو (clean-room)", needs: [T4], gate: G2} T6: {name: "پروتوتایپ E1 با بات رله", needs: [T5, H5], gate: G3} T7: {name: "موتور فالبک Playwright (E2)", needs: [T4], gate: null, optional: true} T8: {name: "adapter سخت‌شده + تست‌ها", needs: [T6 or T7 or stub], gate: null} T9: {name: "پرووایدرهای شخص ثالث + Faster-Whisper", needs: [H3, H7, H8], gate: null, parallel: true} T10: {name: "تقویت اجماع (نرمال‌سازی، کاتالوگ، prompt)", needs: [H3], gate: G5, parallel: true} T11: {name: "اتصال به بات (.env) + E2E + rollback", needs: [T8, H11], gate: G4} T12: {name: "سواک و مانیتورینگ", needs: [T11], gate: G7} T13: {name: "جمع‌آوری داده و فاین‌تیون", needs: [T12, H3], gate: null, optional: true} start_order: [T0, T1, T2, T3, T9, T10] # مستقل از بله، همین حالا شروع می‌شوند stop_rules: - "اگر G0 رد شد: T4..T8 و T11 اجرا نشود؛ فقط T9، T10 و T13" - "اگر G6 (ToS) تأیید نشد: هیچ وظیفه‌ای که با حساب بله کار می‌کند اجرا نشود" ``` ## B5. طراحی هدف (که باید بسازی) ### B5.1 جریان کلی ```mermaid sequenceDiagram autonumber actor U as کاربر participant BOT as بات اصلی (stt.py) participant AD as adapter (bale-stt) participant RB as بات رله (Bot API) participant CR as کلاینت کاربری (حساب کرالر، همان سرویس) participant BS as سرورهای بله U->>BOT: ویس BOT->>AD: POST /v1/audio/transcriptions (بایت‌های صوت) AD->>AD: ffmpeg → OGG/Opus، ساخت nonce AD->>RB: sendVoice(chat_id=کرالر، voice=OGG، caption=nonce) RB->>BS: ارسال BS-->>CR: آپدیت پیام جدید (Media، Peer، message_id، caption=nonce) CR->>BS: GetTranscript(voice, outPeer, messageId) BS-->>CR: mustWait / DownloadSource CR->>BS: (تکرار تا آماده) سپس دانلود متن CR->>BS: حذف پیام CR-->>AD: متن AD-->>BOT: {"text": "..."} یا HTTP 503 ``` ### B5.2 چرا «بات رله» و نه «آپلود از حساب کرالر»؟ - adapter طبق قرارداد OpenAI فقط **بایت صوت** دریافت می‌کند، نه `file_id` بات اصلی. پس ارسال با `file_id` بات اصلی ممکن نیست مگر کد بات تغییر کند. - **گزینه R1 (توصیه من):** یک **بات رله جدا** (توکن مخصوص adapter). adapter با Bot API رسمی `sendVoice` با آپلود multipart می‌فرستد (طبق مستندات رسمی تا ۵۰ مگابایت). کرالر فقط می‌خواند و ترنسکرایب می‌کند. بات اصلی دست‌نخورده می‌ماند. - **گزینه R2:** `file_id` را از بات اصلی پاس بدهیم (نیاز به تغییر کوچک در بات؛ `file_id` مخصوص همان بات است). - **گزینه R3 (گزارش B):** کرالر خودش آپلود کند. نیاز به RPCهای آپلود و ساخت `Media`؛ سخت‌تر. - **قالب صوت:** برای نمایش «ویس» (و در نتیجه دکمه تبدیل به متن) از `sendVoice` استفاده کن و صوت را به **OGG/Opus** تبدیل کن. طبق مستندات، ارسال از طریق URL فقط برای `audio/ogg` با حجم ≤ ۱ مگابایت ویس شناخته می‌شود؛ بزرگ‌تر به‌صورت فایل می‌رود. اگر ویس به‌صورت «فایل» نمایش داده شد، دکمه ترنسکرایب ممکن است نباشد (در T6 آزمایش شود). - **همبستگی:** برای تشخیص پیام درست در سمت کرالر، `caption` را یک **nonce تصادفی** بگذار. اجرای ترتیبی (قفل) هم خطر اشتباه را کم می‌کند. - **پیش‌نیاز:** حساب کرالر باید یک‌بار بات رله را `/start` کند (🟡 فرض؛ در T6 تأیید شود). - **ریسک:** اینکه ترنسکریپت روی پیام **دریافتی از بات** هم کار کند 🟡 (در اپ، دکمه روی ویس دریافتی هست؛ برای بات تأیید نشده). ### B5.3 لایه‌های موتور (به ترتیب اولویت) | اولویت | موتور | شرط انتخاب | |---|---|---| | ۱ | **E1** RPC مستقیم (انتقال از `aiobale`/`autobale`/`BaleClient`، پروتوی خودساخته) | GetTranscript پیدا و بازتولید شد | | ۲ | **E1+** ورود/تمدید توکن با Playwright، کار اصلی با پروتکل | ورود مستقیم ناپایدار است | | ۳ | **E2** Playwright کامل (کلیک دکمه، خواندن DOM) | RPC ممکن نشد ولی دکمه وب هست | | ۴ | **E3** امولاتور اندروید | فقط با تصمیم صریح انسان | > نکته استنباطی: اگر RPC در باندل وب بله وجود دارد، به احتمال زیاد دکمه هم در وب هست (🟡). --- ## B6. وظایف به تفصیل > برای هر وظیفه: **هدف ← مراحل ← خروجی ← معیار پذیرش ← در صورت شکست**. ### T0. بازبینی کد بات - **هدف:** فهمیدن اینکه `stt.py` چطور پرووایدرها را صدا می‌زند و با خطا چه می‌کند. - **مراحل:** 1. `bot/app/stt.py` و `bot/app/config.py` را بخوان؛ پاسخ این‌ها را بنویس: قرارداد درخواست؟ timeout؟ با ۵۰۳/۴۰۱/۴۲۹ چه می‌کند؟ با `{"text": ""}` چه می‌کند؟ چند پرووایدر پشتیبانی می‌شود؟ محل `PROCUREMENT_BOT_STT_PROVIDER_N_*`؟ 2. ببین اجماع (LLM) چطور کاندیدها را می‌گیرد و آیا metadata پرووایدر را می‌بیند. - **خروجی:** `docs/findings/00_bot_review.md`. - **پذیرش:** برای هر سؤال پاسخ مستند با شماره خط. - **اگر شکست:** اگر ۵۰۳ باعث توقف کل خط‌لوله می‌شود، یک تغییر کوچک و جدا (PR جدا) پیشنهاد بده؛ بدون تأیید اعمال نکن. ### T1. تأیید پکیج‌ها و جست‌وجوی `GetTranscript` - **مراحل:** (دستورها در D1) 1. بررسی وجود/نسخه: `aiobale-py`, `aiobale`, `autobale`, `baleclient`, `bale-sdk`, `bbpb`. 2. دانلود sdist `bale-sdk` (بدون نصب در محیط اصلی) و جست‌وجوی `transcri|turing|speech|asr|stt` در `proto/` و `docs/`. 3. اگر پیدا شد: نام سرویس، متد، فیلدها، نوع پاسخ را **فقط برای شناخت** ثبت کن (کپی نکن). 4. مجوز هر پکیج را از `METADATA`/`LICENSE` بخوان. - **خروجی:** `docs/findings/01_packages.md` با جدول (نام، نسخه، تاریخ آخرین انتشار، مجوز، وضعیت). - **پذیرش (G1 (بخش الف)):** وجود یا عدم وجود `GetTranscript` به‌صورت قطعی ثبت شد. - **اگر شکست (متد نبود):** به T4 برو و منتظر کپچر بمان؛ به انسان گزارش بده که ادعای هر دو گزارش تأیید نشد. ### T2. آزمون دقت (کیت ارزیابی) - **هدف:** تصمیم G0. **این مهم‌ترین وظیفه است و به بله وابسته نیست.** - **مراحل:** 1. با H3، پوشه `data/gold/` بساز: فایل صوتی، `ref.txt` (رونوشت انسانی)، `items.json` (کالا، تعداد، واحد). 2. `tools/eval/normalize.py` بنویس: یکسان‌سازی «ي/ی» و «ك/ک»، ارقام فارسی/عربی/لاتین، نیم‌فاصله، حذف علائم، تبدیل اعداد حرفی ساده. 3. `tools/eval/metrics.py`: CER (اصلی برای فارسی)، WER، و **F1 استخراج اقلام** (مقایسه جدول استخراج‌شده با `items.json`). 4. `tools/eval/run_eval.py`: ورودی = پوشه خروجی هر کاندید `candidates//.txt`؛ خروجی = جدول مقایسه + اجماع. 5. **مسیر دستی (فوری):** از انسان بخواه ۳۰ ویس را در اپ بله «تبدیل به متن» کند و متن‌ها را در `candidates/bale/` بگذارد. **مسیر خودکار:** پس از T4 و تأیید دکمه وب، خودت انجام بده. 6. کاندیدهای فعلی بات را روی همان ویس‌ها اجرا کن. - **خروجی:** `docs/findings/02_accuracy.md` با جدول و تصمیم G0. - **پذیرش (G0):** طبق A8. گزارش باید آمار موارد سخت (نام برند، عدد، واحد، لهجه) را هم داشته باشد. - **اگر شکست:** گزارش بده و **T4..T8, T11 را اجرا نکن.** ### T3. پروب Bot API - **هدف:** مطمئن شدن که فیلد مخفی در آپدیت صوتی نیست. - **مراحل:** با یک بات تستی، یک ویس بفرست و JSON خام کامل `getUpdates`/Webhook را ذخیره کن (بدون توکن). همه کلیدها را فهرست کن. بررسی کن `voice` و `message` فیلدی مثل `transcript`/`text`/`caption` دارد؟ - **پذیرش:** فهرست کلیدها ثبت شد. (انتظار: فقط `file_id`, `file_unique_id`، چون مستندات رسمی همین را می‌گویند.) - **اگر فیلد پیدا شد:** پروژه کرالر لازم نیست؛ گزارش فوری. ### T4. بررسی وب بله و کپچر ترافیک - **پیش‌نیاز:** H1 (تأیید ToS)، H2، H4. بدون این‌ها شروع نکن. - **مراحل:** 1. با Playwright (`launch_persistent_context`، پروفایل روی ولوم) `web.bale.ai` را باز کن؛ ورود با شماره H2؛ **وقتی کد پیامکی خواست، متوقف شو و از انسان بخواه** (B9.2). 2. در یک گفتگوی آزمایشی (گفتگو با بات رله یا Saved Messages)، یک ویس بفرست/دریافت کن و ببین دکمه «تبدیل به متن» هست یا نه. عکس صفحه بگیر. 3. اگر دکمه هست: با `capture_ws.py` (D2) تمام فریم‌های WebSocket و درخواست‌های XHR/fetch را هنگام کلیک ذخیره کن. توکن‌ها را redact کن. 4. زمان واقعی از کلیک تا نمایش متن را اندازه بگیر. 5. **تفاوت ویس ارسالی و دریافتی** را ثبت کن (دکمه روی هر دو هست؟). - **خروجی:** `docs/findings/04_web.md` + `captures/*.bin` (redact شده). - **پذیرش (G1):** «دکمه هست/نیست» با عکس و کپچر مستند شد. - **اگر دکمه نیست و RPC در T1 هم نبود:** E1 و E2 هر دو ممکن نیستند ⇒ گزارش به انسان؛ فقط E3 باقی می‌ماند (نیاز به تصمیم صریح). ### T5. رمزگشایی و ساخت پروتو (clean-room) - **مراحل:** 1. فریم‌ها را با `decode_frames.py` بخوان: تشخیص قالب فریم (در POST gRPC-Web پیشوند ۵ بایتی: ۱ بایت فلگ + ۴ بایت طول؛ **در WebSocket بله قالب را از داده بخوان، فرض نکن**). 2. با `protoc --decode_raw` یا `bbpb`/`protoscope` ساختار را ببین؛ فیلدهای `voice(Media)`، `outPeer`، `messageId` و پاسخ `mustWait`/`downloadSource` را بازسازی کن. 3. با کمک چند کپچر مختلف (ویس کوتاه/بلند/دریافتی) نوع فیلدها را تأیید کن. 4. `GetTranscript.proto` خودت را بنویس (نام فیلدها = توصیفی، نه کپی). 5. مشخص کن متن پاسخ مستقیم است یا لینک دانلود (`DownloadSource`) و راه خواندنش چیست. - **خروجی:** `proto/own/gettranscript.proto` + `docs/findings/05_schema.md`. - **پذیرش (G2):** با ارسال همان درخواست بازسازی‌شده از پایتون (توکن معتبر)، پاسخ مشابه کپچر گرفته شد. - **اگر شکست:** اگر ترافیک امضا/ناشناس‌سازی مرورگر لازم دارد ⇒ سوییچ به E2. ### T6. پروتوتایپ E1 با بات رله - **پیش‌نیاز:** T5، H5. - **مراحل:** 1. یکی از کتابخانه‌های MIT را (نسخه pin‌شده) برای انتقال و نشست انتخاب کن؛ اگر کار نکرد، فورک بعدی را امتحان کن (`autobale` ← `BaleClient` ← `aiobale`). 2. حساب کرالر `/start` بزند؛ هویت (`user_id`) را ثبت کن. 3. adapter: صوت → OGG/Opus (ffmpeg) → `sendVoice(caption=nonce)` به کرالر. 4. کرالر: گوش‌دادن به پیام جدید از بات رله با همان nonce ⇒ `GetTranscript` ⇒ (حلقه `mustWait` با فاصله ~۱٫۵ ثانیه و سقف زمانی) ⇒ دریافت متن ⇒ حذف پیام. 5. ۱۰ ویس واقعی را اجرا کن؛ تأخیر هر مرحله را ثبت کن. - **پذیرش (G3):** ۱ ویس ⇒ ۱ متن فارسی معتبر؛ ۱۰ از ۱۰ یا دلیل شکست‌ها مستند. - **اگر شکست:** (الف) اگر ویس بات به‌صورت فایل نمایش داده شد ⇒ قالب صوت را عوض کن؛ (ب) اگر RPC روی پیام دریافتی از بات کار نکرد ⇒ R3 (آپلود از کرالر)؛ (ج) در غیر این صورت E2. ### T7. موتور فالبک Playwright (اختیاری) - **مراحل:** با همان پروفایل ورود، ویس را از طریق همان جریان رله بفرست، روی ویس دریافتی دکمه را کلیک کن، با `MutationObserver` یا `expect(...).to_be_visible` منتظر متن بمان، متن را بخوان، پیام را حذف کن. سلکتورها را در یک فایل جدا و متمرکز نگه دار. رم کانتینر را اندازه بگیر. - **پذیرش:** ۱۰ از ۱۰ ویس؛ رم ≤ ۸۰۰ مگابایت؛ تأخیر مستند. - **نکته:** سلکتورها با هر آپدیت UI می‌شکنند؛ تست طلایی شبانه بنویس. ### T8. adapter سخت‌شده + تست‌ها - **مراحل:** کد مرجع B8 را پیاده کن؛ `stub` engine برای تست بدون بله؛ تست‌ها: 1. بدون/اشتباه بودن کلید ⇒ ۴۰۱. 2. فایل خالی/بزرگ ⇒ ۴۰۰. 3. رسیدن به سقف روزانه ⇒ ۴۲۹؛ و **پایداری شمارنده بعد از ری‌استارت** (و ریست روز بعد). 4. خطای موتور ⇒ ۵۰۳؛ متن خالی ⇒ ۵۰۳. 5. تایم‌اوت ⇒ ۵۰۳. 6. اجرای هم‌زمان ۵ درخواست ⇒ ترتیبی (قفل). 7. `/healthz`. - **پذیرش:** همه تست‌ها سبز؛ بدون مقدار پیش‌فرض برای کلید. ### T9. پرووایدرهای شخص ثالث و مدل خودمیزبان (موازی، مستقل از بله) - **پیش‌نیاز:** H3، H7، H8. - **مراحل:** 1. برای هر کاندید یک adapter OpenAI-compatible یا پرووایدر مستقیم: Avanegar/Partai، Aipa، فارس‌آوا (عامراندیش)، IOType، Google Chirp 2، (در صورت دسترسی) Azure/OpenAI/Groq Whisper. 2. **Faster-Whisper:** سرور OpenAI-compatible داخلی؛ مدل `large-v3` (و در صورت تمایل یک فاینتیون فارسی مثل `vhdm/whisper-v3-turbo-persian-v1.1`)؛ با `initial_prompt`/hotwords از واژگان تدارکات و **VAD**. حتماً ببین هالوسینیشن در سکوت/نویز رخ نمی‌دهد. 3. همه را با کیت T2 روی مجموعه طلایی بسنج؛ هزینه به‌ازای ویس را حساب کن. 4. مدل‌های ضعیف‌تر (Vosk، NeMo FA، SLPL wav2vec2) فقط اگر ارزش افزوده اجماع دارند نگه دار. - **خروجی:** `docs/findings/09_providers.md` با جدول دقت/تأخیر/هزینه و پیشنهاد ترکیب. - **پذیرش:** حداقل ۲ کاندید غیر بله با دقت مستند آماده اتصال. ### T10. تقویت اجماع - **مراحل:** 1. **نرمال‌سازی** (همان `normalize.py`) پیش از اجماع. 2. **کاتالوگ:** فهرست کالا/برند/واحد دیتابیس تدارکات را (خلاصه‌شده) به prompt اجماع‌گیر بده؛ خروجی ساختاریافته (نام، برند، تعداد، واحد). 3. **قاعده اختلاف:** اگر کاندیدها روی **عدد یا واحد** اختلاف دارند، خروجی را «نیازمند تأیید کاربر» علامت بزن. 4. اندازه‌گیری روی مجموعه طلایی: اجماع قبل/بعد از هر تغییر. - **پذیرش (G5):** بهبود F1 اقلام مستند؛ بدون افزایش خطای فاحش. ### T11. اتصال به بات، E2E، rollback - **مراحل:** 1. `docker-compose.stt.yml` را بالا بیاور. 2. `.env` بات: متغیرهای `PROCUREMENT_BOT_STT_PROVIDER_3_*` (B8.7). کلید باید با `BALE_STT_INTERNAL_KEY` یکی باشد و **نه مقدار نمونه**. 3. E2E: یک ویس واقعی به بات اصلی بفرست؛ در لاگ حضور کاندید `bale` در اجماع را تأیید کن. 4. **تست خطا:** adapter را خاموش کن ⇒ بات بی‌وقفه کار کند. adapter را وادار به ۵۰۳ کن ⇒ همین. 5. **تست rollback:** خالی‌کردن env ⇒ بات به حالت قبل برگردد. - **پذیرش (G4 مقدماتی):** هر ۴ سناریو موفق. - **قبل از مرج:** تأیید انسان (H11). ### T12. سواک و مانیتورینگ - **مراحل:** ۷ روز با حجم کم؛ ثبت روزانه: تعداد، موفقیت، p50/p95، انواع خطا، وضعیت نشست، تفاوت با سایر کاندیدها؛ هشدار اگر نرخ خطا در ۳۰ دقیقه > ۲۰٪ یا نشست باطل شد (به انسان اعلان بده). - **پذیرش (G7):** یک هفته بدون ابطال نشست و با G4 برقرار. ### T13. جمع‌آوری داده و فاین‌تیون (اختیاری) - **مراحل:** ذخیره (با رضایت) ویس + متن نهایی تأییدشده؛ ساخت دادگان؛ LoRA روی Whisper؛ ارزیابی روی مجموعه طلایی جدا؛ جایگزینی تدریجی وابستگی به بله. - **پذیرش:** مدل داخلی روی مجموعه طلایی از Faster-Whisper خام بهتر است. --- ## B7. قواعد تصمیم برای ایجنت (جدول if-then) | شرایط | اقدام | |---|---| | T3 فیلد ترنسکریپشن پیدا کرد | کرالر لازم نیست؛ فوراً گزارش بده | | G0 رد شد | T4..T8, T11 را اجرا نکن؛ T9، T10، T13 را ادامه بده | | G0 «کمی بهتر» (۵ تا ۱۵٪) | فقط E1 سبک؛ Playwright و امولاتور را رها کن | | G0 «چشمگیر» (> ۱۵٪) | E1 + فالبک E2 + پیگیری مسیر رسمی | | T1: `GetTranscript` پیدا شد | T5 را با تأکید بر تأیید با کپچر خودت پیش ببر | | T1: پیدا نشد و T4: دکمه هست | E2؛ در موازی با کپچر دنبال RPC بگرد | | T1 و T4 هر دو منفی | به انسان گزارش بده؛ E3 فقط با تصمیم صریح | | پاسخ `GetTranscript` لینک دانلود است | حلقه دریافت و خواندن لینک را پیاده کن؛ زمان‌سنجی کن | | ویس بات به‌صورت فایل نمایش داده شد | قالب OGG/Opus و `sendVoice` را بازبینی کن؛ در نهایت R3 | | خطای ۴۲۹/چالش/ابطال نشست | همه درخواست‌ها را متوقف کن، به انسان خبر بده، ترافیک را به بقیه بسپار | | بیش از ۲۰٪ خطا روی کاندید بله | از اجماع خودکار حذفش کن (۵۰۳) و بررسی کن | | هالوسینیشن Whisper | VAD و `initial_prompt` را بررسی؛ در اجماع وزن کمتر | | کاندیدها روی عدد/واحد اختلاف دارند | «نیازمند تأیید کاربر» | --- ## B8. کد و پیکربندی مرجع > **توجه:** این‌ها نقطه شروع‌اند، نه کد نهایی. بخش «موتور RPC» عمداً شبه‌کد است؛ نام متدها را از کتابخانه واقعی بخوان. ### B8.1 `adapter/counters.py` (شمارنده روزانه پایدار) ```python import os import sqlite3 from contextlib import closing from datetime import date DB_PATH = os.getenv("BALE_STT_DB", "/app/state/usage.db") def _conn() -> sqlite3.Connection: os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) con = sqlite3.connect(DB_PATH, timeout=5) con.execute("CREATE TABLE IF NOT EXISTS usage (day TEXT PRIMARY KEY, n INTEGER NOT NULL)") return con def used_today() -> int: with closing(_conn()) as con: row = con.execute("SELECT n FROM usage WHERE day = ?", (date.today().isoformat(),)).fetchone() return row[0] if row else 0 def bump() -> None: with closing(_conn()) as con, con: con.execute( "INSERT INTO usage(day, n) VALUES (?, 1) " "ON CONFLICT(day) DO UPDATE SET n = n + 1", (date.today().isoformat(),), ) ``` ### B8.2 `adapter/engines/base.py` و `__init__.py` ```python # engines/base.py from typing import Protocol class Engine(Protocol): name: str async def transcribe(self, audio: bytes, filename: str) -> str: """متن فارسی را برگرداند؛ در هر مشکل Exception بدهد.""" ``` ```python # engines/__init__.py import os def load_engine(): name = os.environ["BALE_STT_ENGINE"] # الزامی؛ بدون پیش‌فرض if name == "stub": if os.getenv("BALE_STT_ALLOW_STUB") != "1": raise RuntimeError("stub engine فقط برای تست است") from .stub import StubEngine return StubEngine() if name == "rpc": from .rpc import RpcEngine return RpcEngine() if name == "playwright": from .playwright_engine import PlaywrightEngine return PlaywrightEngine() raise RuntimeError(f"unknown engine: {name}") ``` ### B8.3 `adapter/main.py` (قرارداد OpenAI، سخت‌شده) ```python import asyncio import logging import os import random import secrets from fastapi import FastAPI, File, Form, Header, HTTPException, UploadFile from counters import bump, used_today from engines import load_engine log = logging.getLogger("bale-stt") logging.basicConfig(level=logging.INFO) KEY = os.environ["BALE_STT_INTERNAL_KEY"] # الزامی، بدون پیش‌فرض DAILY_CAP = int(os.getenv("BALE_STT_DAILY_CAP", "150")) TIMEOUT = float(os.getenv("BALE_STT_TIMEOUT", "25")) # کمتر از timeout بات (مثلاً 30) MIN_GAP = float(os.getenv("BALE_STT_MIN_GAP", "1.0")) MAX_GAP = float(os.getenv("BALE_STT_MAX_GAP", "2.5")) MAX_BYTES = int(os.getenv("BALE_STT_MAX_BYTES", str(10 * 1024 * 1024))) app = FastAPI(title="Bale STT adapter") engine = load_engine() lock = asyncio.Lock() # اجرای ترتیبی روی حساب بله @app.get("/healthz") async def healthz(): return {"status": "ok", "engine": engine.name, "used_today": used_today(), "cap": DAILY_CAP} @app.post("/v1/audio/transcriptions") async def transcribe( file: UploadFile = File(...), model: str = Form("bale-stt"), language: str = Form("fa"), authorization: str = Header(""), ): if not secrets.compare_digest(authorization.encode(), f"Bearer {KEY}".encode()): raise HTTPException(status_code=401, detail="Unauthorized") audio = await file.read() if not audio or len(audio) > MAX_BYTES: raise HTTPException(status_code=400, detail="Bad audio size") if used_today() >= DAILY_CAP: raise HTTPException(status_code=429, detail="Daily cap reached") async with lock: try: await asyncio.sleep(random.uniform(MIN_GAP, MAX_GAP)) text = await asyncio.wait_for( engine.transcribe(audio, file.filename or "voice.mp3"), timeout=TIMEOUT ) except Exception: log.exception("engine failed") raise HTTPException(status_code=503, detail="Bale STT unavailable") text = (text or "").strip() if not text: raise HTTPException(status_code=503, detail="Empty transcript") bump() log.info("ok chars=%d", len(text)) # متن کامل را لاگ نکن return {"text": text} ``` ### B8.4 `adapter/engines/rpc.py` (**شبه‌کد**، منطق مورد نیاز) ```python class RpcEngine: name = "rpc" async def transcribe(self, audio: bytes, filename: str) -> str: nonce = new_nonce() ogg = await to_ogg_opus(audio) # ffmpeg await relay_bot.send_voice(chat_id=CRAWLER_USER_ID, voice=ogg, caption=nonce) msg = await crawler.wait_message(from_bot=RELAY_BOT_ID, caption=nonce, timeout=10) resp = await crawler.get_transcript( # نام و فیلدها: از T5 voice=msg.media, out_peer=msg.peer, message_id=msg.id ) deadline = loop_time() + 15 while resp.must_wait and loop_time() < deadline: await asyncio.sleep(1.5) resp = await crawler.get_transcript(voice=msg.media, out_peer=msg.peer, message_id=msg.id) if resp.must_wait: raise TimeoutError("transcript not ready") text = await read_download_source(resp.download_source) # اگر پاسخ لینک است await crawler.delete_messages(msg.peer, [msg.id]) return text ``` ### B8.5 `adapter/Dockerfile` ```dockerfile FROM python:3.11-slim WORKDIR /app ENV TZ=Asia/Tehran PYTHONUNBUFFERED=1 RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8080 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"] ``` > برای موتور Playwright از ایمیج رسمی Playwright پایتون با **نسخه pin‌شده** استفاده کن و `ffmpeg` را اضافه کن. ### B8.6 `docker-compose.stt.yml` ```yaml services: bale-stt: build: context: ./adapter container_name: bale-stt-adapter restart: unless-stopped environment: TZ: Asia/Tehran BALE_STT_ENGINE: ${BALE_STT_ENGINE} BALE_STT_INTERNAL_KEY: ${BALE_STT_INTERNAL_KEY} BALE_STT_DAILY_CAP: ${BALE_STT_DAILY_CAP:-150} BALE_RELAY_BOT_TOKEN: ${BALE_RELAY_BOT_TOKEN} BALE_CRAWLER_USER_ID: ${BALE_CRAWLER_USER_ID} volumes: - bale_session:/app/session - bale_state:/app/state networks: [bot_network] healthcheck: test: ["CMD", "python", "-c", "import urllib.request;urllib.request.urlopen('http://localhost:8080/healthz')"] interval: 30s timeout: 5s retries: 3 volumes: bale_session: bale_state: networks: bot_network: external: true # فرض: همان شبکه بات؛ در T0 تأیید شود ``` ### B8.7 `.env` بات (بدون تغییر کد) ```env PROCUREMENT_BOT_STT_PROVIDER_3_NAME=bale PROCUREMENT_BOT_STT_PROVIDER_3_BASE_URL=http://bale-stt:8080/v1 PROCUREMENT_BOT_STT_PROVIDER_3_API_KEY=<همان BALE_STT_INTERNAL_KEY؛ مقدار واقعی را فقط انسان/سکرت‌منیجر بگذارد> PROCUREMENT_BOT_STT_PROVIDER_3_MODEL=bale-stt PROCUREMENT_BOT_STT_PROVIDER_3_LANGUAGE=fa PROCUREMENT_BOT_STT_PROVIDER_3_TIMEOUT=30 ``` > شماره پرووایدر (`3`) را با وضعیت واقعی بات (T0) تطبیق بده. پرووایدر وقتی فعال است که `BASE_URL` و `API_KEY` پر باشند (طبق گزارش‌ها؛ در T0 تأیید شود). --- ## B9. قالب‌های گزارش‌دهی ### B9.1 گزارش دروازه (برای انسان) ```markdown ## گزارش دروازه Gx — <تاریخ> **نتیجه:** عبور / رد / نیازمند تصمیم شما **آنچه اجرا شد:** (وظایف، دستورها، فایل‌های خروجی) **یافته‌ها:** (جدول؛ هر ردیف با ✅/🟡/❌ و مدرک) **اعداد:** (CER/WER/F1/تأخیر/موفقیت/هزینه) **انحراف از سند:** (چه چیزی با فرض‌های سند نخواند) **ریسک‌های تازه:** **پیشنهاد گام بعد:** **چیزی که از شما لازم دارم:** (H#) ``` ### B9.2 درخواست از انسان (کوتاه، فوری، قابل‌اقدام) ```markdown ⏸️ **نیاز به اقدام شما — H#: <عنوان>** **چرا:** <یک جمله> **دقیقاً چه کنید:** <۱–۳ گام> **چه چیزی را پس می‌دهید:** <فقط همان مقدار لازم، مثلاً کد ۶ رقمی> **تا آن موقع من:** <کاری که موازی انجام می‌دهم> ``` --- ## B10. عیب‌یابی | نشانه | علت محتمل | اقدام | |---|---|---| | ۵۰۳ پیاپی از adapter | نشست باطل / تغییر پروتکل / دکمه وب حذف شد | لاگ را بخوان؛ `/healthz`؛ اگر نشست باطل است H4 را درخواست کن؛ ترافیک به بقیه | | ویس بات به‌صورت فایل نمایش داده می‌شود | قالب یا متد ارسال | `sendVoice` با OGG/Opus؛ حجم ≤ ۱MB برای ویس؛ در نهایت R3 | | ترنسکریپت برای پیام بات کار نمی‌کند | محدودیت سمت سرور | R3 یا E2 | | `mustWait` همیشه true | استنتاج کند یا RPC وضعیت دیگری دارد | زمان‌سنجی؛ افزایش سقف تا ۲۰ ثانیه؛ مقایسه با کپچر وب | | پاسخ خالی | ویس بی‌کلام/نویز | VAD قبل از ارسال؛ ۵۰۳ برای متن خالی (اینجا درست است) | | `compare_digest` خطا می‌دهد | رشته غیر ASCII | همه را `.encode()` کن | | شمارنده روزانه ریست نمی‌شود | منطقه زمانی کانتینر | `TZ=Asia/Tehran` | | Playwright روی سرور بالا نمی‌آید | وابستگی‌های مرورگر | از ایمیج رسمی Playwright همان نسخه | | بن/محدودیت | نرخ زیاد | توقف کامل؛ کاهش سقف؛ فاصله بیشتر؛ گزارش به انسان | | اجماع به‌خاطر بله بدتر شد | بله روی نوع ویس شما ضعیف است | وزن کمتر یا حذف کاندید (G5) | ## B11. تعریف «انجام شد» (DoD) - [ ] T0 تا T3 مستند شده و ادعاهای 🟡 این سند ✅ یا ❌ شده‌اند. - [ ] G0 با اعداد واقعی گزارش شد (حتی اگر پروژه متوقف شود). - [ ] اگر ادامه یافت: adapter با تست‌های T8 سبز است و فقط با `.env` به بات وصل می‌شود. - [ ] kill switch (خالی‌کردن env) و rollback تست شده. - [ ] حداقل ۲ کاندید غیر بله فعال و ارزیابی شده‌اند. - [ ] اجماع با نرمال‌سازی و کاتالوگ بهبود یافته و اندازه‌گیری شده است. - [ ] سواک ۷ روزه گزارش شد؛ هشدارها فعال‌اند. - [ ] مستندات: `docs/findings/*`، دستور اجرا، runbook ورود مجدد (OTP)، مالک نگهداری (H10). - [ ] هیچ سکرت یا کپچر redact‌نشده‌ای در مخزن نیست. --- # بخش C: دانش‌پایه (همه راه‌ها، شواهد و نظرات من) > این بخش مرجع است؛ برای اجرا به بخش B برگردید. هر جا برچسب ✅/🟡/❌ هست، همان تعریف سر سند را دارد. ## C1. راستی‌آزمایی ادعاهای گزارش A | ادعا | وضعیت | یافته من | |---|---|---| | Bot API رسمی STT ندارد | ✅ | مستندات رسمی `docs.bale.ai`: شیء `Voice` فقط `file_id` و `file_unique_id` دارد و هیچ متدی برای ترنسکریپت نیست. **اصلاح:** گزارش می‌گوید «حجم و مدت‌زمان» هم هست؛ در مستندات رسمی نیست (SDKهای شخص ثالث فیلد `duration` را اضافه کرده‌اند). | | `bale-sdk` از `mtp1376`، نسخه 0.1.1، ۶۰۷ RPC در ۵۲ سرویس، پوشه `proto/` | ✅ | صفحه PyPI (27 Jun 2026) دقیقاً همین را می‌گوید. | | `bale-sdk` مجوز اختصاصی «All Rights Reserved» دارد | ✅ | تأیید شد. خود README هم می‌گوید استفاده فقط با حساب‌ها و داده‌هایی که مجاز هستید و مطابق ToS بله باشد. | | سرویس `bale.turing.v1.AI` و متد `GetTranscript` با فیلدهای ۱ تا ۳ | 🟡 | **نتوانستم از منبع عمومی تأیید کنم.** فایل‌های `docs/API_COVERAGE.md` و `docs/API_FA.md` در صفحه PyPI لینک شده‌اند ولی برای من باز نشد. این مهم‌ترین ادعای هر دو گزارش است و باید در ۳۰ دقیقه اول خودتان تأیید شود (وظیفه T1). | | `aiobale-py` توسط امین مدنی، نسخه 0.3.x، متکی بر `blackboxprotobuf` | 🟡 | `aiobale-py` را پیدا نکردم. آنچه پیدا کردم: `aiobale` (Enalite، MIT، آخرین نسخه 0.1.5 در Aug 2025). توضیح خودش می‌گوید «از صفر و بدون فایل proto ساخته شده». | | `aiobale` اولیه رها شده | ✅ | آخرین انتشار Aug 2025؛ فورک‌ها (`autobale`، `BaleClient`) می‌گویند نگهداری نمی‌شود و باگ دارد. | | `autobale` توسط علی عمادی، نسخه 1.0.0، MIT | ✅ | تأیید شد (مخزن `AliEmadii/autobale`). | | `BaleClient` | — | در گزارش نیست، ولی وجود دارد: فورک دیگری از aiobale (`Foad-KhAb/baleclient`، Python 3.11+). | | `bale-mcp-server` روی `aiobale-py` | 🟡/❌ | پروژه‌ای به نام `bale-mcp` پیدا کردم که **با توکن بات رسمی** و `tapi.bale.ai` کار می‌کند، نه کلاینت کاربری. شاید پروژه دیگری باشد. | | `bale-chat-exporter` با نشست کلاینت | 🟡 | اسکرپر Playwright + BeautifulSoup برای Bale Web پیدا کردم (خروجی اکسل). هم‌پروژه‌بودن با آنچه گزارش می‌گوید تأییدنشده است، ولی **الگوی Playwright روی وب بله اثبات‌شده است.** | | `Bale-Self-Bot` در GitLab چابکان | 🟡 | آن را پیدا نکردم. ولی `baleself` (PyPI، `zellias/bale-self-bot`) یک سلف‌بات مبتنی بر **Selenium** است و الگو را تأیید می‌کند. | | MajidAPI یک پراکسی روی موتورهای عمومی است | 🟡 | endpoint را در منابع قابل جست‌وجو پیدا نکردم. «پراکسی روی موتور عمومی» استنباط گزارش است، نه چیزی که ثابت شده باشد. هرچه باشد، SLA ندارد. | | فارس‌آوا (عامراندیش): ۱۰ هزار ساعت داده، نصب On-Premise، ساعتی ۴۵–۶۰ هزار تومان | 🟡 | قیمت و جزئیات را مستقل تأیید نکردم؛ قیمت‌ها زمان‌دارند. قبل از تصمیم، استعلام رسمی بگیرید. | | آی‌او تایپ دقت «بالای ۹۸٪» | 🟡 | ادعای بازاریابی است. روی ویس تدارکات خودتان اندازه بگیرید. | | `hezarai/whisper-small-fa` روی CPU زیر ۲ ثانیه | 🟡 | محتمل برای ویس کوتاه، ولی مدل small روی فارسی محاوره‌ای ضعیف است (C5). | | `nezamisafa/whisper-v3-turbo-persian-v1.0` با WER ‏۲۹.۹٪ | 🟡 | این مدل را نتوانستم باز کنم. مدل مشابه `vhdm/whisper-v3-turbo-persian-v1.1` ‏WER ‏14.07٪ می‌دهد ولی روی **دادگان خودش**؛ اعداد WER بین دادگان‌های مختلف قابل مقایسه نیستند. | | Faster-Whisper تا ۴ برابر سریع‌تر | ✅ (تا حد دانسته‌های من) | ادعای رسمی خود پروژه است. در مقاله PSRB هم Faster-Whisper هالوسینیشن کمتری از Whisper خام داشت. | | «مدل‌های متن‌باز پایدارترین راهکار فنی‌اند» | ❌ | با داده PSRB سازگار نیست: ASR تجاری فارسی و Chirp 2 به‌طور محسوس دقیق‌ترند (C5.2). متن‌باز «مستقل‌ترین» است، نه لزوماً «دقیق‌ترین». | ## C2. آیا دیگران هم همین مشکل را داشته‌اند؟ و چطور حل کرده‌اند؟ ### C2.1 نتیجه کلی **پروژه عمومیِ مشخصی که خروجی STT خود بله را برای یک بات استخراج کند پیدا نکردم** (گزارش A هم همین را می‌گوید). آنچه وجود دارد الگوهای مجاور است: | الگو | نمونه‌های تأییدشده | درسی که برای ما دارد | |---|---|---| | **A. بات رسمی + ASR مستقل** | بات‌های بله روی Webhook (مثلاً `hamedcode/bale-ai-bot` روی Cloudflare Workers)، بات‌های مشتری‌مداری با LLM | رایج‌ترین راه. بله فقط کانال پیام است؛ صوت دانلود و به موتور دیگر داده می‌شود. | | **B. کلاینت کاربری مهندسی‌معکوس‌شده** | `aiobale` (MIT)، فورک‌های `autobale` و `BaleClient`، `bale-sdk` (اختصاصی، ۶۰۷ RPC) | نشان می‌دهد نگهداری نشست کاربری و تماس با RPCهای داخلی شدنی است، ولی پروژه‌ها تازه‌اند و بعضی رها می‌شوند. | | **C. اتوماسیون مرورگر** | `baleself` (Selenium)، اسکرپر چت Bale Web با Playwright | روی وب بله جاافتاده است، ولی معمولاً برای کارهای ساده مثل پاسخ خودکار و بک‌آپ. | | **D. سوءاستفاده خلاقانه از زیرساخت** | `BaleVPN` (تونل روی تماس صوتی بله) | نشان می‌دهد جامعه تا کجا از لایه‌های داخلی استفاده می‌کند؛ برای STT به کار ما نمی‌آید. | | **E. ابزارهای مدیریتی روی Bot API** | `bale-mcp`، SDKهای PHP/Node/Python/Swift/C# | فقط Bot API؛ بدون هیچ راهی برای رسیدن به ترنسکریپت. | ### C2.2 برداشت از همین شواهد 1. **راه مشترک جامعه این است که STT را بیرون از بله انجام می‌دهند.** هیچ‌کس STT بله را «کرال» نکرده یا اگر کرده منتشر نشده است. پس در هر حال ریسک بالاتر و شواهد کمتری برای مسیر شماست. 2. **زیرساخت لازم برای کرالر وجود دارد** (انتقال WebSocket، OTP، نشست، ارسال و دانلود فایل)؛ پس این پروژه «ساخت از صفر» نیست. 3. **پایداری کتابخانه‌ها ضعیف است** (aiobale رها شد، `bale-sdk` نسخه 0.1.1 است). هر معماری باید فرض کند اجزای متن‌باز ممکن است از کار بیفتند. 4. **تنها چیزی که کل شرط‌بندی را توجیه می‌کند، دقت بله روی ویس‌های شماست.** بدون آزمون دستی (وظیفه T2) نباید ساخت شروع شود. --- ## C3. کاتالوگ کامل راهکارها برای هر راهکار: چه کار می‌کند، مزیت، عیب، تلاش (تخمین من)، وضعیت تأیید. ### گروه ۱: استفاده از STT خود بله | # | راهکار | توضیح | مزایا | معایب | تلاش | وضعیت | |---|---|---|---|---|---|---| | **S1** | **مسیر رسمی/تجاری با بله** | ایمیل یا تماس با تیم توسعه‌دهندگان/بیزینس بله برای API رسمی STT یا قرارداد | پایدار، بدون ریسک بن | احتمال موافقت کم؛ زمان‌بر | ۱ ایمیل | هیچ مستند عمومی‌ای برای وجود چنین API نیست | | **S2** | **RPC مستقیم با `bale-sdk`** | استفاده از پروتوها و کلاینت آماده، فراخوانی `GetTranscript` | سریع‌ترین مسیر فنی؛ پروتو آماده | **مجوز اختصاصی**؛ نسخه 0.1.1؛ ادعای RPC تأییدنشده | ۱–۲ روز | 🟡 | | **S3** | **RPC مستقیم با `aiobale` / `autobale` / `BaleClient` (MIT) + پروتوی خودساخته** | انتقال و نشست و آپلود از کتابخانه MIT؛ تعریف `GetTranscript` را از کپچر ترافیک خودتان در `web.bale.ai` بسازید | بدون مشکل مجوز؛ قابل‌نگهداری | کشف RPC و تعریف پروتو کار می‌برد؛ کتابخانه‌ها کم‌نگهداری‌اند | ۳–۵ روز | ✅ ابزارها؛ 🟡 RPC | | **S4** | **Playwright روی `web.bale.ai`** | کرومیوم headless با پروفایل پایدار؛ ارسال ویس، کلیک دکمه، خواندن DOM | مقاوم در برابر تغییر پروتکل؛ الگوی اثبات‌شده | ۳۵۰–۵۰۰MB رم؛ شکننده در برابر تغییر UI؛ وابسته به وجود دکمه در وب | ۱–۲ روز | ✅ الگو؛ 🟡 دکمه وب | | **S5** | **ترکیب «مرورگر برای ورود، پروتکل برای کار»** | Playwright فقط ورود/تمدید توکن (JWT) را انجام می‌دهد؛ کار اصلی را کلاینت WebSocket سبک می‌کند | از سبک‌بودن S3 و پایداری ورود S4 بهره می‌برد | پیچیدگی دو لایه | ۴–۶ روز | پیشنهاد من | | **S6** | **شبیه‌ساز اندروید + uiautomator** | اپ بله در شبیه‌ساز، خواندن متن از رابط | مستقل از وب | رم >2GB؛ شکننده؛ سخت‌ترین نگهداری | ۵+ روز | آخرین چاره | | **S7** | **چت هوش مصنوعی بله (Oracle)** | ارسال ویس به بات هوش مصنوعی بله و گرفتن پاسخ | بدون کشف RPC | خروجی «متن + تفسیر»؛ تأخیر ۵–۱۵ ثانیه؛ اینکه ویس را پیاده می‌کند هم تأییدنشده | ۱ روز | 🟡 | ### گروه ۲: سرویس‌های شخص ثالث | # | راهکار | توضیح | مزایا | معایب | تلاش | وضعیت | |---|---|---|---|---|---|---| | **T1** | **پراکسی‌های غیررسمی (MajidAPI و مشابه)** | فراخوانی REST با لینک ویس | چند ساعت | بدون SLA؛ موتور نامشخص؛ ورودی URL (ویس باید لینک عمومی شود) | ساعتی | 🟡 | | **T2** | **ASR تجاری فارسی** | **Avanegar/Partai** (API: `api.ivira.ai/partai/speechRecognition`)، **Aipa**، عامراندیش (فارس‌آوا)، IOType | دقت بالا روی فارسی؛ پشتیبانی؛ گاهی On-Premise | هزینه ریالی؛ قرارداد | ۱ روز (adapter) | ✅ Avanegar/Aipa در بنچمارک؛ 🟡 قیمت‌ها | | **T3** | **ASR ابری بین‌المللی** | Google **Chirp 2**، Azure Speech، OpenAI Whisper API، Groq Whisper | Chirp 2 تقریباً هم‌سطح بهترین تجاری فارسی | هزینه ارزی؛ محدودیت دسترسی؛ بسته‌به شرایط شبکه | ۱ روز | ✅ Chirp/Azure در بنچمارک | ### گروه ۳: مدل‌های متن‌باز خودمیزبان | # | راهکار | توضیح | مزایا | معایب | تلاش | وضعیت | |---|---|---|---|---|---|---| | **O1** | **Faster-Whisper large-v3 (+ `initial_prompt`/hotwords واژگان تدارکات)** | سرور OpenAI-compatible داخلی | مستقل؛ قابل‌شخصی‌سازی با واژگان؛ بهترین متن‌باز در بنچمارک | WER ≈ 34٪ روی فارسی واقعی؛ نیاز به GPU برای سرعت | ۱ روز | ✅ | | **O2** | **Whisper large-v3-turbo فاین‌تیون فارسی** (مثل `vhdm/whisper-v3-turbo-persian-v1.1`) | مدل فاین‌تیون‌شده روی دادگان فارسی | سریع‌تر؛ احتمالاً دقیق‌تر روی گفتار تمیز | ارقام خودگزارشی و روی دادگان متفاوت؛ باید روی داده شما سنجید | ۱ روز | 🟡 | | **O3** | **NeMo FastConformer فارسی (NVIDIA)** | `stt_fa_fastconformer_hybrid_large` | مدل مستقل از Whisper؛ دیکودر CTC | در بنچمارک WER ≈ 45٪ (ضعیف‌تر از Whisper) | ۲ روز | ✅ | | **O4** | **Vosk فارسی (`vosk-model-fa-0.5`)** | Kaldi/DNN-HMM سبک، CPU | بسیار سبک؛ آفلاین | WER ≈ 45٪ | ساعتی | ✅ | | **O5** | **SLPL wav2vec2 فارسی (دانشگاه شریف)** | XLS-R فاین‌تیون‌شده | مناسب برای تحقیق و ensemble | WER ≈ 47٪ | ۲ روز | ✅ | | **O6** | **Seamless M4T v2** | مدل چندزبانه | — | احتمال بازنویسی/هالوسینیشن (Interpretation Error در مقاله PSRB) | ۲ روز | ✅ مشکل | ### گروه ۴: لایه‌های بالاتر (نه جایگزین، بلکه تقویت‌کننده) | # | راهکار | توضیح | |---|---|---| | **L1** | **LLM اجماع‌گیر با کاتالوگ** | کاندیدها + فهرست کالا/برند/واحد دیتابیس تدارکات را به LLM بدهید تا خروجی ساختاریافته و تصحیح‌شده بسازد. (مقاله PSRB هم می‌گوید LLMها خطاهای جزئی ASR را خوب اصلاح می‌کنند.) | | **L2** | **نرمال‌سازی فارسی** | یکسان‌سازی «ی/ک»، ارقام، نیم‌فاصله (ZWNJ) و اعداد حرفی. PSRB نشان می‌دهد مرزبندی کلمه و نیم‌فاصله WER را بالا می‌برد. | | **L3** | **فاین‌تیون روی داده خودتان** | ویس‌های واقعی + متن نهایی تأییدشده توسط کاربر → LoRA روی Whisper. بلندمدت‌ترین سرمایه‌گذاری برای استقلال از بله. | | **L4** | **مسیریابی بر اساس اعتماد** | اگر کاندیدها در اعداد/واحدها اختلاف دارند، از کاربر تأیید بگیرید (UX)، نه حدس بزنید. | --- ## C4. الگوبرداری از پروژه‌های متن‌باز | مؤلفه | منبع | مجوز | چه چیزی بردارید | ملاحظات | |---|---|---|---|---| | انتقال WebSocket، ورود با OTP، ذخیره نشست، Dispatcher | `aiobale` / `autobale` / `BaleClient` | MIT ✅ | همه چیز؛ حتی می‌توانید فورک کنید | `aiobale` رها شده؛ `autobale` فعال‌تر ادعا می‌شود. نسخه را قفل کنید | | ارسال/دانلود فایل و رسانه | همان کتابخانه‌ها (گزارش A: حدود ۸۰ متد) | MIT | آپلود و ساخت پیام صوتی | اگر معماری بات رله (B5.2) را بپذیرید، آپلود از سمت کرالر لازم نیست | | **تعریف RPC و پروتوها** | `bale-sdk` (`proto/`) | **اختصاصی ❌** | **فقط به‌عنوان مرجع و با اجازه نویسنده**؛ کپی مستقیم ممنوع | راه تمیز: از کپی خود کپچر `web.bale.ai` پروتو بسازید. ابزارها: `bbpb`/`blackboxprotobuf`، `protoscope`، `protoc --decode_raw` | | دیکود بدون `.proto` | `blackboxprotobuf` (NCC Group؛ پکیج رسمی PyPI با نام `bbpb`) | MIT | تحلیل payloadهای ناشناخته | wire format خودتوصیف نیست؛ نام فیلد را باید خودتان حدس بزنید | | الگوی DOM و صفحه‌خوانی بله | اسکرپر Playwright چت (GitHub) و `baleself` (Selenium) | بررسی مجوز هر مخزن | سلکتورها، انتظار برای بارگذاری، اسکرول | سلکتورها با هر آپدیت UI می‌شکنند | | سرور OpenAI-compatible برای Whisper | Faster-Whisper، پروژه‌های سرور مبتنی بر آن | معمولاً MIT/Apache | سرور داخلی با همان `/v1/audio/transcriptions` | همان قرارداد adapter بات؛ تغییر کد لازم نیست | | نرمال‌سازی و ارزیابی فارسی | مقاله و دادگان PSRB (`PartAI/PSRB` در Hugging Face)، `hazm` | بررسی | معیار SW-WER، فهرست خطاهای رایج فارسی | دادگان عمومی فقط زیرمجموعه است | ### قواعد پیشنهادی الگوبرداری 1. **تمیز‌کاری حقوقی:** منطق را از متن‌باز MIT بردارید و تعریف RPC را از کپچر خودتان تولید کنید (clean-room). 2. **لایه‌بندی:** adapter فقط قرارداد OpenAI را بشناسد؛ هیچ import از `bale*` بیرون از ماژول `driver/` نباشد. 3. **قفل نسخه و آینه:** نسخه دقیق کتابخانه‌ها را pin و کپی داخلی نگه دارید (رهاشدن مخزن، مثل `aiobale`، اتفاق افتاده است). 4. **تست قرارداد:** با یک «ویس طلایی» شبانه بررسی کنید پاسخ هنوز ساختار قبلی را دارد. --- ## C5. استفاده از سرویس‌های شخص ثالث ### C5.1 ساده‌ترین کار: هر سرویس یک «پرووایدر» در `stt.py` بات شما اکنون پرووایدرهای شماره‌دار با قرارداد OpenAI دارد. پس هر شخص ثالث را یا مستقیم (اگر OpenAI-compatible است) یا با یک adapter کوچک اضافه کنید. مزیت: چند ASR موازی، اجماع LLM، و حذف خودکار پرووایدر خراب (۵۰۳). ### C5.2 داده مستقل برای انتخاب: بنچمارک PSRB (مقاله arXiv:2505.21230) مقاله «PSRB: A Comprehensive Benchmark for Evaluating Persian ASR Systems» (مرکز تحقیقات Part AI، May 2025) ده سیستم را روی گفتار واقعی (۵۸٪ خودانگیخته، ۷۱٪ غیررسمی، ۵۳٪ نویزی/تلفنی) سنجیده است. | مدل | نوع | CER | WER | SW-WER | |---|---|---|---|---| | **Avanegar** | تجاری فارسی | **8.75** | **19.30** | **15.68** | | **Google Chirp v2** | ابری چندزبانه | 9.05 | 19.92 | 15.99 | | **Aipa** | تجاری فارسی | 10.43 | 24.64 | 19.83 | | Faster-Whisper large-v3 | متن‌باز | 13.72 | 33.93 | 26.77 | | Azure | ابری چندزبانه | 15.78 | 33.94 | 28.06 | | Seamless M4T v2 | متن‌باز | 22.30 | 38.85 | 34.76 | | Whisper large-v3 | متن‌باز | 18.92 | 41.49 | 36.97 | | Vosk fa 0.5 | متن‌باز | 23.96 | 44.62 | 39.41 | | NeMo FastConformer fa | متن‌باز | 19.42 | 44.85 | 37.58 | | SLPL wav2vec2 | متن‌باز | 19.06 | 46.74 | 39.16 | **نکات خواندن این جدول:** - **ASR تجاری فارسی (و Chirp 2) تقریباً ۱۴ امتیاز WER از بهترین متن‌باز جلوتر است.** این یعنی ادعای «متن‌باز پایدارترین راهکار» در گزارش A دقیق نیست. متن‌باز مستقل‌تر است، نه دقیق‌تر. - **سوگیری احتمالی:** نویسندگان از Part AI هستند و از آدرس API در پاورقی مقاله، Avanegar ظاهراً محصول همان شرکت است. پس عدد Avanegar را «بالاترین حد خوش‌بینانه» بدانید. - **داده‌ها عمومی‌اند، نه واژگان تدارکات.** نام کالا، برند، واحد و اعداد در محاوره سخت‌ترین بخش‌اند و فقط آزمون روی ویس‌های خودتان تصمیم‌گیر است. - مقاله می‌گوید خطا در **گفتار غیررسمی و لهجه‌های محلی** (مثلاً مشهدی، یزدی) بیشتر است و **Whisper/Seamless هالوسینیشن و بازنویسی** دارند. هر دو برای متن تدارکات خطرناک‌اند، چون عدد یا واحد اشتباه معنی سفارش را عوض می‌کند. - ترکیب «مرتبه اول: دو سیستم متفاوت» معمولاً از «یک سیستم قوی» پایدارتر است. اجماع LLM هم برای همین است. ### C5.3 ارزیابی شخص ثالث: روش پیشنهادی (بدون وابستگی به ادعاهای فروشنده) 1. **مجموعه طلایی:** ۱۰۰ ویس واقعی (حداقل ۳۰) با رونوشت انسانی و جدول کالا/تعداد/واحد. 2. **اجرای همه کاندیدها** روی همان ویس‌ها: بله (دستی، وظیفه T2)، Avanegar، Aipa، Chirp 2، Faster-Whisper، مدل فاین‌تیون فارسی. 3. **سه معیار:** CER (برای فارسی مناسب‌تر از WER)، WER و مهم‌تر **F1 استخراج اقلام** (نام کالا، تعداد، واحد). 4. **انتخاب بر اساس «بهبود اجماع»،** نه دقت تکی: هر کاندید را به مجموعه فعلی اضافه کنید و ببینید خطای نهایی استخراج اقلام چقدر کم می‌شود. 5. **هزینه:** هزینه به ازای هر ویس ۱۰–۳۰ثانیه‌ای در ماه برای حجم واقعی شما. ### C5.4 چه سرویسی را کجا بگذاریم؟ (پیشنهاد مقدماتی) | نقش | کاندید | چرا | |---|---|---| | پرووایدر ۱ (تجاری فارسی) | Avanegar یا Aipa | بهترین در بنچمارک فارسی و لهجه‌ها | | پرووایدر ۲ (ابری) | Chirp 2 (اگر دسترسی ممکن است) | تقریباً هم‌سطح؛ مکمل متفاوت | | پرووایدر ۳ (خودمیزبان) | Faster-Whisper large-v3 + واژگان تدارکات | مستقل؛ قابل بهبود با داده خودتان | | پرووایدر ۴ (بله) | کرالر بله (S3/S4/S5) | تنها کاندیدی که تفاوت واقعی (آموزش‌دیده روی داده‌های متفاوت) دارد؛ ارزش آن را وظیفه T2 تعیین می‌کند | | پشتیبان سبک | Vosk | فقط برای اینکه همیشه یک خروجی داشته باشیم | --- ## C6. تقویت اجماع (بیشترین بازده دقت) | اقدام | اثر مورد انتظار | هزینه | |---|---|---| | تزریق **کاتالوگ کالا/برند/واحد** به LLM اجماع‌گیر | بیشترین کاهش خطای استخراج اقلام | کم | | `initial_prompt`/hotwords واژگان تدارکات در Faster-Whisper | بهبود نام برندها و اصطلاح‌ها | کم | | نرمال‌سازی فارسی (ZWNJ، ارقام) پیش از اجماع | کاهش اختلاف‌های ساختگی بین کاندیدها | کم | | قاعده «اختلاف عدد/واحد ⇒ تأیید از کاربر» | حذف سفارش اشتباه | متوسط (UX) | | ثبت ویس + متن تأییدشده → LoRA روی Whisper | استقلال بلندمدت؛ بهبود روی لهجه و واژگان شما | بالا (ولی ارزشمندترین) | | VAD پیش از ASR | کاهش هالوسینیشن Whisper روی سکوت/نویز (پیشنهاد مقاله PSRB) | کم | ## C7. نظرات و توصیه‌های من (فهرست کامل) 1. **قبل از ساخت اندازه بگیرید.** بدون آزمون دقت (T2)، هر ساعت کار روی کرالر قمار است. اگر بله بهتر نباشد، کرالر ارزش ندارد. 2. **بله یک کاندید است، نه ستون فقرات.** معماری باید بدون آن هم درست کار کند. همین طراحی fail-soft (۵۰۳ ⇒ رد کاندید) درست است. 3. **دو ادعای کلیدی را با چشم خودتان ببینید:** (الف) وجود `GetTranscript`، (ب) وجود دکمه در وب. هر دو گزارش آن‌ها را فرض کرده‌اند. 4. **RPC بر Playwright ترجیح دارد، اگر کپچر ساده باشد** (سبک، متن خالص، سریع). Playwright فالبک مطمئن‌تر در برابر تغییر پروتکل است ولی به وجود دکمه وب وابسته است. «ورود با مرورگر، کار با پروتکل» (E1+) میان‌راه خوبی است. 5. **از کتابخانه‌های MIT استفاده کنید و پروتوی `GetTranscript` را خودتان از کپچر بسازید** (clean-room). `bale-sdk` مجوز اختصاصی دارد؛ فقط با اجازه نویسنده. 6. **معماری «بات رله» ساده‌تر از آپلود از حساب کرالر است.** بات رسمی ارسال را انجام می‌دهد، کرالر فقط ترنسکرایب و حذف می‌کند. (با ریسک: کار کردن RPC روی پیام دریافتی از بات.) 7. **گزارش B را شبه‌کد بدانید.** نام متدها و فرض «پاسخ = متن مستقیم» را با واقعیت تطبیق دهید. 8. **«متن‌باز بهتر از تجاری است» غلط است** (روی داده PSRB). برای دقت، ASR تجاری فارسی و Chirp 2 را کنار بله بسنجید. متن‌باز برای استقلال و سفارشی‌سازی است. 9. **اجماع چندکاندیدی را تقویت کنید** (کاتالوگ کالا، نرمال‌سازی، قاعده اختلاف عدد/واحد). این بیشترین بازده را دارد و به بله وابسته نیست. 10. **اختلاف عدد/واحد را به کاربر برگردانید.** یک تأیید کوتاه از یک سفارش اشتباه ارزان‌تر است. 11. **داده جمع کنید.** ویس + متن تأییدشده، دارایی بلندمدت برای فاین‌تیون و استقلال از بله است. 12. **مسیر رسمی را هم باز کنید** (ایمیل به بله). احتمال موافقت کم است ولی هزینه‌اش یک ایمیل است. 13. **قیمت‌ها و ادعاهای بازاریابی را باور نکنید؛** استعلام رسمی و آزمون روی داده خودتان. 14. **شبیه‌ساز اندروید فقط آخرین چاره است** و فقط اگر بله به‌طور چشمگیر بهتر باشد و وب و RPC هر دو ممکن نباشند. 15. **چت AI بله و پراکسی‌های غیررسمی را کنار بگذارید** (خروجی ناخالص، بدون SLA). 16. **هر چیز بله پشت قرارداد OpenAI و کلید env** باشد تا جایگزینی، تعطیلی و rollback در چند ثانیه ممکن باشد. 17. **ToS/حقوقی تصمیم مدیریتی است** و باید پیش از هر کار با حساب کاربری گرفته شود (H1). من در این سند آن را بررسی نکرده‌ام، طبق درخواست. --- # بخش D: ضمیمه‌ها ## D1. دستورهای کشف سریع (T1، حدود ۳۰ دقیقه) ```bash python -m venv .v && . .v/bin/activate pip index versions aiobale-py # آیا واقعاً وجود دارد؟ pip index versions aiobale autobale baleclient bale-sdk bbpb mkdir -p /tmp/sdk && cd /tmp/sdk pip download bale-sdk --no-deps --no-binary :all: tar xzf bale_sdk-*.tar.gz grep -rniE "transcri|turing|speech|asr|stt" bale_sdk-*/proto bale_sdk-*/docs | head -50 ``` در پایتون (در محیط جدا): ```python from bale import ALL_RPCS # نام ایمپورت bale است؛ نام توزیع bale-sdk for r in ALL_RPCS: s = str(r).lower() if any(k in s for k in ("transcri", "turing", "speech")): print(r) ``` مجوز هر پکیج: ```bash pip download --no-deps && unzip -p '*.dist-info/METADATA' | grep -iE "^(License|Classifier: License)" ``` ## D2. کپچر ترافیک وب (T4/T5) ```python # tools/capture_ws.py — فریم‌های WebSocket و درخواست‌ها را ذخیره می‌کند (قبل از ذخیره redact کن) import asyncio, json, pathlib, re, time from playwright.async_api import async_playwright OUT = pathlib.Path("captures"); OUT.mkdir(exist_ok=True) REDACT = re.compile(rb"(eyJ[A-Za-z0-9_\-\.]{20,})") # JWT def save(kind: str, url: str, payload): data = payload if isinstance(payload, (bytes, bytearray)) else str(payload).encode() data = REDACT.sub(b"", data) (OUT / f"{int(time.time()*1000)}_{kind}.bin").write_bytes(data) async def main(): async with async_playwright() as p: ctx = await p.chromium.launch_persistent_context("/data/profile", headless=False) page = ctx.pages[0] if ctx.pages else await ctx.new_page() def on_ws(ws): print("WS", ws.url) ws.on("framesent", lambda payload: save("ws_sent", ws.url, payload)) ws.on("framereceived", lambda payload: save("ws_recv", ws.url, payload)) page.on("websocket", on_ws) page.on("request", lambda r: print("REQ", r.method, r.url) if r.resource_type in ("fetch", "xhr") else None) await page.goto("https://web.bale.ai") input("ورود را انجام بده، ویس را بفرست، روی «تبدیل به متن» بزن، سپس Enter...") await ctx.close() asyncio.run(main()) ``` > **نکات:** (۱) پروفایل را روی ولوم امن نگه دار. (۲) فایل‌های `captures/*.bin` را قبل از هر اشتراک بازبینی کن؛ شماره تلفن و شناسه‌ها را هم redact کن. (۳) در سرور بدون نمایشگر از `xvfb` استفاده کن. (۴) ورود OTP را انسان انجام دهد (H4). (۵) نسخه Playwright را pin کن؛ نام رویدادها بین نسخه‌ها فرق می‌کند، پس مستندات نصب‌شده را بخوان. رمزگشایی اولیه: ```bash # فریم باینری بدون پیشوند را مستقیم، یا با برداشتن ۵ بایت ابتدای فریم gRPC-Web protoc --decode_raw < captures/.bin # یا: pip install bbpb و استفاده از decode_message ``` ## D3. دستور شروع برای ایجنت (قابل کپی) ```text نقش: تو ایجنت مجری پروژه «کرالر STT بله» هستی. سند مرجع: این فایل (بخش‌های B و C). قواعد: بخش B1 را کامل رعایت کن. هیچ سکرتی را چاپ نکن. بدون تأیید انسان هیچ کاری در حساب بله انجام نده (H1). شروع: وظایف T0، T1، T3 و T2 (کیت ارزیابی) را همین حالا اجرا کن؛ T9 و T10 مستقل از بله‌اند. هر وظیفه: خروجی در docs/findings/ ، برچسب ✅/🟡/❌ ، و گزارش دروازه با قالب B9.1. جایی که به انسان نیاز داری: از قالب B9.2 استفاده کن و ادامه بده با کارهای مستقل. اگر G0 رد شد: T4..T8 و T11 را اجرا نکن و گزارش بده. در پایان هر مرحله: خلاصه ۵ خطی برای مدیر پروژه بنویس (نتیجه، عدد، ریسک، تصمیم لازم). ``` ## D4. پرسش‌های باز (بررسی در وظایف مربوطه) | # | پرسش | وظیفه | |---|---|---| | 1 | `GetTranscript` واقعاً وجود دارد؟ نام سرویس، فیلدها، نوع پاسخ؟ | T1، T5 | | 2 | دکمه «تبدیل به متن» در `web.bale.ai` برای ویس ارسالی و دریافتی هست؟ | T4 | | 3 | پاسخ RPC متن مستقیم است یا `DownloadSource`؟ | T5 | | 4 | RPC روی ویسی که **بات** فرستاده کار می‌کند؟ | T6 | | 5 | ویس بات رله با OGG/Opus به‌صورت «ویس» نمایش داده می‌شود؟ | T6 | | 6 | حساب کرالر برای دریافت پیام از بات رله باید `/start` کند؟ | T6 | | 7 | `stt.py` با ۵۰۳ و متن خالی چه می‌کند؟ شماره پرووایدرها؟ timeout؟ | T0 | | 8 | نام و وضعیت واقعی `aiobale-py`؟ کدام کتابخانه MIT برای انتقال بهتر کار می‌کند؟ | T1، T6 | | 9 | حجم واقعی ویس‌ها در روز و طول متوسط؟ | H3 / T12 | | 10 | دسترسی شبکه سرور به Chirp 2 و ارائه‌دهندگان خارجی؟ | T9 | | 11 | قیمت واقعی پرووایدرهای تجاری برای حجم شما؟ | H7 | | 12 | مالک نگهداری کرالر پس از استقرار؟ | H10 | | 13 | پاسخ بله به درخواست رسمی (S1)؟ | H9 | ## D5. واژه‌نامه | اصطلاح | معنی | |---|---| | **کاندید** | خروجی یک موتور STT برای یک ویس | | **اجماع** | ترکیب کاندیدها توسط LLM به یک خروجی ساختاریافته | | **adapter** | میکروسرویسی با قرارداد OpenAI که موتور بله را پشت خود پنهان می‌کند | | **بات رله** | بات رسمی جداگانه که فقط ویس را برای حساب کرالر می‌فرستد | | **E1/E1+/E2/E3** | موتورهای RPC / ورود مرورگر + کار پروتکل / Playwright کامل / امولاتور | | **G0…G7** | دروازه‌های تصمیم (A8) | | **H1…H11** | ورودی/تصمیم‌های انسانی (A5) | | **clean-room** | تولید تعریف RPC از مشاهده ترافیک خودمان، نه کپی از پروژه اختصاصی | | **CER/WER** | نرخ خطای کاراکتر/کلمه | | **F1 اقلام** | دقت و بازیابی استخراج «کالا، تعداد، واحد» | | **PSRB** | بنچمارک مستقل ASR فارسی (arXiv:2505.21230) | ## D6. منابع - مستندات رسمی بات بله: https://docs.bale.ai/ - `bale-sdk` (PyPI): https://pypi.org/project/bale-sdk/ و https://pypi.org/project/bale-sdk/0.1.1/ - `aiobale` (PyPI): https://pypi.org/project/aiobale ، مستندات: https://docs.aiobale.ir - `autobale` (PyPI): https://pypi.org/project/autobale/ - `BaleClient` (PyPI): https://pypi.org/project/BaleClient/ - `baleself` (Selenium): https://pypi.org/project/baleself/ - `bale-mcp` (PyPI): https://pypi.org/project/bale-mcp/ - بات هوش مصنوعی بله روی Cloudflare Workers: https://github.com/hamedcode/bale-ai-bot - BaleVPN: https://github.com/2077DevWave/BaleVPN - Blackbox Protobuf (NCC Group): https://www.github.com/nccgroup/blackboxprotobuf (پکیج رسمی PyPI: `bbpb`) - PSRB (Persian ASR benchmark): https://arxiv.org/pdf/2505.21230 و دادگان: https://huggingface.co/datasets/PartAI/PSRB - مدل فارسی Whisper (نمونه): https://huggingface.co/vhdm/whisper-large-fa-v1 - خبر انتشار نسخه بله با تبدیل ویس به متن (آنا): https://ana.ir/fa/news/994908