# Dodaj repozytorium PGDG (oficjalne repo PostgreSQL)
# sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' # wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - # apt update # apt install postgresql-18-pgvector
Klient posgres
# su - postgres
# psql
Sprawdź czy pgvector jest zainstalowany na Twoim serwerze:
> SELECT * FROM pg_available_extensions WHERE name = 'vector'; name | default_version | installed_version | comment --------+-----------------+-------------------+------------------------------ vector | 0.8.2 | 0.8.2 | vector data type and ivfflat and hnsw access methods
Utworzenie usera i bazy danych
> CREATE USER rag_user WITH PASSWORD 'silne_haslo_tutaj'; > CREATE DATABASE rag_db OWNER rag_user; -- Połącz się z nową bazą > \c rag_db -- Zainstaluj rozszerzenie pgvector > CREATE EXTENSION IF NOT EXISTS vector; -- Nadaj uprawnienia > GRANT USAGE ON SCHEMA public TO rag_user; > GRANT ALL PRIVILEGES ON TABLE public.document_chunks TO rag_user; > GRANT USAGE, SELECT ON SEQUENCE public.document_chunks_id_seq TO rag_user;
Tabela chunk-ów dokumentów
> CREATE TABLE document_chunks ( id BIGSERIAL PRIMARY KEY, brand_id INTEGER NOT NULL, source_type VARCHAR(50) NOT NULL, -- 'invoice', 'project', 'faq', itp. source_id INTEGER NOT NULL, -- id rekordu w MariaDB chunk_index INTEGER NOT NULL, -- numer fragmentu w dokumencie content TEXT NOT NULL, -- surowy tekst fragmentu embedding vector(1536), -- OpenAI text-embedding-3-small = 1536 wymiarów meta JSONB, -- dowolne dodatkowe dane (numer faktury, data, itp.) created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW() );
Indeksy
-- Indeks do szybkiego wyszukiwania wektorowego (cosine similarity) -- ivflat (Inverted File Flat) dzieli indexy na grupy/listy > CREATE INDEX ON document_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100); NOTICE: ivfflat index created with little data DETAIL: This will cause low recall. HINT: Drop the index until the table has more data. LUB - inna metoda hnsw > CREATE INDEX ON public.document_chunks USING hnsw (embedding vector_cosine_ops); -- Indeksy pomocnicze > CREATE INDEX ON document_chunks (brand_id); > CREATE INDEX ON document_chunks (source_type, source_id);
Integracja PostgreSQL z CakePHP 5
// Ubuntu/Debian # apt install php-pgsql // sprawdź że jest aktywny # php -m | grep pgsql pdo_pgsql pgsql // Restart Apache # systemctl restart apache2
Połączenie w config/app.php:
return [
'Datasources' => [
'default' => [
'className' => Connection::class,
'driver' => Mysql::class,
'persistent' => false,
'timezone' => 'UTC',
....
],
'vector' => [
'className' => Connection::class,
'driver' => Postgres::class,
'persistent' => false,
'encoding' => 'utf8',
'timezone' => 'UTC',
],
],
];
Połączenie w config/app_local.php:
return [
'Encryption' => [
'key' => 'twój_klucz_base64',
],
'Datasources' => [
'default' => [
'host' => 'localhost',
'username' => 'mariadb_user',
'password' => 'mariadb_pass',
'database' => 'twoja_aplikacja',
],
'vector' => [
'host' => '127.0.0.1',
'port' => '5432',
'username' => 'rag_user',
'password' => 'silne_haslo_tutaj',
'database' => 'rag_db',
],
],
];
Model DocumentChunksTable wskazujący na PostgreSQL:
namespace App\Model\Table; use Cake\ORM\Table; class DocumentChunksTable extends Table { public static function defaultConnectionName(): string { return 'vector'; } public function initialize(array $config): void { parent::initialize($config); $this->setTable('document_chunks'); $this->setPrimaryKey('id'); $this->addBehavior('Timestamp', [ 'events' => [ 'Model.beforeSave' => [ 'created_at' => 'new', 'updated_at' => 'always', ], ], ]); } }
Test połączenia — dodaj tymczasowo w dowolnym kontrolerze w akcji:
$conn = \Cake\Datasource\ConnectionManager::get('vector'); $result = $conn->execute('SELECT version()')->fetch(); debug($result); exit;
Druga wersja która zadziałała
-- Sprawdź bazę
SELECT current_database();
-- Powinno zwrócić rag_db
-- Jeśli nie - przełącz się
\c rag_db
-- Utwórz rozszerzenie
CREATE EXTENSION IF NOT EXISTS vector;
-- Utwórz tabelę
CREATE TABLE public.document_chunks (
id BIGSERIAL PRIMARY KEY,
brand_id INTEGER NOT NULL,
source_type VARCHAR(50) NOT NULL,
source_id INTEGER NOT NULL,
chunk_index INTEGER NOT NULL,
content TEXT NOT NULL,
embedding vector(1536),
meta JSONB,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
GRANT USAGE ON SCHEMA public TO rag_user;
GRANT ALL PRIVILEGES ON TABLE public.document_chunks TO rag_user;
GRANT USAGE, SELECT ON SEQUENCE public.document_chunks_id_seq TO rag_user;
CREATE INDEX ON public.document_chunks
USING hnsw (embedding vector_cosine_ops);
CREATE INDEX ON public.document_chunks (brand_id);
CREATE INDEX ON public.document_chunks (source_type, source_id);