การสร้างระบบ RAG (Retrieval-Augmented Generation) ที่มีประสิทธิภาพไม่ใช่แค่การดึงข้อมูลมาตอบ แต่ต้องควบคุม ค่าใช้จ่าย ให้อยู่ โดยเฉพาะ ค่า Token ที่ส่งเข้า LLM บทความนี้จะสอนเทคนิคลด context length อย่างเป็นระบบ พร้อมตัวอย่างโค้ดที่ใช้งานได้จริงกับ HolySheep AI

ทำไม Context Length ถึงสำคัญ?

ทุกครั้งที่ส่ง request ไปยัง LLM ค่าใช้จ่ายจะคิดจาก input tokens ทั้งหมด ถ้าเราส่ง context 10,000 tokens แทนที่จะเป็น 2,000 tokens ค่าใช้จ่ายต่อ request จะ แพงขึ้น 5 เท่า

ตารางเปรียบเทียบบริการ AI API

บริการ ราคา $1/¥ ประหยัด vs เวอร์ชันอย่างเป็นทางการ รองรับ Model ยอดนิยม ความเร็วเฉลี่ย
HolySheep AI ¥1 = $1 85%+ GPT-4.1, Claude Sonnet 4.5, Gemini 2.5 Flash, DeepSeek V3.2 <50ms
API อย่างเป็นทางการ $1 = $1 เริ่มต้น ราคาเต็ม 50-200ms
บริการ Relay A $1 = $0.95 5% จำกัด 100-300ms
บริการ Relay B $1 = $0.90 10% บางส่วน 80-250ms

เทคนิคที่ 1: Smart Chunking

แทนที่จะ chunk แบบ fixed size ให้ใช้ semantic chunking ที่รักษาความสัมพันธ์ของเนื้อหา

import tiktoken
from langchain.text_splitter import RecursiveCharacterTextSplitter

class SemanticChunker:
    def __init__(self, model_name="cl100k_base"):
        self.encoding = tiktoken.get_encoding(model_name)
        self.max_tokens = 500  # เลือก context ที่เล็กพอ
        
    def chunk_by_semantics(self, document: str) -> list[str]:
        # แบ่งตาม paragraph ก่อน เพื่อรักษาความหมาย
        paragraphs = document.split("\n\n")
        chunks = []
        current_chunk = ""
        
        for para in paragraphs:
            tokens = len(self.encoding.encode(para))
            if tokens + len(self.encoding.encode(current_chunk)) <= self.max_tokens:
                current_chunk += "\n\n" + para
            else:
                if current_chunk:
                    chunks.append(current_chunk.strip())
                current_chunk = para
                
        if current_chunk:
            chunks.append(current_chunk.strip())
            
        return chunks

ตัวอย่างการใช้งาน

chunker = SemanticChunker() documents = chunker.chunk_by_semantics(long_article) print(f"จำนวน chunks: {len(documents)}") print(f"เฉลี่ย tokens ต่อ chunk: {sum(len(chunker.encoding.encode(c)) for c in documents) // len(documents)}")

เทคนิคที่ 2: Query Compression ก่อน Retrieval

บีบอัด query ให้กระชับก่อนดึงข้อมูล จะช่วยให้ retrieval ตรงจุดมากขึ้น

from openai import OpenAI
import os

เชื่อมต่อกับ HolySheep AI

client = OpenAI( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1" ) def compress_query(user_query: str) -> str: """บีบอัด query ให้กระชับและตรงประเด็น""" response = client.chat.completions.create( model="gpt-4.1", # $8/MTok - ใช้ model ราคาถูกสำหรับ compression messages=[ { "role": "system", "content": "你是查询压缩助手。将用户的长查询压缩为3-5个关键词的简短查询,保持核心意图。" }, { "role": "user", "content": f"压缩这个查询: {user_query}" } ], max_tokens=50, temperature=0 ) return response.choices[0].message.content def rag_pipeline(user_query: str, collection): # ขั้นตอนที่ 1: บีบอัด query compressed_query = compress_query(user_query) # ขั้นตอนที่ 2: retrieval ด้วย query ที่บีบอัดแล้ว results = collection.query( query_texts=[compressed_query], n_results=3 # ดึงแค่ 3 ผลลัพธ์ที่ดีที่สุด ) # ขั้นตอนที่ 3: สร้าง context ที่กระชับ context = "\n".join(results['documents'][0]) # ขั้นตอนที่ 4: ตอบด้วย LLM response = client.chat.completions.create( model="deepseek-v3.2", # $0.42/MTok - ประหยัดมาก! messages=[ {"role": "system", "content": "ตอบคำถามโดยใช้ข้อมูลที่ให้มาเท่านั้น"}, {"role": "user", "content": f"คำถาม: {user_query}\n\nข้อมูล: {context}"} ], max_tokens=500 ) return response.choices[0].message.content

ทดสอบ

answer = rag_pipeline("ฉันต้องการทราบเกี่ยวกับวิธีการลดค่าใช้จ่ายในการใช้งาน AI API โดยเฉพาะในเรื่องของการจัดการ context length ที่ส่งเข้าไปยัง LLM ว่ามีวิธีการอย่างไรบ้าง", my_collection) print(answer)

เทคนิคที่ 3: Hybrid Search + Reranking

ใช้ keyword search ผสมกับ vector search แล้ว rerank ผลลัพธ์ เพื่อให้ได้ context ที่ตรงที่สุด

from rank_bm25 import BM25Okapi
import numpy as np

class HybridRetriever:
    def __init__(self, chunks: list[str], embedding_model):
        self.chunks = chunks
        self.embedding_model = embedding_model
        self.bm25 = BM25Okapi([c.split() for c in chunks])
        self.embeddings = embedding_model.encode(chunks)
        
    def retrieve(self, query: str, top_k: int = 5) -> list[tuple[str, float]]:
        # 1. Keyword search (BM25)
        tokenized_query = query.split()
        bm25_scores = self.bm25.get_scores(tokenized_query)
        
        # 2. Vector search
        query_embedding = self.embedding_model.encode([query])[0]
        cosine_scores = np.dot(self.embeddings, query_embedding) / (
            np.linalg.norm(self.embeddings, axis=1) * np.linalg.norm(query_embedding)
        )
        
        # 3. รวม scores (weighted)
        combined_scores = 0.3 * self._normalize(bm25_scores) + 0.7 * cosine_scores
        
        # 4. เลือก top-k
        top_indices = np.argsort(combined_scores)[-top_k:][::-1]
        
        return [(self.chunks[i], combined_scores[i]) for i in top_indices]
    
    def _normalize(self, scores: np.ndarray) -> np.ndarray:
        if scores.max() == 0:
            return scores
        return (scores - scores.min()) / (scores.max() - scores.min())

ใช้กับ HolySheep AI embeddings

from openai import OpenAI client = OpenAI( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1" )

เรียกใช้ embedding API

response = client.embeddings.create( model="text-embedding-3-small", input="ตัวอย่างข้อความ" ) embedding = response.data[0].embedding

เทคนิคที่ 4: Caching และ Deduplication

เก็บผลลัพธ์ที่เคยคำนวณแล้วไว้ใช้ซ้ำ ลดการเรียก API ซ้ำๆ

import hashlib
import json
from functools import lru_cache

class SemanticCache:
    def __init__(self, ttl_seconds: int = 3600):
        self.cache = {}
        self.ttl = ttl_seconds
        self.timestamps = {}
        
    def _get_key(self, text: str) -> str:
        # สร้าง key จาก semantic meaning ไม่ใช่แค่ text
        return hashlib.sha256(text.encode()).hexdigest()[:16]
    
    def get(self, query: str) -> str | None:
        key = self._get_key(query)
        if key in self.cache:
            return self.cache[key]
        return None
    
    def set(self, query: str, response: str):
        key = self._get_key(query)
        self.cache[key] = response
        
    def deduplicate_context(self, contexts: list[str]) -> list[str]:
        """ลบ context ที่ซ้ำกันออก"""
        seen = set()
        unique = []
        for ctx in contexts:
            ctx_hash = hashlib.md5(ctx.encode()).hexdigest()
            if ctx_hash not in seen:
                seen.add(ctx_hash)
                unique.append(ctx)
        return unique

ใช้งาน

cache = SemanticCache() query = "วิธีการ optimize RAG"

ตรวจสอบ cache ก่อน

cached_result = cache.get(query) if cached_result: print(f"พบใน cache: {cached_result}") else: # เรียก API และเก็บใส่ cache result = call_llm(query) cache.set(query, result) print(f"ใหม่: {result}")

เทคนิคที่ 5: Dynamic Context Window

ปรับขนาด context ตามความซับซ้อนของคำถาม

def estimate_context_need(query: str) -> int:
    """ประมาณการว่าต้องการ context มากแค่ไหน"""
    question_words = ["เปรียบเทียบ", "วิเคราะห์", "รายงาน", "สรุป", "อธิบาย"]
    simple_words = ["ใช่ไหม", "กี่", "อะไร", "ที่ไหน"]