---
read_when:
    - การสร้างหรือดีบัก Plugin เนทีฟของ OpenClaw
    - ทำความเข้าใจโมเดลความสามารถของ Plugin หรือขอบเขตความเป็นเจ้าของ
    - การทำงานกับขั้นตอนการโหลดหรือรีจิสทรีของ Plugin
    - การใช้งานฮุกของรันไทม์ผู้ให้บริการหรือ Plugin ของแชนเนล
sidebarTitle: Internals
summary: 'รายละเอียดภายในของ Plugin: โมเดลความสามารถ ความเป็นเจ้าของ สัญญา ขั้นตอนการโหลด และตัวช่วยรันไทม์'
title: รายละเอียดภายในของ Plugin
x-i18n:
    generated_at: "2026-04-26T11:36:01Z"
    model: gpt-5.4
    provider: openai
    source_hash: 16664d284a8bfbfcb9914bb012d1f36dfdd60406636d6bf4b011f76e886cb518
    source_path: plugins/architecture.md
    workflow: 15
---

นี่คือ**เอกสารอ้างอิงสถาปัตยกรรมเชิงลึก**สำหรับระบบ Plugin ของ OpenClaw สำหรับคู่มือเชิงปฏิบัติ ให้เริ่มจากหนึ่งในหน้าที่มุ่งเน้นเฉพาะด้านด้านล่างนี้

<CardGroup cols={2}>
  <Card title="ติดตั้งและใช้งาน Plugin" icon="plug" href="/th/tools/plugin">
    คู่มือสำหรับผู้ใช้ปลายทางในการเพิ่ม เปิดใช้งาน และแก้ไขปัญหา Plugin
  </Card>
  <Card title="การสร้าง Plugin" icon="rocket" href="/th/plugins/building-plugins">
    บทช่วยสอน Plugin แรกพร้อม manifest ที่เล็กที่สุดซึ่งใช้งานได้จริง
  </Card>
  <Card title="Plugin ของแชนเนล" icon="comments" href="/th/plugins/sdk-channel-plugins">
    สร้าง Plugin แชนเนลข้อความ
  </Card>
  <Card title="Plugin ของผู้ให้บริการ" icon="microchip" href="/th/plugins/sdk-provider-plugins">
    สร้าง Plugin ผู้ให้บริการโมเดล
  </Card>
  <Card title="ภาพรวม SDK" icon="book" href="/th/plugins/sdk-overview">
    เอกสารอ้างอิง import map และ API สำหรับการลงทะเบียน
  </Card>
</CardGroup>

## โมเดลความสามารถแบบสาธารณะ

ความสามารถคือโมเดล **native plugin** แบบสาธารณะภายใน OpenClaw ทุก Plugin เนทีฟของ OpenClaw จะลงทะเบียนกับประเภทความสามารถอย่างน้อยหนึ่งประเภท:

| ความสามารถ              | เมธอดการลงทะเบียน                             | ตัวอย่าง Plugin                      |
| ----------------------- | ---------------------------------------------- | ------------------------------------ |
| การอนุมานข้อความ        | `api.registerProvider(...)`                    | `openai`, `anthropic`                |
| แบ็กเอนด์การอนุมาน CLI  | `api.registerCliBackend(...)`                  | `openai`, `anthropic`                |
| เสียงพูด                | `api.registerSpeechProvider(...)`              | `elevenlabs`, `microsoft`            |
| การถอดเสียงแบบเรียลไทม์ | `api.registerRealtimeTranscriptionProvider(...)` | `openai`                             |
| เสียงแบบเรียลไทม์       | `api.registerRealtimeVoiceProvider(...)`       | `openai`                             |
| ความเข้าใจสื่อ          | `api.registerMediaUnderstandingProvider(...)`  | `openai`, `google`                   |
| การสร้างภาพ            | `api.registerImageGenerationProvider(...)`     | `openai`, `google`, `fal`, `minimax` |
| การสร้างเพลง           | `api.registerMusicGenerationProvider(...)`     | `google`, `minimax`                  |
| การสร้างวิดีโอ          | `api.registerVideoGenerationProvider(...)`     | `qwen`                               |
| การดึงข้อมูลเว็บ        | `api.registerWebFetchProvider(...)`            | `firecrawl`                          |
| การค้นหาเว็บ            | `api.registerWebSearchProvider(...)`           | `google`                             |
| แชนเนล / การส่งข้อความ  | `api.registerChannel(...)`                     | `msteams`, `matrix`                  |
| การค้นหา Gateway        | `api.registerGatewayDiscoveryService(...)`     | `bonjour`                            |

<Note>
Plugin ที่ลงทะเบียนความสามารถเป็นศูนย์ แต่มี hook, tool, บริการค้นหา หรือบริการเบื้องหลัง จะถือเป็น Plugin แบบ **legacy hook-only** รูปแบบดังกล่าวยังคงรองรับอย่างสมบูรณ์
</Note>

### จุดยืนด้านความเข้ากันได้ภายนอก

โมเดลความสามารถได้ถูกรวมไว้ในแกนหลักแล้ว และปัจจุบันถูกใช้งานโดย Plugin แบบ bundled/native แต่ความเข้ากันได้ของ Plugin ภายนอกยังต้องมีเกณฑ์ที่เข้มงวดกว่าการคิดว่า “เมื่อมีการ export ก็แปลว่าถูกตรึงแล้ว”

| สถานการณ์ของ Plugin                            | แนวทาง                                                                                             |
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Plugin ภายนอกที่มีอยู่แล้ว                     | รักษาให้การผสานรวมแบบอิง hook ยังคงทำงานได้; นี่คือฐานความเข้ากันได้                               |
| Plugin แบบ bundled/native ใหม่                 | ควรใช้การลงทะเบียนความสามารถแบบชัดเจน แทนการเจาะเข้าไปยังส่วนเฉพาะของผู้ขายหรือการออกแบบ hook-only ใหม่ |
| Plugin ภายนอกที่เริ่มใช้การลงทะเบียนความสามารถ | อนุญาต แต่ให้ถือว่า surface ของตัวช่วยเฉพาะความสามารถยังคงพัฒนาอยู่ เว้นแต่เอกสารจะระบุว่าเสถียร         |

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

### รูปแบบของ Plugin

OpenClaw จัดประเภท Plugin ที่ถูกโหลดทุกตัวเป็นรูปแบบหนึ่งตามพฤติกรรมการลงทะเบียนจริง (ไม่ใช่แค่ข้อมูลเมตาแบบคงที่):

<AccordionGroup>
  <Accordion title="plain-capability">
    ลงทะเบียนความสามารถเพียงประเภทเดียวเท่านั้น (เช่น Plugin ที่เป็นผู้ให้บริการอย่างเดียว เช่น `mistral`)
  </Accordion>
  <Accordion title="hybrid-capability">
    ลงทะเบียนหลายประเภทความสามารถ (เช่น `openai` เป็นเจ้าของการอนุมานข้อความ เสียงพูด ความเข้าใจสื่อ และการสร้างภาพ)
  </Accordion>
  <Accordion title="hook-only">
    ลงทะเบียนเฉพาะ hook (ทั้งแบบมีชนิดหรือแบบกำหนดเอง) ไม่มีความสามารถ tool คำสั่ง หรือบริการ
  </Accordion>
  <Accordion title="non-capability">
    ลงทะเบียน tool คำสั่ง บริการ หรือ route แต่ไม่มีความสามารถ
  </Accordion>
</AccordionGroup>

ใช้ `openclaw plugins inspect <id>` เพื่อดูรูปแบบและรายละเอียดความสามารถของ Plugin ดูรายละเอียดได้ที่ [เอกสารอ้างอิง CLI](/th/cli/plugins#inspect)

### hook แบบเดิม

hook `before_agent_start` ยังคงรองรับในฐานะเส้นทางความเข้ากันได้สำหรับ Plugin แบบ hook-only Plugin แบบเดิมที่ใช้งานจริงยังคงพึ่งพา hook นี้

ทิศทางคือ:

- ให้มันยังใช้งานได้ต่อไป
- บันทึกเอกสารว่าเป็นแบบเดิม
- ควรใช้ `before_model_resolve` สำหรับงานแทนที่โมเดล/ผู้ให้บริการ
- ควรใช้ `before_prompt_build` สำหรับงานเปลี่ยนแปลง prompt
- นำออกได้ก็ต่อเมื่อการใช้งานจริงลดลง และความครอบคลุมของ fixture พิสูจน์ได้ว่าการย้ายใช้งานปลอดภัย

### สัญญาณด้านความเข้ากันได้

เมื่อคุณเรียกใช้ `openclaw doctor` หรือ `openclaw plugins inspect <id>` คุณอาจเห็นป้ายกำกับอย่างใดอย่างหนึ่งต่อไปนี้:

| สัญญาณ                    | ความหมาย                                                  |
| ------------------------- | --------------------------------------------------------- |
| **config valid**          | Config ถูกแยกวิเคราะห์ได้ถูกต้อง และ Plugin ถูก resolve ได้ |
| **compatibility advisory** | Plugin ใช้รูปแบบที่ยังรองรับแต่เก่ากว่า (เช่น `hook-only`) |
| **legacy warning**        | Plugin ใช้ `before_agent_start` ซึ่งเลิกใช้แล้ว           |
| **hard error**            | Config ไม่ถูกต้อง หรือ Plugin โหลดไม่สำเร็จ               |

ทั้ง `hook-only` และ `before_agent_start` จะยังไม่ทำให้ Plugin ของคุณเสียหายในตอนนี้: `hook-only` เป็นเพียงคำแนะนำ และ `before_agent_start` จะแสดงเพียงคำเตือน สัญญาณเหล่านี้ยังปรากฏใน `openclaw status --all` และ `openclaw plugins doctor` ด้วย

## ภาพรวมสถาปัตยกรรม

ระบบ Plugin ของ OpenClaw มี 4 ชั้น:

<Steps>
  <Step title="Manifest + การค้นหา">
    OpenClaw ค้นหา Plugin ที่เป็นไปได้จาก path ที่กำหนดไว้, root ของ workspace, root ของ Plugin ส่วนกลาง และ Plugin ที่ bundled มา การค้นหาจะอ่าน manifest แบบ native `openclaw.plugin.json` รวมถึง bundle manifest ที่รองรับก่อน
  </Step>
  <Step title="การเปิดใช้งาน + การตรวจสอบความถูกต้อง">
    แกนหลักจะตัดสินใจว่า Plugin ที่ค้นพบตัวใดถูกเปิดใช้งาน ปิดใช้งาน ถูกบล็อก หรือถูกเลือกสำหรับสล็อตแบบ exclusive เช่น memory
  </Step>
  <Step title="การโหลดรันไทม์">
    Plugin เนทีฟของ OpenClaw จะถูกโหลดในโปรเซสผ่าน jiti และลงทะเบียนความสามารถเข้าสู่รีจิสทรีส่วนกลาง bundle ที่เข้ากันได้จะถูกทำให้เป็นมาตรฐานให้อยู่ในรูปของระเบียนในรีจิสทรีโดยไม่ต้อง import โค้ดรันไทม์
  </Step>
  <Step title="การใช้งาน surface">
    ส่วนที่เหลือของ OpenClaw จะอ่านรีจิสทรีเพื่อเปิดเผย tool, แชนเนล, การตั้งค่าผู้ให้บริการ, hook, route HTTP, คำสั่ง CLI และบริการต่าง ๆ
  </Step>
</Steps>

โดยเฉพาะสำหรับ Plugin CLI การค้นหาคำสั่งรากจะแบ่งเป็นสองระยะ:

- ข้อมูลเมตาในช่วง parse-time มาจาก `registerCli(..., { descriptors: [...] })`
- โมดูล CLI จริงของ Plugin สามารถคงความ lazy ไว้ได้และลงทะเบียนเมื่อมีการเรียกใช้ครั้งแรก

แนวทางนี้ช่วยให้โค้ด CLI ที่เป็นของ Plugin ยังคงอยู่ภายใน Plugin ขณะเดียวกันก็ยังทำให้ OpenClaw สามารถจองชื่อคำสั่งรากไว้ก่อนการ parse ได้

ขอบเขตการออกแบบที่สำคัญคือ:

- การตรวจสอบ manifest/config ควรทำงานได้จาก **ข้อมูลเมตา manifest/schema** โดยไม่ต้องรันโค้ด Plugin
- การค้นพบความสามารถแบบเนทีฟอาจโหลดโค้ด entry ของ Plugin ที่เชื่อถือได้ เพื่อสร้าง snapshot ของรีจิสทรีที่ยังไม่เปิดใช้งาน
- พฤติกรรมรันไทม์แบบเนทีฟมาจากเส้นทาง `register(api)` ของโมดูล Plugin โดยมี `api.registrationMode === "full"`

การแยกส่วนนี้ทำให้ OpenClaw สามารถตรวจสอบ config อธิบาย Plugin ที่หายไป/ถูกปิดใช้งาน และสร้างคำแนะนำสำหรับ UI/schema ได้ก่อนที่รันไทม์เต็มรูปแบบจะเริ่มทำงาน

### การวางแผนการเปิดใช้งาน

การวางแผนการเปิดใช้งานเป็นส่วนหนึ่งของ control plane ผู้เรียกสามารถถามได้ว่า Plugin ใดเกี่ยวข้องกับคำสั่ง ผู้ให้บริการ แชนเนล route agent harness หรือความสามารถที่เจาะจง ก่อนที่จะโหลดรีจิสทรีรันไทม์ในวงกว้าง

ตัววางแผนยังคงรักษาพฤติกรรม manifest ปัจจุบันให้เข้ากันได้:

- ฟิลด์ `activation.*` เป็นคำใบ้ของตัววางแผนแบบชัดเจน
- `providers`, `channels`, `commandAliases`, `setup.providers`, `contracts.tools` และ hook ยังคงเป็นกลไกสำรองด้านความเป็นเจ้าของของ manifest
- API ของตัววางแผนแบบ ids-only ยังคงมีไว้สำหรับผู้เรียกเดิม
- API ของแผนจะรายงานป้ายกำกับเหตุผล เพื่อให้การวินิจฉัยแยกความแตกต่างระหว่างคำใบ้แบบชัดเจนกับกลไกสำรองด้านความเป็นเจ้าของได้

<Warning>
อย่าถือว่า `activation` เป็น lifecycle hook หรือเป็นตัวแทนของ `register(...)` มันคือข้อมูลเมตาที่ใช้เพื่อจำกัดขอบเขตการโหลด ควรเลือกใช้ฟิลด์ด้านความเป็นเจ้าของเมื่อฟิลด์เหล่านั้นอธิบายความสัมพันธ์ได้อยู่แล้ว และใช้ `activation` เฉพาะเมื่อจำเป็นต้องมีคำใบ้เพิ่มเติมสำหรับตัววางแผน
</Warning>

### Plugin ของแชนเนลและ tool ข้อความแบบใช้ร่วมกัน

Plugin ของแชนเนลไม่จำเป็นต้องลงทะเบียน tool สำหรับส่ง/แก้ไข/ตอบสนองแยกต่างหากสำหรับการทำงานแชตปกติ OpenClaw คง `message` tool แบบใช้ร่วมกันไว้หนึ่งตัวในแกนหลัก และให้ Plugin ของแชนเนลเป็นเจ้าของการค้นหาและการดำเนินการเฉพาะแชนเนลที่อยู่เบื้องหลังมัน

ขอบเขตปัจจุบันคือ:

- แกนหลักเป็นเจ้าของโฮสต์ของ `message` tool แบบใช้ร่วมกัน การเชื่อมต่อกับ prompt การเก็บข้อมูลเซสชัน/เธรด และการกระจายการดำเนินการ
- Plugin ของแชนเนลเป็นเจ้าของการค้นหาการทำงานแบบมีขอบเขต การค้นหาความสามารถ และส่วน schema ใด ๆ ที่เฉพาะกับแชนเนล
- Plugin ของแชนเนลเป็นเจ้าของไวยากรณ์การสนทนาเซสชันเฉพาะผู้ให้บริการ เช่น วิธีที่ conversation id เข้ารหัส thread id หรือสืบทอดจากการสนทนาแม่
- Plugin ของแชนเนลดำเนินการขั้นสุดท้ายผ่าน action adapter ของตนเอง

สำหรับ Plugin ของแชนเนล surface ของ SDK คือ `ChannelMessageActionAdapter.describeMessageTool(...)` การเรียกค้นหาแบบรวมนี้ช่วยให้ Plugin สามารถคืนค่าการทำงานที่มองเห็นได้ ความสามารถ และส่วนเติมแต่งของ schema มาพร้อมกัน เพื่อให้ส่วนเหล่านี้ไม่คลาดเคลื่อนจากกัน

เมื่อพารามิเตอร์ของ message-tool ที่เฉพาะกับแชนเนลมีแหล่งสื่อ เช่น path ในเครื่องหรือ URL ของสื่อระยะไกล Plugin ควรคืนค่า `mediaSourceParams` จาก `describeMessageTool(...)` ด้วย แกนหลักใช้รายการแบบชัดเจนนี้เพื่อปรับรูปแบบ path ภายใต้ sandbox และคำใบ้สำหรับการเข้าถึงสื่อขาออก โดยไม่ต้องฮาร์ดโค้ดชื่อพารามิเตอร์ที่เป็นของ Plugin ควรใช้แผนที่แบบกำหนดขอบเขตต่อ action ตรงนี้ ไม่ใช่รายการแบบแบนหนึ่งรายการทั้งแชนเนล เพื่อไม่ให้พารามิเตอร์สื่อที่ใช้เฉพาะโปรไฟล์ถูกปรับรูปแบบกับ action ที่ไม่เกี่ยวข้อง เช่น `send`

แกนหลักจะส่งขอบเขตรันไทม์เข้าไปในขั้นตอนการค้นหานั้น ฟิลด์สำคัญได้แก่:

- `accountId`
- `currentChannelId`
- `currentThreadTs`
- `currentMessageId`
- `sessionKey`
- `sessionId`
- `agentId`
- `requesterSenderId` ขาเข้าที่เชื่อถือได้

สิ่งนี้สำคัญสำหรับ Plugin ที่ไวต่อบริบท แชนเนลสามารถซ่อนหรือแสดงการทำงานของข้อความตามบัญชีที่ใช้งานอยู่ ห้อง/เธรด/ข้อความปัจจุบัน หรืออัตลักษณ์ของผู้ร้องขอที่เชื่อถือได้ โดยไม่ต้องฮาร์ดโค้ดเงื่อนไขเฉพาะแชนเนลไว้ใน `message` tool ของแกนหลัก

นี่คือเหตุผลที่การเปลี่ยนแปลง routing ของ embedded-runner ยังคงเป็นงานของ Plugin: runner มีหน้าที่ส่งต่ออัตลักษณ์แชต/เซสชันปัจจุบันไปยังขอบเขตการค้นหาของ Plugin เพื่อให้ `message` tool แบบใช้ร่วมกันแสดง surface ที่เป็นของแชนเนลได้อย่างถูกต้องสำหรับรอบการทำงานปัจจุบัน

สำหรับตัวช่วยการดำเนินการที่เป็นของแชนเนล Plugin แบบ bundled ควรเก็บรันไทม์การดำเนินการไว้ภายในโมดูลส่วนขยายของตนเอง แกนหลักไม่ได้เป็นเจ้าของรันไทม์การทำงานข้อความของ Discord, Slack, Telegram หรือ WhatsApp ภายใต้ `src/agents/tools` อีกต่อไป เราไม่ได้เผยแพร่ subpath แบบ `plugin-sdk/*-action-runtime` แยกต่างหาก และ Plugin แบบ bundled ควร import โค้ดรันไทม์ในเครื่องของตนเองโดยตรงจากโมดูลที่เป็นของส่วนขยายนั้นเอง

ขอบเขตเดียวกันนี้ใช้กับ SDK seam ที่ตั้งชื่อตามผู้ให้บริการโดยทั่วไปด้วยเช่นกัน: แกนหลักไม่ควร import convenience barrel ที่เฉพาะกับแชนเนลสำหรับ Slack, Discord, Signal, WhatsApp หรือส่วนขยายลักษณะเดียวกัน หากแกนหลักต้องการพฤติกรรมใดอย่างหนึ่ง ให้เลือกใช้ `api.ts` / `runtime-api.ts` barrel ของ Plugin แบบ bundled นั้นโดยตรง หรือยกระดับความต้องการนั้นให้กลายเป็นความสามารถแบบทั่วไปที่แคบใน SDK ที่ใช้ร่วมกัน

สำหรับโพลโดยเฉพาะ มีเส้นทางการดำเนินการอยู่สองแบบ:

- `outbound.sendPoll` คือพื้นฐานแบบใช้ร่วมกันสำหรับแชนเนลที่เข้ากับโมเดลโพลทั่วไป
- `actions.handleAction("poll")` คือเส้นทางที่ควรใช้สำหรับความหมายของโพลที่เฉพาะกับแชนเนล หรือพารามิเตอร์โพลเพิ่มเติม

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

ดู [รายละเอียดภายในสถาปัตยกรรม Plugin](/th/plugins/architecture-internals) สำหรับลำดับการเริ่มต้นทำงานแบบเต็ม

## โมเดลความเป็นเจ้าของความสามารถ

OpenClaw มองว่า Plugin เนทีฟเป็นขอบเขตความเป็นเจ้าของของ **บริษัท** หรือ **ฟีเจอร์** ไม่ใช่เป็นที่รวมการผสานรวมที่ไม่เกี่ยวข้องกัน

นั่นหมายความว่า:

- Plugin ของบริษัทโดยทั่วไปควรเป็นเจ้าของ surface ทั้งหมดของบริษัทนั้นที่หันหน้าเข้าหา OpenClaw
- Plugin ของฟีเจอร์โดยทั่วไปควรเป็นเจ้าของ surface ของฟีเจอร์ทั้งหมดที่มันนำเข้ามา
- แชนเนลควรใช้ความสามารถแบบใช้ร่วมกันของแกนหลัก แทนการนำพฤติกรรมของผู้ให้บริการกลับไปทำใหม่แบบเฉพาะกิจ

<AccordionGroup>
  <Accordion title="ผู้ขายแบบหลายความสามารถ">
    `openai` เป็นเจ้าของการอนุมานข้อความ เสียงพูด เสียงแบบเรียลไทม์ ความเข้าใจสื่อ และการสร้างภาพ `google` เป็นเจ้าของการอนุมานข้อความ รวมถึงความเข้าใจสื่อ การสร้างภาพ และการค้นหาเว็บ `qwen` เป็นเจ้าของการอนุมานข้อความ รวมถึงความเข้าใจสื่อและการสร้างวิดีโอ
  </Accordion>
  <Accordion title="ผู้ขายแบบความสามารถเดียว">
    `elevenlabs` และ `microsoft` เป็นเจ้าของเสียงพูด; `firecrawl` เป็นเจ้าของ web-fetch; `minimax` / `mistral` / `moonshot` / `zai` เป็นเจ้าของแบ็กเอนด์ media-understanding
  </Accordion>
  <Accordion title="Plugin ของฟีเจอร์">
    `voice-call` เป็นเจ้าของการขนส่งสายเรียกเข้าออก tool, CLI, route และการเชื่อม Twilio media-stream แต่ใช้ความสามารถเสียงพูด การถอดเสียงแบบเรียลไทม์ และเสียงแบบเรียลไทม์แบบใช้ร่วมกัน แทนการ import Plugin ของผู้ขายโดยตรง
  </Accordion>
</AccordionGroup>

สถานะปลายทางที่ตั้งใจไว้คือ:

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

นี่คือความแตกต่างสำคัญ:

- **plugin** = ขอบเขตความเป็นเจ้าของ
- **capability** = สัญญาของแกนหลักที่ Plugin หลายตัวสามารถนำไปใช้หรือใช้งานได้

ดังนั้นถ้า OpenClaw เพิ่มโดเมนใหม่อย่างวิดีโอ คำถามแรกไม่ใช่ “ควรฮาร์ดโค้ดการจัดการวิดีโอไว้กับผู้ให้บริการรายใด?” คำถามแรกคือ “สัญญาความสามารถด้านวิดีโอของแกนหลักคืออะไร?” เมื่อมีสัญญานั้นแล้ว Plugin ของผู้ขายก็สามารถลงทะเบียนกับมันได้ และ Plugin ของแชนเนล/ฟีเจอร์ก็สามารถใช้งานมันได้

หากยังไม่มีความสามารถนั้น การดำเนินการที่ถูกต้องโดยทั่วไปคือ:

<Steps>
  <Step title="กำหนดความสามารถ">
    กำหนดความสามารถที่ยังขาดอยู่ในแกนหลัก
  </Step>
  <Step title="เปิดเผยผ่าน SDK">
    เปิดเผยความสามารถนั้นผ่าน API/รันไทม์ของ Plugin แบบมีชนิด
  </Step>
  <Step title="เชื่อมต่อผู้ใช้งาน">
    เชื่อมแชนเนล/ฟีเจอร์เข้ากับความสามารถนั้น
  </Step>
  <Step title="การนำไปใช้โดยผู้ขาย">
    ให้ Plugin ของผู้ขายลงทะเบียนการนำไปใช้
  </Step>
</Steps>

แนวทางนี้ทำให้ความเป็นเจ้าของชัดเจน ขณะเดียวกันก็หลีกเลี่ยงพฤติกรรมในแกนหลักที่ขึ้นกับผู้ขายเพียงรายเดียวหรือเส้นทางโค้ดเฉพาะ Plugin แบบครั้งคราว

### การจัดชั้นของความสามารถ

ใช้แบบจำลองความคิดนี้เมื่อตัดสินใจว่าโค้ดควรอยู่ที่ใด:

<Tabs>
  <Tab title="ชั้นความสามารถของแกนหลัก">
    การประสานงานแบบใช้ร่วมกัน นโยบาย fallback กฎการรวม config ความหมายของการส่งมอบ และสัญญาแบบมีชนิด
  </Tab>
  <Tab title="ชั้น Plugin ของผู้ขาย">
    API เฉพาะผู้ขาย การยืนยันตัวตน แค็ตตาล็อกโมเดล การสังเคราะห์เสียงพูด การสร้างภาพ แบ็กเอนด์วิดีโอในอนาคต endpoint การใช้งาน
  </Tab>
  <Tab title="ชั้น Plugin ของแชนเนล/ฟีเจอร์">
    การผสานรวม Slack/Discord/voice-call/ฯลฯ ที่ใช้ความสามารถของแกนหลัก และนำเสนอออกมาบน surface
  </Tab>
</Tabs>

ตัวอย่างเช่น TTS ใช้รูปแบบนี้:

- แกนหลักเป็นเจ้าของนโยบาย TTS ตอนตอบกลับ ลำดับ fallback ค่ากำหนด และการส่งมอบผ่านแชนเนล
- `openai`, `elevenlabs` และ `microsoft` เป็นเจ้าของการนำการสังเคราะห์ไปใช้
- `voice-call` ใช้ตัวช่วยรันไทม์ TTS สำหรับโทรศัพท์

ควรใช้รูปแบบเดียวกันนี้กับความสามารถในอนาคต

### ตัวอย่าง Plugin ของบริษัทแบบหลายความสามารถ

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

```ts
import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/plugin-entry";
import {
  describeImageWithModel,
  transcribeOpenAiCompatibleAudio,
} from "openclaw/plugin-sdk/media-understanding";

const plugin: OpenClawPluginDefinition = {
  id: "exampleai",
  name: "ExampleAI",
  register(api) {
    api.registerProvider({
      id: "exampleai",
      // auth/model catalog/runtime hooks
    });

    api.registerSpeechProvider({
      id: "exampleai",
      // vendor speech config — implement the SpeechProviderPlugin interface directly
    });

    api.registerMediaUnderstandingProvider({
      id: "exampleai",
      capabilities: ["image", "audio", "video"],
      async describeImage(req) {
        return describeImageWithModel({
          provider: "exampleai",
          model: req.model,
          input: req.input,
        });
      },
      async transcribeAudio(req) {
        return transcribeOpenAiCompatibleAudio({
          provider: "exampleai",
          model: req.model,
          input: req.input,
        });
      },
    });

    api.registerWebSearchProvider(
      createPluginBackedWebSearchProvider({
        id: "exampleai-search",
        // credential + fetch logic
      }),
    );
  },
};

export default plugin;
```

สิ่งสำคัญไม่ใช่ชื่อตัวช่วยที่แน่นอน รูปแบบต่างหากที่สำคัญ:

- Plugin เดียวเป็นเจ้าของ surface ของผู้ขาย
- แกนหลักยังคงเป็นเจ้าของสัญญาความสามารถ
- แชนเนลและ Plugin ของฟีเจอร์ใช้ตัวช่วย `api.runtime.*` ไม่ใช่โค้ดของผู้ขาย
- การทดสอบสัญญาสามารถยืนยันได้ว่า Plugin ลงทะเบียนความสามารถที่มันอ้างว่าเป็นเจ้าของจริง

### ตัวอย่างความสามารถ: ความเข้าใจวิดีโอ

OpenClaw มองว่าความเข้าใจภาพ/เสียง/วิดีโอเป็นความสามารถแบบใช้ร่วมกันหนึ่งเดียวอยู่แล้ว โมเดลความเป็นเจ้าของเดียวกันนี้ใช้กับส่วนนี้เช่นกัน:

<Steps>
  <Step title="แกนหลักกำหนดสัญญา">
    แกนหลักกำหนดสัญญา media-understanding
  </Step>
  <Step title="Plugin ของผู้ขายลงทะเบียน">
    Plugin ของผู้ขายลงทะเบียน `describeImage`, `transcribeAudio` และ `describeVideo` ตามที่รองรับ
  </Step>
  <Step title="ผู้ใช้งานใช้พฤติกรรมแบบใช้ร่วมกัน">
    แชนเนลและ Plugin ของฟีเจอร์ใช้พฤติกรรมแบบใช้ร่วมกันของแกนหลัก แทนการเชื่อมตรงไปยังโค้ดของผู้ขาย
  </Step>
</Steps>

แนวทางนี้หลีกเลี่ยงการฝังสมมติฐานเกี่ยวกับวิดีโอของผู้ให้บริการรายใดรายหนึ่งไว้ในแกนหลัก Plugin เป็นเจ้าของ surface ของผู้ขาย; แกนหลักเป็นเจ้าของสัญญาความสามารถและพฤติกรรม fallback

การสร้างวิดีโอก็ใช้ลำดับแบบเดียวกันนี้อยู่แล้ว: แกนหลักเป็นเจ้าของสัญญาความสามารถแบบมีชนิดและตัวช่วยรันไทม์ และ Plugin ของผู้ขายลงทะเบียนการนำ `api.registerVideoGenerationProvider(...)` ไปใช้กับมัน

ต้องการรายการตรวจสอบการเปิดตัวแบบเป็นรูปธรรมหรือไม่ ดู [Capability Cookbook](/th/plugins/architecture)

## สัญญาและการบังคับใช้

surface ของ API ของ Plugin ถูกออกแบบให้มีชนิดและรวมศูนย์ไว้ใน `OpenClawPluginApi` โดยตั้งใจ สัญญานี้กำหนดจุดลงทะเบียนที่รองรับและตัวช่วยรันไทม์ที่ Plugin สามารถพึ่งพาได้

เหตุผลที่เรื่องนี้สำคัญ:

- ผู้เขียน Plugin ได้มาตรฐานภายในที่เสถียรเพียงหนึ่งเดียว
- แกนหลักสามารถปฏิเสธความเป็นเจ้าของซ้ำซ้อน เช่น Plugin สองตัวลงทะเบียน provider id เดียวกัน
- การเริ่มต้นทำงานสามารถแสดงการวินิจฉัยที่นำไปปฏิบัติได้สำหรับการลงทะเบียนที่ผิดรูปแบบ
- การทดสอบสัญญาสามารถบังคับใช้ความเป็นเจ้าของของ Plugin แบบ bundled และป้องกันการคลาดเคลื่อนแบบเงียบ

มีการบังคับใช้อยู่สองชั้น:

<AccordionGroup>
  <Accordion title="การบังคับใช้การลงทะเบียนขณะรันไทม์">
    รีจิสทรีของ Plugin จะตรวจสอบการลงทะเบียนขณะที่ Plugin ถูกโหลด ตัวอย่างเช่น provider id ซ้ำ speech provider id ซ้ำ และการลงทะเบียนที่ผิดรูปแบบ จะสร้างการวินิจฉัยของ Plugin แทนที่จะนำไปสู่พฤติกรรมที่ไม่กำหนด
  </Accordion>
  <Accordion title="การทดสอบสัญญา">
    Plugin แบบ bundled จะถูกเก็บไว้ในรีจิสทรีสัญญาระหว่างการรันทดสอบ เพื่อให้ OpenClaw สามารถยืนยันความเป็นเจ้าของได้อย่างชัดเจน ปัจจุบันใช้กับ model providers, speech providers, web search providers และความเป็นเจ้าของการลงทะเบียนแบบ bundled
  </Accordion>
</AccordionGroup>

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

### สิ่งที่ควรอยู่ในสัญญา

<Tabs>
  <Tab title="สัญญาที่ดี">
    - มีชนิด
    - ขนาดเล็ก
    - เฉพาะความสามารถ
    - เป็นของแกนหลัก
    - ใช้ซ้ำได้โดย Plugin หลายตัว
    - แชนเนล/ฟีเจอร์สามารถใช้งานได้โดยไม่ต้องรู้เรื่องผู้ขาย

  </Tab>
  <Tab title="สัญญาที่ไม่ดี">
    - นโยบายเฉพาะผู้ขายที่ซ่อนอยู่ในแกนหลัก
    - ช่องทางเลี่ยงเฉพาะ Plugin แบบครั้งคราวที่ข้ามรีจิสทรี
    - โค้ดแชนเนลที่เข้าถึงการนำไปใช้ของผู้ขายโดยตรง
    - อ็อบเจ็กต์รันไทม์แบบเฉพาะกิจที่ไม่ได้เป็นส่วนหนึ่งของ `OpenClawPluginApi` หรือ `api.runtime`

  </Tab>
</Tabs>

หากไม่แน่ใจ ให้ยกระดับนามธรรมขึ้นก่อน: กำหนดความสามารถก่อน แล้วจึงให้ Plugin เสียบเข้ากับมัน

## โมเดลการดำเนินการ

Plugin เนทีฟของ OpenClaw ทำงาน **ในโปรเซสเดียวกัน** กับ Gateway โดยไม่มี sandbox Plugin เนทีฟที่ถูกโหลดแล้วมีขอบเขตความไว้วางใจในระดับโปรเซสเทียบเท่ากับโค้ดของแกนหลัก

<Warning>
ผลที่ตามมา:

- Plugin เนทีฟสามารถลงทะเบียน tool, ตัวจัดการเครือข่าย, hook และบริการได้
- ข้อบกพร่องใน Plugin เนทีฟอาจทำให้ gateway ล่มหรือไม่เสถียร
- Plugin เนทีฟที่เป็นอันตรายมีค่าเทียบเท่ากับการรันโค้ดโดยอำเภอใจภายในโปรเซส OpenClaw

</Warning>

bundle ที่เข้ากันได้จะปลอดภัยกว่าโดยปริยาย เพราะปัจจุบัน OpenClaw ปฏิบัติกับมันในฐานะแพ็กข้อมูลเมตา/เนื้อหา ในรีลีสปัจจุบัน สิ่งนี้ส่วนใหญ่หมายถึง Skills ที่ bundled มา

ให้ใช้ allowlist และ path ติดตั้ง/โหลดแบบชัดเจนสำหรับ Plugin ที่ไม่ได้ bundled ให้มอง Plugin ของ workspace เป็นโค้ดสำหรับช่วงพัฒนา ไม่ใช่ค่าปริยายสำหรับระบบจริง

สำหรับชื่อแพ็กเกจ workspace แบบ bundled ให้ยึด plugin id ไว้กับชื่อ npm: ใช้ `@openclaw/<id>` เป็นค่าปริยาย หรือใช้ suffix แบบมีชนิดที่ได้รับอนุมัติ เช่น `-provider`, `-plugin`, `-speech`, `-sandbox` หรือ `-media-understanding` เมื่อแพ็กเกจนั้นตั้งใจเปิดเผยบทบาท Plugin ที่แคบกว่าโดยเฉพาะ

<Note>
**หมายเหตุด้านความไว้วางใจ:**

- `plugins.allow` ไว้วางใจ **plugin id** ไม่ใช่ที่มาของซอร์ส
- Plugin ของ workspace ที่มี id เดียวกับ Plugin แบบ bundled จะจงใจบังสำเนาแบบ bundled เมื่อ Plugin ของ workspace นั้นถูกเปิดใช้งาน/อยู่ใน allowlist
- นี่เป็นพฤติกรรมปกติและมีประโยชน์สำหรับการพัฒนาในเครื่อง การทดสอบแพตช์ และ hotfix
- ความไว้วางใจของ Plugin แบบ bundled จะถูกตัดสินจาก snapshot ของซอร์ส — manifest และโค้ดบนดิสก์ในเวลาที่โหลด — ไม่ใช่จากข้อมูลเมตาการติดตั้ง ระเบียนการติดตั้งที่เสียหายหรือถูกสับเปลี่ยนไม่สามารถขยาย surface ความไว้วางใจของ Plugin แบบ bundled ให้กว้างเกินกว่าที่ซอร์สจริงอ้างไว้ได้อย่างเงียบ ๆ

</Note>

## ขอบเขตการ export

OpenClaw export ความสามารถ ไม่ใช่ convenience ของการนำไปใช้

คงการลงทะเบียนความสามารถให้เป็นแบบสาธารณะ และตัดการ export ตัวช่วยที่ไม่ใช่ส่วนของสัญญาออก:

- subpath ของตัวช่วยที่เฉพาะกับ Plugin แบบ bundled
- subpath ของ plumbing รันไทม์ที่ไม่ได้ตั้งใจให้เป็น API สาธารณะ
- ตัวช่วย convenience ที่เฉพาะกับผู้ขาย
- ตัวช่วยสำหรับ setup/onboarding ที่เป็นรายละเอียดการนำไปใช้

subpath ของตัวช่วยบางส่วนสำหรับ Plugin แบบ bundled ยังคงอยู่ในแผนที่ export ของ SDK ที่สร้างขึ้นเพื่อความเข้ากันได้และเพื่อการดูแล Plugin แบบ bundled ตัวอย่างปัจจุบันได้แก่ `plugin-sdk/feishu`, `plugin-sdk/feishu-setup`, `plugin-sdk/zalo`, `plugin-sdk/zalo-setup` และ seam แบบ `plugin-sdk/matrix*` หลายรายการ ให้ถือว่าสิ่งเหล่านี้เป็น export รายละเอียดการนำไปใช้ที่สงวนไว้ ไม่ใช่รูปแบบ SDK ที่แนะนำสำหรับ Plugin ภายนอกใหม่

## ภายในและเอกสารอ้างอิง

สำหรับขั้นตอนการโหลด โมเดลรีจิสทรี ฮุกของรันไทม์ผู้ให้บริการ route HTTP ของ Gateway สคีมาของ message tool การ resolve เป้าหมายของแชนเนล แค็ตตาล็อกผู้ให้บริการ Plugin ของ context engine และคู่มือการเพิ่มความสามารถใหม่ ดูได้ที่ [รายละเอียดภายในสถาปัตยกรรม Plugin](/th/plugins/architecture-internals)

## ที่เกี่ยวข้อง

- [การสร้าง Plugin](/th/plugins/building-plugins)
- [manifest ของ Plugin](/th/plugins/manifest)
- [การตั้งค่า SDK ของ Plugin](/th/plugins/sdk-setup)
