# خطة: ربط قاعدة المعرفة بأنظمة ERP وقواعد البيانات + تقنية DTMF

> نتاج بحث معمق (يوليو 2026) في المنافسين (Vapi, Retell, ElevenLabs)، وتقنيات NL→SQL مفتوحة المصدر (Vanna 2.0, SQLCoder)، وواجهات Odoo/D365/Oracle، وتوثيق Twilio/Plivo — مبنية على البنية الموجودة فعلاً في المنصة.

---

# الجزء الأول: Live Data Connectors — ربط الوكيل ببيانات العميل الحية

## الفكرة (كما طلبتها)
- العميل (مستخدم المنصة) يربط نظامه: **Odoo أو Oracle أو Dynamics 365 أو قاعدة بيانات مخصصة**.
- يصف بالعربية الطبيعية ما يريده: *"جدول الديون فيه رقم العميل والمبلغ المتبقي وتاريخ الاستحقاق"* أو *"رصيد الإجازات لكل موظف برقم الإقامة"*.
- **قبل كل مكالمة** تقرأ المنصة القيم الحية (المديونية، الرصيد...) وتحقنها في سياق الوكيل — فيبدأ المكالمة وهو يعرف: "أستاذ أحمد، مديونيتك الحالية 4,500 ريال".
- **بدون رفع أي بيانات إلى OpenAI كـRAG** — البيانات تبقى في نظام العميل، وفقط قيم العميل المتصل به تدخل البرومبت لحظيًا.

## لماذا هذا التصميم صحيح (من البحث)
- هذا بالضبط نمط المنافسين: Retell تحقن `retell_llm_dynamic_variables` لكل مكالمة، وVapi تستخدم `variableValues` + أدوات مخصصة، وElevenLabs تدعم `dynamic_variables` — **ومنصتك تستخدمها فعلاً** في [outbound-call-service.ts](server/services/outbound-call-service.ts) (`conversation_initiation_client_data.dynamic_variables`). الأساس جاهز.
- نهج Vanna 2.0 (مفتوح MIT): الـLLM يرى **المخطط فقط** (أسماء الجداول والأعمدة) ولا يرى الصفوف أبدًا — نطبق نفس المبدأ: الوصف الطبيعي + المخطط → استعلام مُجمَّع مرة واحدة → **التنفيذ وقت المكالمة بلا LLM إطلاقًا** (سريع، حتمي، وآمن).

## البنية المقترحة

### 1) جدول `data_connections` — اتصالات الأنظمة
```
id, user_id, name, type ('odoo'|'d365'|'oracle'|'postgres'|'mysql'|'mssql'|'rest'),
config (jsonb مشفّر: host/db/credentials أو url/oauth), status, last_tested_at
```
**السائقون (Drivers):**
| النظام | البروتوكول | ملاحظات |
|--------|-----------|---------|
| Odoo | JSON-RPC / XML-RPC (`execute_kw` على `res.partner`, `account.move`...) | يعمل على Community وEnterprise؛ REST من Odoo 19 |
| Dynamics 365 | Dataverse **OData v4** + OAuth2 (client credentials) | نفس الواجهة لكل وحدات D365 |
| Oracle | حزمة `oracledb` (node) أو ORDS REST | مستخدم قراءة فقط |
| Custom DB | `pg` / `mysql2` / `mssql` | الأكثر طلبًا وأسهل تنفيذًا — **ابدأ به** |
| REST عام | OpenAPI endpoint يعرّفه العميل | للحالات الخاصة |

### 2) جدول `data_views` — "العروض الذكية" (قلب الميزة)
```
id, connection_id, user_id, name,
natural_description   -- الوصف العربي الذي كتبه العميل
compiled_query        -- الاستعلام/الاستدعاء المُجمَّع (SELECT فقط، مُعامل)
lookup_key            -- معامل الربط: 'phone' | 'account_no' | 'iqama' | custom_field
output_variables      -- jsonb: [{column: 'amount_due', variable: 'debt_amount', label: 'المديونية'}]
status ('draft'|'approved'|'active'), cache_ttl_seconds (افتراضي 300)
```

### 3) مُجمِّع الوصف الطبيعي (NL→Query Compiler) — يعمل مرة واحدة عند الإنشاء
1. **استكشاف المخطط**: جلب أسماء الجداول/الأعمدة/الأنواع من النظام المتصل (لـOdoo: `fields_get`؛ لـD365: `$metadata`؛ لـSQL: `information_schema`).
2. **الترشيح**: مطابقة الوصف العربي مع الجداول المرشحة (embedding بسيط أو LLM).
3. **التوليد**: LLM (يرى المخطط فقط — صفر بيانات) يولّد استعلامًا مُعاملًا:
   `SELECT amount_due, due_date FROM debts WHERE customer_phone = :lookup LIMIT 1`
4. **الحراسة الصلبة**: تحقق أن الاستعلام SELECT فقط (رفض أي DML/DDL)، معاملات مربوطة، LIMIT إلزامي، مهلة 5 ثوانٍ.
5. **المعاينة والاعتماد**: يُعرض على العميل الاستعلام + **صف عينة حقيقي** → يعتمد → يصبح active. بعدها لا يُستخدم LLM أبدًا في التنفيذ.

### 4) الحقن قبل المكالمة (Pre-call Injection)
نقطة الدمج موجودة: `campaign-executor` و`outbound-call-service` يدعمان `dynamicData` لكل جهة اتصال.
- قبل الاتصال بكل رقم: تنفيذ الـData Views النشطة للمستخدم بمفتاح الربط (الهاتف/رقم الحساب من حقول جهة الاتصال المخصصة `custom_fields`) → دمج النتائج كمتغيرات: `{{debt_amount}}`, `{{due_date}}`, `{{vacation_balance}}`.
- للحملات الكبيرة: **جلب مسبق دفعة واحدة** (استعلام IN لكل أرقام الدفعة) + كاش TTL — حتى لا يُطرق ERP العميل آلاف المرات.
- المتغيرات تُستخدم في system prompt والرسالة الافتتاحية بصيغة `{{variable}}` (مدعومة فعلاً في المنصة).

### 5) الاستعلام أثناء المكالمة (Mid-call Tool)
المنصة تدعم `webhookTools` فعلاً — نولّد تلقائيًا لكل Data View أداة:
- Endpoint داخلي: `POST /api/data-views/:id/lookup` (موقّع بتوكن، لا يحتاج كشف ERP العميل للإنترنت)
- الوكيل يستدعيها عندما يعطيه العميل رقم حساب أثناء الحديث → القيم ترجع للمحادثة فورًا (ElevenLabs يدعم تحديث المتغيرات من نتائج الأدوات أثناء المكالمة).

### 6) الأمان والامتثال (PDPL/ساما)
- بيانات الاتصال مشفّرة (AES) في القاعدة، ويُطلب من العميل **مستخدم قراءة فقط**.
- البيانات لا تغادر السيرفر إلى أي طرف ثالث — فقط قيم العميل المتصل به تدخل برومبت المكالمة (وهو ما يحدث أصلاً مع أي متغير ديناميكي).
- سجل تدقيق لكل تنفيذ (من، متى، أي view) — يتكامل مع `debt_compliance_audit` الموجودة.
- تكامل مع إضافة ساما: التحقق من هوية العميل قبل ذكر المديونية (مطبّق في برومبت الامتثال).

### 7) واجهة المستخدم
صفحة **"مصادر البيانات"** بمعالج من 4 خطوات:
1. اختر النظام (Odoo/D365/Oracle/DB) → أدخل بيانات الاتصال → زر اختبار
2. اكتب وصفك بالعربية الطبيعية
3. عاينة: الاستعلام المولَّد + صف عينة + أسماء المتغيرات الناتجة (قابلة للتعديل)
4. اعتماد → اختر الوكلاء/الحملات التي تستخدمه

## مراحل التنفيذ
| المرحلة | المحتوى | التقدير |
|---------|---------|---------|
| 1 | Custom DB (Postgres/MySQL) + data_views + المُجمِّع + الحقن قبل المكالمة | أسبوع |
| 2 | أداة mid-call + الكاش والجلب المسبق للحملات + واجهة المعالج | أسبوع |
| 3 | موصل Odoo (JSON-RPC) + موصل D365 (OData) | أسبوع |
| 4 | Oracle + REST عام + سجل التدقيق والتقارير | 3-4 أيام |

---

# الجزء الثاني: تقنية DTMF — إدخال رقم الحساب/الإقامة بلوحة المفاتيح

## الوضع عند المنافسين (من البحث)
- Vapi وRetell وLiveKit وPipecat تدعم التقاط DTMF كمسار موازٍ للكلام (speech أساسي + DTMF بديل).
- ElevenLabs تدعم **إرسال** نغمات DTMF فقط (الوكيل يضغط أزرارًا للتنقل في IVR خارجي) — **لا توفر التقاط** ضغطات المتصل.

## اكتشاف تقني مهم في منصتك ✅
Twilio Media Streams (ثنائي الاتجاه — وهو ما تستخدمه منصتك في محرك Twilio+OpenAI) **يرسل حدث `dtmf` جاهزًا** عبر نفس الـWebSocket عندما يضغط المتصل أي زر. جسر الصوت الحالي ([audio-bridge.service.ts](server/engines/twilio-openai/services/audio-bridge.service.ts)) يعالج `connected|start|media|stop|mark` **ويتجاهل `dtmf`** — أي أن التفعيل يحتاج إضافة حالة واحدة، لا بنية جديدة.

## التصميم

### 1) محرك Twilio+OpenAI (الأساس — جاهزية عالية)
- إضافة `'dtmf'` إلى `TwilioMediaStreamEvent` ومعالجتها في الجسر:
  - **مجمّع أرقام** لكل جلسة: يتراكم مع مهلة بين الأرقام (3 ثوانٍ) أو حتى `#`
  - عند الاكتمال: تمرير الرقم إلى OpenAI Realtime كرسالة نظام (`conversation.item.create`) + `response.create` — الوكيل "يسمع" الرقم المُدخل ويكمل طبيعيًا
- **الربط بالجزء الأول**: الرقم المُدخل (حساب/إقامة) يُمرر تلقائيًا كـ`lookup_key` لـData View → جلب بيانات العميل → حقنها في المحادثة → *"أهلاً أستاذ أحمد، رصيد إجازاتك 12 يومًا"*
- وضعان قابلان للضبط في إعدادات الوكيل:
  - `dtmf_capture: منفصل` — الوكيل يقول "أدخل رقم حسابك ثم #"
  - `dtmf_capture: تلقائي` — أي ضغطات تُلتقط في أي لحظة وتُفسَّر في السياق

### 2) محرك Plivo
- Plivo AudioStream يبث الصوت خامًا؛ التقاط DTMF يتم بأحد طريقتين:
  - أ) حدث DTMF من Plivo عبر callback منفصل (`DigitsReceived` في XML) — يتطلب دمجه مع الجلسة
  - ب) **كشف النغمات برمجيًا** من تدفق الصوت (خوارزمية Goertzel — مكتبات node جاهزة مثل `goertzel-node`) داخل الجسر نفسه — يعمل مع أي مزود
- التوصية: البدء بـ(ب) لأنه موحّد ويعمل لاحقًا مع أي محرك streaming.

### 3) وكلاء ElevenLabs الأصليون (قيد معروف)
- المكالمة تُدار بالكامل عند ElevenLabs ولا يصلنا صوت المتصل → **لا يمكن التقاط DTMF**.
- **البديل العملي**: الالتقاط الصوتي — الوكيل يطلب نطق الرقم، وASR يلتقطه (يعمل بالعربية)، ثم يستدعي أداة الـlookup (mid-call tool من الجزء الأول) للتحقق وجلب البيانات. نفس النتيجة النهائية بدون DTMF.
- توثيق التوصية للعملاء: السيناريوهات المصرفية التي تشترط DTMF (خصوصية الإدخال) → محرك Twilio+OpenAI.

### 4) الأمان (مصرفي/ساما)
- الأرقام المُدخلة **لا تُسجَّل في النص الظاهر** (masking: `****3456`)
- خيار "تحقق ثنائي": DTMF لرقم الحساب + سؤال شفهي (آخر 4 أرقام الجوال) قبل الإفصاح عن المديونية — يحقق بند التحقق من الهوية في لوائح ساما (متكامل مع إضافة sama-call-enforcement)

## مراحل التنفيذ
| المرحلة | المحتوى | التقدير |
|---------|---------|---------|
| 1 | التقاط DTMF في جسر Twilio+OpenAI + مجمّع الأرقام + الحقن في Realtime | 2-3 أيام |
| 2 | الربط بـData Views (إدخال رقم → جلب بيانات → رد الوكيل) + إعدادات الوكيل UI | 2-3 أيام |
| 3 | كشف Goertzel لمحرك Plivo + إخفاء الأرقام في السجلات | 3-4 أيام |
| 4 | البديل الصوتي لوكلاء ElevenLabs (أداة lookup بالنطق) + التحقق الثنائي | يومان |

---

# سيناريو النهاية (الميزتان معًا)
> عميل يتصل → الوكيل: "للاستعلام عن مديونيتك، أدخل رقم إقامتك ثم #" → العميل يُدخل `2345678901#` → المنصة تلتقط الرقم، تستعلم من Odoo الخاص بالشركة عبر Data View "جدول الديون" (بدون LLM، في أقل من ثانية) → تُحقن النتيجة → الوكيل: "شكرًا أستاذ أحمد. مديونيتك الحالية 4,500 ريال وتاريخ الاستحقاق 15 أغسطس. هل تود جدولة السداد؟" — **وكل ذلك دون أن تغادر بيانات الشركة خوادمها إلى أي RAG خارجي.**

# مصادر البحث الرئيسية
- Retell Dynamic Variables: docs.retellai.com/build/dynamic-variables • Vapi Variables/Tools: docs.vapi.ai/assistants/dynamic-variables
- ElevenLabs: Dynamic Variables + Play Keypad Touch Tone (system tools) — elevenlabs.io/docs
- Twilio Media Streams DTMF (GA للبث ثنائي الاتجاه): twilio.com/docs/voice/media-streams/websocket-messages
- Vanna 2.0 (MIT, schema-only, أي قاعدة بيانات): github.com/vanna-ai • Defog SQLCoder (يتفوق على GPT-4 في SQL-eval)
- Odoo External API (XML-RPC/JSON-RPC/REST 19): odoo.com/documentation • D365 Dataverse OData v4: truto.one/blog/dynamics-365-crm-api-2026
- نمط IVR الحديث (speech أساسي + DTMF بديل): futureagi.com/blog/ivr-modernization-ai-voice-agents-2026
