Python & AI Tutorials Logo
LangChain & LangGraph

10. Retrieval yang Lebih Cerdas: Filter, Threshold, dan MMR

Di Bab 9, kita membangun sistem RAG sederhana. Sistem itu adalah pipeline yang memecah dokumen menjadi beberapa chunk, meng-embed-nya ke ChromaDB, dan ketika pertanyaan pengguna masuk, mengambil chunk yang relevan lalu menyertakannya dalam prompt. Hal ini memungkinkan LLM menjawab pertanyaan tentang informasi yang tidak pernah dilatihkan kepadanya, seperti dokumen internal perusahaan dan manual produk. Semuanya tampak berjalan dengan baik.

Tapi coba ajukan pertanyaan dengan variasi yang lebih luas, dan kelemahannya akan muncul dengan cepat. Anda bertanya tentang kebijakan refund konsumen, dan konten toko karyawan ikut tercampur, atau Anda bertanya tentang sesuatu yang tidak ada di dokumen mana pun dan LLM mengarang jawaban yang tampak masuk akal, atau Anda menambah jumlah hasil pencarian dan kualitas jawaban justru menurun.

Di bab ini, kita akan mengatasi ketiga masalah ini satu per satu. Kita akan menggunakan metadata filtering untuk membatasi dokumen mana yang dicari, similarity score threshold untuk mengecualikan hasil yang tidak terkait dengan pertanyaan, dan tuning K dan MMR untuk meningkatkan jumlah dan keragaman chunk yang diberikan ke LLM. Tidak ada alat baru yang dibutuhkan. Kita menyempurnakan konfigurasi dan penggunaan similarity_search() serta penyimpanan vektor Chroma yang sudah Anda kenal.

10.1) Apa yang Salah dengan RAG Kita?

Di Bab 9, kita hanya memasukkan dua dokumen ke dalam penyimpanan vektor — kebijakan refund dan kebijakan pengiriman — dan masing-masing dokumen membahas topik yang berbeda. Kita juga hanya menguji pertanyaan yang jawabannya jelas-jelas ada atau tidak ada di dalam dokumen. Kali ini, kita akan membuat skenario yang lebih realistis. Kita akan menambahkan panduan toko karyawan ke dalam penyimpanan vektor. Dokumen ini juga berisi konten terkait refund, tapi ditujukan untuk karyawan, bukan konsumen umum. Kemudian kita akan mengajukan berbagai pertanyaan dan melihat masalah apa yang muncul.

Persiapan Data: Menambahkan Panduan Toko Karyawan

Berikut dua dokumen dari Bab 9 sebagai referensi.

data/docs/refund_policy.md:

markdown
# Refund Policy
 
**Effective Date**: January 1, 2026
 
## Standard Returns
 
All physical products may be returned within 30 days of purchase for a full refund.
The original receipt or order confirmation email is required. Items must be in their
original packaging and unused condition.
 
After 30 days, returns are accepted for store credit only. Store credit does not expire.
 
## Digital Products
 
Digital products (software licenses, e-books, online courses) are non-refundable
once the download or access link has been activated. If you experience technical
issues preventing access, contact support within 7 days for a replacement or refund.
 
## Defective Items
 
Defective items may be returned at any time for a full refund or replacement.
Please include a description of the defect. Shipping costs for defective returns
are covered by the company.
 
## Subscription Services
 
Monthly subscriptions may be cancelled at any time. Refunds are prorated based on
the remaining days in the billing cycle. Annual subscriptions may be refunded in full
within the first 14 days. After 14 days, no refund is available but access continues until the end of the billing period.

data/docs/shipping_info.md:

markdown
# Shipping Information
 
## Domestic Shipping
 
Standard shipping (5-7 business days): Free on orders over $50, otherwise $5.99.
Express shipping (2-3 business days): $12.99.
Overnight shipping (next business day): $24.99.
 
## International Shipping
 
International orders are shipped via tracked airmail. Delivery times vary by
destination, typically 10-21 business days. International shipping costs are
calculated at checkout based on weight and destination.
 
Customs duties and import taxes are the responsibility of the buyer and are not included in the shipping cost.
 
## Order Tracking
 
All orders include a tracking number sent via email within 24 hours of shipment.
Track your order through the tracking link in your email or through the carrier's website.
 
## Lost or Damaged Packages
 
If your package is lost or arrives damaged, contact support within 48 hours.
We will ship a replacement at no additional cost. For damaged items, please
provide photos of the damage and packaging.

Tambahkan panduan toko karyawan di sini.

Buat data/docs/employee_store.md:

markdown
# Employee Store Guide
 
## Eligibility and Benefits
 
Employees can purchase company products at a 30% discount through the internal employee store.
The monthly purchase limit is $500, and payment can be made via payroll deduction or benefit points.
 
## Ordering and Shipping
 
Employee store orders are placed through the internal portal, and delivery is only available to the company address.
Orders are delivered within 3-5 business days, and shipping is free.
 
## Refund Policy
 
Refunds are available within 7 days of purchase for unopened items only.
Cash refunds are not available; refunds are credited as benefit points.
After opening, only exchanges are allowed, limited to one exchange per identical product.
 
## Contact
 
For employee store inquiries, please contact HR at hr@acme.com.

Direktori data/docs/ kini berisi tiga file: refund_policy.md, shipping_info.md, employee_store.md. Jalankan ulang skrip ingestion dari Bab 9 untuk membangun ulang penyimpanan vektor.

python
# ingest.py — pipeline ingestion yang sama dengan Bab 9
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
 
loader = DirectoryLoader(
    "data/docs/", glob="**/*.md",
    loader_cls=TextLoader, loader_kwargs={"encoding": "utf-8"},
)
documents = loader.load()
print(f"Loaded {len(documents)} documents")
 
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200,
    chunk_overlap=80,
    separators=["\n## ", "\n\n", "\n", " ", ""],
)
chunks = text_splitter.split_documents(documents)
print(f"Created {len(chunks)} chunks")
 
embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")
 
vector_store = Chroma.from_documents(
    documents=chunks,
    embedding=embedding_model,
    persist_directory="data/chroma_db",
    collection_name="company_docs",
)
print(f"Stored {len(chunks)} chunks in ChromaDB")

Output:

Loaded 3 documents
Created 12 chunks
Stored 12 chunks in ChromaDB

Masalah 1: Dokumen yang Tidak Relevan Ikut Tercampur ke Hasil Pencarian

Mari kita cari syarat-syarat refund.

python
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
 
embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = Chroma(
    persist_directory="data/chroma_db",
    collection_name="company_docs",
    embedding_function=embedding_model,
)
 
results = vector_store.similarity_search("What are the refund conditions?", k=3)
 
for i, doc in enumerate(results):
    source = doc.metadata["source"]
    print(f"Result {i+1} [{source}] {doc.page_content[:80]}...")

Output:

Result 1 [data/docs/refund_policy.md] ## Standard Returns
All physical products may be returned within 30 days of pur...
 
Result 2 [data/docs/employee_store.md] ## Refund Policy
Refunds are available within 7 days of purchase for unopened i...
 
Result 3 [data/docs/refund_policy.md] ## Subscription Services
Monthly subscriptions may be cancelled at any time. Re...

Perhatikan Result 2. Seorang pelanggan bertanya tentang syarat refund, dan kebijakan refund toko karyawan ikut masuk ke dalam hasil. Kebijakan refund konsumen mengizinkan refund penuh dalam 30 hari, tetapi toko karyawan hanya mengizinkan refund dalam 7 hari untuk barang yang belum dibuka, dengan refund yang dikreditkan sebagai benefit points. Jika kedua kebijakan ini diteruskan ke LLM bersama-sama, pelanggan bisa saja diberi ketentuan refund yang khusus untuk karyawan.

Masalah 2: Hasil Tetap Dikembalikan Bahkan Saat Tidak Ada Konten yang Relevan

Sekarang mari kita tanyakan sesuatu yang tidak ada di mana pun dalam dokumen kita. Kita akan menggunakan similarity_search_with_score(), yang sudah kita pelajari di Bab 9, untuk juga melihat nilai jarak (distance). Pada ChromaDB, nilai distance yang lebih rendah berarti kemiripan yang lebih tinggi.

python
results = vector_store.similarity_search_with_score(
    "What is the hiring process at this company?", k=3
)
 
for doc, score in results:
    source = doc.metadata["source"]
    print(f"[dist={score:.4f}] [{source}] {doc.page_content[:80]}...")

Output:

[dist=1.4648] [data/docs/employee_store.md] ## Refund Policy
Refunds are available within 7 days of purchase for unopened i...
 
[dist=1.4699] [data/docs/employee_store.md] ## Eligibility and Benefits
Employees can purchase company products at a 30% di...
 
[dist=1.4743] [data/docs/refund_policy.md] ## Subscription Services
Monthly subscriptions may be cancelled at any time. Re...

Tidak ada informasi tentang proses rekrutmen di mana pun. Nilai distance semuanya di atas 1.4, menunjukkan kemiripan yang sangat rendah, namun similarity_search_with_score() tetap mengembalikan tiga chunk. Mari kita lihat jawaban apa yang dihasilkan rantai RAG ketika chunk-chunk ini diteruskan kepadanya.

python
from rag_chain import build_rag_chain  # rantai RAG dari Bab 9
 
chain = build_rag_chain()
answer = chain.invoke("What is the hiring process at this company?")
print(answer)

Output:

I don't have enough information to answer that question.

LLM menjawab bahwa ia tidak memiliki cukup informasi untuk menjawab. Ini karena kita menyertakan instruksi "say you don't have enough information" di system prompt milik build_rag_chain(). Namun, LLM tidak selalu bisa membuat penilaian ini. Jika chunk yang diambil mengandung frasa yang tampak terkait dengan pertanyaan, LLM bisa saja menghasilkan jawaban yang salah berdasarkan konten tersebut.

Masalah 3: Apakah Menambah Hasil Pencarian Selalu Membantu?

Anda mungkin berpikir "bukankah lebih banyak konteks akan lebih baik?" Menaikkan k dari 3 menjadi 10 memang meningkatkan peluang bahwa chunk yang dibutuhkan ikut tersertakan. Tapi pada saat yang sama, lebih banyak chunk yang tidak relevan juga ikut masuk. Karena LLM menerima semua chunk ini sebagai konteks dan menghasilkan jawaban darinya, informasi yang tidak perlu atau salah bisa berakhir dalam respons. Lebih banyak konteks tidak selalu berarti jawaban yang lebih baik.

Selain itu, semua chunk yang diambil diteruskan ke LLM sebagai token. Seiring bertambahnya k, biaya panggilan API meningkat dan waktu respons melambat.

Kita telah mengidentifikasi tiga masalah. Sekarang mari kita selesaikan satu per satu.

10.2) Metadata Filtering: Mempersempit Ruang Pencarian

Saat kita mencari syarat refund di Masalah 1, kebijakan refund pelanggan dan kebijakan refund toko karyawan muncul bersama-sama. Ini terjadi karena kita tidak memberitahu similarity_search() dokumen mana yang harus dicari.

Metadata filtering melampirkan atribut seperti kategori, sumber, dan tahun publikasi pada setiap chunk, lalu memfilter chunk berdasarkan atribut-atribut ini sebelum menjalankan similarity search. Hanya chunk yang cocok dengan kondisi yang melewati komputasi kemiripan. Perannya mirip dengan klausa WHERE pada SQL.

10.2.1) Konten Dokumen vs. Metadata Dokumen

Objek Document yang kita pelajari di Bab 9 berisi dua hal:

  • page_content: Teks itu sendiri. Ini di-embed menjadi vektor dan inilah yang menjadi objek operasi similarity search.
  • metadata: Sebuah dictionary yang menyimpan atribut seperti sumber dan kategori. Nilai-nilai ini tidak di-embed.

Similarity search beroperasi pada page_content, sementara metadata filtering beroperasi pada informasi metadata.

10.2.2) Membangun Ulang Penyimpanan Vektor: Menambahkan Metadata

Kita akan menambahkan atribut category untuk filtering dan membangun ulang penyimpanan vektor. Kita hanya perlu menambahkan kode penetapan metadata ke ingest.py dari 10.1.

python
# ingest_with_metadata.py
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
 
# Langkah 1: Muat dokumen (sama dengan 10.1)
loader = DirectoryLoader(
    "data/docs/", glob="**/*.md",
    loader_cls=TextLoader, loader_kwargs={"encoding": "utf-8"},
)
documents = loader.load()
print(f"Loaded {len(documents)} documents")
 
# Langkah 2: [BARU] Tetapkan metadata category berdasarkan nama file
CATEGORY_MAP = {
    "refund_policy.md": "customer",
    "shipping_info.md": "customer",
    "employee_store.md": "employee",
}
for doc in documents:
    filename = doc.metadata["source"].split("/")[-1]
    doc.metadata["category"] = CATEGORY_MAP.get(filename, "unknown")
 
# Langkah 3: Pecah menjadi chunk (sama dengan 10.1)
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200,
    chunk_overlap=80,
    separators=["\n## ", "\n\n", "\n", " ", ""],
)
chunks = text_splitter.split_documents(documents)
print(f"Created {len(chunks)} chunks")
 
# Langkah 4: Bangun penyimpanan vektor (sama dengan 10.1)
embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")
 
vector_store = Chroma.from_documents(
    documents=chunks,
    embedding=embedding_model,
    persist_directory="data/chroma_db",
    collection_name="company_docs",
)
print(f"Stored {len(chunks)} chunks in ChromaDB")

Output:

Loaded 3 documents
Created 12 chunks
Stored 12 chunks in ChromaDB

Satu-satunya perbedaan dari ingest.py yang asli adalah penambahan metadata category ke setiap dokumen.

10.2.3) Menerapkan Filter pada Pencarian

Sekarang kita bisa menggunakan parameter filter dari similarity_search() untuk membatasi dokumen mana yang dicari. Kita akan mencari dengan query yang sama seperti Masalah 1, tetapi menambahkan filter untuk hanya mencari dokumen yang ditujukan untuk pelanggan.

python
results = vector_store.similarity_search(
    "What are the refund conditions?",
    k=3,
    filter={"category": "customer"},
)
 
for i, doc in enumerate(results):
    source = doc.metadata["source"]
    category = doc.metadata["category"]
    print(f"Result {i+1} [{category}] [{source}] {doc.page_content[:80]}...")

Output:

Result 1 [customer] [data/docs/refund_policy.md] ## Standard Returns
All physical products may be returned within 30 days of pur...
 
Result 2 [customer] [data/docs/refund_policy.md] ## Subscription Services
Monthly subscriptions may be cancelled at any time. Re...
 
Result 3 [customer] [data/docs/refund_policy.md] ## Digital Products
Digital products (software licenses, e-books, online course...

Kebijakan refund toko karyawan yang tadinya tercampur saat Masalah 1 sekarang tidak lagi tersertakan. Ini karena kita menentukan bahwa hanya chunk dengan category sama dengan "customer" yang boleh dicari.

Menggabungkan Beberapa Kondisi

Contoh di atas menggunakan satu kondisi (category sama dengan "customer"). Ketika beberapa kondisi perlu diterapkan secara bersamaan, Anda bisa menggabungkannya dengan operator logika seperti $and dan $or.

python
# $and: hanya chunk yang memenuhi SEMUA kondisi
filter={
    "$and": [
        {"category": "customer"},
        {"source": "data/docs/refund_policy.md"},
    ]
}
 
# $or: chunk yang memenuhi SALAH SATU kondisi
filter={
    "$or": [
        {"source": "data/docs/refund_policy.md"},
        {"source": "data/docs/shipping_info.md"},
    ]
}

Operator tambahan termasuk $ne (tidak sama dengan), $gt (lebih besar dari), dan $lt (lebih kecil dari). Lihat dokumentasi ChromaDB untuk daftar operator lengkap.

10.3) Similarity Threshold: Mengecualikan Hasil dengan Relevansi Rendah

Pada Masalah 2, ketika kita bertanya tentang proses rekrutmen, chunk dengan nilai distance di atas 1.4 dikembalikan. Pada ChromaDB, nilai distance setinggi ini menunjukkan hampir tidak ada relevansi. Namun chunk tersebut tetap diambil. Ini karena similarity_search() selalu mengembalikan k hasil.

Masalah ini bisa diselesaikan dengan menetapkan threshold distance. Hasil yang lebih jauh dari threshold tidak disertakan dalam konteks yang diteruskan ke LLM.

Lalu threshold berapa yang harus kita tetapkan? Mari kita bandingkan dulu nilai distance antara pertanyaan yang memiliki konten relevan di dokumen dan pertanyaan yang tidak memilikinya.

10.3.1) Membandingkan Distance: Pertanyaan Dengan dan Tanpa Konten Relevan

python
queries = [
    "Can I cancel a subscription service?",           # konten relevan ada
    "What is the hiring process at this company?",     # tidak ada konten relevan
]
 
for query in queries:
    print(f"\nQuery: {query}")
    results = vector_store.similarity_search_with_score(query, k=1)
    for doc, score in results:
        print(f"  [dist={score:.4f}] {doc.page_content[:80]}...")

Output:

Query: Can I cancel a subscription service?
  [dist=0.7511] ## Subscription Services
Monthly subscriptions may be cancelled at any time. Re...
 
Query: What is the hiring process at this company?
  [dist=1.4648] ## Refund Policy
Refunds are available within 7 days of purchase for unopened i...

Pertanyaan dengan konten relevan memiliki nilai distance sekitar 0.75, sementara pertanyaan tanpa konten relevan memiliki nilai di atas 1.4. Uji beberapa pertanyaan seperti ini, lalu pilih threshold yang jelas memisahkan kedua kasus tersebut. Threshold yang tepat bisa bervariasi tergantung pada model embedding, sifat dokumen Anda, dan ukuran chunk, jadi sebaiknya tentukan dengan menguji langsung menggunakan data Anda sendiri.

10.3.2) Memfilter Hasil Pencarian Berdasarkan Threshold Distance

Setelah threshold ditetapkan, mari kita bangun fungsi yang memfilter hasil yang melampaui threshold. Hanya chunk yang lolos filter yang disertakan dalam konteks yang diteruskan ke LLM. Jika tidak ada chunk yang tersisa di bawah threshold, kita lewati panggilan LLM sepenuhnya dan merespons dengan "I don't have enough information to answer that question."

python
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-5-mini")
 
def retrieve_or_abstain(query: str, max_distance: float = 1.0, k: int = 3):
    """Kembalikan hanya chunk di bawah threshold distance. Mengembalikan None jika tidak ada yang lolos."""
    scored = vector_store.similarity_search_with_score(query, k=k)
    good = [doc for doc, dist in scored if dist <= max_distance]
    return good or None
 
def safe_answer(query: str) -> str:
    docs = retrieve_or_abstain(query)
    if docs is None:
        return "I don't have enough information to answer that question."
 
    context = "\n\n".join(d.page_content for d in docs)
    prompt = (
        "Answer the question using ONLY the context below.\n\n"
        f"Context:\n{context}\n\nQuestion: {query}"
    )
    return llm.invoke(prompt).content

Mari kita uji dengan pertanyaan yang sama seperti sebelumnya.

python
# Pertanyaan dengan konten relevan
print(safe_answer("Can I cancel a subscription service?"))
print("---")
# Pertanyaan tanpa konten relevan
print(safe_answer("What is the hiring process at this company?"))

Output:

Yes. Monthly subscriptions may be cancelled at any time, and refunds are prorated
based on the remaining days in the billing cycle. Annual subscriptions may be refunded
in full within the first 14 days...
---
I don't have enough information to answer that question.

10.4) K dan MMR: Mengendalikan Jumlah dan Keragaman Hasil Pencarian

Di bagian ini, kita akan langsung membandingkan bagaimana hasil pencarian berubah dengan nilai k yang berbeda, dan mempelajari pencarian MMR, yang meningkatkan keragaman hasil.

10.4.1) Apa yang Terjadi Saat Anda Menaikkan K?

Pada Masalah 3, kita mengatakan bahwa menaikkan k juga membawa masuk lebih banyak chunk yang tidak relevan. Mari kita verifikasi ini. Kita akan mencari dengan k=10 dan memeriksa nilai distance dari setiap chunk.

python
results = vector_store.similarity_search_with_score(
    "What are the refund conditions?",
    k=10,
    filter={"category": "customer"},
)
 
for i, (doc, dist) in enumerate(results, 1):
    source = doc.metadata["source"].split("/")[-1]
    print(f"{i:>2}. [dist={dist:.4f}] [{source}] {doc.page_content[:80]}...")

Output:

 1. [dist=0.7798] [refund_policy.md] ## Standard Returns
**Effective Date**: January 1, 2026
All physical products ma...
 
 2. [dist=0.8203] [refund_policy.md] ## Defective Items
Defective items may be returned at any time for a full refun...
 
 3. [dist=0.9353] [refund_policy.md] ## Subscription Services
Monthly subscriptions may be cancelled at any time. Re...
 
 4. [dist=1.4249] [shipping_info.md] ## Lost or Damaged Packages
If your package is lost or arrives damaged, contact...
 
 5. [dist=1.5516] [shipping_info.md] ## Domestic Shipping
Standard shipping (5-7 business days): Free on orders over...
...

Tiga hasil teratas memiliki distance di bawah 1.0 dan semuanya terkait dengan refund. Mulai dari hasil ke-4, distance melonjak di atas 1.4, dan chunk yang tidak terkait dengan syarat refund — seperti informasi pengiriman — mulai bermunculan. Dengan k=10, semua chunk ini diteruskan ke LLM.

Biaya dari menaikkan k adalah sebagai berikut:

  • Noise: Chunk dengan peringkat lebih rendah mungkin sama sekali tidak terkait dengan pertanyaan. Ketika chunk seperti itu disertakan dalam prompt, LLM mungkin menyertakan informasi yang tidak perlu atau salah dalam jawabannya.
  • Biaya lebih tinggi: Lebih banyak chunk berarti lebih banyak token yang dikirim ke LLM, meningkatkan biaya panggilan API.
  • Respons lebih lambat: Lebih banyak token untuk diproses berarti waktu respons yang lebih lama.

10.4.2) MMR: Mencapai Relevansi dan Keragaman Sekaligus

Di lingkungan dunia nyata, seiring bertumbuhnya koleksi dokumen, umum terjadi munculnya beberapa chunk dengan konten yang serupa. Pengaturan chunk_overlap dari Bab 9, yang membuat chunk yang berdekatan berbagi sebagian konten, juga merupakan sumber duplikasi. Dalam kasus seperti itu, bahkan pencarian dengan k=3 bisa mengembalikan tiga chunk yang nyaris identik.

MMR (Maximum Marginal Relevance) adalah metode pencarian yang mencegah hasil condong ke konten yang sama. Similarity search standar mengembalikan k chunk yang paling dekat dengan query, yang bisa menyebabkan chunk serupa mengelompok di bagian atas. MMR memprioritaskan chunk yang sekaligus relevan dengan query dan berbeda dari hasil yang sudah dipilih.

Berikut cara kerjanya:

  1. Sama seperti similarity search standar, ia pertama-tama mengambil fetch_k chunk kandidat yang paling dekat dengan query.
  2. Ia memilih chunk yang paling dekat dengan query sebagai hasil pertama.
  3. Dari kandidat yang tersisa, ia memilih chunk berikutnya yang relevan dengan query tetapi berbeda kontennya dari chunk yang sudah dipilih.
  4. Langkah 3 diulang sampai k chunk terpilih.

Hasilnya adalah sekumpulan chunk yang mempertahankan relevansi sekaligus menghindari konten yang tumpang tindih.

python
results_mmr = vector_store.max_marginal_relevance_search(
    "What are the refund conditions?",
    k=3,
    fetch_k=10,
)
 
for i, doc in enumerate(results_mmr):
    print(f"{i+1}. {doc.page_content[:80]}...")

Output:

1. ## Standard Returns
All physical products may be returned within 30 days of pur...
 
2. ## Defective Items
Defective items may be returned at any time for a full refun...
 
3. ## Digital Products
Digital products (software licenses, e-books, online course...

Dengan data saat ini, tidak ada perbedaan besar dari pencarian standar karena dataset-nya kecil. Namun, seiring dokumen tumbuh menjadi ratusan atau ribuan, chunk yang serupa sering mengelompok di bagian atas hasil, dan di sinilah MMR menjadi sangat berguna. fetch_k adalah ukuran kumpulan kandidat yang menjadi sumber pemilihan MMR — memulai dengan 10–20 adalah pendekatan yang umum.

10.4.3) Kapan Harus Berhenti Melakukan Tuning

Ada beberapa parameter yang bisa disesuaikan: k, fetch_k, metadata filter, threshold distance, dan lainnya. Mengikuti aturan sederhana berikut membantu Anda melakukan tuning secara efisien:

  1. Siapkan beberapa pertanyaan — sebagian dengan konten relevan di dokumen dan sebagian tanpa.
  2. Jalankan pertanyaan-pertanyaan tersebut dan periksa langsung chunk yang diambil.
  3. Jika sebuah masalah ditemukan, ubah hanya satu parameter dalam satu waktu dan uji ulang dengan pertanyaan yang sama.

Jika pertanyaan dengan konten relevan menghasilkan jawaban yang benar dan pertanyaan tanpa konten relevan berujung pada abstain, Anda telah mencapai kualitas dasar. Membuat pengaturan menjadi sempurna sejak awal tidaklah mudah. Tanggapi masalah yang ditemukan selama penggunaan nyata dan tingkatkan secara bertahap.