การสรุปเอกสารยาวเป็นหนึ่งใน Use Case ที่พบบ่อยที่สุดในการใช้ LLM แต่เมื่อเอกสารมีความยาวหลายหมื่นตัวอักษร การเลือก Strategy ที่เหมาะสมจะส่งผลต่อทั้งคุณภาพผลลัพธ์และต้นทุนอย่างมาก
ในบทความนี้ ผมจะอธิบาย 3 Strategy หลักในการจัดการ Long Document Summarization ได้แก่ Stuff, Map-Reduce และ Refine พร้อมโค้ดตัวอย่างที่พร้อมใช้งานจริง ผ่าน API ของ HolySheep AI ที่รองรับทุก Model ชั้นนำในราคาประหยัดสูงสุด 85%
ทำไมต้องเลือก Strategy ให้ถูกต้อง
จากประสบการณ์การพัฒนา RAG System หลายโปรเจกต์ ผมพบว่าการเลือก Strategy ที่ไม่เหมาะสมนำไปสู่ปัญหาหลายประการ:
- Context Overflow — Token เกิน Limit ของ Model
- Information Loss — สรุปแล้วข้อมูลสำคัญหายไป
- Cost Explosion — ใช้ Token มากเกินจำเป็นโดยไม่จำเป็น
- Inconsistent Quality — คุณภาพไม่สม่ำเสมอในแต่ละส่วน
3 Strategy หลักในการสรุปเอกสารยาว
1. Stuff Strategy — ยัดทุกอย่างในครั้งเดียว
หลักการ: รวมเอกสารทั้งหมดเข้าไปใน Prompt เดียว เหมาะกับเอกสารที่สามารถใส่ใน Context Window ได้พอดี
ข้อดี: เรียบง่าย รวดเร็ว และ Model สามารถเห็นความเชื่อมโยงข้อมูลทั้งเอกสาร
ข้อเสีย: จำกัดด้วย Context Window ของ Model
import requests
BASE_URL = "https://api.holysheep.ai/v1"
def stuff_summarize(document_text: str, api_key: str) -> str:
"""
Stuff Strategy: ใส่เอกสารทั้งหมดในครั้งเดียว
เหมาะกับ: เอกสารสั้น-กลาง ที่ไม่เกิน 128K tokens
"""
prompt = f"""คุณเป็นผู้เชี่ยวชาญในการสรุปเอกสารภาษาไทย
จงสรุปเอกสารต่อไปนี้อย่างกระชับ โดยคร่าว เน้นประเด็นสำคัญ:
เอกสาร
{document_text}
รูปแบบการตอบ
- หัวข้อหลัก: [สรุป 1-2 ประโยค]
- ประเด็นสำคัญ: [Bullet points 3-5 ข้อ]
- ข้อสรุป: [ประโยคสรุปท้ายสุด]
"""
response = requests.post(
f"{BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json={
"model": "gpt-4.1",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.3,
"max_tokens": 2000
}
)
result = response.json()
return result["choices"][0]["message"]["content"]
ตัวอย่างการใช้งาน
with open("report.txt", "r", encoding="utf-8") as f:
doc = f.read()
summary = stuff_summarize(doc, "YOUR_HOLYSHEEP_API_KEY")
print(summary)
2. Map-Reduce Strategy — แบ่งและเอาชนะ
หลักการ: แบ่งเอกสารเป็น Chunk เล็กๆ แต่ละ Chunk สรุปแยกก่อน จากนั้นรวม Summary ทั้งหมดเพื่อสร้างสรุปสุดท้าย
import requests
import tiktoken
BASE_URL = "https://api.holysheep.ai/v1"
def count_tokens(text: str, model: str = "gpt-4.1") -> int:
"""นับจำนวน Token โดยประมาณ"""
encoding = tiktoken.encoding_for_model("gpt-4")
return len(encoding.encode(text))
def split_into_chunks(text: str, max_tokens: int = 4000, overlap: int = 200) -> list:
"""
แบ่งเอกสารเป็น Chunk ตามจำนวน Token
max_tokens: จำนวน Token สูงสุดต่อ Chunk (เผื่อไว้ 500 token สำหรับ Prompt)
overlap: จำนวน Token ที่ทับซ้อนกันระหว่าง Chunk
"""
chunks = []
content = text.strip()
# คำนวณจำนวนตัวอักษรโดยประมาณ (1 token ≈ 4 ตัวอักษร)
chars_per_chunk = (max_tokens - 500) * 4
start = 0
while start < len(content):
end = start + chars_per_chunk
# หาจุดตัดที่เหมาะสม (ไม่ตัดกลางประโยค)
if end < len(content):
# หาจุด space ย้อนกลับไป
cutoff = content.rfind(' ', start, end)
if cutoff > start + chars_per_chunk * 0.7:
end = cutoff
chunk = content[start:end].strip()
if chunk:
chunks.append(chunk)
start = end - (overlap * 4) # ถอยกลับตาม overlap
return chunks
def map_summarize_chunk(chunk: str, chunk_index: int, total: int, api_key: str) -> str:
"""Map Step: สรุปแต่ละ Chunk แยกกัน"""
prompt = f"""คุณเป็นผู้เชี่ยวชาญในการสรุปเอกสาร
นี่คือส่วนที่ {chunk_index + 1} จาก {total} ส่วนของเอกสารฉบับเต็ม
จงสรุปส่วนนี้อย่างกระชับ โดยระบุประเด็นหลักและรายละเอียดสำคัญ:
เนื้อหาส่วนที่ {chunk_index + 1}
{chunk}
รูปแบบการตอบ
สรุปใน 3-5 บรรทัด เน้น:
1. หัวข้อหลักของส่วนนี้
2. ข้อมูลสำคัญที่ต้องเก็บ
3. ความเชื่อมโยงกับส่วนอื่น (ถ้ามี)
"""
response = requests.post(
f"{BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json={
"model": "deepseek-v3.2", # ใช้ Model ราคาถูกสำหรับ Step นี้
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.3,
"max_tokens": 500
}
)
result = response.json()
return result["choices"][0]["message"]["content"]
def reduce_final_summary(chunk_summaries: list, original_doc_title: str, api_key: str) -> str:
"""Reduce Step: รวม Summary ทั้งหมดเป็นสรุปสุดท้าย"""
combined_summaries = "\n\n---\n\n".join(chunk_summaries)
prompt = f"""คุณเป็นผู้เชี่ยวชาญในการสรุปเอกสารฉบับยาว
ด้านล่างคือ Summary จากแต่ละส่วนของเอกสาร โปรดรวมเป็นสรุปเดียวที่สมบูรณ์
ชื่อเอกสาร: {original_doc_title}
Summary จากแต่ละส่วน
{combined_summaries}
รูปแบบการตอบ
สรุปเอกสาร: [ชื่อเอกสาร]
ภาพรวม
[สรุป 2-3 ย่อหน้า]
ประเด็นสำคัญ
- [รายการ 5-7 ข้อ]
ข้อสรุปและความสำคัญ
[ประโยคสรุปท้ายสุด]
"""
response = requests.post(
f"{BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json={
"model": "gpt-4.1", # ใช้ Model ดีที่สุดสำหรับ Step สุดท้าย
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.3,
"max_tokens": 2000
}
)
result = response.json()
return result["choices"][0]["message"]["content"]
def map_reduce_summarize(document_text: str, title: str, api_key: str) -> str:
"""
Map-Reduce Strategy: แบ่งสรุป แล้วรวม
เหมาะกับ: เอกสารยาวมาก หรือหลายเอกสาร
"""
print(f"📄 เอกสารมี {count_tokens(document_text)} tokens")
# Step 1: แบ่งเอกสาร
chunks = split_into_chunks(document_text, max_tokens=4000)
print(f"✂️ แบ่งเป็น {len(chunks)} ส่วน")
# Step 2: Map - สรุปแต่ละ Chunk
chunk_summaries = []
for i, chunk in enumerate(chunks):
print(f"📝 กำลังสรุปส่วนที่ {i+1}/{len(chunks)}...")
summary = map_summarize_chunk(chunk, i, len(chunks), api_key)
chunk_summaries.append(summary)
# Step 3: Reduce - รวม Summary
print(f"🔄 กำลังรวม Summary ทั้งหมด...")
final_summary = reduce_final_summary(chunk_summaries, title, api_key)
return final_summary
ตัวอย่างการใช้งาน
with open("annual_report_2024.txt", "r", encoding="utf-8") as f:
doc = f.read()
result = map_reduce_summarize(
document_text=doc,
title="รายงานประจำปี 2567",
api_key="YOUR_HOLYSHEEP_API_KEY"
)
print(result)
3. Refine Strategy — ปรับปรุงทีละขั้นตอน
หลักการ: อ่านเอกสารทีละส่วน โดยใช้ Summary ก่อนหน้าเป็นพื้นฐาน แล้วปรับปรุงเพิ่มเติมเรื่อยๆ
import requests
BASE_URL = "https://api.holysheep.ai/v1"
def refine_summarize(document_text: str, api_key: str) -> str:
"""
Refine Strategy: อ่านทีละส่วน ปรับปรุง Summary ไปเรื่อยๆ
เหมาะกับ: เอกสารที่มีโครงสร้างชัดเจน ต้องการความต่อเนื่องของข้อมูล
"""
# แบ่งเอกสารเป็นส่วน
paragraphs = [p.strip() for p in document_text.split('\n\n') if p.strip()]
current_summary = ""
iteration = 0
for i, paragraph in enumerate(paragraphs):
iteration += 1
if i == 0:
# ส่วนแรก: สร้าง Summary เริ่มต้น
prompt = f"""จากข้อความต่อไปนี้ จงสรุปประเด็นหลักและรายละเอียดสำคัญ:
ข้อความ:
{paragraph}
รูปแบบการตอบ (สรุป 3-5 ประโยค):
"""
else:
# ส่วนถัดๆ ไป: ปรับปรุง Summary เดิม
prompt = f"""คุณกำลังอ่านเอกสารทีละส่วนเพื่อสร้างสรุป
Summary ปัจจุบัน:
{current_summary}
ส่วนใหม่ที่คุณกำลังอ่าน:
{paragraph}
คำสั่ง:
อ่านส่วนใหม่แล้ว ปรับปรุง Summary ข้างบน โดย:
1. เพิ่มประเด็นใหม่ที่สำคัญจากส่วนนี้
2. ลบข้อมูลที่ไม่เกี่ยวข้องออก (ถ้ามี)
3. เชื่อมโยงข้อมูลเดิมกับข้อมูลใหม่ให้สมบูรณ์
รูปแบบการตอบ (สรุป 3-5 ประโยค):
"""
print(f"🔄 ปรับปรุง iteration ที่ {iteration}/{len(paragraphs)}...")
response = requests.post(
f"{BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json={
"model": "gemini-2.5-flash", # เร็วและถูก เหมาะกับงาน iteration
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.3,
"max_tokens": 800
}
)
result = response.json()
current_summary = result["choices"][0]["message"]["content"]
# Final Polish: ปรับแต่งครั้งสุดท้าย
final_prompt = f"""จาก Summary ต่อไปนี้ จงปรับแต่งให้กระชับและสมบูรณ์ที่สุด:
{current_summary}
รูปแบบการตอบ:
- หัวข้อหลัก: [1 ประโยค]
- สรุป: [3-4 ประโยค]
- ประเด็นสำคัญ: [3-5 bullet points]
"""
response = requests.post(
f"{BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json={
"model": "gpt-4.1",
"messages": [{"role": "user", "content": final_prompt}],
"temperature": 0.3,
"max_tokens": 1500
}
)
return response.json()["choices"][0]["message"]["content"]
ตัวอย่างการใช้งาน
with open("product_manual.txt", "r", encoding="utf-8") as f:
doc = f.read()
final_summary = refine_summarize(doc, "YOUR_HOLYSHEEP_API_KEY")
print(final_summary)
เปรียบเทียบ Strategy: Stuff vs Map-Reduce vs Refine
| เกณฑ์ | Stuff | Map-Reduce | Refine |
|---|---|---|---|
| Context Window | จำกัด Model (8K-128K) | ไม่จำกัด | ไม่จำกัด |
| ความเร็ว | เร็วที่สุด (1 API Call) | ปานกลาง (N+1 Calls) | ช้าที่สุด (N Calls) |
| ความสมบูรณ์ | สูง (เห็นทั้งเอกสาร) | ปานกลาง (อาจขาดความเชื่อมโยง) | สูง (อ่านเรียงตามลำดับ) |
| ต้นทุน (Token) | ต่ำ-กลาง | ปานกลาง-สูง | สูง |
| เอกสารที่เหมาะสม | สั้น-กลาง (<50K tokens) | ยาวมาก / หลายเอกสาร | มีโครงสร้างชัดเจน |
| Use Case เหมาะสม | Email, บทความเดี่ยว | รายงานปี, เอกสารทางกฎหมาย | คู่มือ, เอกสารเทคนิค |
การเลือก Model ที่เหมาะสมสำหรับแต่ละ Strategy
จากราคา 2026 ของ HolySheep AI ผมแนะนำการจับคู่ดังนี้:
# ตารางเปรียบเทียบราคา Model ต่อ Million Tokens (2026)
MODELS = {
"deepseek-v3.2": {
"price_per_mtok": 0.42, # ถูกที่สุด
"best_for": ["Map Step", "Refine Iteration", "Draft"]
},
"gemini-2.5-flash": {
"price_per_mtok": 2.50,
"best_for": ["Refine Step", "Quick Summary", "Batch Processing"]
},
"gpt-4.1": {
"price_per_mtok": 8.00,
"best_for": ["Final Summary", "Complex Analysis", "Stuff Strategy"]
},
"claude-sonnet-4.5": {
"price_per_mtok": 15.00,
"best_for": ["High-Quality Summary", "Long Context", "Critical Docs"]
}
}
def calculate_cost(strategy: str, doc_tokens: int, use_holysheep: bool = True) -> dict:
"""
คำนวณต้นทุนโดยประมาณสำหรับแต่ละ Strategy
use_holysheep: True = ใช้ HolySheep (ประหยัด 85%+), False = OpenAI
"""
# สมมติฐาน
overhead_prompt_tokens = 200 # Token สำหรับ System Prompt
if strategy == "stuff":
# 1 API Call
total_tokens = doc_tokens + overhead_prompt_tokens
model = "gpt-4.1"
cost = (total_tokens / 1_000_000) * MODELS[model]["price_per_mtok"]
elif strategy == "map_reduce":
# N Chunks + 1 Final
chunk_size = 4000
n_chunks = (doc_tokens // chunk_size) + 1
# Map Step: ใช้ DeepSeek (ถูก)
map_tokens = n_chunks * (chunk_size + overhead_prompt_tokens)
map_cost = (map_tokens / 1_000_000) * MODELS["deepseek-v3.2"]["price_per_mtok"]
# Reduce Step: ใช้ GPT-4.1 (ดี)
reduce_tokens = n_chunks * 500 + overhead_prompt_tokens
reduce_cost = (reduce_tokens / 1_000_000) * MODELS["gpt-4.1"]["price_per_mtok"]
total_tokens = map_tokens + reduce_tokens
cost = map_cost + reduce_cost
elif strategy == "refine":
# N Paragraphs + 1 Final
n_paragraphs = max(1, doc_tokens // 1000)
# Iteration: ใช้ Gemini Flash (เร็ว+ถูก)
iter_tokens = n_paragraphs * 1000 + overhead_prompt_tokens
iter_cost = (iter_tokens / 1_000_000) * MODELS["gemini-2.5-flash"]["price_per_mtok"]
# Final: ใช้ GPT-4.1
final_tokens = 800 + overhead_prompt_tokens
final_cost = (final_tokens / 1_000_000) * MODELS["gpt-4.1"]["price_per_mtok"]
total_tokens = iter_tokens + final_tokens
cost = iter_cost + final_cost
# ถ้าใช้ OpenAI โดยตรง คูณ 7 เท่า (ประหยัด ~85%)
if not use_holysheep:
cost *= 7
return {
"strategy": strategy,
"document_tokens": doc_tokens,
"total_tokens": total_tokens,
"cost_usd": round(cost, 4),
"cost_thb": round(cost * 35, 2), # ประมาณ 35 บาท/USD
"savings_vs_openai": round(cost * 6, 4) if use_holysheep else 0
}
ตัวอย่าง: เอกสาร 50,000 tokens
for strategy in ["stuff", "map_reduce", "refine"]:
result = calculate_cost(strategy, 50000, use_holysheep=True)
print(f"Strategy: {result['strategy']}")
print(f" Total Tokens: {result['total_tokens']:,}")
print(f" ราคา HolySheep: ฿{result['cost_thb']:.2f}")
print(f" ประหยัด vs OpenAI: ฿{result['savings_vs_openai']*35:.2f}")
print()
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
กรณีที่ 1: Context Overflow - เอกสารใหญ่เกิน Context Window
# ❌ วิธีผิด: พยายามยัดเอกสารทั้งหมดในครั้งเดียว
def bad_stuff_summarize(document):
prompt = f"สรุปเอกสารนี้: {document}" # อาจเกิน limit!
return call_llm(prompt)
✅ วิธีถูก: ตรวจสอบขนาดก่อน แล้วเลือก Strategy
def smart_summarize(document: str, api_key: str) -> str:
"""
เลือก Strategy อัตโนมัติตามขนาดเอกสาร
"""
from tiktoken import Encoding
encoding = tiktoken.encoding_for_model("gpt-4")
tokens = len(encoding.encode(document))
if tokens <= 8000:
# Context เพียงพอ ใช้ Stuff
print(f"📄 {tokens} tokens — ใช้ Stuff Strategy")
return stuff_summarize(document, api_key)
elif tokens <= 50000:
# ปานกลาง ใช้ Map-Reduce
print(f"📄 {tokens} tokens — ใช้ Map-Reduce Strategy")
return map_reduce_summarize(document, "Document", api_key)
else:
# ยาวมาก ใช้ Map-Reduce หรือ Refine
print(f"📄 {tokens} tokens — ใช้ Map-Reduce Strategy (Extended)")
# แบ่ง Chunk ให้เล็กลง
chunks = split_into_chunks(document, max_tokens=2000)
# สรุปแต่ละ Chunk ด้วย Model ราคาถูก
summaries = []
for i, chunk in enumerate(chunks):
summary = map_summarize_chunk(chunk, i, len(chunks), api_key)
summaries.append(summary)
# รวม Summary ด้วย GPT-4.1
return reduce_final_summary(summaries, "Document", api_key)