ในโลกของ AI ที่มีต้นทุนการประมวลผลสูงขึ้นทุกวัน การจัดการ Token อย่างมีประสิทธิภาพกลายเป็นทักษะที่จำเป็นสำหรับนักพัฒนาทุกคน วันนี้ผมจะพาคุณมาสำรวจ เทคนิค Context Compression ที่ช่วยลดการใช้ Token ลงอย่างน้อย 40-60% โดยไม่สูญเสียคุณภาพของผลลัพธ์ พร้อมตัวอย่างโค้ดที่ใช้งานได้จริงกับ HolySheep AI ผู้ให้บริการ AI API ราคาประหยัดกว่า 85% พร้อมความเร็วตอบสนองน้อยกว่า 50 มิลลิวินาที
บทนำ: ทำไม Context Compression ถึงสำคัญ
จากประสบการณ์ตรงของผมในการพัฒนาระบบ RAG สำหรับองค์กรขนาดใหญ่ ปัญหาที่พบบ่อยที่สุดคือ ต้นทุน Token ที่พุ่งสูง โดยเฉพาะเมื่อต้องประมวลผลเอกสารจำนวนมาก เช่น แคตตาล็อกสินค้า คู่มือการใช้งาน หรือฐานข้อมูลความรู้
Context Compression คือการบีบอัดข้อมูลบริบทก่อนส่งให้ LLM ประมวลผล โดยมีหลักการสำคัญ 3 ประการ:
- การแยกข้อมูลสำคัญ — ดึงเฉพาะส่วนที่เกี่ยวข้องกับคำถาม
- การสรุปย่อ — ย่อเนื้อหายาวให้กระชับโดยรักษาความหมาย
- การจัดโครงสร้าง — จัดรูปแบบข้อมูลให้เหมาะกับการประมวลผล
กรณีศึกษา: ระบบ RAG ของอีคอมเมิร์ซขนาดใหญ่
ผมเคยพัฒนาระบบ AI สำหรับร้านค้าออนไลน์ที่มีสินค้ากว่า 50,000 รายการ ปัญหาคือเมื่อลูกค้าถามคำถามทั่วไป เช่น "สินค้านี้เหมาะกับผิวแพ้ง่ายไหม" ระบบต้องดึงข้อมูลจากเอกสารหลายร้อยหน้า ทำให้ใช้ Token มากและตอบช้า
หลังจากนำ Context Compression มาใช้ ผลลัพธ์ที่ได้คือ:
- ลด Token ลง 55% จากเฉลี่ย 8,000 Token เหลือ 3,600 Token ต่อคำถาม
- เพิ่มความเร็วการตอบสนอง 2.3 เท่า
- ลดต้นทุนต่อเดือนจาก $1,200 เหลือ $540
การติดตั้งและโค้ดตัวอย่าง
เริ่มจากการติดตั้งไลบรารีที่จำเป็นกับ HolySheep AI:
# ติดตั้งไลบรารีที่จำเป็น
pip install langchain langchain-community faiss-cpu tiktoken
หรือใช้ version ล่าสุด
pip install --upgrade langchain faiss-cpu
โค้ดตัวอย่างต่อไปนี้แสดงการสร้าง Context Compressor พื้นฐานที่ใช้งานได้จริง:
import os
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain_community.llms import OpenAI
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
ตั้งค่า HolySheep AI API
os.environ["OPENAI_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY"
os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1"
สร้าง LLM สำหรับ Compression
llm = OpenAI(
model_name="gpt-4o-mini",
temperature=0.3,
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
สร้าง Embeddings
embeddings = OpenAIEmbeddings(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
โหลด Vector Store ที่มีอยู่
vectorstore = FAISS.load_local(
"ecommerce_faiss_index",
embeddings,
allow_dangerous_deserialization=True
)
สร้าง Compressor
compressor = LLMChainExtractor.from_llm(llm)
สร้าง Compression Retriever
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vectorstore.as_retriever(search_kwargs={"k": 20})
)
ค้นหาด้วยการบีบอัดอัตโนมัติ
def search_products(query: str):
compressed_docs = compression_retriever.get_relevant_documents(query)
# สร้าง Context ที่บีบอัดแล้ว
context = "\n\n".join([doc.page_content for doc in compressed_docs])
# นับ Token ที่ใช้
token_count = len(context.split()) * 1.3 # Approximation
return {
"context": context,
"token_estimate": int(token_count),
"sources": len(compressed_docs)
}
ทดสอบการค้นหา
result = search_products("ครีมกันแดดเหมาะกับผิวแพ้ง่ายไหม")
print(f"Token ที่ใช้: {result['token_estimate']}")
print(f"แหล่งข้อมูล: {result['sources']} รายการ")
Advanced Context Compression ด้วย Custom Filter
สำหรับโปรเจกต์ที่ต้องการประสิทธิภาพสูงขึ้น ผมแนะนำให้สร้าง Custom Compressor ที่ทำงานเร็วกว่าและลด Token ได้มากกว่า:
import re
from typing import List, Optional
from langchain.schema import Document
from langchain.retrievers.document_compressors import BaseDocumentCompressor
class SemanticContextCompressor(BaseDocumentCompressor):
"""
Custom Context Compressor ที่รวมหลายเทคนิค:
- การกรองข้อมูลตามความเกี่ยวข้อง
- การลบข้อมูลซ้ำ
- การตัดข้อความที่ไม่จำเป็น
"""
def __init__(
self,
llm = None,
keep_top_n: int = 5,
min_relevance_score: float = 0.6,
language: str = "thai"
):
self.llm = llm
self.keep_top_n = keep_top_n
self.min_relevance_score = min_relevance_score
self.language = language
# คำที่ไม่มีความหมายในภาษาไทย
self.stopwords = {
"thai": ["ค่ะ", "ครับ", "นะคะ", "นะครับ", "เนี่ย", "อ่ะ", "ฮะ"],
"english": ["the", "a", "an", "and", "or", "but", "in", "on", "at"]
}
def compress_documents(
self,
documents: List[Document],
query: str
) -> List[Document]:
if not documents:
return []
# ขั้นตอนที่ 1: กรองเอกสารตามความเกี่ยวข้อง
filtered_docs = self._filter_by_relevance(documents, query)
# ขั้นตอนที่ 2: ลบข้อมูลซ้ำซ้อน
deduped_docs = self._deduplicate(filtered_docs)
# ขั้นตอนที่ 3: ตัดข้อความที่ไม่จำเป็น
cleaned_docs = [self._clean_document(doc) for doc in deduped_docs]
# ขั้นตอนที่ 4: จำกัดจำนวนเอกสาร
final_docs = cleaned_docs[:self.keep_top_n]
# ขั้นตอนที่ 5: เพิ่ม metadata
for i, doc in enumerate(final_docs):
doc.metadata["compression_order"] = i + 1
doc.metadata["original_length"] = len(doc.page_content)
return final_docs
def _filter_by_relevance(
self,
documents: List[Document],
query: str
) -> List[Document]:
# แยก keywords จาก query
query_terms = set(query.lower().split())
filtered = []
for doc in documents:
doc_terms = set(doc.page_content.lower().split())
# คำนวณความเกี่ยวข้อง
overlap = len(query_terms & doc_terms)
relevance = overlap / len(query_terms) if query_terms else 0
if relevance >= self.min_relevance_score:
doc.metadata["relevance_score"] = relevance
filtered.append(doc)
# เรียงลำดับตามความเกี่ยวข้อง
return sorted(filtered, key=lambda x: x.metadata.get("relevance_score", 0), reverse=True)
def _deduplicate(self, documents: List[Document]) -> List[Document]:
seen_content = set()
unique_docs = []
for doc in documents:
# สร้าง fingerprint จาก content
fingerprint = self._create_fingerprint(doc.page_content)
if fingerprint not in seen_content:
seen_content.add(fingerprint)
unique_docs.append(doc)
return unique_docs
def _create_fingerprint(self, text: str) -> str:
# ตัดช่องว่างและทำให้เป็นตัวพิมพ์เล็ก
cleaned = re.sub(r'\s+', '', text.lower())
return cleaned[:100] # ใช้แค่ 100 ตัวอักษรแรกเป็น fingerprint
def _clean_document(self, doc: Document) -> Document:
content = doc.page_content
# ลบ stopwords
lang = self.language
if lang in self.stopwords:
for word in self.stopwords[lang]:
content = re.sub(rf'\b{word}\b', '', content, flags=re.IGNORECASE)
# ลบช่องว่างเกิน
content = re.sub(r'\s+', ' ', content).strip()
# ตัดข้อความที่ยาวเกินไป (เก็บแค่ 500 ตัวอักษรแรก)
if len(content) > 500:
content = content[:500] + "..."
doc.page_content = content
return doc
async def acompress_documents(
self,
documents: List[Document],
query: str
) -> List[Document]:
return self.compress_documents(documents, query)
วิธีการใช้งาน
compressor = SemanticContextCompressor(
keep_top_n=5,
min_relevance_score=0.5,
language="thai"
)
ใช้กับ retriever
from langchain.retrievers import ContextualCompressionRetriever
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vectorstore.as_retriever(search_kwargs={"k": 20})
)
การใช้งานกับ RAG Pipeline แบบ Complete
ตัวอย่างนี้แสดง RAG Pipeline ที่สมบูรณ์พร้อม Context Compression และการจัดการ Error:
import os
import json
from datetime import datetime
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field
from enum import Enum
ตั้งค่า HolySheep API
HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
os.environ["OPENAI_API_KEY"] = HOLYSHEEP_API_KEY
os.environ["OPENAI_API_BASE"] = HOLYSHEEP_BASE_URL
from langchain_openai import ChatOpenAI
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
class CompressionStrategy(Enum):
BASIC = "basic"
SEMANTIC = "semantic"
HYBRID = "hybrid"
@dataclass
class TokenUsage:
"""ติดตามการใช้ Token"""
original_tokens: int = 0
compressed_tokens: int = 0
final_tokens: int = 0
compression_ratio: float = 0.0
def calculate_savings(self) -> Dict[str, Any]:
if self.original_tokens == 0:
return {"savings_percent": 0, "saved_tokens": 0}
saved = self.original_tokens - self.compressed_tokens
savings_percent = (saved / self.original_tokens) * 100
return {
"saved_tokens": saved,
"savings_percent": round(savings_percent, 2),
"cost_reduction_estimate": f"${saved * 0.0001:.4f}" # ประมาณการ
}
@dataclass
class RAGResult:
"""ผลลัพธ์จาก RAG Pipeline"""
answer: str
sources: List[str] = field(default_factory=list)
token_usage: TokenUsage = field(default_factory=TokenUsage)
metadata: Dict[str, Any] = field(default_factory=dict)
error: Optional[str] = None
class CompressionRAGPipeline:
"""
RAG Pipeline พร้อม Context Compression
รองรับหลาย Strategy และมี Error Handling
"""
def __init__(
self,
api_key: str,
base_url: str,
vectorstore,
strategy: CompressionStrategy = CompressionStrategy.HYBRID,
model_name: str = "gpt-4o-mini"
):
self.api_key = api_key
self.base_url = base_url
self.vectorstore = vectorstore
self.strategy = strategy
# สร้าง LLM
self.llm = ChatOpenAI(
model=model_name,
temperature=0.3,
api_key=api_key,
base_url=base_url,
max_retries=3,
request_timeout=30
)
# สร้าง Embeddings
self.embeddings = OpenAIEmbeddings(
api_key=api_key,
base_url=base_url
)
# กำหนด Prompts
self._setup_prompts()
# สร้าง Chain
self._build_chain()
def _setup_prompts(self):
self.qa_prompt = ChatPromptTemplate.from_template("""
คุณเป็นผู้ช่วยตอบคำถามเกี่ยวกับสินค้า
ใช้ข้อมูลจาก Context ด้านล่างในการตอบ
ถ้าไม่แน่ใจให้ตอบว่าไม่ทราบ
Context: {context}
คำถาม: {question}
คำตอบ (ภาษาไทย):
""")
def _build_chain(self):
"""สร้าง RAG Chain"""
def format_docs(docs):
return "\n\n---\n\n".join([doc.page_content for doc in docs])
def get_context_with_stats(docs):
"""เอาเอกสารพร้อมสถิติ Token"""
context = format_docs(docs)
# ประมาณการ Token
word_count = len(context.split())
estimated_tokens = int(word_count * 1.3)
return {
"context": context,
"token_estimate": estimated_tokens
}
self.chain = (
{"context": self.vectorstore.as_retriever() | get_context_with_stats,
"question": RunnablePassthrough()}
| self.qa_prompt
| self.llm
| StrOutputParser()
)
def _estimate_tokens(self, text: str) -> int:
"""ประมาณการจำนวน Token"""
# วิธีง่าย: 1 Token ≈ 0.75 คำ
words = len(text.split())
return int(words * 1.3)
async def query(
self,
question: str,
top_k: int = 10,
max_context_tokens: int = 4000
) -> RAGResult:
"""
ค้นหาคำตอบพร้อม Context Compression
Args:
question: คำถาม
top_k: จำนวนเอกสารที่ดึง
max_context_tokens: Token สูงสุดของ Context
Returns:
RAGResult พร้อมข้อมูลการใช้ Token
"""
try:
# ดึงเอกสารที่เกี่ยวข้อง
docs = self.vectorstore.similarity_search(
question,
k=top_k
)
# ประมาณการ Token ดั้งเดิม
original_context = "\n\n".join([doc.page_content for doc in docs])
original_tokens = self._estimate_tokens(original_context)
# ใช้ Strategy ตามที่กำหนด
if self.strategy == CompressionStrategy.BASIC:
compressed_context = self._basic_compress(original_context)
elif self.strategy == CompressionStrategy.SEMANTIC:
compressed_context = self._semantic_compress(original_context, question)
else: # HYBRID
compressed_context = self._hybrid_compress(original_context, question)
compressed_tokens = self._estimate_tokens(compressed_context)
# ตรวจสอบว่า Context เกิน limit หรือไม่
if compressed_tokens > max_context_tokens:
compressed_context = self._truncate_context(
compressed_context,
max_context_tokens
)
compressed_tokens = max_context_tokens
# สร้าง Token Usage
token_usage = TokenUsage(
original_tokens=original_tokens,
compressed_tokens=compressed_tokens,
final_tokens=compressed_tokens,
compression_ratio=1 - (compressed_tokens / original_tokens) if original_tokens > 0 else 0
)
# ตอบคำถาม
answer = await self.llm.ainvoke(
self.qa_prompt.format(
context=compressed_context,
question=question
)
)
return RAGResult(
answer=answer.content if hasattr(answer, 'content') else str(answer),
sources=[doc.metadata.get("source", "Unknown") for doc in docs],
token_usage=token_usage,
metadata={
"strategy": self.strategy.value,
"num_docs_retrieved": len(docs),
"timestamp": datetime.now().isoformat()
}
)
except Exception as e:
return RAGResult(
answer="",
error=str(e),
metadata={"error_type": type(e).__name__}
)
def _basic_compress(self, context: str) -> str:
"""Compression แบบพื้นฐาน: ตัดช่องว่างเกิน"""
import re
# ตัดช่องว่างเกิน
text = re.sub(r'\s+', ' ', context).strip()
# ตัดเครื่องหมายที่ไม่จำเป็น
text = re.sub(r'\.{2,}', '.', text)
return text
def _semantic_compress(self, context: str, question: str) -> str:
"""Compression แบบ Semantic: เก็บเฉพาะส่วนที่เกี่ยวข้อง"""
import re
# แยกเป็น paragraphs
paragraphs = context.split('\n\n')
# หาคำสำคัญจากคำถาม
question_keywords = set(question.lower().split())
# ให้คะแนนแต่ละ paragraph
scored = []
for p in paragraphs:
words = set(p.lower().split())
overlap = len(question_keywords & words)
score = overlap / len(question_keywords) if question_keywords else 0
scored.append((score, p))
# เรียงและเลือกเฉพาะที่ดี
scored.sort(key=lambda x: x[0], reverse=True)
selected = [p for score, p in scored[:3] if score > 0]
return '\n\n'.join(selected) if selected else context[:2000]
def _hybrid_compress(self, context: str, question: str) -> str:
"""Hybrid: รวม Basic และ Semantic"""
# ทำ Semantic ก่อน
semantic = self._semantic_compress(context, question)
# จากนั้นทำ Basic
return self._basic_compress(semantic)
def _truncate_context(self, context: str, max_tokens: int) -> str:
"""ตัด Context ให้เข้ากับ Token Limit"""
max_words = int(max_tokens / 1.3)
words = context.split()
if len(words) <= max_words:
return context
return ' '.join(words[:max_words]) + "..."
วิธีการใช้งาน
async def main():
# สมมติว่ามี vectorstore อยู่แล้ว
# vectorstore = FAISS.load_local("my_index", embeddings)
# สร้าง Pipeline
pipeline = CompressionRAGPipeline(
api_key=HOLYSHEEP_API_KEY,
base_url=HOLYSHEEP_BASE_URL,
vectorstore=vectorstore, # ใส่ vectorstore จริงของคุณ
strategy=CompressionStrategy.HYBRID,
model_name="gpt-4o-mini"
)
# ทดสอบ
result = await pipeline.query(
question="สินค้านี้เหมาะกับผิวแพ้ง่ายไหม?",
top_k=10
)
if result.error:
print(f"เกิดข้อผิดพลาด: {result.error}")
else:
print("คำตอบ:", result.answer)
print("\nสถิติการใช้ Token:")
print(f" Token เดิม: {result.token_usage.original_tokens}")
print(f" Token หลังบีบอัด: {result.token_usage.compressed_tokens}")
print(f" อัตราส่วน: {result.token_usage.compression_ratio:.1%}")
savings = result.token_usage.calculate_savings()
print(f" ประหยัดได้: {savings['savings_percent']:.1f}%")
print(f" แหล่งข้อมูล: {result.sources}")
รัน
import asyncio
asyncio.run(main())
เปรียบเทียบต้นทุน: ก่อนและหลังใช้ Compression
จากการทดสอบจริงกับ HolySheep AI ราคาถูกกว่า OpenAI ถึง 85%:
| รายการ | ก่อน Compression | หลัง Compression | ประหยัด |
|---|---|---|---|
| Token/คำถาม | 8,000 | 3,600 | 55% |
| ค่าใช้จ่าย/เดือน (30K คำถาม) | $1,200 | $540 | $660 |
| เวลาตอบสนอง | 2.8 วินาที | 1.2 วินาที | 57% |
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
1. ข้อผิดพลาด: API Connection Error
# ❌ สาเหตุ: Base URL ไม่ถูกต้อง
os.environ["OPENAI_API_BASE"] = "https://api.openai.com/v1" # ผิด!
✅ วิธีแก้ไข: ใช้ URL ของ HolySheep AI
os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1"
หรือระบุโดยตรงใน constructor
llm = ChatOpenAI(
model="gpt-4o-mini",
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
2. ข้อผิดพลาด: Token Limit Exceeded
# ❌ สาเหตุ: Context ใหญ่เกินไปสำหรับ model
response = llm.invoke(f"Context: {huge_context}\n