Skip to Content

Chunking Strategies

Khái niệm cơ bản

Tưởng tượng bạn đang cắt một chiếc pizza khổng lồ…

  • Miếng quá to: Một người không ăn hết, phí phạm.
  • Miếng quá nhỏ: Mất công gắp, thiếu thỏa mãn.
  • Miếng vừa phải: Perfect!

Chunking documents cho AI cũng vậy:

  • Chunk quá dài: Vector bị “loãng” ý nghĩa, khó match chính xác.
  • Chunk quá ngắn: Thiếu context, AI hiểu sai.
  • Chunk hợp lý: Trọn vẹn một ý, dễ tìm kiếm.

Tại sao phải Chunking?

  1. Context Window giới hạn: Không thể ném cả cuốn sách 500 trang vào prompt.
  2. Chi phí: Tokens = Tiền. Chunk nhỏ = Ít tokens = Rẻ hơn.
  3. Precision: Chunk nhỏ hơn = Vector đại diện chính xác hơn cho nội dung.

4 Chiến lược Chunking

1. Fixed-size Chunking (Cắt cố định)

def fixed_size_chunk(text: str, chunk_size: int = 500, overlap: int = 50): chunks = [] start = 0 while start < len(text): end = start + chunk_size chunks.append(text[start:end]) start = end - overlap # Overlap để giữ mạch văn return chunks
ƯuNhược
Nhanh, đơn giảnCó thể cắt giữa câu
Predictable sizeKhông quan tâm cấu trúc văn bản

Use case: Logs, data đơn giản.


2. Recursive Character Chunking

Cắt thông minh theo hierarchy: \n\n\n.

from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200, separators=["\n\n", "\n", ".", " "] ) chunks = splitter.split_text(long_document)
ƯuNhược
Giữ paragraphs nguyên vẹnChunk size có thể không đều
Ngắt tại điểm tự nhiênVẫn có thể cắt giữa ý

Use case: Tài liệu, sách, articles. Recommended mặc định!


3. Semantic Chunking

Dùng Embeddings để detect sự thay đổi chủ đề:

from langchain_experimental.text_splitter import SemanticChunker from langchain_openai import OpenAIEmbeddings embeddings = OpenAIEmbeddings() chunker = SemanticChunker(embeddings) # Tự động phát hiện khi nào đổi chủ đề chunks = chunker.split_text(document)

Cách hoạt động:

  1. Embed từng câu
  2. Tính cosine similarity giữa câu liên tiếp
  3. Nếu similarity drops mạnh → Ngắt chunk mới
ƯuNhược
Chunks trọn vẹn ý nghĩaTốn compute (phải embed)
Tối ưu cho retrievalChậm hơn

Use case: Research papers, legal docs.


4. Agentic Chunking

Dùng LLM để đọc và quyết định:

def agentic_chunk(text: str) -> list[str]: prompt = f""" Đọc văn bản sau và chia thành các chunks logic. Mỗi chunk phải: - Chứa trọn vẹn một ý/concept - 100-500 từ TEXT: {text} Trả về dạng JSON: ["chunk1", "chunk2", ...] """ return llm.generate(prompt)
ƯuNhược
Chunks chất lượng cao nhấtRất chậm
Hiểu contextĐắt (gọi LLM mỗi doc)

Use case: High-value documents, contract analysis.


Comparison Chart

StrategySpeedQualityCostBest For
Fixed⚡⚡⚡💵Logs, simple data
Recursive⚡⚡⭐⭐⭐💵General docs (default!)
Semantic⭐⭐⭐⭐💵💵Research, legal
Agentic🐢⭐⭐⭐⭐⭐💵💵💵High-value docs

Best Practices

1. Chunk Size Guidelines

Content TypeRecommended SizeOverlap
Short Q&A200-500 tokens20-50
Articles/Docs500-1000 tokens100-200
Books/Long1000-2000 tokens200-400

2. Bao gồm Metadata

chunks = [] for i, chunk in enumerate(raw_chunks): chunks.append({ "content": chunk, "metadata": { "source": "company_handbook.pdf", "page": i // 2, # Ước tính page "chunk_index": i, "total_chunks": len(raw_chunks) } })

3. Test với Real Queries

test_queries = [ "Chính sách nghỉ phép như thế nào?", "Làm sao để xin làm việc từ xa?", "Quy định về OT?" ] for query in test_queries: results = vector_db.search(query, top_k=3) print(f"Query: {query}") print(f"Top result: {results[0]['content'][:100]}...") # Kiểm tra xem chunk có chứa answer không

Bài tập thực hành: So sánh Chunking Strategies

Mục tiêu

So sánh kết quả retrieval giữa Fixed và Recursive chunking.

Code

from langchain.text_splitter import CharacterTextSplitter, RecursiveCharacterTextSplitter import chromadb # Sample document doc = """ [Dán một bài viết dài 2000+ từ ở đây] """ # Strategy 1: Fixed fixed_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50) fixed_chunks = fixed_splitter.split_text(doc) # Strategy 2: Recursive recursive_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) recursive_chunks = recursive_splitter.split_text(doc) print(f"Fixed: {len(fixed_chunks)} chunks") print(f"Recursive: {len(recursive_chunks)} chunks") # So sánh chunk đầu tiên print("\n--- Fixed chunk 1 ---") print(fixed_chunks[0]) print("\n--- Recursive chunk 1 ---") print(recursive_chunks[0])

Câu hỏi phân tích

  1. Chunk nào bị cắt giữa câu?
  2. Strategy nào giữ paragraph nguyên vẹn hơn?
  3. Với query cụ thể, strategy nào return kết quả tốt hơn?

Tóm tắt

Khái niệmÝ nghĩa
ChunkĐoạn văn bản nhỏ để embed
OverlapPhần gối đầu giữa chunks
RecursiveMặc định, cắt theo hierarchy
SemanticCắt dựa trên thay đổi ý nghĩa

Bài tiếp theo: Hybrid Search - Kết hợp keyword + semantic.

Last updated on