Repository navigation
ja plugin certified plugins
認証とは、LangBot が正確で不変な 1 つのプラグイン artifact に対して付与する資格です。その artifact は LangBot Cloud でテナントをまたいで共有実行することが承認されます。
- 認証は artifact そのもの(digest で識別される特定の公開バージョン)に紐付き、プラグイン名や作者には紐付きません。新しいバージョンを公開しても、前のバージョンの認証は引き継がれません。
- 認証は品質評価ではなく、セキュリティ監査の保証でもありません。
LangBot は既定ではプラグインを共有実行しません。共有の単位が artifact だからです。
- 専用モード:共有に参加せず、単独で実行します。
- 共有モード:同じ artifact digest のすべての installation が 1 つの Worker を共有します。
共有の利点は、プロセス数と起動コストが installation 数に比例して増えないことです。代償は、1 つのプロセスが複数の Workspace からの呼び出しを同時に処理することです。インスタンスにテナントデータを置いているプラグインは、テナント間でデータが混ざります。
そのためプラットフォームは、この artifact を審査してステートレス規約を満たすことを確認し、その信頼を署名で artifact に固定したうえで、共有配置に載せます。これが認証の目的です。
ほとんどのプラグインには認証は不要です。 認証しなくてもマーケットプレイスへの公開は可能で、ユーザーの利用も妨げられません。プラグインは専用モードで動作します。
| 状況 | 認証の要否 |
|---|---|
| マーケットプレイスに公開してユーザーに使ってもらいたいだけ | 不要 |
| 呼び出しをまたぐプロセス内状態が必要(常駐する外部セッションプロセスなど) | 不要。専用実行のままにしてください |
| バックグラウンドタスク、タイマー、長期のテナントキャッシュがある | 不要。専用モードのほうが安全です |
| 認証プラグインの表示がほしい | 必要 |
| 同じ artifact の複数 installation で 1 つのプロセスを共有し、コストと起動負荷を下げたい | 必要 |
| Cloud のマルチテナント向けで、共有配置されたい | 必要 |
判断基準は 1 つだけです。インスタンスにテナントや installation に紐づくデータを一切保存しないと本当に言い切れる場合にのみ、認証に意味があります。
審査を通過すると、次のようになります。
- artifact が署名され、インストール時は共有モードで実行されます。同じ digest の複数の installation が 1 つの Worker を共有します。
- その Worker 内には
BasePluginが 1 つ、宣言した各コンポーネント manifest につき 1 つのインスタンス(EventListener、Tool、Command、KnowledgeEngine、Parser、Page、Runner)だけが存在します。 - 各 installation は軽量な設定・権限・ライフサイクルコンテキストを 1 つだけ保持し、呼び出しごとに現在のリクエストがどれを使うかを決定します。
- 認証プラグインの表示が付与されます。
- セルフホスト OSS は認証発行者の公開鍵を既定で持たないため、認証済みの artifact でもセルフホスト環境では専用モードで動作します。
同時に継続的な義務も発生します。以降のすべてのバージョンがステートレス規約を満たし続ける必要があります。 コードを変更したら新しいバージョンを提出して再審査を受けます。認証は自動更新されません。
manifest.yaml の execution 配下に 2 つのフィールドを同時に宣言します。
apiVersion: v1
kind: Plugin
metadata:
author: your-name
name: YourPlugin
version: 0.1.1
spec:
config: []
components: {}
execution:
python:
path: main.py
attr: YourPlugin
sharedRuntime: shared-runtime-v1
componentModel: stateless-v1| フィールド | 有効な値 | 説明 |
|---|---|---|
execution.sharedRuntime |
shared-runtime-v1 |
この値のみ受け付けます。他の値を書くとマニフェスト検証に失敗します |
execution.componentModel |
stateless-v1 |
この値のみ受け付けます |
- 2 つのフィールドは必ず同時に宣言します。
sharedRuntimeだけを宣言しcomponentModelを書かないパッケージは、認証検証の段階で拒否されます。 - これらのフィールドは SDK 0.7.0 以降で認識されます。それ以前の SDK は無視するため、古い SDK で宣言の有効性を確認しないでください。
Warning
宣言は申請であり、承認ではありません。宣言だけでは認証表示も付与されず、実行モードも変わりません。
-
BasePlugin.initialize()とコンポーネントのinitialize()はプロセス単位の初期化で、Worker ごとに 1 回だけ実行されます。この時点では「現在のインストール」も「現在の Workspace」も存在しません。 - コンポーネントインスタンスは異なる Workspace から同時に呼び出されるため、すべてのメソッドは再入可能でなければなりません。
- 現在のインストール設定は呼び出しごとに読み取ります。
BasePluginではself.get_config()、コンポーネントではself.get_plugin_config()またはself.plugin.get_config()、Runner ではctx.configを使用します。 - 設定、資格情報、Workspace / installation ID、リクエストデータをインスタンス属性やモジュールグローバルに保存しないでください。
-
initialize()でインストール設定を読み取り、保持しないでください。 - ストレージ、ファイル、資格情報、ログ、モデルなどのプラットフォーム機能は SDK の Host API 経由で利用します。権限は呼び出しごとに解決されるため、自分でキャッシュしないでください。
- 呼び出しをまたいで保持する必要があるテナント状態は Host サービスに置きます。Runner は
ctx.state_get()/ctx.state_set()などを使用します(ctx.context.available_apis.stateがtrueである必要があります)。プロセス内キャッシュは artifact レベルの公開データのみ許可されます。テナントデータをキャッシュする場合は、キーに完全な installation 識別子を含め、インストール削除時に解放できるようにしてください。 - テナントデータを伴うバックグラウンドタスクやスレッドを開始しないでください。SDK は自分で作成した処理の所有権を追跡しません。どうしてもハンドラーから切り離す場合は、installation binding をキーにした登録表を保ち、
on_installation_revoked()で停止してください。ハンドラーが戻った時点で、その呼び出しのコンテキストと実行関連の呼び出しは無効になります。
1 つの Workspace でプラグインをインストールできるのは 1 回ですが、その installation は複数のプロダクトオブジェクトから再利用されます。
| コンポーネント種別 | 再利用するもの | 分離のされ方 |
|---|---|---|
| Agent / パイプラインの Runner | 各プロセッサ |
ctx.state.* は既にプロセッサ単位(スコープに binding_identity(Agent UUID 由来)を含む) |
| ナレッジベースの KnowledgeEngine / Parser | 各ナレッジベース | プラグイン自身がナレッジベース ID で分離する必要があります |
| Tool / Command / EventListener / Page | 複数のプロセッサ、会話、ページリクエスト | 呼び出しごとに読み取り、インスタンスに蓄積しない |
プラットフォームのストレージキーは (instance, Workspace, owner type, plugin, key) で、プロセッサやナレッジベースの次元を含みません。したがって永続化やキャッシュを行う場合は、キーにプロダクトオブジェクトの識別子を含めてください。
-
設定:呼び出しごとに読み取る(
ctx.config、または task-local なself.get_plugin_config())。インスタンスにキャッシュしない。 - 永続化:キーにプロダクトの識別子(プロセッサ ID / ナレッジベース ID / 会話 ID)を含める。
- プロセス内キャッシュ:キーに完全な installation binding と プロダクトの識別子の両方を含め、installation が取り消されたときに解放する(下記)。
-
ロックとフェンス:
(installation, プロダクトオブジェクト)単位で記録する。1 つのプロセッサやナレッジベースの失敗が、同じ installation の他を遮断してはいけません。 - 失敗の切り分け:手動対账が必要な状態に入るのは、送出済みで結果不明の変更だけにします。決定的な検証エラーや送出前のキャンセルは後続に影響させない。
- ログとテレメトリ:診断の失敗が業務に影響してはいけませんし、テナントデータをプロセス全体のシングルトンに書かないでください。
共有配置では 1 つのオブジェクトグラフが同じ artifact の全 installation にサービスするため、1 つの installation を取り消してもオブジェクトグラフは破棄されません。プラグインが binding 単位のプロセス内状態を自分で解放する必要があります。
class MyPlugin(BasePlugin):
def __init__(self) -> None:
super().__init__()
self._caches: dict[str, Cache] = {}
async def on_installation_revoked(self, binding) -> None:
self._caches.pop(binding.installation_uuid, None)- 呼ばれる場面:アンインストール、無効化、Workspace の削除、およびアップグレード(旧 binding が取り消される)。共有配置でのみ呼ばれます。専用ワーカーは停止されるため、プロセス内状態はプロセスとともに消えます。
- 呼び出し中は有効な invocation がありません。Host API(ストレージ、設定、binding 参照)は利用できず、解放できるのはプロセス内状態だけです。installation は引数で渡されます。
- 例外はログに記録され、取り消し処理を妨げません。
-
BasePlugin.on_installation_revokedを提供する SDK バージョンが必要です。それ以前のバージョンではこのフックは呼ばれません。
| 項目 | 許可 | 禁止 |
|---|---|---|
| 設定の読み取り | 呼び出しごとに読み取る(self.get_plugin_config()、ctx.config) |
initialize() で読み取り self に保持する |
| コネクションプール / HTTP クライアント | テナント資格情報を含まない共有プール | インストールの endpoint や api key をインスタンスに固定する |
| キャッシュ | artifact レベルの公開データ、または完全な installation 識別子をキーとし解放可能なもの | Workspace やユーザーをキーにしたモジュールレベルの dict を長期間保持する |
| 呼び出しをまたぐ状態 | Host サービス(Runner は ctx.state_*) |
インスタンス属性、モジュールグローバル |
| バックグラウンド処理 | なし | ハンドラーが戻った後もそのコンテキストや実行関連の呼び出しを保持する |
| ログ |
ctx.log() または SDK のログ API |
モジュールレベルのバックグラウンドスレッドからテナント設定や資格情報を出力する |
1. 初期化時にインストール設定を読み取る
async def initialize(self):
self.endpoint = self.plugin.config["endpoint"] # プロセス単位の初期化。現在のインストールは存在しない
self.api_key = self.plugin.config["api_key"]共有初期化の段階にはインストール設定がないため、空の値または前回の値しか読めません。
2. テナントデータをモジュールグローバルにキャッシュする
_CACHE = {} # Workspace やユーザーをキーにすると、インストールやテナントをまたいで残留する
async def call(self, params, **kwargs):
if params["user"] not in _CACHE:
_CACHE[params["user"]] = await fetch(params["user"])3. ハンドラーが戻った後も処理を続ける
async def call(self, params, **kwargs):
config = self.get_plugin_config()
asyncio.create_task(self._upload_later(config)) # ハンドラーは既に戻っており、コンテキストと権限は無効
return ok()4. インスタンス属性を設定の入れ物として扱う
async def initialize(self):
self.default_endpoint = "https://api.example.com" # 公開定数は問題ない
self.last_config = {} # インストール設定を書き込むと違反になる正しい形:
class LookupTool(Tool):
async def initialize(self):
# artifact レベルでテナント情報を含まないオブジェクトのみを置く
self.transport = StatelessHTTPTransport()
async def call(self, params, **kwargs):
config = self.get_plugin_config() # 呼び出しごとに現在のインストール設定を読み取る
return await self.transport.lookup(config["endpoint"], params)Runner はステートレス規約との緊張が最も大きくなります。実行器はセッションやコンテキストをプロセス内に保持しがちだからです。
-
RunnerContextは今回の実行中のみ有効です。コンテキストやその要素をインスタンスに保存して呼び出しをまたいで再利用しないでください。 - Runner が呼び出しをまたいで保持する必要があるセッション状態は、
ctx.state_get()/ctx.state_set()(スコープはconversation、actor、subject、runner)または Host が提供するその他のストレージに置き、インスタンス属性は使わないでください。 - ハンドラーが戻った後、バックグラウンドタスクで
ctxを使い続けたり、self.plugin経由の実行関連呼び出しを使い続けたりしないでください。 - 設計上どうしてもプロセス内の呼び出しをまたぐ状態(長期間常駐する外部セッションプロセスなど)に依存する場合、
stateless-v1を宣言しないでください。専用 Worker のままにします。
コンテキスト API の全体は実行コンテキスト(RunnerContext)を参照してください。
提出前に 1 つずつ確認してください。
-
manifest.yamlに 2 つのフィールドが正しい値で宣言されている。 - リポジトリ全体を検索し、モジュールレベルの可変グローバルが存在しないことを確認した。
-
initialize()はプロセス単位の初期化のみを行い、インストール設定を読み取っていない。 - すべてのインストール設定を呼び出し時に読み取っている。
- ハンドラーが戻った後に動作し続けるタスクやスレッドがない。
- 呼び出しをまたぐ状態は Host API 経由で保存している(Runner は
ctx.state_*)。
認証は公開と同じ経路で、追加の手続きはありません。manifest.yaml に上記 2 つのフィールドを宣言し、いつもどおり lbp publish で新しいバージョンを提出します(公開済みのバージョンに後から宣言を追加することはできません)。LangBot Space は提出された正確なパッケージを確認し、補助的なコードレビューを行い、人による承認後に認証プラグインの表示を発行します。認証は審査済みのバージョンとチェックサムに紐付きます。
要件を満たせないと判断された場合は、まず署名なしの通常のプラグインとして公開し、後続の不変バージョンで修正してください。
このチェックで共有実行に不向きだと分かった場合は、通常のプラグインとして提出してください。認証は必須ではありません。公開の流れはマーケットプレイスへの公開を参照してください。
Automatically synchronized from langbot-app/langbot-docs.
简体中文
指南
开发者
- 插件开发
- 插件 SDK API
- 核心开发
文章
- 浏览
- 产品动态
- 在线体验
- 技术解析
- 教程与集成
- 公告
API 参考
- Service API
English
Guides
- Quick Start
- Installation
-
Configure Bots
- Bots
- Discord
- Telegram
- Slack
- Mattermost
- LINE
- Web Page Bot
- HTTP Bot
- KOOK
- Feishu
- DingTalk
- WeChat Official Account
- QQ (OneBot v11)
- Satori (QQ & Multi-Platform)
- QQ Official Bot
- WeCom (Enterprise WeChat)
- AI Configuration
- Advanced Operations
- Using Plugins
- Enterprise support
Developers
-
Plugin Development
- Plugin Development Tutorial
- Completing Plugin Configuration Information
- Plugin Directory Structure
- Component Development
- Code Style Guide
- Certified plugins and shared runtime
- Publish Plugin
- Migration Guide
- Plugin SDK API
- Core Development
Articles
- Browse
- Product Updates
- Engineering
-
Tutorials & Integrations
- LangTARS: Open-Source AI Agent for Remote PC Control — Works with Dify, n8n & 10+ Messaging Platforms
- How to Connect DeepSeek R1 to WeChat, Discord & Telegram in 5 Minutes (FREE)
- Deploy Your Own AI Bot to Discord, Telegram & WeChat in 5 Minutes
- Finally Got My Dify Agent Working in Discord, Telegram and Slack
- How I Built a Multi-Platform AI Bot with Langflow's Drag-and-Drop Workflows
- How I Built a Multi-Platform AI Chatbot with n8n and LangBot
- LangBot 4.6.0 External Knowledge Base Tutorial: Integrating Dify with LangBot for RAG-powered Conversations
- Announcements
API Reference
- Service API
Other pages
日本語
ガイド
開発者
- プラグイン開発
- プラグイン SDK API
- コア開発
記事
- 一覧
- 製品アップデート
- エンジニアリング
-
チュートリアルと連携
- LangTARS:Dify・n8n と連携するオープンソース PC 操作 Agent
- DeepSeek R1 を WeChat・Discord・Telegram に5分で接続する方法
- AI Bot を Discord・Telegram・WeChat に5分でデプロイ
- Dify Agent を Discord・Telegram・Slack で動かす
- Langflow のドラッグ&ドロップでマルチプラットフォーム AI Bot を構築
- n8n と LangBot でマルチプラットフォーム AI Chatbot を構築
- LangBot 4.6.0 外部ナレッジベース入門:Dify と連携した RAG 会話
- お知らせ
API リファレンス
- Service API