Tutorial Haystack: Framework NLP untuk Production

# Haystack - Framework NLP untuk Produksi ## Daftar Isi 1. [Pendahuluan](#pendahuluan) 2. [Prasyarat](#prasyarat) 3. [Memahami Arsitektur Haystack](#memahami-arsitektur-haystack) 4. [Document Store]...

By Ruby Abdullah · · tutorial
HaystackNLPRAGDocument StoreRetrievalProduction

Haystack - Framework NLP untuk Produksi

Daftar Isi

  • Pendahuluan
  • Prasyarat
  • Memahami Arsitektur Haystack
  • Document Store
  • Retriever
  • Reader dan Generator
  • Membangun Pipeline RAG
  • Komponen Kustom
  • Evaluasi Pipeline
  • Deployment REST API
  • Praktik Terbaik
  • Kesimpulan

  • Pendahuluan

    Haystack adalah framework NLP open-source yang dirancang untuk membangun sistem pencarian dan tanya-jawab siap produksi. Dikembangkan oleh deepset, Haystack menyediakan arsitektur modular berbasis pipeline yang memudahkan penggabungan retriever, reader, generator, dan komponen NLP lainnya menjadi aplikasi yang powerful.

    Dengan Haystack 2.x, framework ini telah dirancang ulang dengan sistem pipeline fleksibel yang mendukung alur kerja AI ekstraktif maupun generatif. Baik Anda membangun sistem pencarian dokumen sederhana maupun pipeline Retrieval-Augmented Generation (RAG) yang kompleks, Haystack menyediakan blok pembangun dan penghubung untuk menggabungkannya.

    Tutorial ini mencakup konsep inti Haystack, memandu Anda membangun pipeline praktis, menunjukkan cara membuat komponen kustom, dan mendemonstrasikan cara men-deploy sistem Anda sebagai REST API yang siap untuk lalu lintas produksi.


    Prasyarat

    • Python 3.9 atau lebih tinggi
    • Pemahaman dasar konsep NLP (embedding, retrieval, generation)
    • API key OpenAI (untuk komponen generator)

    pip install haystack-ai "sentence-transformers>=2.2.0" "opensearch-haystack" fastapi uvicorn
    

    export OPENAIAPIKEY="sk-kunci-anda-di-sini"
    


    Memahami Arsitektur Haystack

    Haystack 2.x dibangun di atas tiga konsep inti:

    Komponen

    Komponen adalah blok pembangun Haystack. Setiap komponen melakukan tugas spesifik seperti mengambil dokumen, menghasilkan teks, atau mengonversi format file. Komponen mendeklarasikan input dan output secara eksplisit, memungkinkan framework memvalidasi koneksi saat konstruksi pipeline.

    Pipeline

    Pipeline menghubungkan komponen menjadi graf berarah asiklik (DAG). Data mengalir dari satu komponen ke komponen berikutnya berdasarkan koneksi input/output yang dideklarasikan. Pipeline bisa bersifat linear atau bercabang.

    Document Store

    Document store adalah lapisan persistensi. Mereka menyimpan dokumen beserta metadata dan embedding terkait, menyediakan antarmuka pengambilan untuk komponen pipeline.

    from haystack import Pipeline, Document
    

    from haystack.components.writers import DocumentWriter

    from haystack.components.preprocessors import DocumentCleaner, DocumentSplitter

    Pipeline sederhana yang membersihkan dan memecah dokumen

    preprocessing = Pipeline()

    preprocessing.addcomponent("pembersih", DocumentCleaner())

    preprocessing.addcomponent("pemecah", DocumentSplitter(

    splitby="sentence",

    splitlength=3,

    splitoverlap=1,

    ))

    preprocessing.connect("pembersih", "pemecah")

    Jalankan pipeline

    dokumenlist = [

    Document(content="""Haystack adalah framework NLP open-source. Framework ini menyediakan

    komponen modular untuk membangun sistem pencarian. Framework mendukung pendekatan

    ekstraktif dan generatif. Anda dapat menggabungkan beberapa komponen menjadi pipeline.

    Pipeline dapat di-deploy sebagai REST API.""")

    ]

    hasil = preprocessing.run({"pembersih": {"documents": dokumenlist}})

    for dok in hasil["pemecah"]["documents"]:

    print(f" Potongan: {dok.content[:80]}...")


    Document Store

    Document store adalah tulang punggung dari setiap aplikasi Haystack. Mereka menyimpan dokumen dan memungkinkan pengambilan yang efisien.

    InMemoryDocumentStore

    Terbaik untuk pengembangan, pengujian, dan dataset kecil.

    from haystack.documentstores.inmemory import InMemoryDocumentStore
    
    

    Buat document store in-memory

    store = InMemoryDocumentStore(embeddingsimilarityfunction="cosine")

    Tulis dokumen

    dokumen = [

    Document(

    content="Python adalah bahasa pemrograman serbaguna yang digunakan dalam data science.",

    meta={"kategori": "pemrograman", "level": "pemula"}

    ),

    Document(

    content="Container Docker menyederhanakan deployment dan scaling aplikasi.",

    meta={"kategori": "devops", "level": "menengah"}

    ),

    Document(

    content="PostgreSQL adalah sistem database relasional open-source yang powerful.",

    meta={"kategori": "database", "level": "menengah"}

    ),

    Document(

    content="Kubernetes mengorkestrasikan aplikasi terkontainerisasi dalam skala besar.",

    meta={"kategori": "devops", "level": "lanjut"}

    ),

    Document(

    content="FastAPI adalah framework web Python modern untuk membangun REST API.",

    meta={"kategori": "pemrograman", "level": "menengah"}

    ),

    Document(

    content="Redis menyediakan caching in-memory untuk aplikasi berkinerja tinggi.",

    meta={"kategori": "database", "level": "menengah"}

    ),

    Document(

    content="Model machine learning membutuhkan dataset besar untuk pelatihan yang efektif.",

    meta={"kategori": "ai", "level": "pemula"}

    ),

    Document(

    content="Arsitektur Transformers merevolusi pemrosesan bahasa alami.",

    meta={"kategori": "ai", "level": "lanjut"}

    ),

    ]

    store.writedocuments(dokumen)

    print(f"Dokumen di store: {store.countdocuments()}")

    OpenSearch Document Store

    Untuk beban kerja produksi dengan dataset besar.

    from opensearchhaystack.documentstore import OpenSearchDocumentStore
    
    

    Hubungkan ke kluster OpenSearch

    opensearchstore = OpenSearchDocumentStore(

    hosts="http://localhost:9200",

    index="dokumenhaystack",

    embeddingdim=384,

    returnembedding=True,

    )

    Tulis dokumen dengan pipeline

    from haystack.components.writers import DocumentWriter

    from haystack.components.embedders import SentenceTransformersDocumentEmbedder

    pengindeksan = Pipeline()

    pengindeksan.addcomponent("embedder", SentenceTransformersDocumentEmbedder(

    model="sentence-transformers/all-MiniLM-L6-v2"

    ))

    pengindeksan.addcomponent("penulis", DocumentWriter(documentstore=opensearchstore))

    pengindeksan.connect("embedder.documents", "penulis.documents")

    Indeks dokumen

    pengindeksan.run({"embedder": {"documents": dokumen}})


    Retriever

    Retriever menemukan dokumen yang relevan dari document store berdasarkan query. Haystack mendukung beberapa strategi pengambilan.

    Pengambilan Berbasis Embedding

    from haystack.components.embedders import (
    

    SentenceTransformersTextEmbedder,

    SentenceTransformersDocumentEmbedder,

    )

    from haystack.components.retrievers.inmemory import InMemoryEmbeddingRetriever

    Pertama, embed dan simpan dokumen

    docembedder = SentenceTransformersDocumentEmbedder(

    model="sentence-transformers/all-MiniLM-L6-v2"

    )

    docembedder.warmup()

    dokdenganembedding = docembedder.run(documents=dokumen)

    store = InMemoryDocumentStore(embeddingsimilarityfunction="cosine")

    store.writedocuments(dokdenganembedding["documents"])

    Bangun pipeline pengambilan

    pipelinepengambilan = Pipeline()

    pipelinepengambilan.addcomponent("textembedder", SentenceTransformersTextEmbedder(

    model="sentence-transformers/all-MiniLM-L6-v2"

    ))

    pipelinepengambilan.addcomponent("retriever", InMemoryEmbeddingRetriever(

    documentstore=store,

    topk=3,

    ))

    pipelinepengambilan.connect("textembedder.embedding", "retriever.queryembedding")

    Pencarian

    hasil = pipelinepengambilan.run({"textembedder": {"text": "alat orkestrasikan container"}})

    print("Dokumen yang diambil:")

    for dok in hasil["retriever"]["documents"]:

    print(f" [{dok.score:.4f}] {dok.content}")

    print(f" Meta: {dok.meta}")

    Pengambilan Kata Kunci BM25

    from haystack.components.retrievers.inmemory import InMemoryBM25Retriever
    
    

    BM25 tidak memerlukan embedding

    bm25store = InMemoryDocumentStore()

    bm25store.writedocuments(dokumen)

    bm25retriever = InMemoryBM25Retriever(documentstore=bm25store, topk=3)

    Pencarian

    hasil = bm25retriever.run(query="database PostgreSQL")

    print("Hasil BM25:")

    for dok in hasil["documents"]:

    print(f" [{dok.score:.4f}] {dok.content}")

    Pengambilan Hybrid

    Gabungkan pengambilan embedding dan BM25 untuk hasil terbaik.

    from haystack.components.joiners import DocumentJoiner
    

    from haystack.components.rankers import TransformersSimilarityRanker

    pipelinehybrid = Pipeline()

    Cabang pengambilan embedding

    pipelinehybrid.addcomponent("textembedder", SentenceTransformersTextEmbedder(

    model="sentence-transformers/all-MiniLM-L6-v2"

    ))

    pipelinehybrid.addcomponent("embeddingretriever", InMemoryEmbeddingRetriever(

    documentstore=store, topk=5

    ))

    Cabang pengambilan BM25

    pipelinehybrid.addcomponent("bm25retriever", InMemoryBM25Retriever(

    documentstore=bm25store, topk=5

    ))

    Gabungkan dan rerank

    pipelinehybrid.addcomponent("penggabung", DocumentJoiner())

    pipelinehybrid.addcomponent("peranking", TransformersSimilarityRanker(

    model="cross-encoder/ms-marco-MiniLM-L-6-v2", topk=3

    ))

    Hubungkan komponen

    pipelinehybrid.connect("textembedder.embedding", "embeddingretriever.queryembedding")

    pipelinehybrid.connect("embeddingretriever", "penggabung")

    pipelinehybrid.connect("bm25retriever", "penggabung")

    pipelinehybrid.connect("penggabung", "peranking")

    Jalankan pencarian hybrid

    hasil = pipelinehybrid.run({

    "textembedder": {"text": "alat deployment untuk aplikasi"},

    "bm25retriever": {"query": "alat deployment untuk aplikasi"},

    "peranking": {"query": "alat deployment untuk aplikasi"},

    })

    print("Hasil pencarian hybrid:")

    for dok in hasil["peranking"]["documents"]:

    print(f" [{dok.score:.4f}] {dok.content}")


    Reader dan Generator

    Reader mengekstrak jawaban dari dokumen (QA ekstraktif). Generator membuat teks baru berdasarkan dokumen (QA generatif / RAG).

    Reader Ekstraktif

    from haystack.components.readers import ExtractiveReader
    
    

    reader = ExtractiveReader(model="deepset/roberta-base-squad2")

    reader.warmup()

    Ekstrak jawaban dari dokumen

    hasil = reader.run(

    query="Apa itu PostgreSQL?",

    documents=[

    Document(content="PostgreSQL adalah sistem database relasional open-source yang powerful."),

    Document(content="Redis menyediakan caching in-memory untuk aplikasi berkinerja tinggi."),

    ],

    topk=2,

    )

    for jawaban in hasil["answers"]:

    print(f" Jawaban: '{jawaban.data}' (skor: {jawaban.score:.4f})")

    print(f" Konteks: {jawaban.document.content}")

    Komponen Generatif dengan OpenAI

    from haystack.components.generators import OpenAIGenerator
    

    from haystack.components.builders import PromptBuilder

    Buat prompt builder

    templateprompt = """Berdasarkan dokumen konteks berikut, jawab pertanyaan tersebut.

    Jika jawaban tidak ditemukan dalam konteks, katakan "Saya tidak memiliki informasi yang cukup."

    Konteks:

    {% for dok in documents %}

    • {{ dok.content }}
    {% endfor %}

    Pertanyaan: {{ question }}

    Jawaban:"""

    promptbuilder = PromptBuilder(template=templateprompt)

    generator = OpenAIGenerator(model="gpt-4o")

    Bangun pipeline QA generatif sederhana

    pipelinegen = Pipeline()

    pipelinegen.addcomponent("promptbuilder", promptbuilder)

    pipelinegen.addcomponent("generator", generator)

    pipelinegen.connect("promptbuilder", "generator")

    Jalankan

    dokkonteks = [

    Document(content="PostgreSQL adalah sistem database relasional open-source yang powerful."),

    Document(content="PostgreSQL mendukung tipe data lanjutan, full-text search, dan JSON."),

    ]

    hasil = pipelinegen.run({

    "promptbuilder": {

    "documents": dokkonteks,

    "question": "Fitur apa saja yang didukung PostgreSQL?"

    }

    })

    print(f"Jawaban: {hasil['generator']['replies'][0]}")


    Membangun Pipeline RAG

    Retrieval-Augmented Generation (RAG) adalah pola paling umum dalam aplikasi AI modern. Ia mengambil dokumen yang relevan dan menggunakannya sebagai konteks untuk generasi LLM.

    Pipeline RAG Lengkap

    from haystack import Pipeline
    

    from haystack.components.embedders import SentenceTransformersTextEmbedder

    from haystack.components.retrievers.inmemory import InMemoryEmbeddingRetriever

    from haystack.components.builders import PromptBuilder

    from haystack.components.generators import OpenAIGenerator

    def bangunpipelinerag(documentstore: InMemoryDocumentStore) -> Pipeline:

    """Bangun pipeline RAG lengkap."""

    promptrag = """Anda adalah asisten teknis yang membantu. Jawab pertanyaan berdasarkan

    konteks yang diberikan. Berikan jawaban spesifik dan kutip informasi yang relevan.

    Konteks:

    {% for dok in documents %}

    [Sumber {{ loop.index }}] {{ dok.content }}

    {% endfor %}

    Pertanyaan: {{ question }}

    Instruksi:

    • Jawab hanya berdasarkan konteks yang diberikan
    • Referensikan sumber menggunakan notasi [Sumber N]
    • Jika konteks tidak memuat jawaban, nyatakan dengan jelas
    • Ringkas namun menyeluruh

    Jawaban:"""

    pipeline = Pipeline()

    # Embedding query

    pipeline.addcomponent("textembedder", SentenceTransformersTextEmbedder(

    model="sentence-transformers/all-MiniLM-L6-v2"

    ))

    # Pengambilan dokumen

    pipeline.addcomponent("retriever", InMemoryEmbeddingRetriever(

    documentstore=documentstore,

    topk=5,

    ))

    # Konstruksi prompt

    pipeline.addcomponent("promptbuilder", PromptBuilder(template=promptrag))

    # Generasi LLM

    pipeline.addcomponent("generator", OpenAIGenerator(

    model="gpt-4o",

    generationkwargs={"temperature": 0.3, "maxtokens": 500},

    ))

    # Hubungkan pipeline

    pipeline.connect("textembedder.embedding", "retriever.queryembedding")

    pipeline.connect("retriever.documents", "promptbuilder.documents")

    pipeline.connect("promptbuilder", "generator")

    return pipeline

    Bangun dan jalankan

    rag = bangunpipelinerag(store)

    pertanyaanlist = [

    "Apa alat terbaik untuk orkestrasikan container?",

    "Bahasa pemrograman mana yang terbaik untuk data science?",

    "Bagaimana cara meningkatkan performa database?",

    ]

    for pertanyaan in pertanyaanlist:

    hasil = rag.run({

    "textembedder": {"text": pertanyaan},

    "promptbuilder": {"question": pertanyaan},

    })

    print(f"\nT: {pertanyaan}")

    print(f"J: {hasil['generator']['replies'][0]}")

    RAG dengan Filter Metadata

    def bangunragterfilter(documentstore: InMemoryDocumentStore) -> Pipeline:
    

    """Pipeline RAG dengan dukungan filter metadata."""

    promptrag = """Jawab pertanyaan berdasarkan konteks di bawah.

    Konteks:

    {% for dok in documents %}

    • {{ dok.content }} [{{ dok.meta.kategori }}/{{ dok.meta.level }}]
    {% endfor %}

    Pertanyaan: {{ question }}

    Jawaban:"""

    pipeline = Pipeline()

    pipeline.addcomponent("textembedder", SentenceTransformersTextEmbedder(

    model="sentence-transformers/all-MiniLM-L6-v2"

    ))

    pipeline.addcomponent("retriever", InMemoryEmbeddingRetriever(

    documentstore=documentstore, topk=3

    ))

    pipeline.addcomponent("promptbuilder", PromptBuilder(template=promptrag))

    pipeline.addcomponent("generator", OpenAIGenerator(model="gpt-4o"))

    pipeline.connect("textembedder.embedding", "retriever.queryembedding")

    pipeline.connect("retriever.documents", "promptbuilder.documents")

    pipeline.connect("promptbuilder", "generator")

    return pipeline

    ragterfilter = bangunragterfilter(store)

    hasil = ragterfilter.run({

    "textembedder": {"text": "Alat DevOps apa yang harus saya pelajari?"},

    "retriever": {"filters": {"field": "meta.kategori", "operator": "==", "value": "devops"}},

    "promptbuilder": {"question": "Alat DevOps apa yang harus saya pelajari?"},

    })

    print(f"Jawaban: {hasil['generator']['replies'][0]}")


    Komponen Kustom

    Kekuatan Haystack terletak pada kemampuan perluasannya. Anda dapat membuat komponen kustom untuk tugas apa pun.

    Membuat Komponen Kustom

    from haystack import component
    

    from haystack.dataclasses import Document

    from typing import List, Optional

    @component

    class FilterKonten:

    """Komponen kustom yang memfilter dokumen berdasarkan panjang konten dan keberadaan kata kunci."""

    def init(self, panjangminimal: int = 10, katakunciwajib: Optional[List[str]] = None):

    self.panjangminimal = panjangminimal

    self.katakunciwajib = katakunciwajib or []

    @component.outputtypes(documents=List[Document], jumlahterfilter=int)

    def run(self, documents: List[Document]) -> dict:

    terfilter = []

    for dok in documents:

    if len(dok.content) < self.panjangminimal:

    continue

    if self.katakunciwajib:

    kontenlower = dok.content.lower()

    if not any(kk.lower() in kontenlower for kk in self.katakunciwajib):

    continue

    terfilter.append(dok)

    return {

    "documents": terfilter,

    "jumlahterfilter": len(documents) - len(terfilter),

    }

    Gunakan komponen kustom dalam pipeline

    pipeline = Pipeline()

    pipeline.addcomponent("filter", FilterKonten(

    panjangminimal=20,

    katakunciwajib=["Python", "database"]

    ))

    hasil = pipeline.run({"filter": {"documents": dokumen}})

    print(f"Terfilter: {hasil['filter']['jumlahterfilter']} dokumen dihapus")

    Pengaya Dokumen Kustom

    @component
    

    class PengayaMetadata:

    """Memperkaya dokumen dengan field metadata yang dihitung."""

    def init(self, hitungwaktubaca: bool = True, hitungjumlahkata: bool = True):

    self.hitungwaktubaca = hitungwaktubaca

    self.hitungjumlahkata = hitungjumlahkata

    @component.outputtypes(documents=List[Document])

    def run(self, documents: List[Document]) -> dict:

    diperkaya = []

    for dok in documents:

    metabaru = dict(dok.meta)

    if self.hitungjumlahkata:

    metabaru["jumlahkata"] = len(dok.content.split())

    if self.hitungwaktubaca:

    kata = len(dok.content.split())

    metabaru["waktubacamenit"] = round(kata / 200, 1)

    metabaru["adakode"] = "

    " in dok.content or "def " in dok.content

    diperkaya.append(Document(

    content=dok.content, meta=metabaru, embedding=dok.embedding,

    ))

    return {"documents": diperkaya}

    Gunakan dalam pipeline

    pipelinepengayaan = Pipeline()

    pipelinepengayaan.addcomponent("pengaya", PengayaMetadata())

    hasil = pipelinepengayaan.run({"pengaya": {"documents": dokumen}})

    for dok in hasil["pengaya"]["documents"]:

    print(f" {dok.content[:50]}... | Kata: {dok.meta.get('jumlahkata')} | "

    f"Baca: {dok.meta.get('waktubacamenit')} menit")

    
    

    Peranking Kustom dengan Logika Bisnis

    python

    @component

    class PerankingBisnis:

    """Meranking ulang dokumen berdasarkan aturan bisnis kustom."""

    def init(self, kategoriboost: Optional[List[str]] = None, faktorboost: float = 1.5):

    self.kategoriboost = kategoriboost or []

    self.faktorboost = faktorboost

    @component.outputtypes(documents=List[Document])

    def run(self, documents: List[Document], topk: int = 5) -> dict:

    dokberskor = []

    for dok in documents:

    skor = dok.score or 0.0

    if dok.meta.get("kategori") in self.kategoriboost:

    skor = self.faktorboost

    if dok.meta.get("level") == "pemula":

    skor = 1.1

    dokberskor.append((dok, skor))

    dokberskor.sort(key=lambda x: x[1], reverse=True)

    hasildok = []

    for dok, skor in dokberskor[:topk]:

    dok.score = skor

    hasildok.append(dok)

    return {"documents": hasildok}

    
    

    Evaluasi Pipeline

    Mengevaluasi pipeline Anda sangat penting untuk memahami kekuatan dan kelemahannya.

    python

    import numpy as np

    class EvaluatorPipeline:

    """Evaluasi kualitas pipeline RAG dengan berbagai metrik."""

    def init(self, pipeline: Pipeline):

    self.pipeline = pipeline

    def evaluasipengambilan(self, kasusuji: list[dict], k: int = 5) -> dict:

    """Evaluasi kualitas pengambilan.

    kasusuji: list dari {"query": str, "iddokrelevan": set}

    """

    metrik = {"presisi": [], "recall": [], "mrr": []}

    for kasus in kasusuji:

    hasil = self.pipeline.run({

    "textembedder": {"text": kasus["query"]},

    })

    dokdiambil = hasil.get("retriever", {}).get("documents", [])

    iddiambil = {dok.id for dok in dokdiambil[:k]}

    idrelevan = kasus["iddokrelevan"]

    # Presisi@k

    presisi = len(iddiambil & idrelevan) / len(iddiambil) if iddiambil else 0

    metrik["presisi"].append(presisi)

    # Recall@k

    recall = len(iddiambil & idrelevan) / len(idrelevan) if idrelevan else 0

    metrik["recall"].append(recall)

    # MRR

    mrr = 0

    for i, dok in enumerate(dokdiambil[:k], 1):

    if dok.id in idrelevan:

    mrr = 1.0 / i

    break

    metrik["mrr"].append(mrr)

    return {

    nama: {"ratarata": float(np.mean(nilai)), "std": float(np.std(nilai))}

    for nama, nilai in metrik.items()

    }

    def evaluasigenerasi(self, kasusuji: list[dict]) -> dict:

    """Evaluasi kualitas generasi menggunakan LLM-as-judge."""

    from haystack.components.generators import OpenAIGenerator

    juri = OpenAIGenerator(model="gpt-4o")

    skorlist = []

    for kasus in kasusuji:

    hasil = self.pipeline.run({

    "textembedder": {"text": kasus["query"]},

    "promptbuilder": {"question": kasus["query"]},

    })

    aktual = hasil["generator"]["replies"][0]

    promptjuri = f"""Beri nilai jawaban berikut pada skala 1-5.

    Jawaban yang diharapkan: {kasus['jawabandiharapkan']}

    Jawaban aktual: {aktual}

    Kembalikan hanya angka (1-5)."""

    hasiljuri = juri.run(prompt=promptjuri)

    try:

    skor = int(hasiljuri["replies"][0].strip())

    skorlist.append(min(max(skor, 1), 5))

    except (ValueError, IndexError):

    skorlist.append(1)

    return {

    "skorratarata": float(np.mean(skorlist)),

    "skorminimum": int(min(skorlist)),

    "skormaksimum": int(max(skorlist)),

    "totaldievaluasi": len(skorlist),

    }

    
    

    Deployment REST API

    Deploy pipeline Haystack Anda sebagai REST API siap produksi.

    python

    from fastapi import FastAPI, HTTPException

    from pydantic import BaseModel, Field

    from typing import Optional

    import uvicorn

    app = FastAPI(

    title="API NLP Haystack",

    description="Pipeline NLP siap produksi yang didukung Haystack",

    version="1.0.0",

    )

    pipelinerag = None

    documentstore = None

    @app.onevent("startup")

    async def startup():

    global pipelinerag, documentstore

    documentstore = InMemoryDocumentStore(embeddingsimilarityfunction="cosine")

    docembedder = SentenceTransformersDocumentEmbedder(

    model="sentence-transformers/all-MiniLM-L6-v2"

    )

    docembedder.warmup()

    hasil = docembedder.run(documents=dokumen)

    documentstore.writedocuments(hasil["documents"])

    pipelinerag = bangunpipelinerag(documentstore)

    class PermintaanQuery(BaseModel):

    pertanyaan: str = Field(description="Pertanyaan bahasa alami")

    topk: int = Field(default=5, ge=1, le=20, description="Jumlah dokumen yang diambil")

    filter: Optional[dict] = Field(default=None, description="Filter metadata")

    class DokumenSumber(BaseModel):

    konten: str

    skor: float

    metadata: dict

    class ResponQuery(BaseModel):

    jawaban: str

    sumber: list[DokumenSumber]

    query: str

    @app.post("/query", responsemodel=ResponQuery)

    async def query(permintaan: PermintaanQuery):

    """Ajukan pertanyaan dan dapatkan jawaban yang dihasilkan AI beserta sumbernya."""

    if pipelinerag is None:

    raise HTTPException(statuscode=503, detail="Pipeline belum diinisialisasi")

    try:

    paramrun = {

    "textembedder": {"text": permintaan.pertanyaan},

    "promptbuilder": {"question": permintaan.pertanyaan},

    "retriever": {"topk": permintaan.topk},

    }

    if permintaan.filter:

    paramrun["retriever"]["filters"] = permintaan.filter

    hasil = pipelinerag.run(paramrun)

    jawaban = hasil["generator"]["replies"][0]

    dokretriever = hasil["retriever"]["documents"]

    sumber = [

    DokumenSumber(

    konten=dok.content,

    skor=dok.score or 0.0,

    metadata=dok.meta,

    )

    for dok in dokretriever

    ]

    return ResponQuery(

    jawaban=jawaban, sumber=sumber, query=permintaan.pertanyaan,

    )

    except Exception as e:

    raise HTTPException(statuscode=500, detail=str(e))

    class PermintaanIndeks(BaseModel):

    dokumen: list[dict] = Field(description="Dokumen dengan kunci 'content' dan 'meta' opsional")

    @app.post("/indeks", statuscode=201)

    async def indeksdokumen(permintaan: PermintaanIndeks):

    """Indeks dokumen baru ke document store."""

    dokbaru = [

    Document(content=d["content"], meta=d.get("meta", {}))

    for d in permintaan.dokumen

    ]

    docembedder = SentenceTransformersDocumentEmbedder(

    model="sentence-transformers/all-MiniLM-L6-v2"

    )

    docembedder.warmup()

    hasil = docembedder.run(documents=dokbaru)

    documentstore.writedocuments(hasil["documents"])

    return {"terindeks": len(dokbaru), "total": documentstore.countdocuments()}

    @app.get("/kesehatan")

    async def kesehatan():

    return {

    "status": "sehat",

    "pipelinesiap": pipelinerag is not None,

    "dokumen": documentstore.countdocuments() if documentstore else 0,

    }

    if name == "main":

    uvicorn.run(app, host="0.0.0.0", port=8000)

    ```


    Praktik Terbaik

  • Mulai dengan InMemoryDocumentStore untuk prototyping, lalu beralih ke OpenSearch atau Elasticsearch untuk produksi.
  • Gunakan pengambilan hybrid (embedding + BM25) untuk kualitas pencarian terbaik.
  • Tambahkan reranker setelah pengambilan untuk meningkatkan presisi di posisi teratas.
  • Potong dokumen dengan tepat -- terlalu kecil kehilangan konteks, terlalu besar mengencerkan relevansi.
  • Version control pipeline Anda dengan serialisasi ke YAML untuk reprodusibilitas.
  • Monitor kualitas pengambilan dengan evaluasi rutin terhadap golden test set.
  • Gunakan prompt engineering untuk meningkatkan kualitas generasi -- iterasi pada template prompt Anda.
  • Implementasikan caching untuk komputasi embedding guna mengurangi latensi dan biaya.
  • Tangani error dengan anggun di setiap tahap pipeline dengan perilaku fallback.
  • Lakukan load test deployment Anda sebelum go-live untuk memahami batas throughput.

  • Kesimpulan

    Haystack menyediakan framework yang powerful dan modular untuk membangun sistem NLP produksi. Arsitektur pipeline-nya memudahkan penggabungan retriever, reader, generator, dan komponen kustom menjadi aplikasi yang canggih.

    Poin-poin kunci:

    • Arsitektur komponen-pipeline Haystack 2.x memungkinkan sistem NLP yang bersih dan mudah dipelihara.
    • Document store menyediakan persistensi fleksibel dengan dukungan embedding dan filter metadata.
    • Pengambilan hybrid (embedding + BM25) dengan reranking memberikan kualitas pencarian terbaik.
    • Komponen kustom memungkinkan Anda memperluas Haystack dengan logika spesifik domain.
    • Evaluasi sistematis sangat penting -- ukur presisi pengambilan, recall, dan kualitas generasi.
    • Integrasi FastAPI memudahkan deployment pipeline sebagai REST API produksi.

    Haystack terus berkembang dengan cepat. Tetap mengikuti rilis terbaru untuk komponen baru, peningkatan performa, dan dukungan model yang lebih luas.

    Artikel Terkait

    Tutorial Lengkap txtai: Database Embeddings All-in-One untuk Semantic Search dan LLM Workflows

    Tutorial Lengkap txtai: Database Embeddings All-in-One untuk Semantic Search dan LLM Workflows txtai adalah framework Py...

    Tutorial GraphRAG: Retrieval Augmented Generation Berbasis Graph Knowledge

    Tutorial GraphRAG: Retrieval Augmented Generation Berbasis Graph Knowledge Pendahuluan Retrieval Augmented Generation (R...

    Tutorial ColBERT & RAGatouille: Late-Interaction Retrieval untuk RAG

    ColBERT & RAGatouille: Retrieval Late-Interaction untuk RAG yang Lebih Baik Sebagian besar sistem RAG mengandalkan dense...

    Tutorial LlamaParse: Parsing Dokumen Kompleks untuk RAG Pipeline

    Tutorial LlamaParse: Parsing Dokumen Kompleks untuk RAG Pipeline Membangun aplikasi Retrieval-Augmented Generation (RAG)...