mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-30 12:25:29 +08:00
Merge pull request #62 from langbot-app/feat/new-plugin
Feat/new plugin
This commit is contained in:
@@ -1,164 +0,0 @@
|
||||
# API 参考
|
||||
|
||||
:::info 目录
|
||||
[[toc]]
|
||||
:::
|
||||
|
||||
## 事件 API
|
||||
|
||||
以下 API 仅在事件处理函数中可用。
|
||||
|
||||
### 回复消息
|
||||
|
||||
```python
|
||||
await ctx.reply(message_chain: MessageChain)
|
||||
```
|
||||
|
||||
回复此次事件的发起会话。
|
||||
|
||||
- `message_chain`:[消息链对象](./messages.md),程序能自动转换为目标消息平台消息链
|
||||
|
||||
### 发送主动消息
|
||||
|
||||
> 由于 QQ 官方 API 对主动消息的支持性很差,故若用户使用的是 QQ 官方 API,发送主动消息可能会失败
|
||||
|
||||
```python
|
||||
await ctx.send_message(target_type: str, target_id: str, message_chain: MessageChain)
|
||||
```
|
||||
|
||||
发送主动消息给目标。
|
||||
|
||||
- `target_type`:目标类型,可选值:`"person"`、`"group"`
|
||||
- `target_id`:目标 ID(QQ 号或群号)
|
||||
- `message_chain`:[消息链对象](./messages.md),程序能自动转换为目标消息平台消息链
|
||||
|
||||
### 阻止事件默认行为
|
||||
|
||||
```python
|
||||
ctx.prevent_default()
|
||||
```
|
||||
|
||||
阻止此次事件的默认行为即停止处理流程之后的行为(如私聊收到消息时向接口获取回复、群消息收到消息时向接口获取回复等)。
|
||||
|
||||
### 阻止后续插件执行
|
||||
|
||||
```python
|
||||
ctx.prevent_postorder()
|
||||
```
|
||||
|
||||
阻止此次事件后续插件的执行。插件的执行顺序请通过`插件介绍`中的`插件管理`介绍的方式修改优先级。
|
||||
|
||||
### 添加返回值
|
||||
|
||||
```python
|
||||
ctx.add_return(name: str, value: Any)
|
||||
```
|
||||
|
||||
添加返回值,事件返回值均为**可选**的,每个事件支持的返回值请查看`pkg.plugin.events`中的每个事件的注释。
|
||||
|
||||
## 请求 API
|
||||
|
||||
`请求(Query)`指的是用户向 LangBot 发送一个问题时,LangBot 处理该问题的程序上下文。此段落的 API 用于与这些上下文交互。
|
||||
|
||||
- 事件处理函数中,Query 对象一般位于`ctx.event.query`
|
||||
|
||||
### 设置请求变量
|
||||
|
||||
```python
|
||||
ctx.event.query.set_variable(key: str, value: typing.Any)
|
||||
```
|
||||
|
||||
请求变量是与此次 Query 绑定的一个 `dict`,其中包含了一些程序上下文信息。如果使用的是 Dify 或者 阿里云百炼 等 LLMOps 的runner,[这些变量将被传入对应平台的 API 作为变量](/zh/deploy/pipelines/readme.html#%E5%AF%B9%E8%AF%9D%E5%8F%98%E9%87%8F)。
|
||||
如果您需要使用插件设置变量,建议在`PromptPreProcessing`事件时处理。
|
||||
|
||||
- `key`:该变量的名称
|
||||
- `value`:该变量的值
|
||||
|
||||
### 获取请求变量
|
||||
|
||||
```python
|
||||
ctx.event.query.get_variable(key: str)
|
||||
```
|
||||
|
||||
获取请求变量。
|
||||
|
||||
- `key`:变量名称
|
||||
- 返回值:`typing.Any`
|
||||
|
||||
### 获取所有请求变量
|
||||
|
||||
```python
|
||||
ctx.event.query.get_variables()
|
||||
```
|
||||
|
||||
获取此 Query 对象所有已设置的变量值。
|
||||
|
||||
- 返回值:`dict[str, typing.Any]`
|
||||
|
||||
|
||||
## LangBot API
|
||||
|
||||
以下 API 是 LangBot 直接提供给插件调用的,可以在事件处理函数之外调用。
|
||||
|
||||
- `host` 表示 `pkg.plugin.context.APIHost` 类的对象,会被包含在每个插件类中。
|
||||
|
||||
### 获取已启用的消息平台适配器列表
|
||||
|
||||
```python
|
||||
host.get_platform_adapters()
|
||||
```
|
||||
|
||||
获取已启用的消息平台适配器列表。
|
||||
|
||||
- 返回值:`list[pkg.platform.adapter.MessageSourceAdapter]`
|
||||
|
||||
### 发送主动消息
|
||||
|
||||
```python
|
||||
await host.send_active_message(adapter: pkg.platform.adapter.MessageSourceAdapter, target_type: str, target_id: str, message_chain: MessageChain)
|
||||
```
|
||||
|
||||
发送主动消息给目标。
|
||||
|
||||
- `adapter`:消息平台适配器,通过 `host.get_platform_adapters()` 获取并选择其中一个
|
||||
- `target_type`:目标类型,可选值:`"person"`、`"group"`
|
||||
- `target_id`:目标 ID(QQ 号或群号)
|
||||
- `message_chain`:[消息链对象](./messages.md),程序能自动转换为目标消息平台消息链
|
||||
|
||||
:::info
|
||||
- 某些消息平台可能不支持主动消息,或有严格限制
|
||||
- 某些消息平台适配器(例如`aiocqhttp`)是作为服务端等待消息平台连接上来推送消息,在连接成功之前,发送主动消息会失败
|
||||
:::
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import pkg.platform.types as platform_types
|
||||
|
||||
...
|
||||
|
||||
# 某个插件的 initialize 函数
|
||||
# 此代码将在 LangBot 启动 10 秒后尝试向 QQ 号 1010553892 发送消息 "hello, world!"
|
||||
async def initialize(self):
|
||||
print(self.host.get_platform_adapters())
|
||||
|
||||
async def send_message():
|
||||
print("send message start waiting")
|
||||
await asyncio.sleep(10)
|
||||
|
||||
try:
|
||||
await self.host.send_active_message(
|
||||
adapter=self.host.get_platform_adapters()[0],
|
||||
target_type="person",
|
||||
target_id="1010553892",
|
||||
message=platform_types.MessageChain([
|
||||
platform_types.Plain("hello, world!")
|
||||
])
|
||||
)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
print("send message end")
|
||||
|
||||
asyncio.get_running_loop().create_task(send_message())
|
||||
```
|
||||
@@ -0,0 +1,300 @@
|
||||
# 插件通用 API
|
||||
|
||||
LangBot 为插件提供了一系列 API ,用于操作 LangBot 各个模块、控制消息上下文等。
|
||||
|
||||
:::info 目录
|
||||
[[toc]]
|
||||
:::
|
||||
|
||||
## 请求 API
|
||||
|
||||
操作当前正在处理的用户请求(消息)。仅在`EventListener`和`Command`组件中可用。访问方式如下:
|
||||
|
||||
- `EventListener` 的各个事件处理器方法中:`event_context: context.EventContext`对象内部方法
|
||||
- `Command` 的各个子命令处理器方法中:`context: ExecuteContext`对象内部方法
|
||||
|
||||
### 直接回复消息
|
||||
|
||||
直接回复一个消息链到当前请求所在会话。
|
||||
|
||||
消息链构造方式请参考[消息平台实体](/zh/plugin/dev/apis/messages)。
|
||||
|
||||
```python
|
||||
async def reply(
|
||||
self, message_chain: platform_message.MessageChain, quote_origin: bool = False
|
||||
):
|
||||
"""Reply to the message request
|
||||
|
||||
Args:
|
||||
message_chain (platform.types.MessageChain): LangBot message chain
|
||||
quote_origin (bool): Whether to quote the original message
|
||||
"""
|
||||
|
||||
# 调用示例
|
||||
await event_context.reply(
|
||||
platform_message.MessageChain([
|
||||
platform_message.Plain(text="Hello, world!"),
|
||||
]),
|
||||
)
|
||||
```
|
||||
|
||||
### 获取机器人 UUID
|
||||
|
||||
获取当前请求来源机器人 UUID。
|
||||
|
||||
```python
|
||||
async def get_bot_uuid(self) -> str:
|
||||
"""Get the bot uuid"""
|
||||
|
||||
# 调用示例
|
||||
bot_uuid = await event_context.get_bot_uuid()
|
||||
```
|
||||
|
||||
### 设置请求变量
|
||||
|
||||
单次请求中某些信息会被存储到请求变量中,在使用 Dify 等外部 LLMOps 平台时,[这些变量会显式传入 LLMOps 平台](/zh/deploy/pipelines/readme.html#%E8%AF%B7%E6%B1%82%E5%8F%98%E9%87%8F)。
|
||||
|
||||
```python
|
||||
async def set_query_var(self, key: str, value: Any):
|
||||
"""Set a query variable"""
|
||||
|
||||
# 调用示例
|
||||
await event_context.set_query_var("key", "value")
|
||||
```
|
||||
|
||||
### 获取请求变量
|
||||
|
||||
获取单个请求变量。
|
||||
|
||||
```python
|
||||
async def get_query_var(self, key: str) -> Any:
|
||||
"""Get a query variable"""
|
||||
|
||||
# 调用示例
|
||||
query_var = await event_context.get_query_var("key")
|
||||
```
|
||||
|
||||
### 获取所有请求变量
|
||||
|
||||
```python
|
||||
async def get_query_vars(self) -> dict[str, Any]:
|
||||
"""Get all query variables"""
|
||||
|
||||
# 调用示例
|
||||
query_vars = await event_context.get_query_vars()
|
||||
```
|
||||
|
||||
## LangBot API
|
||||
|
||||
这些 API 可在插件任何组件中调用。访问方式如下:
|
||||
|
||||
- 插件根目录`main.py`中:`self` 对象内部方法,这些 API 均由插件类父类 `BasePlugin` 提供。
|
||||
- 插件任何组件类中:`self.plugin` 对象内部方法。
|
||||
|
||||
### 获取插件配置信息
|
||||
|
||||
插件配置格式可在`manifest.yaml`中编写,用户需要在 LangBot 的插件管理中按照插件配置格式填写。之后插件代码可以调用此 API 获取插件配置信息。
|
||||
|
||||
```python
|
||||
def get_config(self) -> dict[str, typing.Any]:
|
||||
"""Get the config of the plugin."""
|
||||
|
||||
# 调用示例
|
||||
config = self.plugin.get_config()
|
||||
```
|
||||
|
||||
### 获取 LangBot 版本
|
||||
|
||||
获取 LangBot 版本号,返回格式为字符串`v<major>.<minor>.<patch>`。
|
||||
|
||||
```python
|
||||
async def get_langbot_version(self) -> str:
|
||||
"""Get the langbot version"""
|
||||
|
||||
# 调用示例
|
||||
langbot_version = await self.plugin.get_langbot_version()
|
||||
```
|
||||
|
||||
### 获取已配置的机器人列表
|
||||
|
||||
返回由所有机器人 UUID 组成的列表。
|
||||
|
||||
```python
|
||||
async def get_bots(self) -> list[str]:
|
||||
"""Get all bots"""
|
||||
|
||||
# 调用示例
|
||||
bots = await self.plugin.get_bots()
|
||||
```
|
||||
|
||||
### 获取机器人信息
|
||||
|
||||
获取机器人信息。
|
||||
|
||||
```python
|
||||
async def get_bot_info(self, bot_uuid: str) -> dict[str, Any]:
|
||||
"""Get a bot info"""
|
||||
|
||||
# 调用示例
|
||||
bot_info = await self.plugin.get_bot_info("de639861-be05-4018-859b-c2e2d3e0d603")
|
||||
|
||||
# 返回示例
|
||||
{
|
||||
"uuid": "de639861-be05-4018-859b-c2e2d3e0d603",
|
||||
"name": "aiocqhttp",
|
||||
"description": "由 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_account_id": 960164003 # 机器人账号 ID
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 发送主动消息
|
||||
|
||||
通过机器人 UUID 和目标会话 ID 发送主动消息。
|
||||
|
||||
消息链构造方式请参考[消息平台实体](/zh/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:
|
||||
"""Send a message to a session"""
|
||||
|
||||
# 调用示例
|
||||
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]:
|
||||
"""Get all LLM models"""
|
||||
|
||||
# 调用示例
|
||||
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] = {},
|
||||
) -> provider_message.Message:
|
||||
"""Invoke an LLM model"""
|
||||
|
||||
# 调用示例
|
||||
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={},
|
||||
)
|
||||
```
|
||||
|
||||
### 设置插件持久化数据
|
||||
|
||||
持久化存储插件数据。通过这个接口存储的数据,仅可被该插件访问。值需要自行转换为字节。
|
||||
|
||||
```python
|
||||
async def set_plugin_storage(self, key: str, value: bytes) -> None:
|
||||
"""Set a plugin storage value"""
|
||||
|
||||
# 调用示例
|
||||
await self.plugin.set_plugin_storage("key", b"value")
|
||||
```
|
||||
|
||||
### 获取插件持久化数据
|
||||
|
||||
```python
|
||||
async def get_plugin_storage(self, key: str) -> bytes:
|
||||
"""Get a plugin storage value"""
|
||||
|
||||
# 调用示例
|
||||
plugin_storage = await self.plugin.get_plugin_storage("key")
|
||||
```
|
||||
|
||||
### 获取插件所有持久化数据键
|
||||
|
||||
```python
|
||||
async def get_plugin_storage_keys(self) -> list[str]:
|
||||
"""Get all plugin storage keys"""
|
||||
|
||||
# 调用示例
|
||||
plugin_storage_keys = await self.plugin.get_plugin_storage_keys()
|
||||
```
|
||||
|
||||
### 删除插件持久化数据
|
||||
|
||||
```python
|
||||
async def delete_plugin_storage(self, key: str) -> None:
|
||||
"""Delete a plugin storage value"""
|
||||
|
||||
# 调用示例
|
||||
await self.plugin.delete_plugin_storage("key")
|
||||
```
|
||||
|
||||
### 获取工作空间持久化数据
|
||||
|
||||
通过这个接口存储的数据,可被所有插件访问。值需要自行转换为字节。
|
||||
|
||||
```python
|
||||
async def set_workspace_storage(self, key: str, value: bytes) -> None:
|
||||
"""Set a workspace storage value"""
|
||||
|
||||
# 调用示例
|
||||
await self.plugin.set_workspace_storage("key", b"value")
|
||||
```
|
||||
|
||||
### 获取工作空间持久化数据
|
||||
|
||||
```python
|
||||
async def get_workspace_storage(self, key: str) -> bytes:
|
||||
"""Get a workspace storage value"""
|
||||
|
||||
# 调用示例
|
||||
workspace_storage = await self.plugin.get_workspace_storage("key")
|
||||
```
|
||||
|
||||
### 获取工作空间所有持久化数据键
|
||||
|
||||
```python
|
||||
async def get_workspace_storage_keys(self) -> list[str]:
|
||||
"""Get all workspace storage keys"""
|
||||
|
||||
# 调用示例
|
||||
workspace_storage_keys = await self.plugin.get_workspace_storage_keys()
|
||||
```
|
||||
|
||||
### 删除工作空间持久化数据
|
||||
|
||||
```python
|
||||
async def delete_workspace_storage(self, key: str) -> None:
|
||||
"""Delete a workspace storage value"""
|
||||
|
||||
# 调用示例
|
||||
await self.plugin.delete_workspace_storage("key")
|
||||
```
|
||||
@@ -4,45 +4,41 @@
|
||||
[[toc]]
|
||||
:::
|
||||
|
||||
LangBot 支持多种消息平台,但每个消息平台的`消息实体`格式均不相同,为了屏蔽这些差异,LangBot 具有一套统一的标准。插件开发者只需要掌握并在插件中使用此页所述的消息实体,LangBot 内部的消息处理逻辑能够自动完成消息的解析和转换。
|
||||
|
||||
:::info
|
||||
LangBot 的消息平台实体是基于 [YiriMirai](https://github.com/YiriMiraiProject/YiriMirai) 的实现改造的。
|
||||
:::
|
||||
LangBot 支持多种消息平台,每个消息平台的`消息实体`格式均不相同。为了屏蔽这些差异,我们实现了一套统一的标准。插件开发者只需要掌握并在插件中使用此页所述的消息实体,LangBot 内部的消息处理逻辑能够自动完成消息的解析和转换。
|
||||
|
||||
## 消息链
|
||||
|
||||
消息平台的`消息`不同于用于 AI 交互的消息,消息平台的消息以`消息链`的形式描述,QQ 上展示的每一个独立的消息就是一条消息链,消息链中可以包含`文字`、`图片`、`@组件`等多种`消息链组件`,例如:
|
||||
|
||||

|
||||

|
||||
|
||||
这就是一个消息链,包含 一个 Plain 组件(Hello World)和 一个 Image 组件(乌萨奇)
|
||||
|
||||
消息链和消息链组件的定义位于`pkg/platform/types/messages.py`中。
|
||||
消息链和消息链组件的定义位于`langbot_plugin.api.entities.platform.message`中。
|
||||
|
||||
### 构造消息链
|
||||
|
||||
请先引入`pkg.platform.types`包,才能使用其中的消息组件。
|
||||
请先引入`langbot_plugin.api.entities.platform.message`包,才能使用其中的消息组件。
|
||||
|
||||
```python
|
||||
from pkg.platform.types import *
|
||||
from langbot_plugin.api.entities.platform.message import *
|
||||
|
||||
# 构建一个包含文字 Hello LangBot 和图片(从URL获取)的消息
|
||||
msg_chain = MessageChain([
|
||||
Plain("Hello LangBot"),
|
||||
Plain(text="Hello LangBot"),
|
||||
Image(url='https://qchatgpt.rockchin.top/langbot-logo.png')
|
||||
])
|
||||
|
||||
# 构建一个包含 @全体成员 和文字 Hello LangBot 的消息
|
||||
msg_chain = MessageChain([
|
||||
AtAll(),
|
||||
Plain("Hello LangBot")
|
||||
Plain(text="Hello LangBot")
|
||||
])
|
||||
|
||||
# 构建一个包含 @指定成员 和文字 Hello LangBot 的消息
|
||||
msg_chain = MessageChain([
|
||||
At(123456),
|
||||
Plain("Hello LangBot")
|
||||
At(target=123456),
|
||||
Plain(text="Hello LangBot")
|
||||
])
|
||||
```
|
||||
|
||||
@@ -60,29 +56,18 @@ msg_chain = MessageChain([
|
||||
- 在很多平台不受支持,不建议使用
|
||||
- `File` 文件消息
|
||||
|
||||
具体使用方式可以查看源码`pkg/platform/types/messages.py`中的定义。
|
||||
|
||||
## 消息平台事件
|
||||
|
||||
这些事件不同于 LangBot 插件事件,是消息平台系统独立的一套事件系统。这些事件是 LangBot 消息的源事件,可以从每个 Query.message_event 取得。
|
||||
|
||||
```python
|
||||
# 例如在插件中
|
||||
@handler(GroupMessageReceived)
|
||||
async def _(self, ctx: EventContext):
|
||||
# 取得消息平台事件
|
||||
message_event = ctx.query.message_event
|
||||
```
|
||||
|
||||
消息平台事件定义位于`pkg/platform/types/events.py`中。
|
||||
创建组件时,须使用具名参数传入。具体使用方式可以查看源码`langbot_plugin.api.entities.platform.message`中的定义。
|
||||
|
||||
## 其他实体
|
||||
|
||||
除了上述实体之外,还有`好友 Friend`和`群 Group`等实体,这些实体的定义位于`pkg/platform/types/entities.py`中。
|
||||
他们一般被包含于上述事件对象中,可以从中取得一些未被包含在插件事件中的信息。
|
||||
除了上述实体之外,还有`好友 Friend`和`群 Group`等实体,这些实体的定义位于`langbot_plugin.api.entities.platform.entities`中。
|
||||
|
||||
## 访问消息平台底层 API
|
||||
|
||||
:::warning 注意
|
||||
4.x 暂不适用。
|
||||
:::
|
||||
|
||||
为了抹平各个平台的差异,LangBot 在消息平台API之上,提供了一个抽象层,即上方所述的内容,以及`pkg/platform/sources`目录下的各个平台适配器。
|
||||
但由于各个平台的差异较大,抽象层内的实体和 API 封装并不完整,如果您的插件需要访问特定平台的功能,可以按照以下说明访问底层 API。
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
# 流水线事件
|
||||
|
||||
:::info 目录
|
||||
[[toc]]
|
||||
:::
|
||||
|
||||
LangBot 插件可注册并处理流水线事件。使用方法见[组件:事件监听器](/zh/plugin/dev/components/event-listener)。
|
||||
|
||||
## *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"""
|
||||
|
||||
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]
|
||||
"""群号"""
|
||||
|
||||
sender_id: typing.Union[int, str]
|
||||
"""发送者ID"""
|
||||
|
||||
message_chain: platform_message.MessageChain = pydantic.Field(
|
||||
serialization_alias="message_chain"
|
||||
)
|
||||
```
|
||||
|
||||
## *NormalMessageReceived
|
||||
|
||||
接收到群聊或私聊消息,并判断为需要由 LLM 处理的消息(非命令消息)时触发。
|
||||
|
||||
```python
|
||||
class PersonNormalMessageReceived(BaseEventModel):
|
||||
"""判断为应该处理的私聊普通消息时触发"""
|
||||
|
||||
event_name: str = "PersonNormalMessageReceived"
|
||||
|
||||
launcher_type: str
|
||||
|
||||
launcher_id: typing.Union[int, str]
|
||||
|
||||
sender_id: typing.Union[int, str]
|
||||
|
||||
text_message: str
|
||||
"""消息文本"""
|
||||
|
||||
alter: typing.Optional[str] = None
|
||||
"""修改后的消息文本"""
|
||||
|
||||
reply: typing.Optional[list] = None
|
||||
"""回复消息组件列表"""
|
||||
|
||||
class GroupNormalMessageReceived(BaseEventModel):
|
||||
"""判断为应该处理的群聊普通消息时触发"""
|
||||
|
||||
event_name: str = "GroupNormalMessageReceived"
|
||||
|
||||
launcher_type: str
|
||||
|
||||
launcher_id: typing.Union[int, str]
|
||||
|
||||
sender_id: typing.Union[int, str]
|
||||
|
||||
text_message: str
|
||||
"""消息文本"""
|
||||
|
||||
alter: typing.Optional[str] = None
|
||||
"""修改后的消息文本"""
|
||||
|
||||
reply: typing.Optional[list] = None
|
||||
"""回复消息组件列表"""
|
||||
```
|
||||
|
||||
## *CommandSent
|
||||
|
||||
:::warning 注意
|
||||
不再推荐使用,请改为使用[组件:命令](/zh/plugin/dev/components/command)。
|
||||
:::
|
||||
|
||||
接收到群聊或私聊命令时触发。
|
||||
|
||||
```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
|
||||
"""是否为管理员"""
|
||||
|
||||
alter: typing.Optional[str] = None
|
||||
"""修改后的完整命令文本"""
|
||||
|
||||
reply: typing.Optional[list] = None
|
||||
"""回复消息组件列表"""
|
||||
|
||||
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
|
||||
"""是否为管理员"""
|
||||
|
||||
alter: typing.Optional[str] = None
|
||||
"""修改后的完整命令文本"""
|
||||
|
||||
reply: typing.Optional[list] = None
|
||||
"""回复消息组件列表"""
|
||||
```
|
||||
|
||||
## 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: typing.Optional[list] = None
|
||||
"""回复消息组件列表"""
|
||||
```
|
||||
|
||||
## PromptPreProcessing
|
||||
|
||||
在构建 LLM 响应的上下文(prompt)时触发。
|
||||
|
||||
```python
|
||||
class PromptPreProcessing(BaseEventModel):
|
||||
"""会话中的Prompt预处理时触发"""
|
||||
|
||||
event_name: str = "PromptPreProcessing"
|
||||
|
||||
session_name: str
|
||||
|
||||
default_prompt: list[provider_message.Message]
|
||||
"""此对话的情景预设,可修改"""
|
||||
|
||||
prompt: list[provider_message.Message]
|
||||
"""此对话现有消息记录,可修改"""
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
# 插件技术细节
|
||||
|
||||
## 架构
|
||||
|
||||
<img width="600" src="/assets/image/zh/plugin/dev/plugin_system_arch.png" />
|
||||
|
||||
在 4.0 版本中,我们引入了一种高安全性、高灵活性的生产级插件系统,并为插件开发者提供了丰富的 API 和易用的配套工具。
|
||||
|
||||
Plugin Runtime (插件运行时),用于管理插件的生命周期,协调 LangBot 与插件之间的交互。有两种运行模式:`stdio` 和 `websocket`。当 LangBot 是由用户直接启动(未运行在容器内时),会使用 `stdio` 模式,这种场景多为个人用户或轻量级环境。当 LangBot 运行在容器内时,会使用 `websocket` 模式,此场景专为生产级环境设计。
|
||||
|
||||
Plugin Runtime 会自动启动各个已安装的插件,通过 stdio 交互。在插件开发场景中,开发者可以使用 `lbp` 命令行工具启动插件并通过 WebSocket 连接到已启动的 Runtime 进行调试。
|
||||
@@ -0,0 +1,157 @@
|
||||
# 完善插件信息
|
||||
|
||||
## 修改清单文件
|
||||
|
||||
插件目录下的`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' # 插件仓库地址
|
||||
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 # 请勿修改
|
||||
```
|
||||
|
||||
插件多语言遵循 [RFC 4646](https://datatracker.ietf.org/doc/html/rfc4646) 标准,目前支持的语言有:
|
||||
|
||||
- `en_US` 英文
|
||||
- `zh_Hans` 简体中文
|
||||
- `zh_Hant` 繁体中文
|
||||
- `ja_JP` 日语
|
||||
|
||||
## 插件配置项格式
|
||||
|
||||
在 `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 等
|
||||
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 # 配置项是否必填,可选。
|
||||
- name: mode
|
||||
type: select
|
||||
...
|
||||
```
|
||||
|
||||
配置项支持的各个类型及字段如下:
|
||||
|
||||
### type: string
|
||||
|
||||
字符串。
|
||||
|
||||
```yaml
|
||||
- name: api_key
|
||||
type: string
|
||||
...
|
||||
```
|
||||
|
||||
### 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` 选项,表示下拉框的选项。
|
||||
|
||||
```yaml
|
||||
- name: mode
|
||||
type: select
|
||||
options: # 下拉框选项,支持多语言。
|
||||
- name: mode1 # 值
|
||||
label: # 显示名称,支持多语言。
|
||||
en_US: Mode 1
|
||||
zh_Hans: 模式 1
|
||||
- name: mode2
|
||||
label: # 显示名称,支持多语言。
|
||||
en_US: Mode 2
|
||||
zh_Hans: 模式 2
|
||||
...
|
||||
```
|
||||
|
||||
### type: prompt-editor
|
||||
|
||||
提示词编辑器。会展示一个流水线配置页面的提示词编辑器,最终结果表示为 OpenAI 的 `messages` 格式。
|
||||
|
||||
```yaml
|
||||
- name: prompt
|
||||
type: prompt-editor
|
||||
...
|
||||
```
|
||||
|
||||
### type: llm-model-selector
|
||||
|
||||
LLM 模型选择器。会展示一个 LLM 模型选择器,可选择已配置的 LLM 模型,最终结果表示为 LLM 模型 uuid。
|
||||
|
||||
```yaml
|
||||
- name: model
|
||||
type: llm-model-selector
|
||||
...
|
||||
```
|
||||
|
||||
## 接下来做什么
|
||||
|
||||
该教程将指引您逐步完善插件功能。
|
||||
|
||||
- 添加组件:插件组件是插件的核心功能单元,您可以根据需求[添加组件](/zh/plugin/dev/components/add)。
|
||||
@@ -0,0 +1,72 @@
|
||||
# 添加组件
|
||||
|
||||
插件由一种或多种组件构成,为 LangBot 提供不同的功能。目前支持的组件类型有:
|
||||
|
||||
- 事件监听器(EventListener):监听流水线执行期间的事件,对上下文或流水线进行修改。
|
||||
- 命令(Command):由用户通过`!`(或其他已设置的前缀)开头的命令消息触发。
|
||||
- 工具(Tool):供 LangBot 内置的 Local Agent 在执行期间由 LLM 调用。
|
||||
|
||||
## 添加组件
|
||||
|
||||
在插件目录下执行命令:
|
||||
|
||||
```bash
|
||||
lbp comp <component_type>
|
||||
```
|
||||
|
||||
例如,添加一个事件监听器:
|
||||
|
||||
```bash
|
||||
lbp comp EventListener
|
||||
```
|
||||
|
||||
请根据提示输入组件的配置(若有)。
|
||||
|
||||
```bash
|
||||
➜ HelloPlugin > lbp comp EventListener
|
||||
Generating component EventListener...
|
||||
Component EventListener generated successfully.
|
||||
组件 EventListener 生成成功。
|
||||
➜ HelloPlugin >
|
||||
```
|
||||
|
||||
:::info
|
||||
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/
|
||||
...
|
||||
```
|
||||
如需删除组件,可自行删除对应信息。
|
||||
:::
|
||||
|
||||
|
||||
## 组件类型
|
||||
|
||||
各个组件的详细使用方式请参考:
|
||||
|
||||
- [EventListener](./event-listener.md)
|
||||
- [Command](./command.md)
|
||||
- [Tool](./tool.md)
|
||||
@@ -0,0 +1,211 @@
|
||||
# 组件:命令
|
||||
|
||||
命令组件由用户通过`!`(或其他已设置的前缀)开头的命令消息触发。如下是命令`!help`的触发示例:
|
||||
|
||||
<img width="600" src="/assets/image/zh/plugin/dev/components/command_use.png" />
|
||||
|
||||
## 添加命令组件
|
||||
|
||||
单个插件中能添加任意数量的命令,请在插件目录执行命令`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
|
||||
...
|
||||
```
|
||||
|
||||
## 清单文件:命令组件
|
||||
|
||||
```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/<命令名称>.py),您需要在`Info`类的`initialize`方法中注册并实现子命令的处理逻辑。
|
||||
|
||||
```python
|
||||
# Auto generated by LangBot Plugin SDK.
|
||||
# Please refer to https://docs.langbot.app/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}",
|
||||
)
|
||||
```
|
||||
|
||||
该代码中,`send`函数通过装饰器`@self.subcommand`注册为子命令,并在其中打印了命令的上下文(ExecuteContext)信息并拼接成一条回复消息。
|
||||
|
||||
### 子命令注册
|
||||
|
||||
`name` 为子命令名称,留空表示处理主命令。不为空则匹配第二个参数为命令,例如:
|
||||
|
||||
- `!info` 匹配 name="" 的子命令
|
||||
- `!info field` 匹配 name="field" 的子命令
|
||||
- `!info field value` 匹配 name="field" 的子命令,`value` 为子命令参数
|
||||
|
||||
子命令函数中,可通过 `context` 变量读取到命令参数。
|
||||
|
||||
上述命令效果如图:
|
||||
|
||||
<img width="600" src="/assets/image/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)目前支持返回文本(text)和图片(image_url,图片链接)、错误(error)。
|
||||
|
||||
```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)
|
||||
|
||||
## 接下来做什么
|
||||
|
||||
您已经了解了命令注册和命令执行的基本信息,接下来可以:
|
||||
|
||||
- 查看[插件通用 API](/zh/plugin/dev/apis/common)
|
||||
@@ -0,0 +1,101 @@
|
||||
# 组件:事件监听器
|
||||
|
||||
LangBot 的流水线在运行期间会产生一些事件,供插件 Hook 并处理。每个插件中只允许添加一个事件监听器(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
|
||||
...
|
||||
```
|
||||
|
||||
## 清单文件:事件监听器
|
||||
|
||||
由于每个插件仅允许添加一个事件监听器,故清单文件中不需要修改任何内容。
|
||||
|
||||
## 插件处理
|
||||
|
||||
默认会生成如下代码(components/event_listener/default.py),您需要在`DefaultEventListener`类的`initialize`方法中注册并实现事件的处理逻辑。
|
||||
|
||||
```python
|
||||
# Auto generated by LangBot Plugin SDK.
|
||||
# Please refer to https://docs.langbot.app/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]
|
||||
"""此次事件的对象,具体类型为handler注册时指定监听的类型,可查看events.py中的定义"""
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## 事件注册
|
||||
|
||||
事件注册通过装饰器`@self.handler`实现,装饰器参数为事件类型。可监听的事件均在`langbot_plugin.api.entities.builtin.events`中定义。
|
||||
|
||||
## 接下来做什么
|
||||
|
||||
您已经了解了事件监听器的基本信息,接下来可以:
|
||||
|
||||
- 在文档[流水线事件](/zh/plugin/dev/apis/pipeline-events)中查看`可监听的事件列表`和`事件上下文 API`
|
||||
- 查看[插件通用 API](/zh/plugin/dev/apis/common)
|
||||
@@ -0,0 +1,172 @@
|
||||
# 组件:工具
|
||||
|
||||
LangBot 内置的 Local Agent 在执行期间会根据场景调用工具以与外界交互,目前支持通过插件和 MCP 两种方式添加工具。
|
||||
|
||||
## 添加工具组件
|
||||
|
||||
单个插件中能添加任意数量的工具,请在插件目录执行命令`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
|
||||
...
|
||||
```
|
||||
|
||||
## 清单文件:工具
|
||||
|
||||
```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/<工具名称>.py),您需要在`GetWeatherAlerts`类的`call`方法中实现此工具的调用和返回逻辑。
|
||||
|
||||
```python
|
||||
# Auto generated by LangBot Plugin SDK.
|
||||
# Please refer to https://docs.langbot.app/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
|
||||
|
||||
|
||||
class GetWeatherAlerts(Tool):
|
||||
|
||||
async def call(self, params: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Fill your tool code here"""
|
||||
return {}
|
||||
```
|
||||
|
||||
在 `call` 方法中,实现该工具:
|
||||
|
||||
:::info
|
||||
天气获取工具用例来自 MCP 的 [Server 编写示例](https://modelcontextprotocol.io/quickstart/server)。
|
||||
:::
|
||||
|
||||
```python
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from langbot_plugin.api.definition.components.tool.tool import Tool
|
||||
import httpx
|
||||
|
||||
# Constants
|
||||
NWS_API_BASE = "https://api.weather.gov"
|
||||
USER_AGENT = "weather-app/1.0"
|
||||
|
||||
|
||||
async def make_nws_request(url: str) -> dict[str, Any] | None:
|
||||
"""Make a request to the NWS API with proper error handling."""
|
||||
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:
|
||||
"""Format an alert feature into a readable string."""
|
||||
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]) -> dict[str, Any]:
|
||||
"""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="/assets/image/zh/plugin/dev/components/tool_use_in_chat.png" />
|
||||
|
||||
## 接下来做什么
|
||||
|
||||
您已经了解了工具的基本用法,接下来可以:
|
||||
|
||||
- 查看[插件通用 API](/zh/plugin/dev/apis/common)
|
||||
@@ -1,108 +0,0 @@
|
||||
# 组件扩展
|
||||
|
||||
::: warning
|
||||
2025年3月~4月:我们正在进行 4.0 版本重构,组件定义变化较大,现阶段不建议使用组件扩展。请优先考虑使用基于事件的插件接口。
|
||||
:::
|
||||
|
||||
> 请先阅读[插件介绍页](/zh/plugin/plugin-intro)
|
||||
|
||||
:::info 目录
|
||||
[[toc]]
|
||||
:::
|
||||
|
||||
普通的以 `BasePlugin` 子类包装的插件只能监听预定事件,而组件扩展则可以替换整个组件,实现更强大的功能扩展。
|
||||
|
||||
- 组件扩展不需要包装在 `BasePlugin` 子类中,可以放在插件目录下任意一个模块中
|
||||
- 扩展的组件不受插件开关控制
|
||||
- 扩展的组件通常由配置文件中的选项选用
|
||||
|
||||
## 可扩展的组件
|
||||
|
||||

|
||||
|
||||
扩展一个组件的方法,新建一个类,继承于组件的基类,然后在类上使用对应的装饰器,再根据使用方式来配置使用该实现的组件。
|
||||
|
||||
大部分组件中包含了`ap: pkg.core.app.Application`对象,可以用这个对象来与整个程序的各个部分进行交互。
|
||||
大部分组件支持实现`async def initialize(self)`方法,用于在组件加载时进行异步初始化。
|
||||
`ap`对象中包含了各个配置文件的管理器对象,可以用于访问各个配置文件。
|
||||
|
||||
### 命令
|
||||
|
||||
新增一个命令。
|
||||
|
||||
- 基类:`pkg.command.operator.CommandOperator`
|
||||
- 装饰器:`pkg.command.operator.operator_class(name: str, help: str="", usage: str=None, alias: list[str]=[], privilege: int=1, parent_class: typing.Type[CommandOperator] = None)`
|
||||
- 参考实现:`pkg.command.operators/`
|
||||
- 使用方式:加载成功后,执行命令`!cmd`可查看所有命令和命令帮助信息。
|
||||
|
||||
### 内容过滤器
|
||||
|
||||
新增一个在处理消息前后判断是否要继续的内容处理器。
|
||||
|
||||
- 基类:`pkg.pipeline.cntfilter.filter.ContentFilter`
|
||||
- 装饰器:`pkg.pipeline.cntfilter.filter.filter_class(name: str)`
|
||||
- 参考实现:`pkg.pipeline.cntfilter.filters/`
|
||||
- 使用方式:加载成功后,每次都会在过滤器实现声明的检查阶段调用内容过滤器的`process`方法。
|
||||
|
||||
### 长消息处理策略
|
||||
|
||||
新增一个长消息处理策略。类似:文本转合并转发消息组件、文本转图片。
|
||||
|
||||
- 基类:`pkg.pipeline.longtext.strategy.LongTextStrategy`
|
||||
- 装饰器:`pkg.pipeline.longtext.strategy.strategy_class(name: str)`
|
||||
- 参考实现:`pkg.pipeline.longtext.strategies/`
|
||||
- 使用方式:需要在 `data/config/platform.json` 的 `long-text-strategy` 字段中将 `strategy` 设置为新增的策略名称。
|
||||
|
||||
### 会话限速算法
|
||||
|
||||
新增一个会话限速算法。会话请求在进入和退出时均会调用限速算法实现类的方法,实现类可以控制等待或丢弃请求。
|
||||
|
||||
- 基类:`pkg.pipeline.ratelimit.algo.RateLimitAlgo`
|
||||
- 装饰器:`pkg.pipeline.ratelimit.algo.algo_class(name: str)`
|
||||
- 参考实现:`pkg.pipeline.ratelimit.algos/`
|
||||
- 使用方式:需要在 `data/config/pipeline.json` 的 `rate-limit` 字段中将 `algo` 设置为新增的算法名称。
|
||||
|
||||
### LLM 接口请求器
|
||||
|
||||
新增一个 LLM 接口请求器,例如:接入ChatGLM接口等。
|
||||
|
||||
- 基类:`pkg.provider.modelmgr.api.LLMAPIRequester`
|
||||
- 装饰器:`pkg.provider.modelmgr.api.api_requester_class(name: str)`
|
||||
- 参考实现:`pkg.provider.modelmgr.apis/`
|
||||
- 使用方式:需要在`data/metadata/llm-models.json`中添加模型,将模型的`requester`字段设置为新增的请求器名称。具体请查看元数据配置板块。
|
||||
|
||||
### Prompt 加载器
|
||||
|
||||
新增一个 Prompt 加载器。
|
||||
|
||||
- 基类:`pkg.provider.sysprompt.loader.PromptLoader`
|
||||
- 装饰器:`pkg.provider.sysprompt.loader.loader_class(name: str)`
|
||||
- 参考实现:`pkg.provider.sysprompt.loaders/`
|
||||
- 使用方式:需要在`data/config/provider.json`将`prompt-mode`设置为新增的加载器名称。
|
||||
|
||||
### 消息平台适配器
|
||||
|
||||
新增一个消息平台适配器,例如:接入 WhatsApp、Discord 等。
|
||||
|
||||
- 基类:`pkg.platform.adapter.MessageSourceAdapter`
|
||||
- 装饰器:`pkg.platform.adapter.adapter_class(name: str)`
|
||||
- 参考实现:`pkg.platform.adapters/`
|
||||
- 使用方式:`data/config/platform.json`的`platform-adapters`中各个配置信息,将在初始化时自动根据`adapter`名称查找对应的适配器实现,并将配置传递给适配器以进行初始化。消息平台适配器实现复杂,建议多参考现有的实现。
|
||||
|
||||
### 对话历史消息截断器
|
||||
|
||||
新增一个对话历史消息截断器,在发送到模型之前对历史消息进行截断。
|
||||
|
||||
- 基类:`pkg.pipeline.msgtrun.truncator.Truncator`
|
||||
- 装饰器:`pkg.pipeline.msgtrun.truncator.truncator_class(name: str)`
|
||||
- 参考实现:`pkg.pipeline.msgtrun.truncators/`
|
||||
- 使用方式:需要在`data/config/pipeline.json`的`msg-truncate`字段中设置需要使用的截断器`method`为新增的截断器名称。
|
||||
|
||||
### Chat 请求运行器
|
||||
|
||||
新增一个聊天消息运行器,默认的是 local-agent,提供了基本的 Prompt 组合、Agent 编排、多轮请求等功能。如果需要接入知识库或者本地 RAG,请在这里实现。
|
||||
|
||||
- 基类:`pkg.provider.runner.RequesterRunner`
|
||||
- 装饰器:`pkg.provider.runner.runner_class(name: str)`
|
||||
- 参考实现:`pkg.provider.runners/`
|
||||
- 使用方式:需要在`data/config/provider.json`的`runner`字段中设置需要使用的运行器`runner`为新增的运行器名称。
|
||||
@@ -0,0 +1,27 @@
|
||||
# 迁移指南
|
||||
|
||||
本教程高亮了某些注意事项,请各位插件开发者在已阅读现版本插件教程后,再以此文档作为参考进行插件迁移。
|
||||
|
||||
## 从 3.x 支持的异步插件迁移
|
||||
|
||||
:::info
|
||||
插件特征:
|
||||
- 主要时间段:2024.2 ~ 2025.8
|
||||
- 注册方式:`main.py` 中的插件类中使用 `@handler` 装饰器注册事件监听器、使用 `@llm_func` 装饰器注册内容函数(工具);处理函数均为异步函数(async def)
|
||||
:::
|
||||
|
||||
- 插件和组件注册方式:已由以往的单一插件类中的装饰器注册方式,改为了清单文件注册方式,参阅[清单文件](/zh/plugin/dev/basic-info);并将各个不同类型的组件独立出来,详细请见[添加组件](/zh/plugin/dev/components/add)。
|
||||
- 消息链实体:现已重构,每个消息链元素的构造方法现在需要显式传入**具名参数**,参阅[消息平台实体](/zh/plugin/dev/apis/messages)。
|
||||
- 事件监听器:提供向后兼容性,但不再于上下文中提供`query: Query`对象,详细请参阅[流水线事件](/zh/plugin/dev/apis/pipeline-events)。
|
||||
- 命令组件:现已重构,请参阅[命令组件](/zh/plugin/dev/components/command)。
|
||||
|
||||
## 从 2.x 和 3.x 支持的同步插件迁移
|
||||
|
||||
:::info
|
||||
插件特征:
|
||||
- 主要时间段:2023.1 ~ 2024.2
|
||||
- 注册方式:`main.py` 中的插件类中使用 `@on` 装饰器注册事件监听器、使用 `@func` 装饰器注册内容函数(工具);处理函数均为同步函数(def)
|
||||
:::
|
||||
|
||||
- 同步改异步:插件运行时现已改为全异步(基于asyncio),请将插件所有涉及 IO 操作的代码改为异步写法。
|
||||
- 详细内容请参阅上述 3.x 异步插件迁移指南。
|
||||
@@ -0,0 +1,65 @@
|
||||
# 发布插件
|
||||
|
||||
通过 CLI 将插件发布至 [LangBot 插件市场](https://space.langbot.app/market)。
|
||||
|
||||
## 在 LangBot Space 创建账号
|
||||
|
||||
LangBot Space 是 LangBot 提供中心化服务平台。请访问[LangBot Space](https://space.langbot.app),点击右上角使用 GitHub 登录并创建账号。
|
||||
|
||||
<img width="600" src="/assets/image/zh/plugin/dev/login_space.png" />
|
||||
|
||||
这将跳转到 GitHub 授权页面,请根据提示完成授权。
|
||||
|
||||
## 使用 CLI 登录 LangBot Space
|
||||
|
||||
在任意目录执行命令`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="/assets/image/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="/assets/image/zh/plugin/dev/plugin_submission.png" />
|
||||
|
||||
有任何问题,您可以发送邮件到`[email protected]`或通过[社区](/zh/insight/community)联系我们。
|
||||
+133
-312
@@ -1,312 +1,133 @@
|
||||
# 插件开发教程
|
||||
|
||||
:::warning
|
||||
4.0 插件机制正在重构,请先查看 [3.x 插件开发教程](https://v3.docs.langbot.app/plugin/dev/tutor.html)
|
||||
:::
|
||||
|
||||
> 请先阅读[插件介绍页](/zh/plugin/plugin-intro)
|
||||
> 建议先阅读本项目源码,了解项目架构
|
||||
|
||||
> 问题、需求请到仓库issue发起
|
||||
> **提问前请先靠自己尝试**
|
||||
|
||||
:::info 目录
|
||||
[[toc]]
|
||||
:::
|
||||
|
||||
## 💬 简介
|
||||
|
||||
本页讲解普通插件开发步骤,插件内部有两种方法:事件监听器、内容函数。
|
||||
事件监听器能在某些事件触发时进行响应,更改行为。内容函数可供大语言模型调用,具体请查看插件介绍页。
|
||||
|
||||
## 💻 快速开始
|
||||
|
||||
使用[HelloPlugin](https://github.com/langbot-app/HelloPlugin)作为模板生成插件代码仓库,然后将仓库代码克隆到`plugins`目录下。
|
||||
|
||||
修改插件的 README.md 文件中需要修改的部分。
|
||||
|
||||
编辑`main.py`输入以下内容:
|
||||
|
||||
```Python
|
||||
from pkg.plugin.context import register, handler, llm_func, BasePlugin, APIHost, EventContext
|
||||
from pkg.plugin.events import * # 导入事件类
|
||||
|
||||
|
||||
"""
|
||||
在收到私聊或群聊消息"hello"时,回复"hello, <发送者id>!"或"hello, everyone!"
|
||||
"""
|
||||
|
||||
|
||||
class HelloPlugin(BasePlugin):
|
||||
|
||||
# 插件加载时触发
|
||||
def __init__(self, host: APIHost):
|
||||
pass
|
||||
|
||||
# 异步初始化
|
||||
async def initialize(self):
|
||||
pass
|
||||
|
||||
# 当收到个人消息时触发
|
||||
@handler(PersonNormalMessageReceived)
|
||||
async def person_normal_message_received(self, ctx: EventContext):
|
||||
msg = ctx.event.text_message # 这里的 event 即为 PersonNormalMessageReceived 的对象
|
||||
if msg == "hello": # 如果消息为hello
|
||||
|
||||
# 输出调试信息
|
||||
self.ap.logger.debug("hello, {}".format(ctx.event.sender_id))
|
||||
|
||||
# 回复消息 "hello, <发送者id>!"
|
||||
ctx.add_return("reply", ["hello, {}!".format(ctx.event.sender_id)])
|
||||
|
||||
# 阻止该事件默认行为(向接口获取回复)
|
||||
ctx.prevent_default()
|
||||
|
||||
# 当收到群消息时触发
|
||||
@handler(GroupNormalMessageReceived)
|
||||
async def group_normal_message_received(self, ctx: EventContext):
|
||||
msg = ctx.event.text_message # 这里的 event 即为 GroupNormalMessageReceived 的对象
|
||||
if msg == "hello": # 如果消息为hello
|
||||
|
||||
# 输出调试信息
|
||||
self.ap.logger.debug("hello, {}".format(ctx.event.sender_id))
|
||||
|
||||
# 回复消息 "hello, everyone!"
|
||||
ctx.add_return("reply", ["hello, everyone!"])
|
||||
|
||||
# 阻止该事件默认行为(向接口获取回复)
|
||||
ctx.prevent_default()
|
||||
|
||||
# 插件卸载时触发
|
||||
def __del__(self):
|
||||
pass
|
||||
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
解读此插件程序
|
||||
|
||||
- `import pkg.plugin.context`导入`register(用于注册插件类)`, `handler(用于注册事件监听器)`, `llm_func(用于注册内容函数)`,`BasePlugin(插件基类)`, `APIHost(API宿主)`, `EventContext(事件上下文类)`等内容
|
||||
- `import pkg.plugin.events`导入所有支持的事件类
|
||||
- 声明类`HelloPlugin`继承于`BasePlugin`,此类可以随意命名,插件名称只与`register`调用时的参数有关
|
||||
- 声明此类的`__init__`方法,此方法是可选的,其中的代码将在主程序启动时加载插件的时候被执行
|
||||
- 插件类中还支持添加一个异步方法`async def initialize(self)`用于异步初始化
|
||||
- `@handler`将方法`person_normal_message_received`标记为一个事件监听器,处理`PersonNormalMessageReceived`(收到私聊消息并在获取OpenAI回复前触发)事件,此方法可以随意命名,绑定的事件只与`handler`中的参数有关,更多支持的事件可到`pkg.plugin.events`文件中查看或查看下方`API`节
|
||||
- 通过`self.ap.logger`日志器输出调试信息,插件类父类中的`ap`对象是整个程序的上下文对象,可以通过这个对象访问程序内所有对象
|
||||
- 事件监听器方法获得的第二个参数`ctx`即为本次事件的上下文,其中的`event`为本次事件的事件对象,可以从中取出相关参数,具体事件可以取得的参数请查看`pkg.plugin.events`文件中的每个事件类的注释
|
||||
- 事件监听器方法内部从参数中取出`text_message`参数,判断是否为`hello`,如果是就将返回值`reply`设置为`["hello, {}!".format(ctx.event.sender_id)]`,接下来调用`ctx.prevent_default()`方法,阻止原程序默认行为
|
||||
- 每个事件支持的返回值请查看`pkg.plugin.events`中的每个事件的注释
|
||||
- 用相似的程序注册`GroupNormalMessageReceived`事件处理群消息
|
||||
|
||||
编写完毕保存后,重新启动主程序,前往 WebUI 插件页即可看到插件
|
||||
:::
|
||||
|
||||
此插件将实现:私聊收到`hello`消息时回复`hello, <发送者QQ号>!`,群聊收到`hello`消息时回复`hello, everyone!`
|
||||
|
||||
### 编写清单文件
|
||||
|
||||
从 LangBot 4.0 开始,将使用清单文件来注册插件。
|
||||
请在 main.py 同目录下新建文件 `manifest.yaml`。如果已经存在了,请按照下方格式修改。
|
||||
|
||||
```yaml
|
||||
apiVersion: v1 # 不要改动
|
||||
kind: Plugin # 不要改动
|
||||
metadata:
|
||||
# author 和 name 唯一确定一个插件
|
||||
author: langbot # 插件作者,修改为你的名称
|
||||
name: Hello # 插件名称,修改为你的插件名称
|
||||
repository: 'https://github.com/langbot-app/HelloPlugin' # 插件仓库地址,修改为你的插件 GitHub 仓库地址
|
||||
version: 0.1.0 # 插件版本,修改为你的插件版本
|
||||
description: # 插件简介,修改为你的插件简介,支持多语言。语言代码采用 RFC 4646 标准。
|
||||
en_US: Plugin for sending hello
|
||||
zh_Hans: 示例插件
|
||||
label: # 插件显示名称,支持多语言。在 WebUI 上会显示对应语言的 label。语言代码采用 RFC 4646 标准。
|
||||
en_US: Hello
|
||||
zh_Hans: Hello
|
||||
spec:
|
||||
# 插件配置(可选),可配置多项
|
||||
config:
|
||||
- name: github_token # 配置项名称
|
||||
label: # 配置项显示名称,支持多语言。语言代码采用 RFC 4646 标准。
|
||||
en_US: Github Token
|
||||
zh_Hans: Github Token
|
||||
description: # 配置项描述,支持多语言。可选。
|
||||
en_US: Image downloading requires a Github token
|
||||
zh_Hans: 如果不填的话,图片可能会下载失败
|
||||
type: string # 配置项类型,支持 string, integer, float, boolean 等
|
||||
default: '' # 配置项默认值
|
||||
required: false # 配置项是否必填
|
||||
execution:
|
||||
python:
|
||||
path: main.py # 插件主程序路径,必须与上方插件入口代码的文件名相同
|
||||
attr: HelloPlugin # 插件类名,必须与上方代码中声明的类名相同
|
||||
```
|
||||
|
||||
:::info
|
||||
配置项支持类型:
|
||||
|
||||
- string: 字符串
|
||||
- integer: 整数
|
||||
- float: 浮点数
|
||||
- boolean: 布尔值
|
||||
- array[string]: 字符串数组
|
||||
- select: 下拉框,可从多个选项中选择一个
|
||||
- 需要配置 options 选项,表示下拉框的选项
|
||||
|
||||
```yaml
|
||||
- name: mode
|
||||
label:
|
||||
en_US: Mode
|
||||
zh_Hans: 模式
|
||||
type: select
|
||||
options:
|
||||
- name: mode1
|
||||
label:
|
||||
en_US: Mode 1
|
||||
zh_Hans: 模式 1
|
||||
- name: mode2
|
||||
label:
|
||||
en_US: Mode 2
|
||||
zh_Hans: 模式 2
|
||||
```
|
||||
- prompt-editor: 提示词编辑器。
|
||||
- llm-model-selector: LLM模型选择器。
|
||||
|
||||
用户通过 WebUI 修改配置项后,插件可以在 self.config 中获取到配置项的值。
|
||||
:::
|
||||
|
||||
|
||||
## ❗ 规范(重要)
|
||||
|
||||
- 请每个插件独立一个目录以便管理,建议在Github上创建一个仓库储存单个插件,以便获取和更新
|
||||
- 命名规范(重要):
|
||||
- 插件作者:使用英文,大小写均可,如`RockChinQ`
|
||||
- 插件名:使用`大驼峰命名法`,如`Hello`、`ExamplePlugin`、`ChineseCommands`等
|
||||
- 插件仓库:请使用大驼峰命名法,建议直接与插件名相同,如`HelloPlugin`
|
||||
- 插件简介:建议携带`LangBot 插件`字样
|
||||
- 一个目录内可以存放多个Python程序文件,以独立出插件的各个功能,便于开发者管理,但不建议在一个目录内注册多个插件
|
||||
- 插件需要的依赖库请在插件目录下的`requirements.txt`中指定,程序从储存库获取此插件时将自动安装依赖
|
||||
|
||||
## 🪝 内容函数
|
||||
|
||||
通过[GPT的Function Calling能力](https://platform.openai.com/docs/guides/gpt/function-calling)实现的`内容函数`,这是一种嵌入对话中,由GPT自动调用的函数。
|
||||
|
||||
> 您的插件不一定必须包含内容函数,请先查看内容函数页了解此功能
|
||||
|
||||
### 内容函数编写步骤
|
||||
|
||||
1️⃣ 请先按照上方步骤编写您的插件基础结构,现在请删除(当然你也可以不删,只是为了简洁)上述插件内容的诸个由`@handler`装饰的类函数
|
||||
|
||||
<details>
|
||||
<summary>删除后的类结构</summary>
|
||||
|
||||
```python
|
||||
|
||||
class HelloPlugin(Plugin):
|
||||
|
||||
# 插件加载时触发
|
||||
def __init__(self, plugin_host: APIHost):
|
||||
pass
|
||||
|
||||
# 插件卸载时触发
|
||||
def __del__(self):
|
||||
pass
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
2️⃣ 现在我们将以下函数添加到刚刚删除的函数的位置(作为类方法)
|
||||
|
||||
```Python
|
||||
|
||||
# 要添加的函数
|
||||
@llm_func(name="access_the_web") # 设置函数名称
|
||||
async def access_web(self, query, url: str):
|
||||
"""Call this function to search about the question before you answer any questions.
|
||||
- Do not search through baidu.com at any time.
|
||||
- If you need to search somthing, visit https://www.google.com/search?q=xxx.
|
||||
- If user ask you to open a url (start with http:// or https://), visit it directly.
|
||||
- Summary the plain content result by yourself, DO NOT directly output anything in the result you got.
|
||||
|
||||
Args:
|
||||
url(str): url to visit
|
||||
|
||||
Returns:
|
||||
str: plain text content of the web page
|
||||
"""
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
# 你需要先使用
|
||||
# pip install beautifulsoup4
|
||||
# 安装依赖
|
||||
|
||||
r = requests.get(
|
||||
url,
|
||||
timeout=10,
|
||||
headers={
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183"
|
||||
}
|
||||
)
|
||||
soup = BeautifulSoup(r.text, 'html.parser')
|
||||
|
||||
s = soup.get_text()
|
||||
|
||||
# 删除多余的空行或仅有\t和空格的行
|
||||
s = re.sub(r'\n\s*\n', '\n', s)
|
||||
|
||||
if len(s) >= 512: # 截取获取到的网页纯文本内容的前512个字
|
||||
return s[:512]
|
||||
|
||||
return s
|
||||
|
||||
```
|
||||
|
||||
#### 请注意:
|
||||
|
||||
- 函数的注释必须严格按照要求的格式进行书写,具体格式请查看[此文档](https://github.com/RockChinQ/CallingGPT/wiki/1.-Function-Format#function-format)
|
||||
- 内容函数和`以 @handler 装饰的事件监听器`可以同时存在于同一个插件,并同时受到插件开关的控制
|
||||
- 务必确保您使用的模型支持函数调用功能
|
||||
|
||||
3️⃣ 现在您的程序已具备网络访问功能,重启程序,询问机器人有关在线的内容或直接发送文章链接请求其总结。
|
||||
|
||||
- 这仅仅是一个示例,需要更高效的网络访问能力支持插件,请查看[WebwlkrPlugin](https://github.com/RockChinQ/WebwlkrPlugin)
|
||||
|
||||
## 📦 上架插件市场
|
||||
|
||||
当你的插件开发完成后,请上传到 GitHub,并在 [LangBot 仓库提出 issue](https://github.com/langbot-app/LangBot/issues/new?assignees=&labels=%E7%8B%AC%E7%AB%8B%E6%8F%92%E4%BB%B6&projects=&template=submit-plugin.yml&title=%5BPlugin%5D%3A+%E8%AF%B7%E6%B1%82%E7%99%BB%E8%AE%B0%E6%96%B0%E6%8F%92%E4%BB%B6) ,待审核后将会添加到插件市场,约半个小时后会在 LangBot WebUI 插件页的插件市场上可见。
|
||||
|
||||

|
||||
|
||||
## 📄 名词解释
|
||||
|
||||
### 说明
|
||||
|
||||
事件监听器方法获得的`ctx: EventContext`对象可以到`pkg.plugin.context`模块中查看其结构,`ctx`对象的`event`属性为本次事件的事件对象,可以从中取出相关参数,具体事件可以取得的参数请查看`pkg.plugin.events`文件中的每个事件类的注释。
|
||||
事件返回值均为**可选**的,可以通过调用`ctx.add_return(key: str, ret)`来提交返回值
|
||||
|
||||
### 事件
|
||||
|
||||
若一个事件是一次请求(用户发送消息)中的事件,其事件对象中会含有`query`对象,这个对象中包含了此次请求(即用户发送一条消息的处理过程,一个请求过程可能会触发多个事件)处理过程中的上下文数据。
|
||||
所有支持的事件,请查看`pkg.plugin.events`文件中的每个事件类的注释。
|
||||
关于`消息链组件`,请查看 [消息平台实体](./messages)
|
||||
|
||||
### BasePlugin 结构
|
||||
|
||||
#### self.ap
|
||||
|
||||
在插件的方法中访问`self.ap`即为`pkg.core.app.Application`类的对象,包含了整个程序的上下文对象,可以通过这个对象访问程序内所有对象。
|
||||
|
||||
#### self.host
|
||||
|
||||
在插件的方法中访问`self.host`即为`pkg.plugin.context.APIHost`类的对象,提供了主程序的一些API,详细请查看其源码。
|
||||
|
||||
### API 参考
|
||||
|
||||
关于插件可以调用的 API,请查看 [API 参考](./api-ref)
|
||||
|
||||
### 插件开发交流群
|
||||
|
||||
插件开发、发布、交流
|
||||
|
||||
- LangBot 插件开发:1032327138 ([链接](https://qm.qq.com/q/G7ENGTjeou))
|
||||
# 插件开发教程
|
||||
|
||||
## 架构基础
|
||||
|
||||
在 4.0 版本中,我们引入了一种高安全性、高灵活性的生产级插件系统,并为插件开发者提供了丰富的 API 和易用的配套工具。
|
||||
|
||||
<img width="600" src="/assets/image/zh/plugin/dev/plugin_system_arch.png" />
|
||||
|
||||
Plugin Runtime (插件运行时),用于管理插件的生命周期,协调 LangBot 与插件之间的交互。有两种运行模式:`stdio` 和 `websocket`。当 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 # 插件说明文件,描述插件的功能和使用方法,会被展示在插件市场页面
|
||||
└── requirements.txt # 插件依赖文件
|
||||
```
|
||||
|
||||
main.py 中的插件类是每个插件通用的代码,在插件被启动时会被初始化,并传入上下文信息(配置信息等)。您可在此实现插件的初始化和关闭时的逻辑。插件类会继承于`langbot_plugin.api.definition.plugin.BasePlugin`,并提供 [LangBot 全局 API](/zh/plugin/dev/apis/common)。
|
||||
|
||||
各个组件是您插件的核心功能模块,可以根据需要[添加和删除组件](/zh/plugin/dev/components/add)。不同的组件会在不同的情况下被调用,这种设计方便后续扩展插件功能。各个组件均直接继承于`langbot_plugin.api.definition.components`包下的各个组件基类,并在其中包含`plugin: BasePlugin`对象,您可直接调用插件的 API。
|
||||
|
||||
::: info
|
||||
- 详细的 API 说明请参考[API 参考](/zh/plugin/dev/apis/common)。
|
||||
|
||||
请您继续阅读本页教程,了解插件的开发流程。
|
||||
:::
|
||||
|
||||
## 安装 CLI
|
||||
|
||||
请确保您已安装 Python 3.10 或更高版本,并已安装 [uv 包管理器](https://docs.astral.sh/uv/)。
|
||||
|
||||
在任意空目录执行命令,安装 LangBot CLI 和 SDK
|
||||
|
||||
```bash
|
||||
pip install langbot_plugin==0.1.1b5
|
||||
```
|
||||
|
||||
## 初始化插件目录
|
||||
|
||||
假设您的插件名称为 `HelloPlugin`,则在任意目录下创建目录`HelloPlugin`,并进入该目录,执行命令初始化插件:
|
||||
|
||||
```bash
|
||||
lbp init
|
||||
```
|
||||
|
||||
按照提示输入`Author(作者)`、`Description(描述)`等信息
|
||||
|
||||
:::info
|
||||
您也可以使用`lbp init HelloPlugin`命令,在子目录`HelloPlugin`中初始化插件。
|
||||
|
||||
<img width="600" src="/assets/image/zh/plugin/dev/create_plugin.png" />
|
||||
:::
|
||||
|
||||
此操作将生成插件的初始文件。您现在可以在您喜爱的编辑器中打开 `HelloPlugin` 目录,开始编写插件代码。
|
||||
|
||||
:::info
|
||||
若提示找不到 `lbp` 命令,可能是由于您未正确设置 `PATH` 环境变量。
|
||||
可使用`python -m langbot_plugin.cli.__init__`替代`lbp`命令。
|
||||
|
||||
例如:
|
||||
|
||||
```bash
|
||||
python -m langbot_plugin.cli.__init__ init HelloPlugin
|
||||
cd HelloPlugin
|
||||
```
|
||||
:::
|
||||
|
||||
## 启动调试
|
||||
|
||||
您需要先部署并启动 LangBot,确保 Plugin Runtime 正在运行并监听 `5401` 端口。
|
||||
|
||||
::: info
|
||||
Runtime 有两种启动方式:
|
||||
|
||||
- `Stdio` 模式:当您使用源代码启动 LangBot 并未携带启动参数`--standalone-runtime`时,LangBot 会自动以子进程的形式启动 Plugin Runtime,并通过 `stdio`(标准输入输出流)模式与 Plugin Runtime 通信。此时 Runtime 会加载 LangBot 根目录的`data/plugins`目录下的插件。并监听 LangBot 运行主机的`5401`端口作为调试端口。
|
||||
|
||||
- `WebSocket` 模式:
|
||||
- 生产环境:当您使用官方提供的`docker-compose.yaml`启动 LangBot 时,会在独立的容器运行 Plugin Runtime,LangBot 也会因为携带启动参数`--standalone-runtime`而以 WebSocket 模式与 Plugin Runtime 通信。
|
||||
- 开发环境:若您通过源代码启动 LangBot,并携带启动参数`--standalone-runtime`,LangBot 按照`data/config.yaml`中配置的`plugin.runtime_ws_url`地址(端口通常是5400)连接到已启动的 Plugin Runtime。您需要自行启动独立运行的 Plugin Runtime,见[开发 Plugin Runtime](/zh/develop/plugin-runtime)。
|
||||
|
||||
无论是`stdio`或`websocket`模式,Plugin Runtime 都会监听其所在的主机的`5401`端口作为调试端口供插件调试时连接。
|
||||
|
||||
当您在开发插件时,推荐您按照[开发配置](/zh/develop/dev-config)中的方式 LangBot,此方式将以 Stdio 形式启动 Plugin Runtime,便于插件开发。
|
||||
:::
|
||||
|
||||
复制插件目录下的`.env.example`文件为`.env`,检查或修改`DEBUG_RUNTIME_WS_URL`为您的 Plugin Runtime 的 WebSocket 地址。
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
启动插件调试,您将看到插件的输出:
|
||||
|
||||
```bash
|
||||
lbp run
|
||||
```
|
||||
|
||||
<img width="600" src="/assets/image/zh/plugin/dev/launch_debug_plugin.png" />
|
||||
|
||||
并可以在 LangBot 的 WebUI 中看到此插件已被加载。
|
||||
|
||||
<img width="600" src="/assets/image/zh/plugin/dev/debug_plugin_loaded.png" />
|
||||
|
||||
## 接下来做什么
|
||||
|
||||
该教程将指引您逐步完善插件功能。
|
||||
|
||||
- 修改插件信息:插件已创建,并填入了最基础的插件信息,请您[完善插件信息](/zh/plugin/dev/basic-info)。
|
||||
- 添加组件:插件组件是插件的核心功能单元,您可以根据需求[添加组件](/zh/plugin/dev/components/add)。
|
||||
|
||||
Reference in New Issue
Block a user