Add 'docs/' from commit '51dddcb3f891d60bc38645e88b2dbd515344da81'

git-subtree-dir: docs
git-subtree-mainline: 0582d1b3d4
git-subtree-split: 51dddcb3f8
This commit is contained in:
RockChinQ
2026-10-27 08:04:35 +00:00
847 changed files with 87063 additions and 0 deletions
+263
View File
@@ -0,0 +1,263 @@
---
title: "コンテキスト API"
---
コンテキストには、1 回のコンポーネント呼び出しに関するデータが含まれます。コンポーネントごとに受け取るコンテキストが異なるため、まず対象コンポーネントの入口を確認してください。
| コンポーネント | ハンドラーの入口 | コンテキスト型 |
| --- | --- | --- |
| EventListener | `handler(event_context)` | `EventContext` |
| Command | `subcommand(context)` | `ExecuteContext` |
| Tool | `call(params, session, query_id)` | 独立したコンテキストオブジェクトなし |
| Runner | `run(ctx)` または `handler(ctx)` | `RunnerContext` |
| KnowledgeEngine | `ingest(context)`、`retrieve(context)` | `IngestionContext`、`RetrievalContext` |
| KnowledgeRetriever | `retrieve(context)` | `RetrievalContext` |
| Parser | `parse(context)` | `ParseContext` |
| Page | `handle_api(request)` | `PageRequest` |
このページでは、コンテキストのデータとメソッドのみを説明します。モデル、ナレッジベース、ストレージなどの LangBot 機能には `self.plugin` を使用します。詳細は [LangBot API](/ja/plugin/dev/apis/common) を参照してください。プラットフォーム操作は[プラットフォーム API](/ja/plugin/dev/apis/platform)に記載しています。
`EventContext`、`ExecuteContext`、`Session` には `instance_uuid`、`workspace_uuid`、`placement_generation` も含まれます。これらは現在の実行に対応するインスタンスとワークスペースを示すためのもので、プラグイン側の認可情報として使用しないでください。
リトライ、タイムアウト、数値思考予算、prompt caching、ヘッダー、ストリーム終了は [LLM リクエスト設定](/ja/plugin/dev/apis/llm-requests)を参照してください。
## EventListener
EventListener はパイプラインイベントを処理します。イベントハンドラーは `EventContext` を受け取ります。
```python
event = event_context.event
sender_id = event.sender_id
```
### コンテキストデータ
| プロパティ | 型 | 説明 |
| --- | --- | --- |
| `event` | `BaseEventModel` | 現在のイベントオブジェクト。実際の型は登録したイベント型と一致します |
| `event_name` | `str` | 現在のイベントクラス名 |
| `query_id` | `int` | 現在のパイプラインリクエスト ID |
| `query_uuid` | `str \| None` | 現在のパイプラインリクエストの安定した識別子 |
| `eid` | `int` | Plugin Runtime 内の一時的なイベント番号。永続化しないでください |
| `is_prevent_default` | `bool` | デフォルト処理を停止済みか。変更には `prevent_default()` を使用します |
| `is_prevent_postorder` | `bool` | 後続プラグインを停止済みか。変更には `prevent_postorder()` を使用します |
イベントのフィールドと購読可能なイベントは[パイプラインイベント](/ja/plugin/dev/apis/pipeline-events)を参照してください。
### コンテキストメソッド
| メソッド | 戻り値 | 説明 |
| --- | --- | --- |
| `await event_context.reply(message_chain, quote_origin=False)` | - | 現在のリクエストの会話に返信 |
| `await event_context.get_bot_uuid()` | `str` | 送信元 Bot の UUID を取得 |
| `await event_context.set_query_var(key, value)` | - | 現在のリクエストに変数を設定 |
| `await event_context.get_query_var(key)` | `Any` | リクエスト変数を 1 つ取得 |
| `await event_context.get_query_vars()` | `dict[str, Any]` | すべてのリクエスト変数を取得 |
| `await event_context.create_new_conversation()` | `dict[str, Any]` | 現在のパイプライン会話をクリアし、後続リクエストで新しい会話を作成 |
| `await event_context.list_pipeline_knowledge_bases()` | `list[dict[str, Any]]` | 現在のパイプラインの Local Agent に設定されたナレッジベースを取得 |
| `await event_context.retrieve_knowledge(kb_id, query_text, top_k=5, filters=None)` | `list[dict[str, Any]]` | 現在のパイプラインに設定されたナレッジベースを検索 |
| `event_context.prevent_default()` | - | 現在のイベントのデフォルト処理を停止 |
| `event_context.prevent_postorder()` | - | 後続プラグインによる現在のイベント処理を停止 |
`reply()` は [MessageChain](/ja/plugin/dev/apis/messages) を受け取ります。リクエスト変数、返信、パイプラインのナレッジベース API には、関連付けられたパイプラインリクエストが必要です。`retrieve_knowledge()` の `kb_id` は `list_pipeline_knowledge_bases()` の結果から指定します。
`prevent_default()` はデフォルトフローを停止できるイベントにのみ作用します。詳細は[パイプラインイベント](/ja/plugin/dev/apis/pipeline-events#デフォルト動作を阻止)を参照してください。
## Command
Command のサブコマンドハンドラーは `ExecuteContext` を受け取ります。
```python
query = " ".join(context.crt_params)
session = context.session
```
### コンテキストデータ
| プロパティ | 型 | 説明 |
| --- | --- | --- |
| `session` | `Session` | 現在のメッセージが属するセッション |
| `command_text` | `str` | コマンド接頭辞を除いた完全なコマンド文字列 |
| `full_command_text` | `str` | 接頭辞を含む完全なコマンド文字列 |
| `command` | `str` | ルートコマンド名 |
| `crt_command` | `str` | 現在実行中のサブコマンド名 |
| `params` | `list[str]` | ルートコマンド以降のすべての引数 |
| `crt_params` | `list[str]` | 現在のサブコマンドで未処理の引数 |
| `privilege` | `int` | 現在のユーザーのコマンド権限レベル |
| `query_id` | `int` | 現在のパイプラインリクエスト ID |
| `query_uuid` | `str \| None` | 現在のパイプラインリクエストの安定した識別子 |
SDK は現在のサブコマンドに入る前に `context.shift()` を呼び出します。コンポーネントから直接呼び出す必要はありません。
### コンテキストメソッド
| メソッド | 戻り値 | 説明 |
| --- | --- | --- |
| `await context.reply(message_chain, quote_origin=False)` | - | 現在のリクエストの会話に返信 |
| `await context.get_bot_uuid()` | `str` | 送信元 Bot の UUID を取得 |
| `await context.set_query_var(key, value)` | - | 現在のリクエストに変数を設定 |
| `await context.get_query_var(key)` | `Any` | リクエスト変数を 1 つ取得 |
| `await context.get_query_vars()` | `dict[str, Any]` | すべてのリクエスト変数を取得 |
| `await context.create_new_conversation()` | `dict[str, Any]` | 現在のパイプライン会話をクリアし、後続リクエストで新しい会話を作成 |
| `await context.list_pipeline_knowledge_bases()` | `list[dict[str, Any]]` | 現在のパイプラインの Local Agent に設定されたナレッジベースを取得 |
| `await context.retrieve_knowledge(kb_id, query_text, top_k=5, filters=None)` | `list[dict[str, Any]]` | 現在のパイプラインに設定されたナレッジベースを検索 |
これらのリクエストメソッドは EventListener と同じ動作です。登録方法と戻り値は [Command コンポーネント](/ja/plugin/dev/components/command)を参照してください。
## Tool
Tool には独立したコンテキストオブジェクトがありません。`call()` の引数として呼び出し情報を受け取ります。
```python
conversation_type = session.launcher_type.value
conversation_id = session.launcher_id
sender_id = session.sender_id
```
| 引数 | 説明 |
| --- | --- |
| `params` | ツールの JSON Schema に基づいてモデルまたは呼び出し元が生成した引数 |
| `session` | 現在のセッションデータ |
| `query_id` | 現在のパイプラインリクエスト ID。リクエストコンテキストが必要な LangBot API にそのまま渡します |
`Session` の主なプロパティは次のとおりです。
| プロパティ | 説明 |
| --- | --- |
| `launcher_type` | 会話種別:`person` または `group` |
| `launcher_id` | 個人チャットのユーザー ID またはグループ ID |
| `sender_id` | 現在のメッセージ送信者 ID |
| `bot_uuid` | 現在の Bot UUID |
| `using_conversation` | 現在のパイプライン会話 |
| `conversations` | この Session に保存されたパイプライン会話 |
| `use_prompt_name` | 使用中のプロンプト名 |
| `create_time`、`update_time` | Session の作成時刻と更新時刻 |
`session` はデータオブジェクトであり、`reply()` などのコンテキストメソッドはありません。ツールの戻り値は呼び出し元へ返され、チャットプラットフォームには自動送信されません。LangBot とプラットフォームの機能には `self.plugin` を使用します。API が `session` と `query_id` を要求する場合は、現在の呼び出しで受け取った値を渡してください。
## Runner
独自の `run(ctx)` と Runner のイベントハンドラーは、どちらも `RunnerContext` を受け取ります。このコンテキストは現在の実行中だけ有効です。
```python
text = ctx.input.to_text()
event_type = ctx.event.event_type
```
### コンテキストデータ
| プロパティ | 型 | 説明 |
| --- | --- | --- |
| `run_id` | `str` | 現在の実行 ID |
| `trigger` | `AgentTrigger` | トリガーの種類、発生元、時刻 |
| `event` | `AgentEventContext` | イベント ID、種類、発生元、時刻、元データを含む標準イベントエンベロープ |
| `platform_event` | プラットフォームイベント型 | イベント型に応じて解析されたプラットフォームイベント |
| `conversation` | `ConversationContext \| None` | 現在の会話、スレッド、Bot、ワークスペースの識別子 |
| `actor` | `ActorContext \| None` | イベントを発生させた主体 |
| `subject` | `SubjectContext \| None` | イベントの対象となるメッセージやグループなど |
| `input` | `AgentInput` | 現在のテキスト、構造化コンテンツ、添付ファイル |
| `delivery` | `DeliveryContext` | 出力先とストリーミング、編集、リアクションの対応状況 |
| `resources` | `AgentResources` | 今回の実行で許可されたモデル、ツール、ナレッジベース、スキル、ストレージ |
| `context` | `ContextAccess` | 会話カーソル、インライン方針、利用可能なコンテキスト API |
| `state` | `AgentRunState` | 実行開始時に読み込まれたスコープ別状態のスナップショット |
| `runtime` | `AgentRuntimeContext` | LangBot バージョン、トレース ID、期限 |
| `config` | `dict[str, Any]` | 現在の Agent またはプラグインプロセッサーの設定 |
| `adapter` | `AdapterContext \| None` | エントリーアダプターの追加データ |
| `variables` | `dict[str, Any]` | Runner テンプレートで使える公開リクエスト変数 |
| `metadata` | `dict[str, Any]` | ホストが提供するその他のメタデータ |
上表の安定したプロパティを優先してください。`event.data` と `adapter.extra` には、安定したフィールドに含まれない追加データが入ります。
### 返信とログ
| メソッド | 戻り値 | 説明 |
| --- | --- | --- |
| `await ctx.get_bot_uuid()` | `str` | 送信元 Bot UUID を取得。関連する Bot がなければエラー |
| `await ctx.reply(message_chain, quote_origin=False)` | `Any` | 文字列または `MessageChain` で現在のイベントに返信 |
| `ctx.reply_stream()` | 非同期コンテキストマネージャー | 1 件の返信を完全なテキストで逐次更新 |
| `await ctx.log(text, level="info")` | - | 今回の実行ログへ記録。レベルは `debug`、`info`、`warning`、`error` |
`reply_stream().update(text)` には、その時点の完全なテキストを渡します。プラットフォームがストリーミングに対応しない場合、終了時に完全なメッセージを 1 件送信します。デバッグモードでは送信をシミュレートします。
### 利用可能なツール
| メソッド | 戻り値 | 説明 |
| --- | --- | --- |
| `await ctx.get_available_tools()` | `list[dict[str, Any]]` | 今回呼び出せるコンテキスト操作、プラットフォーム API、プラグインツール、MCP ツールを取得 |
| `await ctx.call_tool(tool_name, parameters=None)` | `dict[str, Any]` | 上記一覧のツールを呼び出す |
`get_available_tools()` の各項目には `name`、`description`、`parameters`、`type`、`operations` が含まれます。結果は現在の Agent またはプラグインプロセッサーの設定とイベント機能で絞り込まれています。一覧にないツールは `ctx.call_tool()` で呼び出せません。
### Box と添付ファイル
Box の有効化、取得、再利用方針は Runner が決めます。リソースの取得には [LangBot API](/ja/plugin/dev/apis/common#box-サンドボックス)、実行中の操作には以下を使います。
| メソッド | 戻り値 | 説明 |
| --- | --- | --- |
| `await ctx.bind_box(box_id)` | `BoxBinding` | Box を現在の実行にバインドし、`box_id`、`outbox` を返す。同じ実行内では変更不可 |
| `await ctx.import_box_attachments(attachment_ids=None)` | `list[BoxFile]` | 入力の `ref` を指定してインポート。省略時は全件。サンドボックスの `path` を返す |
| `await ctx.export_box_files()` | `list[BoxFile]` | 現在の実行の outbox をファイルハンドルとしてエクスポート。メッセージは送信しない |
| `await ctx.reply_files(file_ids)` | `Any` | エクスポートした ID で返信。返信ツールの権限制限が適用される |
`BoxFile` は `id`、`name`、`type`、`size`、任意の `path` を含みます。入力の元 URL と内容は保持され、明示的にインポートして初めてファイルパスが生成されます。ハンドルは今回の実行でのみ有効で、重複送信できません。
ネイティブツールやファイル操作の前に Box をバインドします。Box を共有しても添付ディレクトリは実行ごとに分かれます。`ctx.variables` の公開リクエスト変数で再利用キーを計算できます。
Pipeline では `ctx.delivery.automatic_reply` が `True` になり、`RunnerResult.message_completed(ctx.run_id, message, file_ids=[...])` で添付を返せます。Agent とプラグインプロセッサーは `ctx.reply_files()` で明示的に送信します。
外部ランナーも `AgentRunExternalTools` で同じ手順を使用できます:`langbot_get_box_status`、`langbot_list_boxes`、`langbot_acquire_box`、`langbot_bind_box`、`langbot_import_box_attachments`、`langbot_export_box_files`。イベントへの返信権限がある場合は `langbot_reply_files` も利用できます。すべての呼び出しに現在の実行の権限が適用されます。
### プロンプト、履歴、イベント
| メソッド | 戻り値 | 利用可否フィールド |
| --- | --- | --- |
| `await ctx.get_prompt()` | `list[dict[str, Any]]` | `prompt_get` |
| `await ctx.history_page(conversation_id=None, before_cursor=None, after_cursor=None, limit=50, direction="backward", include_attachments=False)` | `HistoryPage` | `history_page` |
| `await ctx.history_search(query, filters=None, top_k=10)` | `HistorySearchResult` | `history_search` |
| `await ctx.event_get(event_id)` | `AgentEventRecord` | `event_get` |
| `await ctx.event_page(conversation_id=None, event_types=None, before_cursor=None, limit=50)` | `EventPage` | `event_page` |
| `await ctx.steering_pull(mode="all", limit=None)` | `SteeringPullResult` | `steering_pull` |
### 状態
| メソッド | 戻り値 | 説明 |
| --- | --- | --- |
| `await ctx.state_get(scope, key)` | `dict[str, Any]` | 状態値を取得 |
| `await ctx.state_set(scope, key, value)` | `dict[str, Any]` | JSON シリアライズ可能な状態値を保存 |
| `await ctx.state_delete(scope, key)` | `dict[str, Any]` | 状態値を削除 |
| `await ctx.state_list(scope, prefix=None, limit=100)` | `dict[str, Any]` | 指定スコープの状態キーを一覧表示 |
`scope` は `conversation`、`actor`、`subject`、`runner` のいずれかです。これらのメソッドには `ctx.context.available_apis.state` が `true` である必要があります。
### 実行記録
| メソッド | 戻り値 | 利用可否フィールド |
| --- | --- | --- |
| `await ctx.run_get(run_id=None)` | `AgentRun` | `run_get` |
| `await ctx.run_list(conversation_id=None, statuses=None, before_cursor=None, limit=50)` | `RunPage` | `run_list` |
| `await ctx.run_events_page(run_id=None, before_cursor=None, after_cursor=None, limit=50, direction="forward")` | `RunEventPage` | `run_events_page` |
| `await ctx.run_cancel(run_id=None, reason=None)` | `AgentRun` | `run_cancel` |
| `await ctx.run_append_result(result)` | `AgentRunEvent` | `run_append_result` |
| `await ctx.run_finalize(run_id=None, status=None, reason=None)` | `AgentRun` | `run_finalize` |
`run_id=None` は現在の実行を指します。通常のイベントハンドラーは SDK が自動的に完了します。独自の `run(ctx)` が `RunnerResult` を yield する場合、同じ結果を `run_append_result()` で重複登録しないでください。
### 利用可否と権限
プロンプト、履歴、イベント、状態、実行記録の API が利用可能かどうかは `ctx.context.available_apis` で確認します。許可されたモデル、ツール、ナレッジベースはそれぞれ `ctx.resources.models`、`ctx.resources.tools`、`ctx.resources.knowledge_bases` にあります。
Runner の実行中に `self.plugin` から呼び出すモデル、ツール、ナレッジベース、ストレージ、プラットフォーム API は、自動的に現在の実行へ関連付けられ、`ctx.resources` に基づいて再検証されます。`run_id` を渡す必要はありません。ハンドラー終了後にバックグラウンドタスクからこのコンテキストや実行に紐づく呼び出しを使用しないでください。
`ctx.api` は低レベルの実行スコーププロキシです。通常のコンポーネントコードでは、このページに記載した `ctx` メソッドと、LangBot API 用の `self.plugin` を使用してください。
## データ専用コンテキスト
以下のコンポーネントが受け取るものはタスク入力であり、EventListener や Runner の実行コンテキストメソッドはありません。フィールド、戻り値、完全な例は各コンポーネントガイドに記載しています。
| コンポーネントの入口 | 主なデータ | コンポーネントガイド |
| --- | --- | --- |
| `KnowledgeEngine.ingest(context: IngestionContext)` | ファイル、対象ナレッジベース、作成設定、解析結果 | [KnowledgeEngine](/ja/plugin/dev/components/knowledge-engine#ドキュメント取り込み) |
| `KnowledgeEngine.retrieve(context: RetrievalContext)` | クエリ、対象ナレッジベース、検索設定、フィルター | [KnowledgeEngine](/ja/plugin/dev/components/knowledge-engine#ナレッジ検索) |
| `KnowledgeRetriever.retrieve(context: RetrievalContext)` | クエリと外部ナレッジベース設定 | [KnowledgeRetriever](/ja/plugin/dev/components/knowledge-retriever#検索コンテキスト) |
| `Parser.parse(context: ParseContext)` | ファイル内容、ファイル名、MIME タイプ、メタデータ | [Parser](/ja/plugin/dev/components/parser#解析メソッド) |
| `Page.handle_api(request: PageRequest)` | エンドポイント、HTTP メソッド、本文、ヘッダー | [Page](/ja/plugin/dev/components/page#pagerequest-フィールド) |
+711
View File
@@ -0,0 +1,711 @@
---
title: "LangBot API"
---
モデル、ツール、ナレッジベース、ストレージには `self.plugin` を使用します。現在のイベント、返信、会話の状態については[コンテキスト API](/ja/plugin/dev/apis/agent-run) を参照してください。
リトライ、タイムアウト、数値思考予算、prompt caching、ヘッダー、ストリーム終了は [LLM リクエスト設定](/ja/plugin/dev/apis/llm-requests)を参照してください。
## LangBot API
これらのAPIは、通常のプラグインコンポーネントで呼び出すことができます。アクセス方法:
- プラグインルートディレクトリの`main.py`内: `self`オブジェクトの内部メソッド。これらのAPIはすべて、プラグインクラスの親クラス`BasePlugin`によって提供されます。
- 通常のプラグインコンポーネントクラス内: `self.plugin`オブジェクトの内部メソッド。
### プラグイン設定の取得
プラグイン設定フォーマットは`manifest.yaml`に記述でき、ユーザーはLangBotのプラグイン管理でプラグイン設定フォーマットに従って入力する必要があります。プラグインコードはこのAPIを呼び出してプラグイン設定情報を取得できます。
```python
def get_config(self) -> dict[str, typing.Any]:
"""プラグインの設定を取得する。"""
# 使用例
config = self.plugin.get_config()
```
### LangBotバージョンの取得
LangBotのバージョン番号を取得します。`v<major>.<minor>.<patch>`形式の文字列として返されます。
```python
async def get_langbot_version(self) -> str:
"""LangBotバージョンを取得する"""
# 使用例
langbot_version = await self.plugin.get_langbot_version()
```
### 設定済みBotリストの取得
すべてのBot UUIDのリストを返します。
```python
async def get_bots(self) -> list[str]:
"""すべてのBotを取得する"""
# 使用例
bots = await self.plugin.get_bots()
```
### Bot情報の取得
Bot情報を取得します。
```python
async def get_bot_info(self, bot_uuid: str) -> dict[str, Any]:
"""Bot情報を取得する"""
# 使用例
bot_info = await self.plugin.get_bot_info("de639861-be05-4018-859b-c2e2d3e0d603")
# 返り値の例
{
"uuid": "de639861-be05-4018-859b-c2e2d3e0d603",
"name": "aiocqhttp",
"description": "Migrated from LangBot v3",
"adapter": "aiocqhttp",
"enable": true,
"use_pipeline_name": "ChatPipeline",
"use_pipeline_uuid": "c30a1dca-e91c-452b-83ec-84d635a30028",
"created_at": "2025-05-10T13:53:08",
"updated_at": "2025-08-12T11:27:30",
"adapter_runtime_values": { # Botが現在実行中の場合に存在
"bot_account_id": 960164003 # BotアカウントID
}
}
```
### プロアクティブメッセージの送信
Bot UUIDとターゲットセッションIDを通じてプロアクティブメッセージを送信します。
メッセージチェーンの構築方法については、[メッセージプラットフォームエンティティ](/en/plugin/dev/apis/messages)を参照してください。
```python
async def send_message(
self,
bot_uuid: str,
target_type: str,
target_id: str,
message_chain: platform_message.MessageChain,
) -> None:
"""セッションにメッセージを送信する"""
# 使用例
await self.plugin.send_message(
bot_uuid="de639861-be05-4018-859b-c2e2d3e0d603",
target_type="person",
target_id="1010553892",
message_chain=platform_message.MessageChain([platform_message.Plain(text="Hello, world!")]),
)
```
### 設定済みLLMモデルリストの取得
設定されたすべてのLLMモデルのUUIDのリストを返します。
```python
async def get_llm_models(self) -> list[str]:
"""すべてのLLMモデルを取得する"""
# 使用例
llm_models = await self.plugin.get_llm_models()
```
### LLMモデルの呼び出し
LLMモデルを呼び出し、LLMメッセージを返します。非ストリーミング。
```python
async def invoke_llm(
self,
llm_model_uuid: str,
messages: list[provider_message.Message],
funcs: list[resource_tool.LLMTool] = [],
extra_args: dict[str, Any] = {},
timeout: float | None = None,
) -> provider_message.Message:
"""LLMモデルを呼び出す"""
# 使用例
llm_message = await self.plugin.invoke_llm(
llm_model_uuid="llm_model_uuid",
messages=[provider_message.Message(role="user", content="Hello, world!")],
funcs=[],
extra_args={},
)
```
### モデルのストリーミングと使用量
| メソッド | 戻り値 |
| --- | --- |
| `invoke_llm_with_usage(llm_model_uuid, messages, funcs=[], extra_args={}, timeout=None)` | `LLMInvokeResult` |
| `invoke_llm_stream(llm_model_uuid, messages, funcs=[], extra_args={})` | `MessageChunk` の非同期イテレーター |
| `invoke_llm_stream_events(llm_model_uuid, messages, funcs=[], extra_args={})` | `LLMStreamEvent` の非同期イテレーター |
引数は `invoke_llm` と同じです。`timeout` は秒数で、非ストリーミング呼び出しの既定値は 120 秒です。トークン使用量が必要な場合は `invoke_llm_with_usage` を使用します:
```python
from langbot_plugin.api.entities.builtin.provider.message import Message
result = await self.plugin.invoke_llm_with_usage(
llm_model_uuid=model_uuid,
messages=[Message(role="user", content="Hello!")],
)
print(result.message.content)
if result.usage is not None:
print(result.usage.total_tokens)
```
`usage` は `None` の場合があり、`prompt_tokens`、`completion_tokens`、`total_tokens` も欠ける場合があります。未取得は使用量ゼロを意味しません。キャッシュなどの追加フィールドは保持されます。
```python
async for event in self.plugin.invoke_llm_stream_events(
llm_model_uuid=model_uuid,
messages=[Message(role="user", content="Hello!")],
):
if event.chunk is not None:
print(event.chunk.content)
if event.usage is not None:
print(event.usage.model_dump())
```
`LLMStreamEvent` は任意の `chunk` と `usage` を持ち、最後が使用量のみの場合もあります。`invoke_llm_stream` はメッセージ・ツール呼び出しのチャンクのみ返します。`MessageChunk.content` は現在の断片、`all_content` は提供される場合に累積テキストです。累積テキストは置換し、差分だけ追加します。`is_final` のチャンクにもテキストが含まれる場合があります。モデルのストリーミングはプラットフォームに送信しません。送信方法は[実行コンテキスト API](/ja/plugin/dev/apis/agent-run)を参照してください。
### 利用可能な Parser の一覧取得
ホスト上で現在利用可能な Parser プラグインを列挙します。MIME タイプで絞り込むこともできます。
```python
async def list_parsers(self, mime_type: str | None = None) -> list[dict[str, Any]]:
"""List available Parser plugins"""
# 使用例
parsers = await self.plugin.list_parsers(mime_type="application/pdf")
# 各要素には plugin_id、plugin_author、plugin_name、name、description、supported_mime_types が含まれます
```
### 利用可能なツール一覧を取得
現在の LangBot インスタンスで利用可能なすべてのツール(プラグインツールおよび MCP ツール)を取得します。
```python
async def list_tools(self) -> list[dict[str, Any]]:
"""利用可能なすべてのツールを取得
Returns:
ツールのリスト。各要素に以下が含まれます:
- name: ツール名
- label: 表示名(多言語)
- description: ツールの説明(多言語)
- icon: ツールアイコン
- spec: ツール仕様(llm_prompt と parameters を含む)
"""
# 使用例
tools = await self.plugin.list_tools()
for tool in tools:
print(f"Tool: {tool['name']}")
```
### ツール詳細を取得
特定のツールの詳細情報を取得します。
```python
async def get_tool_detail(self, tool_name: str) -> dict[str, Any]:
"""特定のツールの詳細情報を取得
Args:
tool_name: ツール名(metadata.name、例: "get_weather_alerts")
Returns:
ツール詳細 dict(name、label、description、spec を含む)
"""
# 使用例
detail = await self.plugin.get_tool_detail("get_weather_alerts")
print(detail['spec']['parameters'])
```
### ツールを呼び出す
指定されたツールを呼び出します。
```python
async def call_tool(
self,
tool_name: str,
parameters: dict[str, Any],
session: dict[str, Any],
query_id: int,
) -> dict[str, Any]:
"""特定のツールを呼び出す
Args:
tool_name: ツール名(metadata.name)
parameters: ツールパラメータ
session: セッション情報
query_id: クエリ ID
Returns:
ツールの応答 dict
"""
# 使用例
result = await self.plugin.call_tool(
tool_name="get_weather_alerts",
parameters={"state": "CA"},
session={},
query_id=0,
)
print(result)
```
<Tip>
ツール名は `metadata.name`(例: `echo_tool`、`get_weather_alerts`)を使用します。作者やプラグイン名のプレフィックスは不要です。
</Tip>
### プラグイン永続データの設定
プラグインデータを永続的に保存します。このインターフェースを通じて保存されたデータは、このプラグインのみがアクセスできます。値は手動でbytesに変換する必要があります。
```python
async def set_plugin_storage(self, key: str, value: bytes) -> None:
"""プラグインストレージ値を設定する"""
# 使用例
await self.plugin.set_plugin_storage("key", b"value")
```
### プラグイン永続データの取得
```python
async def get_plugin_storage(self, key: str) -> bytes:
"""プラグインストレージ値を取得する"""
# 使用例
plugin_storage = await self.plugin.get_plugin_storage("key")
```
### すべてのプラグイン永続データキーの取得
```python
async def get_plugin_storage_keys(self) -> list[str]:
"""すべてのプラグインストレージキーを取得する"""
# 使用例
plugin_storage_keys = await self.plugin.get_plugin_storage_keys()
```
### プラグイン永続データの削除
```python
async def delete_plugin_storage(self, key: str) -> None:
"""プラグインストレージ値を削除する"""
# 使用例
await self.plugin.delete_plugin_storage("key")
```
### ワークスペース永続データの取得
このインターフェースを通じて保存されたデータは、すべてのプラグインがアクセスできます。値は手動でbytesに変換する必要があります。
```python
async def set_workspace_storage(self, key: str, value: bytes) -> None:
"""ワークスペースストレージ値を設定する"""
# 使用例
await self.plugin.set_workspace_storage("key", b"value")
```
### ワークスペース永続データの取得
```python
async def get_workspace_storage(self, key: str) -> bytes:
"""ワークスペースストレージ値を取得する"""
# 使用例
workspace_storage = await self.plugin.get_workspace_storage("key")
```
### すべてのワークスペース永続データキーの取得
```python
async def get_workspace_storage_keys(self) -> list[str]:
"""すべてのワークスペースストレージキーを取得する"""
# 使用例
workspace_storage_keys = await self.plugin.get_workspace_storage_keys()
```
### ワークスペース永続データの削除
```python
async def delete_workspace_storage(self, key: str) -> None:
"""ワークスペースストレージ値を削除する"""
# 使用例
await self.plugin.delete_workspace_storage("key")
```
### プラグインファイル型設定フィールドデータの取得
```python
async def get_config_file(self, file_key: str) -> bytes:
"""設定ファイル値を取得する"""
# 使用例
file_bytes = await self.plugin.get_config_file("key")
```
これは[``fileまたはarray[file``](/en/plugin/dev/basic-info.html#type-file)型の設定フィールドと組み合わせて使用します。
## ナレッジベース API
これらのAPIは通常のコンポーネントから`self.plugin`経由でアクセスでき、パイプライン制限なしにLangBotインスタンス内のすべてのナレッジベースの一覧取得と検索が可能です。
### すべてのナレッジベースの一覧取得
LangBotインスタンスで利用可能なすべてのナレッジベースを一覧取得します。
```python
async def list_knowledge_bases(self) -> list[dict[str, Any]]:
"""すべてのナレッジベースを一覧取得
Returns:
ナレッジベースのリスト、各要素は以下を含む:
- uuid: ナレッジベースUUID
- name: ナレッジベース名
- description: ナレッジベースの説明
"""
# 使用例
knowledge_bases = await self.plugin.list_knowledge_bases()
for kb in knowledge_bases:
print(f"KB: {kb['name']} ({kb['uuid']})")
```
### ナレッジベースからの検索
任意のナレッジベースから関連ドキュメントを検索します。
```python
async def retrieve_knowledge(
self,
kb_id: str,
query_text: str,
top_k: int = 5,
filters: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
"""ナレッジベースから検索
Args:
kb_id: ナレッジベースUUID(list_knowledge_basesから取得)
query_text: 検索クエリテキスト
top_k: 返す結果の数(デフォルト:5)
filters: オプションのメタデータフィルター
Returns:
検索結果エントリのリスト
"""
# 使用例
results = await self.plugin.retrieve_knowledge(
kb_id="kb-uuid-here",
query_text="システムの設定方法は?",
top_k=3,
)
for entry in results:
print(entry)
```
<Tip>
これらのAPIは`query_id`を必要とせず、通常のコンポーネント(Toolコンポーネントを含む)で使用できます。現在のパイプライン設定に制限されることなく、すべてのナレッジベースにアクセスできます。
</Tip>
## RAG API
これらのAPIは`KnowledgeEngine`コンポーネントがLangBotホストの埋め込みモデル、ベクトルデータベース、ファイルストレージにアクセスするために使用できます。アクセス方法:
- `KnowledgeEngine`コンポーネントクラス内: `self.plugin`オブジェクトの内部メソッド。
### 埋め込みモデルの呼び出し
ホストに設定された埋め込みモデルを使用してテキストのベクトルを生成します。
```python
async def invoke_embedding(
self,
embedding_model_uuid: str,
texts: list[str],
) -> list[list[float]]:
"""埋め込みモデルを使用してベクトルを生成
Args:
embedding_model_uuid: 埋め込みモデルUUID
texts: 埋め込むテキストのリスト
Returns:
ベクトルのリスト、入力テキストごとに1つ
"""
# 使用例
vectors = await self.plugin.invoke_embedding("model_uuid", ["Hello", "World"])
```
### ベクトルの書き込み
ホストのベクトルデータベースにベクトルを書き込みます。
```python
async def vector_upsert(
self,
collection_id: str,
vectors: list[list[float]],
ids: list[str],
metadata: list[dict[str, Any]] | None = None,
documents: list[str] | None = None,
) -> None:
"""ベクトルの書き込み
Args:
collection_id: ターゲットコレクションID
vectors: ベクトルのリスト
ids: ベクトルの一意識別子リスト
metadata: オプションのメタデータリスト
documents: オプションの生テキストドキュメントリスト。全文検索や
混合検索をサポートするバックエンドで必要です。
"""
# 使用例
await self.plugin.vector_upsert(
collection_id="kb_uuid",
vectors=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
ids=["chunk_0", "chunk_1"],
metadata=[{"document_id": "doc1"}, {"document_id": "doc1"}],
documents=["チャンクテキスト 0", "チャンクテキスト 1"],
)
```
### ベクトル検索
ホストのベクトルデータベースで類似ベクトルを検索します。
```python
async def vector_search(
self,
collection_id: str,
query_vector: list[float],
top_k: int = 5,
filters: dict[str, Any] | None = None,
search_type: str = "vector",
query_text: str = "",
) -> list[dict[str, Any]]:
"""ベクトル検索
Args:
collection_id: ターゲットコレクションID
query_vector: 類似性検索のクエリベクトル
top_k: 返す結果の数
filters: オプションのメタデータフィルター
search_type: 検索方式、'vector'、'full_text'、'hybrid' のいずれか
query_text: 生のクエリテキスト、全文検索と混合検索で使用
Returns:
検索結果のリスト(id, score, metadataなどを含むdict)
"""
# 使用例
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
top_k=5,
search_type="hybrid",
query_text="検索クエリ",
)
# 返却フォーマット: [{"id": "chunk_0", "score": 0.123, "metadata": {"document_id": "doc1", ...}}, ...]
```
<Info>
`vector_search`が返す各結果はdictで、`id`(ベクトルID)、`score`(距離スコア)、`metadata`(upsert時に提供したメタデータ)の3つのフィールドを含みます。検索結果にテキスト内容が必要な場合は、取り込み時にmetadataにテキストを保存してください。
</Info>
### ベクトルの削除
ホストのベクトルデータベースからベクトルを削除します。
```python
async def vector_delete(
self,
collection_id: str,
file_ids: list[str] | None = None,
filters: dict[str, Any] | None = None,
) -> int:
"""ベクトルの削除
Args:
collection_id: ターゲットコレクションID
file_ids: 削除するファイルIDのリスト
filters: オプションのメタデータフィルター
Returns:
削除されたアイテム数
"""
# 使用例
deleted = await self.plugin.vector_delete(
collection_id="kb_uuid",
file_ids=["doc_001"],
)
```
<Info>
`filters`パラメータはChromaスタイルの`where`構文によるメタデータフィルタリングをサポートしています。複数のトップレベルキーはAND条件として結合されます。サポートされる演算子:`$eq`、`$ne`、`$gt`、`$gte`、`$lt`、`$lte`、`$in`、`$nin`。
```python
# 暗黙的な $eq
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
filters={"file_id": "abc"},
)
# 比較演算子
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
filters={"created_at": {"$gte": 1700000000}},
)
# リスト演算子
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
filters={"file_type": {"$in": ["pdf", "docx"]}},
)
# フィルターによる削除
deleted = await self.plugin.vector_delete(
collection_id="kb_uuid",
filters={"file_type": {"$eq": "pdf"}},
)
```
**注意:** Chroma、Qdrant、SeekDBは完全なメタデータを保存し、任意のフィールドでフィルタリングできます。MilvusとpgvectorはDB側に`text`、`file_id`、`chunk_uuid`のみ保存しており、それ以外のフィールドでのフィルタリングは無視されます。
</Info>
ホストストレージからアップロードされたファイルの内容を取得します。
```python
async def get_knowledge_file_stream(self, storage_path: str) -> bytes:
"""ファイル内容を取得
Args:
storage_path: ファイルのストレージパス(FileObject.storage_pathから取得)
Returns:
ファイル内容のバイトデータ
"""
# 使用例
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
```
## その他のリソース API
以下も `self.plugin` から呼び出します。
### リランクモデルの呼び出し
```python
async def invoke_rerank(
self, rerank_model_uuid: str, query: str, documents: list[str],
top_k: int | None = None, extra_args: dict[str, Any] | None = None,
timeout: float = 60.0,
) -> list[dict[str, Any]]:
...
scores = await self.plugin.invoke_rerank(
rerank_model_uuid=rerank_model_uuid,
query="How do I configure a bot?",
documents=["Bot configuration guide", "Plugin publishing guide"],
top_k=1,
)
```
結果には通常、元の文書の `index` と関連度 `relevance_score` が含まれます。
### パーサーの呼び出し
```python
async def invoke_parser(
self, plugin_author: str, plugin_name: str, storage_path: str,
mime_type: str, filename: str, metadata: dict[str, Any] | None = None,
) -> dict[str, Any]:
...
parsers = await self.plugin.list_parsers(mime_type="application/pdf")
if parsers:
parser = parsers[0]
parsed = await self.plugin.invoke_parser(
plugin_author=parser["plugin_author"],
plugin_name=parser["plugin_name"],
storage_path=storage_path,
mime_type="application/pdf",
filename="guide.pdf",
)
```
`storage_path` はナレッジ取り込み時の `context.file_object.storage_path` など、ホストの保存先です。プラグインのローカルパスではありません。結果の辞書には `text`、`sections`、`metadata` が含まれます。
### ベクトルレコード一覧
```python
async def vector_list(
self, collection_id: str, filters: dict[str, Any] | None = None,
limit: int = 20, offset: int = 0,
) -> dict[str, Any]:
...
page = await self.plugin.vector_list(
collection_id=collection_id,
filters={"file_id": "doc_001"},
limit=20,
offset=0,
)
for item in page["items"]:
print(item["id"], item.get("metadata"))
```
`items` と `total` を返します。各項目は `id`、`document`、`metadata` を含み、`total` はストレージが取得可能な一致件数です。フィルターは上記のベクトル API と同じ形式です。
### プラグインとコマンド一覧
```python
manifests = await self.plugin.list_plugins_manifest()
commands = await self.plugin.list_commands()
```
いずれも引数なしで、ホストが提供するプラグインのマニフェスト一覧とコマンド一覧を返します。
### Box サンドボックス
`self.plugin` から現在のワークスペースに対して呼び出します。Runner 内では実行権限が自動的に引き継がれます。
| メソッド | 戻り値 | 説明 |
| --- | --- | --- |
| `await self.plugin.get_box_status()` | `BoxStatus` | `enabled`、`available`、上限 `limit`、使用数 `used`、残数 `remaining`、任意の `required_reuse_key`、利用不可理由 `reason` |
| `await self.plugin.list_boxes()` | `list[BoxSession]` | `id` と状態 `status`(`idle`、`running`、`closing`)のスナップショット |
| `await self.plugin.acquire_box(reuse_key, options=None)` | `BoxSession` | 同一キーで再利用、または上限内で作成。`options` は `image` をサポート |
不明な数量は `None` です。残数がゼロでも既存 Box の再利用は可能です。新規作成時の上限は Runtime が原子的に確認します。キーは Runner が計算しますが、`required_reuse_key` が返された場合はその値を使います。異なるワークスペース間では同じキーでも共有されません。
取得後は[Runner コンテキスト](/ja/plugin/dev/apis/agent-run#runner)でバインドしてから、添付のインポートやツール実行を行います。
+134
View File
@@ -0,0 +1,134 @@
---
title: "LLM リクエスト設定"
description: "プラグインと Runner のリトライ、タイムアウト、思考予算、キャッシュ、ストリーム終了"
---
本ページの設定には [Core #2661](https://github.com/langbot-app/LangBot/pull/2661) を含む Core が必要です。マージとリリースは別です。既存の `extra_args` で指定し、プラグインと Runner のシグネチャ、メッセージ型、権限確認は変わりません。[LangBot API](/ja/plugin/dev/apis/common) と[コンテキスト API](/ja/plugin/dev/apis/agent-run)も参照してください。
## リトライとタイムアウト
非ストリーミング呼び出しでリクエスト設定と完全な使用量を扱う例です:
```python
from langbot_plugin.api.entities.builtin.provider.message import Message
result = await self.plugin.invoke_llm_with_usage(
llm_model_uuid=model_uuid,
messages=[Message(role="user", content="Hello!")],
timeout=110,
extra_args={
"request_policy": {
"retries": 1,
"total_timeout": 90,
"first_event_timeout": 30,
"stream_idle_timeout": 30,
"connect_timeout": 10,
"read_timeout": 60,
},
},
)
print(result.message.content)
if result.usage is not None:
print(result.usage.model_dump())
```
Runner の `ctx.api.invoke_llm*` でも同じ `extra_args` を使えますが、実行の権限と残り時間に制限されます。実行コンテキスト内の `self.plugin` 呼び出しにも同じ制限が適用されます。
`timeout=` は SDK/RPC の待機期限(非ストリーミングでは既定 120 秒)です。`request_policy` は Core 内部のリクエストを制御します。総予算は外側の RPC/Runner の期限内に収めてください。Core の予算を増やしても外側の期限は延長されません。ストリーミングメソッドには `timeout=` 引数がなく、SDK/Runner の待機期限が適用されます。
| `request_policy` フィールド | 既定値 | 意味 |
| --- | --- | --- |
| `retries` | `0` | 初回以外の追加試行数、整数 0–5 |
| `total_timeout` | `300` | リトライ待機を含む呼び出し全体の予算 |
| `first_event_timeout` | `120` | 最初の表示文字ではなく、最初の上流イベントを待つ期限 |
| `stream_idle_timeout` | `120` | ストリームイベント間の待機期限 |
| `connect_timeout` | `10` | 接続タイムアウト |
| `read_timeout` | `120` | 読み取りタイムアウト |
| `write_timeout` | `30` | 書き込みタイムアウト |
| `pool_timeout` | `10` | 接続プールの待機期限 |
| `backoff_base` / `backoff_max` | `0.5` / `8` | full jitter を使う指数バックオフの基数と上限 |
| `attribution` | `True` | アプリ識別ヘッダーの自動付与 |
| `conversation_header` | `None` | 生成するセッションヘッダー名(後述) |
時間は秒単位で、タイムアウトは有限の正数です。未知のフィールドは拒否されます。優先順位はインスタンスの `llm_request_policy`、モデルの `extra_args`、呼び出しの `extra_args` の順です。同じ階層では `request_policy.retries` が旧 `num_retries` / `max_retries` より優先されます。呼び出し側の旧パラメーターでもモデル設定を上書きでき、`0` でリトライを無効化できます。旧数値 `extra_args["timeout"]` は読み取り、最初のイベント、ストリーム待機を設定し、総予算は延長しません。
認証失敗、不正なリクエスト、クォータ不足はリトライしません。思考や usage を含む上流イベントを受信した後も再試行しません。`Retry-After` は残りの総予算内に制限されます。Core は管理対象の LiteLLM/プロバイダー SDK 内蔵リトライを無効化しますが、中継サービスは独自に再試行する場合があります。ツール再実行、モデル切り替え、embedding/rerank のリトライは追加しません。旧サードパーティ requester の内部通信設定はその実装が管理します。
## 思考予算
既存の `reasoning_level` は変わりません。数値予算は以下を `extra_args` またはモデルの詳細パラメーターに指定します。`level` を省略すると `enabled` です:
```json
{
"reasoning_config": {"budget_tokens": 2048},
"max_tokens": 4096
}
```
予算と effort レベル(例:`medium`)、`disabled`、プロバイダー独自の思考パラメーターを併用しないでください。Anthropic 直結では予算より大きい `max_tokens` を明示する必要があり、Core は出力上限を自動で増やしません。
優先順位は保存済みモデル `reasoning_config`、モデル `extra_args.reasoning_config`、呼び出し `extra_args.reasoning_config`、明示的な Runner レベル指定です。各オブジェクトが前の設定を全置換し、effort 指定は低優先度の数値予算を削除します。
数値予算はネイティブ Anthropic、Gemini 2.5、対応 Qwen、OpenRouter 経由に変換されます。OpenAI/Codex 直結と adaptive のみの Claude は effort を使います。モデルの `reasoning_capabilities.budget_tokens` で対応と範囲を確認してください。未対応なら HTTP 送信前にエラーになります。上流の制限がより厳しい場合や、OpenRouter が予算を effort に変換する場合もあります。
## Prompt caching
`extra_args.prompt_caching` は Core 専用で、そのまま上流には送信されません。呼び出し設定がモデル設定を全置換します。既定の `{"mode": "provider_default"}` はフィールドを追加しません。ローカルの回答キャッシュではありません。
| 設定 | 動作 |
| --- | --- |
| `{"mode": "openai", "retention": "in_memory"}` | 会話コンテキストがあるとモデルと会話別の `prompt_cache_key` を生成。`retention` は省略可能で、対応モデルでは `24h` も指定可能 |
| `{"mode": "anthropic", "ttl": "5m"}` | 先頭の system/developer 接頭辞の最後のテキストブロックに一時キャッシュマーカーを追加。`ttl` は `5m` または `1h` |
OpenAI モードは対応する互換ルートおよび OpenRouter の OpenAI モデル向けです。明示したネイティブ `prompt_cache_key`、`prompt_cache_retention`、`prompt_cache_options` が優先されます。Anthropic モードはネイティブ Claude と OpenRouter Claude に対応します。既存マーカーがあれば追加せず、ツール/履歴の並べ替えやユーザーメッセージへの追加もしません。適切な接頭辞がなければ何も追加しません。
キャッシュヒントはヒットを保証しません。`usage.model_dump()` の `prompt_tokens_details.cached_tokens`、`cache_read_input_tokens` などで確認します。未提供はゼロではありません。最小接頭辞長、保持期間の対応、料金はプロバイダー次第です。Codex の制限付き Responses ボディはこの設定を受け付けず、既存のサーバー側キャッシュを維持します。汎用 Responses 移行と Gemini 明示的キャッシュリソース管理は今回の対象外です。
## アプリとセッションのヘッダー
OpenRouter 直結では `HTTP-Referer: https://langbot.app`、`X-OpenRouter-Title: LangBot`、会話コンテキストがあれば `x-session-id: lb-<opaque scoped hash>` を自動付与します。独自の中継 URL には自動付与しません。`request_policy.attribution=False` は自動アプリ識別のみを無効化します。明示した `extra_headers` は保持され、大文字小文字を区別せず優先されます。
中継サービスに合わせてセッションヘッダーを指定できます:
```json
{
"request_policy": {"conversation_header": "x-litellm-session-id"},
"extra_headers": {
"HTTP-Referer": "https://langbot.app",
"X-OpenRouter-Title": "LangBot"
}
}
```
対応名は `x-session-id`、`x-litellm-session-id`、`x-langbot-conversation-id` です。最後は独自規約であり共通標準ではなく、自動的なルーティングやキャッシュ効果はありません。生成値は workspace/bot/pipeline/session ごとに分離され、生の ID やプロンプトを含みませんが、仮名化であり匿名化保証ではありません。コンテキストがなければ生成しません。New API などはヘッダーを削除・上書きする可能性があるため、転送設定と実際の出力を確認してください。
## ストリーム終了と互換性
途中でループを抜けるときは `aclosing` で生成器を確実に閉じ、タスクのキャンセル例外は伝播させてください:
```python
from contextlib import aclosing
from langbot_plugin.api.entities.builtin.provider.message import Message
async with aclosing(self.plugin.invoke_llm_stream_events(
llm_model_uuid=model_uuid,
messages=[Message(role="user", content="Hello!")],
extra_args={"request_policy": {"retries": 0, "total_timeout": 90}},
)) as events:
async for event in events:
if event.usage is not None:
print(event.usage.model_dump())
if event.chunk is not None and event.chunk.content:
print(event.chunk.content)
break
```
この例は最初の空でない内容で停止するため、最後の usage を受け取れない場合があります。全文取得では `break` を除きます。最後のイベントが chunk なしの usage のみの場合もあります。`invoke_llm` は引き続き `Message`、`invoke_llm_stream` は `MessageChunk` を返します。使用量には `invoke_llm_with_usage` または `invoke_llm_stream_events` を使います。usage は `None` の場合があり、ネストしたキャッシュ/思考使用量や上流独自フィールドは保持されます。
SDK ≤0.7.11 には、プロキシの早期終了が上流へ即時伝播しない問題があります。確実な終了には **langbot-plugin ≥0.7.12** が必要です。この公開版は [SDK #177](https://github.com/langbot-app/langbot-plugin-sdk/pull/177) の修正を含みます。Core の依存固定だけでは別配置の Runtime やプラグイン開発環境は更新されないため、実行する各環境も更新してください。通常のシグネチャ、ペイロード、戻り値は互換です。総期限は消費時間も含みますが、生成器が `yield` で停止中に消費側を中断せず、次の読み取りで期限切れを確認します。
## 診断
Core の監視レコード `details` に `timing`、完全な `usage`、構造化 `error` を記録します。旧 SDK の例外伝送形式は変更しません。構造化エラーは `code`、`status_code`、`retryable`、`retry_after`、`request_id`、`phase` を含みますが、プラグインが受け取る旧例外にこれらがあるとは限りません。
時間はミリ秒で、`preparation_ms`、`transport_setup_ms`、`first_event_ms`、`first_reasoning_ms`、`first_content_ms`、`first_visible_chunk_ms`、`total_ms`、`retry_wait_ms`、および試行数 `attempts` を記録します。最初のイベントは思考や制御の場合があり、最初の表示チャンクも思考を含む場合があります。`request_started_ms` は SDK 呼び出し時刻で、パケット送信時刻ではありません。欠落は未観測でありゼロではありません。キャンセル前の部分 usage は監視に保持されますが、未提供の使用量は復元できません。
+65
View File
@@ -0,0 +1,65 @@
---
title: "メッセージプラットフォームエンティティ"
---
LangBotは複数のメッセージングプラットフォームをサポートしており、各プラットフォームには異なる`メッセージエンティティ`フォーマットがあります。これらの違いを抽象化するために、統一された標準を実装しました。プラグイン開発者は、プラグイン内でこのページに記載されているメッセージエンティティを習得して使用するだけで、LangBotの内部メッセージ処理ロジックがメッセージの解析と変換を自動的に処理します。
## メッセージチェーン
メッセージングプラットフォーム上の`メッセージ`は、AI対話に使用されるメッセージとは異なります。メッセージングプラットフォーム上のメッセージは、`メッセージチェーン`の形式で記述されます。QQに表示される各独立したメッセージはメッセージチェーンであり、`テキスト`、`画像`、`@メンション`などのさまざまな`メッセージチェーンコンポーネント`を含むことができます。例えば:
![](/images/zh/plugin/dev/apis/plugin_dev_messages_01.png)
これは、1つのPlainコンポーネント(Hello World)と1つのImageコンポーネント(Usagi)を含むメッセージチェーンです。
メッセージチェーンとメッセージチェーンコンポーネントの定義は、`langbot_plugin.api.entities.builtin.platform.message`にあります。
### メッセージチェーンの構築
まず、`langbot_plugin.api.entities.builtin.platform.message`パッケージをインポートして、その中のメッセージコンポーネントを使用してください。
```python
from langbot_plugin.api.entities.builtin.platform.message import *
# テキスト「Hello LangBot」と画像(URLから)を含むメッセージを構築
msg_chain = MessageChain([
Plain(text="Hello LangBot"),
Image(url='https://qchatgpt.rockchin.top/langbot-logo.png')
])
# @全員とテキスト「Hello LangBot」を含むメッセージを構築
msg_chain = MessageChain([
AtAll(),
Plain(text="Hello LangBot")
])
# @特定のメンバーとテキスト「Hello LangBot」を含むメッセージを構築
msg_chain = MessageChain([
At(target=123456),
Plain(text="Hello LangBot")
])
```
現在サポートされているメッセージチェーンコンポーネント:
- `Source` - ソースメッセージチェーン情報。メッセージングプラットフォームから受信したメッセージには、メッセージ情報を記録するためにメッセージチェーンの先頭にこのコンポーネントが含まれます
- `Plain` - プレーンテキストメッセージ
- `Quote` - 引用メッセージ
- `Image` - 画像メッセージ
- `AtAll` - @全員メッセージ
- `At` - @特定のメンバーメッセージ
- `Voice` - 音声メッセージ
- メッセージングプラットフォームのサポートを確認する必要があります
- `Forward` - 転送メッセージ
- 多くのプラットフォームでサポートされていないため、推奨されません
- `File` - ファイルメッセージ
コンポーネントを作成する際は、名前付きパラメータを使用する必要があります。具体的な使用方法については、ソースコード`langbot_plugin.api.entities.builtin.platform.message`の定義を確認してください。
## その他のエンティティ
上記のエンティティに加えて、`Friend`や`Group`などのエンティティもあります。これらのエンティティは`langbot_plugin.api.entities.builtin.platform.entities`で定義されています。
## プラットフォーム API
ユーザー取得、グループ管理、固有の操作には[プラットフォーム API](/ja/plugin/dev/apis/platform)を使用してください。Runner も同じ `self.plugin` を使用し、現在の会話には `ctx.reply()` で返信します。
+271
View File
@@ -0,0 +1,271 @@
---
title: "パイプラインイベントとAPI"
---
LangBotプラグインはパイプラインイベントを登録して処理できます。使用方法については、[コンポーネント: イベントリスナー](/ja/plugin/dev/components/event-listener)を参照してください。
## イベント一覧
一部のイベントには`設定可能な属性`があり、プラグインコードで変更して、その後のLangBot処理で使用できます。
### \*MessageReceived
グループチャットまたはプライベートチャットでメッセージを受信したときにトリガーされます。
```python
class PersonMessageReceived(BaseEventModel):
"""プライベートチャットメッセージを受信したとき"""
event_name: str = "PersonMessageReceived"
launcher_type: str
"""起動元オブジェクトタイプ(person)"""
launcher_id: typing.Union[int, str]
"""送信者ID"""
sender_id: typing.Union[int, str]
"""送信者ID、プライベートチャットの場合はlauncher_idと同じ"""
message_event: platform_events.PersonMessage
"""元のメッセージイベントオブジェクト。送信者情報を含みます。"""
message_chain: platform_message.MessageChain = pydantic.Field(
serialization_alias="message_chain"
)
"""メッセージチェーン"""
class GroupMessageReceived(BaseEventModel):
"""グループチャットメッセージを受信したとき"""
event_name: str = "GroupMessageReceived"
launcher_type: str
"""起動元オブジェクトタイプ(group)"""
launcher_id: typing.Union[int, str]
"""グループID"""
sender_id: typing.Union[int, str]
"""送信者ID"""
message_event: platform_events.GroupMessage
"""元のメッセージイベントオブジェクト。グループと送信者情報を含みます。"""
message_chain: platform_message.MessageChain = pydantic.Field(
serialization_alias="message_chain"
)
"""メッセージチェーン"""
```
### \*NormalMessageReceived
グループチャットまたはプライベートチャットメッセージを受信し、LLMで処理する必要があるメッセージ(コマンドでないメッセージ)であると判定されたときにトリガーされます。
```python
class PersonNormalMessageReceived(BaseEventModel):
"""処理すべきプライベートチャット通常メッセージが判定されたときにトリガーされる"""
event_name: str = "PersonNormalMessageReceived"
launcher_type: str
"""起動元オブジェクトタイプ(person)"""
launcher_id: typing.Union[int, str]
"""起動元オブジェクトID"""
sender_id: typing.Union[int, str]
"""送信者ID、プライベートチャットの場合はlauncher_idと同じ"""
text_message: str
"""メッセージテキスト"""
message_event: platform_events.PersonMessage
"""元のメッセージイベントオブジェクト。送信者情報を含みます。"""
message_chain: platform_message.MessageChain = pydantic.Field(
serialization_alias="message_chain"
)
"""メッセージチェーン"""
# ========== 設定可能な属性 ==========
user_message_alter: typing.Optional[provider_message.ContentElement] = None
"""変更されたメッセージテキスト、langbot_plugin.api.entities.builtin.provider.message.ContentElement型"""
reply_message_chain: typing.Optional[platform_message.MessageChain] = None
"""直接返信メッセージチェーン、デフォルト動作を防止した場合のみ有効"""
class GroupNormalMessageReceived(BaseEventModel):
"""処理すべきグループチャット通常メッセージが判定されたときにトリガーされる"""
event_name: str = "GroupNormalMessageReceived"
launcher_type: str
"""起動元オブジェクトタイプ(group)"""
launcher_id: typing.Union[int, str]
"""グループID"""
sender_id: typing.Union[int, str]
"""送信者ID"""
text_message: str
"""メッセージテキスト"""
message_event: platform_events.GroupMessage
"""元のメッセージイベントオブジェクト。グループと送信者情報を含みます。"""
message_chain: platform_message.MessageChain = pydantic.Field(
serialization_alias="message_chain"
)
"""メッセージチェーン"""
# ========== 設定可能な属性 ==========
user_message_alter: typing.Optional[provider_message.ContentElement] = None
"""変更されたメッセージテキスト、langbot_plugin.api.entities.builtin.provider.message.ContentElement型"""
reply_message_chain: typing.Optional[platform_message.MessageChain] = None
"""直接返信メッセージチェーン、デフォルト動作を防止した場合のみ有効"""
```
### \*CommandSent
<Warning>
使用は推奨されなくなりました。代わりに[コンポーネント: Command](/ja/plugin/dev/components/command)を使用してください。
</Warning>
グループチャットまたはプライベートチャットでコマンドを受信したときにトリガーされます。
```python
class PersonCommandSent(BaseEventModel):
"""処理すべきプライベートチャットコマンドが判定されたときにトリガーされる"""
event_name: str = "PersonCommandSent"
launcher_type: str
launcher_id: typing.Union[int, str]
sender_id: typing.Union[int, str]
command: str
"""コマンドテキスト"""
params: list[str]
"""コマンドパラメータ"""
text_message: str
"""メッセージテキスト"""
is_admin: bool
"""管理者かどうか"""
class GroupCommandSent(BaseEventModel):
"""処理すべきグループチャットコマンドが判定されたときにトリガーされる"""
event_name: str = "GroupCommandSent"
launcher_type: str
launcher_id: typing.Union[int, str]
sender_id: typing.Union[int, str]
command: str
"""コマンドテキスト"""
params: list[str]
"""コマンドパラメータ"""
text_message: str
"""メッセージテキスト"""
is_admin: bool
"""管理者かどうか"""
```
### NormalMessageResponded
メッセージがLLMレスポンスを受信したときにトリガーされます。
```python
class NormalMessageResponded(BaseEventModel):
"""通常メッセージに返信するときにトリガーされる"""
event_name: str = "NormalMessageResponded"
launcher_type: str
launcher_id: typing.Union[int, str]
sender_id: typing.Union[int, str]
session: provider_session.Session
"""セッションオブジェクト"""
prefix: str
"""返信メッセージプレフィックス"""
response_text: str
"""返信メッセージテキスト"""
finish_reason: str
"""レスポンス終了理由"""
funcs_called: list[str]
"""呼び出された関数のリスト"""
# ========== 設定可能な属性 ==========
reply_message_chain: typing.Optional[platform_message.MessageChain] = None
"""返信メッセージコンポーネントリスト、デフォルト動作を防止した場合のみ有効"""
```
### PromptPreProcessing
LLMレスポンスコンテキスト(プロンプト)を構築するときにトリガーされます。
```python
class PromptPreProcessing(BaseEventModel):
"""セッション内のプロンプトを前処理するときにトリガーされる"""
event_name: str = "PromptPreProcessing"
session_name: str
"""セッション名、形式は person_1234567890 または group_1234567890"""
# ========== 設定可能な属性 ==========
default_prompt: list[typing.Union[provider_message.Message, provider_message.MessageChunk]]
"""この会話のシナリオプリセット(システムプロンプト)、変更可能、langbot_plugin.api.entities.builtin.provider.message.Messageまたはlangbot_plugin.api.entities.builtin.provider.message.MessageChunk型"""
prompt: list[typing.Union[provider_message.Message, provider_message.MessageChunk]]
"""この会話の既存メッセージレコード、変更可能、langbot_plugin.api.entities.builtin.provider.message.Messageまたはlangbot_plugin.api.entities.builtin.provider.message.MessageChunk型"""
```
## EventListener コンテキスト
イベントハンドラーは `event_context.event` から現在のイベントを取得します。`EventContext` の完全なプロパティとメソッドは[コンテキスト API](/ja/plugin/dev/apis/agent-run#eventlistener)を参照してください。
### デフォルト動作を阻止
`event_context.prevent_default()` を呼び出すと、現在のイベントのデフォルト処理が停止します。この操作を利用できるイベントは次のとおりです。
<Info>
以下のイベントのみデフォルト動作を阻止できます:
- PersonMessageReceived
- GroupMessageReceived
- PersonNormalMessageReceived
- GroupNormalMessageReceived
- PersonCommandSent
- GroupCommandSent
- NormalMessageResponded
</Info>
`event_context.prevent_postorder()` を呼び出すと、後続プラグインによる現在のイベント処理を停止できます。
+78
View File
@@ -0,0 +1,78 @@
---
title: "プラットフォーム API"
---
ボット UUID を指定してプラットフォームアダプターの API を呼び出します。メッセージチェーンの送信は `self.plugin.send_message()`、グループやユーザーの取得などは以下を使用します。
## プラットフォーム API の呼び出し
```python
async def call_platform_api(
self,
bot_uuid: str,
action: str,
params: dict[str, Any] | None = None,
) -> Any:
...
```
Command、EventListener、Tool などのコンポーネントでは `self.plugin` から呼び出します。戻り値は操作に応じた辞書、リスト、スカラー、`None` で、プラットフォーム SDK のオブジェクトではありません。
```python
group = await self.plugin.call_platform_api(
bot_uuid=bot_uuid,
action="get_group_info",
params={"group_id": "123456"},
)
```
`bot_uuid` は `await self.plugin.get_bots()`、またはパイプラインイベントの `await event_context.get_bot_uuid()` で取得できます。プラットフォーム ID には実際のイベントや設定の値を使用します。
### 主な操作
| `action` | `params` |
| --- | --- |
| `get_user_info` | `user_id` |
| `get_friend_list` | `{}` |
| `get_group_info` | `group_id` |
| `get_group_list` | `{}` |
| `get_group_member_list` | `group_id` |
| `get_group_member_info` | `group_id`, `user_id` |
| `get_message` / `delete_message` | `chat_type`, `chat_id`, `message_id` |
| `set_group_name` | `group_id`, `name` |
| `mute_member` | `group_id`, `user_id`, `duration` |
| `unmute_member` / `kick_member` | `group_id`, `user_id` |
| `leave_group` | `group_id` |
| `approve_friend_request` | `request_id`, `approve`, `remark` |
| `approve_group_invite` | `request_id`, `approve` |
| `get_file_url` | `file_id` |
対応する操作はプラットフォームごとに異なり、ボットの管理者権限が必要な場合があります。`duration` は秒数で、`0` の意味はプラットフォームに依存します。共通の永久ミュート値ではありません。取得結果がキャッシュに依存することもあります。ボットが未起動、未対応の操作、引数不正の場合は例外になります。
## プラットフォーム固有の API
外側の `action` を `call_platform_api` にし、`params` にプラットフォームの操作と引数を指定します。OneBot Omni でボットのアカウント情報を取得する例です:
```python
account = await self.plugin.call_platform_api(
bot_uuid=bot_uuid,
action="call_platform_api",
params={"action": "get_login_info", "params": {}},
)
```
アダプターが宣言した操作のみ呼び出せます。すべてのプラットフォーム API を任意に転送できるわけではありません。プラグインはランタイム経由でホストを呼び出し、`ctx.query.adapter` や `adapter.bot` には直接アクセスしません。
## Runner
Runner の実行中、`self.plugin` によるモデル、ツール、ナレッジベース、ストレージ、プラットフォーム呼び出しは現在の実行と権限に自動的に関連付けられます。`run_id` の指定は不要です。同時実行は互いに分離されます。ハンドラー終了後に実行に紐づく API を呼び続けないでください。
現在のボットは `await ctx.get_bot_uuid()` で取得します。プラットフォーム呼び出しは今回のツール権限に従い、イベント対象だけが許可されている場合は別の対象を指定できません。プラットフォーム固有の操作を含む未許可の操作は拒否されます。デバッグではこれらの呼び出しも Mock を使用します。
```python
tools = await ctx.get_available_tools()
if any(tool["name"] == "event_get_actor" for tool in tools):
actor = await ctx.call_tool("event_get_actor")
await ctx.log(str(actor))
```
+13
View File
@@ -0,0 +1,13 @@
---
title: "プラグイン技術詳細"
---
## アーキテクチャ
<img width="600" src="/images/zh/plugin/dev/plugin_system_arch.png" />
バージョン4.0では、高セキュリティで柔軟性の高い本番環境グレードのプラグインシステムを導入し、開発者に豊富なAPIと使いやすいサポートツールを提供しています。
Plugin Runtimeは、プラグインのライフサイクルを管理し、LangBotとプラグイン間の相互作用を調整するために使用されます。`stdio`と`websocket`の2つの動作モードがあります。LangBotがユーザーによって直接起動される場合(コンテナ内で実行されていない場合)、`stdio`モードを使用します。これは個人ユーザーや軽量環境で一般的です。LangBotがコンテナ内で実行される場合、`websocket`モードを使用します。これは本番環境専用に設計されています。
Plugin Runtimeは、インストールされた各プラグインを自動的に起動し、stdioを通じて相互作用します。プラグイン開発シナリオでは、開発者は`lbp`コマンドラインツールを使用してプラグインを起動し、WebSocketを介して実行中のRuntimeに接続してデバッグできます。
+313
View File
@@ -0,0 +1,313 @@
---
title: "プラグイン設定情報の完成"
---
## マニフェストファイルの修正
プラグインディレクトリ内の`manifest.yaml`ファイルは、プラグインの基本情報を宣言します。これはLangBot UIまたはプラグインマーケットプレイスのインターフェースに表示されます。
```yaml
apiVersion: v1 # 変更しないでください
kind: Plugin # 変更しないでください
metadata:
author: RockChinQ # 作成者、正規表現^[a-zA-Z0-9_-]+$に一致する必要があります
name: HelloPlugin # プラグイン名、プラグインを区別するために使用、正規表現^[a-zA-Z0-9-]+$に一致する必要があります
repository: 'https://github.com/langbot-app/HelloPlugin' # プラグインリポジトリURL
version: 0.1.0 # プラグインバージョン
description:
en_US: 'Hello LangBot Plugin' # プラグイン説明、多言語対応
zh_Hans: 'Hello LangBot Plugin'
label:
en_US: HelloPlugin # プラグインラベル、インターフェースに表示、多言語対応
zh_Hans: HelloPlugin
icon: assets/icon.svg # プラグインアイコン、デフォルトはassets/icon.svg、さまざまな画像形式に置き換え可能
spec:
config: [] # プラグイン設定項目フォーマット
components: {} # プラグインコンポーネントリスト、手動で変更する必要はありません
execution:
python:
path: main.py # 変更しないでください
attr: HelloPlugin # 変更しないでください
```
認証を申請し、複数のインストールで 1 つのランタイムを共有する場合は、`execution` 配下に `sharedRuntime` と `componentModel` も宣言します。詳細は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
プラグインの多言語サポートは[RFC 4646](https://datatracker.ietf.org/doc/html/rfc4646)標準に従います。現在サポートされている言語は次のとおりです:
- `en_US` 英語(必須)
- `zh_Hans` 簡体字中国語
- `zh_Hant` 繁体字中国語
- `ja_JP` 日本語
- `vi_VN` ベトナム語
- `th_TH` タイ語
- `es_ES` スペイン語
## プラグイン設定項目フォーマット
`manifest.yaml`ファイルの`spec.config`で宣言されたフィールドは、LangBotによってユーザーが入力するための設定項目フォームとしてレンダリングされます。プラグインは、SDKが提供するAPIを通じて、ユーザーが入力した設定項目を取得できます(後のセクションを参照)。
例えば:
```yaml
spec:
config:
- name: github_token # 必須; 設定項目名、プラグイン内での取得に使用
type: string # 必須; 設定項目タイプ、string、integer、float、boolean、select、prompt-editor、llm-model-selector、bot-selector、tools-selectorなどをサポート
label: # 必須; 設定項目表示名、多言語サポート。言語コードはRFC 4646標準に従います。
en_US: Github Token
zh_Hans: Github Token
description: # 設定項目説明、多言語サポート。オプション。
en_US: Image downloading requires a Github token
zh_Hans: 如果不填的話、图片可能會下載失败
default: '' # 設定項目のデフォルト値、オプション。
required: false # 設定項目が必須かどうか、オプション。
show_if: # 条件付きレンダリング(条件を満たす場合にのみ表示)、オプション。
field: mode
operator: eq
value: mode1
- name: mode
type: select
...
```
設定項目でサポートされるタイプとフィールドは次のとおりです:
### 条件付きレンダリング (`show_if`)
すべてのタイプの設定項目は、`show_if` フィールドを使用した条件付きレンダリングをサポートしています。同じ(または関連する)フォーム内の他のフィールドの値に基づいて、現在のフィールドをレンダリングして表示するかどうかを決定できます。
```yaml
- name: advanced_setting
type: string
show_if:
field: mode # 依存するフィールドの名前
operator: eq # 判定演算子。'eq' (等しい)、'neq' (等しくない)、'in' (リストに含まれる) をサポートします
value: advanced # 判断の基準値
```
> **注意:** クロスフォームのカスケード関係(例:RAG エンジンの `creation_schema` 設定が `retrieval_schema` の値を読み取る、またはその逆)は、LangBot の最新フロントエンドでサポートされています。
### type: string
文字列型。オプションで`options`を設定してプリセット値をサポートできます。設定すると、入力フィールドの横にドロップダウンボタンが表示され、ユーザーがプリセット値をすばやく入力できます。自由入力も可能です。
```yaml
- name: api_key
type: string
...
```
プリセットオプション付き:
```yaml
- name: api_url
type: string
options: # オプションのプリセット値、入力フィールドの横にドロップダウンボタンとして表示。
- name: "https://api.openai.com/v1" # 選択時に入力される値。
label: # 表示名、多言語サポート。
en_US: OpenAI Official
zh_Hans: OpenAI 官方
- name: "https://api.deepseek.com/v1"
label:
en_US: DeepSeek
zh_Hans: DeepSeek
...
```
### type: array[string]
文字列配列。
```yaml
- name: tags
type: array[string]
...
```
### type: integer
整数型。
```yaml
- name: progress
type: integer
...
```
### type: float
浮動小数点型。
```yaml
- name: temperature
type: float
...
```
### type: boolean
ブール型。
```yaml
- name: is_enabled
type: boolean
...
```
### type: select
ドロップダウンメニュー。`options`の設定が必要で、ドロップダウンメニューのオプションを表します。各オプションは`label`をメインテキストとして表示し、`name`(値)を小さなテキストで下に表示します。
```yaml
- name: mode
type: select
allow_custom: true
options: # ドロップダウンオプション、多言語サポート。
- name: mode1 # 値、ドロップダウンで小さなテキストとして表示。
label: # 表示名、多言語サポート。
en_US: Mode 1
zh_Hans: 模式 1
- name: mode2
label: # 表示名、多言語サポート。
en_US: Mode 2
zh_Hans: 模式 2
...
```
`allow_custom: true` を設定すると、プリセットに「カスタム」が追加され、その選択時だけ入力欄が表示されます。保存される値は選択した `name` または入力した文字列であり、「カスタム」というマーカーではありません。ランナーの `spec.config` にも使用できます。
### type: prompt-editor
プロンプトエディター。パイプライン設定ページからプロンプトエディターを表示し、最終結果はOpenAIの`messages`形式で表されます。
```yaml
- name: prompt
type: prompt-editor
...
```
### type: text
大きなテキスト入力。フロントエンドでtextareaとしてレンダリングされ、ユーザーが入力し、文字列としてプラグインに渡されます。
```yaml
- name: prompt
type: text
...
```
### type: file
ファイルアップロード。最大10MBのファイルアップロードをサポートし、`{"file_key": "xxxxx.xxx", "mimetype": "xxxxx"}`形式でプラグインに渡されます。プラグインは`get_config_file` APIを使用してファイルの内容を取得できます。
```yaml
- name: config_file
type: file
accept: 'application/json' # オプション、受け入れるファイルMIMEタイプを指定
...
```
プラグインでファイルを取得する:
```python
# 設定からファイル情報を取得
file_config = self.get_config()['config_file']
file_key = file_config['file_key']
mimetype = file_config['mimetype']
# 出力例: {'file_input': {'file_key': 'plugin_config_d2234fd802054faf80babe0679a97fa9.json', 'mimetype': 'application/json'}}
print(file_config)
# ファイルの内容を取得
file_bytes = await self.get_config_file(file_key)
```
<Info>
一般的なMIMEタイプとファイル拡張子のリファレンス: [MDNドキュメント](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types)
</Info>
### type: array[file]
複数ファイルアップロード。fileタイプと似ていますが、複数のファイルのアップロードをサポートし、`[{"file_key": "xxxxx.xxx", "mimetype": "xxxxx"}]`形式でプラグインに渡されます。
```yaml
- name: resource_files
type: array[file]
accept: 'image/*' # オプション、受け入れるファイルMIMEタイプを指定
...
```
プラグインでファイルを取得する:
```python
# 設定からファイルリストを取得
files_config = self.get_config()['resource_files']
for file_config in files_config:
file_key = file_config['file_key']
mimetype = file_config['mimetype']
# ファイルの内容を取得
file_bytes = await self.get_config_file(file_key)
```
### type: llm-model-selector
LLMモデルセレクター。設定されたLLMモデルを選択できるLLMモデルセレクターを表示し、最終結果はLLMモデルUUIDとして表されます。
```yaml
- name: model
type: llm-model-selector
...
```
### type: bot-selector
Botセレクター。設定されたBotを選択できるBotセレクターを表示し、最終結果はBot UUIDとして表されます。
```yaml
- name: bot
type: bot-selector
...
```
プラグインでBot UUIDを使用する:
```python
# 設定からBot UUIDを取得
bot_uuid = self.get_config()['bot']
print(bot_uuid) # 出力: '550e8400-e29b-41d4-a716-446655440000'
```
### type: tools-selector
ツールセレクター。登録済みのツール(プラグインツールおよび MCP ツール)を複数選択できるセレクターを表示します。最終結果はツール名の配列として表現されます。
```yaml
- name: selected_tools
type: tools-selector
...
```
プラグインで選択されたツール名を使用する:
```python
# 設定から選択されたツール名リストを取得
selected_tools = self.get_config()['selected_tools']
print(selected_tools) # 出力: ['get_weather_alerts', 'web_search']
# 選択されたツールを呼び出す
for tool_name in selected_tools:
result = await self.plugin.call_tool(
tool_name=tool_name,
parameters={"query": "hello"},
session={},
query_id=0,
)
```
## 次のステップ
このチュートリアルでは、プラグイン機能を段階的に完成させる方法を案内します。
- コンポーネントの追加: プラグインコンポーネントはプラグインのコア機能ユニットです。必要に応じて[コンポーネントを追加](/en/plugin/dev/components/add)できます。
+81
View File
@@ -0,0 +1,81 @@
---
title: "コンポーネントの追加"
---
プラグインは、LangBotにさまざまな機能を提供する1つ以上のコンポーネントで構成されています。現在サポートされているコンポーネントタイプには次のものがあります:
- プラグインプロセッサー(Runner):インスタンスを作成してイベントに紐付け、プラグインのコードで処理します。[チュートリアル](/ja/plugin/dev/components/runner)を参照してください。
- Agent ランナー(Runner):Agent の設定で選択する実行コンポーネントです。独立したプラグインプロセッサーとは異なります。
- イベントリスナー(EventListener): パイプライン実行中のイベントをリッスンし、コンテキストやパイプラインを変更します。
- コマンド(Command): `!`(または他の設定されたプレフィックス)で始まるユーザーコマンドメッセージによってトリガーされます。
- ツール(Tool): LangBotの組み込みLocal Agentの実行中にLLMによって呼び出されます。
- RAGエンジン(KnowledgeEngine): LangBotにナレッジベースのインデックス作成と検索機能を提供し、ドキュメントの取り込み、検索、削除を管理します。
- パーサー(Parser): ドキュメント取り込みの前にPDF、Wordなどのバイナリファイルを構造化テキストに解析し、RAGエンジンで使用できるようにします。
- ページ(Page): LangBot WebUIのサイドバーにカスタムビジュアルページを登録し、管理パネルやデータ表示などのインタラクティブなインターフェースを提供します。
## コンポーネントの追加
プラグインディレクトリでコマンドを実行します:
```bash
lbp comp <component_type>
```
例えば、イベントリスナーを追加する場合:
```bash
lbp comp EventListener
```
プロンプトに従って、コンポーネント設定(ある場合)を入力します。
```bash
➜ HelloPlugin > lbp comp EventListener
Generating component EventListener...
Component EventListener generated successfully.
組件 EventListener 生成成功。
➜ HelloPlugin >
```
<Info title="">
CLIはプラグインディレクトリに`components`ディレクトリを生成し、その中に対応するコンポーネントディレクトリを作成します。
```
.
├── assets
│ └── icon.svg
├── components
│ ├── __init__.py
│ └── event_listener
│ ├── __init__.py
│ ├── default.py
│ └── default.yaml
├── main.py
├── manifest.yaml
├── README.md
└── requirements.txt
```
同時に、プラグインの`manifest.yaml`にコンポーネント検出設定が追加されます:
```yaml
...
components:
EventListener:
fromDirs:
- path: components/event_listener/
...
```
コンポーネントを削除するには、対応する情報を手動で削除できます。
</Info>
## コンポーネントタイプ
各コンポーネントの詳細な使用方法については、次を参照してください:
- [Runner](./runner)
- [EventListener](./event-listener)
- [Command](./command)
- [Tool](./tool)
- [KnowledgeEngine](./knowledge-engine)
- [Parser](./parser)
- [Page](./page)
+226
View File
@@ -0,0 +1,226 @@
---
title: "コンポーネント: Command"
---
Commandコンポーネントは、`!`(または他の設定されたプレフィックス)で始まるユーザーコマンドメッセージによってトリガーされます。`!help`コマンドをトリガーする例を次に示します:
<img width="600" src="/images/zh/plugin/dev/components/command_use.png" />
## Commandコンポーネントの追加
1つのプラグインには任意の数のコマンドを含めることができます。プラグインディレクトリでコマンド`lbp comp Command`を実行し、プロンプトに従ってコマンド設定を入力します。
```bash
➜ HelloPlugin > lbp comp Command
Generating component Command...
Command name: info
Command description: Show information of the query
Component Command generated successfully.
組件 Command 生成成功。
```
これにより、`components/commands/`ディレクトリに`info.yaml`と`info.py`ファイルが生成されます。`.yaml`ファイルは`!info`コマンドの基本情報を定義し、`.py`ファイルはコマンドハンドラです:
```bash
➜ HelloPlugin > tree
...
├── components
│ ├── __init__.py
│ ├── commands
│ │ ├── __init__.py
│ │ ├── info.py
│ │ └── info.yaml
...
```
## マニフェストファイル: Commandコンポーネント
```yaml
apiVersion: v1 # 変更しないでください
kind: Command # 変更しないでください
metadata:
name: info # コマンド名、ユーザーは!infoでこのコマンドをトリガーします
label: # コマンド表示名、LangBot UIに表示、多言語対応
en_US: Info
zh_Hans: Info
description: # コマンド説明、LangBot UIに表示、多言語対応。オプション。
en_US: 'Show information of the query'
zh_Hans: '発送此次消息的詳細信息'
spec:
execution:
python:
path: info.py # コマンドハンドラ、変更しないでください
attr: Info # コマンドハンドラクラス名、info.pyのクラス名と一致
```
## プラグイン処理
以下のコードがデフォルトで生成されます(`components/command/<command_name>.py`)。`Info`クラスの`initialize`メソッドでサブコマンド処理ロジックを登録して実装する必要があります。
```python
# Auto generated by LangBot Plugin SDK.
# Please refer to https://langbot.app/docs/en/plugin/dev/tutor.html for more details.
from __future__ import annotations
from typing import Any, AsyncGenerator
from langbot_plugin.api.definition.components.command.command import Command, Subcommand
from langbot_plugin.api.entities.builtin.command.context import ExecuteContext, CommandReturn
class Info(Command):
async def initialize(self):
await super().initialize()
"Fill with your code here"
```
サブコマンドの追加:
```python
...
class Info(Command):
async def initialize(self):
await super().initialize()
@self.subcommand(
name="", # 空文字列はルートコマンドを表す
help="Show information of the query", # コマンドヘルプ情報
usage="info", # コマンド使用例、コマンドヘルプに表示
aliases=["i"], # コマンドエイリアス
)
async def send(self, context: ExecuteContext) -> AsyncGenerator[CommandReturn, None]:
print(context)
reply_text = f"Query ID: {context.query_id}\n"
reply_text += f"command: {context.command}\n"
reply_text += f"command_text: {context.command_text}\n"
reply_text += f"params: {context.params}\n"
reply_text += f"crt_params: {context.crt_params}\n"
reply_text += f"privilege: {context.privilege}\n"
reply_text += f"session: {context.session.launcher_type.value}_{context.session.launcher_id}\n"
yield CommandReturn(
text=reply_text,
)
@self.subcommand(
name="field",
help="Show information of the field",
usage="info field",
aliases=["f"],
)
async def field(self, context: ExecuteContext) -> AsyncGenerator[CommandReturn, None]:
print(context)
field_name = context.crt_params[0]
field_value = getattr(context, field_name)
yield CommandReturn(
text=f"{field_name}: {field_value}",
)
```
このコードでは、`@self.subcommand`デコレータを通じて`send`関数がサブコマンドとして登録され、コマンドコンテキスト(ExecuteContext)情報を出力し、それを返信メッセージに連結します。
### サブコマンド登録
`name`はサブコマンド名です。空のままにすると、メインコマンドを処理することを意味します。空でない場合、2番目のパラメータをコマンドとして照合します。例えば:
- `!info`は、name=""のサブコマンドに一致
- `!info field`は、name="field"のサブコマンドに一致
- `!info field value`は、name="field"のサブコマンドに一致し、`value`がサブコマンドパラメータ
- 特に、`name="*"`は、一致しないすべての第1レベルサブコマンドに一致し、`info`の後の各セクションをパラメータとして渡します。例えば`!info 123`、`!info abc`。ユーザーの入力に応じて、`context.crt_params`から`['123']`または`['abc']`を取得できます。
サブコマンド関数では、`context`変数を通じてコマンドパラメータを読み取ることができます。
上記のコマンドの効果は画像に示されています:
<img width="600" src="/images/zh/plugin/dev/components/info_command_demo.png" />
### コマンドコンテキスト
```python
class ExecuteContext(pydantic.BaseModel):
"""単一コマンド実行コンテキスト"""
query_id: int
"""リクエストID"""
session: provider_session.Session
"""このメッセージのセッションオブジェクト"""
command_text: str
"""完全なコマンドテキスト"""
command: str
"""コマンド名"""
crt_command: str
"""現在のコマンド
マルチレベルコマンドでは、crt_commandは現在のコマンド、commandはルートコマンド。
例: !plugin on Webwlkr
pluginを処理する場合、commandはplugin、crt_commandはplugin
onを処理する場合、commandはplugin、crt_commandはon
"""
params: list[str]
"""コマンドパラメータ
コマンド全体をスペースで分割した後のパラメータリスト
"""
crt_params: list[str]
"""現在のコマンドパラメータ
マルチレベルコマンドでは、crt_paramsは現在のコマンドパラメータ、paramsはルートコマンドパラメータ。
例: !plugin on Webwlkr
pluginを処理する場合、paramsは['on', 'Webwlkr']、crt_paramsは['on', 'Webwlkr']
onを処理する場合、paramsは['on', 'Webwlkr']、crt_paramsは['Webwlkr']
"""
privilege: int
"""セッション権限レベル"""
...
```
### コマンド戻り値
コマンド戻り値(CommandReturn)は、現在テキスト、画像(image_url、画像リンク)、エラーの返却をサポートしています。
```python
yield CommandReturn(
text=reply_text,
)
yield CommandReturn(
image_url=image_url,
)
```
コマンド処理は複数のメッセージを返すことをサポートしているため、`yield`ステートメントを使用してメッセージを返してください。
具体的な戻り値については、`CommandReturn`の定義を参照してください: [langbot_plugin.api.entities.builtin.command.context.CommandReturn](https://github.com/langbot-app/langbot-plugin-sdk/tree/main/src/langbot_plugin/api/entities/builtin/command/context.py)
## 1 つの installation、複数の用途
1 つのコマンドは**複数の会話から同時に**起動されます:
- 設定は呼び出しごとに読み取ってください。`initialize()` で読み取ってインスタンスにキャッシュしないでください。
- コマンド引数、戻り値、会話データをインスタンスフィールドやモジュールレベルの変数に残さないでください。`ExecuteContext` は今回の実行でのみ有効です。
- 同時起動ではインスタンスフィールドの書き込みが交錯します。「現在の会話」のような状態をそこに保存しないでください。呼び出しをまたいで保持するデータは Host ストレージに置き、キーに会話の識別子を含めてください。
- installation 単位でプロセス内キャッシュを保持する場合は、`on_installation_revoked(binding)` で解放してください。
完全な仕様は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
## 次のステップ
コマンド登録とコマンド実行の基本情報を学びました。次は:
- 完全な [`ExecuteContext` API リファレンス](/ja/plugin/dev/apis/agent-run#command)を確認します。
- [LangBot API](/ja/plugin/dev/apis/common)を確認します。
@@ -0,0 +1,119 @@
---
title: "コンポーネント: イベントリスナー"
---
<Info>
このコンポーネントはパイプラインの処理を拡張します。メッセージ受信やメンバー参加などの Bot イベントを処理する場合は、[イベントプロセッサー](/ja/plugin/dev/components/runner)を参照してください。
</Info>
LangBotのパイプラインは実行時にイベントを生成し、プラグインがフックして処理できるようにします。各プラグインは1つのイベントリスナー(components/event_listener/default.yaml)のみを持つことができますが、その中で任意の数のイベントを登録できます。
## イベントリスナーコンポーネントの追加
プラグインディレクトリでコマンド`lbp comp EventListener`を実行します。イベントリスナーコンポーネントの作成には設定は必要ありません。
```bash
➜ HelloPlugin > lbp comp EventListener
Generating component EventListener...
Component EventListener generated successfully.
組件 EventListener 生成成功。
```
これにより、`components/event_listener/`ディレクトリに`default.yaml`と`default.py`ファイルが生成されます。`.yaml`ファイルはイベントリスナーの基本情報を定義し、`.py`ファイルはイベントリスナーハンドラです:
```bash
➜ HelloPlugin > tree
...
├── components
│ ├── __init__.py
│ └── event_listener
│ ├── __init__.py
│ ├── default.py
│ └── default.yaml
...
```
## マニフェストファイル: イベントリスナー
各プラグインは1つのイベントリスナーのみを持つことができるため、マニフェストファイルでの変更は必要ありません。
## プラグイン処理
以下のコードがデフォルトで生成されます(components/event_listener/default.py)。`DefaultEventListener`クラスの`initialize`メソッドでイベント処理ロジックを登録して実装する必要があります。
```python
# Auto generated by LangBot Plugin SDK.
# Please refer to https://langbot.app/docs/en/plugin/dev/tutor.html for more details.
from __future__ import annotations
from langbot_plugin.api.definition.components.common.event_listener import EventListener
from langbot_plugin.api.entities import events, context
class DefaultEventListener(EventListener):
async def initialize(self):
await super().initialize()
"Fill with your code here"
```
特定のイベントのリスナーを追加:
```python
...
class DefaultEventListener(EventListener):
async def initialize(self):
await super().initialize()
@self.handler(events.PersonMessageReceived)
async def handler(event_context: context.EventContext):
print("Hello LangBot Plugin!")
print(event_context)
await event_context.reply(
platform_message.MessageChain([
platform_message.Plain(text=f"Hello from Nahida Plugin!"),
])
)
```
このコードは、`PersonMessageReceived`(プライベートチャットからのメッセージを受信)イベントのリスナーを登録し、イベントがトリガーされたときにイベントコンテキスト(EventContext)情報を出力し、イベントコンテキストAPIを呼び出してメッセージで返信します。
EventContextはこのイベントトリガーの共通コンテキスト情報を保存し、EventContext.eventはPersonMessageReceived(または他の対応するイベントタイプ)のオブジェクトで、イベントの詳細情報を保存します。
```python
# EventContext定義の概要
class EventContext(pydantic.BaseModel):
"""イベントコンテキスト、このイベント実行の情報を保存"""
...
event: pydantic.SerializeAsAny[BaseEventModel]
"""イベントオブジェクト、具体的な型はハンドラ登録時に指定された型、events.pyで見つけることができます"""
...
```
## イベント登録
イベント登録は`@self.handler`デコレータを通じて実装され、デコレータのパラメータはイベントタイプです。監視可能なすべてのイベントは`langbot_plugin.api.entities.builtin.events`で定義されています。
## 1 つの installation、複数の用途
同じイベントリスナーは**複数の Workspace、複数のイベントから同時に**呼び出されます:
- イベント単位の状態(カウンタ、直近のイベント、会話マップなど)をインスタンスに蓄積しないでください。同時実行のコールバックが互いを上書きします。
- コールバックは現在の `EventContext` からのみデータを取得し、コンテキストやそのフィールドを呼び出しをまたいで再利用しないでください。
- 呼び出しをまたいで保持するデータは Host ストレージに置き、キーに installation の識別子とイベントに関連する識別子(Workspace、会話など)を含めてください。
- installation 単位でプロセス内状態を保持する場合は、`on_installation_revoked(binding)` で解放してください。
完全な仕様は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
## 次のステップ
イベントリスナーの基本情報を学びました。次は:
- [パイプラインイベント](/ja/plugin/dev/apis/pipeline-events)でイベントの種類とフィールドを確認します。
- 完全な [`EventContext` API リファレンス](/ja/plugin/dev/apis/agent-run#eventlistener)を確認します。
- [LangBot API](/ja/plugin/dev/apis/common)を確認します。
@@ -0,0 +1,491 @@
---
title: "コンポーネント: 知識エンジン"
---
RAGエンジン(KnowledgeEngine)コンポーネントにより、プラグインはLangBotにナレッジベースの完全なインデックス作成と検索機能を提供できます。ユーザーがLangBotでナレッジベースを作成する際、プラグインが提供するRAGエンジンを選択して、ドキュメントの取り込み、検索、削除を管理できます。また、プラグインを通じてDify、RAGFlow、FastGPTなどの業界先進のナレッジベースサービスとブリッジすることも可能です。
## RAGエンジンコンポーネントの追加
1つのプラグインには任意の数のRAGエンジンを追加できます。プラグインディレクトリでコマンド`lbp comp KnowledgeEngine`を実行し、プロンプトに従ってRAGエンジンの設定を入力します。
```bash
➜ MyRAGPlugin > lbp comp KnowledgeEngine
Generating component KnowledgeEngine...
Knowledge Engine name: simple_rag
Knowledge Engine description: A simple Knowledge Engine with built-in chunking and retrieval
Component KnowledgeEngine generated successfully.
組件 KnowledgeEngine 生成成功。
```
これにより、`components/knowledge_engine/`ディレクトリに`simple_rag.yaml`と`simple_rag.py`ファイルが生成されます。`.yaml`ファイルはRAGエンジンの基本情報と設定スキーマを定義し、`.py`ファイルはこのエンジンのハンドラです:
```bash
➜ MyRAGPlugin > tree
...
├── components
│ ├── __init__.py
│ └── knowledge_engine
│ ├── __init__.py
│ ├── simple_rag.py
│ └── simple_rag.yaml
...
```
## マニフェストファイル: RAGエンジン
```yaml
apiVersion: v1 # 変更しないでください
kind: KnowledgeEngine # 変更しないでください
metadata:
name: simple_rag # RAGエンジン名、このエンジンを識別するために使用
label:
en_US: Simple Knowledge Engine # エンジン表示名、LangBotのUIに表示、多言語対応
zh_Hans: 简易 RAG 引擎
ja_JP: シンプル RAG エンジン
description:
en_US: 'A simple Knowledge Engine with built-in chunking and retrieval'
zh_Hans: '内置分块和检索能力的简易 RAG 引擎'
ja_JP: 'チャンキングと検索を内蔵したシンプルなRAGエンジン'
spec:
creation_schema: # ナレッジベース作成時にユーザーが入力する設定パラメータ
- name: chunk_size
label:
en_US: Chunk Size
zh_Hans: 分块大小
type: integer
required: false
default: 500
- name: chunk_overlap
label:
en_US: Chunk Overlap
zh_Hans: 分块重叠
type: integer
required: false
default: 50
retrieval_schema: # 検索時に設定可能なパラメータ
- name: score_threshold
label:
en_US: Score Threshold
zh_Hans: 分数阈值
type: float
required: false
default: 0.5
execution:
python:
path: simple_rag.py # エンジンハンドラ、変更しないでください
attr: SimpleRag # エンジンハンドラのクラス名、simple_rag.pyのクラス名と一致
```
設定項目フォーマットのリファレンスについては、[プラグインマニフェスト設定フォーマット](/ja/plugin/dev/basic-info)を参照してください。
### creation_schema と retrieval_schema
旧バージョンの`KnowledgeRetriever`コンポーネントが単一の`spec.config`を使用していたのとは異なり、KnowledgeEngineは2つの独立したスキーマを使用します:
- **creation_schema**: ナレッジベース作成時にユーザーが入力するパラメータ。取り込みと検索時に`creation_settings`を通じてプラグインに渡されます。
- **retrieval_schema**: ナレッジベースのクエリ時にユーザーが調整できるパラメータ。`retrieval_settings`を通じてプラグインに渡されます。
## 能力宣言
KnowledgeEngineはサポートする能力を宣言でき、LangBotはこれらの能力宣言に基づいてUIの表示と利用可能な操作を決定します:
```python
from langbot_plugin.api.definition.components.knowledge_engine.engine import KnowledgeEngine, KnowledgeEngineCapability
class SimpleRag(KnowledgeEngine):
@classmethod
def get_capabilities(cls) -> list[str]:
return [
KnowledgeEngineCapability.DOC_INGESTION, # ドキュメントのアップロードと処理をサポート
]
```
利用可能な能力定数:
| 能力 | 説明 |
|------|------|
| `DOC_INGESTION` | ドキュメントのアップロードと処理をサポート。宣言すると、LangBotはナレッジベースの詳細に「ドキュメント」タブを表示します。プラグインが他のナレッジベースサービスとのブリッジ用である場合や、ユーザーによるドキュメントのアップロードが不要な場合は、この能力宣言を省略できます。 |
| `DOC_PARSING` | ネイティブドキュメント解析(ファイルからテキストへの抽出)をサポート。宣言すると、ユーザーはドキュメントアップロード時にRAGエンジンの内蔵解析機能を選択でき、外部[Parserプラグイン](./parser)が不要になります。未宣言の場合、ドキュメントをアップロードするには外部Parserプラグインのインストールが必要です。 |
<Info>
その他の検索動作(リランキング、ハイブリッド検索など)はプラグインの`retrieval_schema`で制御され、能力フラグは不要です。
</Info>
## プラグインハンドラ
以下のコードがデフォルトで生成されます(`components/knowledge_engine/<エンジン名>.py`)。`ingest`、`retrieve`、`delete_document`の3つのコアメソッドを実装する必要があります。完全なコードは[langbot-plugin-demo](https://github.com/langbot-app/langbot-plugin-demo)の`SimpleKnowledgeEngine`で見つけることができます。
```python
from langbot_plugin.api.definition.components.knowledge_engine.engine import KnowledgeEngine, KnowledgeEngineCapability
from langbot_plugin.api.entities.builtin.rag.models import (
IngestionContext,
IngestionResult,
)
from langbot_plugin.api.entities.builtin.rag.context import (
RetrievalContext,
RetrievalResponse,
RetrievalResultEntry,
)
from langbot_plugin.api.entities.builtin.rag.enums import DocumentStatus
from langbot_plugin.api.entities.builtin.provider.message import ContentElement
class SimpleRag(KnowledgeEngine):
@classmethod
def get_capabilities(cls) -> list[str]:
return [KnowledgeEngineCapability.DOC_INGESTION]
async def on_knowledge_base_create(self, kb_id: str, config: dict) -> None:
"""ナレッジベース作成時のコールバック、リソースの初期化に使用できます"""
pass
async def on_knowledge_base_delete(self, kb_id: str) -> None:
"""ナレッジベース削除時のコールバック、リソースのクリーンアップに使用できます"""
pass
async def ingest(self, context: IngestionContext) -> IngestionResult:
"""ドキュメントをナレッジベースに取り込む"""
# 1. ファイル内容を取得
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
# 2. ドキュメントを解析してチャンクに分割
text = file_bytes.decode('utf-8')
chunk_size = context.creation_settings.get('chunk_size', 500)
chunk_overlap = context.creation_settings.get('chunk_overlap', 50)
chunks = self._split_text(text, chunk_size, chunk_overlap)
# 3. ホストの埋め込みモデルを使用してベクトルを生成
embedding_model_uuid = context.creation_settings.get('embedding_model_uuid', '')
vectors = await self.plugin.invoke_embedding(embedding_model_uuid, chunks)
# 4. ホストのベクトルデータベースに書き込み
collection_id = context.get_collection_id()
ids = [f"{context.file_object.metadata.document_id}_{i}" for i in range(len(chunks))]
metadata = [{"document_id": context.file_object.metadata.document_id, "chunk_index": i, "text": chunks[i]} for i in range(len(chunks))]
await self.plugin.vector_upsert(collection_id, vectors, ids, metadata)
return IngestionResult(
document_id=context.file_object.metadata.document_id,
status=DocumentStatus.COMPLETED,
chunks_created=len(chunks),
)
async def retrieve(self, context: RetrievalContext) -> RetrievalResponse:
"""ナレッジベースから関連コンテンツを検索"""
# 1. クエリベクトルを生成
embedding_model_uuid = context.creation_settings.get('embedding_model_uuid', '')
query_vectors = await self.plugin.invoke_embedding(embedding_model_uuid, [context.query])
query_vector = query_vectors[0]
# 2. ベクトル検索
collection_id = context.get_collection_id()
results = await self.plugin.vector_search(collection_id, query_vector, top_k=context.retrieval_settings.get('top_k', 5))
# 3. 検索結果に変換
entries = []
for r in results:
entry = RetrievalResultEntry(
id=r.get('id', ''),
content=[ContentElement.from_text(r.get('metadata', {}).get('text', ''))],
metadata=r.get('metadata', {}),
distance=r.get('score', 0.0),
)
entries.append(entry)
return RetrievalResponse(
results=entries,
total_found=len(entries),
metadata={},
)
async def delete_document(self, kb_id: str, document_id: str) -> bool:
"""ナレッジベースからドキュメントと関連データを削除"""
collection_id = kb_id
deleted = await self.plugin.vector_delete(collection_id, file_ids=[document_id])
return deleted > 0
```
### ライフサイクルフック
KnowledgeEngineは、ナレッジベースの作成・削除時に呼び出される2つのライフサイクルフックを提供します:
```python
async def on_knowledge_base_create(self, kb_id: str, config: dict) -> None:
"""このエンジンを使用するナレッジベースが作成された時のコールバック
Args:
kb_id: ナレッジベースUUID
config: ユーザーが作成時に入力した設定(creation_schemaのフィールド)
"""
async def on_knowledge_base_delete(self, kb_id: str) -> None:
"""このエンジンを使用するナレッジベースが削除された時のコールバック
Args:
kb_id: ナレッジベースUUID
"""
```
### ドキュメントの取り込み
`ingest`メソッドは、ユーザーがナレッジベースにドキュメントをアップロードした時に呼び出されます:
```python
async def ingest(self, context: IngestionContext) -> IngestionResult:
```
**IngestionContext**には以下の情報が含まれます:
```python
class IngestionContext(pydantic.BaseModel):
file_object: FileObject # 取り込むファイルオブジェクト
knowledge_base_id: str # ターゲットのナレッジベースID
collection_id: str | None # ベクトルコレクションID(デフォルトはknowledge_base_idにフォールバック)
creation_settings: dict # ナレッジベース作成時の設定パラメータ
parsed_content: ParseResult | None # 外部Parserプラグインの事前解析コンテンツ(存在する場合)
```
**FileObject**にはファイルのメタ情報が含まれます:
```python
class FileObject(pydantic.BaseModel):
metadata: FileMetadata # filename, file_size, mime_type, document_id, knowledge_base_id等を含む
storage_path: str # ストレージシステム内のファイルパス
```
**IngestionResult**は取り込み結果を返す必要があります:
```python
class IngestionResult(pydantic.BaseModel):
document_id: str # ドキュメントID
status: DocumentStatus # 処理ステータス: COMPLETED / FAILED
chunks_created: int = 0 # 作成されたチャンク数
error_message: str | None = None # 失敗時のエラーメッセージ
metadata: dict = {} # 追加メタデータ
```
### ナレッジ検索
`retrieve`メソッドは、ナレッジベースがクエリされた時に呼び出されます:
```python
async def retrieve(self, context: RetrievalContext) -> RetrievalResponse:
```
**RetrievalContext**には以下の情報が含まれます:
```python
class RetrievalContext(pydantic.BaseModel):
query: str # クエリテキスト
knowledge_base_id: str | None # ナレッジベースID
collection_id: str | None # ベクトルコレクションID
retrieval_settings: dict # 検索時の設定パラメータ(retrieval_schemaのフィールド)
creation_settings: dict # ナレッジベース作成時の設定パラメータ
filters: dict # メタデータフィルター条件(Chromaスタイルwhere構文)
```
<Info>
LangBotホストは`retrieval_settings`をプラグインに渡す前にデフォルトの`top_k`値(デフォルト5)を自動的に注入します。プラグインは`context.retrieval_settings.get('top_k', 5)`でアクセスできます。
`filters`フィールドには`retrieval_settings`から抽出されたChromaスタイルの`where`フィルター条件が含まれます。呼び出し元が検索設定で`filters`を指定した場合(例:時間範囲、ファイルタイプ、カスタムメタデータフィールドによるドキュメントフィルタリング)、ホストがこのフィールドを設定し、プラグインは検索時にフィルタリングを適用できます。フィルターが指定されていない場合、このフィールドは空のdictです。
</Info>
**RetrievalResponse**は検索結果を返す必要があります:
```python
class RetrievalResponse(pydantic.BaseModel):
results: list[RetrievalResultEntry] # 検索結果リスト
total_found: int # 一致した総数
metadata: dict # 追加メタデータ
```
**RetrievalResultEntry**は単一の検索結果を表します:
```python
class RetrievalResultEntry(pydantic.BaseModel):
id: str # 結果ID
content: list[ContentElement] # コンテンツ、ContentElement.from_text()を使用して作成
metadata: dict # メタデータ
distance: float # 距離スコア(小さいほど関連性が高い)
score: float | None = None # 類似度スコア(大きいほど関連性が高い)
```
### ドキュメントの削除
`delete_document`メソッドは、ユーザーがナレッジベースからドキュメントを削除した時に呼び出されます:
```python
async def delete_document(self, kb_id: str, document_id: str) -> bool:
"""ドキュメントと関連データを削除
Args:
kb_id: ナレッジベースID
document_id: ドキュメントID
Returns:
削除が成功したかどうか
"""
```
## ホストRAG API
KnowledgeEngineコンポーネントは`self.plugin`を通じて、LangBotホストが提供する埋め込みモデル呼び出し、ベクトルデータベース操作、ファイル読み取りなどのRAG関連APIを呼び出すことができます。
### 埋め込みモデルの呼び出し
```python
async def invoke_embedding(
self,
embedding_model_uuid: str,
texts: list[str],
) -> list[list[float]]:
"""ホストの埋め込みモデルを使用してベクトルを生成
Args:
embedding_model_uuid: 埋め込みモデルUUID
texts: 埋め込むテキストのリスト
Returns:
ベクトルのリスト、入力テキストごとに1つ
"""
# 使用例
vectors = await self.plugin.invoke_embedding("model_uuid", ["Hello", "World"])
```
### ベクトルの書き込み
```python
async def vector_upsert(
self,
collection_id: str,
vectors: list[list[float]],
ids: list[str],
metadata: list[dict] | None = None,
) -> None:
"""ホストのベクトルデータベースにベクトルを書き込む
Args:
collection_id: ターゲットコレクションID
vectors: ベクトルのリスト
ids: ベクトルの一意識別子リスト
metadata: オプションのメタデータリスト
"""
# 使用例
await self.plugin.vector_upsert(
collection_id="kb_uuid",
vectors=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
ids=["chunk_0", "chunk_1"],
metadata=[{"document_id": "doc1"}, {"document_id": "doc1"}],
)
```
### ベクトル検索
```python
async def vector_search(
self,
collection_id: str,
query_vector: list[float],
top_k: int = 5,
filters: dict | None = None,
) -> list[dict]:
"""ホストのベクトルデータベースで類似ベクトルを検索
Args:
collection_id: ターゲットコレクションID
query_vector: 類似性検索のクエリベクトル
top_k: 返す結果の数
filters: オプションのメタデータフィルター
Returns:
検索結果のリスト(id, score, metadataなどを含むdict)
"""
# 使用例
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
top_k=5,
)
# 返却フォーマット: [{"id": "chunk_0", "score": 0.123, "metadata": {"document_id": "doc1", ...}}, ...]
```
<Info>
`vector_search`が返す各結果はdictで、`id`(ベクトルID)、`score`(距離スコア)、`metadata`(upsert時に提供したメタデータ)の3つのフィールドを含みます。検索結果にテキスト内容が必要な場合は、取り込み時にmetadataにテキストを保存してください。
</Info>
### ベクトルの削除
```python
async def vector_delete(
self,
collection_id: str,
file_ids: list[str] | None = None,
filters: dict | None = None,
) -> int:
"""ホストのベクトルデータベースからベクトルを削除
Args:
collection_id: ターゲットコレクションID
file_ids: 削除するファイルIDのリスト
filters: オプションのメタデータフィルター
Returns:
削除されたアイテム数
"""
# 使用例
deleted = await self.plugin.vector_delete(
collection_id="kb_uuid",
file_ids=["doc_001"],
)
```
<Info>
`filters`パラメータはChromaスタイルの`where`構文によるメタデータフィルタリングをサポートしています。複数のトップレベルキーはAND条件として結合されます。サポートされる演算子:`$eq`、`$ne`、`$gt`、`$gte`、`$lt`、`$lte`、`$in`、`$nin`。例:`{"file_id": {"$eq": "abc"}}`。
**注意:** Chroma、Qdrant、SeekDBは完全なメタデータを保存し、任意のフィールドでフィルタリングできます。MilvusとpgvectorはDB側に`text`、`file_id`、`chunk_uuid`のみ保存しており、それ以外のフィールドでのフィルタリングは無視されます。
</Info>
```python
async def get_knowledge_file_stream(self, storage_path: str) -> bytes:
"""ホストストレージからファイル内容を取得
Args:
storage_path: ファイルのストレージパス(FileObject.storage_pathから取得)
Returns:
ファイル内容のバイトデータ
"""
# 使用例
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
```
## 1 つの installation、複数の用途
1 つの RAG エンジンは**複数のナレッジベース**から同時に使われ、プラットフォームのストレージキー `(instance, Workspace, owner type, plugin, key)` にはナレッジベースの次元がありません:
- 永続化とキャッシュのキーには**ナレッジベース ID** を含めてください。含めないと、別々のナレッジベースのベクトルやドキュメント状態が互いを上書きします。
- ロックと「要対账」状態は `(installation, ナレッジベース)` 単位で記録してください。**1 つのナレッジベースの取り込み失敗が、同じ installation の他のナレッジベースの取り込みや検索を遮断してはいけません。**
- 手動対账が必要な状態に入るのは、送出済みで結果不明の変更だけにしてください。決定的な検証エラーや送出前のキャンセルは後続の呼び出しに影響させないでください。
- テレメトリや診断の失敗は業務に影響させないでください。ログや送信の失敗で取り込みや検索を中断せず、テナントデータをプロセス全体のシングルトンに書き込まないでください。
- installation 単位でプロセス内キャッシュや状態を保持する場合は、`on_installation_revoked(binding)` で解放してください。
完全な仕様は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
## RAGエンジンのテスト
作成後、プラグインディレクトリでコマンド`lbp run`を実行してデバッグを開始します。その後、LangBotで:
1. 「ナレッジベース」ページに移動
2. 「ナレッジベースを作成」をクリック
3. プラグインが提供するRAGエンジンを選択し、エンジンの`creation_schema`に基づいて設定を入力
4. 作成後、ドキュメントをアップロードして取り込み機能をテスト(エンジンが`DOC_INGESTION`能力を宣言している場合)
5. パイプラインにナレッジベースをバインドし、検索機能をテスト
@@ -0,0 +1,278 @@
---
title: "コンポーネント: ナレッジリトリーバー"
---
ナレッジリトリーバーコンポーネントにより、プラグインはLangBotに外部ナレッジベース検索機能を提供できます。ユーザーがLangBotで外部ナレッジベースを作成する際、プラグインが提供するナレッジリトリーバーを選択してナレッジを検索できます。
<img width="600" src="/images/zh/plugin/dev/components/knowledge_retriever_use.png" />
## ナレッジリトリーバーコンポーネントの追加
1つのプラグインには任意の数のナレッジリトリーバーを追加できます。プラグインディレクトリでコマンド`lbp comp KnowledgeRetriever`を実行し、プロンプトに従ってナレッジリトリーバーの設定を入力します。
```bash
➜ FastGPTRetriever > lbp comp KnowledgeRetriever
Generating component KnowledgeRetriever...
KnowledgeRetriever name: fastgpt
KnowledgeRetriever description: Retrieve knowledge from FastGPT knowledge bases
Component KnowledgeRetriever generated successfully.
```
これにより、`components/knowledge_retriever/`ディレクトリに`fastgpt.yaml`と`fastgpt.py`ファイルが生成されます。`.yaml`ファイルはナレッジリトリーバーの基本情報と設定パラメータを定義し、`.py`ファイルはこのリトリーバーのハンドラです:
```bash
➜ FastGPTRetriever > tree
...
├── components
│ ├── __init__.py
│ └── knowledge_retriever
│ ├── __init__.py
│ ├── fastgpt.py
│ └── fastgpt.yaml
...
```
## マニフェストファイル: ナレッジリトリーバー
```yaml
apiVersion: v1 # 変更しないでください
kind: KnowledgeRetriever # 変更しないでください
metadata:
name: fastgpt # ナレッジリトリーバー名、このリトリーバーを識別するために使用
label:
en_US: FastGPT Knowledge Base # リトリーバー表示名、LangBotのUIに表示、多言語対応
zh_Hans: FastGPT 知識庫
ja_JP: FastGPT ナレッジベース
description:
en_US: 'Retrieve knowledge from FastGPT knowledge bases' # リトリーバー説明、LangBotのUIに表示、多言語対応。オプション
zh_Hans: '従 FastGPT 知識庫中検索知識'
ja_JP: 'FastGPT ナレッジベースから知識を取得'
icon: assets/icon.svg # リトリーバーアイコン、外部ナレッジベース設定ページに表示
spec:
config: # リトリーバー設定パラメータ、ユーザーは外部ナレッジベース作成時にこれらのパラメータを入力する必要があります
- name: api_base_url # パラメータ名
type: string # パラメータタイプ: string、number、boolean、select
label:
en_US: API Base URL
zh_Hans: API 基礎地址
ja_JP: API ベース URL
description:
en_US: 'Base URL for FastGPT API'
zh_Hans: 'FastGPT API 基礎地址'
ja_JP: 'FastGPT API ベース URL'
default: 'http://localhost:3000' # デフォルトパラメータ値
required: true # 必須かどうか
- name: api_key
type: string
label:
en_US: FastGPT API Key
zh_Hans: FastGPT API Key
ja_JP: FastGPT API キー
description:
en_US: 'API key from your FastGPT instance'
zh_Hans: '従您的 FastGPT 実例獲取的 API Key'
ja_JP: 'FastGPT インスタンスから取得した API Key'
default: ''
required: true
- name: search_mode # select型パラメータの例
type: select
label:
en_US: Search Mode
zh_Hans: 搜索模式
ja_JP: 検索モード
description:
en_US: 'The search method to use'
zh_Hans: '使用的搜索方法'
ja_JP: '使用する検索方法'
default: 'embedding'
options: # select型パラメータはオプションリストを定義する必要があります
- name: 'embedding'
label:
en_US: 'Embedding Search'
zh_Hans: '向量搜索'
ja_JP: '埋め込み検索'
- name: 'fullTextRecall'
label:
en_US: 'Full-Text Recall'
zh_Hans: '全文検索'
ja_JP: '全文検索'
- name: using_rerank # boolean型パラメータの例
type: boolean
label:
en_US: Use Re-ranking
zh_Hans: 使用重排序
ja_JP: リランキングを使用
description:
en_US: 'Whether to use re-ranking'
zh_Hans: '是否使用重排序'
ja_JP: 'リランキングを使用するかどうか'
default: false
required: false
execution:
python:
path: fastgpt.py # リトリーバーハンドラ、変更しないでください
attr: FastGPT # リトリーバーハンドラのクラス名、fastgpt.pyのクラス名と一致
```
設定項目フォーマットのリファレンスについては、[プラグインマニフェスト設定フォーマット](/en/plugin/dev/basic-info)を参照してください。
## プラグインハンドラ
以下のコードがデフォルトで生成されます(`components/knowledge_retriever/<retriever_name>.py`)。`FastGPT`クラスの`retrieve`メソッドでナレッジ検索ロジックを実装する必要があります。完全なコードは[langbot-plugin-demo](https://github.com/langbot-app/langbot-plugin-demo)で見つけることができます。
```python
# Auto generated by LangBot Plugin SDK.
# Please refer to https://langbot.app/docs/en/plugin/dev/tutor.html for more details.
...
class FastGPT(KnowledgeRetriever):
async def retrieve(self, context: RetrievalContext) -> list[RetrievalResultEntry]:
"""FastGPTナレッジベースからナレッジを検索"""
# 1. 設定パラメータを取得
api_base_url = self.config.get('api_base_url', 'http://localhost:3000')
api_key = self.config.get('api_key')
dataset_id = self.config.get('dataset_id')
search_mode = self.config.get('search_mode', 'embedding')
using_rerank = self.config.get('using_rerank', False)
# 2. パラメータ検証
if not api_key or not dataset_id:
logger.error("Missing required configuration: api_key or dataset_id")
return []
# 3. APIリクエストを構築
url = f"{api_base_url.rstrip('/')}/api/core/dataset/searchTest"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
payload = {
"datasetId": dataset_id,
"text": context.query, # RetrievalContextからクエリテキストを取得
"searchMode": search_mode,
"usingReRank": using_rerank,
}
try:
# 4. 外部APIを呼び出す
async with httpx.AsyncClient() as client:
response = await client.post(url, json=payload, headers=headers, timeout=30.0)
response.raise_for_status()
result = response.json()
# 5. レスポンスを解析してRetrievalResultEntryに変換
results = []
for record in result.get('data', []):
# メインデータと補助データをコンテンツとして結合
content_text = '\n'.join([
record.get('q', ''),
record.get('a', '')
]).strip()
# 検索結果エントリを作成
entry = RetrievalResultEntry(
id=record.get('id', ''),
content=[ContentElement.from_text(content_text)],
metadata={
'dataset_id': record.get('datasetId', ''),
'source_name': record.get('sourceName', ''),
'score': record.get('score', 0.0),
},
# 類似度スコアを距離に変換(スコアが高いほど距離が小さい)
distance=1.0 - float(record.get('score', 0.0)),
)
results.append(entry)
logger.info(f"Retrieved {len(results)} chunks from FastGPT dataset {dataset_id}")
return results
except httpx.HTTPStatusError as e:
logger.error(f"HTTP error from FastGPT API: {e.response.status_code} - {e.response.text}")
return []
except httpx.RequestError as e:
logger.error(f"Request error when calling FastGPT API: {str(e)}")
return []
except Exception as e:
traceback.print_exc()
logger.error(f"Unexpected error during retrieval: {str(e)}")
return []
```
### 検索コンテキスト
`RetrievalContext`にはこの検索のコンテキスト情報が含まれます:
```python
class RetrievalContext(pydantic.BaseModel):
"""ナレッジ検索コンテキスト"""
query: str
"""クエリテキスト、ユーザーの検索質問"""
```
### 検索結果
検索結果は`RetrievalResultEntry`オブジェクトのリストに変換する必要があり、各オブジェクトは検索されたナレッジチャンクを表します:
```python
class RetrievalResultEntry(pydantic.BaseModel):
"""単一検索結果エントリ"""
id: str
"""結果ID、このエントリを一意に識別"""
content: list[ContentElement]
"""結果コンテンツ、ContentElement.from_text()を使用してテキストコンテンツを作成"""
metadata: dict[str, Any]
"""結果メタデータ、ソース、スコアなど、任意のキーと値のペアを含めることができます"""
distance: float
"""距離スコア、クエリとの関連性を示します(小さいほど関連性が高い)
通常は1.0 - similarity_scoreとして計算されます"""
```
### 設定パラメータの取得
`self.config.get(パラメータ名, デフォルト値)`を使用して、ユーザーが設定したパラメータ値を取得します:
```python
# 文字列パラメータを取得
api_base_url = self.config.get('api_base_url', 'http://localhost:3000')
# 数値パラメータを取得
limit = self.config.get('limit', 5000)
similarity = self.config.get('similarity', 0.0)
# ブールパラメータを取得
using_rerank = self.config.get('using_rerank', False)
# selectパラメータを取得
search_mode = self.config.get('search_mode', 'embedding')
```
## 1 つの installation、複数の用途
1 つのナレッジリトリーバーは**複数のナレッジベース**から使われ、プラットフォームのストレージキー `(instance, Workspace, owner type, plugin, key)` にはナレッジベースの次元がありません:
- 検索に使うキャッシュ、インデックス、中間結果は**ナレッジベース ID** で分離し、キーにナレッジベース ID を含めてください。インスタンスに置かないでください。
- 検索のロックと「要対账」状態は `(installation, ナレッジベース)` 単位で記録してください。あるナレッジベースの検索失敗が、同じ installation の他のナレッジベースの検索を利用不能にしてはいけません。
- `RetrievalContext` は 1 回の検索でのみ有効です。インスタンスフィールドに保存して呼び出しをまたいで再利用しないでください。
- 呼び出しをまたいで保持するナレッジベースのデータは Host ストレージに置き、キーに installation の識別子とナレッジベース ID の両方を含めてください。
- installation 単位で接続、インデックス、その他のプロセス内状態をキャッシュする場合は、`on_installation_revoked(binding)` で解放してください。
完全な仕様は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
## リトリーバーのテスト
作成後、プラグインディレクトリでコマンド`lbp run`を実行してデバッグを開始します。その後、LangBotで:
1. 「ナレッジベース」ページに移動
2. 外部ナレッジベースを追加
3. プラグインが提供するナレッジリトリーバーを選択し、設定を入力
<img width="600" src="/images/zh/plugin/dev/components/knowledge_retriever_config.png" />
保存後、LangBotパイプラインでこのナレッジベースを選択できます。
+255
View File
@@ -0,0 +1,255 @@
---
title: "コンポーネント:ページ"
---
Pageコンポーネントを使用すると、プラグインはLangBot WebUIのサイドバーにカスタムビジュアルページを登録できます。ページはiframeサンドボックス内で実行され、Page SDKを介してホストと通信し、プラグインのバックエンドAPIを呼び出すことができます。
## ユースケース
- **管理パネル**:プラグインに視覚的な設定やデータ管理インターフェースを提供(例:FAQ管理、分析ダッシュボード)
- **データ表示**:ランタイムの統計データ、ログ、チャートなどを表示
- **インタラクティブツール**:フォーム、エディタなどの対話式インターフェースを提供
<img width="600" src="/images/zh/plugin/dev/components/plugin-page-demo.png" />
## ページコンポーネントの追加
プラグインディレクトリでコマンドを実行します:
```bash
lbp comp Page
```
ページ名を入力すると、`components/pages/`ディレクトリにページファイルが生成されます:
```bash
├── components
│ └── pages
│ └── dashboard
│ ├── dashboard.yaml # ページマニフェスト
│ ├── dashboard.py # バックエンドハンドラー
│ ├── index.html # ページエントリポイント
│ └── i18n # 翻訳ファイル(オプション)
│ ├── en_US.json
│ └── ja_JP.json
```
`manifest.yaml`にもコンポーネント検出設定が追加されます:
```yaml
spec:
components:
Page:
fromDirs:
- path: components/pages/
maxDepth: 2
```
## マニフェストファイル:ページ
```yaml
apiVersion: v1 # 変更しないでください
kind: Page # 変更しないでください
metadata:
name: dashboard # プラグイン内で一意のページID
label:
en_US: Dashboard # WebUIサイドバーに表示される名前、多言語対応
ja_JP: ダッシュボード
spec:
path: index.html # HTMLエントリファイル(このYAMLファイルからの相対パス)
execution:
python:
path: dashboard.py # バックエンドハンドラーファイル
attr: DashboardPage # ハンドラークラス名
```
## バックエンド処理
Pageコンポーネントのバックエンドハンドラーは`Page`基底クラスを継承し、`handle_api`メソッドを実装してフロントエンドページからのAPIリクエストを処理します。
```python
from langbot_plugin.api.definition.components.page import Page, PageRequest, PageResponse
class DashboardPage(Page):
async def handle_api(self, request: PageRequest) -> PageResponse:
# request.endpoint: APIエンドポイントパス(例:'/stats')
# request.method: HTTPメソッド(GET、POST、PUT、DELETE)
# request.body: リクエストボディ(解析済みJSON、またはNone)
if request.endpoint == '/stats' and request.method == 'GET':
# Host storage から現在の installation のデータを読む
entries = await self.load_entries_for_current_installation()
return PageResponse.ok({'total': len(entries)})
return PageResponse.fail(f'不明なエンドポイント: {request.endpoint}')
```
### PageRequestフィールド
| フィールド | 型 | 説明 |
|-----------|------|------|
| `endpoint` | `str` | APIエンドポイントパス(例:`'/entries'`) |
| `method` | `str` | HTTPメソッド(`GET`、`POST`、`PUT`、`DELETE`) |
| `body` | `Any` | リクエストボディ(解析済みJSON、または`None`) |
### PageResponseの構築
| メソッド | 説明 |
|---------|------|
| `PageResponse.ok(data)` | 成功レスポンス。`data`はJSON変換可能な任意の値 |
| `PageResponse.fail(error)` | エラーレスポンス。`error`はエラーメッセージ文字列 |
## フロントエンドページ開発
HTMLファイルにPage SDKを読み込んで、プラグインバックエンドと通信します:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body {
background: var(--langbot-bg, #ffffff);
color: var(--langbot-text, #0a0a0a);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
padding: 24px;
}
</style>
</head>
<body>
<h1 data-i18n="title">ダッシュボード</h1>
<p id="stats"></p>
<script src="/api/v1/plugins/_sdk/page-sdk.js"></script>
<script>
langbot.onReady(async function(ctx) {
// バックエンドAPIを呼び出す
var data = await langbot.api('/stats', null, 'GET');
document.getElementById('stats').textContent = '合計: ' + data.total;
});
</script>
</body>
</html>
```
### Page SDK API
| メソッド | 説明 |
|---------|------|
| `langbot.onReady(callback)` | SDK準備完了時に呼ばれる。`callback`は`ctx`(`theme`と`language`を含む)を受け取る |
| `langbot.api(endpoint, body?, method?)` | プラグインの`handle_api`を呼び出す。Promiseを返す |
| `langbot.t(key, fallback?)` | 翻訳文字列を取得 |
| `langbot.onThemeChange(callback)` | テーマ変更時のコールバック |
| `langbot.onLanguageChange(callback)` | 言語変更時のコールバック |
| `langbot.applyI18n()` | `data-i18n`翻訳を手動で再適用 |
### ダークモード
SDKはページにCSSカスタムプロパティを自動設定します。直接使用できます:
| CSS変数 | 用途 |
|---------|------|
| `--langbot-bg` | ページ背景色 |
| `--langbot-bg-card` | カード背景色 |
| `--langbot-text` | メインテキスト色 |
| `--langbot-text-muted` | サブテキスト色 |
| `--langbot-border` | ボーダー色 |
| `--langbot-accent` | アクセント色 |
### ページi18n
ページディレクトリ内に`i18n/`ディレクトリを作成し、JSON翻訳ファイルを配置します:
```
pages/dashboard/
├── index.html
└── i18n/
├── en_US.json
└── ja_JP.json
```
翻訳ファイルはフラットなJSONキーバリューペアです:
```json
{
"title": "ダッシュボード",
"totalEntries": "エントリ数"
}
```
HTML要素に`data-i18n`属性を追加すると、SDKが自動的にテキストを翻訳します:
```html
<h1 data-i18n="title">Dashboard</h1>
```
## 完全な例:FAQ管理ページ
FAQエントリのCRUD操作を実装した完全なPageコンポーネントの例です。
**バックエンドハンドラー**(`components/pages/manager/manager.py`):
```python
from langbot_plugin.api.definition.components.page import Page, PageRequest, PageResponse
class ManagerPage(Page):
async def handle_api(self, request: PageRequest) -> PageResponse:
if request.endpoint == '/entries' and request.method == 'GET':
entries = await self.load_entries_for_current_installation()
return PageResponse.ok({'entries': entries})
if request.endpoint == '/entries' and request.method == 'POST':
question = (request.body or {}).get('question', '').strip()
answer = (request.body or {}).get('answer', '').strip()
if not question or not answer:
return PageResponse.fail('質問と回答は必須です')
entry = {'question': question, 'answer': answer}
await self.save_entry_for_current_installation(entry)
return PageResponse.ok({'entry': entry})
if request.endpoint == '/entries' and request.method == 'DELETE':
entry_id = (request.body or {}).get('id', '')
if await self.delete_entry_for_current_installation(entry_id):
return PageResponse.ok({'deleted': entry_id})
return PageResponse.fail('エントリが見つかりません')
return PageResponse.fail(f'不明: {request.method} {request.endpoint}')
```
**フロントエンドページ**(`components/pages/manager/index.html`)では`langbot.api()`でバックエンドを呼び出します:
```javascript
// エントリを読み込む
var data = await langbot.api('/entries', null, 'GET');
// エントリを追加
await langbot.api('/entries', { question: '...', answer: '...' }, 'POST');
// エントリを削除
await langbot.api('/entries', { id: '...' }, 'DELETE');
```
完全なサンプルコードは[FAQManagerプラグイン](https://github.com/langbot-app/langbot-plugin-demo/tree/main/FAQManager)を参照してください。
## 1 つの installation、複数の用途
同じ Page は**複数のページリクエスト**から呼び出されます(同じ installation の別々のリクエスト、別々の Workspace):
- 前回のリクエストのデータをインスタンスに残さないでください。エンドポイント、メソッド、body、現在の installation の情報は、毎回現在の `PageRequest` / binding から取得します。
- ページのデータは常に installation スコープの Host ストレージに置いてください。`self.plugin` はプロセスレベルの共有オブジェクトであり、テナントデータを保存できません。
- 別々の Workspace のリクエストは同じ Page インスタンスを共有するため、インスタンスフィールドの使用が交錯します。リクエストをまたいで保持するデータは Host ストレージに置いてください。
- installation 単位でプロセス内キャッシュを保持する場合は、`on_installation_revoked(binding)` で解放してください。
完全な仕様は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
## 注意事項
- ページは`sandbox="allow-scripts allow-forms"`のiframe内で実行され、ポップアップを開いたり親ページをナビゲートすることはできません
- 認証 Page のテナントデータは installation-scoped Host storage に保存します。`self.plugin` はプロセス全体で共有されるため、テナントのエントリを保持してはいけません。上記 helper 名は説明用です。現在の SDK Host storage API を使用してください。
- `PageResponse.ok()`と`PageResponse.fail()`を使用してレスポンスを構築し、一貫したフォーマットを確保してください
- Page SDKの`<script>`タグは、`langbot`オブジェクトを使用するコードの前に配置する必要があります
+222
View File
@@ -0,0 +1,222 @@
---
title: "コンポーネント: パーサー"
---
パーサー(Parser)コンポーネントにより、プラグインはLangBotにドキュメント解析機能を提供できます。ユーザーがナレッジベースにドキュメントをアップロードする際、LangBotはRAGエンジンの`ingest`ステップの前にパーサーを呼び出し、PDF、Word、Markdownなどのバイナリファイルから構造化テキストを抽出します。
ParserとKnowledgeEngineの関係:
- **Parser** はファイルをテキストに変換する役割(file → text)
- **KnowledgeEngine** はテキストのインデックス作成と検索を行う役割(text → chunks → vectors)
RAGエンジン自体がドキュメント解析機能を持っている場合(`DOC_PARSING`能力を宣言)、ユーザーはRAGエンジンの内蔵解析機能を使用するか、外部Parserプラグインを使用するかを選択できます。
## パーサーコンポーネントの追加
1つのプラグインには任意の数のパーサーを追加できます。プラグインディレクトリでコマンド`lbp comp Parser`を実行し、プロンプトに従ってパーサーの設定を入力します。
```bash
➜ MyParserPlugin > lbp comp Parser
Generating component Parser...
Parser name: pdf_parser
Parser description: A PDF document parser
Component Parser generated successfully.
組件 Parser 生成成功。
```
これにより、`components/parser/`ディレクトリに`pdf_parser.yaml`と`pdf_parser.py`ファイルが生成されます。`.yaml`ファイルはパーサーの基本情報とサポートするMIMEタイプを定義し、`.py`ファイルはこのパーサーのハンドラです:
```bash
➜ MyParserPlugin > tree
...
├── components
│ ├── __init__.py
│ └── parser
│ ├── __init__.py
│ ├── pdf_parser.py
│ └── pdf_parser.yaml
...
```
## マニフェストファイル: パーサー
```yaml
apiVersion: v1 # 変更しないでください
kind: Parser # 変更しないでください
metadata:
name: pdf_parser # パーサー名、このパーサーを識別するために使用
label:
en_US: PDF Parser # パーサー表示名、LangBotのUIに表示、多言語対応
zh_Hans: PDF 解析器
description:
en_US: 'A PDF document parser'
zh_Hans: 'PDF 文档解析器'
spec:
supported_mime_types: # サポートするファイルのMIMEタイプを宣言
- application/pdf
execution:
python:
path: pdf_parser.py # パーサーハンドラ、変更しないでください
attr: PdfParser # パーサーハンドラのクラス名、pdf_parser.pyのクラス名と一致
```
### supported_mime_types
`supported_mime_types`はこのパーサーがサポートするファイルタイプを宣言します。一般的なMIMEタイプ:
| MIMEタイプ | 説明 |
|----------|------|
| `application/pdf` | PDFドキュメント |
| `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | Wordドキュメント(.docx) |
| `text/markdown` | Markdownファイル |
| `text/plain` | プレーンテキストファイル |
| `text/html` | HTMLファイル |
## プラグインハンドラ
以下のコードがデフォルトで生成されます(`components/parser/<パーサー名>.py`)。`parse`メソッドを実装する必要があります。
```python
from langbot_plugin.api.definition.components.parser.parser import Parser
from langbot_plugin.api.entities.builtin.rag.models import (
ParseContext,
ParseResult,
TextSection,
)
class PdfParser(Parser):
"""ファイルからテキストを抽出するパーサーコンポーネント。"""
async def parse(self, context: ParseContext) -> ParseResult:
"""ファイルを解析して構造化テキストを抽出。
Args:
context: file_content(bytes)、mime_type、filename、metadataを含む。
Returns:
抽出されたテキストとオプションの構造化セクションを含むParseResult。
"""
# TODO: 解析ロジックを実装
text = context.file_content.decode("utf-8", errors="replace")
return ParseResult(
text=text,
sections=[
TextSection(
content=text,
heading=context.filename,
level=0,
),
],
metadata={
"filename": context.filename,
"mime_type": context.mime_type,
},
)
```
### 解析メソッド
`parse`メソッドは、ドキュメントがナレッジベースにアップロードされた時(RAGエンジンの取り込み前)に呼び出されます:
```python
async def parse(self, context: ParseContext) -> ParseResult:
```
**ParseContext**には以下の情報が含まれます:
```python
class ParseContext(pydantic.BaseModel):
file_content: bytes # 生のファイルバイト(LangBotがストレージから読み取り)
mime_type: str # 検出されたファイルのMIMEタイプ
filename: str # 元のファイル名
metadata: dict[str, Any] # FileObjectからの追加メタデータ
```
**ParseResult**は解析結果を返す必要があります:
```python
class ParseResult(pydantic.BaseModel):
text: str # 抽出された完全なプレーンテキスト
sections: list[TextSection] = [] # 構造化セクション(オプション)
metadata: dict[str, Any] = {} # 解析メタデータ(例: page_count, language)
```
**TextSection**はドキュメントから抽出されたテキストのセクションを表します:
```python
class TextSection(pydantic.BaseModel):
content: str # セクションのテキスト内容
heading: str | None = None # セクション見出し
level: int = 0 # ネストレベル
page: int | None = None # ソースページ番号(PDFなど)
metadata: dict[str, Any] = {} # 追加セクションメタデータ
```
## KnowledgeEngineとの連携
ユーザーがドキュメントをアップロードする際、LangBotは以下のロジックで解析フローを決定します:
- ユーザーが外部Parserプラグインを選択した場合、LangBotはまずParserの`parse`メソッドを呼び出し、その結果を`IngestionContext.parsed_content`を通じてRAGエンジンの`ingest`メソッドに渡します。
- RAGエンジンが`DOC_PARSING`能力を宣言しており、ユーザーが外部パーサーを選択していない場合、RAGエンジンが自身でドキュメント解析を処理します。
KnowledgeEngineは`IngestionContext.parsed_content`をチェックして、事前解析されたコンテンツが利用可能かどうかを判断できます:
```python
async def ingest(self, context: IngestionContext) -> IngestionResult:
if context.parsed_content:
# 外部Parserの解析結果を使用
text = context.parsed_content.text
sections = context.parsed_content.sections
else:
# ドキュメントを内部で解析
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
text = file_bytes.decode('utf-8')
...
```
## クロスプラグインパーサー呼び出し
他のプラグインが提供するパーサーを呼び出す前に、`self.plugin.list_parsers` を使ってホスト上で現在利用可能なパーサーを列挙できます:
```python
parsers = await self.plugin.list_parsers(mime_type="application/pdf")
# 各要素には plugin_id、plugin_author、plugin_name、name、description、supported_mime_types が含まれます
```
空配列が返る場合、その MIME タイプに対応する接続済みの Parser プラグインは現在存在しません。
`plugin_author` と `plugin_name` を取得したら、`self.plugin.invoke_parser` で対象のパーサーを呼び出せます:
```python
parser = parsers[0]
result = await self.plugin.invoke_parser(
plugin_author=parser["plugin_author"],
plugin_name=parser["plugin_name"],
storage_path=context.file_object.storage_path,
mime_type=context.file_object.metadata.mime_type,
filename=context.file_object.metadata.filename,
metadata={},
)
# resultはtext、sections、metadataを含むdict
```
## 1 つの installation、複数の用途
1 つの Parser は**複数のナレッジベース**から使われます(同じ installation の別々のナレッジベースが異なる MIME タイプのファイルをアップロードします)。プラットフォームのストレージキーにはナレッジベースの次元がありません:
- 解析キャッシュと一時ファイルは**ナレッジベース ID** で分離し、キーにナレッジベース ID を含めてください。インスタンスにキャッシュしないでください。
- `ParseContext` と解析の中間結果は 1 回の解析でのみ有効です。インスタンスフィールドやモジュールレベルの変数に保存しないでください。
- 解析の失敗が影響するのは現在のナレッジベースのそのアップロードだけです。他のナレッジベースの解析を利用不能にしてはいけません。
- installation 単位でモデル、パーサー、一時ディレクトリをキャッシュする場合は、`on_installation_revoked(binding)` で解放し、一時ファイルを確実に削除できるようにしてください。
完全な仕様は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
## パーサーのテスト
作成後、プラグインディレクトリでコマンド`lbp run`を実行してデバッグを開始します。その後、LangBotで:
1. 「ナレッジベース」ページに移動
2. ナレッジベースを選択してドキュメント管理に入る
3. ファイルのアップロード時、パーサーセレクターでプラグインが提供するパーサーを選択
4. アップロード後、ドキュメントが正しく取り込まれたことを確認
+211
View File
@@ -0,0 +1,211 @@
---
title: "コンポーネント: Runner"
---
Runner は Agent の実行ロジック、または「プラグインプロセッサー」用のイベント処理関数を実装するコンポーネントです。どちらも `RunnerContext` を受け取り、インストール後にユーザーがコンポーネントを選択・設定します。
環境の準備は[プラグイン開発チュートリアル](/ja/plugin/dev/tutor)を参照してください。
`RunnerContext` の完全なフィールドとメソッドは[コンテキスト API](/ja/plugin/dev/apis/agent-run#runner)を参照してください。
## Runner コンポーネントの追加
1 つのプラグインに複数のイベントプロセッサーを追加できます。プラグインのディレクトリで `lbp comp Runner` を実行し、名前に `welcome`、説明に `Welcome new members` を入力します。
```bash
lbp comp Runner
```
生成される `components/runner/welcome.yaml` はコンポーネントを定義するファイルで、`welcome.py` に処理を記述します。
## 実行ロジックの実装
モデルの接続や独自の実行ループには `spec.usages: [agent]` を宣言し、`run(ctx)` から結果を返します:
```python
from langbot_plugin.api.definition.components.runner import Runner, RunnerContext, RunnerResult
from langbot_plugin.api.entities.builtin.provider.message import Message
class Echo(Runner):
async def run(self, ctx: RunnerContext):
yield RunnerResult.run_completed(
ctx.run_id,
message=Message(role="assistant", content=ctx.input.to_text()),
)
```
## マニフェストファイル: Runner
`welcome.yaml` を次の内容に変更します。
```yaml
apiVersion: langbot/v1
kind: Runner
metadata:
name: welcome
label:
en_US: Welcome members
zh_Hans: 欢迎新成员
ja_JP: 新しいメンバーを歓迎
description:
en_US: Welcome new members and respond to /hello.
zh_Hans: 欢迎新成员,并响应 /hello 消息。
ja_JP: 新しいメンバーを歓迎し、/hello に返信します。
spec:
usages: [event]
events:
- group.member_joined
- message.received
capabilities:
tool_calling: true
permissions:
tools: [detail, call]
config:
- name: greeting
type: string
label:
en_US: Greeting
zh_Hans: 欢迎语
ja_JP: 歓迎メッセージ
required: true
default: Welcome aboard!
- name: reply_enabled
type: boolean
label:
en_US: Send replies
zh_Hans: 发送回复
ja_JP: 返信する
default: true
execution:
python:
path: ./welcome.py
attr: Welcome
```
`spec.events` は対応するイベント、`spec.config` は詳細画面でユーザーが変更できる設定です。この例ではメンバー参加とメッセージ受信に対応し、歓迎メッセージと返信の有効・無効を設定できます。返信には、上記の `tool_calling` と `permissions.tools` の指定が必要です。
`spec.usages` は必須で、デフォルト値はありません。`spec.usages` は選択先を指定します。`agent` は Agent とパイプライン、`event` はプラグインプロセッサーです。`[agent, event]` の併記も可能です。`event` には `spec.events` が必要です。以下はイベント処理関数の例です。独自の `run(ctx)` から `await super().run(ctx)` で呼び出すこともできます。
## プラグインの実装
`Welcome` クラスの `initialize` メソッド内で処理関数を登録します。`welcome.py` を次の内容に変更してください。
```python
from langbot_plugin.api.definition.components.runner import (
Runner,
RunnerContext,
)
from langbot_plugin.api.entities.builtin.platform.events import (
MemberJoinedEvent,
MessageReceivedEvent,
)
from langbot_plugin.api.entities.builtin.platform.message import Plain
class Welcome(Runner):
async def initialize(self):
await super().initialize()
@self.handler(MemberJoinedEvent)
async def on_join(ctx: RunnerContext):
name = ctx.platform_event.member.nickname or str(ctx.platform_event.member.id)
await ctx.log(f"Member joined: {name}")
if not ctx.config.get("reply_enabled", True):
await ctx.log("Replies are disabled for this processor")
return
greeting = ctx.config.get("greeting", "Welcome aboard!")
await ctx.reply(f"{name}, {greeting}")
await ctx.log("Welcome action completed")
@self.handler(MessageReceivedEvent)
async def on_message(ctx: RunnerContext):
text = "".join(
item.text for item in ctx.platform_event.message_chain if isinstance(item, Plain)
).strip()
if text != "/hello":
await ctx.log("Message ignored: expected /hello")
return
if ctx.config.get("reply_enabled", True):
await ctx.reply(ctx.config.get("greeting", "Welcome aboard!"))
else:
await ctx.log("Replies are disabled for this processor")
```
`on_join` は参加したメンバーに挨拶し、`on_message` は `/hello` に返信します。「返信する」を無効にすると、ログだけを記録します。
`ctx.platform_event` は現在のイベント、`ctx.config` はこのプロセッサーの設定です。返信には `ctx.reply()`、ログの記録には `ctx.log()` を使います。関数の実行が終わると、イベントの処理も終了します。
## ストリーミング返信
`ctx.reply_stream()` で同じ返信を更新します。`update(text)` には、その時点の全文を渡します。
```python
async with ctx.reply_stream() as reply:
await reply.update("処理中…")
await reply.update("完了しました。")
```
`async with` ブロックが正常終了すると返信が完了します。ストリーミングが未対応・無効の場合やメッセージ以外のイベントでは、最後に全文を一度だけ送信します。例外時には、バッファ内の未完成テキストを送信しません。デバッグでは送信をシミュレーションします。
## イベントの登録
`@self.handler(イベントクラス)` で処理関数を登録し、対応する識別子を `spec.events` に追加します。例えば `MemberJoinedEvent` は `group.member_joined` に対応します。
| イベント | マニフェストの識別子 | SDK のイベントクラス |
| --- | --- | --- |
| メッセージ受信 | `message.received` | `MessageReceivedEvent` |
| メッセージ編集 | `message.edited` | `MessageEditedEvent` |
| メッセージ削除 | `message.deleted` | `MessageDeletedEvent` |
| メッセージへのリアクション | `message.reaction` | `MessageReactionEvent` |
| フィードバック受信 | `feedback.received` | `FeedbackReceivedEvent` |
| メンバー参加 | `group.member_joined` | `MemberJoinedEvent` |
| メンバー退出 | `group.member_left` | `MemberLeftEvent` |
| メンバーのミュート | `group.member_banned` | `MemberBannedEvent` |
| グループ情報更新 | `group.info_updated` | `GroupInfoUpdatedEvent` |
| 友達申請受信 | `friend.request_received` | `FriendRequestReceivedEvent` |
| 友達追加 | `friend.added` | `FriendAddedEvent` |
| 友達削除 | `friend.removed` | `FriendRemovedEvent` |
| Bot のグループ招待 | `bot.invited_to_group` | `BotInvitedToGroupEvent` |
| Bot のグループからの削除 | `bot.removed_from_group` | `BotRemovedFromGroupEvent` |
| Bot のミュート | `bot.muted` | `BotMutedEvent` |
| Bot のミュート解除 | `bot.unmuted` | `BotUnmutedEvent` |
| プラットフォーム固有のイベント | `platform.specific` | `PlatformSpecificEvent` |
利用できるイベントとフィールドは、SDK の [events.py](https://github.com/langbot-app/langbot-plugin-sdk/blob/main/src/langbot_plugin/api/entities/builtin/platform/events.py)を参照してください。対応イベントはプラットフォームによって異なります。
## イベントプロセッサーのテスト
[プラグイン開発チュートリアル](/ja/plugin/dev/tutor)に従ってデバッグ接続を設定し、プラグインのディレクトリで `lbp run` を実行します。LangBot で次の操作を行ってください。
1. 「プラグインプロセッサー」を作成し、詳細画面で「新しいメンバーを歓迎」を選び、設定を保存します。
2. 左側でメンバー参加を選択し、ニックネーム、メンバー ID、グループ ID を入力してテストを実行します。
3. 返信とログを確認します。メッセージ受信を選んで `/hello` を入力すると、返信のテストもできます。
デバッグ中の返信は Mock で再現され、実際のメッセージは送信されません。コンポーネントの YAML を変更したら `lbp run` を再起動してください。
インストール後、ボット詳細の「プラグインプロセッサー」でこの設定を追加して保存します。この画面でコンポーネントを選択し、設定を入力して新規作成・紐付けすることもできます。宣言されたイベントは自動で配信され、イベントごとのルート設定は不要です。
プラグインの購読は Agent/パイプラインのルートと独立して実行されます。同じイベントを複数のプロセッサーが処理でき、一つが失敗しても他の実行を妨げません。同じ設定を使用するボットは設定内容と実行状態を共有します。個別に設定する場合は、新しい設定を作成してください。返信などの任意の動作はコンポーネントの設定として提供し、重複返信を避けてください。
## 1 つの installation、複数の用途
1 つの Workspace でプラグインをインストールできるのは 1 回ですが、その installation は**複数のプロセッサ**(Agent / パイプライン)から再利用されます。Runner はプロセッサ間でデータが混ざらないよう自分で保証する必要があります:
- `ctx.state.*` はすでにプロセッサ単位で分離されています。スコープに `binding_identity`(Agent UUID 由来)が含まれるため、同じ installation の別プロセッサが互いを上書きしません。
- プラットフォームのストレージキーは `(instance, Workspace, owner type, plugin, key)` で、**プロセッサの次元を含みません**。したがって自分で Host ストレージやキャッシュに書くキーには、プロセッサまたは会話の識別子も含めてください。
- `RunnerContext`(`ctx`)は 1 回の実行でのみ有効です。インスタンスに保存して呼び出しをまたいで再利用しないでください。呼び出しをまたいで保持する会話状態は `ctx.state_get()` / `ctx.state_set()` を使います。
- 複数のプロセッサは同じ Runner インスタンスを共有するため、現在の実行のデータをインスタンスフィールドに蓄積しないでください。長期常駐の外部セッションプロセスのように、プロセス内に呼び出しをまたぐ状態を持つ必要がある Runner は、共有実行を宣言せず専用のままにしてください。
- installation 単位でプロセス内キャッシュを保持する場合は、`on_installation_revoked(binding)` で解放してください。
完全な仕様は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
## 次のステップ
- [`RunnerContext` API リファレンス](/ja/plugin/dev/apis/agent-run#runner)を確認します。
- モデル、ツール、ナレッジベース、ストレージについては [LangBot API](/ja/plugin/dev/apis/common)を確認します。
- メッセージ送信などについては[プラットフォーム API](/ja/plugin/dev/apis/platform)を確認します。
- [RunnerDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/Runner/RunnerDemo)で他の実装例を確認できます。
- パイプラインの前処理やモデル呼び出し完了時などに機能を追加する場合は、[イベントリスナー](/ja/plugin/dev/components/event-listener)を使用してください。
+188
View File
@@ -0,0 +1,188 @@
---
title: "コンポーネント: Tool"
---
LangBotの組み込みLocal Agentは、実行中にツールを呼び出して外部世界と対話します。現在、プラグインとMCPの両方を通じてツールを追加することがサポートされています。
## Toolコンポーネントの追加
1つのプラグインには任意の数のツールを含めることができます。プラグインディレクトリでコマンド`lbp comp Tool`を実行し、プロンプトに従ってツール設定を入力します。
```bash
➜ HelloPlugin > lbp comp Tool
Generating component Tool...
Tool name: get_weather_alerts
Tool description: Get weather alerts for a US state.
Component Tool generated successfully.
組件 Tool 生成成功。
```
これにより、`components/tools/`ディレクトリに`get_weather_alerts.yaml`と`get_weather_alerts.py`ファイルが生成されます。`.yaml`ファイルはツールの基本情報を定義し、`.py`ファイルはツールハンドラです:
```bash
➜ HelloPlugin > tree
...
├── components
│ ├── __init__.py
│ └── tools
│ ├── __init__.py
│ ├── get_weather_alerts.py
│ └── get_weather_alerts.yaml
...
```
## マニフェストファイル: Tool
```yaml
apiVersion: v1 # 変更しないでください
kind: Tool # 変更しないでください
metadata:
name: get_weather_alerts # ツール名、LLM認識用
label:
en_US: GetWeatherAlerts # ツール表示名、LangBot UIに表示、多言語対応
zh_Hans: GetWeatherAlerts
description:
en_US: 'Get weather alerts for a US state.' # ツール説明、LangBot UIに表示、多言語対応。オプション
zh_Hans: '獲取美国某个州的天気預警'
spec:
parameters: [] # ツールパラメータ、会話コンテキストに基づいてLLMが生成する値
llm_prompt: 'Get weather alerts for a US state.' # ツールプロンプト、LLMがこのツールを呼び出すかどうかを判断するため
execution:
python:
path: get_weather_alerts.py # ツールハンドラ、変更しないでください
attr: GetWeatherAlerts # ツールハンドラクラス名、get_weather_alerts.pyのクラス名と一致
```
## プラグイン処理
以下のコードがデフォルトで生成されます(`components/tools/<tool_name>.py`)。`GetWeatherAlerts`クラスの`call`メソッドでこのツールの呼び出しと戻り値のロジックを実装する必要があります。
```python
# Auto generated by LangBot Plugin SDK.
# Please refer to https://langbot.app/docs/en/plugin/dev/tutor.html for more details.
from __future__ import annotations
from typing import Any
from langbot_plugin.api.definition.components.tool.tool import Tool
from langbot_plugin.api.entities.builtin.provider import session as provider_session
class GetWeatherAlerts(Tool):
async def call(self, params: dict[str, Any], session: provider_session.Session, query_id: int) -> str:
"""Fill your tool code here"""
return {}
```
`call`メソッドでこのツールを実装します:
<Info>
天気検索ツールの例は、MCPの[サーバー作成例](https://modelcontextprotocol.io/quickstart/server)から来ています。
</Info>
```python
from __future__ import annotations
from typing import Any
from langbot_plugin.api.definition.components.tool.tool import Tool
from langbot_plugin.api.entities.builtin.provider import session as provider_session
import httpx
# 定数
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""適切なエラー処理を使用してNWS APIにリクエストを行う。"""
headers = {
"User-Agent": USER_AGENT,
"Accept": "application/geo+json"
}
async with httpx.AsyncClient() as client:
try:
response = await client.get(url, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json()
except Exception:
return None
def format_alert(feature: dict) -> str:
"""アラート機能を読みやすい文字列にフォーマットする。"""
props = feature["properties"]
return f"""
Event: {props.get('event', 'Unknown')}
Area: {props.get('areaDesc', 'Unknown')}
Severity: {props.get('severity', 'Unknown')}
Description: {props.get('description', 'No description available')}
Instructions: {props.get('instruction', 'No specific instructions provided')}
"""
class GetWeatherAlerts(Tool):
async def call(self, params: dict[str, Any], session: provider_session.Session, query_id: int) -> str:
"""Fill your tool code here"""
state = params.get("state", "CA")
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
data = await make_nws_request(url)
if not data or "features" not in data:
return "Unable to fetch alerts or no alerts found."
if not data["features"]:
return "No active alerts for this state."
alerts = [format_alert(feature) for feature in data["features"]]
return "\n---\n".join(alerts)
```
このツールでは、`params`から`state`パラメータを取得し、NWS APIを呼び出してその州の天気アラートを取得する必要があるため、マニフェストファイルの`parameters`で`state`パラメータを定義する必要があります。
`parameters`フォーマットは[JSON Schema](https://json-schema.org/docs)に従い、[OpenAI Function Calling機能](https://platform.openai.com/docs/guides/function-calling#defining-functions)でサポートされており、そのルートタイプは`type: object`に固定されています。`properties`にパラメータを追加し、`required`に必須パラメータの説明を追加してください。
```yaml
...
spec:
parameters:
type: object # 変更しないでください
properties: # 変更しないでください、ここにパラメータを追加
state:
type: string
description: 'Two-letter US state code (e.g. CA, NY)'
required: # ここに必須パラメータの説明を追加
- state
...
```
このツールは`httpx`ライブラリも使用しているため、プラグインディレクトリの`requirements.txt`に`httpx`依存関係を追加する必要があります。
```txt
# HelloPlugin/requirements.txt
langbot-plugin # デフォルトで既に存在
httpx
```
次に、プラグインディレクトリでコマンド`lbp run`を実行してデバッグを開始します。LangBotでツール呼び出しをサポートするモデルを設定し、対応するパイプラインでそのモデルの使用を選択してこのツールを使用します。
<img width="600" src="/images/zh/plugin/dev/components/tool_use_in_chat.png" />
## 1 つの installation、複数の用途
1 つの Tool は**複数のプロセッサと会話**から呼び出されます(1 つのイベントで複数のプラグインプロセッサが起動し得ます):
- 設定は呼び出しごとに読み取ってください(`self.get_plugin_config()` または `ctx.config`)。`initialize()` で読み取ってインスタンスにキャッシュしないでください。
- 呼び出し引数、戻り値、会話データをインスタンスフィールドやモジュールレベルの変数に残さないでください。会話の識別子は現在の呼び出しの `session` / `query_id` から取得します。
- 同時呼び出しは同じインスタンスを共有するため、インスタンスフィールドは書き込みが交錯します。呼び出しをまたいで保持するデータは Host ストレージに置き、対応するプロセッサまたは会話の識別子をキーに含めてください。
- installation 単位でプロセス内キャッシュを保持する場合は、`on_installation_revoked(binding)` で解放してください。
完全な仕様は[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
## 次のステップ
ツールの基本的な使い方を学びました。次は:
- Tool が受け取る [`session` と `query_id`](/ja/plugin/dev/apis/agent-run#tool)を確認します。
- [LangBot API](/ja/plugin/dev/apis/common)を確認します。
+190
View File
@@ -0,0 +1,190 @@
---
title: "プラグインディレクトリ構造"
---
このドキュメントでは、LangBotプラグインの標準ディレクトリ構造とファイル組織仕様を紹介し、開発者が準拠したプラグインを作成できるように支援します。
## 基本ディレクトリ構造
標準的なLangBotプラグインは次のディレクトリ構造に従う必要があります:
```
MyPlugin/
├── manifest.yaml # プラグインマニフェストファイル(必須)
├── main.py # プラグインメインエントリファイル(必須)
├── README.md # 英語プラグインドキュメント(必須)
├── readme/ # 多言語READMEディレクトリ(オプション)
│ ├── README_zh_Hans.md # 簡体字中国語ドキュメント
│ ├── README_ja_JP.md # 日本語ドキュメント
│ └── README_zh_Hant.md # 繁体字中国語ドキュメント(オプション)
├── assets/ # リソースファイルディレクトリ
│ ├── icon.svg # プラグインアイコン(推奨)
│ └── ... # その他のリソースファイル
├── components/ # コンポーネントディレクトリ
│ ├── event_listener/ # イベントリスナーコンポーネント
│ ├── commands/ # コマンドコンポーネント
│ └── tools/ # ツールコンポーネント
├── requirements.txt # Python依存関係(オプション)
└── config/ # 設定ディレクトリ(オプション)
```
## 多言語README仕様
### ファイル配置仕様
LangBotプラグインは、異なる言語のユーザーにローカライズされたプラグイン説明を提供するために、多言語READMEドキュメントをサポートしています。
**重要な仕様:**
1. **`ルートREADME.md(必須)`**
- **英語**で記述する必要があります
- プラグインのデフォルトドキュメントとして機能します
- 要求された言語バージョンが存在しない場合のフォールバックとして使用されます
2. **`readme/ディレクトリ(オプション)`**
- **非英語**のREADMEドキュメントを保存するために使用
- ファイル命名形式: `README_{言語コード}.md`
### サポートされる言語コード
[RFC 4646](https://datatracker.ietf.org/doc/html/rfc4646)標準に従い、LangBotは現在次の言語コードをサポートしています:
| 言語 | 言語コード | ファイル名 | 配置場所 |
| ------ | -------------- | ------------------- | ------------------ |
| 英語 | `en`または`en_US` | `README.md` | **プラグインルートディレクトリ** |
| 簡体字中国語 | `zh_Hans` | `README_zh_Hans.md` | `readme/`ディレクトリ |
| 繁体字中国語 | `zh_Hant` | `README_zh_Hant.md` | `readme/`ディレクトリ |
| 日本語 | `ja_JP` | `README_ja_JP.md` | `readme/`ディレクトリ |
| ベトナム語 | `vi_VN` | `README_vi_VN.md` | `readme/`ディレクトリ |
| タイ語 | `th_TH` | `README_th_TH.md` | `readme/`ディレクトリ |
| スペイン語 | `es_ES` | `README_es_ES.md` | `readme/`ディレクトリ |
### ディレクトリ構造の例
```
MyPlugin/
├── README.md # ✅ 英語版(必須、ルートディレクトリ)
└── readme/ # ✅ 多言語ディレクトリ
├── README_zh_Hans.md # ✅ 簡体字中国語
├── README_ja_JP.md # ✅ 日本語
└── README_zh_Hant.md # ✅ 繁体字中国語
```
### 誤った例
❌ **誤り: readme/ディレクトリに英語READMEを配置**
```
MyPlugin/
├── readme/
│ ├── README_en.md # ❌ 誤り: 英語はreadme/に配置すべきではない
│ └── README_zh_Hans.md
```
❌ **誤り: ルートREADME.mdに非英語コンテンツが含まれる**
```markdown
# MyPlugin
This is a plugin...
これはプラグインです... # ❌ 誤り: ルートREADME.mdは英語のみを含むべき
```
### README言語フォールバックロジック
ユーザーが特定の言語でREADMEを要求した場合、LangBotは次の順序で検索します:
1. `readme/README_{言語コード}.md`を読み取ろうとします
2. 見つからない場合は、ルート`README.md`(英語版)にフォールバックします
**例:**
- ユーザーが簡体字中国語(`zh_Hans`)を要求
- → `readme/README_zh_Hans.md`を検索
- → 見つからない場合は、`README.md`(英語版)を返します
- ユーザーが日本語(`ja_JP`)を要求
- → `readme/README_ja_JP.md`を検索
- → 見つからない場合は、`README.md`(英語版)を返します
## リソースファイルディレクトリ
### assets/ディレクトリ
`assets/`ディレクトリはプラグインの静的リソースファイルを保存するために使用されます。
**推奨構造:**
```
assets/
├── icon.svg # プラグインアイコン(SVG形式推奨)
├── example.png # 例画像
├── screenshot1.png # スクリーンショット
└── logo.png # ロゴ画像
```
**アイコン仕様:**
- プラグインアイコンとして`icon.svg`を使用することを推奨
- サポートされる形式: `.svg`、`.png`、`.jpg`、`.jpeg`、`.gif`
- 推奨サイズ: 最低256x256ピクセル
- `manifest.yaml`での参照: `icon: assets/icon.svg`
### READMEでのリソース参照
READMEドキュメントで相対パスを使用してassets/ディレクトリの画像を参照できます:
```markdown
# MyPlugin
![例](./assets/example.png)
![スクリーンショット](./assets/screenshot1.png)
```
**注意:** プラグインをLangBot Spaceにアップロードすると、リソースファイルが自動的に処理およびホストされ、ユーザーがREADMEを表示したときに画像が正しく表示されます。
## コンポーネントディレクトリ
プラグイン機能はコンポーネントを通じて実装され、タイプ別に`components/`ディレクトリに整理する必要があります:
```
components/
├── event_listener/ # イベントリスナー
│ ├── on_message.py
│ └── on_message.yaml
├── commands/ # コマンド
│ ├── hello.py
│ └── hello.yaml
└── tools/ # ツール
├── search.py
└── search.yaml
```
コンポーネント開発の詳細な仕様については、[コンポーネントの追加](/en/plugin/dev/components/add)を参照してください。
## 設定ファイル
### manifest.yaml
プラグインマニフェストファイル`manifest.yaml`はコア設定ファイルであり、プラグインメタデータ、設定項目、コンポーネントリストなどの情報が含まれます。
詳細な手順については、[プラグイン設定情報の完成](/en/plugin/dev/basic-info)を参照してください。
### requirements.txt
プラグインが追加のPython依存関係を必要とする場合は、ルートディレクトリに`requirements.txt`ファイルを作成します:
```
requests>=2.28.0
beautifulsoup4>=4.11.0
pillow>=9.0.0
```
**注意:** LangBotは、プラグインのインストール時に`requirements.txt`にリストされている依存関係を自動的にインストールします。
## 関連ドキュメント
- [基本チュートリアル](/en/plugin/dev/tutor) - 最初のプラグインを作成する方法を学ぶ
- [プラグイン設定情報の完成](/en/plugin/dev/basic-info) - manifest.yamlの設定
- [コンポーネントの追加](/en/plugin/dev/components/add) - プラグインコンポーネントの開発
- [マーケットプレイスに公開](/en/plugin/dev/publish/market) - プラグインを配布
+27
View File
@@ -0,0 +1,27 @@
---
title: "移行ガイド"
---
このチュートリアルでは特定の考慮事項を強調しています。まず現在のバージョンのプラグインチュートリアルをお読みいただき、その後このドキュメントをプラグイン移行のリファレンスとしてご使用ください。
## 3.xサポートの非同期プラグインからの移行
<Info title="プラグインの特徴:">
- 主な期間: 2024.2 ~ 2025.8
- 登録方法: `main.py`のプラグインクラスで`@handler`デコレータを使用してイベントリスナーを登録し、`@llm_func`デコレータを使用してコンテンツ関数(ツール)を登録; すべてのハンドラ関数は非同期関数(async def)
</Info>
- プラグインとコンポーネント登録方法: 以前の単一プラグインクラスデコレータ登録方法から、マニフェストファイル登録方法に変更されました。[マニフェストファイル](/en/plugin/dev/basic-info)を参照してください; そして、異なるタイプのコンポーネントを独立して分離しました。詳細については、[コンポーネントの追加](/en/plugin/dev/components/add)を参照してください。
- メッセージチェーンエンティティ: リファクタリングされました。各メッセージチェーン要素の構築方法では、**名前付きパラメータ**の明示的な渡しが必要になりました。[メッセージプラットフォームエンティティ](/en/plugin/dev/apis/messages)を参照してください。
- イベントリスナー: 後方互換性を提供しますが、コンテキストで`query: Query`オブジェクトは提供されなくなりました。詳細については、[パイプラインイベント](/en/plugin/dev/apis/pipeline-events)を参照してください。
- コマンドコンポーネント: リファクタリングされました。[Commandコンポーネント](/en/plugin/dev/components/command)を参照してください。
## 2.xおよび3.xサポートの同期プラグインからの移行
<Info title="プラグインの特徴:">
- 主な期間: 2023.1 ~ 2024.2
- 登録方法: `main.py`のプラグインクラスで`@on`デコレータを使用してイベントリスナーを登録し、`@func`デコレータを使用してコンテンツ関数(ツール)を登録; すべてのハンドラ関数は同期関数(def)
</Info>
- 同期から非同期へ: プラグインランタイムは完全に非同期(asyncioベース)に変更されました。IO操作を含むすべてのプラグインコードを非同期記述に変更してください。
- 詳細な内容については、上記の3.x非同期プラグイン移行ガイドを参照してください。
+40
View File
@@ -0,0 +1,40 @@
---
title: "GitHub経由でプラグインを配布"
---
プラグインをGitHubにアップロードし、リポジトリのReleaseを作成して`.lbpkg`ファイルをアップロードすることで、他のユーザーがLangBotでインストールできるようになります。
## プラグインのビルド
プラグインディレクトリで次のコマンドを実行します:
```bash
lbp build
```
これにより、`dist`ディレクトリに`.lbpkg`ファイルが生成されます。
## Releaseの作成
プラグインをGitHubにアップロードし、プラグインリポジトリのReleaseページに移動し、Releaseを作成をクリックして、先ほどビルドした`.lbpkg`ファイルをアップロードします。
<img width="600" src="/images/zh/plugin/dev/dist/github_release.png" />
## Releaseの自動作成(GitHub Actions)
`lbp init` で作成されたプラグインには、GitHub Actions ワークフロー(`.github/workflows/release.yml`)が組み込まれているため、手動でビルドしてアップロードする必要はありません。
プラグインリポジトリを GitHub にプッシュした後、`manifest.yaml` の `version` フィールドを変更して `main` または `master` ブランチにプッシュするたびに、ワークフローが自動的に以下を実行します:
1. `lbp` CLI をインストールし、`lbp build` を実行してプラグインをパッケージ化;
2. `v<version>` をタグとして GitHub Release を作成し、生成された `.lbpkg` ファイルをアップロード。
該当バージョンの Release が既に存在する場合、ワークフローはスキップして重複公開を回避します。リポジトリの Actions ページから手動でトリガーすることもできます(`workflow_dispatch`)。
<Info title="">
このワークフローはリポジトリのデフォルトの `GITHUB_TOKEN` に依存し、`contents: write` 権限が必要です(テンプレートで宣言済み)。リポジトリの Actions が有効になっていること、`manifest.yaml` の `metadata.author`、`metadata.name`、`metadata.version` フィールドが正しく入力されていることを確認してください。Release 名と成果物のファイル名はこれらのフィールドから構成されます(`<author>-<name>-<version>.lbpkg`)。
</Info>
## プラグインのインストール
LangBotのプラグイン管理ページで、右上隅をクリックしてGitHubインストール方法を選択します。
+85
View File
@@ -0,0 +1,85 @@
---
title: "プラグインマーケットプレイスに公開"
---
CLI経由で[LangBotプラグインマーケットプレイス](https://space.langbot.app/market)にプラグインを公開します。
## LangBot Spaceでアカウントを作成
LangBot SpaceはLangBotの集中サービスプラットフォームです。[LangBot Space](https://space.langbot.app)にアクセスし、右上隅をクリックしてGitHubでログインし、アカウントを作成してください。
<img width="600" src="/images/zh/plugin/dev/login_space.png" />
GitHubの認証ページにリダイレクトされますので、プロンプトに従って認証を完了してください。
## CLIを使用してLangBot Spaceにログイン
### 方法1:個人アクセストークンを使用(推奨)
1. [LangBot Space](https://space.langbot.app)にログインし、アバターをクリックして**アカウント設定**を開き、**アカウント**タブの**個人アクセストークン**で新しいトークンを作成します。
2. 生成されたトークンをコピーし(一度しか表示されません)、ターミナルで以下を実行します:
```bash
➜ HelloPlugin > lbp login --token lbpat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
==================================================
✅ 個人アクセストークンでログインしました!
トークンの保存先:~/.langbot/cli/config.json
==================================================
```
### 方法2:デバイスコード認証を使用
任意のディレクトリでコマンド`lbp login`を実行します:
```bash
➜ HelloPlugin > lbp login
Starting LangBot CLI login process...
Generating device code...
==================================================
Please copy the user code and complete verification in your browser:
User Code: 6CD64D56
Verification URL: https://space.langbot.app/auth/device
Device code expires in: 120 seconds
==================================================
Waiting for verification...
```
`Verification URL`をクリックしてLangBot Spaceにリダイレクトし、`User Code`を入力してログインを完了してください。
<img width="600" src="/images/zh/plugin/dev/space_device_flow.png" />
```bash
==================================================
✅ Login successful!
Access token saved to: ~/.langbot/cli/config.json
Token type: Bearer
Expires in: 21600 seconds
==================================================
```
どちらの方法でもログインが成功したら、CLIを使用してプラグインをアップロードできます。
## プラグインのアップロード
プラグインディレクトリで`lbp publish`を実行してください:
```bash
➜ HelloPlugin > lbp publish
Building plugin to dist/tmp...
- Skipping ignored directory: dist
- Skipping ignored directory: __pycache__
...
Plugin built successfully: dist/tmp/RockChinQ-HelloPlugin-0.1.0.lbpkg
✅ Plugin published successfully. You can check it on https://https://space.langbot.app/market
```
[LangBotプラグインマーケットプレイス](https://space.langbot.app/market)でプラグイン提出タスクを確認できます。承認をお待ちください。承認後、マーケットプレイスでプラグインを表示できます。
<img width="600" src="/images/zh/plugin/dev/plugin_submission.png" />
公開の流れ自体に認証は関係しません。プラグインは既定で専用モードで動作します。このバージョンで認証も申請する場合は、[認証プラグインと共有実行](/ja/plugin/certified-plugins)を参照してください。
ご質問がある場合は、`[email protected]`にメールを送信するか、[コミュニティ](/ja/insight/community)を通じてお問い合わせください。
+19
View File
@@ -0,0 +1,19 @@
---
title: "コードスタイルガイド"
---
## ロギング
プラグインでは、`logging`モジュールを使用してログを記録してください。`print`文を使用しないでください(これはコンテナ環境でログが出力されなくなります)。
```python
import logging
logger = logging.getLogger(__name__) # 任意の Python ファイルの先頭で logging モジュールをインポートし、logger オブジェクトを定義します
logger.info('This is an info message') # logger.info を使用して info レベルのログを記録します
logger.warning('This is a warning message')
logger.error('This is an error message')
```
認証プラグインでは、生のプロセスログを 1 つの Workspace に安全に帰属できないため、運用診断専用です。Runner は `RunnerContext.log()` で installation-scoped なテナント表示ログを記録できます。その他のコンポーネントには汎用テナントログ API がまだないため、生のプロセスログは運用者専用です。モジュールグローバルなバックグラウンド処理から設定、認証情報、リクエストデータを出力してはいけません。
+148
View File
@@ -0,0 +1,148 @@
---
title: "プラグイン開発チュートリアル"
---
<Info>
メッセージ受信やメンバー参加などを処理するプラグインを作る場合は、基本環境を準備した後、[イベントプロセッサーのチュートリアル](/ja/plugin/dev/components/runner)に進んでください。
</Info>
## アーキテクチャの基礎
バージョン4.0では、高セキュリティで柔軟性の高い本番環境グレードのプラグインシステムを導入し、開発者に豊富なAPIと使いやすいサポートツールを提供しています。
<img width="600" src="/images/zh/plugin/dev/plugin_system_arch.png" />
Plugin Runtimeは、プラグインのライフサイクルを管理し、LangBotとプラグイン間の相互作用を調整するために使用されます。`stdio`と`websocket`の2つの動作モードがあります。LangBotがユーザーによって直接起動される場合(コンテナ内で実行されていない場合)、`stdio`モードを使用します。これは個人ユーザーや軽量環境で一般的です。LangBotがコンテナ内で実行される場合、`websocket`モードを使用します。これは本番環境専用に設計されています。
Plugin Runtimeは、インストールされた各プラグインを自動的に起動し、stdioを通じて相互作用します。プラグイン開発シナリオでは、開発者は`lbp`コマンドラインツールを使用してプラグインを起動し、WebSocketを介して実行中のRuntimeに接続してデバッグできます。
### プラグイン構造
プラグインのディレクトリ構造は次のようになります:
```bash
➜ HelloPlugin > tree
.
├── assets
│ └── icon.svg # プラグインアイコン、プラグインマーケットプレイスページに表示
├── components # プラグインコンポーネントディレクトリ、さまざまなコンポーネントコードとマニフェストファイルを保存
│ ├── __init__.py
│ ├── commands
│ │ ├── __init__.py
│ │ ├── info.py
│ │ └── info.yaml
│ ├── event_listener
│ │ ├── __init__.py
│ │ ├── default.py
│ │ └── default.yaml
│ └── tools
│ ├── __init__.py
│ ├── get_weather_alerts.py
│ └── get_weather_alerts.yaml
├── main.py # プラグインメインプログラム、プラグインライフサイクルをリッスン
├── manifest.yaml # プラグインマニフェストファイル、プラグインメタデータを記述
├── README.md # プラグインドキュメントファイル、プラグイン機能と使用方法を記述、プラグインマーケットプレイスページに表示
├── readme
│ └── README_zh_Hans.md # 簡体字中国語ドキュメント、多言語ドキュメント用
├── requirements.txt # プラグイン依存関係ファイル
└── .github
└── workflows
└── release.yml # GitHub Actions ワークフロー、バージョン変更をプッシュすると自動的にビルドして Release を公開
```
main.pyのプラグインクラスは各プラグインの共通コードで、プラグイン起動時に初期化され、コンテキスト情報(設定など)が渡されます。ここでプラグインの初期化とシャットダウンロジックを実装できます。プラグインクラスは`langbot_plugin.api.definition.plugin.BasePlugin`を継承し、[LangBotグローバルAPI](/en/plugin/dev/apis/common)を提供します。
各コンポーネントはプラグインのコア機能モジュールであり、必要に応じて[追加および削除](/en/plugin/dev/components/add)できます。異なるコンポーネントは異なる状況で呼び出され、後でプラグイン機能を簡単に拡張できます。各コンポーネントは`langbot_plugin.api.definition.components`パッケージ下のさまざまなコンポーネント基底クラスから直接継承し、`plugin: BasePlugin`オブジェクトを含み、プラグインのAPIを直接呼び出すことができます。
<Info title="">
- 詳細なAPIドキュメントについては、[APIリファレンス](/en/plugin/dev/apis/common)を参照してください。
このチュートリアルを続けて読んで、プラグイン開発プロセスを理解してください。
</Info>
## AI 支援開発
- [skills-LangBotplugin](https://github.com/TyperBody/skills-LangBotplugin) - コミュニティメンバー [@TyperBody](https://github.com/TyperBody) が提供する Skills、AI を使ってプラグインを迅速に開発できます、ぜひお試しください
- *langbotplugin* このSkillはプラグインの自動生成ツールを提供します
- *langbotplugindebug* このSkillはプラグインのデバッグツールを提供します
## CLIのインストール
Python 3.10以上がインストールされており、[uvパッケージマネージャー](https://docs.astral.sh/uv/)がインストールされていることを確認してください。
任意の空のディレクトリで次のコマンドを実行して、LangBot CLIとSDKをインストールします:
```bash
pip install -U langbot_plugin
```
## プラグインディレクトリの初期化
プラグイン名が`HelloPlugin`であると仮定して、任意のディレクトリに`HelloPlugin`ディレクトリを作成し、そのディレクトリに入り、コマンドを実行してプラグインを初期化します:
```bash
lbp init
```
プロンプトに従って`Author`、`Description`などの情報を入力します。
<Info title="">
`lbp init HelloPlugin`コマンドを使用して、サブディレクトリ`HelloPlugin`でプラグインを初期化することもできます。
<img width="600" src="/images/zh/plugin/dev/create_plugin.png" />
</Info>
この操作により、プラグインの初期ファイルが生成されます。お気に入りのエディターで`HelloPlugin`ディレクトリを開き、プラグインコードの記述を開始できます。
<Info title="">
`lbp`コマンドで「command not found」エラーが発生した場合、`PATH`環境変数が適切に設定されていない可能性があります。
`lbp`コマンドの代わりに`python -m langbot_plugin.cli.__init__`を使用できます。
例えば:
```bash
python -m langbot_plugin.cli.__init__ init HelloPlugin
cd HelloPlugin
```
</Info>
## デバッグモードの開始
まずLangBotをデプロイして起動し、Plugin Runtimeが実行されてポート`5401`をリッスンしていることを確認する必要があります。
<Info title="Runtimeには2つの起動モードがあります:">
- `Stdio`モード: 起動パラメータ`--standalone-runtime`なしでソースコードを使用してLangBotを起動すると、LangBotは自動的にPlugin Runtimeをサブプロセスとして起動し、`stdio`(標準入出力ストリーム)を通じてPlugin Runtimeと通信します。この場合、RuntimeはLangBotルートディレクトリの`data/plugins`ディレクトリからプラグインをロードし、LangBotホストのポート`5401`をデバッグポートとしてリッスンします。
- `WebSocket`モード:
- 本番環境: 公式の`docker-compose.yaml`を使用してLangBotを起動すると、Plugin Runtimeは別のコンテナで実行され、起動パラメータ`--standalone-runtime`のためにLangBotはWebSocketモードでPlugin Runtimeと通信します。Runtimeコンテナの5401ポートはデフォルトでホストの5401ポートにマッピングされます。
- 開発環境: 起動パラメータ`--standalone-runtime`を使用してソースコードでLangBotを起動すると、LangBotは`data/config.yaml`で設定された`plugin.runtime_ws_url`アドレス(ポートは通常5400)に従って既に起動されているPlugin Runtimeに接続します。スタンドアロンのPlugin Runtimeを自分で起動する必要があります。[Plugin Runtimeの開発](/en/develop/plugin-runtime)を参照してください。
`stdio`または`websocket`モードのいずれの場合でも、Plugin Runtimeはそのホストのポート`5401`をプラグインデバッグ接続のデバッグポートとしてリッスンします。
プラグインを開発する際は、[開発設定](/en/develop/dev-config)方法に従ってLangBotを起動することをお勧めします。これによりPlugin RuntimeがStdioモードで起動され、プラグイン開発が容易になります。
</Info>
`.env.example` を `.env` にコピーします。LangBot WebUI の **拡張機能 → デバッグ情報** を開き、現在の Workspace のデバッグ URL とデバッグキーを `DEBUG_RUNTIME_WS_URL` と `PLUGIN_DEBUG_KEY` に設定します。キーは Workspace ごとに異なり、2 時間ごとにローテーションするため、期限切れ後は再度コピーしてください。
```bash
cp .env.example .env
```
プラグインデバッグを開始すると、プラグインの出力が表示されます:
```bash
lbp run
```
<img width="600" src="/images/zh/plugin/dev/launch_debug_plugin.png" />
そして、LangBotのWebUIでこのプラグインがロードされたことを確認できます。
<img width="600" src="/images/zh/plugin/dev/debug_plugin_loaded.png" />
## 次のステップ
このチュートリアルでは、プラグイン機能を段階的に完成させる方法を案内します。
- プラグイン情報の変更: プラグインは基本的なプラグイン情報で作成されました。[プラグイン情報を完成させて](/en/plugin/dev/basic-info)ください。
- コンポーネントの追加: プラグインコンポーネントはプラグインのコア機能ユニットです。必要に応じて[コンポーネントを追加](/en/plugin/dev/components/add)できます。