Plugin maintainer reference
โครงสร้างภายในสถาปัตยกรรม Plugin
สำหรับโมเดลความสามารถสาธารณะ รูปแบบ Plugin และสัญญาความเป็นเจ้าของ/การดำเนินการ โปรดดู สถาปัตยกรรม Plugin หน้านี้ครอบคลุมกลไกภายใน ได้แก่ ไปป์ไลน์การโหลด รีจิสทรี ฮุกขณะรัน เส้นทาง HTTP ของ Gateway พาธการนำเข้า และตารางสคีมา
ไปป์ไลน์การโหลด
เมื่อเริ่มทำงาน OpenClaw จะดำเนินการโดยคร่าว ๆ ดังนี้:
- ค้นหารูท Plugin ที่อาจเป็นไปได้
- อ่านแมนิเฟสต์บันเดิลแบบเนทีฟหรือที่เข้ากันได้และข้อมูลเมตาของแพ็กเกจ
- ปฏิเสธรายการที่ไม่ปลอดภัย
- ปรับการกำหนดค่า Plugin ให้เป็นมาตรฐาน (
plugins.enabled,allow,deny,entries,slots,load.paths) - ตัดสินใจว่าจะเปิดใช้งานแต่ละรายการหรือไม่
- โหลดโมดูลเนทีฟที่เปิดใช้งาน: โมดูลที่บันเดิลและสร้างไว้แล้วใช้ตัวโหลดเนทีฟ; ซอร์ส TypeScript ในเครื่องจากบุคคลที่สามใช้ Jiti เป็นทางเลือกฉุกเฉิน
- เรียกฮุก
register(api)แบบเนทีฟและรวบรวมการลงทะเบียนไว้ในรีจิสทรี Plugin - เปิดให้คำสั่งและพื้นผิวขณะรันเข้าถึงรีจิสทรี
ด่านตรวจความปลอดภัยทำงาน ก่อน การดำเนินการขณะรัน การค้นหาจะบล็อกรายการเมื่อ:
- จุดเข้าใช้งานที่แก้ไขพาธแล้วออกนอกรูท Plugin
- พาธ (หรือไดเรกทอรีรูท) เปิดให้ทุกคนเขียนได้
- สำหรับ Plugin ที่ไม่ได้บันเดิล ความเป็นเจ้าของพาธไม่ตรงกับ uid ปัจจุบัน (หรือ root)
สำหรับไดเรกทอรีที่บันเดิลและเปิดให้ทุกคนเขียนได้ ระบบจะลองซ่อมแซมแบบแทนที่ด้วย chmod
ก่อน (การติดตั้ง npm/แบบส่วนกลางอาจจัดส่งไดเรกทอรีแพ็กเกจที่ 0777) แล้วจึงตรวจสอบด่านอีกครั้ง;
ส่วนการตรวจสอบความเป็นเจ้าของจะถูกข้ามทั้งหมดสำหรับต้นทางแบบบันเดิล
รายการที่ถูกบล็อกยังคงมี id ของ Plugin อยู่ในข้อมูลวินิจฉัยที่ปล่อยออกมาเมื่อ ทราบ id ดังกล่าว (รวมถึง id ที่หาได้จากแมนิเฟสต์ภายในไดเรกทอรี ที่ถูกปฏิเสธด้วยเหตุผลอื่น) ดังนั้นการกำหนดค่าที่อ้างอิง id นั้นจะเห็น Plugin ที่ถูกบล็อกซึ่งเชื่อมโยงกับคำเตือนด้านความปลอดภัยของพาธ แทนข้อผิดพลาด "Plugin ที่ไม่รู้จัก" ซึ่งไม่เกี่ยวข้อง
ลักษณะการทำงานที่ใช้แมนิเฟสต์ก่อน
แมนิเฟสต์เป็นแหล่งข้อมูลจริงของระนาบควบคุม OpenClaw ใช้แมนิเฟสต์เพื่อ:
- ระบุ Plugin
- ค้นหาช่องทาง/Skills/สคีมาการกำหนดค่าหรือความสามารถของบันเดิลที่ประกาศไว้
- ตรวจสอบ
plugins.entries.<id>.config - เสริมป้ายกำกับ/ข้อความตัวอย่างในช่องกรอกของ Control UI
- แสดงข้อมูลเมตาการติดตั้ง/แค็ตตาล็อก
- เก็บตัวบ่งชี้การเปิดใช้งานและการตั้งค่าที่มีต้นทุนต่ำโดยไม่โหลดรันไทม์ของ Plugin
สำหรับ Plugin แบบเนทีฟ โมดูลรันไทม์เป็นส่วนของระนาบข้อมูล โดยลงทะเบียน ลักษณะการทำงานจริง เช่น ฮุก เครื่องมือ คำสั่ง หรือโฟลว์ของผู้ให้บริการ
บล็อก activation และ setup ที่เป็นตัวเลือกในแมนิเฟสต์จะยังอยู่ในระนาบควบคุม
บล็อกเหล่านี้เป็นเพียงตัวบ่งชี้ข้อมูลเมตาสำหรับการวางแผนเปิดใช้งานและการค้นหาการตั้งค่า;
ไม่ได้ใช้แทนการลงทะเบียนขณะรัน register(...) หรือ setupEntry
ผู้ใช้ข้อมูลการเปิดใช้งานแบบสดจะใช้คำสั่ง ช่องทาง และคำใบ้ผู้ให้บริการจากแมนิเฟสต์เพื่อ
จำกัดการโหลด Plugin ก่อนสร้างรีจิสทรีในวงกว้าง:
- การโหลด CLI จำกัดเฉพาะ Plugin ที่เป็นเจ้าของคำสั่งหลักที่ร้องขอ
- การตั้งค่าช่องทาง/การแก้ไข Plugin จำกัดเฉพาะ Plugin ที่เป็นเจ้าของ id ช่องทางที่ร้องขอ
- การตั้งค่า/การแก้ไขขณะรันของผู้ให้บริการที่ระบุชัดเจน จำกัดเฉพาะ Plugin ที่เป็นเจ้าของ id ผู้ให้บริการที่ร้องขอ
- การวางแผนเริ่มต้น Gateway ใช้
activation.onStartupสำหรับการนำเข้าเมื่อเริ่มต้น ที่ระบุชัดเจน; Plugin ที่ไม่มีข้อมูลเมตาการเริ่มต้นจะโหลดผ่านทริกเกอร์ การเปิดใช้งานที่แคบกว่าเท่านั้น
ตัววางแผนการเปิดใช้งานมีทั้ง API ที่ส่งคืนเฉพาะ id สำหรับผู้เรียกเดิม และ
API แผนสำหรับการวินิจฉัย รายการในแผนรายงานเหตุผลที่เลือก Plugin
โดยแยกคำใบ้ activation.* ที่ระบุชัดเจนออกจากทางเลือกสำรองตามความเป็นเจ้าของในแมนิเฟสต์:
เหตุผล (จากคำใบ้ activation.*) |
เหตุผล (จากความเป็นเจ้าของในแมนิเฟสต์) |
|---|---|
activation-agent-harness-hint |
— |
activation-capability-hint |
— |
activation-channel-hint |
manifest-channel-owner (channels) |
activation-command-hint |
manifest-command-alias (commandAliases) |
activation-provider-hint |
manifest-provider-owner (providers), manifest-setup-provider-owner (setup.providers) |
activation-route-hint |
— |
| — (ทริกเกอร์ฮุกไม่มีรูปแบบคำใบ้) | manifest-hook-owner (hooks), manifest-tool-contract (contracts.tools) |
การแยกเหตุผลนี้คือขอบเขตความเข้ากันได้: ข้อมูลเมตา Plugin เดิม ยังคงทำงาน ขณะที่โค้ดใหม่ตรวจจับคำใบ้แบบกว้างหรือพฤติกรรมทางเลือกสำรองได้ โดยไม่เปลี่ยนความหมายของการโหลดขณะรัน
การโหลดรันไทม์ล่วงหน้าขณะประมวลผลคำขอที่ขอสโคป all แบบกว้าง ยังคงหา
ชุด id Plugin ที่มีผลอย่างชัดเจนจากการกำหนดค่า การวางแผนเริ่มต้น ช่องทาง
ที่กำหนดค่าไว้ สล็อต และกฎการเปิดใช้งานอัตโนมัติ
(resolveEffectivePluginIds ใน src/plugins/effective-plugin-ids.ts) หาก
ชุดที่หาได้นั้นว่างเปล่า OpenClaw จะคงสโคปให้ว่าง แทนที่จะขยายไปยัง
ทุก Plugin ที่ค้นพบได้
การค้นหาการตั้งค่าจะเลือก id ที่ตัวบ่งชี้เป็นเจ้าของ เช่น setup.providers และ
setup.cliBackends ก่อน เพื่อจำกัด Plugin ที่อาจเป็นไปได้ แล้วจึงใช้
setup-api เป็นทางเลือกสำรองสำหรับ Plugin ที่ยังต้องใช้ฮุกขณะรันในขั้นตอนการตั้งค่า รายการ
การตั้งค่าผู้ให้บริการใช้ providerAuthChoices จากแมนิเฟสต์ ตัวเลือกการตั้งค่า
ที่ได้จากตัวบ่งชี้ และข้อมูลเมตาแค็ตตาล็อกการติดตั้ง โดยไม่โหลดรันไทม์ผู้ให้บริการ ค่า
setup.requiresRuntime: false ที่ระบุชัดเจนเป็นจุดตัดที่ใช้เฉพาะตัวบ่งชี้; หากละเว้น
requiresRuntime ระบบจะเก็บทางเลือกสำรองของ API การตั้งค่าแบบเดิมไว้เพื่อความเข้ากันได้ หาก
Plugin ที่ค้นพบมากกว่าหนึ่งรายการอ้างความเป็นเจ้าของผู้ให้บริการตั้งค่าหรือ
id แบ็กเอนด์ CLI เดียวกันหลังปรับเป็นมาตรฐาน การค้นหาการตั้งค่าจะปฏิเสธเจ้าของที่กำกวม แทนการอาศัย
ลำดับการค้นหา เมื่อรันไทม์การตั้งค่าทำงานจริง ข้อมูลวินิจฉัยของรีจิสทรีจะรายงาน
ความคลาดเคลื่อนระหว่าง setup.providers / setup.cliBackends กับผู้ให้บริการหรือแบ็กเอนด์ CLI
ที่ API การตั้งค่าลงทะเบียนจริง โดยไม่บล็อก Plugin แบบเดิม
ขอบเขตแคชของ Plugin
OpenClaw ไม่แคชผลการค้นหา Plugin หรือข้อมูลรีจิสทรีแมนิเฟสต์โดยตรง ไว้เบื้องหลังช่วงเวลาตามนาฬิกา การติดตั้ง การแก้ไขแมนิเฟสต์ และการเปลี่ยนพาธโหลด ต้องปรากฏให้เห็นในการอ่านข้อมูลเมตาหรือการสร้างสแนปช็อตใหม่แบบระบุชัดเจนครั้งถัดไป ตัวแยกวิเคราะห์ไฟล์แมนิเฟสต์เก็บแคชลายเซ็นไฟล์แบบจำกัดขนาด ซึ่งใช้พาธ แมนิเฟสต์ที่เปิดร่วมกับอุปกรณ์/inode ขนาด และ mtime/ctime เป็นคีย์; แคชดังกล่าวมีไว้เพียง หลีกเลี่ยงการแยกวิเคราะห์ไบต์ที่ไม่เปลี่ยนแปลงซ้ำ และต้องไม่แคชคำตอบเกี่ยวกับการค้นหา รีจิสทรี เจ้าของ หรือนโยบาย
เส้นทางด่วนสำหรับข้อมูลเมตาที่ปลอดภัยคือความเป็นเจ้าของออบเจ็กต์อย่างชัดเจน ไม่ใช่แคชที่ซ่อนอยู่
ฮอตพาธการเริ่มต้น Gateway ควรส่ง PluginMetadataSnapshot ปัจจุบัน
PluginLookUpTable ที่หาได้ หรือรีจิสทรีแมนิเฟสต์ที่ระบุชัดเจนผ่านสายการเรียก
การตรวจสอบการกำหนดค่า การเปิดใช้งานอัตโนมัติเมื่อเริ่มต้น การบูตสแตรป Plugin และการเลือก
ผู้ให้บริการสามารถใช้ออบเจ็กต์เหล่านั้นซ้ำได้ขณะที่ออบเจ็กต์ยังแทนการกำหนดค่าและ
รายการ Plugin ปัจจุบัน การค้นหาการตั้งค่ายังคงสร้างข้อมูลเมตาแมนิเฟสต์ใหม่ตามต้องการ
เว้นแต่เส้นทางการตั้งค่าเฉพาะจะได้รับรีจิสทรีแมนิเฟสต์อย่างชัดเจน; ให้คง
สิ่งนี้เป็นทางเลือกสำรองของโคลด์พาธ แทนการเพิ่มแคชการค้นหาที่ซ่อนอยู่ เมื่อ
อินพุตเปลี่ยน ให้สร้างและแทนที่สแนปช็อตใหม่ แทนการแก้ไขสแนปช็อตเดิมหรือ
เก็บสำเนาย้อนหลัง วิวเหนือรีจิสทรี Plugin ที่ใช้งานอยู่และตัวช่วยบูตสแตรป
ช่องทางแบบบันเดิลควรถูกคำนวณใหม่จากรีจิสทรี/รูทปัจจุบัน แมปอายุสั้น
ใช้ได้ภายในการเรียกหนึ่งครั้งเพื่อขจัดงานซ้ำหรือป้องกันการกลับเข้าใหม่; แต่ต้องไม่กลายเป็น
แคชข้อมูลเมตาระดับโพรเซส
สำหรับการโหลด Plugin ชั้นแคชถาวรคือการโหลดรันไทม์ ซึ่งอาจใช้ สถานะตัวโหลดซ้ำเมื่อมีการโหลดโค้ดหรืออาร์ติแฟกต์ที่ติดตั้งจริง เช่น:
PluginLoaderCacheStateและรีจิสทรีรันไทม์ที่ใช้งานอยู่ซึ่งเข้ากันได้- แคช jiti/โมดูลและแคชตัวโหลดพื้นผิวสาธารณะที่ใช้เพื่อหลีกเลี่ยงการนำเข้า พื้นผิวรันไทม์เดียวกันซ้ำ
- แคชระบบไฟล์สำหรับอาร์ติแฟกต์ Plugin ที่ติดตั้ง
- แมปอายุสั้นต่อการเรียกสำหรับการปรับพาธเป็นมาตรฐานหรือการแก้ไขรายการซ้ำ
แคชเหล่านั้นเป็นรายละเอียดการติดตั้งของระนาบข้อมูล และต้องไม่ตอบ คำถามของระนาบควบคุม เช่น "Plugin ใดเป็นเจ้าของผู้ให้บริการนี้" เว้นแต่ ผู้เรียกจะร้องขอการโหลดรันไทม์โดยเจตนา
ห้ามเพิ่มแคชแบบถาวรหรือตามช่วงเวลานาฬิกาสำหรับ:
- ผลการค้นหา
- รีจิสทรีแมนิเฟสต์โดยตรง
- รีจิสทรีแมนิเฟสต์ที่สร้างใหม่จากดัชนี Plugin ที่ติดตั้ง
- การค้นหาเจ้าของผู้ให้บริการ การระงับโมเดล นโยบายผู้ให้บริการ หรือข้อมูลเมตา ของอาร์ติแฟกต์สาธารณะ
- คำตอบอื่นใดที่ได้จากแมนิเฟสต์ ซึ่งควรเห็นแมนิเฟสต์ ดัชนีที่ติดตั้ง หรือพาธโหลดที่เปลี่ยนแปลงในการอ่านข้อมูลเมตาครั้งถัดไป
ผู้เรียกที่สร้างข้อมูลเมตาแมนิเฟสต์ใหม่จากดัชนี Plugin ที่ติดตั้งและจัดเก็บถาวร จะสร้างรีจิสทรีนั้นใหม่ตามต้องการ ดัชนีที่ติดตั้งเป็นสถานะระนาบต้นทาง แบบคงทน; ไม่ใช่แคชข้อมูลเมตาภายในโพรเซสที่ซ่อนอยู่
โมเดลรีจิสทรี
Plugin ที่โหลดแล้วจะไม่แก้ไขโกลบอลของคอร์แบบสุ่มโดยตรง แต่จะลงทะเบียนเข้าสู่
รีจิสทรี Plugin ส่วนกลาง (PluginRegistry ใน src/plugins/registry-types.ts)
ซึ่งติดตามระเบียน Plugin (ข้อมูลระบุตัวตน แหล่งที่มา ต้นทาง สถานะ ข้อมูลวินิจฉัย)
พร้อมอาร์เรย์สำหรับทุกความสามารถ ได้แก่ เครื่องมือ ฮุกแบบเดิมและฮุกที่มีชนิด
ช่องทาง ผู้ให้บริการ ตัวจัดการ RPC ของ Gateway เส้นทาง HTTP ตัวลงทะเบียน CLI
บริการเบื้องหลัง คำสั่งที่ Plugin เป็นเจ้าของ และกลุ่มผู้ให้บริการที่มีชนิดอีกหลายสิบประเภท
(เสียงพูด การฝังเวกเตอร์ การสร้างภาพ/วิดีโอ/เพลง การดึงข้อมูล/ค้นหาเว็บ
ชุดทดสอบตัวแทน การดำเนินการกับเซสชัน และอื่น ๆ)
จากนั้นฟีเจอร์ของคอร์จะอ่านจากรีจิสทรีนั้น แทนการสื่อสารกับโมดูล Plugin โดยตรง ซึ่งทำให้การโหลดเป็นทิศทางเดียว:
- โมดูล Plugin -> การลงทะเบียนรีจิสทรี
- รันไทม์ของคอร์ -> การใช้รีจิสทรี
การแยกนี้สำคัญต่อความสะดวกในการบำรุงรักษา เพราะหมายความว่าพื้นผิวคอร์ส่วนใหญ่ ต้องการจุดผสานรวมเพียงจุดเดียว: "อ่านรีจิสทรี" ไม่ใช่ "จัดการโมดูล Plugin แต่ละรายการเป็นกรณีพิเศษ"
คอลแบ็กการผูกการสนทนา
Plugin ที่ผูกการสนทนาสามารถตอบสนองเมื่อการอนุมัติได้รับการตัดสินแล้ว
ใช้ api.onConversationBindingResolved(...) เพื่อรับคอลแบ็กหลังคำขอผูก
ได้รับการอนุมัติหรือปฏิเสธ:
export default { id: "my-plugin", register(api) { api.onConversationBindingResolved(async (event) => { if (event.status === "approved") { // ขณะนี้มีการผูกสำหรับ Plugin นี้ + การสนทนาแล้ว console.log(event.binding?.conversationId); return; } // คำขอถูกปฏิเสธ; ล้างสถานะรอดำเนินการในเครื่อง console.log(event.request.conversation.conversationId); }); },};ฟิลด์เพย์โหลดของคอลแบ็ก:
status:"approved"หรือ"denied"decision:"allow-once","allow-always"หรือ"deny"binding: การผูกที่แก้ไขแล้วสำหรับคำขอที่ได้รับอนุมัติrequest: สรุปคำขอเดิม คำใบ้การยกเลิกการเชื่อมโยง id ผู้ส่ง และ ข้อมูลเมตาการสนทนา
คอลแบ็กนี้ใช้สำหรับการแจ้งเตือนเท่านั้น ไม่ได้เปลี่ยนว่าใครได้รับอนุญาตให้ผูก การสนทนา และจะทำงานหลังการจัดการการอนุมัติของคอร์เสร็จสิ้น
ฮุกรันไทม์ของผู้ให้บริการ
Plugin ผู้ให้บริการมีสามชั้น:
- ข้อมูลเมตาแมนิเฟสต์ สำหรับการค้นหาก่อนรันไทม์ที่มีต้นทุนต่ำ:
setup.providers[].envVars,providerAuthAliases,providerAuthChoicesและchannelConfigs - ฮุกในช่วงกำหนดค่า:
catalogรวมถึงapplyConfigDefaults - ฮุกขณะรัน: ฮุกเสริมมากกว่า 40 รายการ ครอบคลุมการยืนยันตัวตน การแก้ไขโมเดล การห่อสตรีม ระดับการคิด นโยบายการเล่นซ้ำ และเอนด์พอยต์การใช้งาน โปรดดู ลำดับฮุกและการใช้งาน
OpenClaw ยังคงเป็นเจ้าของลูปเอเจนต์ทั่วไป การสลับไปใช้ระบบสำรอง การจัดการทรานสคริปต์ และ นโยบายเครื่องมือ ฮุกเหล่านี้เป็นพื้นผิวส่วนขยายสำหรับพฤติกรรมเฉพาะของผู้ให้บริการ โดยไม่จำเป็นต้องใช้การขนส่งการอนุมานแบบกำหนดเองทั้งหมด
ใช้ setup.providers[].envVars ของ manifest เมื่อผู้ให้บริการมี
ข้อมูลประจำตัวที่อิงตามตัวแปรสภาพแวดล้อม ซึ่งเส้นทางทั่วไปสำหรับการยืนยันตัวตน/สถานะ/ตัวเลือกโมเดลควรมองเห็นได้โดยไม่ต้อง
โหลดรันไทม์ของ Plugin ใช้ providerAuthAliases ของ manifest
เมื่อรหัสผู้ให้บริการหนึ่งควรใช้ตัวแปรสภาพแวดล้อม โปรไฟล์การยืนยันตัวตน
การยืนยันตัวตนจากการกำหนดค่า และตัวเลือกการเริ่มต้นใช้งานด้วยคีย์ API ของรหัสผู้ให้บริการอื่นซ้ำ ใช้
providerAuthChoices ของ manifest เมื่อพื้นผิว CLI สำหรับการเริ่มต้นใช้งาน/การเลือกวิธียืนยันตัวตนควรทราบ
รหัสตัวเลือก ป้ายกำกับกลุ่ม และการเชื่อมต่อการยืนยันตัวตนแบบแฟล็กเดียวอย่างง่ายของผู้ให้บริการ โดยไม่ต้อง
โหลดรันไทม์ของผู้ให้บริการ เก็บ envVars ของรันไทม์ผู้ให้บริการ
ไว้สำหรับคำแนะนำที่แสดงต่อผู้ปฏิบัติงาน เช่น ป้ายกำกับการเริ่มต้นใช้งาน หรือตัวแปรการตั้งค่า
รหัสไคลเอนต์/ข้อมูลลับไคลเอนต์ OAuth
อธิบายการตั้งค่าช่องทางและการยืนยันตัวตนที่ขับเคลื่อนด้วยตัวแปรสภาพแวดล้อมผ่าน
channelConfigs.<id>.schema และตัวอธิบายการตั้งค่าที่เป็นเจ้าของส่วนดังกล่าว
ลำดับและการใช้งานฮุก
สำหรับ Plugin โมเดล/ผู้ให้บริการ OpenClaw จะเรียกฮุกตามลำดับคร่าว ๆ ดังนี้
คอลัมน์ "ควรใช้เมื่อใด" เป็นคู่มือประกอบการตัดสินใจฉบับย่อ
ฟิลด์ผู้ให้บริการที่มีไว้เพื่อความเข้ากันได้เท่านั้นและ OpenClaw ไม่ได้เรียกใช้อีกต่อไป เช่น
ProviderPlugin.capabilities และ suppressBuiltInModel จะไม่แสดงไว้
ที่นี่โดยเจตนา
| Hook | หน้าที่ | ควรใช้เมื่อ |
|---|---|---|
catalog |
เผยแพร่การกำหนดค่าผู้ให้บริการไปยัง models.providers ระหว่างการสร้าง models.json |
ผู้ให้บริการเป็นเจ้าของแค็ตตาล็อกหรือค่าเริ่มต้นของ URL ฐาน |
applyConfigDefaults |
ใช้ค่าเริ่มต้นของการกำหนดค่าส่วนกลางที่ผู้ให้บริการเป็นเจ้าของระหว่างการทำให้การกำหนดค่าเป็นรูปธรรม | ค่าเริ่มต้นขึ้นอยู่กับโหมดการตรวจสอบสิทธิ์ สภาพแวดล้อม หรือความหมายของตระกูลโมเดลของผู้ให้บริการ |
| (การค้นหาโมเดลในตัว) | OpenClaw ลองใช้เส้นทางรีจิสทรี/แค็ตตาล็อกตามปกติก่อน | (ไม่ใช่ hook ของ plugin) |
normalizeModelId |
ปรับนามแฝงรหัสโมเดลแบบเดิมหรือแบบพรีวิวให้เป็นมาตรฐานก่อนค้นหา | ผู้ให้บริการเป็นเจ้าของการล้างนามแฝงก่อนการแก้ไขโมเดลเป็นรูปแบบมาตรฐาน |
normalizeTransport |
ปรับ api / baseUrl ของตระกูลผู้ให้บริการให้เป็นมาตรฐานก่อนการประกอบโมเดลทั่วไป |
ผู้ให้บริการเป็นเจ้าของการล้างข้อมูลการขนส่งสำหรับรหัสผู้ให้บริการแบบกำหนดเองในตระกูลการขนส่งเดียวกัน |
normalizeConfig |
ปรับ models.providers.<id> ให้เป็นมาตรฐานก่อนการแก้ไขรันไทม์/ผู้ให้บริการ |
ผู้ให้บริการต้องการการล้างการกำหนดค่าที่ควรอยู่กับ plugin โดยตัวช่วยตระกูล Google ที่รวมมาด้วยยังช่วยรองรับรายการกำหนดค่า Google ที่รองรับ |
applyNativeStreamingUsageCompat |
ใช้การเขียนการกำหนดค่าใหม่เพื่อความเข้ากันได้กับการใช้งานสตรีมมิงแบบเนทีฟกับผู้ให้บริการในการกำหนดค่า | ผู้ให้บริการต้องการแก้ไขข้อมูลเมตาการใช้งานสตรีมมิงแบบเนทีฟที่ขับเคลื่อนโดย endpoint |
resolveConfigApiKey |
แก้ไขการตรวจสอบสิทธิ์ด้วยเครื่องหมายสภาพแวดล้อมสำหรับผู้ให้บริการในการกำหนดค่าก่อนโหลดการตรวจสอบสิทธิ์ของรันไทม์ | ผู้ให้บริการเปิดเผย hook ของตนเองสำหรับแก้ไข API key จากเครื่องหมายสภาพแวดล้อม |
resolveSyntheticAuth |
แสดงการตรวจสอบสิทธิ์แบบภายในเครื่อง/โฮสต์เองหรือที่อิงการกำหนดค่าโดยไม่จัดเก็บข้อความธรรมดา | ผู้ให้บริการสามารถทำงานด้วยเครื่องหมายข้อมูลประจำตัวแบบสังเคราะห์/ภายในเครื่อง |
resolveExternalAuthProfiles |
ซ้อนทับโปรไฟล์การตรวจสอบสิทธิ์ภายนอกที่ผู้ให้บริการเป็นเจ้าของ โดยค่าเริ่มต้น persistence คือ runtime-only สำหรับข้อมูลประจำตัวที่ CLI/แอปเป็นเจ้าของ |
ผู้ให้บริการนำข้อมูลประจำตัวการตรวจสอบสิทธิ์ภายนอกมาใช้ซ้ำโดยไม่จัดเก็บ refresh token ที่คัดลอกมา ให้ประกาศ contracts.externalAuthProviders ใน manifest |
shouldDeferSyntheticProfileAuth |
ลดลำดับความสำคัญของ placeholder โปรไฟล์สังเคราะห์ที่จัดเก็บไว้ภายใต้การตรวจสอบสิทธิ์ที่อิงสภาพแวดล้อม/การกำหนดค่า | ผู้ให้บริการจัดเก็บโปรไฟล์ placeholder สังเคราะห์ที่ไม่ควรมีลำดับความสำคัญเหนือกว่า |
resolveDynamicModel |
การสำรองแบบซิงโครนัสสำหรับรหัสโมเดลที่ผู้ให้บริการเป็นเจ้าของซึ่งยังไม่มีในรีจิสทรีภายในเครื่อง | ผู้ให้บริการยอมรับรหัสโมเดล upstream ใดก็ได้ |
prepareDynamicModel |
อุ่นเครื่องแบบอะซิงโครนัส จากนั้น resolveDynamicModel จะทำงานอีกครั้ง |
ผู้ให้บริการต้องการข้อมูลเมตาจากเครือข่ายก่อนแก้ไขรหัสที่ไม่รู้จัก |
normalizeResolvedModel |
เขียนใหม่เป็นครั้งสุดท้ายก่อนที่ runner แบบฝังตัวจะใช้โมเดลที่แก้ไขแล้ว | ผู้ให้บริการต้องการเขียนการขนส่งใหม่แต่ยังคงใช้การขนส่งหลัก |
normalizeToolSchemas |
ปรับ schema ของเครื่องมือให้เป็นมาตรฐานก่อนที่ runner แบบฝังตัวจะเห็น | ผู้ให้บริการต้องการล้าง schema ของตระกูลการขนส่ง |
inspectToolSchemas |
แสดงการวินิจฉัย schema ที่ผู้ให้บริการเป็นเจ้าของหลังการปรับให้เป็นมาตรฐาน | ผู้ให้บริการต้องการคำเตือนเกี่ยวกับ keyword โดยไม่ต้องสอนกฎเฉพาะผู้ให้บริการให้กับแกนหลัก |
resolveReasoningOutputMode |
เลือกสัญญาเอาต์พุตการให้เหตุผลแบบเนทีฟหรือแบบมีแท็ก | ผู้ให้บริการต้องการเอาต์พุตการให้เหตุผล/ขั้นสุดท้ายแบบมีแท็กแทนฟิลด์แบบเนทีฟ |
prepareExtraParams |
ปรับพารามิเตอร์คำขอให้เป็นมาตรฐานก่อน wrapper ตัวเลือกสตรีมทั่วไป | ผู้ให้บริการต้องการพารามิเตอร์คำขอเริ่มต้นหรือการล้างพารามิเตอร์รายผู้ให้บริการ |
createStreamFn |
แทนที่เส้นทางสตรีมปกติทั้งหมดด้วยการขนส่งแบบกำหนดเอง | ผู้ให้บริการต้องการ wire protocol แบบกำหนดเอง ไม่ใช่เพียง wrapper |
wrapStreamFn |
wrapper สตรีมหลังจากใช้ wrapper ทั่วไปแล้ว | ผู้ให้บริการต้องการ wrapper ความเข้ากันได้สำหรับส่วนหัว/เนื้อหาคำขอ/โมเดลโดยไม่ใช้การขนส่งแบบกำหนดเอง |
resolveTransportTurnState |
แนบส่วนหัวหรือข้อมูลเมตาการขนส่งแบบเนทีฟต่อ turn | ผู้ให้บริการต้องการให้การขนส่งทั่วไปส่งข้อมูลระบุ turn แบบเนทีฟของผู้ให้บริการ |
resolveWebSocketSessionPolicy |
แนบส่วนหัว WebSocket แบบเนทีฟหรือนโยบายช่วงพักของเซสชัน | ผู้ให้บริการต้องการให้การขนส่ง WS ทั่วไปปรับส่วนหัวเซสชันหรือนโยบายสำรอง |
formatApiKey |
ตัวจัดรูปแบบโปรไฟล์การตรวจสอบสิทธิ์: โปรไฟล์ที่จัดเก็บไว้จะกลายเป็นสตริง apiKey ของรันไทม์ |
ผู้ให้บริการจัดเก็บข้อมูลเมตาการตรวจสอบสิทธิ์เพิ่มเติมและต้องการรูปแบบ token ของรันไทม์ที่กำหนดเอง |
refreshOAuth |
แทนที่การรีเฟรช OAuth สำหรับ endpoint การรีเฟรชแบบกำหนดเองหรือนโยบายเมื่อรีเฟรชล้มเหลว | ผู้ให้บริการไม่เข้ากับตัวรีเฟรช OpenClaw ที่ใช้ร่วมกัน |
buildAuthDoctorHint |
คำแนะนำการซ่อมแซมที่เพิ่มต่อท้ายเมื่อการรีเฟรช OAuth ล้มเหลว | ผู้ให้บริการต้องการคำแนะนำการซ่อมแซมการตรวจสอบสิทธิ์ที่ผู้ให้บริการเป็นเจ้าของหลังการรีเฟรชล้มเหลว |
matchesContextOverflowError |
ตัวจับคู่การล้นของหน้าต่างบริบทที่ผู้ให้บริการเป็นเจ้าของ | ผู้ให้บริการมีข้อผิดพลาดการล้นแบบดิบที่ heuristic ทั่วไปตรวจไม่พบ |
classifyFailoverReason |
การจำแนกเหตุผลการ failover ที่ผู้ให้บริการเป็นเจ้าของ | ผู้ให้บริการสามารถแมปข้อผิดพลาด API/การขนส่งแบบดิบเป็นการจำกัดอัตรา/โหลดเกิน/อื่นๆ |
isCacheTtlEligible |
นโยบายแคชพรอมต์สำหรับผู้ให้บริการพร็อกซี/backhaul | ผู้ให้บริการต้องการการควบคุม TTL ของแคชที่เฉพาะกับพร็อกซี |
buildMissingAuthMessage |
ข้อความทดแทนสำหรับข้อความกู้คืนเมื่อขาดการตรวจสอบสิทธิ์แบบทั่วไป | ผู้ให้บริการต้องการคำแนะนำการกู้คืนเมื่อขาดการตรวจสอบสิทธิ์ที่เฉพาะกับผู้ให้บริการ |
augmentModelCatalog |
แถวแค็ตตาล็อกสังเคราะห์/ขั้นสุดท้ายที่เพิ่มหลังการค้นพบ (เลิกใช้แล้ว ดูด้านล่าง) | ผู้ให้บริการต้องการแถวสังเคราะห์เพื่อความเข้ากันได้ในอนาคตใน models list และตัวเลือก |
resolveThinkingProfile |
ชุดระดับ /think เฉพาะโมเดล ป้ายกำกับที่แสดง และค่าเริ่มต้น |
ผู้ให้บริการเปิดเผยลำดับขั้นการคิดแบบกำหนดเองหรือป้ายกำกับไบนารีสำหรับโมเดลที่เลือก |
isBinaryThinking |
hook ความเข้ากันได้สำหรับเปิด/ปิดการให้เหตุผล | ผู้ให้บริการเปิดเผยเฉพาะการเปิด/ปิดการคิดแบบไบนารี |
supportsXHighThinking |
hook ความเข้ากันได้สำหรับการรองรับการให้เหตุผล xhigh |
ผู้ให้บริการต้องการ xhigh เฉพาะในโมเดลบางส่วน |
resolveDefaultThinkingLevel |
hook ความเข้ากันได้สำหรับระดับ /think เริ่มต้น |
ผู้ให้บริการเป็นเจ้าของนโยบาย /think เริ่มต้นสำหรับตระกูลโมเดล |
isModernModelRef |
ตัวจับคู่โมเดลสมัยใหม่สำหรับตัวกรองโปรไฟล์แบบสดและการเลือก smoke | ผู้ให้บริการเป็นเจ้าของการจับคู่โมเดลที่ต้องการสำหรับแบบสด/smoke |
prepareRuntimeAuth |
แลกเปลี่ยนข้อมูลประจำตัวที่กำหนดค่าไว้เป็น token/key ของรันไทม์จริงก่อนการอนุมาน | ผู้ให้บริการต้องการการแลกเปลี่ยน token หรือข้อมูลประจำตัวคำขออายุสั้น |
resolveUsageAuth |
แก้ไขข้อมูลประจำตัวการใช้งาน/การเรียกเก็บเงินสำหรับ /usage และพื้นผิวสถานะที่เกี่ยวข้อง |
ผู้ให้บริการต้องการการแยกวิเคราะห์ token การใช้งาน/โควตาแบบกำหนดเองหรือข้อมูลประจำตัวการใช้งานที่แตกต่าง |
fetchUsageSnapshot |
ดึงและปรับสแนปช็อตการใช้งาน/โควตาเฉพาะผู้ให้บริการให้เป็นมาตรฐานหลังจากแก้ไขการตรวจสอบสิทธิ์แล้ว | ผู้ให้บริการต้องการ endpoint การใช้งานหรือตัวแยกวิเคราะห์ payload ที่เฉพาะกับผู้ให้บริการ |
createEmbeddingProvider |
สร้างอะแดปเตอร์ embedding ที่ผู้ให้บริการเป็นเจ้าของสำหรับหน่วยความจำ/การค้นหา | ลักษณะการทำงานของ embedding สำหรับหน่วยความจำควรอยู่ใน Plugin ของผู้ให้บริการ |
buildReplayPolicy |
ส่งคืนนโยบายการเล่นซ้ำที่ควบคุมการจัดการทรานสคริปต์สำหรับผู้ให้บริการ | ผู้ให้บริการต้องใช้นโยบายทรานสคริปต์แบบกำหนดเอง (เช่น การลบบล็อกการคิด) |
sanitizeReplayHistory |
เขียนประวัติการเล่นซ้ำใหม่หลังจากล้างทรานสคริปต์แบบทั่วไป | ผู้ให้บริการต้องเขียนข้อมูลการเล่นซ้ำใหม่เฉพาะสำหรับผู้ให้บริการ นอกเหนือจากตัวช่วย Compaction ที่ใช้ร่วมกัน |
validateReplayTurns |
ตรวจสอบความถูกต้องหรือปรับโครงสร้างเทิร์นการเล่นซ้ำขั้นสุดท้ายก่อนส่งไปยังรันเนอร์แบบฝัง | การขนส่งของผู้ให้บริการต้องตรวจสอบความถูกต้องของเทิร์นอย่างเข้มงวดยิ่งขึ้นหลังจากการล้างข้อมูลแบบทั่วไป |
onModelSelected |
เรียกใช้ผลข้างเคียงหลังการเลือกที่ผู้ให้บริการเป็นเจ้าของ | ผู้ให้บริการต้องใช้เทเลเมทรีหรือสถานะที่ผู้ให้บริการเป็นเจ้าของเมื่อโมเดลเริ่มทำงาน |
normalizeModelId, normalizeTransport และ normalizeConfig จะตรวจสอบ Plugin ของผู้ให้บริการที่ตรงกันก่อน จากนั้นจึงไล่ตรวจสอบ Plugin ของผู้ให้บริการรายอื่นที่รองรับ hook จนกว่าจะพบ Plugin ที่เปลี่ยนรหัสโมเดลหรือ transport/config จริง วิธีนี้ช่วยให้ shim ของผู้ให้บริการสำหรับ alias/compat ยังคงทำงานได้ โดยผู้เรียกไม่จำเป็นต้องทราบว่า Plugin ที่รวมมาด้วยตัวใดเป็นเจ้าของการเขียนค่าใหม่ หากไม่มี hook ของผู้ให้บริการใดเขียนรายการการกำหนดค่าตระกูล Google ที่รองรับใหม่ ตัวปรับการกำหนดค่า Google ที่รวมมาด้วยจะยังคงดำเนินการล้างข้อมูลเพื่อความเข้ากันได้นั้น
หากผู้ให้บริการต้องใช้โพรโทคอลรับส่งข้อมูลแบบกำหนดเองทั้งหมดหรือตัวดำเนินการคำขอแบบกำหนดเอง นั่นถือเป็นส่วนขยายคนละประเภท hook เหล่านี้มีไว้สำหรับพฤติกรรมของผู้ให้บริการที่ยังคงทำงานบนลูปการอนุมานตามปกติของ OpenClaw
resolveUsageAuth ใช้ตัดสินใจว่า OpenClaw ควรเรียก fetchUsageSnapshot หรือย้อนกลับไปใช้การแก้ไขข้อมูลประจำตัวแบบทั่วไปสำหรับส่วนแสดงการใช้งาน/สถานะ ให้คืนค่า { token, accountId?, subscriptionType?, rateLimitTier? } เมื่อผู้ให้บริการมีข้อมูลประจำตัวสำหรับการใช้งาน (ข้อมูลเมตาของแผนซึ่งเป็นตัวเลือกจะถูกส่งต่อไปยัง fetchUsageSnapshot) ให้คืนค่า { handled: true } เมื่อการยืนยันตัวตนสำหรับการใช้งานที่ผู้ให้บริการเป็นเจ้าของได้จัดการคำขอแล้วและต้องระงับการย้อนกลับไปใช้คีย์ API/OAuth แบบทั่วไป และให้คืนค่า null หรือ undefined เมื่อผู้ให้บริการไม่ได้จัดการการยืนยันตัวตนสำหรับการใช้งาน
ประกาศข้อมูลประจำตัวขององค์กรหรือการเรียกเก็บเงินใน manifest providerUsageAuthEnvVars การทำเช่นนี้ช่วยให้ส่วนการค้นหาและการลบข้อมูลลับแบบทั่วไปสามารถจดจำข้อมูลเหล่านั้นได้ โดยไม่ทำให้ข้อมูลเหล่านั้นเป็นตัวเลือกสำหรับการยืนยันตัวตนเพื่อการอนุมาน
ตัวอย่างผู้ให้บริการ
api.registerProvider({ id: "example-proxy", label: "พร็อกซีตัวอย่าง", auth: [], catalog: { order: "simple", run: async (ctx) => { const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey; if (!apiKey) { return null; } return { provider: { baseUrl: "https://proxy.example.com/v1", apiKey, api: "openai-completions", models: [{ id: "auto", name: "อัตโนมัติ" }], }, }; }, }, resolveDynamicModel: (ctx) => ({ id: ctx.modelId, name: ctx.modelId, provider: "example-proxy", api: "openai-completions", baseUrl: "https://proxy.example.com/v1", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128000, maxTokens: 8192, }), prepareRuntimeAuth: async (ctx) => { const exchanged = await exchangeToken(ctx.apiKey); return { apiKey: exchanged.token, baseUrl: exchanged.baseUrl, expiresAt: exchanged.expiresAt, }; }, resolveUsageAuth: async (ctx) => { const auth = await ctx.resolveOAuthToken(); return auth ? { token: auth.token } : null; }, fetchUsageSnapshot: async (ctx) => { return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn); },});ตัวอย่างที่มีมาให้ในตัว
Plugin ของผู้ให้บริการที่รวมมาด้วยจะผสาน hook ข้างต้นให้เหมาะกับแค็ตตาล็อก การยืนยันตัวตน การคิด การเล่นซ้ำ และความต้องการด้านการใช้งานของผู้จำหน่ายแต่ละราย ชุด hook ที่เป็นแหล่งอ้างอิงหลักอยู่กับแต่ละ Plugin ภายใต้ extensions/; หน้านี้แสดงรูปแบบต่าง ๆ แทนการทำสำเนารายการดังกล่าว
ผู้ให้บริการแค็ตตาล็อกแบบส่งผ่าน
OpenRouter, Kilocode, Z.AI และ xAI ลงทะเบียน catalog ร่วมกับ
resolveDynamicModel / prepareDynamicModel เพื่อให้สามารถแสดงรหัสโมเดลจากต้นทางก่อนแค็ตตาล็อกแบบคงที่ของ OpenClaw
ผู้ให้บริการปลายทาง OAuth และข้อมูลการใช้งาน
GitHub Copilot, Gemini CLI, ChatGPT Codex, MiniMax, Xiaomi และ z.ai จับคู่
prepareRuntimeAuth หรือ formatApiKey กับ resolveUsageAuth +
fetchUsageSnapshot เพื่อเป็นเจ้าของการแลกเปลี่ยนโทเค็นและการผสานรวม /usage
ตระกูลการเล่นซ้ำและการล้างทรานสคริปต์
ตระกูลที่มีชื่อร่วมกัน (google-gemini, passthrough-gemini,
anthropic-by-model, hybrid-anthropic-openai) ช่วยให้ผู้ให้บริการเลือกใช้นโยบายทรานสคริปต์ผ่าน buildReplayPolicy แทนที่แต่ละ Plugin จะนำกระบวนการล้างข้อมูลไปใช้งานซ้ำเอง
ผู้ให้บริการเฉพาะแค็ตตาล็อก
byteplus, cloudflare-ai-gateway, huggingface, kimi-coding, nvidia,
qianfan, synthetic, together, venice, vercel-ai-gateway และ
volcengine ลงทะเบียนเพียง catalog และใช้ลูปการอนุมานร่วมกัน
ตัวช่วยสตรีมเฉพาะ Anthropic
ส่วนหัวเบตา, /fast / serviceTier และ context1m อยู่ภายในจุดเชื่อมต่อ api.ts / contract-api.ts แบบสาธารณะของ Plugin Anthropic
(wrapAnthropicProviderStream, resolveAnthropicBetas,
resolveAnthropicFastMode, resolveAnthropicServiceTier) แทนที่จะอยู่ใน SDK แบบทั่วไป
ตัวช่วยรันไทม์
Plugin สามารถเข้าถึงตัวช่วยหลักที่เลือกไว้ผ่าน api.runtime สำหรับ TTS:
const clip = await api.runtime.tts.textToSpeech({ text: "สวัสดีจาก OpenClaw", cfg: api.config,}); const result = await api.runtime.tts.textToSpeechTelephony({ text: "สวัสดีจาก OpenClaw", cfg: api.config,}); const voices = await api.runtime.tts.listVoices({ provider: "elevenlabs", cfg: api.config,});หมายเหตุ:
textToSpeechคืนเพย์โหลดเอาต์พุต TTS หลักตามปกติสำหรับส่วนไฟล์/บันทึกเสียง- ใช้การกำหนดค่า
messages.ttsและการเลือกผู้ให้บริการจากส่วนหลัก - คืนบัฟเฟอร์เสียง PCM พร้อมอัตราการสุ่มตัวอย่าง Plugin ต้องปรับอัตราการสุ่มตัวอย่าง/เข้ารหัสให้เหมาะกับผู้ให้บริการ
listVoicesเป็นตัวเลือกสำหรับผู้ให้บริการแต่ละราย ใช้สำหรับตัวเลือกเสียงหรือขั้นตอนการตั้งค่าที่ผู้จำหน่ายเป็นเจ้าของ- ส่วนหลักจะส่งกำหนดเวลาสิ้นสุดของคำขอที่แก้ไขแล้วไปยัง hook
listVoicesของผู้ให้บริการ โดยการตั้งค่าระยะหมดเวลาเฉพาะผู้ให้บริการอาจเขียนทับค่านี้ - รายการเสียงสามารถมีข้อมูลเมตาที่ละเอียดยิ่งขึ้น เช่น โลเคล เพศ และแท็กบุคลิกภาพ สำหรับตัวเลือกที่รับรู้ผู้ให้บริการ
- ปัจจุบัน OpenAI และ ElevenLabs รองรับระบบโทรศัพท์ Microsoft ไม่รองรับ
Plugin ยังสามารถลงทะเบียนผู้ให้บริการเสียงพูดผ่าน api.registerSpeechProvider(...) ได้ด้วย
api.registerSpeechProvider({ id: "acme-speech", label: "เสียงพูด Acme", isConfigured: ({ config }) => Boolean(config.messages?.tts), synthesize: async (req) => { return { audioBuffer: Buffer.from([]), outputFormat: "mp3", fileExtension: ".mp3", voiceCompatible: false, }; },});หมายเหตุ:
- เก็บนโยบาย TTS การย้อนกลับ และการส่งคำตอบไว้ในส่วนหลัก
- ใช้ผู้ให้บริการเสียงพูดสำหรับพฤติกรรมการสังเคราะห์ที่ผู้จำหน่ายเป็นเจ้าของ
- อินพุต Microsoft แบบเดิม
edgeจะถูกปรับให้เป็นรหัสผู้ให้บริการmicrosoft - รูปแบบความเป็นเจ้าของที่แนะนำจะยึดตามบริษัท โดย Plugin ของผู้จำหน่ายหนึ่งรายสามารถเป็นเจ้าของผู้ให้บริการข้อความ เสียงพูด รูปภาพ และสื่อในอนาคตได้ เมื่อ OpenClaw เพิ่มสัญญาความสามารถเหล่านั้น
สำหรับการทำความเข้าใจรูปภาพ/เสียง/วิดีโอ Plugin จะลงทะเบียนผู้ให้บริการทำความเข้าใจสื่อแบบมีชนิดหนึ่งราย แทนการใช้ชุดคีย์/ค่าแบบทั่วไป:
api.registerMediaUnderstandingProvider({ id: "google", capabilities: ["image", "audio", "video"], describeImage: async (req) => ({ text: "..." }), transcribeAudio: async (req) => ({ text: "..." }), describeVideo: async (req) => ({ text: "..." }),});หมายเหตุ:
- เก็บการประสานงาน การย้อนกลับ การกำหนดค่า และการเชื่อมต่อช่องทางไว้ในส่วนหลัก
- เก็บพฤติกรรมของผู้จำหน่ายไว้ใน Plugin ของผู้ให้บริการ
- การขยายแบบเพิ่มส่วนควรคงการระบุชนิดไว้ ได้แก่ เมธอดตัวเลือกใหม่ ฟิลด์ผลลัพธ์ตัวเลือกใหม่ และความสามารถตัวเลือกใหม่
- การสร้างวิดีโอใช้รูปแบบเดียวกันอยู่แล้ว:
- ส่วนหลักเป็นเจ้าของสัญญาความสามารถและตัวช่วยรันไทม์
- Plugin ของผู้จำหน่ายลงทะเบียน
api.registerVideoGenerationProvider(...) - Plugin ฟีเจอร์/ช่องทางใช้
api.runtime.videoGeneration.*
สำหรับตัวช่วยรันไทม์เพื่อทำความเข้าใจสื่อ Plugin สามารถเรียกใช้:
const image = await api.runtime.mediaUnderstanding.describeImageFile({ filePath: "/tmp/inbound-photo.jpg", cfg: api.config, agentDir: "/tmp/agent",}); const video = await api.runtime.mediaUnderstanding.describeVideoFile({ filePath: "/tmp/inbound-video.mp4", cfg: api.config,}); const extraction = await api.runtime.mediaUnderstanding.extractStructuredWithModel({ provider: "codex", model: "gpt-5.6-sol", input: [ { type: "image", buffer: receiptImageBuffer, fileName: "receipt.png", mime: "image/png", }, { type: "text", text: "ใช้ฟิลด์ที่พิมพ์ไว้เป็นแหล่งข้อมูลที่เชื่อถือได้" }, ], instructions: "คืนเอนทิตีและแท็กที่ค้นหาได้", schemaName: "example.evidence", jsonSchema: { type: "object", properties: { entities: { type: "array", items: { type: "string" } }, tags: { type: "array", items: { type: "string" } }, }, }, cfg: api.config,});สำหรับการถอดเสียง Plugin สามารถใช้รันไทม์การทำความเข้าใจสื่อหรือ alias STT รุ่นเก่าก็ได้:
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ filePath: "/tmp/inbound-audio.ogg", cfg: api.config, // เป็นตัวเลือกเมื่อไม่สามารถอนุมาน MIME ได้อย่างน่าเชื่อถือ: mime: "audio/ogg",});หมายเหตุ:
api.runtime.mediaUnderstanding.*เป็นส่วนร่วมที่แนะนำสำหรับการทำความเข้าใจรูปภาพ/เสียง/วิดีโอextractStructuredWithModel(...)เป็นจุดเชื่อมต่อสำหรับ Plugin เพื่อการแยกข้อมูลแบบเน้นรูปภาพเป็นหลัก ภายในขอบเขตที่กำหนด และผู้ให้บริการเป็นเจ้าของ ต้องมีอินพุตรูปภาพอย่างน้อยหนึ่งรายการ ส่วนอินพุตข้อความเป็นบริบทเสริม Plugin ผลิตภัณฑ์เป็นเจ้าของเส้นทางและสคีมาของตน ขณะที่ OpenClaw เป็นเจ้าของขอบเขตผู้ให้บริการ/รันไทม์- ใช้การกำหนดค่าเสียงสำหรับการทำความเข้าใจสื่อจากส่วนหลัก (
tools.media.audio) และลำดับการย้อนกลับของผู้ให้บริการ - คืนค่า
{ text: undefined }เมื่อไม่มีเอาต์พุตการถอดเสียงเกิดขึ้น (เช่น อินพุตถูกข้าม/ไม่รองรับ)
Plugin ยังสามารถเปิดการรันเอเจนต์ย่อยเบื้องหลังผ่าน api.runtime.subagent ได้ด้วย:
const result = await api.runtime.subagent.run({ sessionKey: "agent:main:subagent:search-helper", message: "ขยายคำค้นนี้เป็นการค้นหาติดตามผลที่เจาะจง", toolsAlsoAllow: ["my_plugin_progress"], provider: "openai", model: "gpt-4.1-mini", deliver: false,});หมายเหตุ:
providerและmodelเป็นค่าที่เขียนทับได้สำหรับแต่ละการรัน ไม่ใช่การเปลี่ยนแปลงเซสชันแบบถาวรtoolsAlsoAllowรับชื่อเครื่องมือที่ตรงทั้งหมดและมีเจ้าของเพียงรายเดียว ซึ่งลงทะเบียนโดย Plugin ที่เรียกใช้ ชื่อของส่วนหลักและชื่อที่กำกวมจะถูกปฏิเสธ ค่านี้เพิ่มจากโปรไฟล์ปกติ แต่รายการอนุญาตและรายการปฏิเสธของผู้ปฏิบัติงานยังคงมีอำนาจสูงสุด- OpenClaw จะยอมรับฟิลด์เขียนทับเหล่านั้นเฉพาะจากผู้เรียกที่เชื่อถือได้
- สำหรับการรันย้อนกลับที่ Plugin เป็นเจ้าของ ผู้ปฏิบัติงานต้องเลือกใช้งานด้วย
plugins.entries.<id>.subagent.allowModelOverride: true - ใช้
plugins.entries.<id>.subagent.allowedModelsเพื่อจำกัด Plugin ที่เชื่อถือได้ให้ใช้เฉพาะเป้าหมายprovider/modelแบบมาตรฐานที่ระบุ หรือใช้"*"เพื่ออนุญาตเป้าหมายใด ๆ อย่างชัดเจน - การรันเอเจนต์ย่อยของ Plugin ที่ไม่น่าเชื่อถือยังคงทำงานได้ แต่คำขอเขียนทับจะถูกปฏิเสธแทนที่จะย้อนกลับโดยไม่แจ้ง
- เซสชันเอเจนต์ย่อยที่ Plugin สร้างจะถูกติดแท็กด้วยรหัส Plugin ผู้สร้าง
api.runtime.subagent.deleteSession(...)สำหรับการย้อนกลับสามารถลบได้เฉพาะเซสชันที่เป็นเจ้าของเหล่านั้น การลบเซสชันใด ๆ โดยพลการยังคงต้องใช้คำขอ Gateway ที่มีขอบเขตผู้ดูแลระบบ
สำหรับการค้นหาเว็บ Plugin สามารถใช้ตัวช่วยรันไทม์ร่วมกันแทนการเข้าถึงการเชื่อมต่อเครื่องมือเอเจนต์โดยตรง:
const providers = api.runtime.webSearch.listProviders({ config: api.config,}); const result = await api.runtime.webSearch.search({ config: api.config, args: { query: "ตัวช่วยรันไทม์ Plugin ของ OpenClaw", count: 5, },});Plugin ยังสามารถลงทะเบียนผู้ให้บริการค้นหาเว็บผ่าน api.registerWebSearchProvider(...) ได้ด้วย
หมายเหตุ:
- เก็บการเลือกผู้ให้บริการ การแก้ไขข้อมูลประจำตัว และความหมายร่วมของคำขอไว้ในส่วนหลัก
- ใช้ผู้ให้บริการค้นหาเว็บสำหรับ transport การค้นหาเฉพาะผู้จำหน่าย
api.runtime.webSearch.*เป็นส่วนร่วมที่แนะนำสำหรับ Plugin ฟีเจอร์/ช่องทางที่ต้องใช้พฤติกรรมการค้นหาโดยไม่ขึ้นกับ wrapper ของเครื่องมือเอเจนต์
api.runtime.imageGeneration
const result = await api.runtime.imageGeneration.generate({ config: api.config, args: { prompt: "มาสคอตกุ้งล็อบสเตอร์ที่เป็นมิตร", size: "1024x1024" },}); const providers = api.runtime.imageGeneration.listProviders({ config: api.config,});generate(...): สร้างรูปภาพโดยใช้สายโซ่ผู้ให้บริการสร้างรูปภาพที่กำหนดค่าไว้listProviders(...): แสดงรายการผู้ให้บริการสร้างรูปภาพที่พร้อมใช้งานและความสามารถของแต่ละราย
เส้นทาง HTTP ของ Gateway
Plugin สามารถเปิดเผยปลายทาง HTTP ด้วย api.registerHttpRoute(...)
api.registerHttpRoute({ path: "/acme/webhook", auth: "plugin", match: "exact", handler: async (_req, res) => { res.statusCode = 200; res.end("ok"); return true; },});ฟิลด์ของเส้นทาง:
path: พาธของเส้นทางภายใต้เซิร์ฟเวอร์ HTTP ของ Gatewayauth: จำเป็นต้องระบุ โดยเป็น"gateway"หรือ"plugin"ใช้"gateway"เพื่อกำหนดให้ใช้การยืนยันตัวตนตามปกติของ Gateway หรือใช้"plugin"สำหรับการยืนยันตัวตน/การตรวจสอบ Webhook ที่ Plugin จัดการmatch: ไม่บังคับ โดยเป็น"exact"(ค่าเริ่มต้น) หรือ"prefix"handleUpgrade: ตัวจัดการเสริมสำหรับคำขออัปเกรด WebSocket บนเส้นทางเดียวกันreplaceExisting: ไม่บังคับ อนุญาตให้ Plugin เดิมแทนที่การลงทะเบียนเส้นทางที่มีอยู่ของตนเองhandler: คืนค่าtrueเมื่อเส้นทางจัดการคำขอแล้ว
หมายเหตุ:
api.registerHttpHandler(...)ถูกนำออกแล้วและจะทำให้เกิดข้อผิดพลาดในการโหลด Plugin ให้ใช้api.registerHttpRoute(...)แทน- เส้นทางของ Plugin ต้องประกาศ
authอย่างชัดเจน - ข้อขัดแย้งของ
path + matchแบบตรงกันทุกประการจะถูกปฏิเสธ เว้นแต่มีreplaceExisting: trueและ Plugin หนึ่งไม่สามารถแทนที่เส้นทางของ Plugin อื่นได้ - เส้นทางที่ทับซ้อนกันและมีระดับ
authต่างกันจะถูกปฏิเสธ ให้ใช้สายโซ่การส่งต่อexact/prefixเฉพาะภายในระดับการยืนยันตัวตนเดียวกัน - เส้นทาง
auth: "plugin"ไม่ได้ รับขอบเขตรันไทม์ของผู้ดำเนินการโดยอัตโนมัติ เส้นทางเหล่านี้มีไว้สำหรับ Webhook/การตรวจสอบลายเซ็นที่ Plugin จัดการ ไม่ใช่การเรียกตัวช่วย Gateway ที่มีสิทธิ์ระดับสูง - เส้นทาง
auth: "gateway"ทำงานภายในขอบเขตรันไทม์ของคำขอ Gateway พื้นผิวเริ่มต้น (gatewayRuntimeScopeSurface: "write-default") ถูกกำหนดให้มีข้อจำกัดอย่างรอบคอบโดยเจตนา:- การยืนยันตัวตนแบบ bearer ด้วยข้อมูลลับร่วม (
gateway.auth.mode = "token"/"password") และวิธีการยืนยันตัวตนใด ๆ ที่ไม่ใช่พร็อกซีที่เชื่อถือได้ จะได้รับขอบเขตoperator.writeเพียงขอบเขตเดียว แม้ว่าผู้เรียกจะส่งx-openclaw-scopes - ผู้เรียก
trusted-proxyที่ไม่มีส่วนหัวx-openclaw-scopesอย่างชัดเจนจะยังคงใช้พื้นผิวเดิมที่มีเฉพาะoperator.write - ผู้เรียก
trusted-proxyที่ส่งx-openclaw-scopesจะได้รับขอบเขตที่ประกาศไว้แทน - เส้นทางสามารถเลือกใช้
gatewayRuntimeScopeSurface: "trusted-operator"เพื่อให้เคารพx-openclaw-scopesเสมอสำหรับโหมดการยืนยันตัวตนที่มีข้อมูลประจำตัว (และใช้ชุดขอบเขตเริ่มต้นทั้งหมดของ CLI เมื่อไม่มีส่วนหัว)
- การยืนยันตัวตนแบบ bearer ด้วยข้อมูลลับร่วม (
- แท็บ Control UI ภายนอกที่อยู่ในแซนด์บ็อกซ์และรองรับโดยเส้นทาง
auth: "gateway"จะใช้สิทธิ์ผ่านคุกกี้ที่ลงนามและมีอายุสั้น ซึ่งสร้างโดยการบูตสแตรปที่ผ่านการยืนยันตัวตนเท่านั้น ส่วนแท็บที่ใช้การยืนยันตัวตนของ Plugin จะยังคงใช้พาธ iframe โดยตรง ก่อนเมานต์ องค์ประกอบแม่จะเรียกโพรบที่เส้นทางเป็นเจ้าของภายในแซนด์บ็อกซ์ทึบเดียวกัน และจะปฏิเสธแบบปิดเมื่อการตั้งค่าความเป็นส่วนตัวของเบราว์เซอร์บล็อกคุกกี้ สิทธิ์นี้ผูกกับ Plugin เจ้าของ รากของเส้นทางที่ตรงกัน และรุ่นการยืนยันตัวตนปัจจุบัน ชื่อคุกกี้ที่สุ่มเฉพาะกระบวนการช่วยป้องกันไม่ให้ Gateway บนโฮสต์เดียวกันที่เชื่อถือได้เขียนทับกัน แต่คุกกี้ไม่สามารถแยกพอร์ต TCP ได้ ดังนั้นชื่อโฮสต์ของ Gateway จึงเป็นขอบเขตหนึ่งของข้อมูลรับรอง: อย่าโฮสต์บริการที่ไม่ไว้วางใจซึ่งกันและกันบนชื่อโฮสต์นั้น รวมถึงบนพอร์ตอื่น การส่งต่อเส้นทางจะปฏิเสธการนำสิทธิ์ไปใช้ซ้ำกับเส้นทางซ้อนที่ Plugin อื่นเป็นเจ้าของ เนื่องจากองค์ประกอบลูกของแซนด์บ็อกซ์ถือเป็นข้ามไซต์สำหรับวัตถุประสงค์ของคุกกี้ สิทธิ์นี้จึงยอมรับเฉพาะGETและHEADพร้อมoperator.readส่วนการเปลี่ยนแปลงข้อมูลและการอัปเกรด WebSocket ยังคงอยู่บนพื้นผิวที่ผ่านการยืนยันตัวตนของ Gateway อย่างชัดเจน คุกกี้นี้ไม่สามารถใช้ CHIPS ได้โดยเจตนา: เบราว์เซอร์ปัจจุบันรวมบิตบรรพบุรุษข้ามไซต์ไว้ในคีย์พาร์ทิชัน ทำให้เฟรมแซนด์บ็อกซ์ทึบแบบซ้อนสูญเสียสิทธิ์เข้าถึงแอสเซ็ตของเส้นทางเดียวกัน คุกกี้ต้องใช้บริบทที่ปลอดภัยและสิทธิ์จากเบราว์เซอร์สำหรับคุกกี้ข้ามไซต์ ดังนั้นแท็บภายนอกที่ใช้การยืนยันตัวตนของ Gateway จึงไม่พร้อมใช้งานบนต้นทาง LAN แบบ HTTP ธรรมดาหรือเมื่อบล็อกคุกกี้ของบุคคลที่สามทั้งหมด ให้ใช้ HTTPS/Tailscale Serve หรือลูปแบ็กที่เบราว์เซอร์เชื่อถือพร้อมนโยบายคุกกี้ที่เข้ากันได้ - สิทธิ์นี้ป้องกันการเปิดเผย bearer token ของ Gateway และการนำเส้นทาง/ขอบเขตไปใช้ซ้ำโดยไม่ตั้งใจ แต่ไม่ได้สร้างขอบเขตความปลอดภัยระหว่าง Plugin แบบเนทีฟ โค้ด Plugin แบบเนทีฟและเนื้อหา UI ที่โค้ดนั้นให้บริการยังคงเป็นส่วนหนึ่งของขอบเขต Plugin ภายในกระบวนการเดียวกันที่เชื่อถือได้
- กฎในทางปฏิบัติ: อย่าถือว่าเส้นทาง Plugin ที่ใช้การยืนยันตัวตนของ Gateway เป็นพื้นผิวผู้ดูแลระบบโดยปริยาย หากเส้นทางต้องการพฤติกรรมเฉพาะผู้ดูแลระบบ ให้เลือกใช้พื้นผิวขอบเขต
trusted-operatorกำหนดให้ใช้โหมดการยืนยันตัวตนที่มีข้อมูลประจำตัว และจัดทำเอกสารสัญญาส่วนหัวx-openclaw-scopesอย่างชัดเจน - หลังจากจับคู่เส้นทางและยืนยันตัวตนแล้ว ตัวจัดการทั่วไปจะอยู่ภายใต้การรับงานระดับรากของ Gateway โดย Gateway ที่เตรียมพร้อมหรือกำลังเริ่มใหม่จะคืนค่า
503ก่อนเรียกตัวจัดการ ข้อยกเว้นแบบจำกัดคือเส้นทางauth: "gateway"ที่ได้รับสิทธิ์จากแมนิเฟสต์และเลือกใช้พื้นผิวเฉพาะเส้นทางtrusted-operatorด้วย เส้นทางนี้ยังคงเข้าถึงได้เพื่อไม่ให้การส่งคำสั่งควบคุมการระงับติดค้าง ขณะที่เส้นทางพี่น้องทั่วไปจาก Plugin เดียวกันยังคงอยู่หลังขอบเขตการรับงาน การเป็นเจ้าของhandleUpgradeของ WebSocket ใช้ขอบเขตการรับงานแบบอะตอมเดียวกัน เมื่อใดที่ตัวจัดการยอมรับซ็อกเก็ตแล้ว อายุการใช้งานของซ็อกเก็ตหลังจากนั้นจะอยู่ภายใต้การดูแลของ Plugin และขอบเขตนี้จะไม่ติดตามอีกต่อไป
พาธนำเข้าของ Plugin SDK
เมื่อสร้าง Plugin ใหม่ ให้ใช้พาธย่อย SDK แบบเจาะจงแทน barrel ราก openclaw/plugin-sdk
แบบรวมศูนย์ พาธย่อยหลัก:
| พาธย่อย | วัตถุประสงค์ |
|---|---|
openclaw/plugin-sdk/plugin-entry |
องค์ประกอบพื้นฐานสำหรับลงทะเบียน Plugin |
openclaw/plugin-sdk/channel-core |
ตัวช่วยสำหรับจุดเข้าใช้งาน/การสร้างช่องทาง |
openclaw/plugin-sdk/core |
ตัวช่วยร่วมทั่วไปและสัญญาครอบคลุมทั้งหมด |
Plugin ช่องทางเลือกใช้จากชุดจุดเชื่อมต่อแบบเจาะจง ได้แก่ channel-setup,
setup-runtime, setup-tools, channel-pairing,
channel-contract, channel-feedback, channel-inbound, channel-outbound,
command-auth, secret-input, webhook-ingress,
channel-targets และ channel-actions พฤติกรรมการอนุมัติควรรวมไว้ใน
สัญญา approvalCapability เดียว แทนการผสมข้ามฟิลด์ Plugin
ที่ไม่เกี่ยวข้อง ดู Plugin ช่องทาง
ตัวช่วยรันไทม์และการกำหนดค่าอยู่ภายใต้พาธย่อย *-runtime แบบเจาะจงที่สอดคล้องกัน
(approval-runtime, agent-runtime, lazy-runtime, directory-runtime,
text-runtime, runtime-store, system-event-runtime, heartbeat-runtime,
channel-activity-runtime เป็นต้น) ควรใช้ config-contracts,
plugin-config-runtime, runtime-config-snapshot และ config-mutation
แทน barrel ความเข้ากันได้แบบกว้าง config-runtime
จุดเข้าใช้งานภายในรีโพ (ต่อรากแพ็กเกจ Plugin ที่รวมมาให้แต่ละรายการ):
index.js— จุดเข้าใช้งานของ Plugin ที่รวมมาให้api.js— barrel ตัวช่วย/ชนิดข้อมูลruntime-api.js— barrel สำหรับรันไทม์เท่านั้นsetup-entry.js— จุดเข้าใช้งาน Plugin สำหรับการตั้งค่า
Plugin ภายนอกควรนำเข้าเฉพาะพาธย่อย openclaw/plugin-sdk/* เท่านั้น ห้าม
นำเข้า src/* ของแพ็กเกจ Plugin อื่นจากแกนหลักหรือจาก Plugin อื่น
จุดเข้าใช้งานที่โหลดผ่าน facade จะเลือกใช้สแนปช็อตการกำหนดค่ารันไทม์ที่ใช้งานอยู่เมื่อมี
จากนั้นจึงใช้ไฟล์การกำหนดค่าที่แก้ไขพาธแล้วบนดิสก์เป็นทางเลือกสำรอง
พาธย่อยเฉพาะความสามารถ เช่น image-generation, media-understanding
และ speech มีอยู่เพราะ Plugin ที่รวมมาให้ใช้งานอยู่ในปัจจุบัน พาธเหล่านี้ไม่ได้
กลายเป็นสัญญาภายนอกที่ตรึงไว้ระยะยาวโดยอัตโนมัติ โปรดตรวจสอบหน้าอ้างอิง SDK
ที่เกี่ยวข้องเมื่อต้องพึ่งพาพาธเหล่านี้
สคีมาของเครื่องมือข้อความ
Plugin ควรเป็นเจ้าของส่วนเพิ่มเติมของสคีมา describeMessageTool(...) เฉพาะช่องทาง
สำหรับองค์ประกอบที่ไม่ใช่ข้อความ เช่น ปฏิกิริยา การอ่าน และโพล
การนำเสนอการส่งร่วมควรใช้สัญญา MessagePresentation แบบทั่วไป
แทนฟิลด์ปุ่ม คอมโพเนนต์ บล็อก หรือการ์ดแบบเนทีฟของผู้ให้บริการ
ดูสัญญา กฎทางเลือกสำรอง การแมปผู้ให้บริการ และรายการตรวจสอบสำหรับผู้สร้าง Plugin
ได้ที่ การนำเสนอข้อความ
Plugin ที่ส่งข้อความได้จะประกาศสิ่งที่สามารถเรนเดอร์ผ่านความสามารถของข้อความ:
presentationสำหรับบล็อกการนำเสนอเชิงความหมาย (text,context,divider,chart,table,buttons,select)delivery-pinสำหรับคำขอส่งแบบปักหมุด
แกนหลักเป็นผู้ตัดสินใจว่าจะเรนเดอร์การนำเสนอแบบเนทีฟหรือลดระดับเป็นข้อความ อย่าเปิดเผยช่องทางเลี่ยง UI แบบเนทีฟของผู้ให้บริการจากเครื่องมือข้อความทั่วไป ตัวช่วย SDK ที่เลิกใช้แล้วสำหรับสคีมาเนทีฟแบบเดิมยังคงส่งออกไว้สำหรับ Plugin ของบุคคลที่สามที่มีอยู่ แต่ Plugin ใหม่ไม่ควรใช้งาน
การแก้ไขเป้าหมายช่องทาง
Plugin ช่องทางควรเป็นเจ้าของความหมายของเป้าหมายเฉพาะช่องทาง ให้โฮสต์ ขาออกร่วมยังคงเป็นแบบทั่วไป และใช้พื้นผิวอะแดปเตอร์การส่งข้อความสำหรับกฎของผู้ให้บริการ:
messaging.inferTargetChatType({ to })ตัดสินใจว่าควรถือว่าเป้าหมายที่ทำให้เป็นมาตรฐานแล้ว เป็นdirect,groupหรือchannelก่อนค้นหาในไดเรกทอรีmessaging.targetResolver.looksLikeId(raw, normalized)แจ้งแกนหลักว่าอินพุต ควรข้ามไปแก้ไขแบบคล้ายรหัสโดยตรงแทนการค้นหาไดเรกทอรีหรือไม่messaging.targetResolver.reservedLiteralsแสดงรายการคำเดี่ยวที่เป็น การอ้างอิงช่องทาง/เซสชันสำหรับผู้ให้บริการรายนั้น การแก้ไขจะรักษารายการไดเรกทอรี ที่กำหนดค่าไว้ก่อนปฏิเสธลิเทอรัลที่สงวนไว้ จากนั้นจะปฏิเสธแบบปิดเมื่อค้นหา ในไดเรกทอรีไม่พบmessaging.targetResolver.resolveTarget(...)เป็นทางเลือกสำรองของ Plugin เมื่อ แกนหลักต้องการการแก้ไขขั้นสุดท้ายที่ผู้ให้บริการเป็นเจ้าของหลังการทำให้เป็นมาตรฐานหรือหลัง ค้นหาในไดเรกทอรีไม่พบmessaging.resolveOutboundSessionRoute(...)เป็นเจ้าของการสร้างเส้นทางเซสชัน เฉพาะผู้ให้บริการเมื่อแก้ไขเป้าหมายแล้ว
การแบ่งที่แนะนำ:
- ใช้
inferTargetChatTypeสำหรับการตัดสินใจประเภทที่ควรเกิดขึ้นก่อน ค้นหาเพียร์/กลุ่ม - ใช้
looksLikeIdสำหรับการตรวจสอบว่า "ให้ถือว่านี่เป็นรหัสเป้าหมายแบบชัดเจน/เนทีฟ" - ใช้
resolveTargetสำหรับทางเลือกสำรองในการทำให้เป็นมาตรฐานเฉพาะผู้ให้บริการ ไม่ใช่สำหรับ การค้นหาไดเรกทอรีแบบกว้าง - เก็บรหัสแบบเนทีฟของผู้ให้บริการ เช่น รหัสแชต รหัสเธรด JID แฮนเดิล และรหัสห้อง
ไว้ภายในค่า
targetหรือพารามิเตอร์เฉพาะผู้ให้บริการ ไม่ใช่ในฟิลด์ SDK ทั่วไป
ไดเรกทอรีที่อิงการกำหนดค่า
Plugin ที่สร้างรายการไดเรกทอรีจากการกำหนดค่าควรเก็บตรรกะนั้นไว้ใน
Plugin และใช้ตัวช่วยร่วมจาก
openclaw/plugin-sdk/directory-runtime ซ้ำ
ใช้แนวทางนี้เมื่อช่องทางต้องการเพียร์/กลุ่มที่อิงการกำหนดค่า เช่น:
- เพียร์ DM ที่ขับเคลื่อนด้วยรายการอนุญาต
- แมปช่องทาง/กลุ่มที่กำหนดค่าไว้
- ทางเลือกสำรองของไดเรกทอรีแบบคงที่ซึ่งจำกัดตามบัญชี
ตัวช่วยร่วมใน directory-runtime จัดการเฉพาะการดำเนินการทั่วไป:
- การกรองคิวรี
- การใช้ขีดจำกัด
- ตัวช่วยขจัดรายการซ้ำ/ทำให้เป็นมาตรฐาน
- การสร้าง
ChannelDirectoryEntry[]
การตรวจสอบบัญชีและการทำให้รหัสเป็นมาตรฐานเฉพาะช่องทางควรอยู่ใน การนำ Plugin ไปใช้
แค็ตตาล็อกผู้ให้บริการ
Plugin ผู้ให้บริการสามารถกำหนดแค็ตตาล็อกโมเดลสำหรับการอนุมานด้วย
registerProvider({ catalog: { run(...) { ... } } })
catalog.run(...) คืนค่าในรูปแบบเดียวกับที่ OpenClaw เขียนลงใน
models.providers:
{ provider }สำหรับรายการผู้ให้บริการหนึ่งรายการ{ providers }สำหรับรายการผู้ให้บริการหลายรายการ
ใช้ catalog เมื่อ Plugin เป็นเจ้าของรหัสโมเดลเฉพาะผู้ให้บริการ ค่าเริ่มต้นของ URL ฐาน
หรือข้อมูลเมตาของโมเดลที่เข้าถึงได้เมื่อผ่านการตรวจสอบสิทธิ์แล้ว
catalog.order ควบคุมว่าจะผสานแค็ตตาล็อกของ Plugin เมื่อใดโดยเทียบกับผู้ให้บริการโดยนัย
ที่มีมาให้ใน OpenClaw:
simple: ผู้ให้บริการที่ใช้คีย์ API ธรรมดาหรือขับเคลื่อนด้วยตัวแปรสภาพแวดล้อมprofile: ผู้ให้บริการที่ปรากฏเมื่อมีโปรไฟล์การตรวจสอบสิทธิ์paired: ผู้ให้บริการที่สังเคราะห์รายการผู้ให้บริการที่เกี่ยวข้องกันหลายรายการlate: รอบสุดท้าย หลังจากผู้ให้บริการโดยนัยอื่นๆ
ผู้ให้บริการที่มาทีหลังจะชนะเมื่อคีย์ชนกัน ดังนั้น Plugin จึงสามารถแทนที่ รายการผู้ให้บริการที่มีมาให้ซึ่งใช้รหัสผู้ให้บริการเดียวกันได้โดยเจตนา
Plugin ยังสามารถเผยแพร่แถวโมเดลแบบอ่านอย่างเดียวผ่าน
api.registerModelCatalogProvider({ provider, kinds, staticCatalog, liveCatalog }) ได้ด้วย นี่คือแนวทางในอนาคตสำหรับพื้นผิวรายการ/วิธีใช้/ตัวเลือก และรองรับแถว
text, voice, image_generation, video_generation และ music_generation
Plugin ผู้ให้บริการยังคงเป็นเจ้าของการเรียกปลายทางจริง การแลกเปลี่ยนโทเค็น และ
การแมปการตอบกลับจากผู้ขาย ส่วนแกนหลักเป็นเจ้าของรูปแบบแถวร่วม ป้ายกำกับแหล่งที่มา และ
การจัดรูปแบบวิธีใช้เครื่องมือสื่อ การลงทะเบียนผู้ให้บริการสร้างสื่อจะสังเคราะห์
แถวแค็ตตาล็อกแบบคงที่จาก defaultModel, models และ
capabilities โดยอัตโนมัติ
ความเข้ากันได้:
discoveryยังคงทำงานเป็นนามแฝงแบบเดิม แต่จะแสดงคำเตือนการเลิกใช้งาน- หากลงทะเบียนทั้ง
catalogและdiscoveryOpenClaw จะใช้catalogและแสดงคำเตือน augmentModelCatalogเลิกใช้แล้ว ผู้ให้บริการที่รวมมาให้ควรเผยแพร่ แถวเสริมผ่านregisterModelCatalogProvider
การตรวจสอบช่องทางแบบอ่านอย่างเดียว
หาก Plugin ของคุณลงทะเบียนช่องทาง ควรใช้
plugin.config.inspectAccount(cfg, accountId) ควบคู่กับ resolveAccount(...)
เหตุผล:
resolveAccount(...)คือเส้นทางรันไทม์ โดยสามารถถือว่าข้อมูลประจำตัว ได้รับการจัดเตรียมไว้อย่างสมบูรณ์ และสามารถล้มเหลวทันทีเมื่อไม่มีข้อมูลลับที่จำเป็น- เส้นทางคำสั่งแบบอ่านอย่างเดียว เช่น
openclaw status,openclaw status --all,openclaw channels status,openclaw channels resolveและโฟลว์การซ่อมแซม doctor/การกำหนดค่า ไม่ควรต้องจัดเตรียมข้อมูลประจำตัวของรันไทม์เพียงเพื่อ อธิบายการกำหนดค่า
ลักษณะการทำงานที่แนะนำสำหรับ inspectAccount(...):
- ส่งคืนเฉพาะสถานะบัญชีเชิงอธิบาย
- คง
enabledและconfiguredไว้ - รวมฟิลด์แหล่งที่มา/สถานะของข้อมูลประจำตัวเมื่อเกี่ยวข้อง เช่น:
tokenSource,tokenStatusbotTokenSource,botTokenStatusappTokenSource,appTokenStatussigningSecretSource,signingSecretStatus
- ไม่จำเป็นต้องส่งคืนค่าโทเค็นดิบเพียงเพื่อรายงานความพร้อมใช้งาน
แบบอ่านอย่างเดียว การส่งคืน
tokenStatus: "available"(และฟิลด์แหล่งที่มา ที่ตรงกัน) ก็เพียงพอสำหรับคำสั่งประเภทแสดงสถานะ - ใช้
configured_unavailableเมื่อกำหนดค่าข้อมูลประจำตัวผ่าน SecretRef แต่ ไม่พร้อมใช้งานในเส้นทางคำสั่งปัจจุบัน
วิธีนี้ช่วยให้คำสั่งแบบอ่านอย่างเดียวรายงานว่า "กำหนดค่าแล้วแต่ไม่พร้อมใช้งานในเส้นทาง คำสั่งนี้" แทนที่จะขัดข้องหรือรายงานผิดว่าบัญชียังไม่ได้กำหนดค่า
แพ็กเกจรวม
ไดเรกทอรี Plugin อาจมี package.json ที่มี openclaw.extensions:
{ "name": "my-pack", "openclaw": { "extensions": ["./src/safety.ts", "./src/tools.ts"], "setupEntry": "./src/setup-entry.ts" }}แต่ละรายการจะกลายเป็น Plugin หากแพ็กรวมส่วนขยายหลายรายการ รหัส Plugin
จะเป็น <manifestOrPackageName>/<fileBase> (รหัสในไฟล์กำกับจะมีลำดับความสำคัญเมื่อ
มีอยู่ มิฉะนั้นจะใช้ชื่อ package.json ที่ไม่มีขอบเขต)
หาก Plugin ของคุณนำเข้าการขึ้นต่อกันของ npm ให้ติดตั้งรายการเหล่านั้นในไดเรกทอรีนั้นเพื่อให้
node_modules พร้อมใช้งาน (npm install / pnpm install)
ข้อจำกัดด้านความปลอดภัย: ทุกรายการ openclaw.extensions ต้องยังคงอยู่ภายในไดเรกทอรี
Plugin หลังจากแก้ไข symlink แล้ว รายการที่ออกนอกไดเรกทอรีแพ็กเกจจะ
ถูกปฏิเสธ
หมายเหตุด้านความปลอดภัย: openclaw plugins install ติดตั้งการขึ้นต่อกันของ Plugin ด้วย
npm install --omit=dev --ignore-scripts ภายในโปรเจกต์ (ไม่มีสคริปต์วงจรชีวิต
และไม่มีการขึ้นต่อกันสำหรับการพัฒนาในรันไทม์) โดยไม่สนใจการตั้งค่าการติดตั้ง npm ส่วนกลางที่สืบทอดมา
ควรรักษาโครงสร้างการขึ้นต่อกันของ Plugin ให้เป็น "JS/TS ล้วน" และหลีกเลี่ยงแพ็กเกจที่ต้องใช้
บิลด์ postinstall
ไม่บังคับ: openclaw.setupEntry สามารถชี้ไปยังโมดูลขนาดเล็กที่ใช้สำหรับการตั้งค่าเท่านั้น
เมื่อ OpenClaw ต้องการพื้นผิวการตั้งค่าสำหรับ Plugin ช่องทางที่ปิดใช้งาน หรือ
เมื่อเปิดใช้ Plugin ช่องทางแล้วแต่ยังไม่ได้กำหนดค่า ระบบจะโหลด setupEntry
แทนรายการหลักของ Plugin วิธีนี้ช่วยให้การเริ่มทำงานและการตั้งค่าเบาลง
เมื่อรายการหลักของ Plugin เชื่อมต่อเครื่องมือ ฮุก หรือโค้ดอื่นๆ
ที่ใช้เฉพาะในรันไทม์ด้วย
ไม่บังคับ: openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen
สามารถกำหนดให้ Plugin ช่องทางใช้เส้นทาง setupEntry เดียวกันในช่วงเริ่มทำงาน
ก่อนรับฟังของ Gateway ได้ แม้ว่าช่องทางจะได้รับการกำหนดค่าแล้วก็ตาม
ใช้ตัวเลือกนี้เฉพาะเมื่อ setupEntry ครอบคลุมพื้นผิวการเริ่มทำงานที่ต้องมี
ก่อน Gateway เริ่มรับฟังอย่างสมบูรณ์ ในทางปฏิบัติ หมายความว่ารายการตั้งค่า
ต้องลงทะเบียนความสามารถทั้งหมดที่ช่องทางเป็นเจ้าของและการเริ่มทำงานพึ่งพา เช่น:
- การลงทะเบียนช่องทางเอง
- เส้นทาง HTTP ใดๆ ที่ต้องพร้อมใช้งานก่อน Gateway เริ่มรับฟัง
- เมธอด Gateway เครื่องมือ หรือบริการใดๆ ที่ต้องมีอยู่ในช่วงเวลาเดียวกันนั้น
หากรายการแบบเต็มของคุณยังคงเป็นเจ้าของความสามารถที่จำเป็นต่อการเริ่มทำงาน อย่าเปิดใช้ แฟล็กนี้ ให้ Plugin ใช้ลักษณะการทำงานเริ่มต้นต่อไป และปล่อยให้ OpenClaw โหลด รายการแบบเต็มระหว่างการเริ่มทำงาน
ช่องทางที่รวมมาให้ยังสามารถเผยแพร่ตัวช่วยพื้นผิวสัญญาที่ใช้เฉพาะการตั้งค่า ซึ่งแกนหลัก สามารถเรียกใช้ก่อนโหลดรันไทม์ช่องทางแบบเต็มได้ พื้นผิวส่งเสริมการตั้งค่า ปัจจุบันคือ:
singleAccountKeysToMovenamedAccountPromotionKeysresolveSingleAccountPromotionTarget(...)
แกนหลักใช้พื้นผิวดังกล่าวเมื่อต้องส่งเสริมการกำหนดค่าช่องทางบัญชีเดียวแบบเดิม
ให้เป็น channels.<id>.accounts.* โดยไม่โหลดรายการ Plugin แบบเต็ม
Matrix เป็นตัวอย่างที่รวมมาให้ในปัจจุบัน โดยจะย้ายเฉพาะคีย์การตรวจสอบสิทธิ์/การเริ่มต้นระบบไปยัง
บัญชีที่มีชื่อซึ่งส่งเสริมขึ้นเมื่อมีบัญชีที่มีชื่ออยู่แล้ว และสามารถคง
คีย์บัญชีเริ่มต้นที่กำหนดค่าไว้แต่ไม่เป็นแบบมาตรฐาน แทนที่จะสร้าง
accounts.default เสมอ
อะแดปเตอร์แพตช์การตั้งค่าเหล่านั้นช่วยให้การค้นพบพื้นผิวสัญญาที่รวมมาให้ยังคงเป็นแบบโหลดเมื่อจำเป็น เวลาในการนำเข้าจึงยังคงสั้น โดยพื้นผิวส่งเสริมจะโหลดเฉพาะเมื่อใช้งานครั้งแรก แทนที่จะ เข้าสู่การเริ่มทำงานของช่องทางที่รวมมาให้อีกครั้งระหว่างการนำเข้าโมดูล
เมื่อพื้นผิวการเริ่มทำงานเหล่านั้นมีเมธอด RPC ของ Gateway ให้ใช้คำนำหน้า
เฉพาะ Plugin เนมสเปซการดูแลระบบของแกนหลัก (config.*,
exec.approvals.*, wizard.*, update.*) ยังคงสงวนไว้และจะได้รับการแก้ไข
เป็น operator.admin เสมอ แม้ว่า Plugin จะร้องขอขอบเขตที่แคบกว่าก็ตาม
ตัวอย่าง:
{ "name": "@scope/my-channel", "openclaw": { "extensions": ["./index.ts"], "setupEntry": "./setup-entry.ts", "startup": { "deferConfiguredChannelFullLoadUntilAfterListen": true } }}ข้อมูลเมตาแค็ตตาล็อกช่องทาง
Plugin ช่องทางสามารถประกาศข้อมูลเมตาสำหรับการตั้งค่า/การค้นพบผ่าน openclaw.channel และ
คำแนะนำการติดตั้งผ่าน openclaw.install วิธีนี้ทำให้ข้อมูลแค็ตตาล็อกของแกนหลักไม่มีข้อมูลเฉพาะช่องทาง
ตัวอย่าง:
{ "name": "@openclaw/nextcloud-talk", "openclaw": { "extensions": ["./index.ts"], "channel": { "id": "nextcloud-talk", "label": "Nextcloud Talk", "selectionLabel": "Nextcloud Talk (โฮสต์เอง)", "docsPath": "/channels/nextcloud-talk", "docsLabel": "nextcloud-talk", "blurb": "แชตที่โฮสต์เองผ่านบอต Webhook ของ Nextcloud Talk", "order": 65, "aliases": ["nc-talk", "nc"] }, "install": { "npmSpec": "@openclaw/nextcloud-talk", "localPath": "<bundled-plugin-local-path>", "defaultChoice": "npm" } }}ฟิลด์ openclaw.channel ที่มีประโยชน์นอกเหนือจากตัวอย่างขั้นต่ำ:
detailLabel: ป้ายกำกับรองสำหรับพื้นผิวแค็ตตาล็อก/สถานะที่มีรายละเอียดมากขึ้นdocsLabel: แทนที่ข้อความลิงก์สำหรับลิงก์เอกสารpreferOver: รหัส Plugin/ช่องทางที่มีลำดับความสำคัญต่ำกว่าซึ่งรายการแค็ตตาล็อกนี้ควรมีลำดับเหนือกว่าselectionDocsPrefix,selectionDocsOmitLabel,selectionExtras: ตัวควบคุมข้อความบนพื้นผิวการเลือกmarkdownCapable: ระบุว่าช่องทางรองรับ Markdown เพื่อใช้ตัดสินใจจัดรูปแบบขาออกexposure.configured: ซ่อนช่องทางจากพื้นผิวรายการช่องทางที่กำหนดค่าแล้วเมื่อตั้งค่าเป็นfalseexposure.setup: ซ่อนช่องทางจากตัวเลือกการตั้งค่า/กำหนดค่าแบบโต้ตอบเมื่อตั้งค่าเป็นfalseexposure.docs: ระบุช่องทางว่าเป็นช่องทางภายใน/ส่วนตัวสำหรับพื้นผิวการนำทางเอกสารquickstartAllowFrom: กำหนดให้ช่องทางใช้โฟลว์เริ่มต้นอย่างรวดเร็วมาตรฐานallowFromforceAccountBinding: กำหนดให้ต้องผูกบัญชีอย่างชัดเจนแม้ว่าจะมีเพียงบัญชีเดียวpreferSessionLookupForAnnounceTarget: เลือกใช้การค้นหาเซสชันเป็นอันดับแรกเมื่อแก้ไขเป้าหมายการประกาศ
OpenClaw ยังสามารถผสาน แค็ตตาล็อกช่องทางภายนอก (เช่น ข้อมูลส่งออกจากรีจิสทรี MPM) ได้ วางไฟล์ JSON ไว้ที่ตำแหน่งใดตำแหน่งหนึ่งต่อไปนี้:
~/.openclaw/mpm/plugins.json~/.openclaw/mpm/catalog.json~/.openclaw/plugins/catalog.json
หรือกำหนดให้ OPENCLAW_PLUGIN_CATALOG_PATHS (หรือ OPENCLAW_MPM_CATALOG_PATHS) ชี้ไปยัง
ไฟล์ JSON อย่างน้อยหนึ่งไฟล์ (คั่นด้วยจุลภาค/อัฒภาค/PATH) แต่ละไฟล์ควร
มี { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] } ตัวแยกวิเคราะห์ยังยอมรับ "packages" หรือ "plugins" เป็นนามแฝงแบบเดิมสำหรับคีย์ "entries"
รายการแค็ตตาล็อกช่องทางและรายการแค็ตตาล็อกการติดตั้งผู้ให้บริการที่สร้างขึ้นจะแสดง
ข้อเท็จจริงแหล่งที่มาการติดตั้งที่ปรับเป็นมาตรฐานไว้ข้างบล็อก openclaw.install ดิบ
ข้อเท็จจริงที่ปรับเป็นมาตรฐานจะระบุว่าข้อกำหนด npm เป็นเวอร์ชันแบบเจาะจงหรือตัวเลือก
แบบลอยตัว มีข้อมูลเมตาความสมบูรณ์ที่คาดไว้หรือไม่ และมี
เส้นทางแหล่งที่มาภายในเครื่องด้วยหรือไม่ เมื่อทราบอัตลักษณ์ของแค็ตตาล็อก/แพ็กเกจ
ข้อเท็จจริงที่ปรับเป็นมาตรฐานจะแจ้งเตือนหากชื่อแพ็กเกจ npm ที่แยกวิเคราะห์ได้เบี่ยงเบนจากอัตลักษณ์นั้น
นอกจากนี้ยังแจ้งเตือนเมื่อ defaultChoice ไม่ถูกต้องหรือชี้ไปยังแหล่งที่มา
ที่ไม่พร้อมใช้งาน และเมื่อมีข้อมูลเมตาความสมบูรณ์ของ npm โดยไม่มีแหล่งที่มา npm
ที่ถูกต้อง ผู้ใช้ข้อมูลควรถือว่า installSource เป็นฟิลด์เสริมแบบเพิ่มเข้าไป เพื่อให้
รายการที่สร้างด้วยตนเองและชิมแค็ตตาล็อกไม่จำเป็นต้องสังเคราะห์ฟิลด์นี้
วิธีนี้ช่วยให้การเริ่มต้นใช้งานและการวินิจฉัยอธิบายสถานะของระนาบแหล่งที่มาได้โดยไม่ต้อง
นำเข้ารันไทม์ของ Plugin
รายการ npm ภายนอกอย่างเป็นทางการควรใช้ npmSpec แบบเจาะจงร่วมกับ
expectedIntegrity เป็นหลัก ชื่อแพ็กเกจเปล่าและ dist-tag ยังคงทำงานได้เพื่อ
ความเข้ากันได้ แต่จะแสดงคำเตือนของระนาบแหล่งที่มา เพื่อให้แค็ตตาล็อกสามารถมุ่งไปสู่
การติดตั้งแบบตรึงเวอร์ชันและตรวจสอบความสมบูรณ์โดยไม่ทำให้ Plugin ที่มีอยู่เสียหาย
เมื่อการเริ่มต้นใช้งานติดตั้งจากเส้นทางแค็ตตาล็อกภายในเครื่อง ระบบจะบันทึกรายการดัชนี Plugin
ที่มีการจัดการด้วย source: "path" และ sourcePath
ที่สัมพันธ์กับเวิร์กสเปซเมื่อเป็นไปได้ เส้นทางโหลดเชิงปฏิบัติการแบบสัมบูรณ์จะยังคงอยู่ใน
plugins.load.paths ส่วนระเบียนการติดตั้งจะหลีกเลี่ยงการทำซ้ำเส้นทางเวิร์กสเตชันภายในเครื่อง
ลงในการกำหนดค่าระยะยาว วิธีนี้ช่วยให้การติดตั้งเพื่อการพัฒนาภายในเครื่องยังคงมองเห็นได้ในการ
วินิจฉัยระนาบแหล่งที่มา โดยไม่เพิ่มพื้นผิวเปิดเผยเส้นทางระบบไฟล์ดิบอีกชุดหนึ่ง
ตาราง SQLite installed_plugin_index ที่บันทึกถาวรเป็นแหล่งข้อมูลจริงของการติดตั้ง
และสามารถรีเฟรชได้โดยไม่ต้องโหลดโมดูลรันไทม์ของ Plugin
แมป installRecords ของตารางนี้จะคงทนแม้ว่าไฟล์กำกับ Plugin จะหายไปหรือ
ไม่ถูกต้อง ส่วนเพย์โหลด plugins เป็นมุมมองไฟล์กำกับที่สร้างใหม่ได้
Plugin กลไกบริบท
Plugin กลไกบริบทเป็นเจ้าของการประสานบริบทเซสชันสำหรับการรับเข้า การประกอบ
และ Compaction ลงทะเบียนจาก Plugin ของคุณด้วย
api.registerContextEngine(id, factory) จากนั้นเลือกกลไกที่ใช้งานด้วย
plugins.slots.contextEngine
ใช้วิธีนี้เมื่อ Plugin ของคุณต้องแทนที่หรือขยายไปป์ไลน์บริบทเริ่มต้น แทนที่จะเพียงเพิ่มการค้นหาหน่วยความจำหรือฮุก
export default function (api) { api.registerContextEngine("lossless-claw", (ctx) => ({ info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true }, async ingest() { return { ingested: true }; }, async assemble({ messages, sessionKey, availableTools, citationsMode }) { return { messages, estimatedTokens: 0, systemPromptAddition: buildMemorySystemPromptAddition({ availableTools: availableTools ?? new Set(), citationsMode, agentSessionKey: sessionKey, }), }; }, async compact() { return { ok: true, compacted: false }; }, }));}แฟกทอรี ctx เปิดเผยค่า config, agentDir และ workspaceDir
ที่เป็นทางเลือกสำหรับการกำหนดค่าเริ่มต้นในขณะสร้าง
โฮสต์เตรียมพรอมต์หน่วยความจำแบบอะซิงโครนัสที่ลงทะเบียนไว้จนเสร็จสิ้นก่อนเรียก
assemble() ของเอนจินที่ไม่ใช่แบบเดิม buildMemorySystemPromptAddition(...) ยังคง
ทำงานแบบซิงโครนัสและอ่านสแนปช็อตการรันแบบเปลี่ยนแปลงไม่ได้ดังกล่าวขณะที่ assemble() ทำงานอยู่
ส่งต่อบริบทของเครื่องมือและการอ้างอิงที่ได้รับมาโดยไม่เปลี่ยนแปลง เพื่อไม่ให้สแนปช็อต
ข้ามขอบเขตการรัน
assemble() อาจส่งคืน contextProjection เมื่อฮาร์เนสที่ทำงานอยู่มี
เธรดแบ็กเอนด์แบบถาวร ให้ละเว้นค่านี้สำหรับการฉายภาพรายเทิร์นแบบเดิม ส่งคืน
{ mode: "thread_bootstrap", epoch } เมื่อควรแทรกบริบทที่ประกอบแล้ว
ลงในเธรดแบ็กเอนด์หนึ่งครั้งและนำกลับมาใช้ซ้ำจนกว่ายุคจะเปลี่ยนแปลง ให้เปลี่ยน
ยุคหลังจากบริบทเชิงความหมายของเอนจินเปลี่ยนแปลง เช่น หลังจากรอบ
Compaction ที่เอนจินเป็นเจ้าของ โฮสต์อาจเก็บรักษาเมทาดาทาของการเรียกเครื่องมือ รูปแบบ
อินพุต และผลลัพธ์เครื่องมือที่ปกปิดข้อมูลแล้วไว้ในการฉายภาพเริ่มต้นเธรด เพื่อให้เธรด
แบ็กเอนด์ใหม่ยังคงความต่อเนื่องของเครื่องมือโดยไม่คัดลอกเพย์โหลดดิบ
ที่มีข้อมูลลับ
หากเอนจินของคุณ ไม่ได้ เป็นเจ้าของอัลกอริทึม Compaction ให้คงการใช้งาน compact()
ไว้และมอบหมายการทำงานอย่างชัดเจน:
buildMemorySystemPromptAddition, delegateCompactionToRuntime,} from "openclaw/plugin-sdk/core"; export default function (api) { api.registerContextEngine("my-memory-engine", (ctx) => ({ info: { id: "my-memory-engine", name: "My Memory Engine", ownsCompaction: false, }, async ingest() { return { ingested: true }; }, async assemble({ messages, sessionKey, availableTools, citationsMode }) { return { messages, estimatedTokens: 0, systemPromptAddition: buildMemorySystemPromptAddition({ availableTools: availableTools ?? new Set(), citationsMode, agentSessionKey: sessionKey, }), }; }, async compact(params) { return await delegateCompactionToRuntime(params); }, }));}การเพิ่มความสามารถใหม่
เมื่อ Plugin ต้องการพฤติกรรมที่ไม่เข้ากับ API ปัจจุบัน อย่าหลีกเลี่ยง ระบบ Plugin ด้วยการเข้าถึงภายในแบบส่วนตัว ให้เพิ่มความสามารถที่ขาดหายไป
ลำดับที่แนะนำ:
- กำหนดสัญญาของแกนหลัก ตัดสินใจว่าแกนหลักควรเป็นเจ้าของพฤติกรรมที่ใช้ร่วมกันใดบ้าง: นโยบาย การทำงานสำรอง การผสานการกำหนดค่า วงจรชีวิต ความหมายที่แสดงต่อช่องทาง และ รูปแบบตัวช่วยรันไทม์
- เพิ่มพื้นผิวการลงทะเบียน Plugin/รันไทม์ที่มีชนิดข้อมูล ขยาย
OpenClawPluginApiและ/หรือapi.runtimeด้วยพื้นผิวความสามารถแบบมีชนิดข้อมูล ที่เล็กที่สุดและเป็นประโยชน์ - เชื่อมต่อแกนหลักกับผู้ใช้ช่องทาง/ฟีเจอร์ ช่องทางและ Plugin ฟีเจอร์ ควรใช้ความสามารถใหม่ผ่านแกนหลัก ไม่ใช่นำเข้าการใช้งานของผู้ให้บริการโดยตรง
- ลงทะเบียนการใช้งานของผู้ให้บริการ จากนั้น Plugin ของผู้ให้บริการจึงลงทะเบียน แบ็กเอนด์ของตนกับความสามารถนั้น
- เพิ่มการครอบคลุมสัญญา เพิ่มการทดสอบเพื่อให้รูปแบบความเป็นเจ้าของและการลงทะเบียน ยังคงระบุไว้อย่างชัดเจนเมื่อเวลาผ่านไป
นี่คือวิธีที่ OpenClaw รักษาแนวทางที่ชัดเจนโดยไม่ถูกฮาร์ดโค้ดให้ยึดติดกับมุมมองของ ผู้ให้บริการรายเดียว ดูรายการตรวจสอบไฟล์และตัวอย่างโดยละเอียดได้ที่ คู่มือความสามารถ
รายการตรวจสอบความสามารถ
เมื่อเพิ่มความสามารถใหม่ โดยทั่วไปการใช้งานควรแก้ไขพื้นผิวต่อไปนี้ ร่วมกัน:
- ชนิดสัญญาของแกนหลักใน
src/<capability>/types.ts - ตัวรัน/ตัวช่วยรันไทม์ของแกนหลักใน
src/<capability>/runtime.ts - พื้นผิวการลงทะเบียน API ของ Plugin ใน
src/plugins/types.ts - การเชื่อมต่อรีจิสทรี Plugin ใน
src/plugins/registry.ts - การเปิดเผยรันไทม์ของ Plugin ใน
src/plugins/runtime/*เมื่อ Plugin ฟีเจอร์/ช่องทาง จำเป็นต้องใช้งาน - ตัวช่วยบันทึก/ทดสอบใน
src/test-utils/plugin-registration.ts - การยืนยันความเป็นเจ้าของ/สัญญาใน
src/plugins/contracts/registry.ts - เอกสารสำหรับผู้ดำเนินการ/Plugin ใน
docs/
หากพื้นผิวใดพื้นผิวหนึ่งขาดหายไป โดยทั่วไปเป็นสัญญาณว่าความสามารถนั้น ยังไม่ได้ผสานรวมอย่างสมบูรณ์
เทมเพลตความสามารถ
รูปแบบขั้นต่ำ:
// core contractexport type VideoGenerationProviderPlugin = { id: string; label: string; generateVideo: (req: VideoGenerationRequest) => Promise<VideoGenerationResult>;}; // plugin APIapi.registerVideoGenerationProvider({ id: "openai", label: "OpenAI", async generateVideo(req) { return await generateOpenAiVideo(req); },}); // shared runtime helper for feature/channel pluginsconst clip = await api.runtime.videoGeneration.generate({ prompt: "Show the robot walking through the lab.", cfg,});รูปแบบการทดสอบสัญญา (src/plugins/contracts/registry.ts เปิดเผยการค้นหา
ความเป็นเจ้าของ เช่น providerContractPluginIds; การทดสอบยืนยันว่ารายการ
contracts.videoGenerationProviders ของ Plugin ตรงกับสิ่งที่ Plugin ลงทะเบียนจริง):
expect(pluginManifest.contracts?.videoGenerationProviders).toEqual(["openai"]);ซึ่งทำให้กฎเรียบง่าย:
- แกนหลักเป็นเจ้าของสัญญาความสามารถและการประสานงาน
- Plugin ของผู้ให้บริการเป็นเจ้าของการใช้งานของผู้ให้บริการ
- Plugin ฟีเจอร์/ช่องทางใช้ตัวช่วยรันไทม์
- การทดสอบสัญญาทำให้ความเป็นเจ้าของยังคงชัดเจน
ที่เกี่ยวข้อง
- สถาปัตยกรรม Plugin — โมเดลและรูปแบบความสามารถสาธารณะ
- พาธย่อยของ Plugin SDK
- การตั้งค่า Plugin SDK
- การสร้าง Plugin