Composition & ecosystem (sampling, elicitation)
หกบทที่ผ่านมาเดินไปในทิศเดียวเป็นส่วนใหญ่: server เปิด primitive (tool/resource/prompt) ออกมา แล้ว host/client เป็นฝ่ายเรียกเข้าหา server การไหลของอำนาจจึงเป็น Host/Client → Server เกือบตลอด แต่การออกแบบ server จริงจะชนกำแพงหนึ่งเร็วมาก: บางที server เองต่างหากที่ต้องการอะไรบางอย่างกลับ — ต้องการให้ model ช่วยสรุป/ตัดสินกลางทาง หรือต้องการถามผู้ใช้เพิ่มเพราะข้อมูลไม่พอ MCP มีช่องให้ทำสองอย่างนี้ผ่านความสามารถที่ สลับทิศ คือ sampling และ elicitation และเมื่อถอยออกมามองภาพกว้าง คำถามสุดท้ายของการออกแบบไม่ใช่ “server ตัวเดียวควรเปิดอะไร” แต่คือ “เมื่อ host ถือ server หลายตัวพร้อมกัน มันประกอบกันเป็นระบบนิเวศแบบไหน และเราคุมสัญญา (contract) ระหว่างกันอย่างไร” บทนี้ตอบทั้งสองเรื่อง
คอร์สนี้เป็น คอร์สออกแบบ — code ในบทนี้คือ ภาพร่าง ของรูปแบบคำขอ sampling/elicitation และโทโพโลยีการประกอบหลาย server ไม่ใช่ server ที่รันได้จริง ตัวอย่างต่อยอดจาก domain เดิมที่ repo kaen-food-ordering (กำลังจัดทำ): server ออเดอร์ และ การเงิน ที่ต้องขอให้ model ช่วยจัดหมวดคำร้องเรียนกลางทาง (sampling) หรือถาม ops ยืนยันก่อนยิง refund (elicitation) โดยมี ops-copilot (host) เป็นตัวประกอบทั้งหมดเข้าด้วยกัน
Sampling: server ขอ completion จาก LLM ผ่าน client
หัวข้อที่มีชื่อว่า “Sampling: server ขอ completion จาก LLM ผ่าน client”SamplingSamplingกลไกที่ Server ขอให้ Client ยิง completion จาก LLM แทนตัวเอง (Server เองไม่ต้องผูก LLM key ของตัวเอง) เช่น ให้ model ช่วยสรุปผลลัพธ์กลางทางก่อนทำขั้นต่อไป — เป็นความสามารถฝั่ง Client สลับทิศทางจากปกติที่การเรียกไปทาง Host/Client → Server เท่านั้น ต้องผ่านมนุษย์รีวิว prompt และผลลัพธ์Architecture คือกลไกที่ server ขอให้ client ยิง completion จาก LLM ให้ แทนที่ server จะต้องผูก LLM ของตัวเอง spec อธิบายไว้ว่ามันคือ “วิธีมาตรฐานให้ server ร้องขอการทำ LLM sampling… ผ่าน client… โดยไม่ต้องมี API key ฝั่ง server” (sampling, MCP spec 2025-11-25) method คือ sampling/createMessage — server ส่งชุดข้อความ (พร้อมเงื่อนไข) ขึ้นไป แล้ว client เป็นคนไปคุยกับ model จริงแล้วส่งผลกลับลงมา
จุดออกแบบสำคัญสามข้อ:
- server ไม่ถือ API key ของ LLM — นี่คือทั้งความสะดวกและเส้นความปลอดภัย server ที่คุณเขียนไม่ต้องมีสิทธิ์เข้าถึง model โดยตรง มันแค่ ขอ ให้ host จัดการให้ ค่าใช้จ่าย โควตา และการเลือกผู้ให้บริการ model จึงอยู่ที่ host ที่เดียว ไม่กระจายไปทุก server
- เลือก model ด้วยลำดับความสำคัญเชิงนามธรรม ไม่ใช่ล็อกชื่อรุ่น — server บอกความอยากได้ผ่าน
costPriority/speedPriority/intelligencePriority(บวก hint เป็นคำแนะนำ) แล้วปล่อยให้ host เลือกรุ่นจริงเอง (sampling, MCP spec 2025-11-25) การไม่ล็อกชื่อรุ่นตายตัวคือสิ่งที่ทำให้ server ยังเป็นกลางทางค่าย — server ตัวเดียวใช้ได้กับ host ที่หลังบ้านเป็น model คนละยี่ห้อ - มนุษย์ควรรีวิวทั้งขาออกและขาเข้า — spec กำหนดเป็น SHOULD ว่าผู้ใช้ควรได้เห็นและอนุมัติทั้ง prompt ที่ server ส่งออกไป และผลที่ model ตอบกลับ ก่อนมันจะไหลกลับเข้า server นี่คือหลักการ LLM Sampling Controls ข้อเดียวกับที่บทที่ 6 ยกมา — และย้ำอีกครั้งว่าเป็นข้อ แนะนำ ไม่ใช่ข้อบังคับ (MUST) อย่าออกแบบระบบโดยเดาว่าโพรโทคอลบังคับ human gate ให้แล้ว
เหตุที่ sampling ทรงพลัง (และอันตราย) คือมัน สลับทิศการควบคุม: ปกติ model เป็นคนเรียก tool ของ server แต่ตอน sampling กลายเป็น server ที่ยืมสมองของ model มาใช้กลางทาง ใน domain ฟู้ดเดลิเวอรี server ออเดอร์อาจขอให้ model ช่วยจัดหมวดคำร้องเรียนของลูกค้าเป็น “ของขาด / มาช้า / อาหารผิด” ก่อนตัดสินใจขั้นต่อไป — โดยที่ server เองไม่ต้องมี model ของตัวเอง
ก่อนหน้านี้ sampling/createMessage เป็นการขอ completion แบบยิงครั้งเดียวจบ ฉบับ 2025-11-25 เพิ่ม parameter tools และ toolChoice (SEP-1577) ทำให้ completion ที่ server ขอ วน tool-calling ได้เอง — sampling จึงกำลังขยับจาก single-shot ไปเป็น nested agent loop (changelog, MCP spec 2025-11-25) นี่เป็นความสามารถที่เพิ่มใหม่จากฉบับ 2025-06-18 จึงต้องอ่านว่า “ณ spec 2025-11-25…” เสมอ — เป็นส่วนที่ยังเปลี่ยนเร็ว อย่าจำเป็นคุณสมบัติเหนือกาลเวลา
Elicitation: server ขอ input มีโครงสร้างจากผู้ใช้ผ่าน client
หัวข้อที่มีชื่อว่า “Elicitation: server ขอ input มีโครงสร้างจากผู้ใช้ผ่าน client”ElicitationElicitationกลไกที่ Server ขอข้อมูลเพิ่มจากผู้ใช้กลางทางผ่าน Client เช่น ถามยืนยันก่อนลบข้อมูล หรือขอ parameter ที่ยังไม่ครบ — เป็นความสามารถฝั่ง Client เหมือน Sampling ทำให้ Server ไม่ต้องเดาหรือปฏิเสธ request เปล่า ๆ เมื่อข้อมูลไม่พอArchitecture คือพี่น้องของ sampling — แทนที่จะขอ “สมอง” ของ model มันขอ input จากผู้ใช้ spec นิยามมันเป็นกลไกที่ server ร้องขอข้อมูลเพิ่มจากผู้ใช้กลางทาง ผ่าน client โดยมี schema บอกว่าอยากได้อะไรกลับ (elicitation, MCP spec 2025-11-25) แทนที่ server จะต้องเดาหรือปฏิเสธคำขอเปล่าๆ เมื่อข้อมูลไม่ครบ มันหยุดแล้ว “ยกมือถาม” — เช่น ถาม ops ยืนยันก่อนยิง refund หรือขอเลขออเดอร์ที่ยังไม่ได้ระบุ ฉบับ 2025-11-25 ยังขยาย elicitation ให้รองรับโหมด URL (ให้ client พาผู้ใช้ไปทำบางอย่างบนหน้าเว็บ) ด้วย
สังเกตว่ารูปแบบมันเหมือน sampling เป๊ะ: server เป็นฝ่ายเริ่มคำขอ แต่คำขอไหลผ่าน client ออกไปหา “อีกฝั่ง” — ต่างกันแค่ปลายทางเป็น LLM (sampling) หรือเป็นผู้ใช้ (elicitation) ทั้งคู่จึงเป็นสมาชิกของครอบครัวเดียวกัน: “server ขอ X จากฝั่ง client”
ทั้งคู่เป็นความสามารถฝั่ง client ไม่ใช่ primitive ของ server
หัวข้อที่มีชื่อว่า “ทั้งคู่เป็นความสามารถฝั่ง client ไม่ใช่ primitive ของ server”นี่คือประโยคที่ต้องจำให้แม่นที่สุดของบท: sampling และ elicitation ไม่ใช่ primitive ของ server primitive ของ server มีสามแบบเท่านั้น — Tool, Resource, Prompt (บทที่ 2) ส่วน sampling/elicitation เป็น capability ที่ client ประกาศว่ารองรับ แล้ว server ค่อยใช้ประโยชน์จากมันได้ (sampling, MCP spec 2025-11-25 · elicitation, MCP spec 2025-11-25)
ทำไมเส้นแบ่งนี้ถึงสำคัญ? เพราะ triad “ใครควบคุม” ของบทที่ 2 จะเบลอทันทีถ้าเอา sampling/elicitation ไปปนเป็น primitive แบบที่สี่/5 primitive ตอบคำถาม “model/app/ผู้ใช้ ใครเป็นคนตัดสินว่าจะเรียกของที่ server เปิดไว้” ส่วน sampling/elicitation เป็นคนละแกน — มันคือ ช่องทางย้อนทิศ ที่ server ใช้ยืมความสามารถของ client กลับมา ไม่ใช่ของที่ server “เปิดให้เรียก” การแยกสองแกนนี้ให้ขาดคือสิ่งที่ทำให้แบบจำลอง MCP ในหัวไม่พังตอนออกแบบจริง
primitive ของ server คือ Tool/Resource/Prompt เท่านั้น sampling และ elicitation เป็น capability ฝั่ง client ที่ server ขอใช้ — คนละแกนกับ triad “ใครควบคุม” ถ้ารวมมันเข้าไปในรายการ primitive เส้นแบ่ง model/app/user-controlled จะพัง และการให้เหตุผลเรื่องความปลอดภัย (ใครกดสวิตช์ ใครเห็นอะไร) จะคลาดทันที
Composition: host รวมหลาย server เป็นระบบนิเวศเดียว
หัวข้อที่มีชื่อว่า “Composition: host รวมหลาย server เป็นระบบนิเวศเดียว”ย้อนไปบทที่ 1: host หนึ่งตัวถือ client ได้หลายตัว (ตัวละ server) และเป็นศูนย์กลางเดียวที่เห็นบทสนทนาทั้งหมด เมื่อประกอบ server ออเดอร์ + ครัว + การเงินเข้าด้วยกัน host คือคนที่รวมสิ่งที่แต่ละ server ยื่น (ทั้ง primitive และคำขอ sampling/elicitation ที่ย้อนกลับมา) เข้าเป็นบทสนทนาเดียว พร้อมรักษาการแยกส่วนไว้ — server ตัวหนึ่งยังมองไม่เห็น server เพื่อนบ้านและมองไม่เห็นบทสนทนาทั้งหมด นี่คือหัวใจของการประกอบ: พลังเพิ่มขึ้นเพราะ copilot ต่อได้หลายระบบ แต่ host ต้องเป็นด่านที่คุม consent และกันไม่ให้ข้อมูลข้าม server กันเอง
flowchart LR
subgraph HOST["Host — รวมหลาย server + คุม consent/แยกส่วน"]
LLM["LLM + ตัวประสานงาน"]
CA["Client A"]
CB["Client B"]
end
SO["Server: ออเดอร์"]
SF["Server: การเงิน"]
CA <-->|"tool/resource/prompt<br/>Host/Client → Server"| SO
CB <-->|"session 1:1"| SF
SO -.->|"sampling: ขอ completion จาก LLM ผ่าน client"| CA
SO -.->|"elicitation: ขอ input มีโครงสร้างจากผู้ใช้ ผ่าน client"| CA
CA -.->|"ส่งต่อคำขอ + มนุษย์รีวิว (SHOULD)"| LLM
classDef host fill:#c7d2fe,stroke:#3730a3,color:#1a1a1f;
classDef srv fill:#bbf7d0,stroke:#065f46,color:#1a1a1f;
class LLM,CA,CB host;
class SO,SF srv;
คำบรรยายภาพ: เส้นทึบคือทิศปกติ (Host/Client → Server เรียก tool/resource/prompt) ส่วนเส้นประคือทิศย้อนกลับ — server ออเดอร์ยิง sampling (ขอ completion จาก LLM) และ elicitation (ขอ input จากผู้ใช้) โดยทั้งคู่ ไหลผ่าน client ไม่ได้ทะลุไปหา LLM หรือผู้ใช้เอง host (สีน้ำเงิน) ถือ client หลายตัวและรวม server ทั้งหมด (สีเขียว) เข้าเป็นบทสนทนาเดียว พร้อมรักษาการแยกส่วน — server การเงินมองไม่เห็น server ออเดอร์ และไม่มี server ตัวไหนเห็นบทสนทนาทั้งหมด
Versioning ของ contract: server surface คือสัญญาที่มี version
หัวข้อที่มีชื่อว่า “Versioning ของ contract: server surface คือสัญญาที่มี version”พอประกอบหลาย server เข้าด้วยกัน สิ่งที่แต่ละ server เปิดออกมา — ชื่อ tool, schema parameter, รูปแบบ resource, schema ที่ elicitation คาดหวัง — ทั้งหมดนี้กลายเป็น contract ที่ host/agent ฝั่งตรงข้ามพึ่งพา ร้อยกลับเข้าเลนส์ของคอร์สพี่ (Agent as Bounded Context): server surface คือ ภาษาที่เผยแพร่ (published language) ของ bounded context นั้น และภาษาที่เผยแพร่ก็มี version เสมอ
นั่นแปลว่าการเปลี่ยน surface ของ server ที่ถูกประกอบอยู่ = การเปลี่ยน contract ที่อาจ breaking ต่อ consumer เปลี่ยนชื่อ tool, เพิ่ม field บังคับ, หรือปรับ schema elicitation ล้วนกระทบ agent ที่พึ่งภาษาชุดเดิม — เหมือนการแก้ published language ของ bounded context ที่มี downstream พึ่งอยู่ วินัยเดิมทั้งหมดจึงกลับมาใช้ตรงๆ: tool ที่ตั้งชื่อดีและเป็น ACL ที่แคบ (บทที่ 3, 5) ไม่ใช่แค่เรื่องความปลอดภัย แต่คือ contract ที่ เสถียรและอธิบายตัวเองได้ พอที่จะประกอบกับ server อื่นได้โดยไม่พังเงียบๆ เมื่อฝั่งใดฝั่งหนึ่งขยับ version
การต่อ server หลายตัวเข้า agent เดียวเพิ่มพลัง แต่ก็ประกอบ lethal trifecta (บทที่ 6) ได้ง่ายขึ้น — ข้อมูลลับจาก server หนึ่ง + เนื้อหาที่ควบคุมไม่ได้จากอีก server + ช่องส่งออก sampling/elicitation ยิ่งเพิ่มทิศทางที่ข้อมูลไหลได้ host จึงต้องเป็นด่านเดียวที่รักษา consent และการแยกส่วน ไม่ใช่หวังพึ่งให้ server แต่ละตัวคุมกันเอง (server มองไม่เห็นกันอยู่แล้ว)
- sampling = server ขอ completion จาก LLM ผ่าน client ด้วย
sampling/createMessage— ไม่ต้องมี API key ฝั่ง server, เลือก model ด้วย cost/speed/intelligence priority ไม่ใช่ล็อกชื่อรุ่น, มนุษย์ควร (SHOULD) รีวิวทั้ง prompt และผล และ ณ spec 2025-11-25 เรียก tool ได้เองจนกลายเป็น nested loop - elicitation = server ขอ input มีโครงสร้างจากผู้ใช้ ผ่าน client — พี่น้องของ sampling ในครอบครัว “server ขอ X จากฝั่ง client”
- ทั้งคู่เป็น capability ฝั่ง client ไม่ใช่ primitive ของ server (primitive มีแค่ Tool/Resource/Prompt) — คนละแกนกับ triad “ใครควบคุม”
- host เป็นตัวประกอบ หลาย server เข้าเป็นบทสนทนาเดียว พร้อมรักษาการแยกส่วน และ contract ที่แต่ละ server เปิดออกมามี version — เปลี่ยน surface = เปลี่ยนภาษาที่เผยแพร่ที่ downstream พึ่งอยู่
บทนี้อ้างอิงแหล่งปฐมภูมิโดยตรง (ยังไม่มีการ์ด DevIQ สำหรับหัวข้อ sampling/elicitation ของ MCP โดยเฉพาะ):
- sampling (MCP spec 2025-11-25) — server ขอ completion จาก LLM ผ่าน client โดยไม่ต้องมี API key ฝั่ง server และเลือก model ด้วย priority เชิงนามธรรม
- elicitation (MCP spec 2025-11-25) — server ขอ input มีโครงสร้างจากผู้ใช้ผ่าน client
- changelog (MCP spec 2025-11-25) — SEP-1577 เพิ่ม tool-calling ให้ sampling ทำให้กลายเป็น nested loop
- คอร์ส Agent as Bounded Context — เลนส์ DDD (server surface เป็น published language, tool เป็น ACL) ที่เรื่อง versioning ของ contract ต่อยอด
เช็กความเข้าใจ — บทที่ 7
ข้อ 1 / 3sampling (sampling/createMessage) ให้ server ขอ completion จากใคร และด้วยเงื่อนไขอะไร?