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

จัด​ระเบียบ​ผล​ของ tool

ตั้งแต่​บท​ที่ 2 คุณ​คิด​เรื่อง context ใน​ฐานะ​สิ่ง​ที่​คุณ เลือก​ใส่ เข้าไป — retrieval ดึง​ข้อมูล​เข้า​มา memory เขียน​โน้ต​กลับ​มา compaction สรุป​ประวัติ​ให้​สั้น​ลง แต่​มี​แหล่ง context อีก​ก้อน​หนึ่ง​ที่​มัก​ถูก​มอง​ข้าม เพราะ​มัน​ไม่​ได้​มา​จาก​มือ​คุณ​โดยตรง — มัน​คือ สิ่ง​ที่ tool ของ​คุณ คืน​กลับ​มา ใน​คอร์ส​ที่ 15 getOrder คืน record ออเดอร์​ทั้ง​ก้อน​เข้า loop ทุก field ใน​ก้อน​นั้น — line item ที่​อยู่​จัด​ส่ง payment token field audit — เดินทาง​เข้า context window เท่า​กัน​หมด ไม่​ว่า agent จะ​ได้​ใช้​มัน​จริง​หรือ​ไม่ บท​นี้​ปรับ​สายตา​อีก​องศา: ผลลัพธ์​ของ tool คือ context ที่​คุณ​ออกแบบ​ได้ และ​การ​ปั้น​มัน​ให้​เหลือ​แต่​ของ​ที่​จำเป็น​คือ​หนึ่ง​ใน​การ​ใช้​จ่าย​งบ​ที่​คุ้ม​ที่สุด

📦 code ตัวอย่าง

บท​นี้​ปั้น getOrder ตัว​เดิม​ของ agent Order จาก repo kaen-food-ordering (กำลัง​จัด​ทำ) ให้​คืน​เฉพาะ field ที่ support agent ใช้​เหตุผล​ได้​จริง แล้ว​วัดผล​ต่าง​ของ token ด้วย field usage เทคนิค​นี้​ต่อยอด​จาก​การ​เคลียร์​ผล​ของ tool ที่​เกริ่น​ไว้​ใน​บท​ที่ 5 และ​ใช้​เครื่อง​วัด token ตัว​เดียว​กับ​บท​ที่ 4

เวลา​เรา​พูด​ถึง “การ​จัด​ระเบียบ context” ภาพ​ใน​หัว​มัก​เป็นการ​คัด prompt หรือ​คัด​ประวัติ​บทสนทนา แต่​ใน​ระบบ​ที่​วน​เป็น loop ปริมาณ context ก้อน​ใหญ่​ที่สุด​ที่​พอก​ขึ้น​ต่อ​เนื่อง กลับ​เป็น ผล​ของ tool — และ​ตรง​นั้น​แหละ​ที่​วิศวกร​มัก​ปล่อย​ตาม​ยถากรรม เพราะ​โดย default เรา​เขียน tool ให้ return object ที่​มี​อยู่​ใน​มือ ซึ่ง​บ่อย​ครั้ง​คือ aggregate ทั้ง​ก้อน​จาก repository

การ​จัด​ระเบียบ​ผล​ของ toolTool-result Curationการ​ออกแบบ​สิ่ง​ที่ tool 'คืน​กลับ' ให้​เป็น output สัญญาณ​สูง แทน​การ​ทุ่ม JSON ดิบ​ทั้ง​ก้อน — เช่น​เปลี่ยน `getOrder` ให้​คืน view ที่​มี​แค่ 3–5 field ที่ agent ใช้​เหตุผล​จริง (id, status, ลูกค้า, ยอด) payload ที่ tool คืน​คือ context ที่​เรา​กำลัง engineer อยู่ ยิ่ง object เล็ก token ยิ่ง​น้อย โดย agent ยัง​แก้​เคส​ได้​ครบProcess คือ​การ​ออกแบบ สิ่ง​ที่ tool คืน​กลับ​มา อย่าง​จงใจ ให้ agent เห็น​เฉพาะ field ที่​มัน​ต้อง​ใช้​เหตุผล​จริง ไม่ใช่​ทั้ง JSON ก้อน — มอง​อีก​มุม​คือ payload ที่ tool คืน ก็​คือ context ที่​คุณ​กำลัง engineer ไม่​ต่าง​จาก system prompt หรือ​ผล​ของ retrieval มัน​เดินทาง​เข้า context window ผ่าน block tool_result ใน content[] (แบบ​เดียว​กับ​ที่​คุณ​เห็น​ใน​คอร์ส #15) ทุก byte ที่​คุณ return เกิน​ความ​จำเป็น คือ token ที่​ไป​กิน งบ tokenToken Budgetการ​มอง context window เป็น​งบ token ที่​ใช้​แล้ว​หมด​และ​ต้อง​จัด​ลำดับ​ความ​สำคัญ​ว่า​จะ​ใส่​อะไร — เพดาน​หน้าต่าง​สูงสุด​คือ 'เพดาน ไม่ใช่​เป้า' วัด​การ​ใช้​จริง​จาก field `usage` ของ agent (#15) เทิร์น​ต่อ​เทิร์น​แล้ว​ตัด token สัญญาณ​ต่ำ (เช่น blob JSON ดิบ​เก่า ๆ) ทิ้ง​ก่อนProcess ของ​ทุก​เทิร์นที่​เหลือ เพราะ​ผล​ก้อน​นั้น​ค้าง​อยู่​ใน​ประวัติ​และ​ถูก​ส่ง​กลับ​เข้า model ซ้ำ​ทุกรอบ​จนกว่า​จะ​ถูก​เคลียร์

นี่​คือ​เหตุผล​ที่ Anthropic จัด​ให้ tool เป็น​คัน​โยก​ด้าน​ประสิทธิภาพ​โดยตรง — “tools promote efficiency, both by returning information that is token efficient and by encouraging efficient agent behaviors.” สังเกต​ว่า​ประโยค​นี้​พูด​ถึง​สอง​ด้าน: คืน​ผล​ที่​ประหยัด token และ ชักนำ​ให้ agent ทำงาน​อย่าง​ประหยัด ทั้ง​คู่​ขึ้น​กับ​สิ่ง​ที่ tool คืน​กลับ​มา

ลอง​ดู getOrder version ที่​คุณ​น่า​จะ​เขียน​แบบ​ตรง​ไป​ตรง​มา​ที่สุด​ก่อน:

// ❌ version ดิบ — คืน Order aggregate ทั้งก้อนเข้า context
AITool getOrder = AIFunctionFactory.Create(
(string orderId) => repo.Find(orderId), // Order ทั้งก้อน: line items, ที่อยู่จัดส่ง,
// payment token, field audit, timestamp ภายใน ฯลฯ
name: "getOrder",
description: "Look up a food-delivery order.");

version นี้​ทำงาน​ถูกต้อง — agent ได้​ข้อมูล​ครบ แต่​มัน​คืน ทุก​อย่าง ที่ aggregate มี ทั้ง​ที่ support agent ที่​กำลัง​ตัดสิน​ใจ​เรื่อง​คืน​เงิน​แทบ​ไม่​ต้อง​ดู payment token หรือ field audit เลย ทุก field ส่วนเกิน​นี้:

  • กิน​งบ token ทันที — ก้อน JSON ยิ่งใหญ่ = tool_result ยิ่ง​ยาว และ​มัน​ค้าง​ใน​ประวัติ ถูก​ส่ง​ซ้ำ​ทุก​เทิร์น​ถัด​ไป งบ​ที่​ควร​เหลือ​ไว้​ให้​ผล retrieval หรือ​ประวัติ​ที่​สำคัญ​กว่า ถูก field ที่​ไม่​ได้​ใช้​แย่ง​ไป
  • เจือจาง​สัญญาณ — ดาวเหนือ​ของ​ทั้ง​คอร์ส (จาก​บท​ที่ 1) คือ “finding the smallest possible set of high-signal tokens that maximize the likelihood of some desired outcome” field ที่ agent ไม่​ได้​ใช้​ไม่ใช่​แค่ “เปล่า​ประโยชน์” แต่​มัน​เป็น noise ที่ model ต้อง​แบ่ง​ความ​สนใจ​ไป​แยก​ทิ้ง
  • เพิ่ม​พื้นที่​ให้​พลาด — payment token หรือ​ข้อมูล​ส่วน​บุคคล​ที่​ไม่​จำเป็นต่อ​การ​ตัดสิน​ใจ ไม่​ควร​เดินทาง​เข้า context ตั้งแต่​แรก​อยู่​แล้ว

Anthropic วาง​กติกา​ของ tool ที่​ดี​ไว้​ชัด — “tools should be self-contained, robust to error, and extremely clear with respect to their intended use.” tool ที่​คืน aggregate ทั้ง​ก้อน​ละเมิด​ข้อ “clear with respect to their intended use” เพราะ​มัน​ไม่​ได้​สื่อ​ว่า agent ควร สนใจ field ไหน มัน​โยน​ทุก​อย่าง​ให้​แล้ว​ปล่อย​ให้ model เดา​เอง

⚠️ หมายเหตุ​การ​อ้างอิง

Anthropic ไม่​ได้ มี​ประ​โยคตรงๆ ว่า tool ควร “คืน​ผล​ที่ high-signal” คำ​ว่า “high-signal” เป็น​ของ ดาวเหนือ ที่​พูด​ถึง token โดย​รวม ("smallest possible set of high-signal tokens…") ไม่ใช่​กติกา​เฉพาะ​ของ tool ดังนั้น​หลัก “คืน​เฉพาะ field ที่​ใช้​จริง ไม่ใช่ JSON ทั้ง​ก้อน” คือ​การ ประยุกต์ แนวทาง token-efficiency ของ Anthropic ลง​กับ​ผล​ของ tool ไม่ใช่​การ​ยก​ประโยค​ที่​มี​อยู่​แล้ว​มา​อ้าง

วิธี​แก้​ตรง​ไป​ตรง​มา: อย่า​คืน aggregate คืน view ที่​มี​เฉพาะ field ที่ support agent ใช้​เหตุผล — order id, สถานะ, ชื่อ​ลูกค้า, ยอด​รวม และ​เวลา​ที่​สั่ง เท่านั้น ประกาศ​เป็น record เล็กๆ ตัว​หนึ่ง แล้ว​ให้ tool คืน​มัน​แทน:

// ✅ curated — คืนเฉพาะ field ที่ support agent ใช้ตัดสินใจ
// OrderStatus คือ enum 7 ค่าจาก domain ฟู้ดเดลิเวอรี (เช่น Placed, Delivered, Cancelled…)
record OrderView(
string OrderId,
OrderStatus Status,
string CustomerName,
decimal Total,
DateTimeOffset PlacedAt);
AITool getOrderView = AIFunctionFactory.Create(
(string orderId) => repo.Find(orderId) is { } o
? new OrderView(o.Id, o.Status, o.CustomerName, o.Total, o.PlacedAt)
: null,
name: "getOrder",
description: "Look up a food-delivery order. Returns id, status, customer, total, placed-at only.");

จุด​ที่​ต้อง​สังเกต:

  • name ยัง​เป็น "getOrder" เหมือน​เดิม — จาก​มุม​ของ agent สัญญา​ยัง​หน้าตา​เดิม เปลี่ยน​แค่ รูปร่าง​ของ​ผล​ที่​คืน ให้​กระชับ​ลง ไม่​ต้อง​แตะ prompt หรือ loop
  • description บอก​ชัด​ว่า​คืน​อะไร​บ้าง — “Returns id, status, customer, total, placed-at only” ทำให้ tool “clear with respect to its intended use” ตาม​กติกา​ข้าง​บน model รู้​ล่วงหน้า​ว่า​จะ​ได้ field ไหน
  • object ที่ AIFunctionFactory.Create คืน ถูก serialize เป็น block tool_result ใน content[] (invariant เดิม​จาก #15 — คำ​ตอบ​และ​ผล​ของ tool อยู่​ใน content[] ไม่มี .text ระดับ​บน​สุด) object เล็ก​ลง = tool_result สั้น​ลง = token น้อยลงตรงๆ
  • ฝั่ง chat ทุก​อย่าง​เหมือน #15 ทั้งหมด — header x-api-key (ไม่ใช่ Authorization: Bearer), anthropic-version: 2023-06-01, model id เปล่า claude-opus-4-8 (ตัวอย่าง ณ 2026-07), และ ไม่​ส่ง temperature การ​จัด​ระเบียบ​ผล​ของ tool เป็น​เรื่อง​ของ payload ที่​คืน ไม่​แตะ​การ​ตั้ง​ค่า request เลย
flowchart TB
  subgraph RAW["❌ version ดิบ — คืน Order ทั้งก้อน"]
    R1["getOrder"] --> R2["JSON ก้อนใหญ่<br/>line items · ที่อยู่ · payment token · audit"]
    R2 --> RW["context window<br/>เปลือง token กับ field ที่ไม่ได้ใช้"]
  end
  subgraph CUR["✅ curated — คืนเฉพาะ 5 field"]
    C1["getOrder"] --> C2["OrderView<br/>id · สถานะ · ลูกค้า · ยอด · เวลาสั่ง"]
    C2 --> CW["context window<br/>token สัญญาณสูง เหลือที่ว่าง"]
  end
  classDef bloat fill:#b91c1c,stroke:#7f1d1d,color:#f8fafc;
  classDef lean fill:#0891b2,stroke:#064e5b,color:#f8fafc;
  class RW bloat;
  class CW lean;

คำ​บรรยาย​ภาพ: บน — getOrder version ดิบ​คืน JSON ก้อน​ใหญ่​ทั้ง line item ที่​อยู่ payment token และ field audit เข้า context window ทุก field กิน​งบ​เท่า​กัน​ไม่​ว่า​จะ​ได้​ใช้​จริง​หรือ​ไม่ ล่าง — version curated คืน​แค่ OrderView 5 field ที่ support agent ใช้​ตัดสิน​ใจ ผล​ของ tool สั้น​ลง เหลือ​งบ​ไว้​ให้ context ที่​สำคัญ​กว่า tool ตัว​เดิม สัญญา​เดิม — เปลี่ยน​แค่​รูปร่าง​ของ​สิ่ง​ที่​คืน​กลับ​มา

หัวใจ​ของ​วินัย​ทั้ง​คอร์ส​คือ วัด ไม่ใช่​เชื่อ ใช้​เครื่อง​วัด token ตัว​เดียว​กับ​บท​ที่ 4 — field usage — เดินทราน script เดิม 5 เทิร์น สอง​รอบ เปลี่ยน​แค่​ว่า​ผูก getOrder version ดิบ​หรือ version getOrderView:

// เทิร์นเดียวกัน เปลี่ยนแค่ tool ที่ผูก แล้วอ่าน usage
var options = new ChatOptions { Tools = [getOrderView, getDeliveryStatus, issueRefund] };
var reply = await chat.GetResponseAsync(history, options);
// input token ที่ model เห็นในเทิร์นนี้ (จากบทที่ 4)
Console.WriteLine($"input tokens: {reply.Usage?.InputTokenCount}");

รัน​เทียบ2 version แล้ว​คุณ​จะ​เห็น​สอง​อย่าง​พร้อม​กัน: (1) จำนวน input token ของ version curated ต่ำ​กว่า​อย่าง​เห็น​ได้ และ​ช่องว่าง​ยิ่ง​ถ่าง​เมื่อ​ประวัติ​สะสม​ผล​ของ tool มาก​ขึ้น เพราะ​ก้อน​ที่​เล็ก​กว่า​ถูก​ส่ง​ซ้ำ​ทุก​เทิร์น (2) agent ยัง ปิด​เคส​ได้​เหมือน​เดิม — ยืนยัน​สถานะ​ออเดอร์​และ​ตัดสิน​ใจ​คืน​เงิน​ได้​ครบ เพราะ field ที่​มัน​ใช้​จริง​ยัง​อยู่ นี่​คือ​รูปธรรม​ของ “ผลลัพธ์​เท่า​เดิม​ที่ token น้อย​ลง” ที่​ดาวเหนือ​เล็ง​ไว้

⚠️ usage ต้อง​ให้ connector เติม​ให้

reply.Usage เป็น UsageDetails และ field อย่าง InputTokenCount จะ​มี​ค่า​ก็​ต่อ​เมื่อ connector ฝั่ง Anthropic เติม​ค่า​ให้ (ข้อ​ควร​ระวัง​เดียว​กับ​ที่​กำกับ​ไว้​ใน​บท​ที่ 4) ถ้า​ได้ null ให้​ถอย​ไป​นับ​ด้วย POST /v1/messages/count_tokens หรือ field usage จาก Messages API ดิบตรงๆ อย่า​เดา token ของ Claude ด้วย tiktoken เพราะ​คนละ tokenizer นับ​ต่ำ​กว่า​จริง​ราว 15–20%

การ​จัด​ระเบียบ​ตอน​ที่ tool คืน​ผล เป็น​ด่าน​แรก แต่​ยัง​มี​ด่าน​ที่​สอง: เมื่อ​ผล​ก้อน​นั้น​ทำ​หน้าที่​เสร็จ​แล้ว มัน​ก็​ไม่​จำเป็น​ต้อง​ค้าง​ใน​ประวัติ​อีก ถ้า agent ดึง​ออเดอร์​มา​ดู ตัดสิน​ใจ​คืน​เงิน และ​ออก​คำ​สั่ง​คืน​เงิน​ไป​แล้ว ผล​ของ getOrder เทิร์นก่อนหน้า​ก็​หมด​หน้าที่ — เคลียร์​มัน​ออก​จาก​ประวัติ​ได้​เลย นี่​คือ “รูปแบบ​ที่​เบา​และ​ปลอดภัย​ที่สุด​ของ compaction” ที่​บท​ที่ 5 พูด​ถึง สอง​เทคนิค​นี้​เสริม​กัน: curation ทำให้​แต่ละ​ผล​ของ tool เล็ก​ตั้งแต่​ต้น ส่วน​การ​เคลียร์​ทำให้​ผล​ที่ หมด​อายุ หลุด​ออก​จาก​งบ

🔁 ต่อ​จาก​บท​ที่ 5

ดู​รายละเอียด​การ​เคลียร์​ผล​ของ tool และ compaction ที่ บท​ที่ 5 — Compaction สู้ context rot — curation กับ​การ​เคลียร์​ใน​บท​นั้น คือ​สอง​ด้าน​ของ​การ​ดูแล​ผล​ของ tool ให้​เบา​ตลอด lifecycle

ระวัง​อย่า​ตีความ​บท​นี้​ว่า “tool ยิ่ง​น้อย​ยิ่ง​ดี” หรือ “ต้อง​ยุบ tool ให้​เหลือ​ตัว​เดียว” ประเด็น​คือ รูปร่าง​ของ​ผล​ที่​คืน ไม่ใช่​จำนวน tool ใน​ทาง​กลับ​กัน anti-pattern ที่ Anthropic เตือน​คือ tool ที่ เยอะ​และ​ซ้ำซ้อน ต่างหาก — “One of the most common failure modes we see is bloated tool sets that cover too much functionality or lead to ambiguous decision points about which tool to use.”

ชุด getOrder / getDeliveryStatus / issueRefund จาก​คอร์ส #15 เป็น​ตัวอย่าง ที่​ดี ของ​ข้อ​นี้​อยู่​แล้ว — แต่ละ​ตัว​มีหน้าที่​เดียว​ชัดเจน ไม่​ทับซ้อน​กัน model ไม่​ต้อง​ลังเล​ว่า​จะ​เรียกตัว​ไหน สิ่ง​ที่​บท​นี้​เพิ่ม​เข้าไป​ไม่ใช่​การ​ลด​จำนวน tool แต่​คือ​การ​ทำให้ ผล​ที่​แต่ละ​ตัว​คืน ประหยัด token ขึ้น — ชุด​ที่​กระชับ​ดี​อยู่​แล้ว บวก​กับ​ผล​ที่​กระชับ​ขึ้น คือ​ทั้ง​สอง​ด้าน​ของ​ประโยค “return token efficient + encourage efficient behavior” ที่​เปิด​บท​นี้


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

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

  • Anthropic, “Effective context engineering for AI agents” (2025-09-29) — tool ใน​ฐานะ​คัน​โยก​ด้าน​ประสิทธิภาพ (คืน​ผล​ที่​ประหยัด token และ​ชักนำ​พฤติกรรม​ที่​ประหยัด), กติกา​ของ tool ที่​ดี (self-contained, robust to error, ชัดเจน​ต่อ​การ​ใช้งาน), anti-pattern ของ bloated tool sets และ​ดาวเหนือ “smallest set of high-signal tokens”
  • Microsoft Learn, “Microsoft.Extensions.AI libraries” (ms.date 2025-12-10, ปรับ 2026-06-18) — AIFunctionFactory.Create, AITool, ChatOptions.Tools และ ChatResponse.Usage / UsageDetails สำหรับ​วัด token

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

ข้อ 1 / 3

การจัดระเบียบผลของ tool (tool-result curation) หมายถึงอะไร?