5 Oppsett av vektordatabase
Som beskrevet i Kapittel 2 finnes det flere muligheter når man velger vektordatabase. I denne seksjonen vil vi detaljere hvordan vi satt opp BigQuery, PostgreSQL og Azure AI Search som vektordatabaser. I neste seksjon, Kapittel 6, vil vi gå gjennom hvordan vi sammenlignet vektordatabasene for å lande én tilbyder.
Vi kommer til å anta at den originale kunnskapsbasen allerede eksisterer og at den er mulig å hente ut programmatisk. For NKS sin kunnskapsbase konverterte vi artiklene til Markdown før vi begynte å behandle dem. Dette ble gjort for å redusere antall ekstra tegn (mao. HTML tag-er), men samtidig beholde strukturer som overskrifter. Et enklere format kan hjelpe språkmodellen til å enklere hente ut direkte sitater, det blir mindre jobb med å rense output og det kan gjør det enklere å prompt-engineer D.8 senere.
Hvis kunnskapsbasen er tilgjengelig som filer kan pandoc være et godt verktøy å bruke for å konvertere mellom formater.
Videre kommer vi til å bruke LangChain som rammeverk for å interagere med dokumenter, vektordatabasen og språkmodellen. Bruk av LangChain er valgfritt, men det kan hjelpe med å komme i gang. I vårt tilfelle har vi benyttet LangChain for å sette opp vektordatabasene og ordne riktig indeksering.
For å laste opp dokumenter til vektordatabasene må vi først dele de opp slik at teksten ikke blir for stor for embedding modellen. Det viktigste med oppdelingen er at hvert tekst fragment vi sender til embedding modellen er semantisk lik, det er derfor viktig å være bevist på hvordan tekst behandles før det ender opp i vektordatabasen.
Teknikkene vi går gjennom under vil være de samme uavhengig av rammeverk.
5.0.1 Forberede Document-er
LangChain organiserer kunnskapsbasene sine rundt Document som er et dokument med tekst, page_content, og metadata. Som en første tilnærming leser vi bare inn innholdet i et Document og legger ved all relevant metadata som vi har fra den originale kunnskapsbasen.
client = bigquery.Client(project=project)
results = client.query(sql).result()
docs: list[Document] = []
for row in results:
content = row["Content"]
metadata = {k: v for k, v in row.items() if k != "Content"}
docs.append(Document(page_content=content, metadata=metadata))5.0.2 Dele opp Document-er
Fordi artikler i kunnskapsbasen kan være større enn det embedding modellen klarer å håndtere må vi dele opp dokumentene i flere små dokumenter. LangChain støtter dette rett ut av boksen og tilbyr flere måter å dele opp på. Den vanligste måten å dele opp er ved å rekursivt prøve å dele dokumentet i en gitt størrelse, med eller uten overlapp. LangChain prøver å dele så nærme nye paragrafer, dette gjør at innhold som er tett knyttet - ved å være i samme paragraf, holdes sammen.
Det er vanskelig å komme med en klar anbefaling på størrelsen på dokumentene. På den ene siden er man begrenset av embedding modellen, men å prøve å fylle dette vinduet er heller ikke den beste løsningen. Vi ønsker å dele dokumentet inn i underdeler som er semantisk forskjellige slik at vi kan hente de minste tekstene vi kan når vi gir det videre til språkmodellen.
To vanlige konfigurasjoner er enten 500 tegn uten overlapp eller 1000 tegn med overlapp på 200. Hvilken man velger burde baseres på testing.
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, chunk_overlap=200,
)
splits: list[Document] = text_splitter.split_documents(docs)Metadata på det originale dokumentet dupliseres så til hvert nye dokument som genereres slik at vi har mulighet til å hente ut den samme metadata.
Husk å ha en form for ID på original dokumentene slik at man kan identifisere splitter som hører sammen.
Hvis du er interessert i å lese om noen mer avanserte teknikker for dokumentoppdeling, sjekk Kapittel 8.
5.0.3 Opplasting til vektordatabase
Tilslutt kan vi laste opp til vektordatabasen. Litt avhengig av hvordan man oppretter LangChain vektordatabasen kan man enten laste opp ved add_documents eller from_documents.
db = BigQueryVectorSearch.from_documents(splits, AzureOpenAIEmbeddings())Både BigQuery, PostgreSQL og Azure Search integrasjonen til LangChain vil opprette en tabell som ser omtrent slik ut:
| ID | Tekst | Embedding vektor | Metadata |
|---|---|---|---|
| UUID | STRING | VECTOR(FLOAT) | JSON |
Dette er et greit utgangspunkt, hvor man enkelt kan søke med embedding vektorene, men det er litt begrensende når det kommer til å søke i metadata.
BigQuery eller PostgreSQL
Det burde vurderes å bruke BigQuery eller PostgreSQL direkte og heller kopiere fra LangChain integrasjonen som inspirasjon. Da har man mulighet til å sette opp flere kolonner som man enklere kan benytte i søket.
PostgreSQL har også mulighet for å gjøre fulltekstsøk samtidig som et vektorsøk, eksempel med Reciprocal Rank Fusion, men det krever manuelt oppsett.
Azure AI Search
Når AzureSearch- vektordatabasen opprettes med LangChain kan man samtidig angi tilleggsfelter som det kan filtreres etter og/eller søkes i. I vårt oppsett har vi lagt til hvert metadata-felt som custom kolonner i Azure-indexen. For å gjøre dette må man definere et schema for Azure-indexen som inneholder metadatafeltene pluss Azure sitt default schema. Eksempelet under viser hvordan man definerer et schema med “Tittel” som ekstra søkbart metadatafelt.
from azure.search.documents.indexes.models import (
SearchableField,
SearchField,
SearchFieldDataType,
SimpleField,
)
fields = [
# default felter som må være med
SimpleField(
name="id",
type=SearchFieldDataType.String,
key=True,
filterable=True,
),
SearchableField(
name="content",
type=SearchFieldDataType.String,
searchable=True,
),
SearchField(
name="content_vector",
type=SearchFieldDataType.Collection(SearchFieldDataType.Single),
searchable=True,
vector_search_dimensions=len(embedding_function("Text")),
vector_search_profile_name="myHnswProfile",
),
SearchableField(
name="metadata",
type=SearchFieldDataType.String,
searchable=True,
),
# custom kolonne
SearchableField(
name="Tittel",
type=SearchFieldDataType.String,
searchable=True,
),
]
index_name: str = "navn_på_index"
vector_store: AzureSearch = AzureSearch(
azure_search_endpoint=vector_store_address,
azure_search_key=vector_store_password,
index_name=index_name,
embedding_function=embedding_function,
fields=fields,
)Ved oppsett av custom schema for Azure Search index kan man også vurdere å legge til en scoring profile. En scoring profile kan benyttes for å vekte relevansen av kolonnene som ikke er den vektoriserte teksten, noe som kan være nyttig ved hybridsøk.