Skip to content
Tayakorn

Harness Engineering Handbook

LEVEL 1 · เสาหลัก

Tool Design — ออกแบบ API ให้ผู้ใช้ที่เป็น AI

ทีมหนึ่งต่อ MCP server เข้า agent อย่างภูมิใจ — เครื่องมือ 60 ตัวพร้อมใช้ · ผลคือ agent กลับโง่ลง: เลือกเครื่องมือผิด สับสนระหว่าง get_user กับ fetch_user ที่ต่างกันนิดเดียว แถมจ่ายค่า context ให้ schema ที่แทบไม่เคยถูกเรียก · บทนี้ว่าด้วยศาสตร์ที่ฟังดูคุ้นแต่กติกาใหม่: ออกแบบ API ที่ "ผู้ใช้" เป็น AI

4.1 tool หนึ่งตัว = สัญญา 3 ส่วน

เครื่องมือหนึ่งตัวที่ harness ประกาศให้โมเดลเห็น ประกอบด้วย name (ชื่อ), description (คำอธิบาย), และ input schema (ฟอร์มที่ต้องกรอก) · จุดที่คนมองข้าม: description คือ prompt ที่ฝังถาวร — มันนั่งอยู่ใน context ทุกรอบ และโมเดลอ่านมันทุกครั้งที่ตัดสินใจว่าจะหยิบเครื่องมือไหน เขียน description ห่วย = เขียน prompt ห่วยที่ส่งซ้ำอัตโนมัติตลอดกาล

tool หนึ่งตัว = สัญญา 3 ส่วนname: search_customer_ordersชื่อบอก intent ของงาน ไม่ใช่ชื่อ endpointdescription ← prompt ที่ฝังถาวร"ค้นออร์เดอร์จากชื่อ/อีเมลลูกค้าใช้เมื่อถูกถามสถานะการสั่งซื้อ · คืน ≤20 แถว"ทำอะไร · ใช้เมื่อไหร่ · ขอบเขต — ครบในนี้input_schema: {customer*, status?, page?}ฟอร์มที่โมเดลกรอก — required/enum ต้องชัดโมเดลtool call (JSON)harnessตรวจ schema + สิทธิ์รันจริงAPI · ไฟล์ · เชลล์ผลลัพธ์กลับเข้า contextทุกตัวอักษรของผลลัพธ์ = ค่าเช่า contextที่จ่ายซ้ำทุกรอบที่เหลือ (บท 3)
FIG 4.1 สัญญา 3 ส่วนของ tool กับเส้นทางไป-กลับใน loop · โมเดลเขียนแค่คำขอ — harness เป็นคนตรวจและรันจริงเสมอ

4.2 ดี vs แย่ — คู่เดียวเห็นครบ

✗ tool ที่ออกแบบตาม API ดิบ

{ "name": "api_v2_query_exec",            ← ชื่อบอกแค่ "รันคิวรี" — งานอะไร? เมื่อไหร่ควรใช้?
  "description": "Executes query against the v2 endpoint",
  "input_schema": {
    "type": "object",
    "properties": {
      "q":    { "type": "string" },      ← q คืออะไร? SQL? ข้อความ? format ไหน?
      "opts": { "type": "string",
                "description": "JSON-encoded options" } } } }
                                         ← JSON ซ้อนใน string — จุดพังคลาสสิก

✓ tool ที่ออกแบบตาม workflow

{ "name": "search_customer_orders",
  "description": "ค้นออร์เดอร์ของลูกค้าจากชื่อหรืออีเมล
      ใช้เมื่อผู้ใช้ถามถึงสถานะหรือประวัติการสั่งซื้อ
      คืนไม่เกิน 20 รายการ (ใหม่→เก่า) — อยากได้หน้าถัดไปส่ง page เพิ่ม",
  "input_schema": {
    "type": "object",
    "properties": {
      "customer": { "type": "string", "description": "ชื่อหรืออีเมลลูกค้า" },
      "status":   { "type": "string", "enum": ["paid","shipped","refunded"] },
      "page":     { "type": "integer", "default": 1 }
    },
    "required": ["customer"] } }

4.3 หลักออกแบบสามข้อ

  • ออกแบบตาม workflow ไม่ใช่ตาม API ดิบ — นักพัฒนาที่เป็นคนอ่าน docs ได้ จำ state ข้ามหน้าจอได้ แต่โมเดลรู้เท่าที่ description ใน context บอก · เครื่องมือที่ดีคือ "หนึ่ง intent จบในตัว" (ค้นออร์เดอร์ของลูกค้าคนนี้) ไม่ใช่ "ประตูไปยัง endpoint" ที่ผู้ใช้ต้องประกอบเอง 3 จังหวะ
  • จำนวนพอดี ไม่ใช่ 100 ชิ้นจิ๋ว — ทุก schema กิน context (บท 3) และทุกตัวเลือกที่เพิ่มคือโอกาสเลือกผิดที่เพิ่ม โดยเฉพาะเครื่องมือที่ทับซ้อนกันเอง · เครื่องมือน้อยตัวแต่ทรงพลัง ชนะเครื่องมือเยอะแต่จิ๋วเกือบทุกครั้ง
  • ผลลัพธ์ต้องกระชับ — เพราะผลลัพธ์กลายเป็น context ทันที: คืนเฉพาะฟิลด์ที่ใช้ มี pagination ตัด log ให้เหลือท่อนที่เกี่ยว · tool ที่ dump ข้อมูลดิบ 5,000 บรรทัดคือ tool ที่สั่งให้ agent โง่ลงในอีก 10 รอบข้างหน้า

4.4 error message = prompt ขากลับ

เมื่อ tool พัง ข้อความ error จะถูกยัดกลับเข้า context และโมเดลจะอ่านมันแน่นอนเพื่อวางแผนรอบถัดไป — error จึงไม่ใช่ log แต่คือ prompt ที่เราส่งให้โมเดลตอนมันกำลังหลงทาง:

✗ Error: 400 Bad Request
  โมเดลไปต่อไม่ถูก → เดามั่ว หรือวนเรียกซ้ำท่าเดิม (บท 8)

✓ Error: ไม่พบลูกค้าชื่อ "สมชาย จ."
  ชื่ออาจสะกดไม่ตรงกับในระบบ — ลองอย่างใดอย่างหนึ่ง:
  · ค้นด้วยอีเมลแทน: search_customer_orders(customer="somchai@...")
  · หรือเรียก list_customers(query="สมชาย") หา id ที่ถูกก่อน
  บอกว่าผิดอะไร + แก้ยังไง → รอบถัดไปแก้เกมถูกทันที

4.5 MCP — มาตรฐานเปิดของการเสียบเครื่องมือ

เล่ม #07 แนะนำ MCP (Model Context Protocol) ไว้ว่าเป็น "ปลั๊ก USB" ที่ต่อ AI เข้ากับแอปและข้อมูลจริง · มุมของคนออกแบบ harness: MCP แยกคนสร้างเครื่องมือออกจากคนสร้าง harness — เขียน server ครั้งเดียว เสียบได้กับทุก harness ที่พูดโปรโตคอลนี้ ไม่ต้องผูกกับ vendor ไหน · แต่ระวัง: MCP ทำให้เสียบง่ายขึ้น ไม่ได้ทำให้ออกแบบดีขึ้น — server ที่ดัมพ์ API ดิบทั้งชุด 60 endpoints คือตัวอย่างคลาสสิกของสิ่งที่บทนี้เตือนทั้งบท

อัปเดตที่ตอกย้ำหลักการนี้: harness ยุคใหม่เริ่มไม่ยัด schema ของทุกเครื่องมือใน MCP server ทั้งชุดเข้า context ตั้งแต่ต้นอีกต่อไป — แต่ให้ agent ค้นหาก่อนว่ามีเครื่องมือไหนเกี่ยวกับงานตรงหน้า แล้วค่อยโหลด schema เฉพาะตัวที่จะใช้จริง · หลัก progressive disclosure (บทที่ 9) ไล่ตามมาถึงชั้น tools เองในที่สุด — ต่อ MCP server กี่สิบตัวก็ไม่ต้องแลกกับ context ที่บวมแบบที่เคยเป็นปัญหาคลาสสิกอีกต่อไป

สรุปบทที่ 4 — tool = สัญญา 3 ส่วน (name, description, schema) — description คือ prompt ที่ฝังถาวร · ออกแบบตาม workflow ไม่ใช่ API ดิบ · จำนวนพอดี — ทุก schema กิน context ทุกตัวเลือกเพิ่มโอกาสพลาด · ผลลัพธ์กระชับเพราะมันกลายเป็น context · error ที่ดี = prompt ขากลับที่สอนวิธีแก้ · MCP ทำให้เสียบง่าย แต่หลักออกแบบทั้งบทนี้ยังใช้เต็ม ๆ

Read the full book