docs(plugin): document unified Runner development and APIs

This commit is contained in:
RockChinQ
2026-09-10 18:04:42 +08:00
parent cb26081730
commit 5cbe5eee59
30 changed files with 233 additions and 246 deletions
+3 -3
View File
@@ -677,7 +677,7 @@
"group": "Component Development",
"pages": [
"en/plugin/dev/components/add",
"en/plugin/dev/components/event-processor",
"en/plugin/dev/components/runner",
"en/plugin/dev/components/event-listener",
"en/plugin/dev/components/command",
"en/plugin/dev/components/tool",
@@ -983,7 +983,7 @@
"group": "组件开发",
"pages": [
"zh/plugin/dev/components/add",
"zh/plugin/dev/components/event-processor",
"zh/plugin/dev/components/runner",
"zh/plugin/dev/components/event-listener",
"zh/plugin/dev/components/command",
"zh/plugin/dev/components/tool",
@@ -1284,7 +1284,7 @@
"group": "コンポーネント開発",
"pages": [
"ja/plugin/dev/components/add",
"ja/plugin/dev/components/event-processor",
"ja/plugin/dev/components/runner",
"ja/plugin/dev/components/event-listener",
"ja/plugin/dev/components/command",
"ja/plugin/dev/components/tool",
+11 -14
View File
@@ -2,16 +2,13 @@
title: "Run Context APIs"
---
AgentRunner and EventProcessor use run-scoped APIs to access authorized models, tools, knowledge bases, and context. Neither component exposes `self.plugin`.
Runner provides `ctx.api` bound to the current run ID, context, and resource grants. Custom `run(ctx)` implementations and registered handlers share `RunnerContext`. `self.plugin` still exposes [Common APIs](/en/plugin/dev/apis/common), but does not automatically represent the current run.
## Access
```python
# Inside an EventProcessor handler
# Inside Runner.run(ctx) or a registered handler
api = ctx.api
# Inside AgentRunner.run(ctx)
api = self.get_run_api(ctx)
```
In the examples below, `api` refers to the current run API. Use `await` for the methods below, except synchronous `get_allowed_*` methods and streaming context/iterator APIs. Do not retain or reuse it across runs.
@@ -52,7 +49,7 @@ Check context capabilities through `api.ctx.context.available_apis`. Do not call
## Replies and logs
| EventProcessor method | Purpose |
| Runner method | Purpose |
| --- | --- |
| `await ctx.reply(text)` | Send text to the current event conversation; return the action result. |
| `await ctx.log(text, level="info")` | Record a log at `debug`, `info`, `warning`, or `error` level. |
@@ -69,7 +66,7 @@ async with api.reply_stream() as reply:
result = reply.result
```
EventProcessor can use `ctx.reply_stream()` to also record the action start and result.
Runner can use `ctx.reply_stream()` to also record the action start and result.
`update(text)` takes the **complete text so far**, not a delta, and returns the action result. Normal exit completes the reply. Message events with platform streaming support enabled update the platform message; other replyable events buffer text and send once on successful exit. Errors or cancellation discard buffered partial text; already displayed content is not guaranteed to be retracted. Debug mode simulates platform delivery.
@@ -157,19 +154,19 @@ The following methods require the matching `available_apis` flag and only access
| `run_append_result(result)` | `AgentRunEvent` |
| `run_finalize(run_id=None, status=None, reason=None)` | `AgentRun` |
`run_id=None` selects the current run. EventProcessor handlers finish automatically when they return and usually do not need to append or finalize records manually. AgentRunner reports results with `yield AgentRunResult`; do not also append the same result through `run_append_result`.
`run_id=None` selects the current run. Registered handlers finish automatically when they return. Custom `run(ctx)` implementations can yield `RunnerResult`; do not append the same result again through `run_append_result`.
## Structured interactions (AgentRunner)
## Structured interactions (Runner)
To request confirmation or form input, yield `AgentRunResult.interaction_requested(ctx.run_id, request)`. `request` accepts an `InteractionRequest` object or dictionary. Required fields are `interaction_id`, `title`, and `fallback_text`; optional fields include `kind` (`form`, `confirmation`, `choice`), `fields`, `actions`, and `expires_at`.
To request confirmation or form input, yield `RunnerResult.interaction_requested(ctx.run_id, request)`. `request` accepts an `InteractionRequest` object or dictionary. Required fields are `interaction_id`, `title`, and `fallback_text`; optional fields include `kind` (`form`, `confirmation`, `choice`), `fields`, `actions`, and `expires_at`.
This requires `spec.capabilities.interactions: true`, `spec.permissions.interactions: [request]`, and Host authorization. Inspect `ctx.delivery.interactions` for surface support; submissions return as `interaction.submitted` events. This is a Runner result event, not a method on `ctx.api`, and is not directly exposed by ordinary EventProcessor handlers.
This requires `spec.capabilities.interactions: true`, `spec.permissions.interactions: [request]`, and Host authorization. Inspect `ctx.delivery.interactions` for surface support; submissions return as `interaction.submitted` events. This is a Runner result event, not a method on `ctx.api`, and is yielded from a custom run(ctx) implementation.
## Error handling
```python
from langbot_plugin.api.proxies.agent_run import PermissionDeniedError
from langbot_plugin.api.entities.builtin.agent_runner.errors import AgentAPIException
from langbot_plugin.api.proxies.runner import PermissionDeniedError
from langbot_plugin.api.entities.builtin.runner.errors import AgentAPIException
try:
result = await api.call_tool("event_get_actor", {})
@@ -179,4 +176,4 @@ except AgentAPIException as exc:
await ctx.log(f"{exc.error.code}: {exc.error.message}", level="error")
```
This example runs inside an EventProcessor. `PermissionDeniedError` means a resource, operation, or capability is not granted. `AgentAPIException.error` contains `code`, `message`, `retryable`, and `details`. A timed-out send may already have succeeded; do not resend solely because `retryable` is true.
This example runs inside a Runner. `PermissionDeniedError` means a resource, operation, or capability is not granted. `AgentAPIException.error` contains `code`, `message`, `retryable`, and `details`. A timed-out send may already have succeeded; do not resend solely because `retryable` is true.
+1 -1
View File
@@ -4,7 +4,7 @@ title: "Plugin Common APIs"
LangBot provides a series of APIs for plugins to operate various LangBot modules and control message contexts.
Regular components such as `Command`, `EventListener`, and `Tool` use these APIs through `self.plugin`. `AgentRunner` and `EventProcessor` use [Run Context APIs](/en/plugin/dev/apis/agent-run), without `self.plugin`. See [Platform APIs](/en/plugin/dev/apis/platform) for platform queries and specific actions.
All components can access these LangBot APIs through `self.plugin`. Runner also provides `ctx.api`, bound to the current invocation and its resource grants; see [Run Context APIs](/en/plugin/dev/apis/agent-run). Operations with an explicit bot and target are covered in [Platform APIs](/en/plugin/dev/apis/platform).
## Request API
+1 -1
View File
@@ -62,4 +62,4 @@ In addition to the above entities, there are also entities like `Friend` and `Gr
## Platform APIs
Use [Platform APIs](/en/plugin/dev/apis/platform) to query users, manage groups, and call platform-specific actions. EventProcessor and AgentRunner call authorized platform tools through [Run Context APIs](/en/plugin/dev/apis/agent-run).
Use [Platform APIs](/en/plugin/dev/apis/platform) to query users, manage groups, and call platform-specific actions. Runner call authorized platform tools through [Run Context APIs](/en/plugin/dev/apis/agent-run).
+1 -1
View File
@@ -66,7 +66,7 @@ Only actions declared by the adapter are allowed; this is not unrestricted pass-
## Calling from an event processor
EventProcessor and AgentRunner use run-authorized tools and do not expose the generic `call_platform_api` method. For example, when the event supports actor lookup and grants it:
Runner can also call these platform APIs through `self.plugin`, specifying the bot and target explicitly. To use the current event grants and record actions in its run trace, use `ctx.api` or `ctx.reply()`:
```python
tools = {tool.tool_name for tool in ctx.api.get_allowed_tools()}
+3 -3
View File
@@ -4,8 +4,8 @@ title: "Adding Components"
Plugins consist of one or more components that provide different functionalities to LangBot. Currently supported component types include:
- Plugin processor (EventProcessor): handles platform events in plugin code after an instance is created and bound. See the [tutorial](/en/plugin/dev/components/event-processor).
- Agent runner (AgentRunner): provides execution logic selected in an Agent configuration; it is distinct from a standalone plugin processor.
- Plugin processor (Runner): handles platform events in plugin code after an instance is created and bound. See the [tutorial](/en/plugin/dev/components/runner).
- Agent runner (Runner): provides execution logic selected in an Agent configuration; it is distinct from a standalone plugin processor.
- Event Listeners (EventListener): Listen to events during pipeline execution and modify context or pipelines.
- Commands (Command): Triggered by user command messages starting with `!` (or other configured prefixes).
- Tools (Tool): Called by LLMs during execution of LangBot's built-in Local Agent.
@@ -72,7 +72,7 @@ To remove components, you can delete the corresponding information manually.
For detailed usage of each component, please refer to:
- [EventProcessor](./event-processor)
- [Runner](./runner)
- [EventListener](./event-listener)
- [Command](./command)
- [Tool](./tool)
+1 -1
View File
@@ -3,7 +3,7 @@ title: "Component: Event Listener"
---
<Info>
This component extends Pipeline processing steps. To handle Bot events such as incoming messages and new group members, see [Event Processor](/en/plugin/dev/components/event-processor).
This component extends Pipeline processing steps. To handle Bot events such as incoming messages and new group members, see [Event Processor](/en/plugin/dev/components/runner).
</Info>
LangBot's pipeline generates events during runtime for plugins to hook into and process. Each plugin can only have one event listener (components/event_listener/default.yaml), but can register any number of events within it.
@@ -1,28 +1,45 @@
---
title: "Component: Event Processor"
title: "Component: Runner"
---
The EventProcessor component handles Bot events such as incoming messages and new group members. After installing the plugin, users create a **Plugin processor** in LangBot, select the component, and bind Bot events to it.
Runner implements Agent execution or typed event handlers for a **Plugin processor**. Both styles receive `RunnerContext`. After installation, users select and configure the component in LangBot.
For environment setup, see the [plugin development tutorial](/en/plugin/dev/tutor).
## Adding an Event Processor Component
## Adding a Runner Component
A plugin can contain several event processors. Run `lbp comp EventProcessor` in the plugin directory. Enter `welcome` as the name and `Welcome new members` as the description.
A plugin can contain several event processors. Run `lbp comp Runner` in the plugin directory. Enter `welcome` as the name and `Welcome new members` as the description.
```bash
lbp comp EventProcessor
lbp comp Runner
```
The generated `components/event_processor/welcome.yaml` defines the component, and `welcome.py` contains its implementation.
The generated `components/runner/welcome.yaml` defines the component, and `welcome.py` contains its implementation.
## Manifest File: Event Processor
## Custom execution
To integrate a model or implement an execution loop, declare `spec.usages: [agent]` and yield results from `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()),
)
```
## Manifest File: Runner
Replace `welcome.yaml` with:
```yaml
apiVersion: langbot/v1
kind: EventProcessor
kind: Runner
metadata:
name: welcome
label:
@@ -34,6 +51,7 @@ metadata:
zh_Hans: 欢迎新成员,并响应 /hello 消息。
ja_JP: 新しいメンバーを歓迎し、/hello に返信します。
spec:
usages: [event]
events:
- group.member_joined
- message.received
@@ -65,14 +83,17 @@ execution:
`spec.events` lists supported events, and `spec.config` defines settings users can edit on the processor detail page. This example handles new members and incoming messages, with a greeting and a reply switch. Sending replies requires the `tool_calling` and `permissions.tools` settings shown above.
`spec.usages` controls where the component can be selected: `agent` for Agents and Pipelines, `event` for Plugin processors. Both can be declared as `[agent, event]`. Event usage requires `spec.events`. The example uses typed handlers; a custom `run(ctx)` can also call `await super().run(ctx)` to dispatch them.
## Plugin Implementation
Register handlers in the `Welcome` class's `initialize` method. Replace `welcome.py` with:
```python
from langbot_plugin.api.definition.components.event_processor import (
EventProcessor,
EventProcessorContext,
from langbot_plugin.api.definition.components.runner import (
Runner,
RunnerContext,
)
from langbot_plugin.api.entities.builtin.platform.events import (
MemberJoinedEvent,
@@ -81,13 +102,13 @@ from langbot_plugin.api.entities.builtin.platform.events import (
from langbot_plugin.api.entities.builtin.platform.message import Plain
class Welcome(EventProcessor):
class Welcome(Runner):
async def initialize(self):
await super().initialize()
@self.handler(MemberJoinedEvent)
async def on_join(ctx: EventProcessorContext):
name = ctx.event.member.nickname or str(ctx.event.member.id)
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")
@@ -97,9 +118,9 @@ class Welcome(EventProcessor):
await ctx.log("Welcome action completed")
@self.handler(MessageReceivedEvent)
async def on_message(ctx: EventProcessorContext):
async def on_message(ctx: RunnerContext):
text = "".join(
item.text for item in ctx.event.message_chain if isinstance(item, Plain)
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")
@@ -112,7 +133,7 @@ class Welcome(EventProcessor):
`on_join` welcomes new members, and `on_message` replies to `/hello`. With **Send replies** turned off, they only write logs.
`ctx.event` contains the event data, and `ctx.config` contains this processor's settings. Use `ctx.reply()` to reply and `ctx.log()` to write logs. Processing finishes when the handlers return.
`ctx.platform_event` contains the event data, and `ctx.config` contains this processor's settings. Use `ctx.reply()` to reply and `ctx.log()` to write logs. Processing finishes when the handlers return.
## Streaming Replies
@@ -126,7 +147,7 @@ async with ctx.reply_stream() as reply:
The reply finishes when the `async with` block exits normally. If streaming is unsupported, disabled, or the event is not a message, the Host sends one complete message at the end. Exceptions discard buffered partial text. Debug runs simulate delivery.
This API uses the same permission as `ctx.reply()`. AgentRunner components can use `self.get_run_api(ctx).reply_stream()`; model output text is still not sent automatically.
This API uses the same permission as `ctx.reply()`. Runner components can use `self.get_run_api(ctx).reply_stream()`; model output text is still not sent automatically.
## Registering Events
@@ -168,7 +189,7 @@ After installing the plugin, bind the Bot's **Member joined** and **Message rece
## Next Steps
- Explore more examples in [EventProcessorDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/EventProcessorDemo).
- Explore more examples in [RunnerDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/RunnerDemo).
- To extend Pipeline steps such as preprocessing or completion of a model call, use [Event Listener](/en/plugin/dev/components/event-listener).
See [Run Context APIs](/en/plugin/dev/apis/agent-run) for replies, models, tools, history, and state.
+1 -1
View File
@@ -3,7 +3,7 @@ title: "Plugin Development Tutorial"
---
<Info>
To build a plugin for incoming messages, new group members, and other Bot events, complete the basic setup here and continue with the [Event Processor tutorial](/en/plugin/dev/components/event-processor).
To build a plugin for incoming messages, new group members, and other Bot events, complete the basic setup here and continue with the [Event Processor tutorial](/en/plugin/dev/components/runner).
</Info>
## Architecture Foundation
+11 -14
View File
@@ -2,16 +2,13 @@
title: "実行コンテキスト API"
---
AgentRunner と EventProcessor は、現在の実行に許可されたモデル、ツール、ナレッジベース、コンテキストへアクセスする API を使用します。両コンポーネントでは `self.plugin` を使用できません。
Runner の `ctx.api` は現在の実行 ID、コンテキスト、リソースの許可範囲に結び付いています。独自の `run(ctx)` と登録した処理関数は同じ `RunnerContext` を使用します。`self.plugin` から[共通 API](/ja/plugin/dev/apis/common)も利用できますが、現在の実行を自動的に表すものではありません。
## アクセス方法
```python
# Inside an EventProcessor handler
# Inside Runner.run(ctx) or a registered handler
api = ctx.api
# Inside AgentRunner.run(ctx)
api = self.get_run_api(ctx)
```
以下の `api` は現在の実行の API です。 同期の `get_allowed_*` とストリーミングのコンテキスト・イテレーター以外は、`await` で呼び出します。別の実行へ持ち越して再利用しないでください。
@@ -52,7 +49,7 @@ knowledge_bases = api.get_allowed_knowledge_bases() # Each item has kb_id
## 返信とログ
| EventProcessor メソッド | 用途 |
| Runner メソッド | 用途 |
| --- | --- |
| `await ctx.reply(text)` | 現在のイベントの会話へテキストを送信し、操作結果を返します。 |
| `await ctx.log(text, level="info")` | `debug`、`info`、`warning`、`error` のログを記録します。 |
@@ -69,7 +66,7 @@ async with api.reply_stream() as reply:
result = reply.result
```
EventProcessor では `ctx.reply_stream()` を使うと、操作の開始と結果も記録されます。
Runner では `ctx.reply_stream()` を使うと、操作の開始と結果も記録されます。
`update(text)` には差分ではなく**その時点の全文**を渡します。戻り値は操作結果です。正常終了時に返信を完了します。プラットフォームでストリーミング返信が有効なメッセージイベントでは表示を更新し、それ以外の返信可能なイベントでは正常終了時に全文を一度送信します。例外やキャンセルでは未送信のテキストを破棄しますが、表示済みの内容の撤回は保証されません。デバッグ時の送信は Mock です。
@@ -157,19 +154,19 @@ if api.ctx.context.available_apis.state:
| `run_append_result(result)` | `AgentRunEvent` |
| `run_finalize(run_id=None, status=None, reason=None)` | `AgentRun` |
`run_id=None` は現在の実行を指します。EventProcessor はハンドラーの完了時に自動終了するため、通常は記録の手動追加・終了は不要です。AgentRunner は `yield AgentRunResult` で結果を報告します。同じ結果を `run_append_result` で重複登録しないでください。
`run_id=None` は現在の実行を指します。登録した処理関数は戻ると自動終了します。独自の `run(ctx)` は `yield RunnerResult` で結果を報告できます。同じ結果を `run_append_result` で重複登録しないでください。
## 構造化インタラクション(AgentRunner)
## 構造化インタラクション(Runner)
確認やフォーム入力には `yield AgentRunResult.interaction_requested(ctx.run_id, request)` を使用します。`request` は `InteractionRequest` または辞書で、必須フィールドは `interaction_id`、`title`、`fallback_text` です。`kind`(`form`、`confirmation`、`choice`)、`fields`、`actions`、`expires_at` は任意です。
確認やフォーム入力には `yield RunnerResult.interaction_requested(ctx.run_id, request)` を使用します。`request` は `InteractionRequest` または辞書で、必須フィールドは `interaction_id`、`title`、`fallback_text` です。`kind`(`form`、`confirmation`、`choice`)、`fields`、`actions`、`expires_at` は任意です。
`spec.capabilities.interactions: true` と `spec.permissions.interactions: [request]` の宣言、およびホストの許可が必要です。画面の対応状況は `ctx.delivery.interactions` で確認し、送信結果は `interaction.submitted` イベントとして返ります。これは Runner の結果イベントであり、`ctx.api` のメソッドではありません。通常の EventProcessor ハンドラーには直接公開されていません。
`spec.capabilities.interactions: true` と `spec.permissions.interactions: [request]` の宣言、およびホストの許可が必要です。画面の対応状況は `ctx.delivery.interactions` で確認し、送信結果は `interaction.submitted` イベントとして返ります。これは Runner の結果イベントであり、`ctx.api` のメソッドではありません。独自の run(ctx) から yield します。
## エラー処理
```python
from langbot_plugin.api.proxies.agent_run import PermissionDeniedError
from langbot_plugin.api.entities.builtin.agent_runner.errors import AgentAPIException
from langbot_plugin.api.proxies.runner import PermissionDeniedError
from langbot_plugin.api.entities.builtin.runner.errors import AgentAPIException
try:
result = await api.call_tool("event_get_actor", {})
@@ -179,4 +176,4 @@ except AgentAPIException as exc:
await ctx.log(f"{exc.error.code}: {exc.error.message}", level="error")
```
EventProcessor 内の例です。`PermissionDeniedError` はリソース・操作・機能の権限不足を示します。`AgentAPIException.error` は `code`、`message`、`retryable`、`details` を持ちます。送信のタイムアウト時は成功済みの場合もあるため、`retryable` だけで再送しないでください。
Runner 内の例です。`PermissionDeniedError` はリソース・操作・機能の権限不足を示します。`AgentAPIException.error` は `code`、`message`、`retryable`、`details` を持ちます。送信のタイムアウト時は成功済みの場合もあるため、`retryable` だけで再送しないでください。
+1 -1
View File
@@ -4,7 +4,7 @@ title: "プラグイン共通API"
LangBotは、プラグインがさまざまなLangBotモジュールを操作し、メッセージコンテキストを制御するための一連のAPIを提供します。
`Command`、`EventListener`、`Tool` などの通常のコンポーネントは `self.plugin` を使用します。`AgentRunner` と `EventProcessor` は `self.plugin` ではなく[実行コンテキスト API](/ja/plugin/dev/apis/agent-run)を使用してください。プラットフォームの操作は[プラットフォーム API](/ja/plugin/dev/apis/platform)を参照してください。
すべてのコンポーネントは `self.plugin` から本ページの LangBot API を利用できます。Runner の `ctx.api` は現在の実行とリソースの許可範囲に結び付いています。[実行コンテキスト API](/ja/plugin/dev/apis/agent-run)を参照してください。Bot と宛先を指定する操作は[プラットフォーム API](/ja/plugin/dev/apis/platform)に記載しています。
## リクエストAPI
+1 -1
View File
@@ -62,4 +62,4 @@ msg_chain = MessageChain([
## プラットフォーム API
ユーザー取得、グループ管理、固有の操作には[プラットフォーム API](/ja/plugin/dev/apis/platform)を使用してください。EventProcessor と AgentRunner は[実行コンテキスト API](/ja/plugin/dev/apis/agent-run)から許可されたツールを呼び出します。
ユーザー取得、グループ管理、固有の操作には[プラットフォーム API](/ja/plugin/dev/apis/platform)を使用してください。Runner は[実行コンテキスト API](/ja/plugin/dev/apis/agent-run)から許可されたツールを呼び出します。
+1 -1
View File
@@ -66,7 +66,7 @@ account = await self.plugin.call_platform_api(
## イベントプロセッサーからの呼び出し
EventProcessor と AgentRunner は実行に許可されたツールを使用し、汎用の `call_platform_api` は公開しません。現在のイベントで発起者の取得が許可されている場合の例です:
Runner も `self.plugin` から Bot と宛先を指定して本ページの API を利用できます。現在のイベントの許可範囲で操作し、実行記録に残すには `ctx.api` または `ctx.reply()` を使用します:
```python
tools = {tool.tool_name for tool in ctx.api.get_allowed_tools()}
+3 -3
View File
@@ -4,8 +4,8 @@ title: "コンポーネントの追加"
プラグインは、LangBotにさまざまな機能を提供する1つ以上のコンポーネントで構成されています。現在サポートされているコンポーネントタイプには次のものがあります:
- プラグインプロセッサー(EventProcessor):インスタンスを作成してイベントに紐付け、プラグインのコードで処理します。[チュートリアル](/ja/plugin/dev/components/event-processor)を参照してください。
- Agent ランナー(AgentRunner):Agent の設定で選択する実行コンポーネントです。独立したプラグインプロセッサーとは異なります。
- プラグインプロセッサー(Runner):インスタンスを作成してイベントに紐付け、プラグインのコードで処理します。[チュートリアル](/ja/plugin/dev/components/runner)を参照してください。
- Agent ランナー(Runner):Agent の設定で選択する実行コンポーネントです。独立したプラグインプロセッサーとは異なります。
- イベントリスナー(EventListener): パイプライン実行中のイベントをリッスンし、コンテキストやパイプラインを変更します。
- コマンド(Command): `!`(または他の設定されたプレフィックス)で始まるユーザーコマンドメッセージによってトリガーされます。
- ツール(Tool): LangBotの組み込みLocal Agentの実行中にLLMによって呼び出されます。
@@ -72,7 +72,7 @@ components:
各コンポーネントの詳細な使用方法については、次を参照してください:
- [EventProcessor](./event-processor)
- [Runner](./runner)
- [EventListener](./event-listener)
- [Command](./command)
- [Tool](./tool)
+1 -1
View File
@@ -3,7 +3,7 @@ title: "コンポーネント: イベントリスナー"
---
<Info>
このコンポーネントはパイプラインの処理を拡張します。メッセージ受信やメンバー参加などの Bot イベントを処理する場合は、[イベントプロセッサー](/ja/plugin/dev/components/event-processor)を参照してください。
このコンポーネントはパイプラインの処理を拡張します。メッセージ受信やメンバー参加などの Bot イベントを処理する場合は、[イベントプロセッサー](/ja/plugin/dev/components/runner)を参照してください。
</Info>
LangBotのパイプラインは実行時にイベントを生成し、プラグインがフックして処理できるようにします。各プラグインは1つのイベントリスナー(components/event_listener/default.yaml)のみを持つことができますが、その中で任意の数のイベントを登録できます。
@@ -1,28 +1,45 @@
---
title: "コンポーネント: イベントプロセッサー"
title: "コンポーネント: Runner"
---
イベントプロセッサー(EventProcessor)は、メッセージ受信やメンバー参加などの Bot イベントを処理するコンポーネントです。インストール後、ユーザーは LangBot で「プラグインプロセッサー」を作成してコンポーネントを選び、Bot のイベントを紐付けます。
Runner は Agent の実行ロジック、または「プラグインプロセッサー」用のイベント処理関数を実装するコンポーネントです。どちらも `RunnerContext` を受け取り、インストール後にユーザーがコンポーネントを選択・設定します。
環境の準備は[プラグイン開発チュートリアル](/ja/plugin/dev/tutor)を参照してください。
## イベントプロセッサーコンポーネントの追加
## Runner コンポーネントの追加
1 つのプラグインに複数のイベントプロセッサーを追加できます。プラグインのディレクトリで `lbp comp EventProcessor` を実行し、名前に `welcome`、説明に `Welcome new members` を入力します。
1 つのプラグインに複数のイベントプロセッサーを追加できます。プラグインのディレクトリで `lbp comp Runner` を実行し、名前に `welcome`、説明に `Welcome new members` を入力します。
```bash
lbp comp EventProcessor
lbp comp Runner
```
生成される `components/event_processor/welcome.yaml` はコンポーネントを定義するファイルで、`welcome.py` に処理を記述します。
生成される `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: EventProcessor
kind: Runner
metadata:
name: welcome
label:
@@ -34,6 +51,7 @@ metadata:
zh_Hans: 欢迎新成员,并响应 /hello 消息。
ja_JP: 新しいメンバーを歓迎し、/hello に返信します。
spec:
usages: [event]
events:
- group.member_joined
- message.received
@@ -65,14 +83,17 @@ execution:
`spec.events` は対応するイベント、`spec.config` は詳細画面でユーザーが変更できる設定です。この例ではメンバー参加とメッセージ受信に対応し、歓迎メッセージと返信の有効・無効を設定できます。返信には、上記の `tool_calling` と `permissions.tools` の指定が必要です。
`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.event_processor import (
EventProcessor,
EventProcessorContext,
from langbot_plugin.api.definition.components.runner import (
Runner,
RunnerContext,
)
from langbot_plugin.api.entities.builtin.platform.events import (
MemberJoinedEvent,
@@ -81,13 +102,13 @@ from langbot_plugin.api.entities.builtin.platform.events import (
from langbot_plugin.api.entities.builtin.platform.message import Plain
class Welcome(EventProcessor):
class Welcome(Runner):
async def initialize(self):
await super().initialize()
@self.handler(MemberJoinedEvent)
async def on_join(ctx: EventProcessorContext):
name = ctx.event.member.nickname or str(ctx.event.member.id)
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")
@@ -97,9 +118,9 @@ class Welcome(EventProcessor):
await ctx.log("Welcome action completed")
@self.handler(MessageReceivedEvent)
async def on_message(ctx: EventProcessorContext):
async def on_message(ctx: RunnerContext):
text = "".join(
item.text for item in ctx.event.message_chain if isinstance(item, Plain)
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")
@@ -112,7 +133,7 @@ class Welcome(EventProcessor):
`on_join` は参加したメンバーに挨拶し、`on_message` は `/hello` に返信します。「返信する」を無効にすると、ログだけを記録します。
`ctx.event` は現在のイベント、`ctx.config` はこのプロセッサーの設定です。返信には `ctx.reply()`、ログの記録には `ctx.log()` を使います。関数の実行が終わると、イベントの処理も終了します。
`ctx.platform_event` は現在のイベント、`ctx.config` はこのプロセッサーの設定です。返信には `ctx.reply()`、ログの記録には `ctx.log()` を使います。関数の実行が終わると、イベントの処理も終了します。
## ストリーミング返信
@@ -126,7 +147,7 @@ async with ctx.reply_stream() as reply:
`async with` ブロックが正常終了すると返信が完了します。ストリーミングが未対応・無効の場合やメッセージ以外のイベントでは、最後に全文を一度だけ送信します。例外時には、バッファ内の未完成テキストを送信しません。デバッグでは送信をシミュレーションします。
権限は `ctx.reply()` と共通です。AgentRunner も `self.get_run_api(ctx).reply_stream()` を利用できます。モデルの出力テキストが自動送信されることはありません。
権限は `ctx.reply()` と共通です。Runner も `self.get_run_api(ctx).reply_stream()` を利用できます。モデルの出力テキストが自動送信されることはありません。
## イベントの登録
@@ -168,7 +189,7 @@ async with ctx.reply_stream() as reply:
## 次のステップ
- [EventProcessorDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/EventProcessorDemo)で他の実装例を確認できます。
- [RunnerDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/RunnerDemo)で他の実装例を確認できます。
- パイプラインの前処理やモデル呼び出し完了時などに機能を追加する場合は、[イベントリスナー](/ja/plugin/dev/components/event-listener)を使用してください。
返信、モデル、ツール、履歴、状態は[実行コンテキスト API](/ja/plugin/dev/apis/agent-run)を参照してください。
+1 -1
View File
@@ -3,7 +3,7 @@ title: "プラグイン開発チュートリアル"
---
<Info>
メッセージ受信やメンバー参加などを処理するプラグインを作る場合は、基本環境を準備した後、[イベントプロセッサーのチュートリアル](/ja/plugin/dev/components/event-processor)に進んでください。
メッセージ受信やメンバー参加などを処理するプラグインを作る場合は、基本環境を準備した後、[イベントプロセッサーのチュートリアル](/ja/plugin/dev/components/runner)に進んでください。
</Info>
## アーキテクチャの基礎
@@ -1,84 +0,0 @@
# EventProcessor tutorial verification
The published tutorial is in `{zh,en,ja}/plugin/dev/components/event-processor.mdx`.
All three translations contain identical executable Python and component YAML.
Run the example checks with the SDK used by your LangBot installation.
## Repeatable checks
The API references in `{zh,en,ja}/plugin/dev/apis/` are also checked against the
installed SDK. Run `python scripts/verify-plugin-api-examples.py` to execute
the examples, validate method arguments, and check translated code parity.
The Host transport is mocked; this does not verify external delivery.
With the matching SDK installed in the active Python environment:
```bash
python scripts/verify-event-processor-tutorial.py
```
This extracts the actual MDX examples, compares all translations, loads the
component, applies YAML defaults, and executes ten cases. They cover member names
and ID fallback, private/group commands, disabled replies, ordinary/empty/image
messages, and a failed delivery that must retain its logs without completing.
Its tool API is mocked; it does not claim platform delivery.
With Node.js 24 or later:
```bash
npm ci
npm test
npm run typecheck
npm run build
```
The build also checks static HTML, Markdown exports, navigation, canonical URLs,
alternate languages, and preservation of legacy documentation routes.
The companion `langbot-plugin-demo/EventProcessorDemo` repository contains
`examples/event-matrix.json` and `scripts/event_matrix.py`. See its README for
authenticated Host checks covering all 17 event types and invalid payloads.
Use a dedicated observer instance with payload logging enabled and no Bot binding.
## Verified development snapshot: 2026-09-09
Documentation work started from the updated `langbot-app/langbot-docs` main commit
`a86ecb4`, on branch `docs/4.11-event-processor`. The local directory still uses
the previous name `langbot-wiki`.
The tested SDK was `dev/4.11.x` at
`f82b3ce935f9a33afee389fb39fe8dc29a45b615`, both as a freshly built wheel and via
a Git installation from the matching SDK branch in an isolated Python 3.12 environment.
The Host was `dev/4.11.x` at `ea3e32c` with local processor UI refinements.
| Layer | Observed result |
| --- | --- |
| Documentation | 36 Node tests, 6 Python tests, TypeScript check and full build passed; all 30 post-build static checks passed |
| SDK CLI, packaging and event tests | 246 passed, including 92 new event-matrix cases and packaged CLI execution of generated EventProcessor/AgentRunner components |
| Host processor service and run ledger | 73 passed |
| Platform EBA conversion, routing and run-log authorization | 212 passed |
| EventProcessorDemo unit tests | 8 passed, including scenario subtests, lookup errors and instance isolation |
| Installed observer through HTTP → Host → Runtime | 48 passed: 42 valid cases and 6 rejected inputs; one completed run per valid input |
| Tutorial via developer Runtime and installed package | Both workflows passed; final installed checks cover 9 valid cases plus unsupported-event rejection |
| Python extracted from all three tutorial pages | 10 passed, also using the SDK installed from Git |
| Local OneBot transport → adapter → binding → installed processor | 5 passed: unbound, bound, disabled, higher priority and equal-priority order |
| Authenticated browser tutorial | Selected member joined, entered Alice/alice/tutorial-group, clicked once; one reply trace, one completed run, and automatic switch to Logs |
The generated AgentRunner was also connected with `lbp run` and invoked through
the real Host, producing its expected echo. The tutorial EventProcessor was built
and uploaded as `tutorial-WelcomeEvents-0.1.0.lbpkg`; it continued to work after
the developer process stopped and the Host restarted.
The localhost OneBot peer received exactly one `send_group_msg` in the bound and
equal-priority cases. Other cases sent none. It exercised normal routing, not the
debug Mock, but the remote platform was simulated locally: this is not evidence
of delivery through QQ or every other third-party service. Original QA Bot
bindings and the local extension limit were restored after testing.
Python hot reload worked during development. Component YAML and manifest changes
required restarting `lbp run`; the tutorial explicitly documents that boundary.
This matrix covers the registered event model and representative data, failure,
concurrency and routing branches. Arbitrary plugin business logic and every real
platform/account combination require their own integration checks. Local passes
are not a CI, publication or deployment result.
+14
View File
@@ -0,0 +1,14 @@
# Runner tutorial verification
With the local SDK installed, run:
```bash
python scripts/verify-runner-tutorial.py
python scripts/verify-plugin-api-examples.py
npm test
npm run build
```
The tutorial checker extracts the published Chinese, English and Japanese examples, checks that their code and manifests match, and executes event handlers including disabled replies, empty input, private/group messages and delivery failure. Platform actions use Mock; these tests do not prove external message delivery.
The official `langbot-plugin-demo/RunnerDemo` plugin adds multi-step actions, configuration isolation and progress/error examples. Its `scripts/smoke.py` installs a built package and runs the scenarios against a local Host. Use the same SDK and Core revision when testing.
+6 -6
View File
@@ -15,12 +15,12 @@ from types import SimpleNamespace
from typing import Any
from unittest.mock import AsyncMock
from langbot_plugin.api.entities.builtin.agent_runner.context import AgentRunContext
from langbot_plugin.api.entities.builtin.agent_runner.errors import (
from langbot_plugin.api.entities.builtin.runner.context import RunnerContext
from langbot_plugin.api.entities.builtin.runner.errors import (
AgentAPIError,
AgentAPIException,
)
from langbot_plugin.api.proxies.agent_run import AgentRunAPIProxy
from langbot_plugin.api.proxies.runner import RunnerAPIProxy
from langbot_plugin.api.proxies.langbot_api import LangBotAPIProxy
from langbot_plugin.api.entities.builtin.provider.message import Message
@@ -74,7 +74,7 @@ class Host:
def context():
return AgentRunContext.model_validate(
return RunnerContext.model_validate(
{
"run_id": "docs-run",
"trigger": {"type": "user_message"},
@@ -131,7 +131,7 @@ async def execute(block, namespace):
cls = (
LangBotAPIProxy
if owner == "self.plugin"
else AgentRunAPIProxy
else RunnerAPIProxy
if owner == "api"
else None
)
@@ -154,7 +154,7 @@ async def main():
assert blocks == snippets(locale, page), f"{page}: {locale} code differs"
for block in blocks:
host = Host()
api = AgentRunAPIProxy(context(), host)
api = RunnerAPIProxy(context(), host)
ctx = SimpleNamespace(api=api, log=AsyncMock())
namespace = {
"Any": Any,
@@ -1,6 +1,6 @@
"""Execute the published tutorial with the matching 4.11 SDK installed.
Run: python scripts/verify-event-processor-tutorial.py
Run: python scripts/verify-runner-tutorial.py
The tool API is mocked; real Host/runtime checks are a separate validation layer.
"""
@@ -11,6 +11,7 @@ from types import SimpleNamespace
from unittest.mock import AsyncMock, Mock
import yaml
from langbot_plugin.api.definition.components.runner import RunnerContext
ROOT = Path(__file__).resolve().parents[1]
@@ -19,21 +20,21 @@ ROOT = Path(__file__).resolve().parents[1]
def load_tutorial():
examples = []
for locale in ("zh", "en", "ja"):
path = ROOT / locale / "plugin/dev/components/event-processor.mdx"
path = ROOT / locale / "plugin/dev/components/runner.mdx"
source = path.read_text()
python = re.findall(r"^```python\n(.*?)^```", source, re.M | re.S)
manifests = [
block
for block in re.findall(r"^```yaml\n(.*?)^```", source, re.M | re.S)
if "kind: EventProcessor" in block
if "kind: Runner" in block
]
python = [block for block in python if "class Welcome(EventProcessor):" in block]
python = [block for block in python if "class Welcome(Runner):" in block]
assert len(python) == len(manifests) == 1, path
examples.append((python[0], manifests[0]))
assert examples[0] == examples[1] == examples[2], "Locale examples differ"
code, manifest = examples[0]
namespace = {}
exec(compile(code, "event-processor.mdx", "exec"), namespace)
exec(compile(code, "runner.mdx", "exec"), namespace)
manifest = yaml.safe_load(manifest)
assert manifest["spec"]["events"] == ["group.member_joined", "message.received"]
assert manifest["spec"]["capabilities"]["tool_calling"] is True
@@ -121,12 +122,14 @@ async def main():
)
)
component.get_run_api = Mock(return_value=api)
context = SimpleNamespace(
run_id=name, config={**defaults, **config}, event=SimpleNamespace(data=data)
)
context = RunnerContext.model_validate({
"run_id": name, "trigger": {"type": data["type"]},
"event": {"event_id": name, "event_type": data["type"], "source": "test", "data": data},
"config": {**defaults, **config}, "input": {}, "delivery": {"surface": "test"}, "resources": {}, "runtime": {},
})
results = []
try:
async for result in component.run(context):
async for result in component.invoke(context):
results.append(result)
except RuntimeError as exc:
assert failure and str(exc) == "Delivery failed"
+21 -21
View File
@@ -2142,27 +2142,6 @@
<xhtml:link rel="alternate" hreflang="ja" href="https://langbot.app/docs/ja/plugin/dev/components/event-listener" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://langbot.app/docs/en/plugin/dev/components/event-listener" />
</url>
<url>
<loc>https://langbot.app/docs/en/plugin/dev/components/event-processor</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://langbot.app/docs/en/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="zh-CN" href="https://langbot.app/docs/zh/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="ja" href="https://langbot.app/docs/ja/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://langbot.app/docs/en/plugin/dev/components/event-processor" />
</url>
<url>
<loc>https://langbot.app/docs/zh/plugin/dev/components/event-processor</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://langbot.app/docs/en/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="zh-CN" href="https://langbot.app/docs/zh/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="ja" href="https://langbot.app/docs/ja/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://langbot.app/docs/en/plugin/dev/components/event-processor" />
</url>
<url>
<loc>https://langbot.app/docs/ja/plugin/dev/components/event-processor</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://langbot.app/docs/en/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="zh-CN" href="https://langbot.app/docs/zh/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="ja" href="https://langbot.app/docs/ja/plugin/dev/components/event-processor" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://langbot.app/docs/en/plugin/dev/components/event-processor" />
</url>
<url>
<loc>https://langbot.app/docs/en/plugin/dev/components/knowledge-engine</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://langbot.app/docs/en/plugin/dev/components/knowledge-engine" />
@@ -2247,6 +2226,27 @@
<xhtml:link rel="alternate" hreflang="ja" href="https://langbot.app/docs/ja/plugin/dev/components/parser" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://langbot.app/docs/en/plugin/dev/components/parser" />
</url>
<url>
<loc>https://langbot.app/docs/en/plugin/dev/components/runner</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://langbot.app/docs/en/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="zh-CN" href="https://langbot.app/docs/zh/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="ja" href="https://langbot.app/docs/ja/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://langbot.app/docs/en/plugin/dev/components/runner" />
</url>
<url>
<loc>https://langbot.app/docs/zh/plugin/dev/components/runner</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://langbot.app/docs/en/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="zh-CN" href="https://langbot.app/docs/zh/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="ja" href="https://langbot.app/docs/ja/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://langbot.app/docs/en/plugin/dev/components/runner" />
</url>
<url>
<loc>https://langbot.app/docs/ja/plugin/dev/components/runner</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://langbot.app/docs/en/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="zh-CN" href="https://langbot.app/docs/zh/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="ja" href="https://langbot.app/docs/ja/plugin/dev/components/runner" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://langbot.app/docs/en/plugin/dev/components/runner" />
</url>
<url>
<loc>https://langbot.app/docs/en/plugin/dev/components/tool</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://langbot.app/docs/en/plugin/dev/components/tool" />
+11 -14
View File
@@ -2,16 +2,13 @@
title: "运行上下文 API"
---
AgentRunner 和 EventProcessor 使用当前运行的 API,访问已授权的模型、工具、知识库和上下文。这两个组件不提供 `self.plugin`。
Runner 的 `ctx.api` 携带当前运行的 ID、上下文与资源授权;自定义 `run(ctx)` 和事件处理函数使用同一种 `RunnerContext`。`self.plugin` 仍可访问[插件通用 API](/zh/plugin/dev/apis/common),但不会自动代表当前运行。
## 访问方式
```python
# Inside an EventProcessor handler
# Inside Runner.run(ctx) or a registered handler
api = ctx.api
# Inside AgentRunner.run(ctx)
api = self.get_run_api(ctx)
```
以下示例中的 `api` 均指当前运行的 API。 除 `get_allowed_*` 同步方法和流式上下文/迭代接口外,下文方法均使用 `await` 调用。不要跨运行保存或复用这个对象。
@@ -52,7 +49,7 @@ knowledge_bases = api.get_allowed_knowledge_bases() # Each item has kb_id
## 回复与日志
| EventProcessor 方法 | 用途 |
| Runner 方法 | 用途 |
| --- | --- |
| `await ctx.reply(text)` | 向当前事件的会话发送文本,返回动作结果。 |
| `await ctx.log(text, level="info")` | 记录日志;级别为 `debug`、`info`、`warning`、`error`。 |
@@ -69,7 +66,7 @@ async with api.reply_stream() as reply:
result = reply.result
```
EventProcessor 也可使用 `ctx.reply_stream()`,它会同时记录动作的开始与结果。
Runner 也可使用 `ctx.reply_stream()`,它会同时记录动作的开始与结果。
`update(text)` 接收**当前完整文本**,不是新增片段;返回本次动作结果。正常退出时完成回复。支持流式且开启流式回复的消息事件会更新平台消息;其他可回复事件会缓存文本,在成功结束时发送一次。异常或取消时丢弃缓存中的未完成文本,已显示的内容不保证能撤回。调试模式仅模拟平台发送。
@@ -157,19 +154,19 @@ if api.ctx.context.available_apis.state:
| `run_append_result(result)` | `AgentRunEvent` |
| `run_finalize(run_id=None, status=None, reason=None)` | `AgentRun` |
`run_id=None` 默认当前运行。普通 EventProcessor 处理函数返回后会自动结束,通常不需要手动追加或结束运行记录。AgentRunner 通过 `yield AgentRunResult` 上报结果,避免同时用 `run_append_result` 重复记录相同结果。
`run_id=None` 表示当前运行。事件处理函数返回后自动结束;自定义 `run(ctx)` 可通过 `yield RunnerResult` 上报结果,不要再用 `run_append_result` 重复登记。
## 结构化交互(AgentRunner)
## 结构化交互(Runner)
需要用户确认或填写表单时,通过 `yield AgentRunResult.interaction_requested(ctx.run_id, request)` 请求交互。`request` 可以是 `InteractionRequest` 对象或字典,必填字段为 `interaction_id`、`title`、`fallback_text`;可选字段有 `kind`(`form`、`confirmation`、`choice`)、`fields`、`actions`、`expires_at`。
需要用户确认或填写表单时,通过 `yield RunnerResult.interaction_requested(ctx.run_id, request)` 请求交互。`request` 可以是 `InteractionRequest` 对象或字典,必填字段为 `interaction_id`、`title`、`fallback_text`;可选字段有 `kind`(`form`、`confirmation`、`choice`)、`fields`、`actions`、`expires_at`。
清单需声明 `spec.capabilities.interactions: true` 和 `spec.permissions.interactions: [request]`,并获得宿主授权。界面支持情况见 `ctx.delivery.interactions`;提交结果以 `interaction.submitted` 事件返回。这是 Runner 的结果事件,不是 `ctx.api` 方法,EventProcessor 的普通处理函数不直接提供此接口。
清单需声明 `spec.capabilities.interactions: true` 和 `spec.permissions.interactions: [request]`,并获得宿主授权。界面支持情况见 `ctx.delivery.interactions`;提交结果以 `interaction.submitted` 事件返回。这是 Runner 的结果事件,不是 `ctx.api` 方法,Runner 的普通处理函数不直接提供此接口。
## 错误处理
```python
from langbot_plugin.api.proxies.agent_run import PermissionDeniedError
from langbot_plugin.api.entities.builtin.agent_runner.errors import AgentAPIException
from langbot_plugin.api.proxies.runner import PermissionDeniedError
from langbot_plugin.api.entities.builtin.runner.errors import AgentAPIException
try:
result = await api.call_tool("event_get_actor", {})
@@ -179,4 +176,4 @@ except AgentAPIException as exc:
await ctx.log(f"{exc.error.code}: {exc.error.message}", level="error")
```
这是 EventProcessor 中的示例。`PermissionDeniedError` 表示资源、操作或能力未授权;`AgentAPIException.error` 包含 `code`、`message`、`retryable`、`details`。发送消息等动作超时后,结果可能不确定,不应仅依据 `retryable` 自动重发。
这是 Runner 中的示例。`PermissionDeniedError` 表示资源、操作或能力未授权;`AgentAPIException.error` 包含 `code`、`message`、`retryable`、`details`。发送消息等动作超时后,结果可能不确定,不应仅依据 `retryable` 自动重发。
+1 -1
View File
@@ -4,7 +4,7 @@ title: "插件通用 API"
LangBot 为插件提供了一系列 API ,用于操作 LangBot 各个模块、控制消息上下文等。
`Command`、`EventListener`、`Tool` 等普通组件通过 `self.plugin` 使用本页 API。`AgentRunner` 和 `EventProcessor` 请使用[运行上下文 API](/zh/plugin/dev/apis/agent-run),不使用 `self.plugin`。平台查询和专有动作见[平台 API](/zh/plugin/dev/apis/platform)。
所有组件都可通过 `self.plugin` 使用本页的 LangBot API。Runner 的 `ctx.api` 则携带当前运行的上下文和资源授权,见[运行上下文 API](/zh/plugin/dev/apis/agent-run)。指定机器人和目标的平台操作见[平台 API](/zh/plugin/dev/apis/platform)。
## 请求 API
+1 -1
View File
@@ -62,4 +62,4 @@ msg_chain = MessageChain([
## 平台 API
查询用户、管理群组和调用平台专有接口,请使用[平台 API](/zh/plugin/dev/apis/platform)。EventProcessor 和 AgentRunner 通过[运行上下文 API](/zh/plugin/dev/apis/agent-run)调用已授权的平台工具。
查询用户、管理群组和调用平台专有接口,请使用[平台 API](/zh/plugin/dev/apis/platform)。Runner 通过[运行上下文 API](/zh/plugin/dev/apis/agent-run)调用已授权的平台工具。
+1 -1
View File
@@ -66,7 +66,7 @@ account = await self.plugin.call_platform_api(
## 在事件处理器中调用
EventProcessor 和 AgentRunner 使用运行授权的工具,不提供通用的 `call_platform_api` 方法。例如,在当前事件支持查询发起者且已授权时:
Runner 也可以通过 `self.plugin` 调用本页的平台 API,显式指定机器人和目标。若要按当前事件的授权查询或回复,并在运行记录中展示动作,请使用 `ctx.api` 或 `ctx.reply()`:
```python
tools = {tool.tool_name for tool in ctx.api.get_allowed_tools()}
+3 -3
View File
@@ -4,8 +4,8 @@ title: "添加组件"
插件由一种或多种组件构成,为 LangBot 提供不同的功能。目前支持的组件类型有:
- 事件处理器(EventProcessor):处理机器人收到的消息、成员入群等事件。详见[开发教程](/zh/plugin/dev/components/event-processor)。
- Agent 运行器(AgentRunner):为 Agent 提供模型调用、外部 Agent 服务连接等运行方式。
- 事件处理器(Runner):处理机器人收到的消息、成员入群等事件。详见[开发教程](/zh/plugin/dev/components/runner)。
- Agent 运行器(Runner):为 Agent 提供模型调用、外部 Agent 服务连接等运行方式。
- 事件监听器(EventListener):监听流水线执行期间的事件,对上下文或流水线进行修改。
- 命令(Command):由用户通过`!`(或其他已设置的前缀)开头的命令消息触发。
- 工具(Tool):供 LangBot 内置的 Local Agent 在执行期间由 LLM 调用。
@@ -73,7 +73,7 @@ components:
各个组件的详细使用方式请参考:
- [EventProcessor](./event-processor)
- [Runner](./runner)
- [EventListener](./event-listener)
- [Command](./command)
- [Tool](./tool)
+1 -1
View File
@@ -3,7 +3,7 @@ title: "组件:事件监听器"
---
<Info>
本组件用于扩展流水线的处理流程。如果您需要处理机器人收到的消息、成员入群等事件,请参阅[事件处理器](/zh/plugin/dev/components/event-processor)。
本组件用于扩展流水线的处理流程。如果您需要处理机器人收到的消息、成员入群等事件,请参阅[事件处理器](/zh/plugin/dev/components/runner)。
</Info>
LangBot 的流水线在运行期间会产生一些事件,供插件 Hook 并处理。每个插件中只允许添加一个事件监听器(components/event_listener/default.yaml),但在其中可以注册任意数量的事件。
@@ -1,28 +1,45 @@
---
title: "组件:事件处理器"
title: "组件:Runner"
---
事件处理器(EventProcessor)组件用于处理机器人收到的消息、成员入群等事件。用户安装插件后,可在 LangBot 中创建「插件处理器」并选择该组件,再绑定机器人的事件。
Runner 组件用于实现 Agent 的运行逻辑,也可以按事件注册处理函数,供「插件处理器」使用。两种写法共用 `RunnerContext`,插件安装后由用户选择组件并配置。
环境准备见[插件开发教程](/zh/plugin/dev/tutor)。
## 添加事件处理器组件
## 添加 Runner 组件
单个插件中可以添加多个事件处理器。请在插件目录执行命令 `lbp comp EventProcessor`,名称填写 `welcome`,描述填写 `Welcome new members`。
单个插件中可以添加多个事件处理器。请在插件目录执行命令 `lbp comp Runner`,名称填写 `welcome`,描述填写 `Welcome new members`。
```bash
lbp comp EventProcessor
lbp comp Runner
```
生成的 `components/event_processor/welcome.yaml` 定义组件信息,`welcome.py` 是处理程序。
生成的 `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: EventProcessor
kind: Runner
metadata:
name: welcome
label:
@@ -34,6 +51,7 @@ metadata:
zh_Hans: 欢迎新成员,并响应 /hello 消息。
ja_JP: 新しいメンバーを歓迎し、/hello に返信します。
spec:
usages: [event]
events:
- group.member_joined
- message.received
@@ -65,14 +83,17 @@ execution:
`spec.events` 指定支持的事件,`spec.config` 定义用户可在处理器详情页修改的配置。本例支持成员入群和收到消息,并提供欢迎语、回复开关两个配置项。回复消息需要上面的 `tool_calling` 和 `permissions.tools` 配置。
`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.event_processor import (
EventProcessor,
EventProcessorContext,
from langbot_plugin.api.definition.components.runner import (
Runner,
RunnerContext,
)
from langbot_plugin.api.entities.builtin.platform.events import (
MemberJoinedEvent,
@@ -81,13 +102,13 @@ from langbot_plugin.api.entities.builtin.platform.events import (
from langbot_plugin.api.entities.builtin.platform.message import Plain
class Welcome(EventProcessor):
class Welcome(Runner):
async def initialize(self):
await super().initialize()
@self.handler(MemberJoinedEvent)
async def on_join(ctx: EventProcessorContext):
name = ctx.event.member.nickname or str(ctx.event.member.id)
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")
@@ -97,9 +118,9 @@ class Welcome(EventProcessor):
await ctx.log("Welcome action completed")
@self.handler(MessageReceivedEvent)
async def on_message(ctx: EventProcessorContext):
async def on_message(ctx: RunnerContext):
text = "".join(
item.text for item in ctx.event.message_chain if isinstance(item, Plain)
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")
@@ -112,7 +133,7 @@ class Welcome(EventProcessor):
`on_join` 在成员入群时发送欢迎语,`on_message` 回复 `/hello`。关闭「发送回复」后,只记录日志。
处理函数中,`ctx.event` 保存当前事件的数据,`ctx.config` 是当前处理器的配置。调用 `ctx.reply()` 回复消息,`ctx.log()` 记录日志。函数执行完毕,本次处理即结束。
处理函数中,`ctx.platform_event` 保存当前事件的数据,`ctx.config` 是当前处理器的配置。调用 `ctx.reply()` 回复消息,`ctx.log()` 记录日志。函数执行完毕,本次处理即结束。
## 流式回复
@@ -126,7 +147,7 @@ async with ctx.reply_stream() as reply:
正常退出 `async with` 时完成回复。平台不支持流式、未开启流式回复或当前不是消息事件时,会在结束后发送一条完整消息;发生异常时不发送缓存中的未完成文本。调试模式仅模拟发送。
此接口沿用 `ctx.reply()` 的权限。AgentRunner 可通过 `self.get_run_api(ctx).reply_stream()` 使用同一接口,模型返回文本仍不会自动发送。
此接口沿用 `ctx.reply()` 的权限。Runner 可通过 `self.get_run_api(ctx).reply_stream()` 使用同一接口,模型返回文本仍不会自动发送。
## 事件注册
@@ -168,7 +189,7 @@ async with ctx.reply_stream() as reply:
## 接下来做什么
- 查看 [EventProcessorDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/EventProcessorDemo) 中的更多示例。
- 查看 [RunnerDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/RunnerDemo) 中的更多示例。
- 如果要扩展流水线的预处理、模型调用完成等步骤,请使用[事件监听器](/zh/plugin/dev/components/event-listener)。
回复、模型、工具、历史与状态接口见[运行上下文 API](/zh/plugin/dev/apis/agent-run)。
+1 -1
View File
@@ -3,7 +3,7 @@ title: "插件开发教程"
---
<Info>
要开发处理消息、成员入群等事件的插件,可在完成基础环境准备后,继续阅读[事件处理器教程](/zh/plugin/dev/components/event-processor)。
要开发处理消息、成员入群等事件的插件,可在完成基础环境准备后,继续阅读[事件处理器教程](/zh/plugin/dev/components/runner)。
</Info>
## 架构基础