优秀实践
插件加载方式
nonebot.load_plugin()
还是 muicebot.plugin.load_plugin()
?
在 Muicebot 中,插件分为两类,一类分为 Nonebot 插件,另一类是 Muicebot 插件。
其中 Nonebot 插件的安装方式是通过 nb plugin install
,然后读取 pyproject.toml
中的插件条目进行加载
Muicebot 插件则是统一存放在 plugins
或者是 plugins/store
文件夹下,通过 muicebot.plugin.load_plugins()
函数统一加载
在 v1.0.1 之后的版本,这两种方式没有本质上的区别,因为 muicebot.plugin.load_plugins()
最后会使用 nonebot.load_plugin()
进行插件的加载
也就是说,Muicebot 的插件实际上就是Nonebot 插件,只不过不能在其他 Nonebot 项目中使用而已
在下文中,我们将首先介绍 Muicebot 的数据接口,然后再介绍 Muicebot 插件开发者所有你可能会使用到的类和接口以方便你开发插件。
存储数据
目前,Muicebot 仅提供了获取数据目录的接口,如需获取数据库会话,建议使用 nonebot_plugin_orm
进行数据库操作
获取数据目录(muicebot内置方法)
获取数据目录方法:
from muicebot.plugin import get_plugin_data_dir
data_dir = get_plugin_data_dir()
该函数文档说明如下:
def get_plugin_data_dir() -> Path:
"""
获取 Muicebot 插件数据目录
对于 Muicebot 的插件,它们的插件目录位于 Muicebot 的插件目录中下的 `plugins` 文件夹,并以插件名命名
(`nonebot_plugin_localstore.get_plugin_data_dir`)
"""
这个方法将返回只属于该插件名的一个目录,这个目录位于 nonebot_plugin_localstore.get_plugin_data_dir() / "plugin" / plugin_name
这个文件夹是默认创建的。如果出于未知的情况无法获取插件名,则会使用 .unknown
命名该文件夹
如果出现 .unknown
的情况,还请及时向我们反馈!
获取数据目录(nonebot_plugin_localstore方法)
获取数据目录方法:
from nonebot_plugin_localstore import get_plugin_data_dir
data_dir = get_plugin_data_dir()
获取数据库会话(nonebot_plugin_orm)
参见开发者指南
插件信息
像 Nonebot 一样,在 Muicebot 中每个插件都有自己的一个 Plugin
对象,这其中存储了插件系统所需要的一系列信息,其中就包括插件元数据 PluginMetadata
,它允许插件开发者为插件添加一些额外的信息。这些信息编写于插件模块的顶层,可以直接通过源码查看。
在插件顶层模块 __init__.py
中添加插件元数据,如下所示:
from muicebot.plugin import PluginMetadata
from .config import Config
__plugin_meta__ = PluginMetadata(
name="示例插件",
description="这是一个示例插件",
usage="没什么用",
homepage="https://example.com/",
config=Config,
extra={},
)
TIP
对于要发布在 Muicebot-Plugin-Index 的插件来说,插件元数据是必须的,没有插件元数据将无法通过工作流测试
Muicebot 消息处理概述
模型交互
获取全局模型实例
from muicebot.muice import Muice
model = Muice.get_instance().model
这将获取全局对话中的模型实例
通过模型配置文件初始化实例
from muicebot.llm import load_model
from muicebot.config import get_model_config
multimodal_model = load_model(get_model_config(config.meme_multimodal_config))
multimodal_model.load() # 加载模型
与模型交互
from muicebot.llm import ModelCompletions, ModelRequest
from muicebot.model import Resource
# 确保是多模态模型
if not (model and model.is_running):
raise RuntimeError("LLM 尚未运行!")
elif not model.config.multimodal:
raise RuntimeError("LLM 不是多模态的!")
# 构建多模态资源
image = Resource("image", path="./Muika_cos.png")
# 构建模型请求,并传入系统提示词和 Resource 多模态资源
model_request = ModelRequest(prompt, system=system, resources=[image])
# 初始化模型用量, -1 表示该模型加载器不支持模型用量查询
response_usage = -1
logger.debug(f"向 LLM 发送请求: {model_request}")
# 与LLM交互
response = await model.ask(model_request, stream=model.config.stream)
# 如果是非流式调用
if isinstance(response, ModelCompletions):
response_text = response.text
response_usage = response.usage
response_status = response.succeed
# 如果是流式调用
else:
response_chunks: list[str] = []
async for chunk in response:
response_chunks.append(chunk.chunk)
response_usage = chunk.usage or chunk.usage
response_status = chunk.succeed if not chunk.succeed else response_status
# 拼接流式响应
response_text = "".join(response_chunks)
# 断言是否请求成功
assert response_status, "LLM请求失败"
logger.debug(f"LLM 请求已完成,用量: {response_usage}")
提取思考结果
模型交互层不会自动提取思考结果,也无法从配置文件中推断目前选用的是否是推理模型,因此无论是否启用了推理模式,你都需要手动处理模型输出后的结果
import re
def process_message(message: str) -> str:
"""
提取思考结果
"""
if not message.startswith("<think>"):
return message
thoughts_pattern = re.compile(r"<think>(.*?)</think>", re.DOTALL)
result = thoughts_pattern.sub("", message).strip()
return result