تخطَّ إلى المحتوى
kazma.
البحث في كاظمه والتوثيق
EN

التحويل إلى Postgres وSaaS

تعدد مستخدمي SaaS + التحويل الكامل إلى Postgres

Section titled “تعدد مستخدمي SaaS + التحويل الكامل إلى Postgres”

ما الذي ينتقل إلى Postgres عند ضبط KAZMA_DATABASE_URL

Section titled “ما الذي ينتقل إلى Postgres عند ضبط KAZMA_DATABASE_URL”
المخزنالجدول(ات)الوحدة
الضبط / الإعدادات / مستخدمو المنصةkazma_settingsconfig_store.py
جلسات الدردشةkazma_chat_sessionssession_manager.py
مهام السرب + المقاييسkazma_swarm_tasks, kazma_swarm_worker_metricstask_store.py
نقاط تفتيش LangGraphمخطط LangGraph الداخليAsyncPostgresSaver، يفتحه kazma_core/checkpoints_pg.py (الوكيل والبوابة). يُقلَّم منذ 2026-09-27: كل دردشة تحتفظ بأحدث 200 نقطة تفتيش لها، والدردشة الخاملة منذ checkpoints.retention_days (الإعدادات ← النظام ← سجل خطوات الدردشة، الافتراضي 30؛ و0 يحتفظ بالكل) تحتفظ بأحدث 10 نقاط لها
جلسات الويبما تزال مفاتيح ConfigStore ‏(وعبر Postgres أيضًا من خلال الإعدادات)web_sessions.py

يبقى SQLite الافتراضي عند عدم ضبط عنوان قاعدة بيانات (الاختبارات، والعقدة المحلية الواحدة).

ضبط KAZMA_DATABASE_URL يرقّي أيضًا الاسترجاع الكثيف V2 من sqlite-vec إلى pgvector في القاعدة نفسها (الجدول kazma_memory — وهو ضبط memory.backends.vector.collection — وcosine، وفهرس HNSW)، إن كان ذلك الـ Postgres يستطيع حمل المتجهات. ويبقى المخزن المعرفي (memory_state.db) على SQLite حتى تضبط KAZMA_MEMORY_STATE_ROLE=primary.

لا حاجة إلى pgvector ولا إلى Qdrant: بدونهما يكون البحث المعنوي في الذاكرة دقيقًا على كل ذاكرة، محليًا (sqlite-vec أو NumPy، نحو 1 ms لكل 1,000 ذاكرة).

يجب أن يأتي الخادم بالامتداد. صورة postgres:16-alpine — وهي الصورة في docker-compose.postgres.yml وdocker-compose.ha.yml — لا تأتي به؛ استخدم pgvector/pgvector:pg16 (أو Postgres مُدارًا يقدّم pgvector).

  1. تنشئ كاظمه الامتداد والجدول عند أول استخدام عندما يسمح دورها بذلك. وإلا فبصفتك مستخدمًا أعلى: CREATE EXTENSION IF NOT EXISTS vector; في قاعدة بيانات كاظمه، وامنح الدور CREATE على مخططها.
  2. تفحص كاظمه عند الإقلاع وعند كل بحث في الذاكرة (مخزّنًا مؤقتًا دقيقة واحدة):
    • اختيار pgvector تلقائيًا (بلا اختيار صريح) على Postgres بلا الامتداد: سطر INFO واحد، وتبقى الذاكرة على sqlite-vec، وتعرض الإعدادات ← الذاكرة Vector: full (local) مع السبب.
    • pgvector مُختار في الإعدادات لكنه غير قابل للاستخدام (لا امتداد، الدور لا يستطيع إنشاءه، جدول بحجم متجه آخر، خادم متوقف): تحذير WARNING واحد يسمّي الإصلاح، ولافتة الإعدادات تقول الشيء نفسه.
    • Test vector في الإعدادات ← الذاكرة يشغّل الفحص نفسه عند الطلب. حين اختير pgvector تلقائيًا والامتداد غائب، يختبر المخزن المستخدم فعلًا (sqlite-vec المحلي) ويبلّغ Vector OK مع ملاحظة تشرح لماذا لا يُستخدم pgvector؛ أما pgvector اخترته أنت ولا يمكنه العمل فيبلّغ Vector failed مع الإصلاح.
  3. أعد بناء التضمينات مرة واحدة إن كان لديك سجل بالفعل: الإعدادات ← الذاكرة ← إعادة بناء التضمينات (upsert إلى pgvector).
  4. تغيير حجم المضمِّن: الجدول مُقاس على المضمِّن ولا يُعاد تقاسه أبدًا. وجّه memory.backends.vector.collection إلى اسم جديد، ثم أعد بناء التضمينات.
  5. مفتاح الإيقاف: KAZMA_PGVECTOR=0 ‏(sqlite-vec عمدًا، بلا فحص). وQdrant المُصرَّح به صراحةً في الإعدادات لا يُستبدل أبدًا.

نقل قاعدة بيانات postgres:16-alpine قائمة إلى pgvector/pgvector:pg16: فرّغ ثم استرجع، ولا تكتفِ بتبديل الصورة على الوحدة نفسها. فـ Alpine تستخدم musl وصورة pgvector تستخدم glibc؛ وفهارس النص المبنية تحت ترتيب أبجدي واحد تصبح خارج الترتيب تحت الآخر. مع إيقاف كاظمه، فرّغ القاعدة كاملة من الحاوية القديمة (pg_dump -Fc)، واسترجعها بـ pg_restore داخل حاوية pgvector على وحدة تخزين جديدة، ووجّه KAZMA_DATABASE_URL إليها ثم شغّل كاظمه. أبقِ الوحدة القديمة حتى تشتغل الوحدة الجديدة مدةً من الوقت. (scripts/pg_backup.py يفرّغ جداول كاظمه نفسها فقط — وهذا صحيح لقاعدة مشتركة، لا لنقل قاعدة كاملة.)

الاسترجاع بـ Postgres أساسيًا (state.role=primary) هو ILIKE + pgvector RRF، وليس ILIKE وحده.

  1. ثبّت الإضافات: pip install -e ".[postgres]"
  2. شغّل Postgres:
    نافذة طرفيّة
    docker compose -f docker-compose.postgres.yml up -d db
  3. هاجر بـكل المخازن:
    نافذة طرفيّة
    export KAZMA_DATABASE_URL=postgresql://kazma:PASSWORD@localhost:5432/kazma
    python scripts/migrate_sqlite_to_postgres.py --data-dir kazma-data
  4. شغّل كاظمه بالعنوان نفسه (يضبطه compose تلقائيًا).
  5. فحص سريع:
    • تسجيل الدخول (مستخدم / سر / OIDC)
    • تاريخ الدردشة يبقى عبر إعادة التشغيل
    • قائمة مهام السرب
    • الإعدادات تُحفَظ
  • /login — تبويبات المستخدم · السر · SSO
  • الإعدادات ← الحساب — المستخدمون + المستأجرون (المشرف)
  • الترويسة — شارة الدور + تسجيل الخروج

إنشاء مشرف:

from kazma_core.security.platform_rbac import create_local_user
create_local_user("admin", "long-password-here", role="admin")
نافذة طرفيّة
KAZMA_DATABASE_URL=postgresql://…
KAZMA_DB_BACKEND=postgres # optional force
KAZMA_PRODUCTION=1
KAZMA_VAULT_KEY=…
KAZMA_SECRET=…
KAZMA_PUBLIC_URL=https://…
# OIDC optional
KAZMA_OIDC_ISSUER=…
KAZMA_OIDC_CLIENT_ID=…
KAZMA_OIDC_CLIENT_SECRET=…

دور قاعدة بيانات ليس مستخدمًا خارقًا

Section titled “دور قاعدة بيانات ليس مستخدمًا خارقًا”

تحتاج كاظمه إلى أن تملك جداولها وحدها لا أكثر. يستطيع تسجيل دخول المستخدم الخارق قراءة كل قاعدة بيانات على الخادم وتغييرها وتشغيل برامج على مضيف قاعدة البيانات، لذا يحذّر كل إقلاع حين تسجّل كاظمه دخولها بوصفها واحدًا منهم ([security] Kazma signs in to Postgres as kazma, a superuser). وPOSTGRES_USER في إعداد Docker هو أول مستخدم خارق على الخادم، وهكذا ينتهي أغلب التثبيتات على هذه الحال.

deploy/postgres/kazma_app_role.sql ينشئ الدور kazma_app (ليس مستخدمًا خارقًا، ولا يجوز له إنشاء أدوار، ويجوز له إنشاء قواعد بيانات لنسخة بروفة الاستعادة الأسبوعية المؤقتة) ويعطيه قاعدة البيانات ومخططها وكل جدول وتسلسل ودالة. يعمل في معاملة واحدة ويمكن تشغيله مجددًا. يرفض Postgres الصيغة الأقصر REASSIGN OWNED BY <the first superuser>، لذا يسمّي السكربت كل كائن. ويشغّل tests/test_pg_app_role.py إعداد كاظمه لكل مخزن Postgres، وتفريغًا، وبروفة الاستعادة بالدور الجديد.

  1. شغّل السكربت بالمستخدم الخارق، في قاعدة بيانات كاظمه:

    نافذة طرفيّة
    docker exec -i kazma-db-win psql -U kazma -d kazma -v ON_ERROR_STOP=1 < deploy/postgres/kazma_app_role.sql

    ينتهي بـ COMMIT. وإن توقف عند lock timeout فقد أمسك استعلام طويل بجدول: شغّله مجددًا حين تكون كاظمه خاملة. تواصل كاظمه عملها طوال ذلك: فهي ما تزال تسجّل دخولها بالمستخدم الخارق حتى إعادة التحميل.

  2. أنشئ كلمة مرور من حروف وأرقام و- و_ (أي شيء آخر يجب ترميزه بالنسبة المئوية في العنوان):

    نافذة طرفيّة
    python -c "import secrets; print(secrets.token_urlsafe(24))"

    وضع العنوان الجديد في .env، مبقيًا على المضيف والمنفذ واسم قاعدة البيانات: KAZMA_DATABASE_URL=postgresql://kazma_app:<password>@127.0.0.1:5433/kazma.

  3. أعطِ الدور كلمة المرور نفسها، مقروءةً من .env حتى لا تُكتب ولا تُعرض ولا تُحفظ في سجل صدفتك (PowerShell):

    نافذة طرفيّة
    $line = (Get-Content <install>\.env | Where-Object { $_ -like 'KAZMA_DATABASE_URL=*' }) -replace '^KAZMA_DATABASE_URL=',''; $pw = ([uri]$line.Trim().Trim('"')).UserInfo.Split(':',2)[1]; "ALTER ROLE kazma_app PASSWORD '$pw';" | docker exec -i kazma-db-win psql -U kazma -d kazma -v ON_ERROR_STOP=1

    يطبع ALTER ROLE. لا تستخدم موجّه \password في psql: فكلمة المرور الملصقة فيه قد تصل فارغة، فيمسح Postgres عندئذ كلمة مرور الدور بمجرد إشعار (حيًّا، 2026-11-03).

  4. تحقّق من تسجيل الدخول قبل إعادة التحميل، بـ Python الخاص بالتثبيت (<install> هو المجلد الذي ثُبّتت فيه كاظمه):

    نافذة طرفيّة
    <install>\.venv\Scripts\python.exe <install>\scripts\check_database_login.py

    يسجّل الدخول كما ستفعل كاظمه ويطبع OK: Kazma signs in to kazma as kazma_app (not a superuser).، أو NOT OK: مع سبب Postgres. أما فحص يجري بـ psql داخل حاوية قاعدة البيانات فلا يثبت شيئًا: فالحاوية تقبل تسجيلات دخول loopback بلا كلمة مرور.

  5. إن كانت مرآة الذاكرة مفعّلة (الإعدادات -> الذاكرة -> خلفيات الذاكرة -> الحالة المشتركة مضبوطة على Postgres)، فاضبط Postgres DSN الخاص بها على العنوان نفسه واضغط حفظ الخلفيات. فهي تحتفظ بنسختها الخاصة من العنوان.

  6. أعد التحميل: python scripts/service/kazma_guard.py --reload --when-idle.

  7. تحقّق: لم يعد سجل الإقلاع يحمل تحذير المستخدم الخارق، ويجيب /health/ready بالرمز 200. وإن انتظر الخادم قاعدة بياناته بدلًا من ذلك، فسجله يسمّي سبب Postgres ([PostgresPool] Postgres refused the password for kazma_app ...).

للتراجع، أعد العنوان القديم إلى .env (وإلى مرآة الذاكرة) وأعد التحميل؛ فما يزال المستخدم الخارق يستطيع استخدام كل جدول. أبقِ المستخدم الخارق kazma في مكانه: فهو ما تدير به الخادم.

النسخ الاحتياطي والتعافي من الكوارث

Section titled “النسخ الاحتياطي والتعافي من الكوارث”

تنسخ كاظمه جداول Postgres الخاصة بها احتياطيًا بشكل آلي (أُضيف ذلك بعد حادثة 2026-08-14 التي حذف فيها تطبيق آخر جداول كاظمه من قاعدة بيانات مشتركة):

  • كل 6 ساعات، آلي: تُفرِغ حلقة النسخ/التصدير الجداول المذكورة في kazma_core.db.pg_backup.KAZMA_PG_TABLES بالضبط إلى {kazma-data}/backups/pg/pg_shared_<epoch>.dump (صيغة -Fc المخصصة، كتابة ذرّية، تحقق بالبصمة السحرية؛ يُحتفظ بثلاثة تفريغات محلية، وrestic يحفظ التاريخ → backups.pg.retention / KAZMA_PG_BACKUP_RETENTION). أول تفريغ بعد ~2 دقيقة من الإقلاع. التمرين العميق الأسبوعي يسترجع أحدثها إلى قاعدة بيانات مؤقتة، ويفحصها ثم يُسقطها (مفعّل افتراضيًا منذ 2026-09-27؛ التعافي من الكوارث).
  • تفريغ يدوي الآن: python scripts/pg_backup.py backup
  • الاسترجاع: python scripts/pg_backup.py restore --latest (--file <name>، و--dry-run، وlist). يسترجع جداول كاظمه نفسها فقط — لا يُمَسّ تطبيق خارجي يشارك القاعدة أبدًا.
  • حارس الإقلاع: إن غاب جدول مطلوب عند الإقلاع، يسجّل الخادم CRITICAL مع أمر الاسترجاع بدل العرج مع أخطاء UndefinedTable.
  • مفتاح الإيقاف: KAZMA_PG_BACKUP_ENABLED=0 (أو backups.pg.enabled=false).
  • التفريغ مُرشَّح حسب الجداول عمدًا — وليس تفريغ قاعدة كاملة أبدًا — فلا تتسرب بيانات أي تطبيق خارجي إلى نسخ كاظمه الاحتياطية.

استخدم docs/ops/DISASTER_RECOVERY.md مع pg_dump لـ Postgres. بعد الاسترجاع، يجب أن تتطابق الأسرار (KAZMA_VAULT_KEY, KAZMA_SECRET).