Journal Query Understanding & Retrieval
จากคำถามสู่คำตอบ: คำถามแบบไหนกลายเป็น tool-call แบบไหนกลายเป็น vector search
คำถามแต่ละชนิดเข้ากับวิธีดึงข้อมูลคนละแบบ — บทนี้พาเข้าใจว่าคำถามแบบไหนเข้ากับ tool-call/text-to-SQL, vector, hybrid หรือ graph และทำไม ผ่าน retrieval pipeline ทั้งเส้น · เมื่อเห็นว่าแต่ละวิธีเก่งงานแบบไหนและแลกมาด้วยอะไร วิธีที่เข้ากับคำถามมักเป็นวิธีที่เบาที่สุดด้วย บทที่ 5 ของ LLM systems series
ผู้ใช้พิมพ์ประโยคเดียว — “ออเดอร์ #1234 ส่งถึงไหนแล้ว” · แต่ก่อนจะตอบ ระบบต้องตอบคำถามที่ผู้ใช้ไม่เห็นก่อน — คำถามนี้เข้ากับการดึงข้อมูลแบบไหน
คำถามแต่ละแบบมีรูปร่างไม่เหมือนกัน · วิธีดึงข้อมูลแต่ละวิธีก็เก่งคำถามคนละแบบ · บทนี้พาดูว่าคำถามแบบไหนเข้ากับวิธีไหน และทำไม · เมื่อจับคู่ถูก วิธีที่ใช่มักเป็นวิธีที่เบาที่สุดด้วย
ขอบเขต — บทนี้คือบทที่ 5 ของ LLM systems series · ต่อจาก เลือกระดับ · เลือก vendor · เลือกอย่างปลอดภัย · วางโครงให้ทน · บทก่อนพูดว่า data layer ต้องนิ่ง — บทนี้พูดว่า จะดึงจากมันให้เข้ากับคำถามได้อย่างไร
หัวใจ: คำถามไม่ได้มีชนิดเดียว
เวลาพูดถึง RAG หลายคนนึกถึงภาพเดียว — เอาคำถามไป vector search · แต่ vector search เป็นแค่หนึ่งในหลายวิธี
คำถามของคนแบ่งได้ตาม สิ่งที่มันถามหา:
- อยากได้ คำตอบที่แน่นอน / คำนวณได้ → เข้ากับ structured query (tool-call, text-to-SQL)
- อยากได้ ความหมายที่ใกล้เคียง → เข้ากับ semantic search (vector)
- มีทั้งสองอย่างปนกัน → hybrid · ถามความสัมพันธ์ → graph
ลองวางคำถามจากร้านค้าร้านเดียวเป็นชั้น ๆ — ไล่จาก structured สุด ลงไป semantic สุด · แต่ละชั้นเข้ากับวิธีดึงคนละแบบ:
| ตัวอย่าง | ชนิดคำถาม | เข้ากับ |
|---|---|---|
| “ออเดอร์ #1234 ถึงไหน” | หาตัวเดียวด้วย key | tool-call · point lookup |
| “เมนูราคาเกิน 100” · “เมนูใส่สตรอเบอร์รี” | กรองด้วย field | SQL filter — WHERE |
| “เดือนนี้ลูกค้าใหม่กี่คน” · “เมนูขายดีสุด” | นับ / คำนวณ / จัดอันดับ | SQL aggregate — COUNT/SUM |
| “เมนูไหนสดชื่น” · “อะไรเบา ๆ” | คุณภาพที่ไม่มี field | semantic — vector หรือ LLM→tag |
| “นโยบายคืนของเป็นยังไง” | อธิบาย / แนวคิด | vector — RAG บนเอกสาร |
| “เงื่อนไขโปร X11 ล่าสุด” | คำเฉพาะ + ความหมาย | hybrid — BM25 + vector |
| “คนที่ซื้อ A มักซื้ออะไรต่อ” | ความสัมพันธ์หลายชั้น | graph |
เส้นแบ่ง vector กับ SQL จำง่าย ๆ — คำตอบเป็น แถวที่กรองหรือนับได้ → SQL · เป็น ความหมายที่ต้องเทียบความใกล้ → vector · มีทั้งสอง → hybrid · เป็นความสัมพันธ์ → graph
catalog กับ vector — ดูที่รูปของข้อมูล
เส้นแบ่งจะคมขึ้นถ้าดูอีกชั้น — ไม่ใช่แค่คำถามถามอะไร แต่ ข้อมูลที่ต้องดึงอยู่ในรูปไหน:
- catalog (structured) — เมนู ราคา สต็อก สถานะออเดอร์ · มี field/key ให้ query ตรง · นี่คืองานของ database
- text อิสระ (unstructured) — นโยบาย FAQ คำอธิบาย เอกสาร · ไม่มี field ให้ filter มีแต่ความหมาย · ที่ของ vector อยู่ตรงนี้
ทำไมแยกตรงนี้: catalog มี key อยู่แล้ว การ query ตรงจึงเร็วและเป๊ะ · ถ้าเอา catalog ไป embed แล้ว vector search ผลจะช้ากว่าและนับ/กรองไม่แม่น เพราะ vector จับ ความหมาย ไม่ได้จับ ค่าเป๊ะ · คำถามที่ตัดได้เลย — ข้อมูลที่ต้องดึงมี key ให้ค้นไหม · มี → query catalog · ไม่มี เหลือแต่ความหมาย → vector
สังเกตว่าแม้แต่ใน catalog เดียวกัน คำถามยังแยกอีกชั้น — สิ่งที่ถามเป็น field ไหม · ราคา/ส่วนผสมเป็น field → filter ตรง เร็วและเป๊ะ · ส่วนคุณภาพที่ไม่มี field อย่าง “สดชื่น” (หรือ “เบา ๆ”, “เหมาะหน้าฝน”) ต้องตีความก่อน — ให้ LLM แปลงเป็น tag ที่มีอยู่ (เย็น · ซิตรัส · มินต์) แล้ว filter · หรือ vector บนคำอธิบายเมนู
นี่คือเหตุผลที่ร้านซึ่งมี catalog ปิด (เมนูจำกัด สินค้านับชิ้นได้) มักไม่ต้องแตะ vector เลย · key-value lookup เบากว่าและตรงกว่าสำหรับข้อมูลแบบนั้น · vector ไม่ใช่ขั้นที่ “สูงกว่า” ของ catalog — มันคือเครื่องมือสำหรับข้อมูลคนละรูป
Pipeline ทั้งเส้น — หัวใจอยู่ตรงกลาง
รู้แล้วว่ามีหลายวิธี และอะไรเข้ากับอะไร · ทีนี้มาดูว่าคำถามเดินจากปากผู้ใช้ไปถึงวิธีที่ใช่ได้ยังไง
คำถามกว่าจะเป็นคำตอบ เดินผ่านห้าขั้น · งานที่ตัดสินทุกอย่างอยู่ที่ ขั้น 2-3 — เข้าใจ แล้วแปลงรูป · ขั้น 4-5 แค่ทำตามที่สองขั้นนี้วางไว้
ขั้น 2 · เข้าใจคำถาม — อ่าน “รูป” ของมัน
ก่อนดึงอะไร ระบบตอบสองอย่างให้ได้ก่อน — intent (ผู้ใช้ถามหาอะไร) และ entity/filter (มีค่าเจาะจงอะไรในประโยค)
คำถามจริงไม่ได้มาเดี่ยว ๆ · “เอาอีกแก้ว” หรือ “อันนั้นเท่าไหร่” ตีความไม่ได้ถ้าไม่รู้ว่าคุยอะไรกันมาก่อน · ขั้นนี้จึงอ่านคำถาม พร้อม thread ของมัน ไม่ใช่อ่านบรรทัดเดียว · บทนี้เดินตามคำถามเดียวเพื่อให้เห็นภาพ — แต่ในระบบจริง ทุกคำถามถูกตีความบนประวัติที่สะสมมา
อ่านรูปของคำถามได้สองวิธี ต่างกันที่น้ำหนัก:
- Rule / classifier เบา ๆ — regex จับ ID/วันที่/ตัวเลข + keyword list · เร็วระดับ µs, ไม่ใช้ LLM call · ครอบคลุมคำถามซ้ำ ๆ ส่วนใหญ่ได้
- LLM router — ให้ LLM จัด intent + ดึง entity เป็น structured output · ยืดหยุ่นกว่า แลกกับ latency + cost หนึ่ง call · เหมาะกับคำถามที่ rule จับไม่อยู่
คำถามอย่าง “ออเดอร์ #\d+ ส่งถึงไหน” มี pattern ชัด · regex อ่าน intent ได้เอง ไม่ต้องให้ LLM ช่วย “เข้าใจ” · พอแยกออกว่าคำถามไหน rule พอ คำถามไหนต้องใช้ LLM · เราก็ใช้ LLM เฉพาะที่มันสร้างความต่างจริง — เร็วและถูกในคราวเดียว
อีกชั้นของความยืดหยุ่น — route ไม่ต้องผูกกับ keyword ตายตัว · เก็บ “คำอธิบายของแต่ละงาน” ไว้เป็นข้อมูล แล้วให้ router จับคู่คำถามกับคำอธิบายนั้น · เพิ่มงานใหม่ก็แค่เพิ่มข้อมูลหนึ่งแถว ไม่ต้องแก้โค้ด
ผลของขั้นนี้คือ structured object เช่น {intent: "order_status", order_id: 1234} หรือ {intent: "policy_lookup", topic: "return"} — เอาไปป้อนขั้นถัดไป
ขั้น 3 · แปลงรูป — คำถามกลายเป็นอะไร
นี่คือขั้นที่คนสงสัยบ่อยสุด: คำถามภาษาคน กลายเป็นการดึงข้อมูลจริงได้ยังไง · มีสามเส้นทางหลัก
เส้นทาง A · กลายเป็น tool-call / text-to-SQL — เมื่อ intent คือคำตอบที่แน่นอน
LLM ไม่ตอบคำถามเอง · มันแปลคำถามเป็น structured args ตาม schema ที่เรากำหนด · เช่น
"เดือนนี้ลูกค้าใหม่กี่คน"
→ call get_customer_count(period="2026-06", filter="new")
→ SELECT COUNT(*) FROM customers WHERE created >= '2026-06-01' AND type='new'
ทำไมเข้ากับคำถามแบบนี้: คำตอบมาจาก source of truth โดยตรง · ตัวเลขเป๊ะเพราะ DB เป็นคนนับ ไม่ใช่ LLM เดา · เร็วระดับ ms · ไม่ต้อง embed อะไรเลย · ความแม่นอยู่ที่ schema/description ของ tool ที่ชัดพอให้ LLM เลือกถูก — description สำคัญกว่าโครง schema (ดูบทที่ 4)
เส้นทาง B · กลายเป็น vector search — เมื่อ intent คือความหมายที่ใกล้เคียง
คำถามถูกแปลงเป็น embedding (เวกเตอร์ตัวเลข) แล้วหา neighbor ที่ใกล้ที่สุดใน vector store
"นโยบายคืนของเป็นยังไง"
→ embed(query) → ANN search → top-k chunks ที่ความหมายใกล้
สามจุดที่กำหนดว่า vector search จะเบาหรือหนัก:
- top-k — ดึง 50 chunk มาทั้งที่ใช้จริง 3 · context ยาวขึ้น ต้นทุนสูงขึ้น chunk ส่วนเกินกลายเป็น noise กลบสัญญาณ · เริ่มที่ k เล็กแล้ว rerank ให้ผลตรงข้าม
- สิ่งที่เอาไป embed — บางครั้ง embed คำตอบสมมติ (HyDE) แทนคำถามดิบให้ผลใกล้กว่า เพราะ “คำตอบ” อยู่ใกล้ “เอกสารคำตอบ” มากกว่า “คำถามสั้น ๆ”
- filter ก่อน vector — ถ้ารู้ว่า scope แค่ “หมวดนโยบาย” การตัด search space ลงก่อน ทำให้ไม่ต้องสแกนทั้ง corpus
เส้นทาง C · hybrid — เมื่อคำถามมีทั้งคำเฉพาะและความหมาย · รวม BM25 (keyword exact) + vector (semantic) + metadata filter แล้ว rerank ผลรวม · เข้ากับคำถามที่มี ID/ชื่อรุ่นซึ่ง vector เดี่ยว ๆ มักจับไม่ติด ปนกับเจตนาเชิงความหมาย
Query rewrite / decompose — คำถามคลุมเครือ rewrite ให้ชัดก่อนได้ · คำถามที่ต้องหลายแหล่ง แตกเป็นหลาย sub-query ได้ · ประโยชน์ชัดเมื่อคำถามต้องหลายแหล่งจริง · ถ้า single query ตอบได้ การแตกเพิ่มจ่ายค่า LLM หลายเท่าเพื่อผลเท่าเดิม · จึงอยู่ที่ว่าคำถามตรงหน้าต้องการแค่ไหน
ขั้น 4-5 · ดึง · จัด · ประกอบ
ขั้นที่เหลือเบากว่าเมื่อสองขั้นแรกทำดี:
ดึง (retrieve) — execute SQL/tool หรือ vector query · ปัญหาคลาสสิกของเส้น vector คือ chunk ที่ใช่ดันอยู่อันดับ 12 — top-5 พลาด แต่ดัน top-k เป็น 20 ก็ท่วม context · กุญแจคือเห็นว่าปัญหาอยู่ที่ การจัดอันดับ ไม่ใช่ จำนวนที่ดึง:
- retrieve กว้าง แล้ว rerank — ดึง candidate มาเยอะด้วยวิธีถูก (vector/hybrid) แล้วให้ reranker (cross-encoder) ให้คะแนนใหม่ทีละคู่กับคำถาม · chunk ที่ embedding จัดอันดับพลาดถูกดันขึ้น top-3 · LLM เห็นแค่ไม่กี่ chunk — ได้ทั้ง recall และ context สั้น
- แก้ที่เหตุว่าทำไมมันอันดับต่ำ — embedding ล้วนมักพลาดคำเฉพาะ · hybrid (BM25 + vector) ดันคำตรงขึ้น · query rewrite / HyDE ให้คำถามอยู่ใกล้ chunk คำตอบ · filter ตัด candidate ที่ไม่เกี่ยวออกก่อน
- บีบ แทนที่จะเพิ่ม — ถ้าต้องดึงเยอะจริง compress ก่อนส่ง (สกัดเฉพาะประโยคที่เกี่ยว) · ได้ recall สูงโดย token ไม่บาน
context ที่ท่วมจึงแก้ด้วยการจัดอันดับให้ดีขึ้น ไม่ใช่ดึงมาเยอะขึ้น
จัด context ให้พอดีกับโมเดล — chunk ที่ rerank แล้วไม่ได้ไปอยู่ลำพัง · มันแชร์ window กับ system prompt, ประวัติ, และที่ต้องเหลือให้ output · แล้ว window ของแต่ละโมเดลก็ไม่เท่ากัน
- งบจริงน้อยกว่าที่โฆษณา — โมเดลโฆษณา context เป็นล้านโทเค็น แต่ความแม่นมักตกหลัง ~100-200k · “ใส่ได้” ไม่เท่ากับ “โมเดลใช้ได้ดี” (effective context ≠ advertised — ดูบทที่ 2)
- ตำแหน่งมีผล — โมเดลอ่านต้นกับท้าย window แม่นกว่ากลาง (lost-in-the-middle) · chunk ที่ตรงสุดควรวางที่ขอบ ไม่ใช่ฝังกลางกอง
- ผูกกับโมเดลที่ใช้ — window, ความสนใจ, พฤติกรรม prompt-caching ต่างกันต่อโมเดล · เปลี่ยนโมเดลทีไร กลยุทธ์ context ก็ขยับ — อีกเหตุผลที่ model อยู่หลัง gateway (ดูบทที่ 4)
retrieval ที่ดีจึงไม่จบที่ “ได้ chunk ที่ใช่” · มันจบที่ chunk ที่ใช่ วางถูกที่ ในงบที่โมเดลใช้ได้จริง
ประกอบ (synthesize) — มีสองวิธีสร้างคำตอบ ต่างกันที่ ใครเป็นคนเขียนส่วนที่ต้องจริง
- code render — เมื่อคำตอบเป็น closed set (เมนู ราคา สถานะออเดอร์) code ประกอบประโยคจาก source ตรง ๆ · LLM ไม่แตะ output · ค่าที่ต้องเป๊ะถูกล็อกด้วยโครงสร้าง ไม่ใช่ด้วยคำสั่ง
- LLM synthesize + cite — เมื่อคำตอบเป็น open (อธิบาย สรุป เทียบ) เลี่ยงให้ LLM เขียนไม่ได้ · ให้มันเรียบเรียงจากบริบทที่ดึงมา แล้วแนบแหล่งอ้างอิง
แก่นของการกันหลอนอยู่ตรงนี้ — หลอนเป็นคุณสมบัติของ “ใครเขียนส่วนที่ต้องจริง” · ถ้า code เขียนค่าจาก source โมเดลก็เติมเองไม่ได้ · citation ทำให้ ตรวจเจอ ว่าหลอน · code render ทำให้ หลอนไม่ได้ ตั้งแต่แรก · closed set จึงกันได้ · open answer ทำได้แค่ลดความเสี่ยงกับตรวจให้เจอ
“3 RAG systems” คือจุดบน spectrum เดียว
ภาพยอดนิยมชอบแบ่ง RAG เป็นสามแบบ — Standard, Graph, Agentic · จริง ๆ มันไม่ใช่สามทางเลือกแยกกัน แต่คือจุดบนเส้นเดียว: ใครเลือกแหล่ง และดึงจากกี่แหล่ง
- Standard RAG — vector ทางเดียว: embed → top-k → LLM · คือเส้นทาง vector + rerank ที่เพิ่งเดินมา
- Graph RAG — แตก entity ออกจากคำถาม → เดินใน knowledge graph → ได้ context ที่โยงกัน · เข้ากับคำถามชนิด “ความสัมพันธ์หลายชั้น”
- Agentic RAG — ไม่ fix เส้นไว้ล่วงหน้า · ปล่อยให้ agent เลือกเองว่าจะใช้ vector / graph / web / tool ไหน แล้ววน self-eval จนพอ
มันคือกรอบเดิม — เลือกระดับ (workflow ที่ fix เส้น vs agent ที่เลือกเอง · บทที่ 1) คูณกับ routing (คำถามไหนไปแหล่งไหน · ทั้งบทนี้) · Agentic RAG ก็คือเอา routing นี้ไปให้ agent รันแบบ dynamic แล้วตรวจด้วย eval (บทที่ 4)
เลยไม่ต้องเลือกว่าจะเป็น “ระบบ RAG แบบไหน” · เลือกที่ คำถาม ว่าต้องการแหล่งไหน แล้วค่อยเพิ่มความซับซ้อน (จาก fix เส้น → ให้ agent เลือกเอง) เมื่องานเรียกร้องจริง
ร้อยเข้าด้วยกัน
เมื่อเห็นว่าคำถามแต่ละชนิดถามหาอะไร และแต่ละวิธีเก่งงานแบบไหน แลกมาด้วยอะไร การจับคู่จะชัดขึ้นเอง — คำถามคำนวณ/เจาะจงไป tool-call/SQL · คำถามความหมายไป vector · ปนกันไป hybrid · ความสัมพันธ์ไป graph · ส่วนชั้นเสริมอย่าง rewrite, decompose, LLM router มีที่ของมันเมื่อคำถามต้องการจริง ๆ
ที่ระบบช้าและแพงมักเพราะใช้เครื่องมือหนักเกินคำถาม · เครื่องมือที่หนักกว่าไม่ได้แปลว่าดีกว่า — ความเร็วและความถูกมาจากการจับคู่ให้พอดี ไม่ใช่จากการเพิ่มชั้นความฉลาด · วิธีที่เข้ากับคำถาม จึงมักเป็นวิธีที่เบาที่สุด และนั่นคือที่มาของ performance
เหมือนแกนของทั้ง series — เข้าใจปัญหาก่อน แล้วรูปร่างที่พอดีจะปรากฏเอง · กับ retrieval มันแปลว่า เข้าใจคำถามก่อน แล้ววิธีดึงที่ใช่จะตามมา
ขอบเขต — บทนี้พาเข้าใจ “คำถามแต่ละแบบเข้ากับการดึงข้อมูลแบบไหน และทำไม” · เดินตามคำถามเดียวเพื่อให้เห็นภาพ · ชั้น conversation-state ที่ตีความคำถามบนประวัติทั้งบทสนทนา เป็นมิติที่ใหญ่พอจะแยกเล่า · ต่อยอดจาก data layer ใน บทที่ 4 และระดับ Augmented LLM ใน บทที่ 1 · retrieval ที่เข้ากับคำถามคือสิ่งที่ทำให้ระดับ Augmented LLM ทำงานได้จริง · แนวคิดหลัก: tool/function calling, text-to-SQL, dense retrieval + HyDE, hybrid search (BM25 + vector), query routing, two-stage retrieve→rerank (cross-encoder), contextual compression, context budgeting (lost-in-the-middle · effective ≠ advertised window), constrain-render (code-built output) · รายละเอียดเชิงเครื่องมือเปลี่ยนไปตามยุค แต่ความเข้าใจ “คำถามแบบไหนเข้ากับวิธีไหน” อยู่นาน