0Pricing
AI Engineering Academy · Pelajaran

Pengindeksan: Menyematkan dan Menyimpan Bagian

Sematkan setiap bagian menggunakan API penyematan OpenAI dan masukkan vektor yang dihasilkan beserta metadata ke penyimpanan vektor untuk membangun indeks dokumen yang dapat dicari.

Pengindeksan: Menyematkan dan Menyimpan Bagian adalah pelajaran AI Engineering Academy gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Engineering Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Engineering Academy mencakup 4 pelajaran total.

Tahap Pengindeksan: Gambaran Umum

Setelah memuat dan membagi dokumen menjadi potongan, Anda memasuki tahap pengindeksan: mengubah potongan teks menjadi penyematan vektor dan menyimpannya dalam basis data vektor yang dapat ditelusuri. Ini adalah langkah luring terakhir sebelum kueri dapat dijawab. Kualitas penyematan serta efisiensi strategi penyimpanan dan pengindeksan Anda secara langsung menentukan seberapa cepat dan akurat sistem RAG bekerja saat kueri dijalankan.

Membuat Penyematan melalui API OpenAI

Pendekatan yang paling umum adalah memanggil API penyematan OpenAI dengan teks potongan Anda. Model text-embedding-3-small menghasilkan vektor berdimensi 1536 dan biayanya $0.02 per juta token — sangat murah untuk sebagian besar beban kerja. Kirim beberapa teks dalam satu panggilan API (hingga 2048 input) untuk memaksimalkan keluaran. Respons berisi satu vektor penyematan untuk setiap teks input dalam urutan yang sama.

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')

Pemrosesan Berkelompok untuk Efisiensi

Saat mengindeks ribuan potongan, efisiensi sangat penting. Proses potongan dalam kelompok berisi 100–500 potongan untuk menyeimbangkan keluaran dan penggunaan memori. Lacak posisi Anda agar dapat melanjutkan setelah terjadi kegagalan tanpa menyematkan ulang potongan yang sudah diproses. Catat kemajuan secara berkala. Untuk 100.000 potongan dengan 500 potongan per kelompok, Anda akan melakukan 200 panggilan API — proses ini biasanya selesai dalam beberapa menit.

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 embedded

Menangani Batas Laju Selama Pengindeksan

API penyematan OpenAI memiliki batas laju yang diukur dalam token per menit (TPM). Pekerjaan pengindeksan besar akan mencapai batas ini dan menerima RateLimitError. Terapkan kemunduran eksponensial dengan pengacakan: ketika kesalahan batas laju terjadi, tunggu interval acak yang singkat sebelum mencoba lagi, lalu gandakan waktu tunggu pada setiap kegagalan berikutnya. Dengan cara ini, percobaan ulang tersebar dan semua pekerja paralel tidak membebani API pada saat yang sama.

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 []

Memasukkan Vektor ke Pinecone

Setelah menghasilkan embedding, masukkan semuanya ke penyimpanan vektor dengan operasi upsert. Upsert berarti memasukkan vektor baru atau memperbarui vektor yang sudah ada jika ID yang sama sudah tersedia — dirancang agar idempoten. Di Pinecone, setiap rekaman yang di-upsert berisi ID vektor, nilai embedding, dan kamus metadata berisi bidang yang ingin Anda gunakan untuk penyaringan atau tampilkan nanti. Lakukan upsert dalam kelompok yang masing-masing berisi hingga 100 rekaman per pemanggilan untuk mendapatkan throughput optimal.

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)}')

Menyimpan di pgvector

Dengan pgvector, Anda memasukkan embedding langsung ke dalam tabel PostgreSQL menggunakan SQL standar. Tipe data vector menerima daftar float Python yang diserialisasi sebagai string. Setelah semua baris dimasukkan, buat indeks HNSW untuk kueri tetangga terdekat aproksimasi yang cepat. Pengindeksan tabel yang sudah berisi jutaan baris dapat memerlukan beberapa menit, jadi buat indeks setelah penyisipan massal, bukan sebelumnya.

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()

Membangun Indeks HNSW

HNSW (Hierarchical Navigable Small World) adalah tipe indeks yang memungkinkan pencarian tetangga terdekat aproksimasi dengan cepat. Berbeda dari pencarian brute force (yang membandingkan vektor kueri dengan setiap vektor tersimpan), HNSW membangun struktur graf berlapis yang mempersempit ruang pencarian. Parameter m mengatur jumlah koneksi yang dimiliki setiap simpul (nilai lebih tinggi = recall lebih baik, tetapi membutuhkan lebih banyak memori), sedangkan ef_construction mengatur kualitas indeks selama proses pembangunan.

-- 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';

Merancang Skema Metadata

Metadata yang disimpan bersama setiap vektor memungkinkan pengambilan dengan penyaringan yang canggih. Rancang skema metadata sebelum pengindeksan — penambahan bidang baru nanti memerlukan pengindeksan ulang. Sertakan bidang yang akan Anda gunakan untuk penyaringan (departemen, tipe dokumen, rentang tanggal), bidang yang akan ditampilkan dalam sitasi (judul, halaman, penulis), dan bidang yang berguna untuk penelusuran kesalahan (indeks potongan, jumlah total potongan, waktu pengindeksan). Buat nilai metadata tetap sederhana: string, angka, dan boolean dapat diindeks serta difilter secara efisien; objek bertingkat tidak.

# 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'
}

Membuat Titik Pemeriksaan untuk Pekerjaan Pengindeksan yang Panjang

Mengindeks korpus besar dapat memerlukan waktu berjam-jam. Kerusakan di tengah proses akan menghilangkan semua kemajuan. Terapkan file titik pemeriksaan yang mencatat potongan mana yang telah berhasil diindeks. Saat dimulai ulang, lewati potongan yang sudah diindeks dan lanjutkan dari posisi terakhir. Dengan demikian, pekerjaan pengindeksan menjadi idempoten dan aman untuk dilanjutkan. Simpan titik pemeriksaan sebagai himpunan ID potongan yang telah diproses dalam file JSON atau tabel basis data.

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)

Memverifikasi Kelengkapan Indeks

Setelah pengindeksan, verifikasi bahwa semua potongan telah masuk ke penyimpanan vektor. Bandingkan jumlah potongan yang dihasilkan pemisah Anda dengan jumlah vektor yang dilaporkan indeks. Kuerikan indeks menggunakan teks dokumen yang diketahui dan pastikan hasil yang diharapkan muncul di 5 teratas. Jalankan beberapa kueri yang diketahui dari set pengujian acuan Anda dan periksa apakah presisinya berada pada tingkat yang diharapkan. Jangan pernah menganggap indeks sudah lengkap tanpa memverifikasinya.

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}')

Alternatif Embedding Lokal

Untuk data sensitif yang tidak boleh keluar dari infrastruktur Anda, gunakan model embedding yang di-host secara lokal. Pustaka sentence-transformers menyediakan model berkualitas tinggi seperti all-MiniLM-L6-v2 (384 dimensi, 22 MB, sangat cepat) dan bge-large-en-v1.5 (1024 dimensi, kualitas lebih baik). Jalankan model tersebut di CPU untuk beban kerja sedang atau di GPU untuk pekerjaan pengindeksan besar. Model lokal menghilangkan biaya API dan pengeluaran data, tetapi Anda perlu mengelola file model serta sumber daya komputasi.

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])

Pemeriksaan Singkat

Uji pemahaman Anda tentang konsep Rekayasa AI dari pelajaran ini.

Rangkuman Pelajaran

Dalam pelajaran ini, Anda mempelajari: menghasilkan embedding secara berkelompok dengan API OpenAI dan menangani batas laju menggunakan backoff eksponensial, melakukan upsert vektor ke Pinecone dan pgvector dengan metadata, membangun indeks HNSW untuk pencarian tetangga terdekat aproksimasi yang cepat, serta praktik terbaik untuk produksi, termasuk melanjutkan proses berdasarkan titik pemeriksaan, merancang skema metadata, dan memverifikasi kelengkapan indeks. Berikutnya, kita akan membangun pipeline kueri yang mengambil potongan dan menghasilkan jawaban yang berlandaskan sumber.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Pengindeksan: Menyematkan dan Menyimpan Bagian” gratis?

Ya — teks lengkap “Pengindeksan: Menyematkan dan Menyimpan Bagian” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Engineering Academy, upgrade ke CoddyKit PRO. Kursus AI Engineering Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Pengindeksan: Menyematkan dan Menyimpan Bagian”?

Sematkan setiap bagian menggunakan API penyematan OpenAI dan masukkan vektor yang dihasilkan beserta metadata ke penyimpanan vektor untuk membangun indeks dokumen yang dapat dicari. Kamu berlatih AI Engineering Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai AI Engineering Academy?

Tidak diperlukan pengalaman sebelumnya. AI Engineering Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Pengindeksan: Menyematkan dan Menyimpan Bagian” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Engineering Academy ini?

Ya. Setiap pelajaran AI Engineering Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Memuat Dokumen dan Mengekstrak Teks
  2. Strategi Pembagian: Tetap vs Kalimat vs Rekursif
  3. Pengindeksan: Menyematkan dan Menyimpan Bagian
  4. Mengueri, Mengambil, dan Menghasilkan
← Kembali ke AI Engineering Academy