Sebastien Rousseau

Àkàndé: GPT-Powered Voice Assistant for Executives

معمارية مساعد صوتي مفتوح المصدر بـ Python: Whisper وGPT-4 وذاكرة SQLite وfpdf2

5 دقيقة قراءة
Banner for: Àkàndé: GPT-Powered Voice Assistant for Executives

الملخص التنفيذي / النقاط الرئيسية

  • Àkàndé ⧉ مساعد صوتي مفتوح المصدر بلغة Python يربط تحويل الكلام إلى نص عبر OpenAI Whisper واستكمالات GPT-4 وذاكرة تخزين مؤقت محلية SQLite وتصدير PDF بـ fpdf2 في سير عمل صوتي واحد دون الحاجة لتخزين سحابي أو أوزان نماذج AI محلية.
  • ذاكرة SQLite تخزن تجزئات SHA-256 لسلاسل الاستعلام المُعيَّرة مُعيَّنة إلى نص استجابة API الخام؛ نتائج الذاكرة لا تكلف أي رموز وتعود في أقل من 10 مللي ثانية مما يجعل الاستعلامات المتكررة (كمراجعة قرار من وقت سابق في الاجتماع) مجانية فعلياً.
  • المحادثة متعددة الأدوار تُحافَظ عليها ببناء قائمة messages في الذاكرة وتمريرها في كل استدعاء Chat Completions API — يتلقى النموذج كامل سجل الجلسة ليتمكن من الإشارة إلى التبادلات السابقة، على حساب زيادة تدريجية في استخدام الرموز لكل دور.
  • توليد ملخص PDF يُسلسل قائمة messages للجلسة في مستند fpdf2 منسّق: أدوار المستخدم والمساعد مُعلَّمة، والطوابع الزمنية مُدرجة، والترقيم التلقائي يعالج جلسات بأي طول؛ يُكتب الملف في نظام الملفات المحلي ولا يُرفع.
  • حدود الخصوصية: فقط الاستعلام المباشر (وسجل الجلسة حتى حد نافذة السياق) يغادر الجهاز — لا تسجيلات صوتية ولا نصوص منقولة ولا ردود مخزنة تُرسَل لأي خدمة بعيدة غير OpenAI API.

Àkàndé ⧉ مساعد صوتي مفتوح المصدر بلغة Python مبني حول ثلاثة مكوّنات قابلة للتركيب: OpenAI Whisper للتعرف على الكلام، وواجهة برمجية GPT-4 Chat Completions لفهم اللغة وتوليدها، وقاعدة بيانات SQLite محلية لتخزين الردود مؤقتاً واستمرارية الجلسات. والنتيجة سير عمل صوتي يمكن تشغيله على حاسب محمول دون أوزان نماذج محلية أو بنية تحتية للتخزين غير المتصل أو حزمة حاويات.

تصف هذه المقالة البنية التقنية لكل مكوّن وقرارات التصميم المتعلقة بالتخزين المؤقت والسياق متعدد الأدوار وخط أنابيب تصدير PDF.

نظرة عامة على خط الأنابيب #

تفاعل Àkàndé الواحد يتبع هذا التسلسل:

  1. التقاط الصوت — يتحدث المستخدم؛ يسجّل التطبيق الصوت في ملف WAV مؤقت باستخدام sounddevice أو مكتبة صوتية متوافقة.
  2. تحويل الكلام إلى نص — يُرسَل ملف WAV إلى openai.audio.transcriptions.create() (Whisper API)؛ يُعاد النص المنقول كسلسلة نصية عادية.
  3. البحث في الذاكرة — يُعيَّر النص المنقول (تصغير الحروف وضغط المسافات) ويُجزَّأ بـ SHA-256؛ يُبحث عن التجزئة في جدول SQLite المحلي response_cache.
  4. استدعاء API أو نتيجة من الذاكرة — عند الإخفاق، يُضاف النص المنقول إلى قائمة messages للجلسة ويُرسَل إلى openai.chat.completions.create()؛ يُخزَّن نص الاستجابة في الذاكرة.
  5. تحويل النص إلى كلام — يُحوَّل نص الاستجابة إلى صوت باستخدام نقطة النهاية openai.audio.speech.create() (TTS) أو مكتبة TTS محلية ويُشغَّل.
  6. تصدير PDF (عند الطلب) — تُسلسَل قائمة messages الكاملة في مستند fpdf2 منسّق وتُكتب على القرص.

تكامل OpenAI: Chat Completions وWhisper #

تستخدم Àkàndé openai Python SDK للتعرف على الكلام وتوليد النص. استدعاء نسخ Whisper:

with open(audio_file_path, "rb") as f:
    transcript = openai.audio.transcriptions.create(
        model="whisper-1",
        file=f,
        language=None  # auto-detect
    )
user_text = transcript.text

استدعاء Chat Completions يحتفظ بقائمة messages محدودة النطاق بالجلسة:

messages.append({"role": "user", "content": user_text})

response = openai.chat.completions.create(
    model="gpt-4-turbo-preview",
    messages=messages,
    temperature=0.2,
    max_tokens=1024
)

assistant_text = response.choices[0].message.content
messages.append({"role": "assistant", "content": assistant_text})

يُضاف موجّه النظام مرة واحدة في بداية الجلسة ويتحكم في شخصية Àkàndé وتنسيق الإخراج وأي قيود خاصة بالمجال:

messages = [
    {
        "role": "system",
        "content": (
            "You are Àkàndé, a concise executive assistant. "
            "Respond in plain prose. Do not use markdown. "
            "If asked to summarise, produce three bullet points maximum."
        )
    }
]

يُقايض إعداد temperature=0.2 التنوع الإبداعي بالحتمية — مهم للاستعلامات الواقعية كاستذكار قرار من وقت سابق في الجلسة.

ذاكرة التخزين المؤقت SQLite للردود #

مخطط الذاكرة بسيط:

CREATE TABLE IF NOT EXISTS response_cache (
    query_hash  TEXT PRIMARY KEY,
    response    TEXT NOT NULL,
    created_at  INTEGER NOT NULL  -- Unix timestamp
);

مسار البحث والكتابة:

import hashlib, sqlite3, time

def _normalise(text: str) -> str:
    return " ".join(text.lower().split())

def cache_get(conn: sqlite3.Connection, query: str) -> str | None:
    h = hashlib.sha256(_normalise(query).encode()).hexdigest()
    row = conn.execute(
        "SELECT response FROM response_cache WHERE query_hash = ?", (h,)
    ).fetchone()
    return row[0] if row else None

def cache_set(conn: sqlite3.Connection, query: str, response: str) -> None:
    h = hashlib.sha256(_normalise(query).encode()).hexdigest()
    conn.execute(
        "INSERT OR REPLACE INTO response_cache VALUES (?, ?, ?)",
        (h, response, int(time.time()))
    )
    conn.commit()

يضمن INSERT OR REPLACE تحديث الاستجابة المخزنة إذا أُرسِل الاستعلام ذاته بعد ترقية النموذج. يمكن جدولة استعلام إخلاء قائم على TTL (DELETE WHERE created_at < ?) عند بدء التشغيل للتحكم في حجم الذاكرة.

أداء نتائج الذاكرة: يعود بحث SQLite على SSD محلي في أقل من 1 مللي ثانية لجداول تصل إلى 100,000 صف تقريباً. يبلغ كمون الرحلة الكاملة لاستدعاء GPT-4 API المباشر عادةً 600–900 مللي ثانية للردود القصيرة. بالنسبة لإحاطة يومية مع عدد قليل من الاستعلامات المتكررة، تُلغي الذاكرة معظم استدعاءات API بعد الجلسة الأولى.

توليد ملخص PDF #

يستخدم تصدير PDF مكتبة fpdf2 وهي مكتبة Python للـ PDF نشطة الصيانة بلا تبعيات ثنائية:

from fpdf import FPDF
from datetime import datetime

def export_session_pdf(messages: list[dict], output_path: str) -> None:
    pdf = FPDF()
    pdf.add_page()
    pdf.set_font("Helvetica", size=11)
    pdf.set_margins(20, 20, 20)

    pdf.set_font("Helvetica", "B", 14)
    pdf.cell(0, 10, f"Àkàndé Session — {datetime.now():%Y-%m-%d %H:%M}", ln=True)
    pdf.ln(4)

    for msg in messages:
        if msg["role"] == "system":
            continue
        label = "You" if msg["role"] == "user" else "Àkàndé"
        pdf.set_font("Helvetica", "B", 10)
        pdf.cell(0, 6, label, ln=True)
        pdf.set_font("Helvetica", size=10)
        pdf.multi_cell(0, 5, msg["content"])
        pdf.ln(3)

    pdf.output(output_path)

تعالج multi_cell() التفاف الأسطر والفواصل التلقائية للصفحات، لذا تنتج جلسات بأي طول مستنداً منسقاً جيداً دون منطق ترقيم يدوي. الإخراج ملف متوافق مع PDF/A بلا خطوط مضمّنة تتجاوز مقاييس Helvetica القياسية.

نموذج الخصوصية #

يتحدد حد الخصوصية في Àkàndé بثلاث حقائق:

  1. يُرسَل الصوت إلى Whisper API عبر HTTPS ولا يحتفظ به OpenAI بعد استدعاء API (وفق سياسة استخدام بيانات OpenAI API اعتباراً من فبراير 2024).
  2. تنقل استدعاءات Chat Completions API قائمة messages للجلسة — التي قد تحتوي على كامل سجل المحادثة للجلسات متعددة الأدوار.
  3. تعيش قاعدة بيانات SQLite وملفات PDF كلياً على نظام الملفات المحلي؛ لا يحدث مزامنة خلفية لأي خدمة سحابية.

لحالات الاستخدام التنفيذية التي تتضمن موضوعات حساسة — مناقشات الاندماج والاستحواذ وشؤون الموظفين والاستراتيجية التنظيمية — ينبغي مراجعة سجل الجلسة المرسَل إلى API مقابل سياسة استخدام AI للمنظمة قبل النشر. يمكن استخدام حد max_tokens في موجّه النظام للحيلولة دون الإرسال العرضي لسياق يتجاوز نطاق الإفصاح المقصود.

الأسئلة الشائعة #

هل تحتفظ Àkàndé بسجل المحادثة بعد انتهاء الجلسة؟ تُتجاهل قائمة messages في الذاكرة عند خروج العملية. يُحفظ سجل المحادثة فقط إذا أطلق المستخدم تصدير PDF أو إذا أُضيفت طبقة استمرارية مخصصة. تخزّن ذاكرة SQLite تجزئات الاستعلامات ونصوص الردود، لا سياق المحادثة الكامل.

كيف تعالج الذاكرة الاستعلامات المتشابهة وليست متطابقة؟ تستخدم الذاكرة التجزئة بالمطابقة التامة على سلسلة الاستعلام المُعيَّرة. استعلامان يختلفان بكلمة واحدة سينتجان تجزئتين مختلفتين ويؤديان إلى استدعاءين منفصلين لـ API. التخزين المؤقت الدلالي (باستخدام تشابه التضمينات لمطابقة الاستعلامات المتشابهة تقريباً) يتطلب خطوة بحث متجهي إضافية وليس ضمن التطبيق الأساسي.

ما نموذج GPT الذي تستخدمه Àkàndé افتراضياً؟ الافتراضي هو gpt-4-turbo-preview اعتباراً من فبراير 2024. اسم النموذج معامل تهيئة، لذا يمكن استبداله بأي نموذج استكمال محادثة لـ OpenAI. التبديل إلى gpt-3.5-turbo يخفض تكلفة API بمقدار 20 ضعفاً تقريباً لكل رمز لكنه يقلل جودة الاستدلال للاستعلامات متعددة الخطوات المعقدة.

هل يمكن تخصيص تنسيق تصدير PDF؟ نعم. تقبل دالة تصدير fpdf2 قائمة messages كمدخل مطلوب وحيد، لذا يمكن تغيير الخط والهوامش وحجم الصفحة ومحتوى الرأسية والتسميات عن طريق تعديل دالة التصدير. يدعم fpdf2 أيضاً إضافة الصور والجداول وخطوط Unicode مما يتيح تخطيطات مستندات أكثر ثراءً للمنظمات ذات متطلبات العلامة التجارية المحددة.

المراجع #

  1. OpenAI. Audio Transcriptions — Whisper API. OpenAI Platform Documentation, 2024. https://platform.openai.com/docs/api-reference/audio/createTranscription
  2. OpenAI. Chat Completions API. OpenAI Platform Documentation, 2024. https://platform.openai.com/docs/api-reference/chat/create
  3. Voss, J. et al. fpdf2: Modern PDF generation for Python. GitHub, 2024. https://github.com/py-pdf/fpdf2
  4. SQLite Consortium. SQLite Documentation. sqlite.org, 2024. https://www.sqlite.org/docs.html

آخر مراجعة .

آخر مراجعة .