doc: 更换VitePress后的初步内容

This commit is contained in:
lazy
2024-08-28 00:50:44 +08:00
parent f7d7dab296
commit d1698186e1
128 changed files with 3490 additions and 6886 deletions
+25
View File
@@ -0,0 +1,25 @@
# API 参考
以下 API 仅针对 事件插件 可用,组件扩展形式请自行阅读源码。
## 消息处理
### 回复消息
ctx.reply(message_chain: mirai.MessageChain)
回复此次事件的发起会话。
- `message_chain`:[YiriMirai 的 MessageChain 对象](https://yiri-mirai.wybxc.cc/docs/basic/message-chain),若用户使用的不是 YiriMirai 适配器,程序也能自动转换为目标消息链
### 发送主动消息
> 由于 QQ 官方 API 对主动消息的支持性很差,故若用户使用的是 QQ 官方 API,发送主动消息可能会失败
ctx.send_message(target_type: str, target_id: str, message_chain: mirai.MessageChain)
发送主动消息给目标。
- `target_type`:目标类型,可选值:`"person"`、`"group"`
- `target_id`:目标 ID(QQ 号或群号)
- `message_chain`:[YiriMirai 的 MessageChain 对象](https://yiri-mirai.wybxc.cc/docs/basic/message-chain),若用户使用的不是 YiriMirai 适配器,程序也能自动转换为目标消息链
+102
View File
@@ -0,0 +1,102 @@
# 组件扩展
> 请先阅读[插件介绍页](../plugin/intro.md)
:::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`设置为新增的加载器名称。
### 消息平台适配器
新增一个消息平台适配器,例如:接入Telegram等。
- 基类:`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`为新增的运行器名称。
+220
View File
@@ -0,0 +1,220 @@
# 插件开发
> 请先阅读[插件介绍页](../plugin/intro.md)
> 建议先阅读本项目源码,了解项目架构
> 问题、需求请到仓库issue发起
> **提问前请先靠自己尝试**
:::info 目录
[[toc]]
:::
## 💬简介
本页讲解普通插件开发步骤,插件内部有两种方法:事件监听器、内容函数。
事件监听器能在某些事件触发时进行响应,更改行为。内容函数可供大语言模型调用,具体请查看插件介绍页。
QChatGPT 还支持组件扩展,可以不局限于监听预定事件,做到整个组件替换,具体请阅读完此页后查看**组件扩展**页。
## 💻快速开始
按照文档部署此项目,并使其正常运行。
在`plugins`目录下新建目录`hello`,在其中新建空文件`__init__.py`以标记此目录为软件包,继续新建文件`main.py`。
> 建议使用[HelloPlugin](https://github.com/RockChinQ/HelloPlugin)作为模板直接生成插件代码仓库后按照下方步骤进行修改。
编辑`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!"
"""
# 注册插件
@register(name="Hello", description="hello world", version="0.1", author="RockChinQ")
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
```
此插件将实现:私聊收到`hello`消息时回复`hello, <发送者QQ号>!`,群聊收到`hello`消息时回复`hello, everyone!`
### 解读此插件程序
- `import pkg.plugin.context`导入`register(用于注册插件类)`, `handler(用于注册事件监听器)`, `llm_func(用于注册内容函数)`,`BasePlugin(插件基类)`, `APIHost(API宿主)`, `EventContext(事件上下文类)`等内容
- `import pkg.plugin.events`导入所有支持的事件类
- `@register`将类`HelloPlugin`标记为一个插件类,声明插件名称为`Hello`以及插件简介、版本、作者
- 声明类`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`事件处理群消息
编写完毕保存后,重新启动主程序,向机器人发送`!plugin`命令,若有`Hello`插件则说明插件加载成功
## ❗规范(重要)
- 请每个插件独立一个目录以便管理,建议在Github上创建一个仓库储存单个插件,以便获取和更新
- 插件名使用`大驼峰命名法`,如`Hello`、`ExamplePlugin`、`ChineseCommands`等
- 一个目录内可以存放多个Python程序文件,以独立出插件的各个功能,便于开发者管理,但不建议在一个目录内注册多个插件
- 插件需要的依赖库请在插件目录下的`requirements.txt`中指定,程序从储存库获取此插件时将自动安装依赖
## 🪝内容函数
通过[GPT的Function Calling能力](https://platform.openai.com/docs/guides/gpt/function-calling)实现的`内容函数`,这是一种嵌入对话中,由GPT自动调用的函数。
> 您的插件不一定必须包含内容函数,请先查看内容函数页了解此功能
### 内容函数编写步骤
1️⃣ 请先按照上方步骤编写您的插件基础结构,现在请删除(当然你也可以不删,只是为了简洁)上述插件内容的诸个由`@handler`装饰的类函数
<details>
<summary>删除后的类结构</summary>
```python
# 注册插件
@register(name="Hello", description="hello world", version="0.1", author="RockChinQ")
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 装饰的事件监听器`可以同时存在于同一个插件,并同时受到`plugins/plugins.json`中的插件开关的控制
- 务必确保您使用的模型支持函数调用功能
3️⃣ 现在您的程序已具备网络访问功能,重启程序,询问机器人有关在线的内容或直接发送文章链接请求其总结。
- 这仅仅是一个示例,需要更高效的网络访问能力支持插件,请查看[WebwlkrPlugin](https://github.com/RockChinQ/WebwlkrPlugin)
## 📄名词解释
### 说明
事件监听器方法获得的`ctx: EventContext`对象可以到`pkg.plugin.context`模块中查看其结构,`ctx`对象的`event`属性为本次事件的事件对象,可以从中取出相关参数,具体事件可以取得的参数请查看`pkg.plugin.events`文件中的每个事件类的注释。
事件返回值均为**可选**的,可以通过调用`ctx.add_return(key: str, ret)`来提交返回值
### 事件
若一个事件是一次请求(用户发送消息)中的事件,其事件对象中会含有`query`对象,这个对象中包含了此次请求(即用户发送一条消息的处理过程,一个请求过程可能会触发多个事件)处理过程中的上下文数据。
所有支持的事件,请查看`pkg.plugin.events`文件中的每个事件类的注释。
关于`YiriMirai`支持的消息链组件,请查看 [YiriMirai的文档](https://yiri-mirai.wybxc.cc/docs/basic/message-chain)
### BasePlugin 结构
#### self.ap
在插件的方法中访问`self.ap`即为`pkg.core.app.Application`类的对象,包含了整个程序的上下文对象,可以通过这个对象访问程序内所有对象。
#### self.host
在插件的方法中访问`self.host`即为`pkg.plugin.context.APIHost`类的对象,提供了主程序的一些API,详细请查看其源码。
+99
View File
@@ -0,0 +1,99 @@
# 插件介绍
:::info 目录
[[toc]]
:::
## 插件分类
**插件**,是QChatGPT的扩展模块,其中包含**事件监听器**和**内容函数**两种可执行内容。事件监听器由主程序运行中的事件驱动,而内容函数由GPT生成的内容驱动。这两种驱动方式都统一包装在插件类中。
### 事件监听器
插件监听 QChatGPT 运行期间的各种事件,QChatGPT 在事件发生时,携带事件相关信息调用插件注册的监听器。插件可以通过注册监听器以在指定情况下被触发。
### 内容函数
内容函数不同于 通过事件触发插件,而是在对话期间,当对话内容符合内容函数的描述时,由 GPT 指定要调用的函数,并由 QChatGPT 进行调用。
这是基于 [GPT的Function Calling能力](https://platform.openai.com/docs/guides/gpt/function-calling) 实现的,这是一种嵌入对话中,由GPT自动调用的函数。
> 就是 ChatGPT官方插件 那种东西
例如我们为GPT提供一个函数`access_the_web`,并提供其详细的描述以及其参数的描述,那么当我们在与GPT对话时涉及类似以下内容时:
```
Q: 请搜索一下github上有那些QQ机器人项目?
Q: 请为我搜索一些不错的云服务商网站?
Q:阅读并总结这篇文章:https://zhuanlan.zhihu.com/p/607570830
Q:搜一下清远今天天气如何
```
GPT将会回复一个对`access_the_web`的函数调用请求,QChatGPT将调用插件提供的内容函数,并返回调用结果给GPT使其生成新的回复。
当然,函数调用功能不止局限于网络访问,还可以实现图片处理、科学计算、行程规划等需要调用函数的功能,理论上我们可以通过内容函数实现与`ChatGPT Plugins`相同的功能。
## 插件用法
### 安装
> 这里只是给出了插件的基本安装方法,详细需查看各插件文档或咨询其开发者
#### 储存库克隆(推荐)
在运行期间,使用管理员账号对机器人私聊发送`!plugin get <GitHub储存库地址>`即可自动获取源码并安装插件,程序会根据仓库中的`requirements.txt`文件自动安装依赖库
例如安装`hello_plugin`插件
```
!plugin get https://github.com/RockChinQ/hello_plugin
```
安装完成后重启程序。
#### 手动安装
将获取到的插件程序放置到`plugins`目录下,具体使用方式请查看各插件文档或咨询其开发者。
### 管理
#### !plugin 命令
```
!plugin 列出所有已安装的插件
!plugin get <储存库地址> 从GitHub储存库地址安装插件(需要管理员权限)
!plugin update all 更新所有插件(需要管理员权限,仅支持从储存库安装的插件)
!plugin update <插件名> 更新指定插件
!plugin del <插件名> 删除插件(需要管理员权限)
!plugin on <插件名> 启用插件(需要管理员权限)
!plugin off <插件名> 禁用插件(需要管理员权限)
!func 列出所有内容函数
```
#### plugins/plugins.json 配置文件
程序启动时,会同步这里的设置和实际加载的插件设置。
**这个文件内容是由程序自动生成的,不要手动添加或删除项目,仅可修改优先级和启用状态**,修改后需重启。
示例:
```json
"plugins": [
{
"name": "Nahida",
"description": "Hello Nahida",
"version": "0.1",
"author": "RockChinQ",
"source": "",
"main_file": "plugins/Nahida/main.py",
"pkg_path": "plugins/Nahida/",
"priority": 0,
"enabled": true
}
]
```
`source`:源码地址,若无内容,则表示这个插件不支持程序自动更新
`priority`: 插件运行优先级,在初始化、事件调用时,高优先级的插件将优先被处理
`enabled`:设置为false时,不会初始化,也不会被调用
+110
View File
@@ -0,0 +1,110 @@
# 技术信息
以下是QChatGPT实现原理等技术信息,贡献之前请仔细阅读
> 此文档为 QChatGPT 2.x 的技术文档,已过时,3.x 版本请直接查看源码。
> 请先阅读OpenAI API的相关文档 https://beta.openai.com/docs/ ,以下信息假定您已了解OpenAI模型的相关特性及其接口的调用方法。
## 术语
包含OpenAI API涉及的术语和项目中的概念的命名
括号中是程序中相应术语的命名,无括号的为抽象概念
### 模型(model)
AI模型,程序调用OpenAI的接口获取的内容均为OpenAI的模型生成的内容。
### 字符(tokens)
OpenAI定义的字符,ASCII字符为1 token,其他为2 token。
### 提示符(prompt)
i. 调用OpenAI的文字补全模型时的提示语,模型接口会根据提示语返回回复内容。程序底层会将对话内容进行封装生成提示符。调用文字补全模型时的提示符均由`user_name`(默认为`You`,可在配置文件修改)和`bot_name`(默认为`Bot`,可在配置文件修改)标记对话角色以供模型识别,以下是实例:
```
You:今天天气真不错
Bot:很高兴你喜欢今天的天气:)
You:谢谢你
Bot:不客气:)
```
补全模型调用的程序实现请查看下文`实现`节。
ii. 调用OpenAI的绘图模型时的提示语,模型会根据提示语进行绘图并返回图片URL。
### 对象
程序将单个人或单个QQ群视为一个对象,对象和模型是一次会话中的对话双方。
### 会话(session)
会话只对文字补全功能有效,绘图功能无会话概念。每个对象使用同一个会话,会话中仅有对象和模型两个角色,故群内所有的人都将被视为同一个角色与模型进行对话。
程序获取回复的本质是`文字补全`。
由于对话需要实现联系上下文,故程序会将模型与对象的对话历史记录作为`提示符`发送给OpenAI的接口以获取符合前文的回复。
而OpenAI的文字补全接口的提示符具有长度限制(默认使用的`text-davinci-003`限制为4096 tokens),
所以增加`会话`概念以管理向接口发送的提示符内容。
会话的存活时间可以在`config.py`中设置,默认为20分钟。会话过期之后会被存入数据库并重置。下一次该对象发起对话时将重启新的会话。
### 预设值、人格(default_prompt)
每个会话的预设对话信息,可在`config.py`中设置,程序会在每个会话创建时向提示符写入以下内容:
```
You:<预设信息>
Bot:好的
```
## 实现
### QQ机器人
> 程序路径:
> pkg.qqbot
- `pkg.qqbot.manager`中的`QQBotManager`实现了接收消息、调用OpenAI模块处理消息、报告审计模块记录使用量等功能,并提供通知管理员、发送消息等方法供其他模块调用。
- `pkg.qqbot.filter`提供了敏感词过滤的相关操作。
- `pkg.qqbot.process`提供了私聊消息和群聊消息的统一处理逻辑。
使用mirai及YiriMirai作为Python与QQ交互的框架,详细请见其文档。
在启动时会调用YiriMirai的函数以创建一个bot对象,用于程序通过mirai与QQ进行交互,在上层程序调用此bot对象的方法进行消息处理。
由于YiriMirai暂时无法关闭机器人,故在热重载前后维持同一个bot对象,这意味着QQ机器人的相关配置(QQ号、适配器等)信息不支持热重载。
### 数据库
> 程序路径:
> pkg.database
- `pkg.database.manager`中的`DatabaseManager`封装了诸多调用数据库的方法以供其他模块调用。
使用SQLite作为数据库,储存所有对象的历史会话信息、api-key的费用情况、api-key的使用量情况。
### OpenAI交互
> 程序路径:
> pkg.openai
- `pkg.openai.manager`中的`OpenAIInteract`类封装了OpenAI的文字补全`Completion`API和绘图API供机器人模块调用,并在接口调用成功之后向审计模块报告当前使用的api-key的使用量信息。
- `pkg.openai.keymgr`实现了多api-key的管理,其中以`exceeded`变量在运行时记录api-key的超额报错记录,并提供根据超额记录进行的api-key切换功能。
- `pkg.openai.pricing`记录各个模型的费用信息,供调用接口时估算费用,费用估算功能不再与api-key的切换挂钩,api-key仅在调用接口报错超额时进行切换。
- `pkg.openai.session`中的`Session`进行会话管理。
### utils模块
#### context模块
保存前述模块中的对象,并允许各个模块从此处获取其他模块的对象以调用其方法。
#### 热重载功能
> pkg.utils.reloader
重载前保存context中的所有对象,执行`main.py`中的程序关闭流程,使用`importlib`的`reload`函数重载所有模块(包含配置文件,包含新增的模块),重载后将context恢复,并执行程序启动流程。
所有模块都会重新创建对象,但QQ机器人模块中的bot对象不会被重新创建,这是因为YiriMirai提供的shutdown方法无法使用,这意味着`config.py`中关于QQ机器人的配置不支持热重载。
#### 热更新功能
> pkg.utils.updater
使用`dulwich`库执行pull操作拉取远程仓库的最新源码,并进行一次热重载加载最新代码。