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

ประกอบ​เป็น context layer ตัว​เต็ม

เจ็ด​บท​ที่​ผ่าน​มา​คุณ​สร้าง​เครื่องมือ​มา​ที​ละ​ชิ้น — retrieval ที่​ดึง​เฉพาะ​ข้อมูล​ที่​เกี่ยว (บท​ที่ 2), memory ที่ agent เขียน​โน้ต​เก็บ​ไว้​นอก​หน้าต่าง (บท​ที่ 3), การ​มอง​หน้าต่าง​เป็น​งบ​ที่​ใช้​แล้ว​หมด (บท​ที่ 4), compaction ที่​สรุป​ประวัติ​ให้​สั้น​โดย​รักษา​สิ่ง​ที่​ชี้ขาด (บท​ที่ 5) และ​การ​ปั้น​ผล​ของ tool ให้​เหลือ​แต่ field ที่​ใช้​จริง (บท​ที่ 6) แต่ละ​ชิ้น​คุณ​ได้​ทดลอง​แยก​กัน บท​นี้​คือ​บท​ที่​ทุก​ชิ้น​มา​ประกอบ​เป็น​ระบบ​เดียว — context layerContext Engineeringตาม​นิยาม​ของ Anthropic คือ 'ชุด​กลยุทธ์​ใน​การ​คัด​สรร​และ​รักษา​เซต​ของ token (ข้อมูล) ที่​เหมาะสม​ที่สุด​ระหว่าง​ที่ model กำลัง inference' — ไม่ใช่​แค่ prompt แต่​รวม​ทุก​อย่าง​ที่ model เห็น​ใน​แต่ละ​เทิร์น (system prompt, tools, ผล​ของ tool, ประวัติ​บทสนทนา) เป็น​วิวัฒนาการ​ต่อยอด​จาก Prompt Engineering: prompt เขียน​ครั้ง​เดียว แต่ context ต้อง​คัด​สรร​ใหม่​ทุก​เทิร์น ดาวเหนือ​ของ​ทั้ง​ศาสตร์​คือ 'หา​เซต high-signal token ที่​เล็ก​ที่สุด​ที่​ยัง​ทำให้​ได้​ผลลัพธ์​ที่​ต้องการ'Process ชั้น​หนึ่ง​ที่​คร่อม agent Order ตัว​เดิม​จาก​คอร์ส​ที่ 15 แล้ว​ทำงาน​ร่วม​กัน​ใน​ทุก​เทิร์น ไม่ใช่​ห้า​เทคนิค​ที่​ต่าง​คน​ต่าง​อยู่ แต่​เป็น​สายพาน​เดียว​ที่​ทุก​ส่วน​เล็ง​ไป​ที่​ดาวเหนือ​ดวง​เดียวกัน

📦 code ตัวอย่าง

บท​นี้​ประกอบ context layer เต็ม​ตัว​คร่อม agent Order จาก repo kaen-food-ordering (กำลัง​จัด​ทำ) — retrieval, memory, compaction และ tool ที่ curate มา​รวม​เป็น pipeline เดียว​ต่อ​เทิร์น ทุก​ฝั่ง chat ยัง​ยึด invariant เดิม​ของ #15 ทั้งหมด และ​ฝั่ง embeddings ยัง​เป็น provider ที่​สอง (จาก​บท​ที่ 2) code ใน​บท​นี้​เป็นการ ต่อ ชิ้น​ส่วน​ที่​คุณ​สร้าง​ใน​บท​ก่อนๆ เข้า​ด้วย​กัน ไม่ใช่​ของ​ใหม่

ก่อน​ต่อ code ทวน​ภาพ​รวม​ว่า​แต่ละ​ชั้น​ทำ​อะไร​ใน​หนึ่ง​เทิร์น และ​มัน​เชื่อม​กัน​ตรง​ไหน context layer ไม่ใช่​กล่อง​ห้า​ใบ​วาง​เรียง​กัน แต่​เป็น​ลำดับ​ขั้น​ที่​ผล​ของ​ขั้น​ก่อน​ป้อน​ขั้น​ถัด​ไป:

  1. อ่าน memory ที่​เกี่ยว (บท​ที่ 3) — ก่อน​เรียก model ดึง​โน้ต​ความ​จำ​ยาว​ที่​เกี่ยว​กับ​เคส​นี้ (Kind = "memory") มา​เสียบ​เข้า system message
  2. เช็ค​งบ token (บท​ที่ 4) — วัด input token ของ​ประวัติ​ปัจจุบัน ถ้า​เกิน​เพดาน​ที่​ตั้ง​ไว้ ให้​เข้า​ขั้น​ถัด​ไป ถ้า​ยัง​ไม่​เกิน​ก็​ข้าม
  3. compaction (บท​ที่ 5) — สรุป​ประวัติ​ที่​ยาว​ให้​เหลือ summary ก้อน​เดียว รักษา order id + เหตุผล​คืน​เงิน ทิ้ง blob ที่​จัดการ​ไป​แล้ว
  4. เรียก model ด้วย tool ที่ curate + retrieval (บท​ที่ 2, 6) — GetResponseAsync โดย​ผูก tool ที่​คืน​ผล​กระชับ บวก searchOrderKnowledge ที่​ดึง​เฉพาะ​ข้อความ​ที่​เกี่ยว
  5. เขียน memory ปิด​ท้าย (บท​ที่ 3) — หลัง​เทิร์น​จบ ให้ agent saveMemory สิ่ง​ที่​ต้อง​รอด​ข้าม​การ​รีเซ็ต​หน้าต่าง​ครั้ง​หน้า

สังเกต​ว่า​ชั้น retrieval (บท​ที่ 2) กับ​ชั้น memory (บท​ที่ 3) ใช้​โครงสร้าง​เก็บ​ข้อมูล​ตัว​เดียวกัน — Vector StoreVector Storeที่​เก็บ embedding ที่​ค้น​ด้วย​ความ​คล้าย​เชิง​ความหมาย​ได้ — เก็บ​ทั้ง​เวกเตอร์​และ​ข้อความ​ต้นทาง​ที่​จะ​ป้อน​กลับ​ให้ model ใน​คอร์ส​ใช้ Microsoft.Extensions.VectorData (MEVD): abstraction เป็น GA แล้ว แต่​ตัว store จริง​ยัง prerelease (ใส่ `--prerelease`) model ข้อมูล​ประกาศ​ด้วย attribute เช่น `[VectorStoreVector(Dimensions: 1536)]` ที่ 'มิติ​ต้อง​ตรง​กับ model embedding' เป๊ะ ๆArchitecture ก้อน​เดียว​ที่​แยก​บทบาท​ด้วย field Kind ที่​ถูก index ไว้ นี่​คือ​กุญแจ​ของ​การ​ประกอบ: คุณ​ไม่​ต้อง​มี​สอง​ระบบ retrieval กับ memory คือ การ​อ่าน จาก​คลัง​เดียวกัน​ด้วย​ตัว​กรอง​ต่าง​กัน

flowchart TB
  U["ข้อความลูกค้า"] --> MEM
  MEM["1 · อ่าน long-term memory<br/>Kind = memory"] --> BUD
  BUD{"2 · งบ token<br/>เกินเพดาน?"} -->|ใช่| COMP["3 · compaction<br/>สรุปประวัติ"]
  BUD -->|ไม่| ASM
  COMP --> ASM
  ASM["ประกอบ context<br/>retrieval + memory + ประวัติ + tool view"] --> LLM["4 · agent loop จาก #15<br/>claude-opus-4-8"]
  LLM --> TOOLS["เรียก tool ที่ curate<br/>searchOrderKnowledge · getOrder · issueRefund"]
  TOOLS --> LLM
  LLM --> WRITE["5 · saveMemory<br/>สิ่งที่ต้องรอดข้ามเทิร์น"]
  WRITE --> ANS["คำตอบถึงลูกค้า"]
  VS[("Vector Store ก้อนเดียว<br/>policy · faq · order · memory")]
  VS -.-> MEM
  VS -.-> TOOLS
  WRITE -.-> VS
  classDef store fill:#0891b2,stroke:#064e5b,color:#f8fafc;
  class VS store;

คำ​บรรยาย​ภาพ: context layer หนึ่ง​เทิร์น​เดิน​จาก​บน​ลง​ล่าง — อ่าน memory ที่​เกี่ยว เช็ค​งบ token แล้ว compaction ถ้า​เกิน​เพดาน ประกอบ context ที่​คัด​แล้ว​ป้อน agent loop เดิม​ของ #15 ซึ่ง​เรียก tool ที่ curate ไว้ และ​ปิด​ท้าย​ด้วย​การ​เขียน memory สิ่ง​ที่​ต้อง​รอด​ข้าม​เทิร์น ช่อง​สี​ฟ้า​คือ Vector Store ก้อน​เดียว​ที่​ทั้ง retrieval (searchOrderKnowledge) และ memory อ่าน/เขียน​ร่วม​กัน แยก​บทบาท​ด้วย field Kind

จุด​เริ่ม​ของ​การ​ประกอบ​คือ collection เดียว​ที่​คุณ​สร้าง​ไว้​ใน​บท​ที่ 2 เพียง​แต่​ตอน​นี้​มัน​รับ record สอง​ชนิด — เอกสาร​นโยบาย/FAQ/ออเดอร์​สำหรับ retrieval และ​โน้ต​ความ​จำ​ที่ agent เขียน​เอง — แยก​กัน​ด้วย field Kind ที่ index ไว้ ทั้ง​สอง​บทบาท​ค้น​ด้วย SearchAsync ตัว​เดียวกัน ต่าง​แค่​ตัว​กรอง:

// retrieval (บทที่ 2): ค้นเฉพาะนโยบาย/FAQ
await foreach (var r in col.SearchAsync(qVector, top: 3,
new VectorSearchOptions<OrderDoc> { Filter = d => d.Kind == "policy" || d.Kind == "faq" }))
hits.Add(r.Record.Text);
// long-term memory (บทที่ 3): ค้นเฉพาะโน้ตของ agent เอง
await foreach (var r in col.SearchAsync(qVector, top: 3,
new VectorSearchOptions<OrderDoc> { Filter = d => d.Kind == "memory" }))
notes.Add(r.Record.Text);

คลัง​เดียว​สอง​บทบาท​นี้​คือ​เหตุผล​ที่ retrieval กับ memory ไม่ใช่2 project แยก​กัน — ทั้ง​คู่​เป็นการ​อ่าน context ที่​เกี่ยว​เข้า​มา​แบบ just-in-time ต่าง​กัน​แค่​ว่า​ใคร​เป็น​คน เขียน ของ​ลง​คลัง: เอกสาร​นโยบาย​คุณ​เป็น​คน​ใส่​ล่วงหน้า ส่วน​โน้ต Kind = "memory" คือ​สิ่ง​ที่ agent เขียน​เอง หลัง​แต่ละ​เทิร์น

ชุด tool ของ context layer ต่อยอด​จาก​สาม​ตัว​เดิม​ของ #15 ตรงๆ — เพิ่ม searchOrderKnowledge จาก​บท​ที่ 2 และ saveMemory จาก​บท​ที่ 3 ส่วน getOrder ใช้ version getOrderView ที่ curate แล้ว​จาก​บท​ที่ 6:

// ชุด tool เต็มของ context layer — สามตัวเดิมของ #15 + retrieval + memory
var options = new ChatOptions
{
Tools = [ searchKnowledge, saveMemory, getOrderView, getDeliveryStatus, issueRefund ]
};
// loop เดิมของ #15: x-api-key (ไม่ใช่ Authorization: Bearer), anthropic-version 2023-06-01,
// model id เปล่า "claude-opus-4-8" (ตัวอย่าง ณ 2026-07), ไม่ส่ง temperature
IChatClient chat = anthropicChatClient.AsBuilder().UseFunctionInvocation().Build();

แล้ว​ห่อ​หนึ่ง​เทิร์น​ด้วย pipeline ห้า​ขั้น​ข้าง​บน — code นี้​คือ context layer ทั้ง​ชั้น​ใน​รูป method เดียว:

async Task<ChatResponse> RunTurnAsync(
List<ChatMessage> history, string userText, IChatClient chat)
{
// 1. อ่าน memory ที่เกี่ยว (Kind == "memory") เสียบเข้า system message (บทที่ 3)
var memoryNotes = await RecallMemoryAsync(userText);
history[0] = new(ChatRole.System, baseSystemPrompt + "\n\nโน้ตความจำที่เกี่ยว:\n" + memoryNotes);
// 2. เช็คงบ token ของประวัติ (บทที่ 4) -> 3. compaction ถ้าเกินเพดาน (บทที่ 5)
if (await CountInputTokensAsync(history) > CompactionThreshold)
history = await CompactAsync(history, chat); // รักษา order id + เหตุผลคืนเงิน ทิ้ง blob ที่จัดการแล้ว
history.Add(new(ChatRole.User, userText));
// 4. เรียก model ด้วย tool ที่ curate + retrieval (บทที่ 2, 6)
var reply = await chat.GetResponseAsync(history, options);
history.AddMessages(reply);
// 5. ให้ agent เขียน memory สิ่งที่ต้องรอดข้ามเทิร์น (บทที่ 3)
// saveMemory เป็น tool ในชุด agent จึงเรียกเองได้ระหว่างเทิร์น
// หรือจะ upsert โน้ตปิดท้ายจากฝั่งเราก็ได้
return reply;
}

ทุก​ขั้น​ใน method นี้​คือ​ชั้น​หนึ่ง​ที่​คุณ​สร้าง​มา​แล้ว — RecallMemoryAsync และ saveMemory คือ memory (บท​ที่ 3), CountInputTokensAsync คือ​เครื่อง​วัด​งบ (บท​ที่ 4), CompactAsync คือ compaction (บท​ที่ 5), getOrderView ใน options คือ curation (บท​ที่ 6) และ searchKnowledge คือ retrieval (บท​ที่ 2) สิ่ง​ที่​บท​นี้​เพิ่ม​คือ ลำดับ ที่​ทำให้​ทั้ง​ห้า​ทำงาน​ประสาน​กัน​ต่อ​เทิร์น ไม่ใช่​ตัว​เทคนิค​ใหม่

⚠️ ทุก invariant ของ #15 ยัง​อยู่​ครบ

ฝั่ง chat ทั้งหมด​ใน​บท​นี้​ยึด​กติกา​เดิม​ของ​คอร์ส #15 — header x-api-key (ไม่ใช่ Authorization: Bearer), anthropic-version: 2023-06-01, model id เปล่า claude-opus-4-8 (ตัวอย่าง ณ 2026-07 — id เลื่อน​ไหว​ตาม​เวลา), ไม่​ส่ง temperature/top_p/top_k และ​คำ​ตอบ/ผล​ของ tool อ่าน​จาก content[] ไม่ใช่ .text ระดับ​บน​สุด ส่วน​ฝั่ง embeddings ยัง​เป็น provider ที่​สอง (OpenAI text-embedding-3-small, 1536 มิติ) เพราะ Claude ไม่มี endpoint สำหรับ embedding — context layer บังคับ​ให้​คุณ​ถือ2 provider เสมอ

วิธี​พิสูจน์​ว่า context layer ทำงาน​จริง ไม่ใช่​แค่​ประกอบ code ผ่าน คือ​เดิน​เคส support หนึ่ง​เคส​ที่ แตะ​ทุก​ชั้น​ใน​เทิร์น​เดียวกัน:

  1. ลูกค้า​ถาม​เรื่อง​คืน​เงิน → agent เรียก searchOrderKnowledge ดึง​นโยบาย​คืน​เงิน​เข้า​มา (retrieval)
  2. agent เขียน​โน้ต​ว่า​เคส​นี้​เข้า​เงื่อนไข​คืน​เงิน saveMemory (memory)
  3. บทสนทนา​ยาว​จน​งบ token เกิน​เพดาน → CompactAsync สรุป​ประวัติ (compaction)
  4. หลัง compaction agent ยัง​ปิด​เคส​ได้​ถูก — ยืนยัน order id และ​เหตุผล​คืน​เงิน​ยัง​อยู่​ครบ ที่​จำนวน token ต่ำ​กว่า agent #15 แบบ​ดิบ​ที่​ไม่มี context layer

เกณฑ์​ผ่าน​คือ​ข้อ 4: order id + เหตุผล​คืน​เงิน​รอด​ข้าม compaction และ จำนวน input token รวม​ต่ำ​กว่า version ดิบ​อย่าง​วัด​ได้ — นี่​คือ​รูปธรรม​ของ “minimal performance degradation” ที่​บท​ที่ 5 พูด​ถึง วัด​ด้วย field usage ตัว​เดียว​กับ​บท​ที่ 4 อย่า​เชื่อ ให้​วัด

context layer ที่​คุณ​เพิ่ง​ประกอบ ไม่ใช่​ห้า​เทคนิค​ที่​บังเอิญ​มา​อยู่​ด้วย​กัน แต่​เป็น​ห้า​วิธี​เข้า​ใกล้ ดาวเหนือ​ดวง​เดียว ที่​บท​ที่ 1 วาง​ไว้ Anthropic ปิด​ท้าย​ด้วย​ประโยค​ที่​ควร​จำ​ขึ้นใจ — “Whether you’re implementing compaction for long-horizon tasks, designing token-efficient tools, or enabling agents to explore their environment just-in-time, the guiding principle remains the same: find the smallest set of high-signal tokens that maximize the likelihood of your desired outcome.”

อ่าน​ประโยค​นั้น​แล้ว map กลับ​ลง​บน pipeline ของ​คุณ: compaction คือ “long-horizon tasks”, curation คือ “token-efficient tools”, retrieval คือ “explore their environment just-in-time” — ทั้งหมด​เล็ง​ไป​ที่ “the smallest set of high-signal tokens” เดียวกัน นี่​คือ​เหตุผล​ที่​ทั้ง​คอร์สร้อย​เป็น​เรื่อง​เดียว ไม่ใช่​รายการ​เทคนิค​แยก​ส่วน

และ​ปลายทาง​ที่​ผู้ใหญ่​ที่สุด​ไม่ใช่ “retrieval อย่าง​เดียว” หรือ “compaction อย่าง​เดียว” แต่​คือ​การ​ผสม Anthropic เรียก​มัน​ว่า hybrid — “the most effective agents might employ a hybrid strategy, retrieving some data up front for speed, and pursuing further autonomous exploration at its discretion.” context layer ของ​คุณ​ก็​เป็น​แบบ​นั้น: นโยบาย​ที่​ใช้​บ่อย​อาจ pre-stuff ไว้​ใน system prompt ส่วน​ที่​เหลือ​ค้น​เอา​ตอน runtime และ​สำหรับ agent ที่​รัน​ยาว ให้​จับ​คู่ memory กับ compaction เข้า​ด้วย​กัน — “For long-running agents, consider using both: compaction keeps the active context small without client-side bookkeeping, and memory preserves the information that must survive summarization.”

ตัวเลข​นี้​ลง​วัน​ที่​ไว้

Anthropic รายงาน​ว่า memory + context editing ให้ “84% token savings and a 39% performance improvement” บน eval หนึ่ง​ชุด (งาน​ค้น​เว็บ 100 เทิร์น, กันยายน 2025) — ใช้​เป็น ทิศทาง ว่าการ​จับ​คู่​สอง​เทคนิค​นี้​ช่วย​ได้​จริง ไม่ใช่​ตัวเลข​ที่​จะ​ได้​เป๊ะ​กับ workload ของ​คุณ ตัวเลข​ทุก​ตัว​ใน​คอร์ส​นี้​เป็น “หลักการ​ที่​ลง​วัน​ที่” ไม่ใช่​กฎ​ตายตัว

context layer ทำให้ agent เดิม​ของ #15 เห็น​ของ​ที่​ถูกต้อง ใน​ทุก​เทิร์น — แต่​มี​คำถาม​หนึ่ง​ที่​บท​นี้​ตอบ​เอง​ไม่​ได้ และ​ไม่​ควร​แกล้ง​ตอบ: คุณ​รู้​ได้​อย่างไร​ว่า context layer ช่วย​จริง ไม่ใช่​แค่​รู้สึก​ว่า​ช่วย? compaction ที่ “รักษา​สิ่ง​ที่​ชี้ขาด” อาจ​ทิ้ง​รายละเอียด​ที่​สำคัญ​ไป​เงียบๆ retrieval ที่ recall ต่ำ​อาจ​พลาด​ข้อความ​ที่​ตัดสิน​เคส สิ่ง​เหล่า​นี้​มอง​ด้วย​ตา​ไม่​เห็น — ต้อง วัด ด้วย​ชุด​ประเมิน​ผล​ที่​ออกแบบ​มา นั่น​คือ​งาน​ของ​คอร์ส​ถัด​ไป ไม่ใช่​ข้อ​อ้าง​ที่​บท​นี้​จะ​สรุป​เอง

🔁 ส่ง​ไม้​ต่อ​จาก​บท​นี้

context layer พร้อม​แล้ว จาก​นี้​ต่อ​ได้​สี่​ทาง:

  • ประเมิน​ว่า​มัน​ช่วย​จริง​ไหม → คอร์ส #13 Evals สำหรับ AI agent — คำถาม​ธรรมชาติ​ถัด​จาก​บท​นี้: compaction ทิ้ง​รายละเอียด​ที่​ชี้ขาด​ไป​หรือ​เปล่า retrieval ดึง​ของ​ที่​เกี่ยว​มา​ครบ​ไหม ตอบ​ได้​ด้วย eval เท่านั้น
  • ขอบเขต​ของ agent → คอร์ส #12 Agent ใน​ฐานะ bounded context — context layer อยู่​ใน​ขอบเขต​ของ agent หนึ่ง​ตัว เมื่อไหร่​ควร​แยก​เป็น​อีก context
  • ห่อ tool เป็น server → คอร์ส #14 ออกแบบ MCP serversearchOrderKnowledge กับ tool อื่นๆ แพ็ก​เป็น server ที่ agent ตัว​ไหน​ก็​ต่อ​ได้​อย่างไร
  • ทบทวน agent ฐาน → คอร์ส #15 สร้าง AI agent ตัว​แรก​ด้วย C# — LLM + tool + loop ที่ context layer นี้​คร่อม​อยู่

จบ​คอร์ส​นี้ คุณ​ไม่​ได้​มี agent ที่​ฉลาด​ขึ้น​เพราะ model ใหญ่​ขึ้น แต่​มี agent เดิม​ที่ เห็น​ของ​ที่​ถูกต้อง ใน​ทุก​เทิร์น — นั่น​คือ​ทั้งหมด​ที่ context engineering สัญญา​ไว้​ตั้งแต่​บท​ที่ 1


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้

บท​นี้​อิง​ต้นทาง​ที่​ลง​วัน​ที่​กำกับ อ่าน​ต่อ​ได้​โดยตรง:

  • Anthropic, “Effective context engineering for AI agents” (2025-09-29) — ดาวเหนือ​ที่​ร้อย​ทุก​เทคนิค (“smallest set of high-signal tokens…”), hybrid strategy เป็น​ปลายทาง​ที่​ผู้ใหญ่​ที่สุด และ​การ​มอง​ทุก​ชั้น​เป็นการ​คัด​สรร context ต่อ​เทิร์น
  • Anthropic, “Memory tool” docs (เข้าถึง 2026-07-19) — การ​จับ​คู่ memory กับ compaction สำหรับ agent ที่​รัน​ยาว
  • Anthropic, “Context management” (กันยายน 2025) — ตัวเลข 84% token / 39% performance จาก eval 100 เทิร์น (ลง​วัน​ที่​ไว้ ไม่ใช่​กฎ)
  • Microsoft Learn, “Use vector stores in .NET AI apps” (ms.date 2026-02-28) — SearchAsync, VectorSearchOptions<T> และ​การกรอง​ด้วย property ที่ index ไว้​สำหรับ​คลัง​เดียว​สอง​บทบาท
  • Microsoft Learn, “Microsoft.Extensions.AI libraries” (ms.date 2025-12-10, ปรับ 2026-06-18) — IChatClient, ChatOptions.Tools, UseFunctionInvocation และ ChatResponse.Usage สำหรับ​ประกอบ pipeline และ​วัด token

เช็กความเข้าใจ — บทที่ 8

ข้อ 1 / 3

ในการประกอบ context layer ทำไม retrieval (บทที่ 2) กับ long-term memory (บทที่ 3) จึงใช้ Vector Store ก้อนเดียวกันได้?