Адзін Retriever, трыя бэкенда: перанос алгорытму пошуку NumPy у Chroma, FAISS, Pinecone
Перанесіце ручная версія алгоритму пошуку косынуса з NumPy у Chroma, FAISS і Pinecone, а таксама дазнаецеся, які функцыі вносзяць індэксы, фільтры метаданых і можлівасць зберагчыць інфармацыю, а таксама пра падзеяхі, якія паслабляюць релевантнасць.
Крыніцы: Vector Databases, Hands-On — From Your numpy Array to Pinecone
Самым простым сховішчам вектораў є матрыца NumPy з эмбеддінгамі плюс адна лінія арыметычных вырахункаў: chunk_vectors @ q_vec практыкуе оцэнку кожнага фрагмента па вопыту, а сортаванне выбірае найкращыя рэзультаты. Гэта справжняя векторная база дадзеных, проста як можна мінімальная. У цым кераванні мы бераме самэ гэтае засоба для пошуку і переносімо яго на тры рэальныя бэкенды — Chroma, FAISS і Pinecone, ўпрымоўкі, каб вы моглі точна пазнакоміцца з тым, што дадае кожны з іх, які з багатых продактав (Chroma, FAISS, Qdrant, Weaviate, Milvus, pgvector, Pinecone) падходзіць для кожай ситуацыі, і якія памылкі таямна паспрабоўваюць зіпсаваць релевантнасць.
Местныя прыклады не патрабуюць ключаў API і не купуюцца. Pinecone адказвае безкоштовным серверлессам стартовым ўровнем, які выказывае патрэбу ў регистрацыі і ключы, але на момент напісання не патрабуе карточкі платежа, таму можна безкоштовна спробаваць і кераваны варіант. Код прыпускае наявнасць аднаго скрыпта vdb.py, які запускаецца блак за блакам.
Калі масів NumPy вяршыцца
Ручны алгорытм вычысляе косінусную схожасць з кожным зафіксаваным фрагментам пад кожны запит. З каляквітасцю фрагментав выкаонанне вяршыцца мгновэна. Калі ж фрагментав каля мільйона, працэс становіцца повольным, весь масіў патрабуецца ў RAM, а кожны эмбеддінг зникае пасля завершэння працэсу. База дадзеных вектароў усуне тры галінкі:
- Скорасць у масштабе. У працэ заместо точнага параболічнага аналізу з кожным векторам, ствараецца апроксиматыўны індэкс найбліжэйшых суседаў, такі як HNSW, який знаходзіць майже найбліжэйшыя векторы за час, прыблізна равны вырастку логарыма з n. Часткова падваўляецца точнасць аднаходжэння даных, але значна прыбывае скорасць.
- Стойкасць. Векторы зберагаюцца на дыску або на кіраванам сервере і застаюцца доступнымі пасля перзапуску.
- Метаданы і фільтрацыя. Кожны вектор можа мець атрыбуты, такія як
{"source": "doc12", "date": "2026-01"}, і запит можа быць абмежаны ўсёма векторамі з такімі атрыбутамі, напрыклад, найбліжэйшымі векторамі, дзе істочнік —doc12.
Разлік между гэтымі двума спосабамі аднаходжэння даных можна выразіць за два рэчы:
your numpy array: query ──► compare to ALL n vectors ──► sort ──► top-k (exact, slow at scale)
a vector database: query ──► walk HNSW graph ──► ~top-k (approximate, fast, on disk, filterable)
Гэта ўсё сутнае прызначэнне. Усё, што следуе, — це тэхнічныя аспекты.
Словнік, з яким вам давацца сталкаць
- Умяшчэнне або вектар: спіс цых лякучых числаў, які кодуе значэнне; у прыкладах тут выкарыстоўваецца
all-MiniLM-L6-v2. - Метрыка адстані: спосаб меры блізкасці. Косай кант (кут межы вектараў) ёсць стандартны выбор для тексту; скалярны добутак і эвклідава (L2) метрыка ўжо як альтэратывы. Выбірайце тую, для якой быў натрэнаваны ваша модель умяшчэння.
- Індекс: структура, якая спрыяе шырокамасштабным пошукам. HNSW, як навігабельны граф, ёсць стандартам у большасці баз дадзенаў; IVF і PQ разнымі спосабамі паказваюць кампроміс межы памяцю і швальнасцю.
- Upsert: дадаць вектар па ID або апдэйтаваць яго, якщо ID вялік.
Chroma: натуральны першы апград
Chroma працюе ўнутрь вашага процэсу Python пасля адной установкі, без сервера і без Docker. Чырвоныя гэтае яго прыводзіць да таго, што ён стае найболей прымітным спосабам пераходу з масівай NumPy. Установіце яго разам з бібліятэкай embedding:
pip install chromadb sentence-transformers
У наведанам выкладзе скрыпт выкарыстоўвае невялікі корпус, який описвае вымышлены продукт для запісу нотатак пад назвай Nimbus, з тэгам тэмы для кожнага дакумента. Ён стварае кліента ў памяці, ачынае колекцыю, настроеную для вычыслення косай вялічыны на адстань за дапамогою hnsw:space, явна уключае дакументы і дадзяўае ўсім іх ID-ы і метаданы. Потым ён уключае запит і запрашвае два найбліжэйшыя дакументы, выдрукаваючы кожны з іх адстаннёй. Зверніце увагу на каментар да chromadb.Client(): дастатньва прыменіць PersistentClient(path=...), ўсё, што трэба, каб даны засталіся на дыску.
# vdb.py
import chromadb
from sentence_transformers import SentenceTransformer
embedder = SentenceTransformer("all-MiniLM-L6-v2")
# Same Nimbus corpus from the RAG tutorial
DOCS = [
"Nimbus Pro costs 8 dollars per month billed annually, with 50 GB storage.",
"Nimbus offers a 30-day refund policy on all paid plans.",
"Nimbus Lite (free) includes 1 GB of storage and up to 50 notes.",
"Nimbus stores notes encrypted at rest with AES-256; E2E encryption is Pro-only.",
"Live chat support is Pro-only, Mon-Fri 9-6 UTC; free users get 48h email support.",
]
metadatas = [{"topic": t} for t in ["pricing","refund","pricing","security","support"]]
client = chromadb.Client() # in-memory; use PersistentClient(path=...) to save to disk
collection = client.create_collection(
"nimbus", metadata={"hnsw:space": "cosine"} # choose the distance metric
)
# Embed and add. (↔ your chunk_vectors = embedder.encode(...))
collection.add(
ids=[f"doc{i}" for i in range(len(DOCS))],
embeddings=embedder.encode(DOCS).tolist(),
documents=DOCS,
metadatas=metadatas,
)
# Query. (↔ your retrieve(): embed query, find nearest)
q = "How much does the paid plan cost?"
res = collection.query(query_embeddings=embedder.encode([q]).tolist(), n_results=2)
for doc, dist in zip(res["documents"][0], res["distances"][0]):
print(f"[{dist:.3f}] {doc}")
Эта версія адаптавана да ручнай версіі, і па сутнасці нічога новага няма. collection.add заменяе процэс кодавання дакументаў і зберагчыцьця матрыцы. collection.query заменяе вычысленне матрыцэйнага добутку, функцію argsort і аперэйцію выбору топ-k элементаў. Чым спадзяецца Chroma, такім ёсць індэкс HNSW, можлівасць стойкага зберагчыцьця дакументаў і падтрымка метаданых, якія базуюцца на той жа ідэі.
Chroma таксама можа аўтоматычна укладзіць текст за дапамогою вбудованага модэлю, якщо паслаць параметр documents= без embeddings=. Якщо ж укладанне текста выкарыстоўваць явна, як паказана вышэй, механізмы застаюцца виднымі, а выбор модэлю залишаецца у вашых руках, што мае значэнне, таму ўсё ж трэба выкарыстоўваць той самы модэль як для дадавання, так і для запытанняў.
Фільтрацыя па метаданых
Фільтрацыя — это функция, якой некалендарны ўжоў ніколи не хацелі. Наступны запит шукае „data security“, але обмежвае результаты вектарамі, чыя topic ў значэнні security, за дапамогою аргумента where:
res = collection.query(
query_embeddings=embedder.encode(["data security"]).tolist(),
n_results=3,
where={"topic": "security"}, # only search vectors tagged security
)
print(res["documents"][0])
У прымэтнай експлуатацыі гэта ўтварае адпаведныя запиты, такія як „падобныя заявкі ад гэтага кліента“ чы „дасягнення документацыі ў гэтым годзе“. Фільтрацыя застаўляецца разам з пошукам вектароў, а не як крок пасля обрабаткі, таму вы все раве атрымаеце да n_results паспадобных рэзультатаў. Як вправа, змяніце фільтр на where={"topic": "pricing"} і пераканайцеся, што документы пра вярненне грошаў і безпеку больш не будуць атрымліваны.
FAISS: быстрая бібліятэка індексаў, а не база дадзенаў
FAISS, які выкалічваецца компанія Meta, ўжо не являецца базай знакоў. Цэта адчынна оптымізаваная бібліятэка для пошуку найбліжэйшых суседзей, якая не мае метаданоў і сопныяй складовай для зберагачвання, і прызначаная для вбудовывання ў вашу аплікацыю. Багато іншых системаў выкарыстоўваюць яе внутршняя. Зінсталюйте версію для CPU:
pip install faiss-cpu
Код кодуе дакументы з параметрам normalize_embeddings=True і ператварае іх у формат float32, які трэба FAISS. Ён стварае індекс IndexFlatIP, які ўжо є індексам внутранняго добутку; адтаколькі вектары нормалізуюцца да дужэй дужыны, внутранні добуткі паўтараюць косай схожасць. Пасля дадання вектараў код такім жа спосабам кодуе запит і выклікае функцыю search, каб знайсці два найбліжэйшыя результаты, а пасля — перакладае повернутыя пазіцыі назад у первісныя дакументы.
import faiss, numpy as np
vectors = embedder.encode(DOCS, normalize_embeddings=True).astype("float32")
index = faiss.IndexFlatIP(vectors.shape[1]) # IP = inner product = cosine on normalized vectors
index.add(vectors) # ↔ exactly your numpy array, but optimized in C++
q = embedder.encode(["refund window?"], normalize_embeddings=True).astype("float32")
scores, idx = index.search(q, k=2) # ↔ your argsort top-k
for s, i in zip(scores[0], idx[0]):
print(f"[{s:.3f}] {DOCS[i]}")
Гэта практычна ўсё та ж самая прыемка з NumPy: IndexFlatIP — это пошук за дапамою косай вялічыны методом грубой сілы, рэалізаваны ў оптымізаванам C++. Корыстнае наследжэнне праблічаецца, калі яго заменіць на апрыксиматыўны індэкс, такі як IndexHNSWFlat або IndexIVFFlat, якія зберагаюць той самы інтерфейс, але ведуць пошук за час, меньшы за лінейны. Выберыце FAISS, калі хочаце максимальной швальнасці ўнутрь адного процэса і гатовы самі кераваць перзистэнцыяй і метаданамі, напрыклад, палячы окольны карточнік з пазіцый на записі дакументаў.
Pinecone: абсалютна кераваныя і без сервера
Усё, што было раней, працавало ў вашым сабе процэсе. Pinecone — это кераваная база дадзеных вектароў, доступная через сеть: тут няма сервера, які трэба запускаць, і ёна автаматычна масштабуецца. Гэта варыянт для команд, якія не хочуць кераваць інфраструктурой.
Зважайце, што SDK Pinecone сэрьозна змянілася. Стары стыль pinecone.init(api_key=, environment=) больш не існуе; тепер трэба стварыць об’ект-кліента Pinecone, а пакет называецца pinecone, а не pinecone-client. Адзін раз пераканаўцеся ў актуальной даследовачкай документацыі SDK, якщо ў чым-небудзь з’явіцца проблема.
Блок пачынаецца з команды інсталяцыі, а пасля — з коду на Python (якщо копіюеце, раздзеліце іх). У ям создаёцца кліент, а таксама безсерверны індэкс пад назвай nimbus, толькі якщо ён яшчэ не існуе; пры гэтым задаюцца параметры dimension=384, каб ён падходзіў да MiniLM, і metric="cosine", пасля чаго выступаеся зяўнерычанне да гэтага індэксу. У ям выкарыстоўваецца операция upsert для дадзення пар (id, vector, metadata), пры чым аўтэнтычны текст зберагаецца ў metadata, адтакуль Pinecone вяртае толькі тое, што вы збераглі. На завершанне выкарыстоўваецца запыт пра два лепшых рэзультата, абмежаны фільтрам у стыле Mongo $eq па параметру topic, і выводзяцца кожны рэйтынг і текст. У рэальным кодзе ключ лепей чытаць з аб’екта сераўісу зменных сяродовыша або сховішча секрэтных дадзеных, а не ставіць яго ў сам код.
pip install pinecone
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key="YOUR_API_KEY") # get a free key at app.pinecone.io
# Create a serverless index (only once). dimension must match your embedder: MiniLM = 384.
if not pc.has_index("nimbus"):
pc.create_index(
name="nimbus",
dimension=384,
metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
)
index = pc.Index("nimbus")
# Upsert: (id, vector, metadata) tuples
index.upsert(vectors=[
(f"doc{i}", embedder.encode(d).tolist(), {"text": d, "topic": m["topic"]})
for i, (d, m) in enumerate(zip(DOCS, metadatas))
])
# Query with metadata filtering
res = index.query(
vector=embedder.encode("how much is pro?").tolist(),
top_k=2,
include_metadata=True,
filter={"topic": {"$eq": "pricing"}}, # Mongo-style filter operators
)
for match in res["matches"]:
print(f"[{match['score']:.3f}] {match['metadata']['text']}")
Формат адаптаваныя ўсё точна да Chroma: ствараецца індэкс, выкананыя аператыўы upsert для ID, вектараў і метаданых, пасля чаго выконваецца запыт top-k з неабяжным фільтрам. Этап выкарыстоўвання дадзеных аднойчыны такі ж, як і ранейш, але тепер ён выконваецца на інфраструктурэ автаскалявання іншага аператора через API. Pinecone таксама падтрымляе namespaces, якія дзельнуюць адны індэкс, а таксама integrated inference, дзе функцыі upsert_records і search_records самыя включаюць текст, таму не трэба самостайна выканваць процес включэння тексту.
Абщая насведчанне заключаецца ў тым, што пераход з Chroma на Pinecone змяніў толькі спосаб прыяўлення і вызову сховішча дадзеных, а не логіку RAG, якая яго апрацоўвае. У добра структураваным прыкладзе програмы сховішча вектараў ёсць компонент, які можна заменіць, і самэ гэта ўзгляду на тое, чаму методы фреймворкаў, такія як .as_retriever() з LangChain, застаюцца ідэнтычныя незалежна ад бэкенда.
Выбір бэкенду па вашыя абмежэння
Няма ўніверсальнай найкращай базы дадзеных для вектараў; кожная з іх падходзіць для пэўнага набору абмежэнняў. Прыблізна:
- Масіва NumPy (самастраўленыя): працуюць у межах процесу; падходзяць для прыблізна 10 тысяч вектараў, для навучэння або калі трэба абсолютны контроль.
- Chroma: вбудованая база дадзеных, працуючая у межах процесу або як локальны сервер; ідеальна для протатэў і малых-середніх дапытак, лёгкая для запуску.
- FAISS: бібліятэка індексаў, працуючая у межах процесу; даўае максымальную швалерасць, калі вы самі керуеце збераганнем і метаданнымі.
- pgvector: расшырэнне для Postgres у вашай існуючай базе дадзеных; ідеальна, калі вы вядома выкарыстоўваете Postgres і хочаце зберагваць вектары разам з реляцыйнымі дадзеннямі.
- Qdrant: база дадзеных, якая можа працаваць самастоятельна за дапамогою Docker або ў хмаре; мае сильныя фільтрацыі, высокую швалерасць завдака техналогій Rust, ёсць адкрытым кодам і падтрымлеў гібранічны пошук.
Найчастае пытанне, якое встае, — гэта выкарыстанне адмініструемых служб проты самастаўлення. Адмініструемыя службы, такія як Pinecone чыют іншыя ў версіях для хмары, пазбавляюць ад неабходнасці керування, але ствараюць постоянныя витраты і размешчаюць вашы даны на серверах постачальніка. Самастаўленне Chroma, Qdrant, Weaviate, Milvus чыют pgvector значыць, што вы самі керуеце імі, заўсёды контролюячы витаткі і месца размешчэння даных. Адно з популярных правіла ўзводзіць, што самастаўленне становіцца выгодным, калі месячныя витаткі на адмініструемыя службы досягаюць канікулярных сум у доларах; нижэй гэтага рубежа часы, якія витрачаюцца на керування, зазвычай коштаюць больш, чым заўсёды эканоміцца. Вважайце гэта простым напамінанням, а не законам, адтолькі як спроможнасць вашай команды керування мае такое ж значэнне, як і сума рахунку.
Для навчання і большасці падзейных проектаў дастатньа Chroma або pgvector. Pinecone або Qdrant Cloud стануць корыстнымі, калі інфраструктуру краща заўважыць команде, якая не хоча ёю кераваць, а Milvus — калі вы справжнім чынам працуеце з мільярдамі вектароў. Якщо вы разважаеце, чы рэальна вам патрэбны спецыяльны хранілнік вектароў, прачытайце чаму продакшн-рэшанцыяў RAG пераходзяць за межы спецыяльных баз дадзеных вектароў.
Памылкі, якія пасля запуску базы дадзеных ствараюць проблемы
- Неспадзянанне размера вымераў — гэта найпашчэрэзнейшая памылка. Размер
dimensionіндэкса павінен дапаможыцца з розмерам выходных дадзеных ад модуля эмбеддінгу: 384 для MiniLM, 1536 для багатох модэляў эмбеддінгу OpenAI. Адначаснае викорыстоўванне розных модэляў у аднам індэксе гарантавана прыводзіць да багоў.
ef_search для HNSW або nprobe для IVF, якае падвышае рівень вярнаго аднаходжэння дадзеных за рахунак затрымкі. Чырвоныя дапамогі па гэтым параметрам можна знайсці ў налаштаванні індэксаў HNSW для прыменення ў RAG.PersistentClient ад Chroma або базы дадзеных, якая працюе на серверы.Хорашы спосаб — узяць існуючы скрыпт RAG на базе NumPy і заменіць толькі функцыю адзыскання на колекцыю Chroma, паказаную раней. Частка генеравання зовсім не зменшваецца, што паказвае, ўсё-такі адзыскання являе сабою чысты, заменяемы слой.
Карточка швайнароўкі проблем
dimension does not match: вашы вектары не маюць таго размеру, якого чакае індекс. Створыце індекс з правильнымdimensionабо выкорыстаце інструмент для уключэння, які быў нарабоўаны для яго.- Рэзультаты Chroma выглядаюць аднойчы: па верагоднасці метрыкі змешаны, або для дадзення і запыту былі выкорыстаны разныя інструменты для уключэння. Оба павінны збігатыся.
has_index або create_index выклікае памяці: пераканайцеся, што вы викорыстоўваете актуальны SDK (from pinecone import Pinecone), а не застарэлы спосаб pinecone.init.search вяртае ID -1: у індэксе меньша колькасць вектароў, чым k. Дадзіце больш дакументаў або зменшыце значэнне k.Ключовыя выводы
- База дакументаў на вектарах — это самастраўлены механізм пошуку, які робіцца швыракім, стойкім і здатным да фільтрацыі. Дадаванне вектароў абарачаецца ў кодаванні і збераганні; запытвання выконваюцца за дапамогою методу cosine top-k.
- Індэкс ANN — ежына новае рашэнне: адхоўваючы можлівасць апрыксиматывных адказаў, пошук застаецца швыракім нават праз зростанне колькасці дакументаў.