บริบทเครื่องมือและการแคชพรอมต์
ทุกคำขอที่ฮาร์เนสในตัวส่งออกมีสองสิ่งเป็นจริงเสมอ: นิยามเครื่องมือคือต้นทุนคงที่ก้อนใหญ่ที่สุดในคำขอนั้น และแทบไม่มีสักตัวที่จะถูกใช้เลย หน้านี้อธิบายว่า Control Center จัดการเรื่องนี้อย่างไร และทำไมคำตอบจึงไม่ใช่แค่ “ส่งเครื่องมือให้น้อยลง”
ปัญหาคือความใส่ใจ ไม่ใช่จำนวนไบต์
หัวข้อที่มีชื่อว่า “ปัญหาคือความใส่ใจ ไม่ใช่จำนวนไบต์”Control Center ลงทะเบียนเครื่องมือไว้ราว 126 รายการ เมื่อส่งทั้งหมด นิยามของพวกมันกินราว 23,000 โทเคนในทุกคำขอ — ต้นทุนที่การบีบอัดบริบทแตะต้องไม่ได้ เพราะนิยามเครื่องมือเดินทางไปในส่วนของคำขอที่แยกจากบทสนทนา
บิลโทเคนคือข้อร้องเรียนที่เห็นได้ชัด และเมื่อมีแคชพรอมต์ มันก็เป็นต้นทุนที่เล็กกว่าด้วย ต้นทุนจริงคือ ความแม่นยำในการเลือก ผลการประเมินที่เผยแพร่เกี่ยวกับการเรียกใช้เครื่องมือเห็นตรงกันว่า โมเดลเลือกเครื่องมือได้อย่างเชื่อถือได้ถึงราว 30–50 เครื่องมือในบริบท และยิ่งแย่ลงเรื่อย ๆ เมื่อเกินกว่านั้น สาเหตุที่ระบุไว้ไม่ใช่จำนวนโทเคน แต่เป็น ความทับซ้อนเชิงความหมาย: เมื่อแค็ตตาล็อกมีเครื่องมือหลายตัวที่คำอธิบายอ่านคล้ายกัน โมเดลก็กำลังเลือกระหว่างของที่หน้าตาเหมือนกัน แทนที่จะนึกถึงขีดความสามารถหนึ่งขึ้นมา
เมื่อมีเครื่องมือ 126 ตัว เอเจนต์จึงอยู่ไกลเกินช่วงนั้นไปแล้วในทุก ๆ เทิร์น — รวมถึงหลายเทิร์นที่มันแค่ต้องอ่านไฟล์
พื้นผิวสองชั้น
หัวข้อที่มีชื่อว่า “พื้นผิวสองชั้น”การรันหนึ่งครั้งจึงได้รับข้อมูลสองอย่าง:
- ชุด ประจำ — เครื่องมือราวสองโหล ส่งพร้อมสคีมาเต็มในทุกคำขอ;
- ดัชนีชื่อ ของอย่างอื่นทั้งหมด จัดกลุ่มใต้หัวข้ออย่าง “Tickets and projects” หรือ “Memory and knowledge” กินโทเคนเพียงไม่กี่ร้อยตัวสำหรับทั้งแค็ตตาล็อก
เมื่อวัดกับแค็ตตาล็อกปัจจุบัน วิธีนี้พาบล็อกเครื่องมือจาก ~23.1k โทเคนเหลือ ~5.0k เล็กลงราว 78% และวางชุดที่มองเห็นได้เสมอไว้ในช่วงที่การเลือกเชื่อถือได้
ชุดประจำคือเครื่องมือที่การรันใช้ใน เกือบทุก การรัน: คำศัพท์ว่าด้วยไฟล์และเชลล์ (read, write, edit, search, bash, …) เครื่องมือสำหรับค้นหา เช็กลิสต์งาน การค้นหาหน่วยความจำ และคำกริยาใดก็ตามที่โหมดปัจจุบันต้องเรียกเพื่อส่งผลลัพธ์ของมัน คำกริยาส่งผลลัพธ์ของโหมดเองอยู่ในชุดประจำเสมอ — การรันต้องไม่ต้องออกตามหาการเรียกเพียงหนึ่งเดียวที่ผลิตสิ่งที่ต้องส่งมอบ
การหน่วงไว้ไม่ได้แปลว่าใช้ไม่ได้
หัวข้อที่มีชื่อว่า “การหน่วงไว้ไม่ได้แปลว่าใช้ไม่ได้”นี่คือส่วนที่ทำให้การออกแบบนี้ปลอดภัย ไม่ใช่แค่ประหยัด เครื่องมือที่ถูกหน่วงไว้เรียกใช้ได้ตั้งแต่เทิร์นแรก มีเพียงสคีมาของมันที่ถูกกันไว้
เอเจนต์เห็นชื่อนั้นอยู่ในพรอมต์ของมัน ถ้ามันเรียกชื่อนั้นโดยตรง ฮาร์เนสจะโหลดสคีมาและรันเครื่องมือในขั้นตอนเดียวกัน — ไม่มีข้อผิดพลาด ไม่มีรอบไปกลับ โมเดลไม่จำเป็นต้องรู้ว่าเครื่องมือนั้นเคย “ถูกถอดออก” มาก่อน
เมื่อเอเจนต์รู้ว่างานคืออะไรแต่ไม่รู้ชื่อเครื่องมือ search_tools รับคำอธิบายภาษาธรรมชาติ (“assign a ticket to someone”) จัดอันดับพื้นผิวทั้งหมดของการรัน แล้วคืนรายการที่ตรง พร้อมสคีมาที่โหลดไว้แล้ว เทิร์นถัดไปจึงเรียกตัวใดตัวหนึ่งได้ทันที ถ้าการค้นหาไม่พบอะไร มันจะบอกตรง ๆ และเสนอถ้อยคำอื่นหรือการค้นหาที่กว้างขึ้น — จงใจให้เป็นเช่นนั้น เพราะเอเจนต์ที่อ่านผลการค้นหาว่างเปล่าว่า “ขีดความสามารถนี้ไม่มีอยู่” จะคิดค้นสิ่งทดแทนขึ้นมาแทนที่จะมองหาอีกครั้ง
search_tools มีอยู่เฉพาะในฮาร์เนสในตัว ไคลเอนต์ MCP ภายนอกใช้ search_tool_bm25 แทน ซึ่งจัดอันดับดัชนีเดียวกันแต่เพียงบอกถึงรายการที่ตรง — ไคลเอนต์เหล่านั้นได้รับสคีมาทุกตัวจาก tools/list ไปแล้ว จึงไม่มีอะไรต้องโหลด
สิ่งที่การหน่วงไม่เคยทำ
หัวข้อที่มีชื่อว่า “สิ่งที่การหน่วงไม่เคยทำ”การหน่วงถูกนำมาใช้ หลัง ตัวกรองเครื่องมือของโหมด ไม่เคยใช้แทนตัวกรองนั้น มันซ่อนสคีมาเท่านั้น ไม่สามารถเปิดเผยเครื่องมือที่โหมดปฏิเสธไปแล้วได้ เครื่องมือที่ถูกเปิดใช้งานยังต้องผ่านพรอมต์ขออนุมัติและกฎคุมการกระทำเหมือนเครื่องมือประจำทุกประการ และเครื่องมือไม่สามารถโหลดชื่อที่ไม่ได้อยู่บนพื้นผิวของการรันตั้งแต่แรกได้
พื้นผิว MCP ที่ไคลเอนต์ภายนอกเห็น ไม่ ได้รับผลกระทบ tools/list ยังคืนแค็ตตาล็อกครบถ้วนเหมือนเดิม ความพยายามตัดทอนมันในอดีตทำให้เอเจนต์พังไปเลย: ไคลเอนต์ MCP ตรวจสอบชื่อเครื่องมือกับรายการที่แคชไว้ของตนเอง และปฏิเสธทุกชื่อที่ไม่อยู่ในรายการโดยไม่ถามเซิร์ฟเวอร์เลย เครื่องมือที่ “ซ่อนแต่เรียกได้” จึงเข้าถึงไม่ได้เสียเปล่า การหน่วงอยู่ในฮาร์เนส ซึ่งการโหลดเครื่องมือกลางการรันเป็นสิ่งที่ฮาร์เนสทำได้จริง
การปิดการหน่วง
หัวข้อที่มีชื่อว่า “การปิดการหน่วง”--tool-deferral=off (หรือ CC_SERVER_TOOL_DEFERRAL=off) ทำให้เครื่องมือทุกตัวที่ได้รับอนุญาตกลับเข้าชุดประจำอีกครั้ง ส่งคำขอเหมือนที่เซิร์ฟเวอร์เคยส่งก่อนมีการหน่วงทุกประการ ดูเอกสารอ้างอิง CLI ของ cc_server
การแคชพรอมต์
หัวข้อที่มีชื่อว่า “การแคชพรอมต์”ภาระงานของเอเจนต์ไม่สมดุล: มันส่งพรอมต์ขนาดมหึมาและรับคำตอบเล็ก ๆ ซ้ำแล้วซ้ำอีกในแต่ละเทิร์น โดยมีคำนำหน้าเดิมทุกครั้ง ผู้ให้บริการแคชคำนำหน้านั้นไว้ และการอ่านจากแคชมีต้นทุนราวหนึ่งในสิบของการอ่านใหม่ อัตราการเข้าแคชจึงเป็นคุณสมบัติชั้นต้นของฮาร์เนส ไม่ใช่เรื่องของการปรับแต่ง — และบน Anthropic มันยังซื้ออัตราการประมวลผลให้ด้วย เพราะโทเคนที่แคชไว้ไม่นับรวมกับขีดจำกัดอินพุตต่อนาที
การแคชทำงานบนการจับคู่คำนำหน้าแบบตรงกันทุกไบต์ ซึ่งนำมาสู่กฎครอบคลุมข้อเดียว
ไม่เขียนทับสิ่งที่ส่งไปแล้วเด็ดขาด
หัวข้อที่มีชื่อว่า “ไม่เขียนทับสิ่งที่ส่งไปแล้วเด็ดขาด”ไบต์ใดก็ตามที่เปลี่ยนไปจะทำให้แคชเป็นโมฆะตั้งแต่จุดนั้นเป็นต้นไป ฮาร์เนสจึง ต่อท้าย เท่านั้น: ข้อความใหม่ต่อท้าย และสคีมาเครื่องมือที่เพิ่งเปิดใช้งานวางไว้ หลัง บล็อกเครื่องมือที่แคชไว้ ไม่ใช่ผสานเข้าไปในบล็อกนั้น ลำดับเช่นนี้คือเหตุผลที่การเปิดใช้งานมีต้นทุนต่ำ — คำนำหน้าที่แคชไว้ยังคงเหมือนเดิม และการรันจ่ายเฉพาะสคีมาที่เพิ่งโหลดเข้ามา
ลูปยังตัดผลลัพธ์เครื่องมือที่เก่าเกินไปจากเทิร์นก่อน ๆ ออกเพื่อคืนพื้นที่บริบท มันรอจนมีของให้คืนมากพอที่จะคุ้มกับการเขียนใหม่ การตัดทิ้งจึงเป็นการเขียนใหม่ครั้งใหญ่ที่เกิดไม่บ่อย แทนที่จะเป็นการพลาดแคชอย่างต่อเนื่อง
จุดตัดแคชอยู่ตรงไหน
หัวข้อที่มีชื่อว่า “จุดตัดแคชอยู่ตรงไหน”ผู้ให้บริการอนุญาตจุดทำเครื่องหมายแคชได้จำนวนจำกัด และ Control Center ใช้มันอย่างมีเจตนา:
| จุดทำเครื่องหมาย | ครอบคลุม | อายุ |
|---|---|---|
| เครื่องมือประจำตัวสุดท้าย | บล็อกเครื่องมือ | 1 ชั่วโมง |
| บล็อกระบบตัวสุดท้าย | เครื่องมือ + พรอมต์ระบบ | 1 ชั่วโมง |
| ส่วนท้ายของเทิร์นก่อนหน้า | ประวัติจนถึงคำขอล่าสุด | 5 นาที |
| ส่วนท้ายของเทิร์นปัจจุบัน | ประวัติทั้งหมด | 5 นาที |
สองจุดหลังเป็นคู่ที่หมุนเวียนกัน และเหตุผลของคู่นี้ออกจะละเอียดอ่อน: การค้นหาแคชจะย้อนกลับไปในจำนวนบล็อกที่จำกัดเพื่อหาสิ่งที่คำขอก่อนหน้าเคยเก็บไว้ เทิร์นเดียวที่รันเครื่องมือหลายตัวพร้อมกันอาจปล่อยบล็อกออกมามากกว่าหน้าต่างนั้น เมื่อถึงจุดนั้นจุดทำเครื่องหมายส่วนท้ายจุดหนึ่งจะพลาดอย่างเงียบ ๆ และคำขอต้องจ่ายเต็มราคาโดยไม่มีอะไรในคำตอบบอกว่ามันเกิดขึ้น การทำเครื่องหมายตรงจุดที่คำขอก่อนหน้าหยุดพอดีทำให้การเข้าแคชเป็นเรื่องเชิงโครงสร้าง ไม่ใช่การแข่งขัน
คำนำหน้าของเครื่องมือและระบบได้อายุยาว เพราะมันคงที่ตลอดการรัน ถูกใช้ร่วมกันโดยทุกการรันและซับเอเจนต์ที่มีรูปร่างเดียวกัน และเพราะนาฬิกาเริ่มนับเมื่อคำขอ เริ่มต้น — เทิร์นที่สตรีมหลายนาทีได้ใช้หน้าต่างสั้น ๆ ไปเกือบหมดแล้วก่อนคำขอถัดไปจะก่อตัวขึ้น
ซับเอเจนต์ใช้คำนำหน้าร่วมกัน
หัวข้อที่มีชื่อว่า “ซับเอเจนต์ใช้คำนำหน้าร่วมกัน”ซับเอเจนต์ชนิดเดียวกันให้คำนำหน้าพรอมต์ที่เหมือนกันทุกไบต์ จึงควรอ่านรายการแคชรายการเดียวแทนที่ต่างคนต่างเขียนของตัวเอง แต่รายการหนึ่งจะอ่านได้ก็ต่อเมื่อคำขอที่เขียนมันเริ่มตอบกลับ การกระจายงานที่เริ่มขึ้นพร้อมกันทั้งหมดจึงทำให้ลูกทุกตัวต้องจ่ายราคาที่แพงกว่าสำหรับคำนำหน้าที่ทุกตัวใช้ร่วมกัน
ฮาร์เนสให้ลูกตัวแรกของรูปร่างหนึ่งเริ่มก่อน แล้วปล่อยพี่น้องของมันทันทีที่มันเริ่มสตรีม การรอนี้มีขอบเขต: ลูกตัวแรกที่ช้าทำให้เสียการเข้าแคชหนึ่งครั้ง ไม่เคยทำให้การกระจายงานทั้งชุดเสียหาย ด้วยเหตุผลเดียวกัน ซับเอเจนต์ทุกตัวได้ชุดเครื่องมือประจำชุดเดียวกันไม่ว่าโปรไฟล์ของมันจะเป็นอย่างไร — การแปรเปลี่ยนตามโปรไฟล์จะทำให้คำนำหน้าที่ยังใช้ร่วมกันแตกร้าวโดยไม่ได้อะไรตอบแทน เพราะโปรไฟล์ตัดสินไปแล้วว่าลูกเรียกอะไรได้บ้าง
ดูการทำงานจริง
หัวข้อที่มีชื่อว่า “ดูการทำงานจริง”ทั้งสองส่วนนี้สังเกตได้ ไม่ใช่เรื่องที่ต้องสมมติ:
- ตัวสำรวจบริบทในบทสนทนารายงานเครื่องมือประจำ ดัชนีที่ถูกหน่วงไว้ และสคีมาที่ถูกกันไว้แต่ละตัว จะ มีต้นทุนเท่าใด โดยสร้างจากเส้นทางโค้ดเดียวกับที่การรันจริงใช้
- การรันแต่ละครั้งบันทึกว่าโหลดเครื่องมือที่ถูกหน่วงไว้ตัวใดและอะไรเป็นตัวกระตุ้น ผลการค้นหาที่ไม่พบอะไรจึงปรากฏให้เห็นว่าเป็นการพลาดในการดึงข้อมูล แทนที่จะหายไปกับเอเจนต์ที่เงียบ ๆ ไปทำอะไรที่ใกล้เคียงกัน
- การสังเกตการณ์รายงาน อัตราแคช เป็นสัดส่วนของการอ่านแคชต่อโทเคนพรอมต์ทั้งหมดที่ประมวลผล รวมการเขียนแคชด้วย — การรันที่เอาแต่สร้างคำนำหน้าขึ้นใหม่จะได้คะแนนต่ำ แทนที่จะได้คะแนนเหมือนการรันที่ไม่เคยต้องใช้แคชเลย