ข้าม​ไป​ยัง​เนื้อหา

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 ตัวอย่าง (กำลัง​จัด​ทำ)

คอร์ส​นี้​เป็น คอร์ส​ออกแบบ — code ใน​บท​นี้​คือ ภาพร่าง ของ​รูปแบบ​คำขอ sampling/elicitation และโทโพโล​ยี​การ​ประกอบ​หลาย server ไม่ใช่ server ที่​รัน​ได้​จริง ตัวอย่าง​ต่อยอด​จาก domain เดิม​ที่ repo kaen-food-ordering (กำลัง​จัด​ทำ): server ออเดอร์ และ การเงิน ที่​ต้อง​ขอ​ให้ model ช่วย​จัด​หมวด​คำร้องเรียน​กลาง​ทาง (sampling) หรือ​ถาม ops ยืนยัน​ก่อน​ยิง refund (elicitation) โดย​มี ops-copilot (host) เป็น​ตัวประกอบ​ทั้งหมด​เข้า​ด้วย​กัน

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 ของ​ตัวเอง

🆕 ณ spec 2025-11-25: sampling เรียก tool ได้​แล้ว (SEP-1577)

ก่อนหน้า​นี้ 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…” เสมอ — เป็น​ส่วน​ที่​ยัง​เปลี่ยน​เร็ว อย่า​จำเป็น​คุณสมบัติ​เหนือ​กาล​เวลา

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 ใน​หัว​ไม่​พัง​ตอน​ออกแบบ​จริง

⚠️ อย่า​นับ sampling/elicitation เป็น primitive ที่​สี่/ห้า

primitive ของ server คือ Tool/Resource/Prompt เท่านั้น sampling และ elicitation เป็น capability ฝั่ง client ที่ server ขอ​ใช้ — คนละ​แกน​กับ triad “ใคร​ควบคุม” ถ้า​รวม​มัน​เข้าไป​ใน​รายการ primitive เส้น​แบ่ง model/app/user-controlled จะ​พัง และ​การ​ให้​เหตุผล​เรื่อง​ความ​ปลอดภัย (ใคร​กด​สวิตช์ ใคร​เห็น​อะไร) จะ​คลาด​ทันที

ย้อน​ไป​บท​ที่ 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 ตัว​ไหน​เห็น​บทสนทนา​ทั้งหมด

พอ​ประกอบ​หลาย 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

⚠️ ยิ่ง​ประกอบ​มาก ยิ่ง​ต้อง​คุม consent ที่ host

การ​ต่อ 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 / 3

sampling (sampling/createMessage) ให้ server ขอ completion จากใคร และด้วยเงื่อนไขอะไร?