البحث المتّجهي والذكاء الاصطناعي مع pgvector

خزّن التضمينات (embeddings) في قاعدة بيانات PostgreSQL الخاصة بك وابحث فيها بالمعنى - تفعيل pgvector، ومثال يعمل، والفهارس، وما ينبغي التحقّق منه أولًا.

ميزة الذكاء الاصطناعي - بحث دلالي، أو «اعثر على ما يشبه»، أو استرجاع لروبوت محادثة، أو توصية - تحتاج إلى مكان تُحفظ فيه التضمينات (embeddings) وإلى طريقة للعثور على أقربها. وعلى ISOGrid هذا المكان هو قاعدة بيانات PostgreSQL العادية لديك مع إضافة pgvector: المتّجهات موجودة بجانب الصفوف التي تصفها، في المعاملات نفسها، والنسخ الاحتياطية نفسها، وقواعد الوصول نفسها. لا توجد قاعدة بيانات ثانية تشغّلها.

تفعيله

افتح قاعدة البيانات، وانتقل إلى عرض الإضافات فيها، وفعّل pgvector. المنصّة تُنشئ الإضافة لك، لأن إنشاءها يتطلّب مستخدمًا أعلى، ودورك أنت ليس كذلك.

أمران تتحقّق منهما إن ظهر pgvector كغير متوفّر على الخادم:

  • خادم قاعدة بيانات خاصّ بك أُنشئ قبل وصول pgvector يعمل بصورة أقدم. أعد نشر المثيل؛ فينتقل إلى الصورة الحالية وتصبح الإضافة متاحة. ولا تُمسّ بياناتك.
  • قاعدة بيانات على خادم مشترك تحصل على pgvector حين يحمله الخادم المشترك في المنطقة. فإن لم يكن خادمك يحمله بعد، اطلب خادم قاعدة بيانات خاصًّا بك، وهو يحمله دائمًا.

مثال يعمل

-- One row per document, with its embedding. 1536 is the size of the vectors
-- your embedding model returns; use the size of yours.
CREATE TABLE documents (
    id        bigserial PRIMARY KEY,
    title     text NOT NULL,
    body      text NOT NULL,
    embedding vector(1536)
);

-- Store a document with the vector your embedding model returned for it.
INSERT INTO documents (title, body, embedding)
VALUES ('Refund policy', 'Refunds are made within 14 days...', '[0.012, -0.034, ...]');

-- The five documents closest in meaning to a question: embed the question with
-- the same model, then order by distance. <=> is cosine distance.
SELECT id, title
FROM documents
ORDER BY embedding <=> '[0.008, -0.021, ...]'
LIMIT 5;

التضمينات نفسها تأتي من النموذج الذي تختاره وتستدعيه من تطبيقك؛ أمّا قاعدة البيانات فتخزّنها وتبحث فيها. استعمل نموذجًا واحدًا للعمود الواحد: المتّجهات الآتية من نماذج مختلفة لا يمكن مقارنتها.

جعله سريعًا

دون فهرس، يقرأ كلّ بحث الجدول كلّه، وهذا مقبول حتى بضع عشرات الآلاف من الصفوف. وبعد ذلك، أضف فهرسًا تقريبيًا:

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);

طابِق صنف العوامل (operator class) مع العامل الذي تبحث به: vector_cosine_ops لـ <=>، و vector_l2_ops لـ <->، و vector_ip_ops لـ <#>. الفهرس المبنيّ لأحدها لا يستعمله عامل آخر. وبناؤه يأخذ ذاكرة ووقتًا بما يتناسب مع الجدول؛ فأنجزه على قاعدة بيانات بحجم مناسب لذلك، لا على أصغر قاعدة.

التصفية والدمج

لأنها PostgreSQL، البحث المتّجهي بند واحد من استعلام عادي. قيّد بحسب العميل أو التاريخ أو الحالة في التعليمة نفسها:

SELECT id, title
FROM documents
WHERE customer_id = 42 AND published
ORDER BY embedding <=> '[...]'
LIMIT 5;

وهذا هو السبب الرئيسي لإبقاء المتّجهات في قاعدة بياناتك بدل مخزن متّجهات منفصل: التصفية والبحث لا يمكن أن يختلفا حول الصفوف الموجودة.

ما ينبغي معرفته قبل الاعتماد عليه

  • النسخ الاحتياطية تشمل المتّجهات، مثل كلّ ما في قاعدة البيانات.
  • يفشل تعطيل pgvector ما دام عمود أو فهرس يستعمله، مع شرح PostgreSQL نفسه.
  • قاعدة البيانات عالية التوافر تنسخ المتّجهات نسخًا متماثلًا مع البقيّة؛ والبحث الكثيف القراءة يمكن توجيهه إلى النسخ.

ماذا تقرأ بعد ذلك