Files
ERP/bale stt report/bale_stt_crawler_playbook.md
T

389 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# پلی‌بوک اجرایی: افزودن موتور 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](https://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 (تکرارپذیر)
```bash
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 (قابل استفاده برای هر سه موتور)
```python
# 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](https://pypi.org/project/aiobale-py/) · [GitHub](https://github.com/aminmadaniofficial/aiobale) · [مستندات](https://aminmadaniofficial.github.io/aiobale/)
- مستندات رسمی بات بله: [docs.bale.ai](https://docs.bale.ai)
- سرویس ثالث STT: [majidapi.ir](https://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.