Menerapkan strategi pemotongan RAG dengan LangChain dan watsonx.ai

Dalam tutorial ini, Anda akan bereksperimen dengan beberapa strategi pemotongan menggunakan LangChain dan model IBM Granite terbaru yang sekarang tersedia di watsonx.ai. Tujuan keseluruhannya adalah untuk melakukan retrieval-augmented generation (RAG).

Apa itu pemotongan?

Pemotongan mengacu pada proses memecah potongan teks besar menjadi segmen atau potongan teks yang lebih kecil. Untuk menekankan pentingnya pemotongan, akan sangat membantu untuk memahami RAG. RAG adalah teknik dalam pemrosesan bahasa alami (NLP) yang menggabungkan pengambilan informasi dan model bahasa besar (LLM) untuk mengambil informasi yang relevan dari kumpulan data tambahan untuk mengoptimalkan kualitas output LLM. Untuk mengelola dokumen besar, kita dapat menggunakan pemotongan untuk membagi teks menjadi potongan kecil dari potongan yang berarti. Potongan teks ini kemudian dapat ditanam dan disimpan dalam basis data vektor melalui penggunaan model penanaman. Terakhir, sistem RAG kemudian dapat menggunakan pencarian semantik untuk mengambil hanya potongan yang paling relevan. Potongan yang lebih kecil cenderung mengungguli potongan yang lebih besar karena cenderung lebih mudah dikelola untuk model dengan ukuran jendela konteks yang lebih kecil.

Beberapa komponen utama pemotongan meliputi:

  • Strategi pemotongan: Memilih strategi pemotongan yang tepat untuk aplikasi RAG Anda penting, karena ini menentukan batas untuk menetapkan potongan. Kami akan menjelajahi beberapa di antaranya di bagian selanjutnya.
  • Ukuran potongan: Jumlah maksimum token yang ada di setiap potongan. Menentukan ukuran potongan yang sesuai biasanya melibatkan beberapa percobaan.  
  • Potongan tumpang tindih: Jumlah token yang tumpang tindih antara potongan untuk mempertahankan konteks. Ini adalah parameter opsional.

Strategi pemotongan

Ada beberapa pilihan strategi pemotongan yang berbeda. Penting untuk memilih teknik pemotongan yang paling efektif untuk contoh penggunaan spesifik aplikasi LLM Anda. Beberapa proses pemotongan yang umum digunakan meliputi:r: 

  • Pemotongan ukuran tetap: Pemisahan teks dengan ukuran potongan tertentu dan tumpang tindih potongan opsional. Pendekatan ini adalah yang paling umum dan mudah.
  • Pemotongan rekursif: Mengulangi pemisah default hingga salah satu dari mereka menghasilkan ukuran potongan yang diinginkan. Pemisah default termasuk ["\n\n", "\n", " ", ""]. Metode pemotongan ini menggunakan pemisah hirarkis sehingga paragraf, diikuti oleh kalimat dan kemudian kata-kata, sebisa mungkin tetap bersama.
  • Pemotongan semantik: Memisahkan teks dengan cara mengelompokkan kalimat berdasarkan kemiripan semantik dari penanaman. Penanaman kesamaan semantik tinggi lebih dekat satu sama lain daripada penanaman dengan kesamaan semantik rendah. Hal ini menghasilkan potongan yang sadar konteks.
  • Pemotongan berbasis dokumen: Pemisahan berdasarkan struktur dokumen. Pembagi ini dapat menggunakan teks Markdown, gambar, tabel, dan bahkan kelas dan fungsi kode Python sebagai cara untuk menentukan struktur. Dengan demikian, dokumen besar dapat dipotong dan diproses oleh LLM.
  • Pemotongan agen: Memanfaatkan AI agen dengan memungkinkan LLM menentukan pemisahan dokumen yang sesuai berdasarkan makna semantik serta struktur konten seperti jenis paragraf, judul bagian, petunjuk langkah demi langkah, dan lainnya. Pemotong ini bersifat eksperimental dan mencoba menyimulasikan penalaran manusia saat memproses dokumen yang panjang.

Langkah-langkah

Langkah 1. Siapkan lingkungan Anda

Meskipun terdapat pilihan beberapa alat, tutorial ini akan memandu Anda untuk menyiapkan akun IBM menggunakan Jupyter Notebook.

  1. Masuk ke watsonx.ai menggunakan akun IBM Cloud Anda.

  2. Buat proyek watsonx.ai.

    Anda bisa mendapatkan ID proyek dari dalam proyek Anda. Klik tab Kelola . Kemudian, salin ID proyek dari bagian Detail di halaman Umum . Anda memerlukan ID ini untuk tutorial ini.

  3. Buat Jupyter Notebook.

Langkah ini akan membuka lingkungan Notebook tempat Anda dapat menyalin kode dari tutorial ini. Sebagai alternatif, Anda dapat mengunduh notebook ini ke sistem lokal Anda dan mengunggahnya ke proyek watsonx.ai Anda sebagai aset. Untuk melihat tutorial Granite lainnya, kunjungi Komunitas IBM Granite. Aplikasi Jupyter Notebook ini bersama dengan kumpulan data yang digunakan dapat ditemukan di GitHub.

Langkah 2. Siapkan instans waktu proses watsonx.ai dan kunci API

  1. Buat instans layanan watsonx.ai Runtime (pilih wilayah yang sesuai dan pilih paket Lite, yang merupakan instans gratis).

  2. Buat Kunci API.

  3. Tautkan instans layanan watsonx.ai Runtime ke proyek yang telah Anda buat di watsonx.ai.

Langkah 3. Instal dan impor perpustakaan yang relevan dan siapkan kredensial Anda

#installations
!pip install -q langchain langchain-ibm langchain_experimental langchain-text-splitters langchain_chroma transformers bs4 langchain_huggingface sentence-transformers
# imports
import getpass
from langchain_ibm import WatsonxLLM
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from ibm_watsonx_ai.metanames import GenTextParamsMetaNames as GenParams
from transformers import AutoTokenizer

Untuk mengatur kredensial, kita memerlukan WATSONX_APIKEY dan WATSONX_PROJECT_ID yang Anda buat dalam langkah 1. Kita juga akan mengatur URL yang berfungsi sebagai titik akhir API.

WATSONX_APIKEY = getpass.getpass("Please enter your watsonx.ai Runtime API key (hit enter): ")
WATSONX_PROJECT_ID = getpass.getpass("Please enter your project ID (hit enter): ")
URL = "https://us-south.ml.cloud.ibm.com"

Langkah 4. Awali LLM Anda

Kami akan menggunakan Granite 3.1 sebagai LLM untuk tutorial ini. Untuk mengawali LLM, kita perlu mengatur parameter model. Untuk mengetahui lebih lanjut tentang parameter model ini, seperti batas token minimum dan maksimum, lihat dokumentasi.

llm = WatsonxLLM(
        model_id= "ibm/granite-3-8b-instruct",
        url=URL,
        apikey=WATSONX_APIKEY,
        project_id=WATSONX_PROJECT_ID,
        params={
            GenParams.DECODING_METHOD: "greedy",
            GenParams.TEMPERATURE: 0,
            GenParams.MIN_NEW_TOKENS: 5,
            GenParams.MAX_NEW_TOKENS: 2000,
            GenParams.REPETITION_PENALTY:1.2
        }
)

Langkah 5. Muat dokumen Anda

Konteks yang kami gunakan untuk saluran RAG kami adalah pengumuman resmi IBM untuk rilis Granite 3.1. Kita dapat memuat blog ke dokumen langsung dari halaman web dengan menggunakan WebBaseLoader dari LangChain.

url = "https://www.ibm.com/id-id/new/announcements/ibm-granite-3-1-powerful-performance-long-context-and-more"
doc = WebBaseLoader(url).load()

Langkah 6. Lakukan pemisahan teks

Mari kita berikan contoh kode untuk menerapkan tiap strategi pemotongan yang kita bahas sebelumnya dalam tutorial ini yang tersedia melalui LangChain.

Pemotongan berukuran tetap

Untuk mengimplementasikan pemotongan dengan ukuran tetap, kita dapat menggunakan CharacterTextSplitter dari LangChain dan mengatur chunk_size serta chunk_overlap. Chunk_size diukur dengan jumlah karakter. Jangan ragu untuk bereksperimen dengan nilai yang berbeda. Kami juga akan mengatur agar pemisah menjadi karakter baris baru sehingga kami dapat membedakan antara paragraf. Untuk tokenisasi, kita dapat menggunakan komponen tokenisasi granite-3.1-8b-instruct . Tokenisasi memecah teks menjadi token yang dapat diproses oleh LLM.

from langchain_text_splitters import CharacterTextSplitter
tokenizer = AutoTokenizer.from_pretrained(“ibm-granite/granite-3.1-8b-instruct”)
text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
                    tokenizer,
                    separator=”\n”, #default: “\n\n”
                    chunk_size=1200, chunk_overlap=200)
fixed_size_chunks = text_splitter.create_documents([doc[0].page_content])

Kita dapat mencetak salah satu potongan untuk memahami struktur mereka lebih baik.

fixed_size_chunks[1]

Output: (terpotong)

Document(metadata={}, page_content=’As always, IBM’s historical commitment to open source is reflected in the permissive and standard open source licensing for every offering discussed in this article.\n\r\n Granite 3.1 8B Instruct: raising the bar for lightweight enterprise models\r\n \nIBM’s efforts in the ongoing optimization the Granite series are most evident in the growth of its flagship 8B dense model. IBM Granite 3.1 8B Instruct now bests most open models in its weight class in average scores on the academic benchmarks evaluations included in the Hugging Face OpenLLM Leaderboard...’)

Kami juga dapat menggunakan tokenisasi untuk memverifikasi proses dan memeriksa jumlah token yang ada di setiap potongan. Langkah ini opsional dan hanya untuk menunjukkan pada Anda.

for idx, val in enumerate(fixed_size_chunks):
    token_count = len(tokenizer.encode(val.page_content))
    print(f”The chunk at index {idx} contains {token_count} tokens.”)

Output:

The chunk at index 0 contains 1106 tokens.
The chunk at index 1 contains 1102 tokens.
The chunk at index 2 contains 1183 tokens.
The chunk at index 3 contains 1010 tokens.

Hebat! Sepertinya ukuran potongan kami diimplementasikan dengan tepat.

Pemotongan rekursif

Untuk pemotongan rekursif, kita dapat menggunakan RecursiveCharacterTextSplitter dari LangChain. Seperti contoh pemotongan berukuran tetap, kita dapat bereksperimen dengan ukuran potongan dan tumpang tindih yang berbeda.

from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)
recursive_chunks = text_splitter.create_documents([doc[0].page_content])
recursive_chunks[:5]

Output:

[Document(metadata={}, page_content=’IBM Granite 3.1: powerful performance, longer context and more’),
Document(metadata={}, page_content=’IBM Granite 3.1: powerful performance, longer context, new embedding models and more’),
Document(metadata={}, page_content=’Artificial Intelligence’),
Document(metadata={}, page_content=’Compute and servers’),
Document(metadata={}, page_content=’IT automation’)]

Pembagi berhasil memotong teks dengan menggunakan pemisah default: [“\ n\ n”, “\ n”, ““, “”].

Pemotongan semantik

Pemotongan semantik membutuhkan model penanaman atau encoder. Kita dapat menggunakan model Granite-embedding-30m-english sebagai model penanaman kita. Kami juga dapat mencetak salah satu potongan untuk pemahaman yang lebih baik tentang strukturnya.

from langchain_huggingface import HuggingFaceEmbeddings
from langchain_experimental.text_splitter import SemanticChunker

embeddings_model = HuggingFaceEmbeddings(model_name=”ibm-granite/granite-embedding-30m-english”)
text_splitter = SemanticChunker(embeddings_model)
semantic_chunks = text_splitter.create_documents([doc[0].page_content])
semantic_chunks[1]

Output: (terpotong)

Document(metadata={}, page_content=’Our latest dense models (Granite 3.1 8B, Granite 3.1 2B), MoE models (Granite 3.1 3B-A800M, Granite 3.1 1B-A400M) and guardrail models (Granite Guardian 3.1 8B, Granite Guardian 3.1 2B) all feature a 128K token context length.We’re releasing a family of all-new embedding models. The new retrieval-optimized Granite Embedding models are offered in four sizes, ranging from 30M–278M parameters. Like their generative counterparts, they offer multilingual support across 12 different languages: English, German, Spanish, French, Japanese, Portuguese, Arabic, Czech, Italian, Korean, Dutch and Chinese. Granite Guardian 3.1 8B and 2B feature a new function calling hallucination detection capability, allowing increased control over and observability for agents making tool calls...’)

Pemotongan berbasis dokumen

Dokumen dari berbagai jenis file kompatibel dengan pemisah teks berbasis dokumen dari LangChain. Untuk tujuan tutorial ini, kami akan menggunakan file Markdown. Untuk contoh pemisahan JSON rekursif, pemisahan kode, dan pemisahan HTML, lihat dokumentasi LangChain.

Contoh file Markdown yang dapat kita muat adalah file README untuk Granite 3.1 di GitHub IBM.

url = “https://raw.githubusercontent.com/ibm-granite/granite-3.1-language-models/refs/heads/main/README.md”
markdown_doc = WebBaseLoader(url).load()
markdown_doc

Output:

[Document(metadata={‘source’: ‘https://raw.githubusercontent.com/ibm-granite/granite-3.1-language-models/refs/heads/main/README.md’}, page_content=’\n\n\n\n :books: Paper (comming soon)\xa0 | :hugs: HuggingFace Collection\xa0 | \n :speech_balloon: Discussions Page\xa0 | 📘 IBM Granite Docs\n\n\n---\n## Introduction to Granite 3.1 Language Models\nGranite 3.1 language models are lightweight, state-of-the-art, open foundation models that natively support multilinguality, coding, reasoning, and tool usage, including the potential to be run on constrained compute resources. All the models are publicly released under an Apache 2.0 license for both research and commercial use. The models\’ data curation and training procedure were designed for enterprise usage and customization, with a process that evaluates datasets for governance, risk and compliance (GRC) criteria, in addition to IBM\’s standard data clearance process and document quality checks...’)]

Sekarang, kita dapat menggunakan MarkdownHeaderTextSplitter dari LangChain untuk membagi file berdasarkan jenis judul, yang kita atur dalam daftar headers_to_split_on. Kami juga akan mencetak salah satu potongan sebagai contoh.

#document based chunking
from langchain_text_splitters import MarkdownHeaderTextSplitter
headers_to_split_on = [
    (“#”, “Header 1”),
    (“##”, “Header 2”),
    (“###”, “Header 3”),
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on)
document_based_chunks = markdown_splitter.split_text(markdown_doc[0].page_content)
document_based_chunks[3]

Output:

Document(metadata={‘Header 2’: ‘How to Use our Models?’, ‘Header 3’: ‘Inference’}, page_content=’This is a simple example of how to use Granite-3.1-1B-A400M-Instruct model. \n```python\nimport torch\nfrom transformers import AutoModelForCausalLM, AutoTokenizer\n\ndevice = “auto”\nmodel_path = “ibm-granite/granite-3.1-1b-a400m-instruct”\ntokenizer = AutoTokenizer.from_pretrained(model_path)\n# drop device_map if running on CPU\nmodel = AutoModelForCausalLM.from_pretrained(model_path, device_map=device)\nmodel.eval()\n# change input text as desired\nchat = [\n{ “role”: “user”, “content”: “Please list one IBM Research laboratory located in the United States. You should only output its name and location.” },\n]\nchat = tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)\n# tokenize the text\ninput_tokens = tokenizer(chat, return_tensors=”pt”).to(device)\n# generate output tokens\noutput = model.generate(**input_tokens,\nmax_new_tokens=100)\n# decode output tokens into text\noutput = tokenizer.batch_decode(output)\n# print output\nprint(output)\n```’)

Seperti yang Anda lihat di output, pemotongan berhasil membagi teks berdasarkan jenis judul.

Langkah 7. Buat penyimpanan vektor

Sekarang kita telah bereksperimen dengan berbagai strategi pemotongan, mari kita beralih ke implementasi RAG. Untuk tutorial ini, kita akan memilih potongan yang dihasilkan oleh pemisahan semantik dan mengubahnya menjadi penanaman vektor. Penyimpanan vektor sumber terbuka yang dapat kita gunakan adalah Chroma DB. Kita dapat dengan mudah mengakses fungsionalitas Chroma melalui paket langchain_chroma.

Mari kita awali basis data vektor Chroma, memberikannya model penanaman, dan menambahkan dokumen kita yang dihasilkan oleh pemotongan semantik.

vector_db = Chroma(
    collection_name=”example_collection”,
    embedding_function=embeddings_model,
    persist_directory=”./chroma_langchain_db”, # Where to save data locally
)
vector_db.add_documents(semantic_chunks)

Output:

[‘84fcc1f6-45bb-4031-b12e-031139450cf8’,
‘433da718-0fce-4ae8-a04a-e62f9aa0590d’,
‘4bd97cd3-526a-4f70-abe3-b95b8b47661e’,
‘342c7609-b1df-45f3-ae25-9d9833829105’,
‘46a452f6-2f02-4120-a408-9382c240a26e’]

Langkah 8. Susun templat prompt

Selanjutnya, kita dapat beralih ke pembuatan templat prompt untuk LLM kita. Templat prompt ini memungkinkan kita untuk mengajukan banyak pertanyaan tanpa mengubah struktur prompt awal. Kami juga dapat memberikan penyimpanan vektor sebagai pengambil. Langkah ini menyelesaikan struktur RAG.

from langchain.chains import create_retrieval_chain
from langchain.prompts import PromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain

prompt_template = """<|start_of_role|>user<|end_of_role|>Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.
{context}
Question: {input}<|end_of_text|>
<|start_of_role|>assistant<|end_of_role|>"""

qa_chain_prompt = PromptTemplate.from_template(prompt_template)
combine_docs_chain = create_stuff_documents_chain(llm, qa_chain_prompt)
rag_chain = create_retrieval_chain(vector_db.as_retriever(), combine_docs_chain)

Langkah 9. Perintahkan rantai RAG

Dengan menggunakan alur kerja RAG yang telah selesai, mari kita panggil kueri pengguna. Pertama, kita dapat secara strategis memerintahkan model tanpa konteks tambahan dari penyimpanan vektor yang kita buat untuk menguji apakah model menggunakan pengetahuan bawaannya atau benar-benar menggunakan konteks RAG. Blog pengumuman Granite 3.1 menyebutkan Docling, alat bantu IBM untuk mengurai berbagai jenis dokumen dan mengubahnya menjadi Markdown atau JSON. Mari kita tanyakan kepada LLM tentang Docling.

output = llm.invoke(“What is Docling?”)
output

Output:

‘?\n\n”Docling” does not appear to be a standard term in English. It might be a typo or a slang term specific to certain contexts. If you meant “documenting,” it refers to the process of creating and maintaining records, reports, or other written materials that provide information about an activity, event, or situation. Please check your spelling or context for clarification.’

Jelas, model ini tidak dilatih tentang informasi tentang Docling dan tanpa alat atau informasi eksternal, model ini tidak dapat memberikan informasi ini kepada kami. Sekarang, mari kita coba memberikan kueri yang sama ke rantai RAG yang kita buat.

rag_output = rag_chain.invoke({“input”: “What is Docling?”})
rag_output[‘answer’]

Output:

‘Docling is a powerful tool developed by IBM Deep Search for parsing documents in various formats such as PDF, DOCX, images, PPTX, XLSX, HTML, and AsciiDoc, and converting them into model-friendly formats like Markdown or JSON. This enables easier access to the information within these documents for models like Granite for tasks such as RAG and other workflows. Docling is designed to integrate seamlessly with agentic frameworks like LlamaIndex, LangChain, and Bee, providing developers with the flexibility to incorporate its assistance into their preferred ecosystem. It surpasses basic optical character recognition (OCR) and text extraction methods by employing advanced contextual and element-based preprocessing techniques. Currently, Docling is open-sourced under the permissive MIT License, and the team continues to develop additional features, including equation and code extraction, as well as metadata extraction.’

Hebat! Model Granite dengan tepat menggunakan konteks RAG untuk memberi tahu kami informasi yang benar tentang Docling sekaligus mempertahankan koherensi semantik. Kami membuktikan hasil yang sama ini tidak mungkin terjadi tanpa menggunakan RAG.

Ringkasan

Dalam tutorial ini, Anda membuat saluran RAG dan bereksperimen dengan beberapa strategi pemotongan untuk meningkatkan akurasi pengambilan sistem. Menggunakan model Granite 3.1, kami berhasil menghasilkan respons model yang sesuai untuk kueri pengguna yang terkait dengan dokumen yang disediakan sebagai konteks. Teks yang kami gunakan untuk implementasi RAG ini dimuat dari blog di ibm.com yang mengumumkan rilis Granite 3.1. Model ini memberi kami informasi yang hanya dapat diakses melalui konteks yang disediakan, karena bukan bagian dari basis pengetahuan awal model.

Bagi mereka yang mencari bacaan lebih lanjut, lihat hasil proyek yang membandingkan kinerja LLM menggunakan pemotongan terstruktur HTML dibandingkan dengan pemotongan watsonx.

Solusi terkait
IBM watsonx.ai

Latih, validasi, lakukan tuning, dan terapkan AI generatif, model dasar, dan kemampuan machine learning dengan IBM watsonx.ai, studio perusahaan generasi berikutnya untuk pembangun AI. Bangun aplikasi AI dalam waktu singkat, dengan sedikit data.

Temukan watsonx.ai
Solusi kecerdasan buatan (AI)

Gunakan AI di bisnis Anda dalam perpaduan antara keahlian AI terdepan di industri dari IBM dan portofolio solusi Anda.

Jelajahi solusi AI
Layanan AI

Temukan kembali alur kerja dan operasi yang penting dengan menambahkan AI untuk memaksimalkan pengalaman, pengambilan keputusan secara real-time, dan nilai bisnis.

Jelajahi layanan AI
Ambil langkah selanjutnya

Dapatkan akses satu atap ke kemampuan yang mencakup siklus hidup pengembangan AI. Hasilkan solusi AI yang kuat dengan antarmuka ramah pengguna, alur kerja yang efisien, serta akses ke API dan SDK berstandar industri.

Jelajahi watsonx.ai Pesan demo langsung