Files
ERP/bale stt report/bale_stt_crawler_playbook.md
T

34 KiB
Raw Blame History

پلی‌بوک اجرایی: افزودن موتور STT بله به خط لوله چندمدله‌ی بات تدارکات

نسخه: ۱.۰ مخاطب: ایجنت هوش مصنوعی مجری + تیم فنی + مدیر محصول ماهیت سند: سند تصمیم‌محور — هر تست به یک «دروازه‌ی تصمیم (Gate)» می‌رسد و هر دروازه، جدول «وضعیت مشاهده‌شده ← اقدام» دارد. ایجنت باید فقط بر اساس وضعیت‌های واقعیِ مشاهده‌شده در همان لحظه، شاخه‌ی درست را انتخاب کند. هدف نهایی: افزودن ترنسکریپشن موتور بله (STT سمت سرور بله) به‌عنوان یکی از کاندیداهای خط لوله‌ی Multi-Provider STT فعلیِ بات، بدون هیچ تغییر ساختاری در بات.


۰. قواعد الزام‌آور اجرا (برای ایجنت — قبل از هر اقدام بخوانید)

  1. مبنای گیت فقط production است. سایر برنچ‌ها (از جمله prod-v7 که کد بات روی آن است) متعلق به جریان کاری خودشان‌اند و نباید توسط ایجنت دست‌کاری، مرج یا پوش شوند. تنها مهندس ارشد شرکت مرج می‌کند. هیچ پوشی بدون تأیید صریح مالک انجام نشود.
  2. درخواست‌های شبکه به بله باید کم‌حجم و انسان‌نما باشند. هشدار جدی: سازندگان کتابخانه‌های مهندسی‌معکوس بله تأکید کرده‌اند که «استفاده‌ی زیاد از POST gRPCِ غیر-احراز (پیام/مدیا/حضور) می‌تواند فلگ، rate-limit یا بن موقت ایجاد کند». پس: ترجیح کانال WebSocket، ترتیبی نه موازی، تأخیر انسانی، سقف روزانه.
  3. حساب‌ها: فقط شماره‌ی اختصاصی شرکت (Bale ئرنر) — هرگز شماره‌ی شخصی. حساب فقط برای این کار باشد.
  4. حریم خصوصی: صدای کاربران تدارکات روی سرورهای بله پردازش می‌شود (همین حالا هم ویس از بله می‌آید)؛ اما اگر ایجنت صدا را مجدداً به یک چت آپلود می‌کند (مسیر R2/R3)، باید بلافاصله پس از ترنسکرایب پیام را حذف کند و آدرسی از صدا نگه ندارد.
  5. هیچ سکرتی (توکن بات، توکن کاربر، session_file، کلید LLM) نباید در گیت کامیت شود. سکرت‌های موجود در docker-compose.yml (توکن بات و کلید DeepSeek به‌عنوان پیش‌فرض) باید در فرصت مناسب rotate و به ${VAR:?…} تبدیل شوند.
  6. معیار موفقیت، سرعت نیست — دقت است. بدون عبور از دروازه‌ی دقت (G5) هیچ ساخت/استقراری مجاز نیست.

۱. واقعیت‌های تأییدشده (پایه‌ی همه‌ی تصمیم‌ها)

۱.۱ — سیستم فعلی: بات و خط لوله‌ی STT

مؤلفه مسیر توضیح
خط لوله STT bot/app/stt.py transcribe_multi() صدای MP3 (خروجی ffmpeg: mono 16kHz) را به موازات به همه‌ی پرووایدرها می‌فرستد؛ هر پرووایدر یک POST {base_url}/audio/transcriptions (multipart، فیلد file، به‌همراه model و language=fa) است و خروجی {"text": "..."} انتظار دارد. fail-soft: خطای هر پرووایدر فقط آن را حذف می‌کند (return None)؛ بقیه ادامه می‌دهند.
پیکربندی پرووایدرها bot/app/config.py STT_PROVIDER_1..3_NAME/BASE_URL/API_KEY/MODEL/LANGUAGE + STT_PROVIDERS_JSON + fallback قدیمی STT_*. نکته: یک پرووایدر فقط وقتی فعال می‌شود که هم BASE_URL و هم API_KEY پر باشند.
مصرف خروجی‌ها bot/app/nlu.py همه‌ی ترنسکریپت‌های کاندید به DeepSeek می‌روند و خروجی نهایی consensus_text + intent + patch (اقلام استعلام) است. یعنی افزودن بله = یک کاندید بیشتر در ورودی consensus.
مسیر ویس در بات bot/app/handlers/__init__.py ویس → دانلود (getFile) → transcribe_multi → متن به‌عنوان ورودی NLU.

نتیجه‌ی معماری (کلیدی): افزودن هر مسیر STT = ساخت یک سرویس adapter با قرارداد OpenAI-compatible + پر کردن STT_PROVIDER_3_*. صفر تغییر در کد بات. (این نقطه‌ی اتصال رسمی و پایدار است.)

۱.۲ — شواهد تجربی Bot API (تست‌شده — تکرارپذیر)

روی توکن بات فعلی (procurement_assistant_bot)، متدهای مشکوک STT تست شدند:

POST https://tapi.bale.ai/bot<TOKEN>/transcribeVoice   → {"ok":false,"error_code":404,"description":"Bad Request: handler not found"}
POST .../getTranscription                              → (همان 404)
POST .../transcribe                                    → (همان 404)
POST .../speechToText                                  → (همان 404)
POST .../voiceToText                                   → (همان 404)
POST .../getMe (کنترل)                                 → {"ok":true,"result":{"username":"procurement_assistant_bot",...}}

مستندات رسمی (docs.bale.ai) هم هیچ متد یا فیلد ترنسکریپشن برای آبجکت Voice/Audio ندارند. نتیجه: Bot API بله قطعاً STT ندارد. مسیر «فیلد مخفی» فقط با تست T1 (بخش ۳) قابل رد شدن نهایی است.

۱.۳ — یافته‌های گزارش‌های ایجنت شرکت (خلاصه‌ی راستی‌آزمایی‌شده)

یافته وضعیت راستی‌آزمایی
کلاینت وب بله روی gRPC-Web + Protocol Buffers (@bufbuild/protobuf) است منطقی و هم‌راستا: کتابخانه‌های مستقل مهندسی‌معکوس بله هم همین را تأیید می‌کنند؛ باید در T2 مجدداً مشاهده شود.
گیت‌وی‌ها: https://maviz-ws.bale.ai و wss://maviz-ws.bale.ai/ws/?uid={user_id} (+ بتا/نکست) در وب ایندکس نشده (طبیعی) — باید در T2 با چشم مشاهده شود.
ماژول‌های SpeechTranscript و AIChat در باندل سرنخ معتبر — باید در T2/T3 تأیید و دیکود شود.
«روش دوم غیرعملی است چون باید کل .protoها بازسازی شوند» نادرست/بیش‌برآورد: با دیکود جنریک Protobuf و استخراج توصیف‌گر از خود باندل (T3) یا با استفاده از aiobale-py (T4)، این کار در حد «افزودن یک متد» کوچک می‌شود.
ماتریس تأخیر («روش دوم زیر ۱ ثانیه») گمراه‌کننده: گلوگاه، استنتاج ASR روی سرور بله است؛ عدد واقعی فقط از T2/T5 می‌آید.

۱.۴ — اکوسیستم مهندسی معکوس موجود (مهم — دارایی راهبردی)

  • aiobale-py (v0.3.9، فعال): فریم‌ورک async برای بله روی WebSocket + Protobuf، الهام‌گرفته از aiogram؛ ~۸۰ متد RPC (پیام، فایل، گروه، مخاطبین…)، ورود تلفنی با OTP، session_file، توکن JWT، rate-limiter داخلی، docs و مخزن عمومی:
    • PyPI: pip install aiobale-py (ایمپورت: aiobale)
    • GitHub: github.com/aminmadaniofficial/aiobale — مستندات: aminmadaniofficial.github.io/aiobale/
    • متد ترنسکرایب ندارد → باید همان RPC کشف‌شده در T2 به آن افزوده شود (زیرساخت لاگین/WS/کدک آماده است).
  • نسخه‌ی اولیه‌ی Enalite/aiobale (GitHub الان 404؛ نام PyPI توسط یک پکیج خالی اشغال شده) → فقط به فورک فعال aiobale-py اتکا کنید.
  • پروژه‌ی جانبی مرتبط: bale-mcp-server (MCP برای بله با aiobale-py) — برای الگوگیری نحوه‌ی استفاده.
  • سرویس ثالث STT: majidapi.ir (/ai/stt?url=...&token=...) — مسیر جایگزین R7 (بخش ۵).

۱.۵ — وضعیت برنچ‌ها (قواعد همکاری)

برنچ وضعیت مجاز برای ایجنت؟
production (253c922) مبنای رسمی کار ✅ فقط خواندن/مبنا
prod-v7 (کد بات/ایجنت، 638076d) کار مالک این سند؛ منتظر تست نهایی و مرج توسط مهندس ارشد ⛔ دست‌کاری/پوش ممنوع
prod-v8/9/10، pm، pm_2 و بقیه برنچ‌های سایر اعضا؛ در حال تست ⛔ هیچ دخالتی

۲. نقشه‌ی وضعیت‌ها (State Machine) — نمای کلی مسیر

S0 (شروع)
 │
 ├─T1─> G1 ──(فیلد ترنسکریپشن پیدا شد)───────────────► R1: پایان! (بدون کرالر) ✅
 │             └─(پیدا نشد)──► T2
 │
 ├─T2─> G2 ──(RPC ساده/دیکودپذیر)──► T3+T4 ─> G3 ──(موفق)──► T5+ G4 ─► T7 ─> G5
 │             ├─(فقط روی WS)──────────► همان T3+T4 (aiobale مسیر WS است) ▲
 │             ├─(رمزنگاری/امضای خاص)──► R4 (Playwright) یا R3 ─► T5/T7
 │             └─(نیاز به اثر انگشت مرورگر)─► R4 ─► T5/T7
 │
 ├─(T5/T7)─> G5 ──(بهبود دقت کافی)──► G6 (ToS) ──(سبز)──► BUILD (R2/R3/R4) ─► G7 (سواک) ─► استقرار کامل
 │                     └─(بهبود ناکافی)──► STOP: همان پرووایدرهای فعلی ⛔
 │                                    └─(قرمز)──► STOP یا فقط R4 در حد محدود ⛔

۳. فاز A — تست‌های کشف (T1…T9)

برای هر تست: هدف، پیش‌نیاز، مراحل، خروجی، معیار قبولی، و حالت‌های ممکن. ایجنت بعد از هر تست حتماً به جدول دروازه‌ی مربوطه (بخش ۴) مراجعه کند.

T1 — تست payload خام ویس (۳۰ دقیقه)

هدف: رد یا تأیید نهایی وجود فیلد ترنسکریپشن مخفی در پیام‌های Bot API. پیش‌نیاز: یک ربات تستی مستقل (از BotFather بله بسازید؛ از ربات فعلی استفاده نکنید چون در حال long-poll است و مصرف‌کننده‌ی دوم تعارض ایجاد می‌کند). یک حساب بله برای فرستادن ویس. مراحل:

  1. اسکریپت مینیمال پایتون: getUpdates بدون offset + چاپ JSON کامل هر آپدیت در فایل.
  2. یک ویس فارسی واقعی (۵–۱۰ ثانیه) به ربات تستی بفرستید.
  3. JSON کامل ویس را ذخیره کنید؛ کلیدهای آبجکت voice/audio/message را لیست کنید.
  4. getFile را هم صدا بزنید و فیلدهای پاسخ را ضبط کنید. معیار قبولی/حالت‌ها:
  • حالت A: هر فیلدی مثل transcription/transcript/speech/text روی ویس وجود دارد → پایان پروژه‌ی کرالر (R1)؛ فقط به بات وصل کنید.
  • حالت B: خبری نیست (قوی‌ترین احتمال طبق مستندات) → برو به T2.

T2 — کپچر ترافیک کلاینت وب (نیم‌روز) — قاطع‌ترین تست

هدف: کشف «دقیق» مکانیزم ترنسکرایب: نام RPC، ترابری (unary/WS)، قالب فریم، هدر احراز، و زمان واقعی ASR. پیش‌نیاز: حساب سوخته‌ی اختصاصی (سیم‌کارت شرکت)، مرورگر Chromium، ابزار دیکود Protobuf: protoscope یا blackboxprotobuf (نصب با pip/go)، و اختیاری mitmproxy. مراحل:

  1. ورود به web.bale.ai؛ DevTools → Network با گزینه‌ی Preserve log؛ فیلتر WS و Fetch/XHR را فعال نگه دارید.
  2. یک ویس در یک چت باز کنید و دکمه‌ی «تبدیل به متن / متن پیاده» را بزنید.
  3. همه‌ی درخواست‌های همان لحظه را ضبط کنید:
    • اگر POST روی هاستی مثل maviz-ws.bale.ai با content-type: application/grpc-web+proto (یا grpc-web-text) ظاهر شد → همین RPC است.
    • اگر قاب باینری روی WebSocket wss://…/ws/?uid=… رفت → RPC روی WS است.
  4. برای هر نمونه ذخیره کنید: req.bin، resp.bin، هدرهای مهم (نام هدر توکن: authorization/x-auth/…)، URL کامل، و زمان پاسخ از تب Timing (همین = تأخیر واقعی ASR بله).
  5. ۵ تا ۱۰ ویس متنوع (کوتاه/بلند/با لهجه) تکرار کنید تا پایداری اسکیما معلوم شود؛ در samples/ ذخیره و در findings.md خلاصه کنید.
  6. دیکود آفلاین: به یاد داشته باشید پیام‌های gRPC-Web یک پیشوند ۵ بایتی دارند (۱ بایت فلگ + ۴ بایت طول) و سپس Protobuf می‌آید → ابتدا پیشوند را بردارید، سپس: protoc --decode_raw < resp_payload.bin یا protoscope resp_payload.bin
  7. اگر آرایه‌ی WS دیده شد: فریم‌ها را از DevTools کپی و با protoscope دیکود کنید؛ به دنبال پیام معادل SpeechTranscript بگردید. خروجی: capture/findings.md شامل: نام دقیق RPC، ترابری، framing، هدر احراز، تأخیر واقعی، و نقشه‌ی مقدماتی فیلدها (کدام فیلد = message_id؟ file_id؟ متن؟).

T3 — استخراج اسکیما (۱–۲ روز)

هدف: تبدیل کپچرها به یک اسکیمای قابل استفاده (و ترجیحاً .proto). روش‌ها (به ترتیب):

  1. از خود باندل: باندل وب بله (/static/js/index.*.js و در صورت وجود .js.map) را از قبل دارید؛ پروژه‌های ساخته‌شده با @bufbuild/protobuf معمولاً FileDescriptorProto باینری را داخل JS جاسازی می‌کنند. روش عملی: جست‌وجوی بایت‌های توصیف‌گر، استخراج و سپس: protoc --decode=google.protobuf.FileDescriptorProto google/protobuf/descriptor.proto < desc.bin (یا دیکود همان بایت‌ها با پایتون + grpcio-tools). اگر sourcemap موجود بود، مستقیم‌تر است.
  2. از نمونه‌ها: با چند نمونه‌ی req/resp دیکودشده با protoc --decode_raw، همبستگی فیلدها را استخراج کنید (فیلد شماره‌ی متن جواب، message_id درخواست و…).
  3. مسیر موازی (اپ اندروید): APK رسمی بله (از سایت رسمی) + jadx؛ جست‌وجوی "SpeechTranscript"؛ در اندروید معمولاً شماره‌ی فیلدها واضح‌ترند. معیار قبولی: توان ساخت درخواست معتبر و خواندن پاسخ — حتی به‌صورت «فیلد ۱ = message_id، فیلد ۲ = متن». داشتن .proto کامل لازم نیست.

T4 — اسپایک aiobale-py (۱ روز)

هدف: اثبات امکان اجرای ترنسکرایب با یک اکانت کاربری، بدون مرورگر. پیش‌نیاز: venv ایزوله (نه محیط بات)، حساب سوخته، اسکیمای T3. مراحل:

  1. pip install aiobale-py → ورود: Client(session_file="bale.session", phone_number="…") و عبور از OTP (دروازه‌ی انسانی).
  2. کاوش لایه‌ی کدک: در site-packages/aiobale/… ببینید متدها چطور تعریف می‌شوند (احتمالاً مپینگ متد→پیام Protobuf). یک متد سفارشی برای RPC ترنسکریپت کشف‌شده اضافه کنید.
  3. کشف مکانیزم دقیق: آیا RPC ترنسکرایب message_id می‌خواهد (یعنی صدا باید از قبل «پیامی در بله» باشد) یا file_id/بایت خام قبول می‌کند؟ هر سه فرض را تست کنید:
    • (الف) آپلود ویس به یک چت اسکرچ (چت با خود یا گروه خصوصی اختصاصی) → گرفتن message_id → ترنسکرایب → حذف پیام.
    • (ب) upload_file (در aiobale موجود است) → ترنسکرایب با فایل.
    • (ج) اگر RPC ورودی بایت می‌گیرد → ساده‌ترین (بدون پیام واسط).
  4. تست صحت: متن برگشتی را با متنی که وب بله برای همان ویس نشان می‌دهد مقایسه کنید (باید یکسان/نزدیک باشد).
  5. تست پایداری نشست: ری‌استارت پروسه و استفاده‌ی مجدد از session file؛ ثبت زمان انقضای توکن. حالت‌ها:
  • موفق کامل → مسیر R2 برای ساخت adapter انتخاب می‌شود.
  • نیمه‌موفق (login/WS می‌شود ولی RPC دیکود نمی‌شود) → T3 را عمیق‌تر + کپچر بیشتر؛ در غیر این صورت R3/R4.
  • ناموفق (چالش احراز، بن، نبود قابلیت افزودن متد) → R3 و R4.

T-extra — تست «AI Chat به‌عنوان اوراکل» (نیم‌روز، موازی با T2/T4)

هدف: اگر چت هوش مصنوعی بله ویس را با متن پیاده‌شده پاسخ می‌دهد، حتی نیازی به RPC جدید نیست: با متدهای پیام‌رسانیِ موجودِ aiobale، ویس را به چت AI بفرستید و متن را از پاسخ استخراج کنید. مراحل: با اکانت سوخته در وب بله، یک ویس به چت AI بفرستید؛ دقیقاً ببینید آیا متن پیاده‌شده در پاسخ می‌آید و با چه قالبی. سپس همان را با aiobale بازسازی کنید. معیار: اگر متن پیاده با قالب قابل‌اعتماد (ثابت) برگردد → مسیر R3 فعال می‌شود (ارزان‌تر از R2).

T5 — اندازه‌گیری تأخیر/پایداری واقعی (۱–۲ روز)

هدف: جایگزینی اعداد ساختگی گزارش با اعداد واقعی. مراحل: ۵۰–۱۰۰ ویس واقعی از مسیر انتخابی (ترتیبی، با فاصله‌ی انسانی ۱–۳ ثانیه)؛ ثبت p50/p95، نرخ خطا، انواع خطا، و هر نشانه‌ی rate-limit. معیار پذیرش پیشنهادی: نرخ موفقیت ≥ ۹۵٪ و p95 ≤ ۸ ثانیه (برای UX بات). در غیر این صورت G4 قرمز می‌شود.

T7 — بنچمارک دقت (۱ روز) — دروازه‌ی کسب‌وکاری، قبل از هر ساخت

هدف: آیا افزودن بله واقعاً خروجی نهایی را بهتر می‌کند؟ مراحل:

  1. ۲۰–۳۰ ویس واقعی تدارکاتی جمع کنید (با اطلاع فرستندگان؛ صداها را بلندمدت نگه ندارید).
  2. برای هر ویس سه خروجی بگیرید: (الف) وضعیت فعلی (whisper/سایر + consensus فعلی)، (ب) فقط متن بله، (ج) consensus با اضافه‌شدن کاندید بله.
  3. سه معیار بسنجید: WER متن، دقت استخراج موجودیت‌ها (نام کالا/برند/تعداد/واحد در JSON نهایی)، و تعداد «موارد سخت» که با بله درست شده‌اند. معیار پیشنهادی: اگر کاهش خطای استخراج ≥ ~۱۵٪ نسبی یا حل ≥ ۲ مورد سختِ لهجه‌ای → پروژه توجیه دارد؛ در غیر این‌صورت STOP (همان خط لوله‌ی فعلی کافی است).

T6 + T9 — سواک بن و ماندگاری نشست (۱–۲ هفته، پسیو)

استفاده‌ی کم‌حجم روزانه (مثلاً ≤ ۱۰۰ ترنسکرایب در روز) از IP شرکت؛ ثبت: تعداد ورود مجدد لازم، چالش‌های امنیتی، هشدارها، عمر واقعی توکن. خروجی: سقف عملیاتی + runbook ورود مجدد.

T8 — بازبینی حقوقی/ToS (موازی — دروازه‌ی انسانی)

ایجنت فقط شواهد را جمع می‌کند؛ تصمیم Go/No-Go توسط مدیریت مستند می‌شود. معیار: سند تصمیم مکتوب.


۴. دروازه‌های تصمیم (جدول‌های وضعیت ← اقدام)

G1 (بعد از T1)

وضعیت مشاهده‌شده اقدام
فیلد ترنسکریپشن روی ویس هست پایان: R1 — مستقیم به بات وصل کن (بدون هیچ کرالری)
نیست برو T2

G2 (بعد از T2) — مهم‌ترین دروازه‌ی فنی

وضعیت مشاهده‌شده معنا اقدام
RPC از نوع unary POST gRPC-Web با پروتوباف دیکودپذیر بهترین حالت برو T3+T4 → R2
RPC فقط روی WebSocket حالت مطلوب دوم (aiobale دقیقاً WS است) برو T3+T4 → R2 (از کانال WS؛ POST gRPC زیاد نزن)
درخواست رمزنگاری/امضای اختصاصی دارد (فراتر از پروتوباف) هزینه‌ی نگهداری بالا R4 (Playwright) یا R3 به‌عنوان مسیر اصلی؛ R2 فقط اگر … شکست
ترنسکرایب نیازمند اثر انگشت/رفتار مرورگر است R2 غیرممکن R4
ترنسکرایب message_id روی پیامی موجود می‌خواهد نیازمند «آپلود به چت اسکرچ» R2 با فلوی «آپلود→ترنسکرایب→حذف» (بخش ۵.۲)

G3 (بعد از T3/T4)

وضعیت اقدام
ساخت درخواست + خواندن پاسخ ممکن شد برو T5 → T7
فقط یکی از دو جهت ممکن است کپچر بیشتری بگیر (T2)؛ اگر نشد → R3/R4
شکست کامل R3 (اگر T-extra موفق بود) وگرنه R4؛ اگر هر دو هم نشد → STOP

G4 (بعد از T5)

وضعیت اقدام
نرخ موفقیت ≥ ۹۵٪ و p95 ≤ ۸s ادامه
rate-limit مشاهده شد کاهش نرخ + backoff نمایی + سقف روزانه؛ مجدداً بسنج
بن/چالش امنیتی توقف فوری؛ رفتن به R4 با اکانت جدید، یا STOP

G5 (بعد از T7) — دروازه‌ی کسب‌وکار

وضعیت اقدام
بهبود دقت مطابق آستانه ادامه به G6
بهبود ناکافی STOP — همان پرووایدرها؛ یافته‌ها را مستند کن

G6 (ToS) / G7 (سواک)

وضعیت اقدام
ToS سبز + سواک سالم استقرار کامل (بخش ۶)
ToS قرمز STOP یا فقط R4 با حجم محدود (طبق تصمیم مدیریت)
سواک ناسالم rollback به پرووایدرهای قبلی (حذف envها) و بازگشت به G4

۵. فاز B — پیاده‌سازی (فقط پس از عبور از دروازه‌ها)

۵.۱ — قرارداد مشترک adapter (برای همه‌ی مسیرها یکسان)

سرویس bale-stt (کانتینر داخلی، بدون پورت عمومی):

  • POST /v1/audio/transcriptions — multipart، فیلد file (فایل MP3)، model، language=fa → پاسخ {"text": "..."} (دقیقاً قرارداد OpenAI که بات انتظار دارد).
  • هدر Authorization: Bearer <INTERNAL_KEY> را بپذیرد (فقط کلید داخلی، نه توکن بله).
  • GET /healthz برای سلامت.
  • موتور داخلی قابل‌سوییچ با env: BALE_STT_ENGINE=grpc|aichat|playwright → همه‌ی مسیرها همزیستی دارند و تعویض موتور بدون تغییر بات.

۵.۲ — R2: مهندسی معکوس (مسیر پیشنهادی اصلی)

معماری: bot → adapter → aiobale-py (اکانت سوخته) → WS/gRPC بله → متن نکات پیاده‌سازی:

  • اکانت: session_file روی یک داکر ولوم؛ هرگز در گیت. BALE_PHONE و سکرت‌ها در env.
  • اگر RPC به message_id نیاز دارد: فلوی «آپلود ویس به چت اسکرچ → ترنسکرایب → حذف پیام» — با send_voice و delete_message موجود در aiobale.
  • ترتیبی اجرا کنید (قفل asyncio)، فاصله‌ی ۱–۳ ثانیه، سقف روزانه قابل‌تنظیم (BALE_STT_DAILY_CAP).
  • مدیریت انقضای توکن: تشخیص خطای احراز → علامت‌گذاری «نیازمند OTP» + هشدار به ادمین (دروازه‌ی انسانی).
  • fail-soft مضاعف: adapter در هر خطا {"text": ""} (یا 5xx کنترل‌شده) برگرداند تا بات فقط این کاندید را حذف کند.

۵.۳ — R3: اوراکل AI Chat

همان adapter، اما داخلش: ارسال ویس به چت AI (با aiobale) → استخراج متن از قالب پاسخ → بازگرداندن. سبک‌ترین پیاده‌سازی؛ وابسته به پایداری قالب پاسخ.

۵.۴ — R4: Playwright (فالبک)

کانتینر جدا با Chromium headless + پروفایل پایدار (userDataDir روی ولوم)، یک worker تک‌نمونه با صف؛ مراحل: ورود یک‌باره (OTP انسانی) → برای هر درخواست: آپلود ویس به یک چت اسکرچ → کلیک «تبدیل به متن» → انتظار روی سلکتور متن → استخراج → حذف پیام. RAM/زمان بیشتر؛ مقاوم‌تر به تغییر پروتکل.

۵.۵ — ادغام با بات (بدون تغییر کد)

در docker-compose.yml (نسخه‌ی تیم/سرور، نه برنچ دیگران) سرویس bale-stt اضافه و در سرویس bot این 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=<INTERNAL_KEY>
PROCUREMENT_BOT_STT_PROVIDER_3_MODEL=bale-stt
PROCUREMENT_BOT_STT_PROVIDER_3_LANGUAGE=fa

(یادآوری از کد: پرووایدر فقط با پر بودن همزمان BASE_URL و API_KEY فعال می‌شود.)

۵.۶ — سکرت‌ها و امنیت

سکرت محل نکته
توکن بات بله env سرور موجود در compose کامیت‌شده → rotate
کلید DeepSeek env سرور همین‌طور rotate
session_file اکانت سوخته داکر ولوم هرگز گیت؛ مالک مشخص
INTERNAL_KEY adapter env فقط بین bot و adapter
DRAFT_TOKEN_SECRET بات env پیش‌فرض فعلی در کد ضعیف است → ست شود

۵.۷ — مقیاس

فاز اول: تک‌نمونه، ترتیبی، سقف روزانه. توسعه فقط بعد از G7 و فقط با شواهد پایداری.


۶. پذیرش، استقرار، پایش، بازگشت

تست‌های پذیرش (اجباری):

  1. curl -F "file=@voice.mp3" -F model=bale-stt http://localhost:8080/v1/audio/transcriptions → {"text": "<متن فارسی درست>"}.
  2. بات: ارسال ویس واقعی → پاسخ استخراج‌شده‌ی صحیح.
  3. fail-soft: توقف کانتینر bale-stt → بات همچنان ویس را (با پرووایدرهای دیگر) پردازش کند ✓.
  4. حریم خصوصی: بررسی کنید هیچ فایل صوتی روی adapter باقی نمی‌ماند و پیام‌های اسکرچ حذف شده‌اند.

استقرار فازبندی‌شده: dev → سرور با حجم کم (۱ هفته) → کامل. پایش: شمارش درخواست، نرخ موفقیت، هیستوگرام تأخیر، انواع خطا، وضعیت نشست. هشدار اگر نرخ خطا در ۳۰ دقیقه > ۲۰٪. بازگشت (Rollback): حذف STT_PROVIDER_3_* از env بات + توقف کانتینر adapter — بات فوراً به وضعیت قبل برمی‌گردد (بدون deploy مجدد).


۷. رجیستر ریسک

ریسک احتمال اثر مقابله
بن/rate-limit اکانت متوسط توقف مسیر حجم کم، WS-first، backoff، اکانت اختصاصی قابل‌تعویض، G4
تغییر پروتکل بله متوسط قطع سرویس pایش نسخه‌ی باندل (hash)، آلرت خطا، فالبک R4، سوییچ موتور با env
انقضای نشست/نیاز OTP بالا (دوره‌ای) توقف موقت ولوم session + آلرت + runbook ورود (انسانی)
دقت ناکافی نسبت به هزینه نامعلوم هزینه‌ی بی‌فایده G5 قبل از ساخت
ریسک حقوقی/ToS نامعلوم توقف اجباری T8/G6 — تصمیم مدیریت
حریم خصوصی صدا کم اعتباری حذف فوری پیام‌ها/فایل‌ها؛ عدم نگهداری
وابستگی به فرد نگهدارنده متوسط رکود مستندسازی در همین پلی‌بوک + مالک رسمی

۸. دروازه‌های انسانی (فقط انسان می‌تواند)

  1. OTP ورود اکانت (بار اول و هر بار انقضا).
  2. تصمیم ToS/حقوقی (G6).
  3. جمع‌آوری نمونه‌های واقعی ویس برای T7 (با رضایت).
  4. تأیید نهایی استقرار و تأیید مرج برنچ توسط مهندس ارشد.

ضمیمه A — دستورات probe (تکرارپذیر)

TOKEN="<BOT_TOKEN>"
for M in transcribeVoice getTranscription transcribe speechToText voiceToText; do
  curl -s -X POST "https://tapi.bale.ai/bot$TOKEN/$M" -H "Content-Type: application/json" -d '{}'; echo
done
curl -s -X POST "https://tapi.bale.ai/bot$TOKEN/getMe" -H "Content-Type: application/json" -d '{}'

خروجی مورد انتظار: پنج 404 «handler not found» و یک getMe سالم. (همین تست را قبل از هر تصمیم مجدداً اجرا کنید — شاید بله متد اضافه کرده باشد.)

ضمیمه B — اسکلت adapter (قابل استفاده برای هر سه موتور)

# bale_stt/adapter.py — قرارداد OpenAI-compatible برای بات
import os
from fastapi import FastAPI, File, Form, Header, HTTPException, UploadFile

app = FastAPI()
INTERNAL_KEY = os.environ["BALE_STT_INTERNAL_KEY"]
ENGINE = os.environ.get("BALE_STT_ENGINE", "grpc")  # grpc | aichat | playwright

@app.get("/healthz")
def healthz():
    return {"status": "ok", "engine": ENGINE}

@app.post("/v1/audio/transcriptions")
async def transcribe(file: UploadFile = File(...), model: str = Form("bale-stt"),
                     language: str = Form("fa"), authorization: str = Header("")):
    if authorization != f"Bearer {INTERNAL_KEY}":
        raise HTTPException(status_code=401)
    audio = await file.read()
    if ENGINE == "grpc":
        text = await grpc_transcribe(audio)      # ← aiobale-py + RPC کشف‌شده
    elif ENGINE == "aichat":
        text = await aichat_transcribe(audio)    # ← ارسال به چت AI و استخراج متن
    else:
        text = await playwright_transcribe(audio)
    return {"text": text or ""}
# نکته: هرگز استثنا را بیرون نده؛ در خطا {"text": ""} برگردان تا بات fail-soft بماند.

ضمیمه C — منابع

  • aiobale-py: PyPI · GitHub · مستندات
  • مستندات رسمی بات بله: docs.bale.ai
  • سرویس ثالث STT: majidapi.ir
  • گزارش‌های ایجنت شرکت: bale_stt_integration_report.md و bale_stt_integration_report_2.md (کنار همین فایل)

ضمیمه D — چک‌لیست سریع ایجنت (TL;DR)

  1. قواعد بخش ۰ را خواندم.
  2. T1 (payload ویس) → G1.
  3. T2 (کپچر) → G2 → مسیر (R2 / R3 / R4) انتخاب شد.
  4. T3+T4 (اسکیما + اسپایک) → G3.
  5. T-extra (اوراکل AI Chat) اگر مسیر R3 مدنظر است.
  6. T5 (تأخیر واقعی) → G4.
  7. T7 (دقت) → G5 — بدون این، ساخت ممنوع.
  8. T8 (ToS — انسانی) → G6.
  9. ساخت adapter (۵.۱) + ادغام env (۵.۵) + تست‌های پذیرش (بخش ۶).
  10. سواک (T6/T9) → G7 → استقرار کامل. هر شکست ⇒ جدول همان دروازه ⇒ اقدام جایگزین یا STOP.