Tools
การตรวจจับลูปของเครื่องมือ
OpenClaw มี guardrail สองส่วนที่ทำงานร่วมกันสำหรับรูปแบบการเรียกเครื่องมือซ้ำ ๆ:
- การตรวจจับลูป (
tools.loopDetection.enabled) — ปิดใช้งานโดยค่าเริ่มต้น เฝ้าดูประวัติการเรียกเครื่องมือแบบเลื่อนต่อเนื่องเพื่อหารูปแบบที่เกิดซ้ำและการลองซ้ำกับเครื่องมือที่ไม่รู้จัก - ตัวป้องกันหลัง Compaction (
tools.loopDetection.postCompactionGuard) — เปิดใช้งานโดยค่าเริ่มต้น เว้นแต่tools.loopDetection.enabledจะถูกตั้งเป็นfalseอย่างชัดเจน จะเริ่มทำงานหลังการลองซ้ำหลัง Compaction ทุกครั้ง และยุติการรันเมื่อเอเจนต์ส่งสามค่า(tool, args, result)เดิมภายในหน้าต่างการตรวจสอบ
ทั้งสองส่วนกำหนดค่าอยู่ใต้บล็อก tools.loopDetection เดียวกัน แต่ตัวป้องกันหลัง Compaction จะทำงานเมื่อสวิตช์หลักไม่ได้ถูกปิดอย่างชัดเจน ตั้งค่า tools.loopDetection.enabled: false เพื่อปิดทั้งสองพื้นผิว
เหตุผลที่มีสิ่งนี้
- ตรวจจับลำดับที่เกิดซ้ำซึ่งไม่เกิดความคืบหน้า
- ตรวจจับลูปความถี่สูงที่ไม่มีผลลัพธ์ (เครื่องมือเดิม อินพุตเดิม ข้อผิดพลาดซ้ำ)
- ตรวจจับรูปแบบการเรียกซ้ำเฉพาะสำหรับเครื่องมือ polling ที่รู้จัก
- ป้องกันวงจรบริบทล้น จากนั้น Compaction จากนั้นกลับสู่ลูปเดิมไม่ให้ทำงานต่อไปไม่สิ้นสุด
บล็อกการกำหนดค่า
ค่าเริ่มต้นแบบทั่วระบบ พร้อมแสดงทุกฟิลด์ที่มีเอกสารกำกับ:
{
tools: {
loopDetection: {
enabled: false, // master switch for the rolling-history detectors
historySize: 30,
warningThreshold: 10,
criticalThreshold: 20,
unknownToolThreshold: 10,
globalCircuitBreakerThreshold: 30,
detectors: {
genericRepeat: true,
knownPollNoProgress: true,
pingPong: true,
},
postCompactionGuard: {
windowSize: 3, // armed after compaction-retry; runs unless enabled is explicitly false
},
},
},
}
การแทนที่ค่ารายเอเจนต์ (ไม่บังคับ):
{
agents: {
list: [
{
id: "safe-runner",
tools: {
loopDetection: {
enabled: true,
warningThreshold: 8,
criticalThreshold: 16,
},
},
},
],
},
}
พฤติกรรมของฟิลด์
| ฟิลด์ | ค่าเริ่มต้น | ผลลัพธ์ |
|---|---|---|
enabled |
false |
สวิตช์หลักสำหรับตัวตรวจจับประวัติแบบเลื่อนต่อเนื่อง การตั้งเป็น false จะปิดตัวป้องกันหลัง Compaction ด้วย |
historySize |
30 |
จำนวนการเรียกเครื่องมือล่าสุดที่เก็บไว้สำหรับการวิเคราะห์ |
warningThreshold |
10 |
เกณฑ์ก่อนที่รูปแบบจะถูกจัดประเภทเป็นคำเตือนเท่านั้น |
criticalThreshold |
20 |
เกณฑ์สำหรับบล็อกรูปแบบลูปที่เกิดซ้ำ |
unknownToolThreshold |
10 |
บล็อกการเรียกซ้ำไปยังเครื่องมือที่ไม่พร้อมใช้งานตัวเดิมหลังจากพลาดครบจำนวนนี้ |
globalCircuitBreakerThreshold |
30 |
เกณฑ์ breaker แบบทั่วระบบสำหรับภาวะไม่คืบหน้าในทุกตัวตรวจจับ |
detectors.genericRepeat |
true |
ตรวจจับรูปแบบเครื่องมือเดิม + พารามิเตอร์เดิมที่เกิดซ้ำ |
detectors.knownPollNoProgress |
true |
ตรวจจับรูปแบบที่คล้าย polling ซึ่งรู้จักและไม่มีการเปลี่ยนแปลงสถานะ |
detectors.pingPong |
true |
ตรวจจับรูปแบบ ping-pong แบบสลับไปมา |
postCompactionGuard.windowSize |
3 |
จำนวนการเรียกเครื่องมือหลัง Compaction ที่ตัวป้องกันยังคงทำงาน และจำนวนสามค่าที่เหมือนกันซึ่งจะทำให้ยุติการรัน |
สำหรับ exec การตรวจสอบภาวะไม่คืบหน้าจะเปรียบเทียบผลลัพธ์ของคำสั่งที่เสถียร และละเว้นเมทาดาทารันไทม์ที่เปลี่ยนแปลงง่าย เช่น ระยะเวลา, PID, session ID และไดเรกทอรีทำงาน เมื่อมี run id ประวัติการเรียกเครื่องมือล่าสุดจะถูกประเมินเฉพาะภายในการรันนั้น เพื่อให้รอบ Heartbeat ตามกำหนดเวลาและการรันใหม่ไม่รับจำนวนลูปเก่าจากการรันก่อนหน้า
การตั้งค่าที่แนะนำ
- สำหรับโมเดลขนาดเล็ก ให้ตั้ง
enabled: trueและคงค่าเกณฑ์ไว้ตามค่าเริ่มต้น โมเดลระดับ flagship แทบไม่ต้องใช้การตรวจจับประวัติแบบเลื่อนต่อเนื่อง และสามารถปล่อยสวิตช์หลักไว้ที่falseขณะที่ยังได้ประโยชน์จากตัวป้องกันหลัง Compaction - คงลำดับเกณฑ์ไว้เป็น
warningThreshold < criticalThreshold < globalCircuitBreakerThreshold - หากเกิด false positive:
- เพิ่ม
warningThresholdและ/หรือcriticalThreshold - อาจเพิ่ม
globalCircuitBreakerThreshold - ปิดเฉพาะตัวตรวจจับที่ก่อปัญหา (
detectors.<name>: false) - ลด
historySizeเพื่อให้บริบทประวัติเข้มงวดน้อยลง
- เพิ่ม
- หากต้องการปิดทุกอย่าง (รวมถึงตัวป้องกันหลัง Compaction) ให้ตั้ง
tools.loopDetection.enabled: falseอย่างชัดเจน
ตัวป้องกันหลัง Compaction
เมื่อ runner ทำ compaction-retry สำเร็จหลังบริบทล้น ระบบจะเริ่มตัวป้องกันหน้าต่างสั้น ๆ ที่เฝ้าดูการเรียกเครื่องมือไม่กี่ครั้งถัดไป หากเอเจนต์ส่งสามค่า (toolName, argsHash, resultHash) เดิมหลายครั้งภายในหน้าต่าง ตัวป้องกันจะสรุปว่า Compaction ไม่ได้ตัดลูป และยุติการรันด้วยข้อผิดพลาด compaction_loop_persisted
ตัวป้องกันถูกควบคุมด้วยแฟล็กหลัก tools.loopDetection.enabled แต่มีข้อยกเว้นหนึ่งอย่าง: ตัวป้องกันจะยัง เปิดใช้งานเมื่อไม่ได้ตั้งค่าแฟล็กหรือแฟล็กเป็น true และจะปิดเฉพาะเมื่อแฟล็กถูกตั้งเป็น false อย่างชัดเจน นี่เป็นพฤติกรรมที่ตั้งใจไว้ ตัวป้องกันนี้มีไว้เพื่อหลุดออกจากลูป Compaction ที่มิฉะนั้นจะใช้โทเค็นได้ไม่จำกัด ดังนั้นผู้ใช้ที่ไม่ได้กำหนดค่าก็ยังได้รับการป้องกัน
{
tools: {
loopDetection: {
// master switch; set false to disable the guard along with the rolling detectors
enabled: true,
postCompactionGuard: {
windowSize: 3, // default
},
},
},
}
windowSizeที่ต่ำกว่าจะเข้มงวดกว่า (จำนวนครั้งที่ลองก่อนยุติน้อยกว่า)windowSizeที่สูงกว่าจะให้เอเจนต์มีโอกาสกู้คืนมากขึ้น- ตัวป้องกันจะไม่ยุติการทำงานเมื่อผลลัพธ์กำลังเปลี่ยนแปลง จะยุติเฉพาะเมื่อผลลัพธ์เหมือนกันทุกไบต์ตลอดหน้าต่าง
- ตั้งใจให้อยู่ในขอบเขตแคบ: ทำงานเฉพาะทันทีหลัง compaction-retry
ล็อกและพฤติกรรมที่คาดหวัง
เมื่อตรวจพบลูป OpenClaw จะรายงานเหตุการณ์ลูป และจะลดทอนหรือบล็อกรอบการใช้เครื่องมือถัดไปตามระดับความรุนแรง สิ่งนี้ช่วยปกป้องผู้ใช้จากการใช้โทเค็นที่บานปลายและการค้าง ขณะเดียวกันยังคงการเข้าถึงเครื่องมือตามปกติไว้
- คำเตือนจะมาก่อน
- การระงับจะตามมาเมื่อรูปแบบยังคงอยู่เกินเกณฑ์คำเตือน
- เกณฑ์ระดับวิกฤตจะบล็อกรอบการใช้เครื่องมือถัดไป และแสดงเหตุผลการตรวจจับลูปอย่างชัดเจนในบันทึกการรัน
- ตัวป้องกันหลัง Compaction จะส่งข้อผิดพลาด
compaction_loop_persistedพร้อมชื่อเครื่องมือที่เป็นต้นเหตุและจำนวนการเรียกที่เหมือนกัน