PostgreSQL + pgvector і SQL Server 2025 як вектарныя сховішчы
PostgreSQL 18 з pgvector супраць SQL Server 2025 як вектарных сховішчаў: як кожны працуе ў ролі вектарнай базы і як усталяваць pgvector на Windows.
Крыніцы: 🗄️ PostgreSQL + pgvector and SQL Server 2025 as Vector Stores for RAG — A Practitioner’s Guide
Наступныя прытамлівкі паказваюць практычны падход да кнігі «PostgreSQL + pgvector і SQL Server 2025 як сховішчы вектараў для RAG — Практычныя наставы». Акцэнт ставіцца на умовы, пераконтроўванні і месца для коду, які можна легка адразу застосавіць, а не на мотывацыйныя аспекты.
Ландшафт баз дадзеных вектароў у 2025 году
Калі працюеце над этапам «Ландшафт баз дадзеных вектароў», спачатку запісайце умовы: неабходныя вхідныя даны, сігнал успеху і тое, што выходзіць у разе частковай нявыполнення. Такі спіс дапамагае заставіць пазнейшыя змены коду быць чыстымі. Расследзіце гэты этап як угоду межа вхіднымі данымі і перакантроўванымі выходнымі рэзультатамі. Дайце назвы элементам, задаць крытэрыя успеху і не падпішыцеся на тыхое частковае завершэння. Замерьце рывень вярнага аднаходжэння на фіксаванай сэтцы запытаў прычым рэгулюванні прамптав. Частае змены прамптав рэдка калі вярнуць слабыя рэзультаты аднаходжэння.
-- Find the most relevant chunks, but only from documents
-- belonging to enterprise-tier customers — a single SQL query
SELECT c.content, 1 - (c.embedding <=> query_vec) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC
LIMIT 5;
Полны стак
Калі працюеце над стадзіяй The Full Stack, спачатку запісайце контракт: неабяжлівыя даны, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі спіс пераконвае ў тым, што пазнейшыя змены коду будуць чыстымі. Запісвайце час выканання і кост токена або запыту праза функцыйнальнымі рэзултатамі. Відразы коста з самага пачатку запобегае неспакоўным рахункам, калі працэс пераходзіць з дэмавайнтару ў спяльныя асяроддзя. Замерьце рэткі адзыв на фіксаваныя наборы запытаў прычым рэгулюванні прапазаў. Частыя змены прапазаў рэдка калі вядуць да павышэння якасці адзыва.
Частка 1 — PostgreSQL 18 + pgvector
Калі працуеце над першым этапам PostgreSQL 18, спачатку запісайце умовы вікорыстання: неабяжлівыя даны, сігнал успеху і тое, што выходзіць пад частыя неудачы. Такі спіс дапамагае заліцварваць будучыя змены ў кодзе. Храніце настройкі парадульна ад коду прыемліка. Файлы сераўнавання, хранальнікі секрэтных даных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаць пераглядаць іх без неабяжлівага чытання всей структуры. Перад налаштаваннем запітоў пераканайцеся, што сістэма правільна адзыходзіць ад заданых пытанняў, вымерваючы рэгрэсію. Частае змены запітоў рэдка калі выправляюць слабыя аспекты пошуку.
Інсталяцыя pgvector на Windows
Калі працюеце над этапам з інсталяцыі pgvector на Windows, спачатку запісайце шэраг правіла: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі шэраг дапамагае заліцьварыць пазнейшыя змены ў кодзе. Дакументавайце як правільны, так і альтэрнатыўны шляхы працы. Практыка перапрыбуткі, людзкі контроль і обработка некорэктных паведамленняў ёсць часткай продукту, а не элементамі пазнейшага доўрабкі. Перад налаштаваннем запітоў пераканайцеся, што сістэма правільна адпавядае на фіксованы набор запітанняў. Частае зміненне запітоў рэдка калі вярна выправляе слабыя аспекты адзысквання інформацыі.
set "PGROOT=C:\Program Files\PostgreSQL\18"
cd %TEMP%
git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git
cd pgvector
nmake /F Makefile.win
nmake /F Makefile.win install
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres --name pgvector pgvector/pgvector:pg18
conda install -c conda-forge pgvector
Стварэнне табліц у pgAdmin
Калі працуеце над этапам «Стварэнне таблыцаў» у pgAdmin, спачатку запісайце умовы вярбавання: неабходныя даныя, сігнал успеху і тое, што выходзіць на частым неудачам. Такі спіс дапамагае заліцварыць пазнейшыя змены ў кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выходзіць, неудача должна адносіцца да адной відпаведальнасці, а не да заплутанага ланца задач. Перад налаштаваннем запітоў пераканайцеся, наскількі эфектыва ўраджэнне адпаведных адказоў на фіксованыя запітанні. Частае змены запітоў рэдка калі вярнуе хорашую якасць адказоў. Калі працуеце над этапам «Стварэнне таблыцаў» у pgAdmin, спачатку запісайце умовы вярбавання: неабходныя даныя, сігнал успеху і тое, што выходзіць на частым неудачам. Такі спіс дапамагае заліцварыць пазнейшыя змены ў кодзе. Запісвайце час выканання і кост токенаў або запытаў разам з функцыйнальнымі рэзултатамі. Відразувыя даныя пра косты запобегаюць неспакойным рахункам, калі праця пераходзіць з дэмавайнага рэжыма ў спяльныя асяроддзя.
-- Enable the pgvector extension
CREATE EXTENSION IF NOT EXISTS vector;
-- Main chunks table with VECTOR(768) column
CREATE TABLE IF NOT EXISTS rag_chunks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source TEXT NOT NULL,
chunk_index INTEGER NOT NULL,
content TEXT NOT NULL,
file_hash TEXT,
ingested_at TIMESTAMPTZ DEFAULT NOW(),
embedding vector(768) -- pgvector native type
);
-- HNSW index for approximate cosine similarity search
CREATE INDEX IF NOT EXISTS idx_rag_chunks_embedding
ON rag_chunks USING hnsw (embedding vector_cosine_ops);
-- Source filter index
CREATE INDEX IF NOT EXISTS idx_rag_chunks_source
ON rag_chunks (source);
-- Staleness registry
CREATE TABLE IF NOT EXISTS rag_staleness (
doc_name TEXT PRIMARY KEY,
file_hash TEXT NOT NULL,
chunk_count INTEGER,
ingested_at TIMESTAMPTZ DEFAULT NOW(),
version INTEGER DEFAULT 1
);
-- CDC chunk registry
CREATE TABLE IF NOT EXISTS rag_chunk_registry (
doc_name TEXT NOT NULL,
chunk_hash TEXT NOT NULL,
chunk_id TEXT NOT NULL,
PRIMARY KEY (doc_name, chunk_hash)
);
-- Conversation sessions
CREATE TABLE IF NOT EXISTS rag_sessions (
session_id TEXT PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
model TEXT,
embed_model TEXT,
turn_count INTEGER DEFAULT 0
);
-- Conversation turns
CREATE TABLE IF NOT EXISTS rag_turns (
id SERIAL PRIMARY KEY,
session_id TEXT REFERENCES rag_sessions(session_id) ON DELETE CASCADE,
role TEXT NOT NULL CHECK (role IN ('user','assistant')),
content TEXT NOT NULL,
sources TEXT[],
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_rag_turns_session
ON rag_turns (session_id, created_at);
Залежнасці Python
Этап залежнасцяў Python работае наякша, калі яго розглядаць як мерыябельную плошчу. Запісаць адны ідэальны прыклад працы, адзін прыклад неудачы і прыметку па абратанню змян перш чым расшырваць сферу дзеяння. Канфігурацыю трэба заставіць пазначкай за межамі коду прыемліка. Файлы сяродавішча, хранільнікі секрэтных дадзеных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаць адбавляць контроль без неабяжнага чытання всіх элементаў. Зафіксаваць версію інтэрпрэтара і файл з правіламі залежнасцяў перш чым пачынаць розбіраць цыклы. Разніця межаў лептопа і асяроддзя CI ёсць найчастэйшай прычыной тых, чаму дэманстраціі API не працуюць.
uv add google-genai pypdf pgvector psycopg2-binary python-dotenv huggingface_hub
Наладжэнне з’ѐеднання (Клетка 2)
Этап «Стварэнне з’язку, клетка 2» працюе наяўней, калі яго спрыяваць як мерыемую паверхню. Зафіксавайце адна ідэальная транскрыпцыю, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расшырваць масштабы. Дакументавайце як успішны, так і вярнучыся шляхы. Перапрыбуткі, людзкія контрольныя пункты і обработка некорэктных паведамленняў ёсць часткай продукту, а не наступным этапам дорабкі. Раздзеліце політыку частковай обработкі дадзеных ад політыкі ўтрымання іх. Змена адной з яных не должна вымагаць перапісвання другой, калі зменяюцыся показнікі якосці.
import psycopg2
from pgvector.psycopg2 import register_vector
PG_HOST = "localhost"
PG_PORT = 5432
PG_DB = "postgres"
PG_USER = "postgres"
PG_PASSWORD = os.environ.get("PG_PASSWORD", "postgres")
def get_pg_conn():
"""Returns a fresh PostgreSQL connection with pgvector registered."""
conn = psycopg2.connect(
host=PG_HOST, port=PG_PORT,
dbname=PG_DB, user=PG_USER, password=PG_PASSWORD
)
register_vector(conn) # tells psycopg2 how to handle vector type
return conn
Зберагчыце інтэграцыі (Клетка 6)
Этап 6 клеткі «Зберагчык эмбеддзінга» працюе наявнасць калі яго расследжваць як меравальную паверхню. Запісаце адна ідэальная транскрыпцыя, адзін прыклад неудачы і прыметку па адвярненні працы перш чым расшырваць масштаб. Воліце маленькія, тэставальныя елементы замест большых скрыптов. Калі якісь крок не выйшае, прычына неудачы павінна вказваць на адную адпаведальнасць, а не на заплутаны ланцужок задач. Раздзеляйце правілы часткавання дадзеных і правілы ўтрымання іх. Змена адных не павінна вымагаць перапісвання іншых, калі змянююцца паказнікі якосці.
import numpy as np
def store_in_postgres(chunks, embeddings, doc_name) -> int:
conn = get_pg_conn()
cur = conn.cursor()
for i, (chunk, emb) in enumerate(zip(chunks, embeddings)):
cur.execute("""
INSERT INTO rag_chunks (source, chunk_index, content, embedding)
VALUES (%s, %s, %s, %s)
""", (doc_name, i, chunk, np.array(emb))) # np.array → pgvector handles serialization
conn.commit()
conn.close()
return len(chunks)
Этап 6 клеткі «Зберагчык эмбеддзінга» працюе наявнасць калі яго расследжваць як меравальную паверхню. Запісаце адна ідэальная транскрыпцыя, адзін прыклад неудачы і прыметку па адвярненні працы перш чым расшырваць масштаб. Запісвайце часы выканання і кост токеноў або запітаў разам з функцыональнымі рэзультатамі. Відразлівасць костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмовай асяроддзя ў спяльныя сераўы.
Утрыманне дадзеных за дапамою косінай сэмплярнасці (Клетка 6, продаж)
Для этапы адаптавання за допамогай косайн-сэмілярнасці неабходна прадзеяванне вхідных дадзеных, апаведчэнне адпавядаючага апарату і вакладкі завершэння працы перад змянайом коду. Апараты должны магчымаць перапрацоўкю этапу з вядомага пункту контролю, не прыпускаючы невідомых станоў. Канфігурацыю трэба захаваць праз ваняродны код аплікацыі. Файлы сераўіса, хранільнікі секрэтных дадзеных і флагі функцыйяў должны знаходзіцца ў аднам месцы, якое апараты можаць пераглядаць, не чытаючы весь граф. Неабходна апаведчаць тыя часткі тэксту, якія фактычна лежалі в основе адпаведзення. Без цых апаведчэнняў апараты не можаць разлічыць галюцынацію ад працягу індэксавання.
def retrieve_context(query: str) -> list[dict]:
query_embedding = embed_query(query)
conn = get_pg_conn()
cur = conn.cursor()
cur.execute("""
SELECT
content,
source,
chunk_index,
ingested_at,
1 - (embedding <=> %s) AS cosine_score -- <=> is cosine distance
FROM rag_chunks
ORDER BY embedding <=> %s -- sort ascending (smallest distance first)
LIMIT %s
""", (np.array(query_embedding), np.array(query_embedding), TOP_K))
rows = cur.fetchall()
conn.close()
return [{"text": r[0], "source": r[1], "chunk_index": r[2],
"ingested_at": str(r[3]) if r[3] else "",
"score": round(float(r[4]), 4)} for r in rows]
CDC з pgvector — шаблон Upsert
Для CDC з фазай Upsert пакета pgvector неабяжна ўзначыць вхідныя даны, адпаведальнага за выкананне крока і крытэрыя завершэння пры зміне коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю, не падозрываючы прыхованы стан. Неабяжна задокументаваць як шлях успеху, так і шлях вярнення да нормальнага стану. Перапрыбуткі, людзкія перакрыцця і апрацоўка некорэктных паведамленняў ёсць часткай продукту, а не дадатковымі удосконаленнямі. Паказваць часткі тэксту, якія фактычна лежаць у падставе адпаведнай адказы. Без ціх цытатаў аператары не можуць розразліці галюцынацію ад прычыны, зв’язанай з індэксаванням.
cur.execute("""
INSERT INTO rag_staleness (doc_name, file_hash, chunk_count, version)
VALUES (%s, %s, %s, 1)
ON CONFLICT (doc_name) DO UPDATE SET
file_hash = EXCLUDED.file_hash,
chunk_count = EXCLUDED.chunk_count,
ingested_at = NOW(),
version = rag_staleness.version + 1;
""", (doc_name, file_hash, chunk_count))
Частка 2 — SQL Server 2025 (Native Vector)
Для другага этапа SQL Server неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крытэрыя завершэння пры зміне коду. Аператары должны магчыма ўвайсці крок з вядомага пункта контролю, не спрабоўваючы з’ясаваць захаваны стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі крок не выйшоў, прычына нехарактэрнага рэзультата должна вказываць на адзін конкрэтны элемент, а не на заплутаны ланцуг задач. Наводзіце тыя часткі тэксту, якія фактычна лежаць у падставе адпаведнай адказы. Без ціх цытатаў аператары не зможаць розразліць галюцынацыю ад працягу індэксавання.
Чаму SQL Server 2025 не патрабуе расшырэнняў
Для этапы «Чаму SQL Server 2025» неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтыяры завершэння пры зміне коду. Аперацыйныя працавнікі должны магчымае запускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Спрыяйце цій этапы як даговору межа вхіднымі данымі і перакананымі выходнымі рэзультатамі. Даўце назвы артыфактам, узначыць перакананні успеху і адмовіцеся ад тыхняга частковага завершэння без паведамлення. Цітуйце тыя часткі, якія фактычна сталі падставай для адпаведнай адказы. Без цітаў аперацыйныя працавнікі не зможуць адразніць галюцынацыю ад прасоўкі індэксаў.
Настройка ў SSMS
У стадыі налаштавання ў SSMS неабходна пазначыць параметры вхідных дадзеных, адпаведальнага за выкананне крока і крытэрыя завершэння пры перадзеіснавленні коду. Аператары должны магчымае перадзеіснавляць крок з вядомага пункта контролю, не прыпускаючы стану, які застаецца незрозумелым. Запісваюць час выканання і вартасць токена або запыту разам з функцыйнальнымі рэзултатамі. Відразы вартасці з самага пачатку запобегае неспакоўным рахункам, калі працэс пераходзіць з дэмовай асяроддзя ў спакульную. Прыкладзіце часткі тэксту, якія фактычна сталі падставай для адпаведнай адказы. Без ціх цытатаў аператары не зможаць разлічыць галюцынацію ад прасоўкі ў індэксах.
-- Batch 1: Run this first — must commit before vector objects are recognized
ALTER DATABASE SCOPED CONFIGURATION SET PREVIEW_FEATURES = ON;
-- Batch 2: Create all tables
CREATE TABLE rag_chunks (
id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
source NVARCHAR(500) NOT NULL,
chunk_index INT NOT NULL,
content NVARCHAR(MAX) NOT NULL,
file_hash NVARCHAR(64),
ingested_at DATETIME2 DEFAULT GETUTCDATE(),
embedding VECTOR(768) -- native SQL Server 2025 type
);
CREATE INDEX idx_rag_source ON rag_chunks(source);
CREATE TABLE rag_staleness (
doc_name NVARCHAR(500) PRIMARY KEY,
file_hash NVARCHAR(64) NOT NULL,
chunk_count INT,
ingested_at DATETIME2 DEFAULT GETUTCDATE(),
version INT DEFAULT 1
);
CREATE TABLE rag_chunk_registry (
doc_name NVARCHAR(500) NOT NULL,
chunk_hash NVARCHAR(64) NOT NULL,
chunk_id NVARCHAR(64) NOT NULL,
PRIMARY KEY (doc_name, chunk_hash)
);
CREATE TABLE rag_sessions (
session_id NVARCHAR(100) PRIMARY KEY,
created_at DATETIME2 DEFAULT GETUTCDATE(),
updated_at DATETIME2 DEFAULT GETUTCDATE(),
model NVARCHAR(200),
embed_model NVARCHAR(200),
turn_count INT DEFAULT 0
);
CREATE TABLE rag_turns (
id INT IDENTITY(1,1) PRIMARY KEY,
session_id NVARCHAR(100) NOT NULL REFERENCES rag_sessions(session_id),
role NVARCHAR(20) NOT NULL CHECK (role IN ('user','assistant')),
content NVARCHAR(MAX) NOT NULL,
sources NVARCHAR(MAX),
created_at DATETIME2 DEFAULT GETUTCDATE()
);
CREATE INDEX idx_rag_turns_session ON rag_turns(session_id, created_at);
-- Batch 3: Must run AFTER Batch 2 commits
-- Cannot run inside a transaction — this is a known SQL Server 2025 preview constraint
CREATE VECTOR INDEX idx_rag_embedding
ON rag_chunks(embedding)
WITH (METRIC = 'COSINE');
Залежнасці Python
Для этапы залежнасцей Python неабяжна практычна вказаць на вхідныя даны, адміністратара крока і крытэрыя завершэння пры зміне коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю, не падозрываючы схованы стан. Конфігурацыю трэба захаваць пазнаходзіцца за межамы коду прыемліка. Файлы сераўнавання, хранільнікі секрэтных дадзеных і флагі функцый належаць у аднам месца, якое аператары можаць пераглядаць без неабяжнага чытання всіх элементаў сістэмы. Трэба аддзеліць стварэнне кліента ад цыклу перадачы паведамленняў, каб было можна змяніць прадаўцоў без перапісвання машыны стану дыялогу.
uv add google-genai pypdf pyodbc python-dotenv huggingface_hub fpdf2
Установка з’ўязку (Клетка 2)
Для этапы «Настройка з’еднання, клетка 2» неабяцо пазначыць вхідныя даны, адміністратара крока і крытэрыя завершэння пры перадзеіснаванні коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю, не падозрываючы схованы стан. Неабяцо задокументаваць як шлях успеху, так і шлях вярнення. Перапрыбуткі, людзкіе перакрыцця і апрацоўка некорэктных паведамленняў ёсць часткай продукту, а не наступным этапам дапрацоўкі. Прыцягніце цытаты з тых частак, якія фактычна лежалі в основе адпаведнай адказу. Без цытатаў аператары не зможаць разлічыць галюцынацію ад прасоў у індэксаванні.
import pyodbc
SQL_SERVER = "YOUR_SERVER_NAME" # from SSMS title bar
SQL_DATABASE = "local_rag"
SQL_CONN_STR = (
f"DRIVER={{ODBC Driver 17 for SQL Server}};"
f"SERVER={SQL_SERVER};"
f"DATABASE={SQL_DATABASE};"
f"Trusted_Connection=yes;" # Windows Authentication - no password needed
)
def get_conn():
return pyodbc.connect(SQL_CONN_STR)
def get_conn_autocommit():
"""Required for CREATE/DROP VECTOR INDEX - cannot run inside a transaction."""
return pyodbc.connect(SQL_CONN_STR, autocommit=True)
Зберагчыце інтэграціі (Клетка 6)
Для этапы 6 «Зберагчэнне эмбедынгаў» неабходна перад зменай коду адзначыць вхідныя даны, адпаведальнага за крок і крытэрыя завершэння. Аператары должны магчымае перайсці на выкананне кроку з вядомага пункта контролю, не спрабоўваючы здагадвацца пра схованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптаў. Калі крок не выкананы, прычына неудачы должна вказываць на адзін конкрэтны элемент, а не на заплутаны ланцужок задач. Прытамульвайце тыя часткі тэксту, якія фактычна лежаць у падставе адпаведнай адказы. Без ціх прытамульванняў аператары не зможаць розлічыць галюцынацію ад працягу ў індэксаванні. Для этапы 6 «Зберагчэнне эмбедынгаў» неабходна перад зменай коду адзначыць вхідныя даны, адпаведальнага за крок і крытэрыя завершэння. Аператары должны магчымае перайсці на выкананне кроку з вядомага пункта контролю, не спрабоўваючы здагадвацца пра схованы стан. Запісвайце час выканання і кост токеноў або запытаў разам з функцыйнальнымі рэзультатамі. Відразуваяя інформацыю пра косты, можна ухіліцца ад неспакою, калі працэс пераходзіць з дэмавайнага рэжыма ў спяльную срэду.
Указаныя пункты.
import json
def vec_to_json(embedding: list[float]) -> str:
return json.dumps(embedding) # '[0.12, 0.34, ...]'
def store_in_sqlserver(chunks, embeddings, doc_name) -> int:
drop_vector_index() # must drop before any INSERT
conn = get_conn()
cur = conn.cursor()
sql = """
INSERT INTO rag_chunks (source, chunk_index, content, embedding)
VALUES (?, ?, ?, CAST(? AS VECTOR(768)))
"""
for i, (chunk, emb) in enumerate(zip(chunks, embeddings)):
cur.setinputsizes([
(_pyodbc.SQL_WVARCHAR, 500, 0),
_pyodbc.SQL_INTEGER,
(_pyodbc.SQL_WVARCHAR, 0, 0),
(_pyodbc.SQL_VARCHAR, 0, 0), # ← must be VARCHAR, not NTEXT
])
cur.execute(sql, (doc_name, i, chunk, vec_to_json(emb)))
conn.commit()
conn.close()
create_vector_index() # recreate after all inserts
return len(chunks)
Адзысквэнтнае выкарыстоўванне з VECTORDISTANCE (Клетка 6, продажнае)
Калі працуеце над этапам клеткі «Адзысквэнтнае выкарыстоўванне з VECTORDISTANCE», спачатку запісайце неабходныя умовы: трэбаваныя даныя, сігнал успеху і тое, што выканаецца у разы ўзельнага неяксамоства. Такі чарткі спамагаюць залічыцца з пазнейшымі зменамі ў кодзе. Зберагаюце настройкі за межамі коду прыемліка. Файлы сяродавішча, хранільнікі секрэтных дадзеных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаць пераглядаць без неабходнасці чытання всей структуры. Перад налагоджэньем запытанняў пераканайцеся ў рывні адзысквэнтнасці на фіксаванай групе запытанняў. Частае змена запытань рэдка калі выправляе слабую структуру адзысквэнтнасці.
def retrieve_context(query: str) -> list[dict]:
query_embedding = embed_query(query)
query_vec_json = vec_to_json(query_embedding)
conn = get_conn()
cur = conn.cursor()
cur.setinputsizes([(_pyodbc.SQL_VARCHAR, 0, 0)]) # force VARCHAR for vector param
cur.execute(f"""
SELECT TOP ({TOP_K})
content, source, chunk_index, ingested_at,
VECTOR_DISTANCE('cosine', embedding, CAST(? AS VECTOR(768))) AS distance
FROM rag_chunks
ORDER BY distance ASC;
""", (query_vec_json,))
rows = cur.fetchall()
conn.close()
return [{"text": r[0], "source": r[1], "chunk_index": r[2],
"ingested_at": str(r[3]) if r[3] else "",
"score": round(1 - float(r[4]), 4)} for r in rows]
Прыкметы вектарных функцый SQL Server 2025 — усе, з якімі сталося сустрэча
Калі працюеце з этапам Vector у SQL Server 2025, спачатку запісайце шаблон: неабяжныя вхідныя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі спіс контроля дапамагае залічыць змяны ў кодзе пазнейшаю часам. Документавайце як шлях успеху, так і шлях вярнення да стану нормы. Перапрыбуткі, людзкія перэкрыцці і апрацоўка непрацюючых паведамленняў ёсць часткай продукту, а не наступным этапам дорабкі. Перад налаштаваннем запітоў пераканайцеся, насколькі эфектыва працуе сістэма з фіксаваным наборам запітанняў. Частае зменшэнне кількасці запітоў рэдка колі вылечвае слабкую эфектывнасць пошуку.
Проблема 1 — Невядомы тып об’екта ‘VECTOR’ у запітэ CREATE
Калі працуеце над стадзіяй «Gotcha 1: Невядомы об’ект», спачатку запісайце умовы вярбунка: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць пад частковыя неудачы. Такі спіс контроля дапамагае заліцьварыць пазнейшыя змены ў кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выходзіць, неудача должна адносіцца да адной відпаведальнасці, а не да заплутанага ланца задач. Перад налаштаваннем запітоў пераканайцеся ў рэгулярнасці адпаведзей на фіксованы набор запітанняў. Частае змены запітоў рэдка калі вярнуе слабкую эфектыўнасць адзысквання інформацыі. Калі працуеце над стадзіяй «Gotcha 1: Невядомы об’ект», спачатку запісайце умовы вярбунка: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць пад частковыя неудачы. Такі спіс контроля дапамагае заліцьварыць пазнейшыя змены ў кодзе. Запісвайце час выканання і кост токеноў або запітаў разам з функцыональнымі рэзултатамі. Відразувыя даныя пра косты запобегаюць неспакойным рахункам, калі праця пераходзіць з дэмаверсіі ў спакульнаныя сераўысы.
Msg 343, Level 15: Unknown object type 'VECTOR' used in CREATE, DROP, or ALTER statement.
Gotcha 2 — Ключ абсалютнай прыямоўнасці павінен быць адной 4-байтовай столбцам тыпу INT
Этап з ключам абсалютнай прыямоўнасці Gotcha 2 даўа найлепыя рэзультаты, калі яго спрыяглядаць як меркаваную плошчу. Зберагучы адну ідэальную копію дадзеных, адны прыклад неудачы і запіс пра вярнэнне да пачатковага стану, перш чым расшырваць масштабы. Канфігурацыю трэба залічваць парадзельна ад коду прыемліка. Файлы сяродавішча, хранільнікі секрэтных дадзеных і флагі функцый должны знаходзіцца ў аднам месцы, куда аператары можуць адбавляць аўдыт без неабяжнага чытання всіх дадзеных. Палітыку разбівання на часткі трэба аддзельна ад палітыкі выявлення дадзеных. Змена адной з іх не должна вымагаць перапісвання другой, калі зменяюцыся паказнікі якосці.
Msg 42217: Table must have a clustered primary key on a single 4 byte INT column to create a vector index.
-- ❌ Does not work with vector index
id UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID()
-- ✅ Required
id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
Gotcha 3 — Нельга выпрацавваць оперэйсы INSERT/DELETE/UPDATE, калі існуе вектарны індэкс
Функцыя Gotcha 3 Cannot INSERT stage працюе наўсёх лепшая, калі яе розглядаць як вімерную паверхню. Зафіксавайце адна ідэальная транскрыпцыю, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расшырваць сферу дзеяння. Документавайце як шлях успеху, так і шлях вярнэння ў нормальны стан адночасна. Перапрыбуткі, людзкі контроль і обработка некорэктных паведамленняў ёсць часткай продукту, а не етапамі далейшай наладкі. Раздзеліце правілы частковай обработкі дадзеных ад правіл ўтрымання іх. Змена аднаго з яных не должна прымусіваць перапісванне другога, калі змянююцыся паказчыкі якосці.
Msg 42231: Data modification statement failed because table 'rag_chunks' has a vector index on it.
def drop_vector_index():
conn = get_conn_autocommit() # autocommit required
conn.cursor().execute("""
IF EXISTS (
SELECT 1 FROM sys.indexes
WHERE name = 'idx_rag_embedding'
AND object_id = OBJECT_ID('rag_chunks')
)
DROP INDEX idx_rag_embedding ON rag_chunks;
""")
conn.close()
def create_vector_index():
conn = get_conn_autocommit() # autocommit required
conn.cursor().execute("""
CREATE VECTOR INDEX idx_rag_embedding
ON rag_chunks(embedding)
WITH (METRIC = 'COSINE');
""")
conn.close()
Gotcha 4 — CREATE VECTOR INDEX не можа выконвацца ў межах транзакцыі
Этап Gotcha 4 CREATE VECTOR працюе найкрашча, калі яго розглядаць як вімерную паверхню. Зафіксуйце адны ідэальны прыклад, адну справу з бягамі і прыметку па вярнэнню да пачатковага стану пры расшырэнні масштаба. Валіце варот на маленькія, тэставаныя элементы замест большых скрыптов. Калі якісь крок не выйшае, прычына бягу должна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцюг задач. Раздзеліце правілы часткавання дадзеных ад правіл ўтрымання іх. Змена адных не должна вымагаць перапісву іншых, калі зменяюцца паказнікі якосці. Этап Gotcha 4 CREATE VECTOR працюе найкрашча, калі яго розглядаць як вімерную паверхню. Зафіксуйце адны ідэальны прыклад, адну справу з бягамі і прыметку па вярнэнню да пачатковага стану пры расшырэнні масштаба. Запісвайце часы выканання і косты токенав або запітоў разам з функцыональнымі рэзультатамі. Відразлівасць костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмавайнага рэжыма ў спяльныя сераўы.
Msg 574: CREATE VECTOR INDEX statement cannot be used inside a user transaction.
# ❌ Fails — implicit transaction
conn = pyodbc.connect(SQL_CONN_STR)
conn.cursor().execute("CREATE VECTOR INDEX ...")
# ✅ Works - no transaction wrapper
conn = pyodbc.connect(SQL_CONN_STR, autocommit=True)
conn.cursor().execute("CREATE VECTOR INDEX ...")
Gotcha 5 — Не дазволяецца явна перакладчыка з ntext у vector
Для этапу явнай перакладчыкі Gotcha 5 неабяжна праказаць вхідныя даны, адпаведальнага за шаг і крэтарыя выходу пры перадзеі коду. Аператары должны магчымаць перзапуск шагу з вядомай точкі контролю, не спрабоўваючы здагадвацца пра схованы стан. Конфігурацыю трэба залічыць праза код аплікацыі. Файлы сераўнавання, храненні секрэтных даных і флагі функцый должны знаходзіцца ў аднам месцы, якое аператары можаць пераглядаць без неабяжнай чытанняў усіх элементаў структуры. Праказваць неабяжна тые часткі тэксту, якія фактычна лежаць у падставе адпаведнай адказы. Без ціх цытатаў аператары не можаць разлічыць галюцынацію ад працягу індэксавання.
Msg 529: Explicit conversion from data type ntext to vector is not allowed.
cur.setinputsizes([
(_pyodbc.SQL_WVARCHAR, 500, 0), # source — Unicode fine
_pyodbc.SQL_INTEGER, # chunk_index
(_pyodbc.SQL_WVARCHAR, 0, 0), # content — Unicode fine
(_pyodbc.SQL_VARCHAR, 0, 0), # embedding ← must be ASCII VARCHAR
])
cur.execute(sql, (doc_name, i, chunk, vec_to_json(emb)))
Gotcha 6 — VECTOR_SEARCH не прымае заменнікі параметра ?
Для процэсу Gotcha 6 у VECTORSEARCH неабяжна падготовка: перш чым зменіць код, трэба задаць вхідныя даны, адпаведальнага за крок і крытэрыі завершэння. Аператары должны магчымаць перзапуск кроку з вядомай точкі контролю, не падозрываючы прыхованы стан. Неабяжна задокументаваць як шлях успеху, так і шлях вярнення. Перапрыбуткі, людзкі контроль і обработка некорэктных паведамленняў ёсць часткай продукту, а не яго пазнейшай дапрацоўкі. Паказваць трэба тые часткі тэксту, якія фактычна лежаць у падставе адпаведнай адказы. Без ціх цытатаў аператары не зможуць розразліць галюцынацыю ад прасоўкі ў індэксаванні.
Msg 102: Incorrect syntax near '('.
-- ❌ VECTOR_SEARCH with parameter placeholder — fails
FROM VECTOR_SEARCH(
TABLE = rag_chunks USING VECTOR INDEX idx_rag_embedding,
SIMILAR_TO = CAST(? AS VECTOR(768)), -- pyodbc cannot pass ? here
...
)
-- ✅ VECTOR_DISTANCE - fully parameterized, GA, works perfectly
SELECT TOP (5)
content,
VECTOR_DISTANCE('cosine', embedding, CAST(? AS VECTOR(768))) AS distance
FROM rag_chunks
ORDER BY distance ASC;
PostgreSQL проты SQL Server — паўнае порэванне
Для этапа PostgreSQL протыма SQL Server неабяжна прадзецелі, адпаведальнага за крок і крэтарыя выходу пры змены коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю, не падозрываючы схованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі крок не выйшае, прычына нехаспеку должна вказваць на адзінную адпаведальнасць, а не на заплутаны працэс. Наводзіце тыя часткі, якія фактычна леглі в основу адпаведнай адказы. Без ціх цытатаў аператары не зможаць разлічыць галюцынацію ад прасоўкі індэксаў.
Што дадваяць базы даных, чаго немае ChromaDB
Для этапы «Што дадаюць обе базы даных» неабяжна практычна апранавальніка, власнікэ крока і крытэрыі завершэння перад змянайом коду. Апранавальнікі должны магчымаць перзапуск крока з вядомага пункта контролю, не спрабоўваючы з’ясаваць захованы стан. Спрыятлівайце гэтую стадыю як даговор межа вхіднымі данымі і пасвярджанымі выходнымі рэзультатамі. Даўце назвы артыфактам, практычная апранавальніка успеху і не прымайце часткова завершанне без падтверджэння. Указайце тыя часткі тэксту, якія фактычна лежалі в основе адпаведнай адказы. Без цых цітатаў апранавальнікі не зможаць разлічыць галюцынацію ад працягу індэксавання.
-- PostgreSQL: Find chunks from documents belonging to a specific customer
SELECT c.content, 1 - (c.embedding <=> query_vec) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC
LIMIT 5;
-- SQL Server: Same query, T-SQL syntax
SELECT TOP 5
c.content,
1 - VECTOR_DISTANCE('cosine', c.embedding, CAST(? AS VECTOR(768))) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC;
Заключэнне
У стадыі заканчэння неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крытэрыя завершэння пры перадзеіснавленні коду. Аператары должны магчымаць перзапуск кроку з вядомай точкі контролю, не падозрываючы прыхованы стан. Запісваць час выканання і вартасць токеноў або запытак праза функцыйнае рэзультат. Відкрытая візуабілізацыя вартасцей запобегае неспакоўным рахункам, калі працэс пераходзіць з дэмовай асяроддзя ў спяльнаныя сераўеры. Указваць тые часткі тэксту, якія фактычна ляглі в основу адпаведнай адпаведзі. Без цых цытатаў аператары не можуць разлічыць галюцынацію ад прасоў у індэксаванні.