Agent ที่คุณยังไม่รู้จัก: หลักการ สถาปัตยกรรม และแนวทางปฏิบัติทางวิศวกรรม

@HiTw93
จีน3 เดือนที่ผ่านมา · 19 มี.ค. 2569
1.7M
5.1K
1.2K
121
10.7K

TL;DR

คู่มือทางเทคนิคฉบับนี้สำรวจหลักการสำคัญของสถาปัตยกรรม AI Agent โดยเน้นที่การทำ Context Engineering การออกแบบเครื่องมือ ACI และกรอบการประเมินผลที่แข็งแกร่ง เพื่อการสร้างระบบอัตโนมัติที่มีเสถียรภาพ

0. สรุปโดยย่อ

หลังจากเขียนบทความ "The Claude Code ที่คุณไม่รู้: สถาปัตยกรรม การกำกับดูแล และแนวปฏิบัติทางวิศวกรรม" ผมก็ตระหนักว่าความเข้าใจของผมเกี่ยวกับเลเยอร์พื้นฐานของ Agents ยังไม่ลึกพอ เมื่อรวมกับประสบการณ์สำคัญของทีมเราในการส่งมอบโซลูชันทางธุรกิจที่ใช้ Agent ผมจึงรู้สึกว่าจำเป็นต้องมีการทบทวนอย่างเป็นระบบ ดังนั้น ผมจึงรวบรวมเอกสาร โค้ดโอเพนซอร์ส และโค้ดของตัวเองเพื่อจัดระเบียบบทความนี้

บทความนี้เน้นที่ส่วนต่างๆ ของสถาปัตยกรรม Agent ที่ส่งผลต่อผลลัพธ์ทางวิศวกรรมมากที่สุด รวมถึงการควบคุมโฟลว์ วิศวกรรมบริบท การออกแบบเครื่องมือ หน่วยความจำ การจัดระเบียบหลาย Agent การประเมินผล การติดตาม และความปลอดภัย สุดท้าย เราจะใช้การใช้งาน OpenClaw เพื่อเชื่อมโยงหลักการออกแบบเหล่านี้เข้าด้วยกัน

ในการจัดระเบียบนี้ มีข้อสรุปหลายข้อที่แตกต่างจากสมมติฐานเริ่มต้นของผม: การปรับปรุงที่ได้จากโมเดลที่มีราคาแพงกว่ามักจะน้อยกว่าที่คาดไว้ แต่คุณภาพของ Harness และการทดสอบตรวจสอบกลับมีผลกระทบต่ออัตราความสำเร็จมากกว่า เมื่อดีบักพฤติกรรมของ Agent ให้優先ตรวจสอบคำจำกัดความของเครื่องมือ เพราะข้อผิดพลาดในการเลือกเครื่องมือส่วนใหญ่เกิดจากคำอธิบายที่ไม่แม่นยำ นอกจากนี้ ปัญหาภายในระบบประเมินผลเองมักตรวจจับได้ยากกว่าบั๊กของ Agent หากคุณปรับแต่งโค้ด Agent ไปเรื่อยๆ แต่ไม่สำเร็จ คำตอบอาจอยู่ในพื้นที่เหล่านี้

1. การทำงานพื้นฐานของ Agent Loop

ตรรกะการใช้งานหลักของ Agent Loop เมื่อถูกทำให้เป็นนามธรรมแล้ว มีโค้ดน้อยกว่า 20 บรรทัด:

typescript
1const messages: MessageParam[] = [{ role: "user", content: userInput }];
2
3while (true) {
4 const response = await client.messages.create({
5 model: "claude-opus-4-6",
6 max_tokens: 8096,
7 tools: toolDefinitions,
8 messages,
9 });
10
11 if (response.stop_reason === "tool_use") {
12 const toolResults = await Promise.all(
13 response.content
14 .filter((b) => b.type === "tool_use")
15 .map(async (b) => ({
16 type: "tool_result" as const,
17 tool_use_id: b.id,
18 content: await executeTool(b.name, b.input),
19 }))
20 );
21 messages.push({ role: "assistant", content: response.content });
22 messages.push({ role: "user", content: toolResults });
23 } else {
24 return response.content.find((b) => b.type === "text")?.text ?? "";
25 }
26}

โฟลว์การควบคุมที่สอดคล้องกันมีดังนี้: วงจรต่อเนื่องของการรับรู้ -> การตัดสินใจ -> การกระทำ -> ข้อเสนอแนะ จนกว่าโมเดลจะส่งคืนข้อความธรรมดา:

Tw93 - inline image

หลังจากเห็นการใช้งาน Agent และ SDK อย่างเป็นทางการหลายตัว โครงสร้างก็คล้ายกัน ตัวลูปเองค่อนข้างเสถียร จากการใช้งานขั้นต่ำไปจนถึงการรองรับ sub-agent การบีบอัดบริบท และการโหลดทักษะ ลูปหลักแทบไม่เปลี่ยนแปลง ความสามารถใหม่ๆ มักจะถูกเพิ่มเป็นเลเยอร์ภายนอกลูป แทนที่จะแก้ไขภายใน

ความสามารถใหม่ๆ ถูกรวมเข้าด้วยกันในสามวิธีหลัก: การขยายชุดเครื่องมือและตัวจัดการ การปรับโครงสร้าง system prompt และการทำให้สถานะเป็นภายนอกไปยังไฟล์หรือฐานข้อมูล ตัวลูปไม่ควรกลายเป็น state machine ขนาดใหญ่ โมเดลจัดการกับการใช้เหตุผล ในขณะที่ระบบภายนอกจัดการกับสถานะและขอบเขต เมื่อการแบ่งงานนี้ถูกกำหนดไว้แล้ว ตรรกะของลูปหลักก็แทบไม่จำเป็นต้องปรับเปลี่ยนบ่อยๆ

Workflow กับ Agent ต่างกันอย่างไร?

Anthropic แยกความแตกต่างโดยตรง: ระบบที่เส้นทางการดำเนินการถูกเขียนไว้ล่วงหน้าในโค้ดคือ Workflow ระบบที่ LLM ตัดสินใจขั้นตอนถัดไปแบบไดนามิกคือ Agent ความแตกต่างหลักคือใครเป็นผู้ควบคุม ในความเป็นจริง ผลิตภัณฑ์หลายอย่างที่ถูกติดป้ายว่า Agent นั้นใกล้เคียงกับ Workflow มากกว่า แต่ไม่มีสิ่งใดดีกว่าโดยเนื้อแท้ สิ่งสำคัญคือการหาโซลูชันที่เหมาะสมกับงาน

Tw93 - inline image

เมื่อดูจากแผนภาพเดียวก็จะเข้าใจได้ง่ายขึ้น:

Tw93 - inline image

รูปแบบการควบคุมทั่วไปห้ารูปแบบ

ระบบ AI ส่วนใหญ่ เมื่อแยกส่วนประกอบออกมาแล้ว จะเป็นการรวมกันของรูปแบบทั้งห้านี้ หลายสถานการณ์ไม่จำเป็นต้องใช้ความเป็นอิสระเต็มรูปแบบของ Agent การรวมรูปแบบเหล่านี้สองสามรูปแบบก็เพียงพอแล้ว สิ่งสำคัญคือการออกแบบแบบใดที่เหมาะกับงาน

  1. Prompt Chaining: งานถูกแบ่งออกเป็นขั้นตอนตามลำดับ โดยแต่ละขั้นตอนของ LLM จะประมวลผลผลลัพธ์ก่อนหน้า สามารถเพิ่มจุดตรวจสอบโค้ดได้ เหมาะสำหรับกระบวนการเชิงเส้น เช่น การแปลหลังจากสร้างเนื้อหา หรือการเขียนเนื้อหาหลังจากโครงร่าง
  2. Routing: จัดประเภทอินพุตและนำไปยังกระบวนการเฉพาะ คำถามง่ายๆ ไปยังโมเดลน้ำหนักเบา คำถามซับซ้อนไปยังโมเดลที่แข็งแกร่ง คำถามด้านเทคนิคและการเรียกเก็บเงินมีตรรกะที่แตกต่างกัน
  3. Parallelization: สองรูปแบบ: การแบ่งส่วน (แบ่งงานเป็นงานย่อยอิสระ) และการลงคะแนน (รันงานเดียวกันหลายครั้งเพื่อหาฉันทามติ) เหมาะสำหรับการตัดสินใจที่มีความเสี่ยงสูงหรือความต้องการหลายมุมมอง
  4. Orchestrator-Workers: LLM กลางแยกงานแบบไดนามิกและมอบหมายให้ LLM ผู้ปฏิบัติงาน จากนั้นสังเคราะห์ผลลัพธ์ นี่คือต้นแบบของ spawn tool ของ nanobot และโหมด sub-agent ของ learn-claude-code
  5. Evaluator-Optimizer: ตัวสร้างสร้างผลลัพธ์ และตัวประเมินให้ข้อเสนอแนะในลูปจนกว่าจะได้มาตรฐาน เหมาะสำหรับงานเช่นการแปลหรืองานเขียนเชิงสร้างสรรค์ที่มาตรฐานคุณภาพยากที่จะกำหนดอย่างแม่นยำในโค้ด
Tw93 - inline image

รูปแบบเหล่านี้แก้ปัญหาวิธีสร้างโฟลว์การควบคุม ตอนนี้เรามาดูคำถามที่เน้นวิศวกรรมมากขึ้น: ทำไมระบบถึงทำงานได้อย่างเสถียร?

2. ทำไม Harness ถึงสำคัญกว่าโมเดล

Harness หมายถึงโครงสร้างพื้นฐานการทดสอบ การตรวจสอบ และข้อจำกัดที่สร้างขึ้นรอบๆ Agent Harness ประกอบด้วยอย่างน้อยสี่ส่วน: เกณฑ์พื้นฐานการยอมรับ ขอบเขตการดำเนินการ สัญญาณข้อเสนอแนะ และวิธีการสำรอง

ในขณะที่โมเดลมีความสำคัญ เงื่อนไขทางวิศวกรรมรอบข้างเหล่านี้มักจะกำหนดว่าระบบทำงานได้อย่างเสถียรหรือไม่ การตัดสินนี้เป็นจริงมากที่สุดสำหรับงานที่ตรวจสอบได้สูง เช่น การเขียนโค้ด แต่ในงานที่ตรวจสอบได้น้อย เช่น การวิจัยแบบเปิดหรือการเจรจาหลายรอบ ขีดจำกัดบนของโมเดลยังคงมีความสำคัญมากกว่า

แนวปฏิบัติการพัฒนาแบบ Agent-First ของ OpenAI

วิศวกรสามคนเขียนโค้ดหนึ่งล้านบรรทัดในห้าเดือนด้วย PR เกือบ 1,500 รายการ ซึ่งเร็วกว่าการพัฒนาแบบดั้งเดิมถึง 10 เท่า ความเร็วนี้ไม่ได้มาจากความแข็งแกร่งของโมเดลเพียงอย่างเดียว แต่มาจากการตัดสินใจทางวิศวกรรมที่ถูกต้อง:

  1. เนื้อหาที่ Agent มองไม่เห็น = ไม่มีอยู่: ความรู้ต้องอยู่ใน codebase เอง เอกสารภายนอกจะมองไม่เห็นสำหรับ Agent ที่กำลังทำงาน AGENTS.md ถูกเก็บไว้ที่ประมาณ 100 บรรทัดเพื่อเป็นดัชนี โดยรายละเอียดจะถูกแยกออกเป็นไดเรกทอรี docs เพื่อการอ้างอิงตามความต้องการ
  2. บังคับใช้ด้วยโค้ด อย่าเขียนเป็นเอกสาร: บรรทัดฐานในเอกสารมักถูกมองข้าม ข้อจำกัดที่ถูกเข้ารหัสใน Linters, ระบบ type หรือกฎ CI สามารถดำเนินการได้ การแบ่งชั้นสถาปัตยกรรมถูกบังคับใช้โดยกลไกด้วย Linters ที่กำหนดเอง ไม่ใช่การตรวจสอบด้วยตนเอง
  3. การทำงานให้เสร็จสมบูรณ์แบบ end-to-end โดยอัตโนมัติ: ตั้งแต่การตรวจสอบสถานะและการทำซ้ำบั๊ก ไปจนถึงการแก้ไขและขับเคลื่อนการตรวจสอบแอป การเปิด PR การจัดการรีวิว และการรวมโค้ด — ทั้งหมดนี้ไม่ต้องการการแทรกแซงของมนุษย์ Agent จะตรวจสอบ logs, metrics และ traces อย่างแข็งขัน
  4. ลดแรงเสียดทานในการรวมโค้ด: จัดการกับความล้มเหลวของการทดสอบเป็นระยะด้วยการลองใหม่ แทนที่จะขัดขวางความคืบหน้า ในสภาพแวดล้อมที่มีปริมาณงานสูง ต้นทุนของการรอการตรวจสอบด้วยตนเองมักจะสูงกว่าการแก้ไขข้อผิดพลาดเล็กน้อย วินัยในการเขียนโค้ดไม่ได้หายไป แต่เปลี่ยนจากการตรวจสอบด้วยตนเองไปเป็นข้อจำกัดที่ดำเนินการโดยเครื่อง
Tw93 - inline image

แอปกระจาย logs, metrics และ traces ผ่าน Vector ไปยังเลเยอร์จัดเก็บ Victoria ซึ่งสอดคล้องกับอินเทอร์เฟซ LogQL, PromQL และ TraceQL Codex ค้นหา เชื่อมโยง และใช้เหตุผลผ่านอินเทอร์เฟซเหล่านี้ หลังจากเปลี่ยนแปลง มันจะรีสตาร์ทแอป รัน workloads อีกครั้ง และป้อนผลลัพธ์กลับไปยัง Codex UI Journeys ก็เป็นอินพุตเช่นกัน stack การสังเกตการณ์นี้ถูกสร้างขึ้นต่อหนึ่งงานและถูกทำลายเมื่อเสร็จสิ้น Agent ไม่ต้องรอให้บอกว่ามีข้อผิดพลาด มันสอบถามสถานะระบบเพื่อตรวจสอบการแก้ไข

ข้อสรุปสำคัญสำหรับ Harness คืออะไร?

Tw93 - inline image

แผนภาพใช้ความชัดเจนของงานและการตรวจสอบอัตโนมัติเพื่อแบ่งงานออกเป็นสี่สถานะ มุมขวาบน (เป้าหมายชัดเจน, การตรวจสอบอัตโนมัติ) คือโซนที่เหมาะสำหรับ Agent มุมซ้ายบน (งานชัดเจนแต่ต้องตรวจสอบด้วยตนเอง) ถูกจำกัดด้วยความเร็วของการตรวจสอบโดยมนุษย์ มุมขวาล่าง (ข้อเสนอแนะอัตโนมัติแต่เป้าหมายคลุมเครือ) นำไปสู่การเคลื่อนที่อย่างมีประสิทธิภาพในทิศทางที่ผิด มุมซ้ายล่าง (ขาดทั้งสองอย่าง) ทำให้ Agent ไร้ประโยชน์

หน้าที่ของ Harness คือการผลักดันงานไปยังมุมขวาบน เพื่อให้แน่ใจว่าถูกหรือผิดถูกตัดสินโดยมาตรฐานที่เครื่องดำเนินการได้ ไม่ใช่ด้วยสายตามนุษย์

3. ทำไมวิศวกรรมบริบทถึงกำหนดความเสถียร

ความซับซ้อนของความสนใจของ Transformer คือ O(n²) ยิ่งบริทยาวนาน สัญญาณสำคัญก็ยิ่งถูกเจือจางด้วยสัญญาณรบกวนได้ง่ายขึ้น โหมดความล้มเหลวทั่วไปคือ "Context Rot" ซึ่งเนื้อหาที่ไม่เกี่ยวข้องครอบงำบริบท ทำให้คุณภาพการตัดสินใจของ Agent ลดลง ปัญหามากมายที่ดูเหมือนเป็นความสามารถที่ไม่เพียงพอของโมเดล จริงๆ แล้วเกิดจากการจัดระเบียบบริบทที่ไม่ดี

ทำไมต้องจัดบริบทเป็นชั้นๆ?

ปัญหามักจะไม่ใช่หน้าต่างสั้นเกินไป แต่เป็นความหนาแน่นของข้อมูลที่ไม่ถูกต้อง การโหลดรายการที่ไม่ค่อยได้ใช้ทุกครั้ง หรือการผสมกฎที่เสถียรกับสถานะแบบไดนามิก ทำให้โมเดลสังเกตเห็นสิ่งที่มีประโยชน์ได้ยากขึ้น

Tw93 - inline image

วิธีแก้คือการจัดข้อมูลเป็นชั้นๆ ตามความถี่และความเสถียร:

  • เลเยอร์ถาวร: เอกลักษณ์ ข้อตกลงของโครงการ ข้อห้ามเด็ดขาด เนื้อหาที่ต้องคงอยู่ทุกเซสชัน เก็บให้สั้น แข็ง และดำเนินการได้
  • การโหลดตามความต้องการ: ทักษะและความรู้เฉพาะด้าน เก็บคำอธิบายให้ถาวร แต่แทรกเนื้อหาเต็มเมื่อถูกเรียกใช้เท่านั้น
  • การแทรกขณะรันไทม์: ข้อมูลไดนามิก เช่น เวลาปัจจุบัน ID ช่อง ความชอบของผู้ใช้ แทรกต่อรอบตามความจำเป็น
  • เลเยอร์หน่วยความจำ: ประสบการณ์ข้ามเซสชันที่เขียนไปยัง MEMORY.md ไม่อยู่ใน system prompt โดยตรง อ่านเมื่อจำเป็นเท่านั้น
  • เลเยอร์ระบบ: Hooks หรือกฎโค้ดสำหรับตรรกะที่กำหนดได้ อยู่นอกบริบทโดยสิ้นเชิง

อย่าใส่ตรรกะที่กำหนดได้ไว้ในบริบท สิ่งใดก็ตามที่สามารถแสดงผ่าน Hooks, กฎโค้ด หรือข้อจำกัดของเครื่องมือ ควรจัดการโดยระบบภายนอก

กลยุทธ์การบีบอัดสามแบบทั่วไป

  1. Sliding Window: ทิ้งข้อความเก่า ต้นทุนต่ำ แต่สูญเสียบริบทเริ่มต้น เหมาะสำหรับการแชทสั้นๆ
  2. LLM Summary: โมเดลสร้างสรุป ต้นทุนปานกลาง สูญเสียรายละเอียดแต่คงการตัดสินใจ เหมาะสำหรับงานยาว
  3. Tool Result Replacement: แทนที่ผลลัพธ์ดิบด้วยตัวยึดตำแหน่ง ต้นทุนต่ำ เหมาะสำหรับงานที่ใช้เครื่องมือมาก

Sliding windows ง่ายที่สุดแต่สูญเสียภูมิหลังเริ่มต้น LLM Summaries ขั้นสูงใช้ "branch summarization" โดยคงการตัดสินใจทางสถาปัตยกรรม งานที่ยังไม่เสร็จ และข้อจำกัดสำคัญไว้อย่างชัดเจน ในการแทนที่เครื่องมือ micro_compact จะแทนที่ผลลัพธ์เครื่องมือเก่าทุกรอบ ในขณะที่ auto_compact จะทำงานเมื่อบริบทเกินเกณฑ์

Prompt Caching เพื่อลดค่าใช้จ่ายที่ซ้ำซ้อน

การอนุมาน LLM คำนวณคู่ Key-Value สำหรับแต่ละ token หากคำนำหน้าตรงกับคำขอก่อนหน้าทุกประการ ก็จะถูกอ่านจากแคช การแคชต้องการการจับคู่คำนำหน้าที่แน่นอน การออกแบบที่เป็นมิตรกับแคชเน้นที่ความเสถียร: system prompts, คำจำกัดความของเครื่องมือ และเอกสารยาวมีความเสถียรและเหมาะสำหรับการแคช ข้อมูลไดนามิก (เวลา อินพุต ผลลัพธ์เครื่องมือ) ควรวางไว้ที่ส่วนท้าย

สิ่งนี้เกี่ยวข้องกับการจัดบริบทเป็นชั้นๆ ยิ่งเลเยอร์ถาวรมีความเสถียรมากเท่าไร อัตราการชนะแคชก็จะสูงขึ้นและต้นทุนส่วนเพิ่มก็จะต่ำลง "สั้นและเสถียร" ไม่ใช่แค่เพื่อประหยัด tokens แต่เพื่อปกป้องแคช การโหลดทักษะที่ล่าช้าก็ช่วยได้โดยการต่อท้ายเนื้อหาหลังจากคำนำหน้าที่เสถียร จุดที่ขัดกับสัญชาตญาณ: system prompt ขนาดใหญ่ที่เสถียรอาจถูกกว่าขนาดเล็กที่เปลี่ยนแปลงบ่อย เนื่องจากส่วนลด 90% สำหรับการอ่านครั้งต่อๆ ไปมีมากกว่าต้นทุนการเขียนครั้งแรก

ทำไมต้องโหลดทักษะตามความต้องการ?

ทักษะเป็นรูปแบบที่มีประสิทธิภาพ: เก็บเฉพาะดัชนีใน system prompt โหลดความรู้เต็มเมื่อจำเป็น

typescript
1const systemPrompt = `
2ทักษะที่มี:
3- deploy: กระบวนการปรับใช้การผลิตเต็มรูปแบบ
4- code-review: รายการตรวจสอบโค้ด
5- git-workflow: กลยุทธ์สาขาและบรรทัดฐาน PR
6`;
7
8async function executeLoadSkill(name: string): Promise<string> {
9 return fs.readFile(`./skills/${name}.md`, "utf-8");
10}

คำอธิบายทักษะต้องสั้นเพื่อหลีกเลี่ยง token มากเกินไป และควรทำหน้าที่เป็นเงื่อนไขการกำหนดเส้นทาง อธิบายว่าเมื่อใดควรใช้ เมื่อใดไม่ควรใช้ และผลลัพธ์คืออะไร ใช้ "ใช้เมื่อ / อย่าใช้เมื่อ" พร้อมตัวอย่างเชิงลบ ความล้มเหลวในการกำหนดเส้นทางส่วนใหญ่เกิดจากขอบเขตที่ไม่ชัดเจน ไม่ใช่ความสามารถของโมเดล system prompt ควรชี้แจงกฎ: สแกน available_skills ก่อนตอบแต่ละครั้ง โหลด SKILL.md เฉพาะเมื่อตรงกัน และโหลดครั้งละหนึ่งทักษะเท่านั้น

Tw93 - inline image

ข้อมูลชัดเจน: หากไม่มีตัวอย่างเชิงลบ ความแม่นยำลดลงจาก 73% เป็น 53% การเพิ่มตัวอย่างเชิงลบทำให้เพิ่มขึ้นเป็น 85% และลดเวลาตอบสนองลง 18.1% ตัวอย่างเชิงลบคือกุญแจสำคัญ

ตัวอธิบายทักษะมีกับดักสองประการ ประการแรก จำนวนคำ: คำอธิบายยาวสำหรับทุกทักษะจะเพิ่มขึ้นเรื่อยๆ ประการที่สอง ความแม่นยำ: "ช่วยเหลือเกี่ยวกับแบ็กเอนด์" นั้นคลุมเครือเกินไป ตัวอธิบายที่มีประสิทธิภาพคือเงื่อนไขการกำหนดเส้นทาง ไม่ใช่การแนะนำฟีเจอร์ "เมื่อใดควรใช้ฉัน" สำคัญกว่า "ฉันทำอะไรได้บ้าง"

ควบคุมปริมาณ: เก็บเฉพาะทักษะที่ใช้บ่อยใน prompt ถาวร ทักษะที่ใช้น้อยสามารถแนะนำด้วยตนเองหรือเก็บเป็นเอกสาร รูปแบบที่ไม่ดีทั่วไป ได้แก่ การยัดคู่มือหลายร้อยบรรทัดลงในทักษะเดียว หรือให้ทักษะเดียวครอบคลุมงานที่แตกต่างกันมากเกินไป (รีวิว ปรับใช้ ดีบัก)

อะไรที่การบีบอัดมักจะสูญเสียได้ง่ายที่สุด?

ปัญหาที่พบบ่อยที่สุดไม่ใช่สรุปยาวเกินไป แต่เป็นลำดับความสำคัญในการเก็บรักษาที่ผิด LLMs มักจะลบข้อมูลที่ดูเหมือนจะหาได้อีกครั้ง ผลลัพธ์เครื่องมือจะถูกลบก่อน แต่การตัดสินใจทางสถาปัตยกรรมและเส้นทางความล้มเหลวที่เกี่ยวข้องมักจะหายไปด้วย กำหนดลำดับความสำคัญในการเก็บรักษาอย่างชัดเจนใน CLAUDE.md:

markdown
1### คำแนะนำการบีบอัด: วิธีเก็บข้อมูลสำคัญ
2ลำดับความสำคัญ:
31. การตัดสินใจทางสถาปัตยกรรม (ห้ามสรุป)
42. ไฟล์ที่ถูกแก้ไขและการเปลี่ยนแปลงสำคัญ
53. สถานะการตรวจสอบ (ผ่าน/ไม่ผ่าน)
64. TODOs ที่ยังไม่แก้ไขและบันทึกการย้อนกลับ
75. ผลลัพธ์เครื่องมือ (สามารถลบได้ เก็บเฉพาะข้อสรุปผ่าน/ไม่ผ่าน)

กับดักอีกอย่าง: อย่าเปลี่ยนตัวระบุ UUID, hashes, IP และชื่อไฟล์ต้องคงไว้ตามเดิม อักขระผิดหนึ่งตัวใน commit hash จะทำให้การเรียกเครื่องมือในภายหลังเสียหาย

ทำไมระบบไฟล์ถึงเป็นอินเทอร์เฟซบริบทที่ยอดเยี่ยม

Cursor เรียกสิ่งนี้ว่า "Dynamic Context Discovery": ให้ข้อมูลน้อยลงโดยค่าเริ่มต้น อ่านเมื่อจำเป็น ระบบไฟล์เป็นอินเทอร์เฟซตามธรรมชาติ การเรียกเครื่องมือมักจะส่งคืน JSON ขนาดใหญ่ แทนที่จะยัดเยียดเข้าไปในบริบท ให้เขียนลงไฟล์ Agent สามารถใช้ grep หรือ rg เพื่ออ่านตามต้องการ สิ่งนี้ทำให้บริบทสะอาดและนักพัฒนาสามารถอ่านได้

Cursor ยืนยันสิ่งนี้ด้วยเครื่องมือ MCP: พวกเขาซิงค์คำอธิบายเครื่องมือไปยังโฟลเดอร์ Agent จะเห็นเฉพาะชื่อเครื่องมือโดยค่าเริ่มต้นและสอบถามคำจำกัดความเมื่อจำเป็น ในการทดสอบ A/B สิ่งนี้ลดการใช้ token ทั้งหมดลง 46.9%

สิ่งนี้ยังใช้ได้กับการบีบอัดงานยาว แทนที่จะทิ้งประวัติ ให้บันทึกบันทึกการแชททั้งหมดลงไฟล์และอ้างอิงพาธในสรุป หาก Agent ต้องการรายละเอียด ก็สามารถดึงข้อมูลจากไฟล์ ทำให้การบีบอัดเป็นการดำเนินการที่สูญเสียแต่สามารถติดตามได้

4. การออกแบบเครื่องมือกำหนดสิ่งที่ Agent สามารถทำได้

บริบทกำหนดสิ่งที่โมเดลเห็น เครื่องมือกำหนดสิ่งที่มันสามารถทำได้ คุณภาพสำคัญกว่าปริมาณ เซิร์ฟเวอร์ MCP เพียง 5 ตัวอาจมีค่าใช้จ่ายประมาณ 55,000 tokens ในคำจำกัดความ ซึ่งเกือบ 30% ของบริบท 200K ก่อนที่การแชทจะเริ่มต้นด้วยซ้ำ เครื่องมือมากเกินไปทำให้ความสนใจของโมเดลเจือจาง

ปัญหาเครื่องมือส่วนใหญ่ไม่ใช่การมีน้อยเกินไป แต่เป็นการเลือกผิด คำอธิบายที่ไม่เข้าใจ หรือการส่งคืนข้อมูลที่ไร้ประโยชน์

Tw93 - inline image

การออกแบบเครื่องมือวิวัฒนาการอย่างไร

การออกแบบเครื่องมือผ่านสามขั้นตอน ในช่วงแรก API ที่มีอยู่ถูกห่อหุ้มเป็นเครื่องมือ ต่อมาพบว่าข้อผิดพลาดในการเลือกมักเกิดจากเครื่องมือที่ออกแบบมาสำหรับวิศวกร ไม่ใช่สำหรับ Agent

รุ่นที่ 1: การห่อหุ้ม API: แต่ละ endpoint เป็นเครื่องมือหนึ่งอัน ละเอียดเกินไป Agent ต้องประสานงานหลายเครื่องมือเพื่อเป้าหมายเดียว

รุ่นที่ 2: ACI (Agent-Computer Interface): เครื่องมือสอดคล้องกับเป้าหมายของ Agent ไม่ใช่ API ระดับต่ำ แทนที่จะเป็น create_file และ set_permissions ให้ใช้ create_script(path, content, executable)

รุ่นที่ 3: การใช้เครื่องมือขั้นสูง: การปรับปรุงการค้นพบและการเรียก:

  • Tool Search: อย่ายัดเยียดคำจำกัดความทั้งหมดในครั้งเดียว Agent ค้นหาคำจำกัดความผ่าน search_tools การคงบริบทถึง 95%
  • Programmatic Tool Calling: ให้โมเดลใช้โค้ดเพื่อจัดระเบียบการเรียกหลายครั้ง ผลลัพธ์ระหว่างกลางอยู่ในสภาพแวดล้อมการดำเนินการ ไม่ใช่บริบท LLM tokens สามารถลดลงจาก 150,000 เหลือ 2,000
  • Tool Use Examples: แต่ละเครื่องมือได้รับตัวอย่างจริง 1-5 ตัวอย่าง JSON Schema อธิบายประเภท แต่ตัวอย่างแสดงการใช้งาน ความแม่นยำสามารถเพิ่มขึ้นจาก 72% เป็น 90%

หลักการออกแบบเครื่องมือ ACI

การออกแบบเครื่องมือส่งผลต่อ Agent โดยตรง ไม่ใช่แค่ "เรียกได้ไหม" แต่ "สามารถแก้ไขตัวเองได้ไหมหากเรียกผิด?"

การออกแบบที่ไม่ดีมีพารามิเตอร์คลุมเครือและข้อผิดพลาดที่ไม่สามารถแก้ไขได้ การออกแบบที่ดีใช้ betaZodTool เพื่อผูกคำจำกัดความและการดำเนินการ โดยใช้ Zod สำหรับข้อจำกัดรูปแบบและคำแนะนำข้อผิดพลาดที่มีโครงสร้าง:

typescript
1const updateTool = betaZodTool({
2 name: "update_yuque_post",
3 description: "อัปเดตเนื้อหาโพสต์ Yuque ไม่ใช่สำหรับสร้างโพสต์ใหม่",
4 inputSchema: z.object({
5 post_id: z.string().describe("ID โพสต์ Yuque, สตริงตัวเลขเช่น '12345678'"),
6 title: z.string().optional().describe("ชื่อโพสต์, ละเว้นหากไม่เปลี่ยนแปลง"),
7 content_markdown: z.string().describe("เนื้อหา Markdown"),
8 }),
9 run: async (input) => {
10 const post = await getPost(input.post_id);
11 if (!post) throw new ToolError("ไม่มี ID โพสต์นี้", {
12 error_code: "POST_NOT_FOUND",
13 suggestion: "เรียก list_yuque_posts ก่อนเพื่อรับ post_id ที่ถูกต้อง",
14 });
15 return await updatePost(input.post_id, input.title, input.content_markdown);
16 },
17});
Tw93 - inline image

การออกแบบที่ไม่ดีบอกเพียงว่ามันทำอะไร ไม่ใช่เมื่อใดควรใช้ การออกแบบ ACI ที่ดีมีขอบเขตชัดเจนและข้อผิดพลาดที่มีโครงสร้าง ช่วยให้ Agent เลือกได้ถูกต้องและแก้ไขความล้มเหลวได้อย่างรวดเร็ว ดีบักเครื่องมือก่อน ข้อผิดพลาดส่วนใหญ่อยู่ในคำอธิบาย ไม่ใช่ความสามารถของโมเดล

ทำไมต้องแยกข้อความเครื่องมือ?

เฟรมเวิร์กสร้างเหตุการณ์ภายใน (การบีบอัด การแจ้งเตือน) สิ่งเหล่านี้ควรอยู่ในประวัติเซสชัน แต่ไม่ควรส่งไปยัง LLM เพราะเป็นการสิ้นเปลือง tokens และทำให้โมเดลสับสน วิธีแก้คือข้อความสองประเภท: AgentMessage สำหรับเลเยอร์แอป (พร้อมฟิลด์ที่กำหนดเอง) และ Message มาตรฐาน (user, assistant, tool_result) สำหรับ LLM

5. การออกแบบระบบหน่วยความจำ

Agent ขาดความต่อเนื่องทางเวลาตามธรรมชาติ บริบทจะถูกล้างหลังจากเซสชัน เพื่อให้เกิดความสอดคล้องข้ามเซสชัน ต้องออกแบบเลเยอร์หน่วยความจำให้เป็นโครงสร้างพื้นฐาน ไม่ใช่สิ่งที่คิดทีหลัง

หน่วยความจำสี่ประเภทอยู่ที่ไหน?

จำแนกตามปัญหาที่แก้ไข:

  • Context Window (หน่วยความจำทำงาน): ข้อมูลขั้นต่ำสำหรับงานปัจจุบัน tokens จำกัด ต้องจัดการ
  • ทักษะ (หน่วยความจำขั้นตอน): วิธีทำสิ่งต่างๆ (เวิร์กโฟลว์ บรรทัดฐาน) โหลดตามความต้องการ
  • ประวัติเซสชัน JSONL (หน่วยความจำเหตุการณ์): สิ่งที่เกิดขึ้น เก็บไว้ในดิสก์ ค้นหาได้
  • MEMORY.md (หน่วยความจำความหมาย): ข้อเท็จจริงที่เสถียรซึ่งเขียนโดย Agent แทรกใน system prompts
Tw93 - inline image

MEMORY.md และทักษะทำงานร่วมกันอย่างไร

แกนหลักคือการเก็บข้อเท็จจริงสำคัญในขณะที่ควบคุมปริมาณเนื้อหา

หน่วยความจำ 4 ชั้นของ ChatGPT: โครงสร้างเรียบง่าย เมตาดาตาเซสชัน (ไม่ถูกเก็บ), หน่วยความจำผู้ใช้ (~33 ข้อเท็จจริง, ถูกเก็บ/แทรก), สรุปการสนทนา (~15 สรุปล่าสุด, ถูกเก็บ), เซสชันปัจจุบัน (sliding window)

การดึงข้อมูลแบบผสมของ OpenClaw: บันทึกรายวัน (memory/YYYY-MM-DD.md), MEMORY.md สำหรับข้อเท็จจริงที่คัดสรร, และ memory_search โดยใช้การดึงข้อมูลแบบผสม (ความคล้ายคลึงเวกเตอร์ 70% + คำสำคัญ 30%) สำหรับ Agent ส่วนใหญ่ Markdown ที่มีโครงสร้าง + การค้นหาคำสำคัญก็เพียงพอสำหรับการดีบักและต้นทุน

การกระตุ้นและการย้อนกลับการรวมหน่วยความจำ

Tw93 - inline image

เมื่อ tokenUsage / maxTokens >= 0.5 ให้กระตุ้นการรวม สรุปข้อความ ต่อท้าย MEMORY.md และอัปเดตดัชนี หากล้มเหลว ให้เขียนข้อความดิบไปยังที่เก็บถาวร กระบวนการต้องสามารถย้อนกลับได้ ย้ายพอยน์เตอร์ อย่าลบข้อมูลดิบ

6. การเพิ่มความเป็นอิสระของ Agent อย่างค่อยเป็นค่อยไป

ความเป็นอิสระต้องการโครงสร้างพื้นฐานสามอย่าง: การดำเนินการต่อข้ามเซสชัน ข้อจำกัดความคืบหน้าภายในเซสชัน และ I/O พื้นหลังสำหรับงานที่ช้า

วิธีดำเนินการงานยาวต่อข้ามเซสชัน

งานยาวล้มเหลวเมื่อเซสชันสิ้นสุดก่อนเสร็จสมบูรณ์ วิธีการที่เสถียรใช้ Initializer Agent และ Coding Agent Initializer รันครั้งเดียวเพื่อสร้าง feature-list.json, init.sh และ claude-progress.txt จากนั้น Coding Agent รันในหลายเซสชัน ดำเนินการต่อจากไฟล์เหล่านี้ ใช้ฟีเจอร์หนึ่ง รันการทดสอบ และอัปเดตไฟล์ความคืบหน้า สิ่งนี้ทำให้งานเป็นสถานะภายนอก

Tw93 - inline image

เก็บความคืบหน้าในไฟล์ ไม่ใช่บริบท ใช้ JSON สำหรับโครงสร้าง งานจะเสร็จก็ต่อเมื่อฟีเจอร์ทั้งหมดใน feature-list.json มี passes: true

ทำไมต้องเขียนสถานะงานอย่างชัดเจน?

หากไม่มีจุดยึดภายนอก Agent จะล่องลอยหรือเสร็จก่อนกำหนด บันทึกสถานะเป็นวัตถุควบคุมภายนอก:

json
1{
2 "tasks": [
3 {"id": "1", "desc": "อ่านการกำหนดค่า", "status": "completed"},
4 {"id": "2", "desc": "แก้ไข schema", "status": "in_progress"}
5 ]
6}

ข้อจำกัด: มี in_progress ได้ครั้งละหนึ่งรายการเท่านั้น อัปเดตสถานะหลังจากทุกขั้นตอน

การรวม I/O พื้นหลัง

I/O ที่ช้า (การดำเนินการไฟล์, เครือข่าย) ไม่ควรบล็อกลูปหลัก วางซับโพรเซสที่ช้าในเธรดพื้นหลังและแทรกผลลัพธ์ผ่านคิวการแจ้งเตือนก่อนการเรียก LLM ครั้งถัดไป วิธีนี้บำรุงรักษาได้ดีกว่ารันไทม์ async ที่ซับซ้อน

7. การจัดระบบหลาย Agent

วิศวกรรมระบบหลาย Agent เกี่ยวข้องกับการแยกและการทำงานร่วมกัน

โหมด Director: ซิงโครนัส มนุษย์โต้ตอบอย่างใกล้ชิดกับ Agent หนึ่งตัว บริบทจะหายไปเมื่อเซสชันสิ้นสุด

โหมด Coordinator: การมอบหมายแบบอะซิงโครนัส มนุษย์กำหนดเป้าหมาย Agent ทำงานแบบขนาน มนุษย์ตรวจสอบผลลัพธ์ ผลลัพธ์กลายเป็นสิ่งประดิษฐ์ที่คงอยู่ (PRs, branches)

Tw93 - inline image

Orchestrator จัดการ sub-agents ที่ทำงานแบบขนาน สื่อสารผ่านโปรโตคอลอินบอกซ์ JSONL และใช้ Worktrees เพื่อการแยก

Tw93 - inline image

Sub-Agents เหมาะกับอะไร?

การค้นหาและการลองผิดลองถูกไม่ควรปนเปื้อนบริบทของ Agent หลัก Agent หลักต้องการเพียงข้อสรุป

typescript
1const result = await runAgentLoop(task, { messages: [] });
2return summarize(result); // บริบทหลักเห็นเฉพาะบรรทัดนี้

ทำไมต้องเขียนการทำงานร่วมกันเป็นโปรโตคอล?

การทำงานร่วมกันด้วยภาษาธรรมชาติล้มเหลวเมื่อ Agent ลืมสัญญา ใช้โปรโตคอลที่มีโครงสร้าง:

typescript
1{ request_id, from_agent, to_agent, content, status: 'pending', timestamp }

ใช้อินบอกซ์ JSONL แบบต่อท้ายเท่านั้นเพื่อการกู้คืนจากความล้มเหลว แยกก่อน แล้วค่อยร่วมมือ

Tw93 - inline image

ภาพหลอนขยายในระบบหลาย Agent

ข้อผิดพลาดกระจายระหว่าง Agent การตรวจสอบข้ามกันทำลายห่วงโซ่นี้โดยให้ Agent อิสระหรือข้อเสนอแนะภายนอก (การทดสอบ คอมไพเลอร์) ตัดสินข้อสรุป

Tw93 - inline image

8. วิธีประเมิน Agent

การประเมินต้องใช้กรณีทดสอบ มาตรฐานการให้คะแนน และการตรวจสอบอัตโนมัติ

Tw93 - inline image

การประเมินแบบเทิร์นเดียวแบบดั้งเดิม (Prompt -> Response) ไม่เพียงพอ การประเมิน Agent ต้องใช้เครื่องมือและสภาพแวดล้อม การให้คะแนนขึ้นอยู่กับสิ่งที่เกิดขึ้นในสภาพแวดล้อม ไม่ใช่แค่สิ่งที่ Agent พูด

Tw93 - inline image

แนวคิดสำคัญ: Task, Trial, Grader Transcript (บันทึกการดำเนินการ) กับ Outcome (สถานะสุดท้าย) คุณต้องมีทั้งสองอย่าง Agent อาจพูดว่า "จองตั๋วแล้ว" (transcript) แต่ล้มเหลวในการสร้างบันทึกในฐานข้อมูล (outcome)

สถานะและเมตริกการประเมิน

หลายทีมยังคงพึ่งพาการตรวจสอบด้วยตนเองหรือผู้ประเมิน LLM เมตริกทั่วไป: Pass@k (สามารถทำได้ในทางทฤษฎีหรือไม่?) และ Pass^k (มีความเสถียรสำหรับการผลิตหรือไม่?) อย่าสับสนระหว่างทั้งสอง

Tw93 - inline image

ผู้ประเมินสามประเภท

  1. Code Graders: การจับคู่สตริง, การทดสอบหน่วย มีความแน่นอนสูงสุด
  2. Model Graders: ผู้ประเมิน LLM ตามเกณฑ์ที่กำหนด เหมาะสำหรับคุณภาพเชิงความหมาย
  3. Human Graders: การตรวจสอบโดยผู้เชี่ยวชาญ ช้าแต่สร้างเกณฑ์พื้นฐาน

การสร้างระบบประเมินจากศูนย์

เริ่มต้นด้วยกรณีความล้มเหลวจริง 20-50 กรณี ตรวจสอบให้แน่ใจว่าสภาพแวดล้อมแยกกันเพื่อให้การทดสอบไม่รบกวนกัน รวมทั้งกรณีบวกและกรณีลบ หากผู้เชี่ยวชาญสองคนไม่เห็นด้วยในกรณีใดกรณีหนึ่ง แสดงว่าเกณฑ์ยังไม่ชัดเจน

แก้ไขระบบประเมินก่อนแก้ไข Agent

หากคะแนนลดลง ให้ตรวจสอบระบบประเมินก่อน ปัญหาสภาพแวดล้อม (ข้อจำกัดหน่วยความจำ, บั๊กในผู้ประเมิน) อาจดูเหมือนการเสื่อมของโมเดล

Tw93 - inline image

9. การติดตามกระบวนการทำงาน

หากไม่มีร่องรอย (traces) ความล้มเหลวจะไม่สามารถทำซ้ำได้ เมตริก APM (เวลาแฝง, อัตราข้อผิดพลาด) ไม่เพียงพอ คุณต้องมีห่วงโซ่การให้เหตุผล

สิ่งที่ควรบันทึกใน Trace?

พรอมต์เต็ม, ข้อความหลายรอบ, การเรียกใช้เครื่องมือ/อาร์กิวเมนต์/ค่าที่ส่งกลับ, ห่วงโซ่การคิด, ผลลัพธ์สุดท้าย, โทเค็น, และเวลาแฝง

การสังเกตการณ์สองชั้น

  1. การสุ่มตัวอย่างด้วยตนเอง: การสุ่มตัวอย่างตามกฎของข้อผิดพลาดหรือข้อเสนอแนะเชิงลบเพื่อค้นหารูปแบบความล้มเหลว
  2. การประเมินอัตโนมัติด้วย LLM: ครอบคลุม traces ทั้งหมดโดยใช้ชั้นการสุ่มตัวอย่างด้วยตนเองเป็นเกณฑ์การปรับเทียบ
Tw93 - inline image

Event Streams เป็นพื้นฐาน

ส่งเหตุการณ์ที่ tool_start, tool_end, และ turn_end ระบบปลายทาง (logs, UI, eval) ใช้เหตุการณ์เหล่านี้โดยไม่ต้องแก้ไขโค้ดหลัก

Tw93 - inline image

10. การนำ Agent ไปใช้กับ OpenClaw

OpenClaw ใช้ห้าชั้นที่แยกออกจากกัน: Gateway, Channel Adapters, Pi Agent (ลูปหลัก), Toolsets (การออกแบบ ACI), และ Context/Memory

Tw93 - inline image

การแยกส่วนด้วย Message Bus

Message Bus แยกช่องทางออกจาก Agent ช่องทางจัดการ I/O เท่านั้น; Agent จัดการเฉพาะการประมวลผล

System Prompts แบบเป็นชั้น

SOUL.md กำหนดเอกลักษณ์และมาตรฐานการทำงานให้สมบูรณ์ Prompts ถูกจัดเป็นชั้น: ข้อมูลรันไทม์ -> เอกลักษณ์ -> หน่วยความจำ -> ทักษะ -> การแทรกแบบไดนามิก

Tw93 - inline image

กำหนดขอบเขตความปลอดภัยก่อน

ก่อนเพิ่มฟีเจอร์ ให้กำหนด: User Whitelists, Workspace Isolation (การตรวจสอบเส้นทาง), และ Audit Logs

การป้องกัน Prompt Injection: ถือว่าเนื้อหาภายนอกไม่น่าเชื่อถือ ใช้การแยกแหล่งที่มา-ปลายทาง อย่าให้เครื่องมือที่ Agent ไม่จำเป็น ต้องมีการยืนยันจากมนุษย์อย่างชัดเจนสำหรับการกระทำที่ละเอียดอ่อน

Provider Fallback: สลับผู้ให้บริการโดยอัตโนมัติ (Anthropic -> OpenAI) หากผู้ให้บริการหนึ่งล้มเหลว

11. รูปแบบที่ไม่ควรทำทั่วไป

  1. System prompt ที่ใช้เป็นฐานความรู้ (ยาวเกินไป)
  2. เครื่องมือมากเกินไป (Agent เลือกเครื่องมือผิด)
  3. ขาดลูปการตรวจสอบ
  4. ระบบหลาย Agent โดยไม่มีขอบเขต
  5. ไม่มีการรวมหน่วยความจำ (คุณภาพลดลงหลังจาก 20 รอบ)
  6. ไม่มีระบบประเมิน
  7. ความซับซ้อนหลาย Agent ก่อนเวลาอันควร
  8. พึ่งพาความคาดหวังแทนข้อจำกัดเชิงกลไก

12. บทสรุป

  1. แกนหลักของ Agent คือลูปที่เสถียร; ฟีเจอร์ใหม่ควรถูกทำให้เป็นภายนอก
  2. Harness กำหนดการลู่เข้ามากกว่าโมเดล
  3. Context engineering ป้องกัน "Context Rot"
  4. การออกแบบเครื่องมือ ACI มุ่งเน้นเป้าหมายและการแก้ไขข้อผิดพลาด
  5. หน่วยความจำถูกจัดเป็นชั้น (Working, Procedural, Episodic, Semantic)
  6. งานที่ยาวนานพึ่งพาสถานะและไฟล์ภายนอก
  7. ระบบหลาย Agent ต้องการโปรโตคอลและการแยกส่วน
  8. ประเมิน Pass@k สำหรับความสามารถ, Pass^k สำหรับคุณภาพ
  9. การติดตาม (Tracing) เป็นข้อกำหนดเบื้องต้นสำหรับการดีบัก
  10. Agent ที่เสถียรขึ้นอยู่กับรายละเอียดทางวิศวกรรม เช่น การแยกส่วนและขอบเขตความปลอดภัย
Save to YouMind

Use YouMind to read viral articles deeply

Save the source, ask focused questions, summarize the argument, and turn a viral article into reusable notes in one AI workspace.

Explore YouMind

แพตเทิร์นให้ถอดรหัสเพิ่มเติม

บทความไวรัลล่าสุด

สำรวจบทความไวรัลเพิ่มเติม