Indeksointi: palojen upottaminen ja tallentaminen
Upotatte kunkin palan OpenAI Embeddings API:n avulla ja upsertoitte tuloksena saadut vektorit metatietoineen vektoritallennustilaan, jolloin dokumenteista muodostuu haettava indeksi.
Indeksointi: palojen upottaminen ja tallentaminen on ilmainen AI Engineering Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu AI Engineering Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. AI Engineering Academy-kurssilla on yhteensä 4 oppituntia.
Indeksointivaihe: yleiskatsaus
Kun dokumentit on ladattu ja jaettu osiin, siirrytte indeksointivaiheeseen: tekstiosien muuntamiseen vektoriupotuksiksi ja niiden tallentamiseen haettavaan vektoritietokantaan. Tämä on viimeinen offline-vaihe ennen kyselyihin vastaamista. Upotusten laatu sekä tallennus- ja indeksointistrategian tehokkuus määrittävät suoraan, kuinka nopea ja tarkka RAG-järjestelmänne on kyselyhetkellä.
Upotusten luominen OpenAI API:n kautta
Yleisin tapa on kutsua OpenAI:n embeddings API:a osan tekstillä. text-embedding-3-small-malli tuottaa 1536-ulotteisia vektoreita ja maksaa 0,02 dollaria miljoonaa tokenia kohden — useimpiin käyttötarkoituksiin erittäin vähän. Lähettäkää useita tekstejä yhdessä API-kutsussa (enintään 2048 syötettä), jotta läpimeno on mahdollisimman suuri. Vastaus sisältää yhden upotusvektorin kutakin syötetekstiä kohden samassa järjestyksessä.
from openai import OpenAI
client = OpenAI()
def embed_batch(texts, model='text-embedding-3-small'):
response = client.embeddings.create(
model=model,
input=texts # up to 2048 texts per call
)
return [item.embedding for item in response.data]
# Embed one batch of 100 chunk texts
texts = [chunk['text'] for chunk in chunks[:100]]
vectors = embed_batch(texts)
print(f'Embedding dimension: {len(vectors[0])}')
print(f'Embedded {len(vectors)} chunks')Eräajo tehokkuuden parantamiseksi
Tuhansia osia indeksoitaessa tehokkuudella on merkitystä. Käsitelkää osat 100–500 osan erissä, jotta läpimeno ja muistin käyttö pysyvät tasapainossa. Seuratkaa sijaintianne, jotta voitte jatkaa virheen jälkeen upottamatta jo käsiteltyjä osia uudelleen. Kirjatkaa eteneminen säännöllisesti. Kun käsiteltävänä on 100 000 osaa ja erän koko on 500, API-kutsuja tarvitaan 200 — tämä valmistuu yleensä muutamassa minuutissa.
def embed_all_chunks(chunks, batch_size=200):
embedded = []
total = len(chunks)
for i in range(0, total, batch_size):
batch = chunks[i:i+batch_size]
texts = [c['text'] for c in batch]
vectors = embed_batch(texts)
for chunk, vector in zip(batch, vectors):
embedded.append({
**chunk,
'embedding': vector
})
if (i // batch_size) % 10 == 0:
print(f'Progress: {min(i+batch_size, total)}/{total}')
return embeddedNopeusrajoitusten käsittely indeksoinnin aikana
OpenAI:n embeddings API:n nopeusrajoitukset mitataan tokeneina minuutissa (TPM). Suuret indeksointityöt saavuttavat nämä rajat ja saavat virheen RateLimitError. Toteuttakaa eksponentiaalinen uudelleenyritys satunnaisella vaihtelulla: nopeusrajoitusvirheen ilmetessä odottakaa lyhyt satunnainen aika ennen uutta yritystä ja kaksinkertaistakaa odotusaika jokaisen peräkkäisen epäonnistumisen jälkeen. Näin uudelleenyritykset hajautuvat eivätkä kaikki rinnakkaiset työntekijät kuormita API:a samanaikaisesti.
import time
import random
from openai import RateLimitError
def embed_batch_with_retry(texts, max_retries=5):
for attempt in range(max_retries):
try:
return embed_batch(texts)
except RateLimitError:
if attempt == max_retries - 1:
raise
wait = (2 ** attempt) + random.uniform(0, 1)
print(f'Rate limited. Waiting {wait:.1f}s...')
time.sleep(wait)
return []Vektoreiden lisääminen Pineconeen
Kun upotukset on luotu, lisää ne vektorivarastoon upsert-toiminnolla. Upsertointi tarkoittaa uusien vektoreiden lisäämistä tai olemassa olevien päivittämistä, jos sama tunnus on jo olemassa — toiminto on suunniteltu idempotentiksi. Pinecrafta jokainen upsertattu tietue sisältää vektorin tunnuksen, upotuksen arvot ja metadatakirjaston kentistä, joiden perusteella haluat myöhemmin suodattaa tai joita haluat näyttää. Suorita upsertointi erissä, joissa on enintään 100 tietuetta kutsua kohden, jotta suorituskyky on optimaalinen.
import pinecone
pc = pinecone.Pinecone(api_key='YOUR_KEY')
index = pc.Index('rag-index')
def upsert_to_pinecone(embedded_chunks, batch_size=100):
for i in range(0, len(embedded_chunks), batch_size):
batch = embedded_chunks[i:i+batch_size]
vectors = [
(
chunk['id'],
chunk['embedding'],
{
'text': chunk['text'],
'source': chunk['metadata']['source'],
'page': chunk['metadata'].get('page', 0)
}
)
for chunk in batch
]
index.upsert(vectors=vectors)
print(f'Upserted {min(i+batch_size, len(embedded_chunks))}/{len(embedded_chunks)}')Tallentaminen pgvectoriin
pgvectorin avulla upotukset lisätään suoraan PostgreSQL-tauluun tavallisella SQL:llä. vector-tietotyyppi hyväksyy merkkijonoksi muunnetun Python-liukulukulistan. Kun kaikki rivit on lisätty, luo HNSW-indeksi nopeita likimääräisiä lähinaapurikyselyitä varten. Olemassa olevan, miljoonia rivejä sisältävän taulun indeksointi voi kestää useita minuutteja, joten luo indeksi massalisäyksen jälkeen äläkä ennen sitä.
import psycopg2
from psycopg2.extras import execute_values
def upsert_to_pgvector(conn, embedded_chunks):
with conn.cursor() as cur:
records = [
(
chunk['id'],
chunk['text'],
chunk['metadata']['source'],
chunk['metadata'].get('page', 0),
chunk['embedding'] # list of floats
)
for chunk in embedded_chunks
]
execute_values(cur, '''
INSERT INTO document_chunks (id, text, source, page, embedding)
VALUES %s
ON CONFLICT (id) DO UPDATE
SET text = EXCLUDED.text, embedding = EXCLUDED.embedding
''', records)
conn.commit()HNSW-indeksin luominen
HNSW (Hierarchical Navigable Small World) on indeksityyppi, joka mahdollistaa nopean likimääräisen lähinaapurihaun. Toisin kuin raakavoimahaku, jossa kyselyvektoria verrataan jokaiseen tallennettuun vektoriin, HNSW rakentaa monikerroksisen graafirakenteen, joka karsii hakutilaa. m-parametri määrittää kunkin solmun yhteyksien määrän (suurempi arvo = parempi kattavuus mutta enemmän muistia), ja ef_construction määrittää indeksin laadun rakentamisen aikana.
-- Build HNSW index after bulk insertion
CREATE INDEX CONCURRENTLY ON document_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Set ef_search at query time to trade recall vs speed
SET hnsw.ef_search = 100;
-- Verify index was created
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'document_chunks';Metadatan skeeman suunnittelu
Kunkin vektorin yhteyteen tallennettu metadata mahdollistaa tehokkaan suodatetun haun. Suunnittele metadatan skeema ennen indeksointia — uusien kenttien lisääminen myöhemmin edellyttää uudelleenindeksointia. Lisää kentät, joiden perusteella suodatat (osasto, asiakirjatyyppi, ajanjakso), kentät, jotka näytät viittauksissa (otsikko, sivu, tekijä), sekä virheenkorjauksessa hyödylliset kentät (chunk_index, total_chunks, indexed_at). Pidä metadatan arvot yksinkertaisina: merkkijonot, luvut ja totuusarvot indeksoituvat ja suodattuvat tehokkaasti, sisäkkäiset objektit eivät.
# Well-designed metadata schema
METADATA_SCHEMA = {
# For filtering at retrieval time
'department': 'HR', # string
'doc_type': 'policy', # string
'year': 2025, # integer
'is_active': True, # boolean
# For display in citations
'title': 'Employee Handbook 2025',
'author': 'HR Team',
'page': 12,
'source': 's3://docs/handbook_2025.pdf',
# For debugging and updates
'chunk_index': 3,
'total_chunks': 24,
'indexed_at': '2025-09-01T10:00:00Z'
}Pitkien indeksointitöiden tarkistuspisteet
Suuren aineiston indeksointi voi kestää tunteja. Kesken työn tapahtuva kaatuminen hukkaa kaiken edistymisen. Ota käyttöön tarkistuspistetiedosto, joka tallentaa onnistuneesti indeksoidut osat. Uudelleenkäynnistyksen yhteydessä ohita jo indeksoidut osat ja jatka siitä, mihin jäit. Näin indeksointityöstä tulee idempotentti ja se voidaan jatkaa turvallisesti. Tallenna tarkistuspiste käsiteltyjen osien tunnusten joukkona JSON-tiedostoon tai tietokantatauluun.
import json
from pathlib import Path
CHECKPOINT_FILE = '/tmp/index_checkpoint.json'
def load_checkpoint():
if Path(CHECKPOINT_FILE).exists():
return set(json.loads(Path(CHECKPOINT_FILE).read_text()))
return set()
def save_checkpoint(indexed_ids):
Path(CHECKPOINT_FILE).write_text(json.dumps(list(indexed_ids)))
def index_with_checkpoint(chunks, index):
done = load_checkpoint()
remaining = [c for c in chunks if c['id'] not in done]
print(f'Resuming: {len(done)} done, {len(remaining)} remaining')
for chunk in remaining:
upsert_to_pinecone([chunk], index)
done.add(chunk['id'])
save_checkpoint(done)Indeksin täydellisyyden varmistaminen
Varmista indeksoinnin jälkeen, että kaikki osat päätyivät vektorivarastoon. Vertaa jakajan tuottamien osien määrää indeksin ilmoittamaan vektorien määrään. Kyselytä indeksiä tunnetun asiakirjan tekstillä ja varmista, että odotettu tulos näkyy viiden parhaan joukossa. Suorita muutama tunnettu kysely kultaisesta testijoukostasi ja tarkista, että täsmällisyys on odotetulla tasolla. Älä koskaan oleta indeksin olevan täydellinen ilman varmistusta.
def verify_index(index, chunks, sample_size=10):
index_stats = index.describe_index_stats()
total_vectors = index_stats.total_vector_count
expected = len(chunks)
print(f'Index vectors: {total_vectors}, Expected: {expected}')
if total_vectors != expected:
print('WARNING: mismatch — some chunks may not have been indexed')
# Spot-check retrieval
import random
sample = random.sample(chunks, sample_size)
for chunk in sample:
vec = embed_batch([chunk['text']])[0]
results = index.query(vector=vec, top_k=1, include_metadata=True)
top_id = results.matches[0].id if results.matches else None
if top_id != chunk['id']:
print(f'WARNING: expected {chunk["id"]}, got {top_id}')Paikalliset vaihtoehdot upotuksille
Käytä paikallisesti isännöityjä upotusmalleja tietosuojan kannalta arkaluonteisille tiedoille, joita ei voida siirtää infrastruktuurisi ulkopuolelle. sentence-transformers-kirjasto tarjoaa laadukkaita malleja, kuten all-MiniLM-L6-v2 (384 ulottuvuutta, 22 Mt, erittäin nopea) ja bge-large-en-v1.5 (1024 ulottuvuutta, parempi laatu). Suorita niitä suorittimella kohtuullisissa työkuormissa tai grafiikkasuorittimella suurissa indeksointitöissä. Paikalliset mallit poistavat API-kustannukset ja tiedonsiirtokustannukset, mutta edellyttävät mallitiedostojen ja laskentaresurssien hallintaa.
from sentence_transformers import SentenceTransformer
# Load once at startup
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
def embed_locally(texts, batch_size=64):
# encode() handles batching internally
embeddings = model.encode(
texts,
batch_size=batch_size,
show_progress_bar=True,
convert_to_numpy=True
)
return embeddings.tolist() # convert numpy array to Python list
vectors = embed_locally([c['text'] for c in chunks])Pikatarkistus
Testaa, miten hyvin ymmärrät tämän oppitunnin AI Engineering -käsitteet.
Oppitunnin kertaus
Tässä oppitunnissa opit: upotusten luomisen erissä OpenAI API:n avulla ja nopeusrajoitusten käsittelyn eksponentiaalisella perääntymisellä, vektoreiden upsertoinnin Pineconeen ja pgvectoriin metadatan kanssa, HNSW-indeksien rakentamisen nopeaa likimääräistä lähinaapurihakua varten sekä tuotantokäytännöt, kuten tarkistuspisteisiin perustuvan jatkamisen, metadatan skeeman suunnittelun ja indeksin täydellisyyden varmistamisen. Seuraavaksi rakennamme kyselyputken, joka hakee osat ja tuottaa lähteisiin perustuvia vastauksia.
Opi Python tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 30
- Oppitunnit
- 120
Usein kysytyt kysymykset
Onko oppitunti ”Indeksointi: palojen upottaminen ja tallentaminen” ilmainen?
Kyllä – oppitunnin ”Indeksointi: palojen upottaminen ja tallentaminen” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko AI Engineering Academy-kurssin, päivitä CoddyKit PROhon. AI Engineering Academy-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Indeksointi: palojen upottaminen ja tallentaminen”?
Upotatte kunkin palan OpenAI Embeddings API:n avulla ja upsertoitte tuloksena saadut vektorit metatietoineen vektoritallennustilaan, jolloin dokumenteista muodostuu haettava indeksi. Harjoittelet AI Engineering Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni AI Engineering Academy-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin AI Engineering Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.
Kuinka kauan ”Indeksointi: palojen upottaminen ja tallentaminen”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä AI Engineering Academy-oppitunnilla?
Kyllä. Jokainen AI Engineering Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Dokumenttien lataus ja tekstin poiminta
- Pilkkomisstrategiat: kiinteä koko, lauseet ja rekursiivisuus
- Indeksointi: palojen upottaminen ja tallentaminen
- Kysy, hae ja generoi