docs: 新增 openspec 规范文档

- abp.md: ABP (AstrBot Plugin) 插件协议规范
- api.md: API 规范
- agent-message.md: Agent 消息格式规范
- config.md: 配置系统规范 (含 GPG 安全配置)
- env.md: 环境变量规范
- path.md: XDG 路径规范
This commit is contained in:
LIghtJUNction
2026-03-27 00:15:37 +08:00
parent 93a6672fac
commit e1ac6733e8
6 changed files with 3743 additions and 205 deletions

View File

@@ -5,7 +5,10 @@
ABP 是 AstrBot 的插件通信协议,用于插件的注册、消息处理、工具调用和生命周期管理。
ABP 采用**插件作为独立服务**的设计,插件通过配置加载,可使用任意编程语言实现。
**核心定位**ABP 定义了 AstrBot 编排器与插件之间的交互接口,支持进程内和跨进程两种加载模式。
**核心原则**
- **主进程不读取插件配置文件**:插件配置通过协议获取
- **数据目录由主进程分配**:插件的数据存储目录由主进程在握手时告知
- **零侵入性**:插件无需了解 AstrBot 内部结构
## 与 MCP 的关系
@@ -13,46 +16,22 @@ ABP 采用**插件作为独立服务**的设计,插件通过配置加载,可
|------|-----|-----|
| 定位 | 插件(功能扩展) | 工具/数据源连接 |
| 通信模式 | 双向(消息+工具+事件) | 单向(工具调用+资源访问) |
| 配置获取 | 通过协议握手获取 | 通过配置文件声明 |
| 数据目录 | 主进程分配 | 插件自行管理 |
| 消息处理 | 支持 | 不支持 |
| 生命周期 | 完整 | 轻量 |
| 编程语言 | 任意 | 任意 |
## 加载方式
## 插件加载配置
### 1. 进程内加载In-Process
- **直接函数调用**:无序列化开销,零拷贝
- **适用场景**:内置插件、高频调用、对延迟敏感
### 2. 跨进程加载Out-of-Process
- **独立进程运行**:插件崩溃不影响主进程
- **协议**JSON-RPC 2.0 over Unix Socket / HTTP
- **适用场景**:第三方插件、需要资源隔离
## 插件配置
类似 MCP 服务器配置,在 AstrBot 配置文件中声明插件:
AstrBot 配置文件声明插件基本信息(不包含插件配置):
```json
{
"plugins": [
{
"name": "weather-plugin",
"version": "1.0.0",
"load_mode": "out_of_process",
"command": "python",
"args": ["/path/to/weather_server.py"],
"env": {
"API_KEY": "xxx"
}
},
{
"name": "code-analysis",
"load_mode": "out_of_process",
"command": "./code-analysis-server",
"transport": "http",
"url": "http://localhost:9001"
"args": ["/path/to/weather_server.py"]
}
]
}
@@ -63,20 +42,183 @@ ABP 采用**插件作为独立服务**的设计,插件通过配置加载,可
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 是 | 插件名称 |
| `version` | string | 否 | 插件版本 |
| `load_mode` | string | 是 | `in_process``out_of_process` |
| `command` | string | 跨进程时必填 | 启动命令 |
| `args` | array | 跨进程时可选 | 命令参数 |
| `env` | object | 否 | 环境变量 |
| `transport` | string | 跨进程时可选 | `stdio` / `unix_socket` / `http` |
| `url` | string | HTTP 传输时必填 | 服务器地址 |
## 初始化握手
插件配置通过 `initialize` 握手交换,**主进程不读取插件的配置文件**。
### Initialize主进程 → 插件)
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "1.0.0",
"clientInfo": {
"name": "astrbot",
"version": "4.16.0"
},
"capabilities": {
"streaming": true,
"events": true
},
"pluginConfig": {
"user_config": {}
},
"dataDirs": {
"root": "/var/lib/astrbot",
"plugin_data": "/var/lib/astrbot/data/plugins/my-plugin",
"temp": "/var/cache/astrbot/temp"
}
}
}
```
### Initialize Response插件 → 主进程)
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "1.0.0",
"serverInfo": {
"name": "weather-plugin",
"version": "1.0.0"
},
"capabilities": {
"tools": true,
"handlers": true,
"events": true,
"resources": false
},
"configSchema": {
"type": "object",
"properties": {
"api_key": {
"type": "string",
"description": "Weather API Key",
"required": true
},
"default_location": {
"type": "string",
"description": "默认查询城市",
"default": "北京"
}
}
},
"metadata": {
"display_name": "天气插件",
"description": "提供天气查询功能",
"author": "Author",
"support_platforms": ["telegram", "discord"]
}
}
}
```
### 握手参数说明
**主进程 → 插件Initialize params**
| 字段 | 类型 | 说明 |
|------|------|------|
| `pluginConfig.user_config` | object | 用户配置(来自 secrets.yaml 等) |
| `dataDirs.root` | string | AstrBot 数据根目录 |
| `dataDirs.plugin_data` | string | 插件专用数据目录 |
| `dataDirs.temp` | string | 插件临时文件目录 |
**插件 → 主进程Initialize result**
| 字段 | 类型 | 说明 |
|------|------|------|
| `configSchema` | object | 插件配置 Schema用于 UI 生成配置表单) |
| `metadata.*` | object | 插件元数据 |
## 数据目录
插件的数据存储目录由主进程分配,插件通过握手获取:
```
dataDirs.plugin_data/
├── config.json # 插件配置(主进程写入)
├── data/ # 插件业务数据
└── cache/ # 插件缓存
```
**原则**
- 插件不自行决定数据目录
- 配置由主进程管理,插件通过 `dataDirs.plugin_data/config.json` 读取
- 插件只需读写自己的目录,无需了解 AstrBot 目录结构
## 配置传递
### 1. 用户配置(主进程 → 插件)
用户通过 WebUI 或配置文件设置的配置,通过握手传递给插件:
```json
{
"pluginConfig": {
"user_config": {
"api_key": "xxx",
"default_location": "上海"
}
}
}
```
### 2. 配置更新(主进程 → 插件)
配置变更时,主进程通知插件:
```json
{
"jsonrpc": "2.0",
"method": "plugin.config_update",
"params": {
"user_config": {
"api_key": "yyy"
}
}
}
```
### 3. 配置 Schema插件声明
插件声明配置 Schema用于主进程生成配置 UI
```json
{
"configSchema": {
"type": "object",
"properties": {
"api_key": {
"type": "string",
"description": "API Key",
"secret": true
},
"default_location": {
"type": "string",
"description": "默认城市"
}
},
"required": ["api_key"]
}
}
```
## 传输协议
### Stdio进程启动
用于进程内加载或通过 stdio 启动的外部插件:
```
{"jsonrpc":"2.0","method":"initialize","params":{...},"id":1}
{"jsonrpc":"2.0","method":"plugin.handle_event","params":{...}}
@@ -96,71 +238,7 @@ Content-Length: <字节数>\r\n
## 消息格式
所有消息均为 JSON-RPC 2.0
### 1. 初始化initialize
**请求**
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "1.0.0",
"clientInfo": {
"name": "astrbot",
"version": "4.16.0"
},
"capabilities": {
"streaming": true,
"events": true
}
}
}
```
**响应**
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "1.0.0",
"serverInfo": {
"name": "weather-plugin",
"version": "1.0.0"
},
"capabilities": {
"tools": true,
"handlers": true,
"events": true
},
"metadata": {
"display_name": "天气插件",
"description": "提供天气查询功能",
"author": "Author"
}
}
}
```
### 2. 插件能力
```json
{
"capabilities": {
"tools": true, // 支持工具调用
"handlers": true, // 支持消息处理器
"events": true, // 支持事件订阅
"resources": false // 是否提供资源
}
}
```
### 3. 工具调用(跨进程)
### 1. 工具调用
**请求**
@@ -186,16 +264,13 @@ Content-Length: <字节数>\r\n
"id": "req-001",
"result": {
"content": [
{
"type": "text",
"text": "北京天气25°C"
}
{ "type": "text", "text": "北京天气25°C" }
]
}
}
```
### 4. 消息处理
### 2. 消息处理
**请求**
@@ -210,13 +285,8 @@ Content-Length: <字节数>\r\n
"message_id": "msg-123",
"unified_msg_origin": "telegram:private:12345",
"message_str": "/weather 北京",
"sender": {
"user_id": "12345",
"nickname": "用户"
},
"message_chain": [
{ "type": "plain", "text": "/weather 北京" }
]
"sender": { "user_id": "12345", "nickname": "用户" },
"message_chain": [{ "type": "plain", "text": "/weather 北京" }]
}
}
}
@@ -230,29 +300,25 @@ Content-Length: <字节数>\r\n
"id": "req-002",
"result": {
"handled": true,
"results": [
{ "type": "plain", "text": "北京天气25°C" }
],
"results": [{ "type": "plain", "text": "北京天气25°C" }],
"stop_propagation": false
}
}
```
### 5. 事件订阅
### 3. 事件订阅
**通知**客户端 → 插件):
**通知**主进程 → 插件):
```json
{
"jsonrpc": "2.0",
"method": "plugin.subscribe",
"params": {
"event_type": "llm_request"
}
"params": { "event_type": "llm_request" }
}
```
**通知**(插件 → 客户端
**通知**(插件 → 主进程
```json
{
@@ -260,80 +326,23 @@ Content-Length: <字节数>\r\n
"method": "plugin.notify",
"params": {
"event_type": "tool_called",
"data": {
"tool": "get_weather",
"args": { "location": "北京" }
}
"data": { "tool": "get_weather", "args": { "location": "北京" } }
}
}
```
### 6. 工具列表
**请求**
```json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
```
**响应**
```json
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "get_weather",
"description": "获取天气信息",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称"
}
},
"required": ["location"]
}
}
]
}
}
```
### 7. 资源(可选)
```json
{
"jsonrpc": "2.0",
"method": "resources/list",
"params": {}
}
```
## 核心方法
### 初始化
| 方法 | 方向 | 描述 |
|------|------|------|
| `initialize` | C→P | 初始化连接 |
| `initialized` | P→C | 初始化完成通知 |
### 生命周期
| 方法 | 方向 | 描述 |
|------|------|------|
| `initialize` | C→P | 初始化连接,交换配置和数据目录 |
| `initialized` | P→C | 初始化完成通知 |
| `plugin.start` | C→P | 启动插件 |
| `plugin.stop` | C→P | 停止插件 |
| `plugin.reload` | C→P | 重载插件 |
| `plugin.config_update` | C→P | 配置更新通知 |
### 工具
@@ -341,7 +350,6 @@ Content-Length: <字节数>\r\n
|------|------|------|
| `tools/list` | C→P | 列出可用工具 |
| `tools/call` | C→P | 调用工具 |
| `tools/metadata` | P→C | 工具元数据更新 |
### 消息处理
@@ -361,7 +369,7 @@ Content-Length: <字节数>\r\n
## 插件元数据
插件通过 `initialize` 响应返回元数据:
插件 `initialize` 响应返回元数据:
```json
{
@@ -374,13 +382,17 @@ Content-Length: <字节数>\r\n
"handlers": true,
"events": true
},
"configSchema": {
"type": "object",
"properties": {
"api_key": { "type": "string", "secret": true }
}
},
"metadata": {
"display_name": "天气插件",
"description": "提供天气预报功能",
"author": "Author Name",
"homepage": "https://github.com/author/weather-plugin",
"support_platforms": ["telegram", "discord"],
"astrbot_version": ">=4.16,<5"
"description": "提供天气查询功能",
"author": "Author",
"support_platforms": ["telegram", "discord"]
}
}
```
@@ -409,13 +421,14 @@ Content-Length: <字节数>\r\n
## 进程内插件
对于进程内插件load_mode: in_process编排器直接调用插件方法,无需序列化
对于进程内插件load_mode: in_process主进程直接调用插件方法:
```python
# 插件直接实现为类
class MyPlugin:
def __init__(self, context):
def __init__(self, context, user_config: dict, data_dirs: dict):
self.context = context
self.user_config = user_config
self.data_dir = data_dirs["plugin_data"]
async def handle_event(self, event):
return [PlainResult("Hello!")]
@@ -427,19 +440,15 @@ class MyPlugin:
return {
"name": "my-plugin",
"version": "1.0.0",
"capabilities": {
"tools": True,
"handlers": True,
"events": False
}
"capabilities": {"tools": True, "handlers": True, "events": False},
"configSchema": {...}
}
```
## 实现注意事项
## 设计原则
- 插件是独立服务,通过配置声明式加载
- 支持任意编程语言实现Python、Go、Rust、JavaScript 等)
- 跨进程插件使用标准 JSON-RPC 2.0 通信
- 插件元数据通过 `initialize` 响应动态获取
- 支持热重载:配置更新后可在线重载插件
- 事件通知采用异步机制,不阻塞主流程
1. **主进程不读取插件配置**:插件配置通过协议传递
2. **数据目录由主进程分配**:插件通过握手获取 `dataDirs`
3. **配置 Schema 声明**:插件声明配置结构,主进程生成 UI
4. **插件无侵入性**:插件无需了解 AstrBot 内部结构
5. **热重载支持**:配置更新通过 `plugin.config_update` 通知

2127
openspec/agent-message.md Normal file

File diff suppressed because it is too large Load Diff

651
openspec/api.md Normal file
View File

@@ -0,0 +1,651 @@
# HTTP API 服务器规范
## 概述
AstrBot 提供 HTTP API 服务器,用于第三方应用集成。支持 API Key 认证,采用 RESTful 风格设计。
**特性**
- API Key 认证
- SSE 流式响应
- 文件上传/下载
- 主动消息推送
## 服务器配置
### 启动参数
| 参数 | 环境变量 | 说明 |
|------|----------|------|
| `--host` | `ASTRBOT_HOST` | 绑定地址,默认 `0.0.0.0` |
| `--port` | `ASTRBOT_PORT` | 绑定端口,默认 `6185` |
| `--ssl` | `ASTRBOT_SSL_ENABLE` | 启用 SSL/TLS |
| `--ssl-cert` | `ASTRBOT_SSL_CERT` | SSL 证书路径 |
| `--ssl-key` | `ASTRBOT_SSL_KEY` | SSL 私钥路径 |
### 配置示例
```yaml
# system.yaml
api:
host: "0.0.0.0"
port: 6185
ssl:
enable: true
cert: "/etc/astrbot/certs/fullchain.pem"
key: "/etc/astrbot/certs/privkey.pem"
# API 请求超时(秒)
timeout: 60
# 最大请求体大小MB
max_body_size: 100
```
## 认证
### API Key 认证
所有 `/api/v1/*` 端点需要 API Key 认证:
| 方式 | 头部 | 示例 |
|------|------|------|
| Header | `X-API-Key` | `X-API-Key: sk-xxxxx` |
| Bearer | `Authorization` | `Authorization: Bearer sk-xxxxx` |
**API Key 配置**
```yaml
# secrets.yaml
api:
keys:
- id: "my-app"
key: "sk-xxxxxxxxxxxx"
name: "My Application"
permissions:
- "chat"
- "file"
- "message"
rate_limit:
requests: 100
period: 60 # seconds
```
### 权限说明
| 权限 | 说明 |
|------|------|
| `chat` | 聊天功能(发送消息、查询会话) |
| `file` | 文件上传/下载 |
| `message` | 主动消息推送 |
| `admin` | 管理功能(预留) |
## 错误响应
### 错误格式
```json
{
"status": "error",
"code": 401,
"message": "Invalid API key",
"request_id": "req-abc123"
}
```
### HTTP 状态码
| 状态码 | 说明 |
|--------|------|
| `200` | 请求成功 |
| `400` | 请求参数错误 |
| `401` | 未授权API Key 无效) |
| `403` | 禁止访问(权限不足) |
| `404` | 资源不存在 |
| `429` | 请求过于频繁(限流) |
| `500` | 服务器内部错误 |
| `503` | 服务不可用 |
### 错误码
| 码值 | 说明 |
|------|------|
| `invalid_api_key` | API Key 无效 |
| `expired_api_key` | API Key 已过期 |
| `insufficient_permissions` | 权限不足 |
| `rate_limit_exceeded` | 请求频率超限 |
| `invalid_parameter` | 参数错误 |
| `session_not_found` | 会话不存在 |
| `config_not_found` | 配置不存在 |
| `file_not_found` | 文件不存在 |
| `upload_failed` | 文件上传失败 |
| `platform_error` | 平台错误 |
## 端点列表
| 方法 | 路径 | 说明 |
|------|------|------|
| `GET` | `/api/v1/im/bots` | 获取机器人列表 |
| `POST` | `/api/v1/file` | 上传文件 |
| `GET` | `/api/v1/file` | 下载文件 |
| `POST` | `/api/v1/chat` | 发送聊天消息SSE |
| `GET` | `/api/v1/chat/sessions` | 获取会话列表 |
| `POST` | `/api/v1/im/message` | 发送主动消息 |
| `GET` | `/api/v1/configs` | 获取配置列表 |
---
## API 端点详情
### GET /api/v1/im/bots
获取已配置的机器人/平台 ID 列表。
**请求**
```
GET /api/v1/im/bots
X-API-Key: sk-xxxxx
```
**响应**
```json
{
"status": "ok",
"data": {
"bot_ids": [
"telegram:bot:123456789",
"discord:bot:987654321",
"webchat:FriendMessage:openapi_probe"
]
}
}
```
---
### POST /api/v1/file
上传文件,获取 `attachment_id` 供后续使用。
**请求**
```
POST /api/v1/file
X-API-Key: sk-xxxxx
Content-Type: multipart/form-data
file: <binary>
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | binary | 是 | 上传的文件 |
**响应**
```json
{
"status": "ok",
"data": {
"attachment_id": "9a2f8c72-e7af-4c0e-b352-111111111111",
"filename": "document.pdf",
"type": "application/pdf",
"size": 102400
}
}
```
**限制**
- 最大文件大小100MB
- 支持格式:图片、音频、视频、文档等
---
### GET /api/v1/file
下载已上传的文件。
**请求**
```
GET /api/v1/file?attachment_id=9a2f8c72-e7af-4c0e-b352-111111111111
X-API-Key: sk-xxxxx
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `attachment_id` | string | 是 | 上传时返回的附件 ID |
**响应**
```
200 OK
Content-Type: application/octet-stream
<binary>
```
---
### POST /api/v1/chat
发送聊天消息,支持 SSE 流式响应。
**请求**
```
POST /api/v1/chat
X-API-Key: sk-xxxxx
Content-Type: application/json
{
"message": "Hello, how are you?",
"username": "alice",
"session_id": "my_session_001",
"enable_streaming": true,
"selected_provider": "openai_chat_completion",
"selected_model": "gpt-4.1-mini"
}
```
**请求体参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `message` | string \| array | 是 | 消息内容或消息链 |
| `username` | string | 是 | 用户名 |
| `session_id` | string | 否 | 会话 ID不提供则自动创建 UUID |
| `conversation_id` | string | 否 | `session_id` 的别名 |
| `selected_provider` | string | 否 | 指定 LLM 提供商 |
| `selected_model` | string | 否 | 指定模型 |
| `enable_streaming` | boolean | 否 | 启用 SSE 流式响应,默认 `true` |
| `config_id` | string | 否 | 指定配置文件 ID |
| `config_name` | string | 否 | 指定配置文件名称(当 config_id 未提供时) |
**消息链格式**
```json
{
"message": [
{ "type": "plain", "text": "Please analyze this file" },
{ "type": "file", "attachment_id": "9a2f8c72-e7af-4c0e-b352-111111111111" }
]
}
```
**消息类型**
| 类型 | 说明 | 字段 |
|------|------|------|
| `plain` | 文本消息 | `text` |
| `reply` | 回复消息 | `text`, `message_id` |
| `image` | 图片 | `attachment_id``url` |
| `file` | 文件 | `attachment_id`, `filename` |
| `record` | 语音 | `attachment_id` |
| `video` | 视频 | `attachment_id` |
**SSE 流式响应**
```
200 OK
Content-Type: text/event-stream
event: message
data: {"type": "message", "content": "Hello"}
event: message
data: {"type": "message", "content": " world"}
event: done
data: {"type": "done", "message_id": "msg-123"}
event: error
data: {"type": "error", "message": "Error description"}
```
**SSE 事件类型**
| 事件 | 说明 | 数据格式 |
|------|------|----------|
| `message` | 消息片段 | `{"type": "message", "content": "..."}` |
| `tool_call` | 工具调用 | `{"type": "tool_call", "tool": "name", "args": {...}}` |
| `tool_result` | 工具结果 | `{"type": "tool_result", "tool": "name", "result": {...}}` |
| `done` | 完成 | `{"type": "done", "message_id": "..."}` |
| `error` | 错误 | `{"type": "error", "message": "..."}` |
**完整响应(非流式)**
```json
{
"status": "ok",
"data": {
"message_id": "msg-123",
"content": "Hello! How can I help you today?",
"sender": {
"user_id": "alice",
"nickname": "Alice"
},
"timestamp": "2026-03-26T10:00:00Z"
}
}
```
---
### GET /api/v1/chat/sessions
获取用户的会话列表(分页)。
**请求**
```
GET /api/v1/chat/sessions?username=alice&page=1&page_size=20
X-API-Key: sk-xxxxx
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `username` | string | 是 | 用户名 |
| `page` | integer | 否 | 页码,默认 `1` |
| `page_size` | integer | 否 | 每页数量,默认 `20`,最大 `100` |
| `platform_id` | string | 否 | 平台过滤器 |
**响应**
```json
{
"status": "ok",
"data": {
"sessions": [
{
"session_id": "my_session_001",
"platform_id": "webchat:FriendMessage:openapi_probe",
"creator": "alice",
"display_name": "Alice",
"is_group": 0,
"created_at": "2026-03-26T10:00:00Z",
"updated_at": "2026-03-26T10:30:00Z"
}
],
"page": 1,
"page_size": 20,
"total": 1
}
}
```
---
### POST /api/v1/im/message
向平台机器人发送主动消息。
**请求**
```
POST /api/v1/im/message
X-API-Key: sk-xxxxx
Content-Type: application/json
{
"umo": "webchat:FriendMessage:openapi_probe",
"message": "ping from api"
}
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `umo` | string | 是 | 统一消息源地址 |
| `message` | string \| array | 是 | 消息内容或消息链 |
**UMO 格式**
```
platform:message_type:session_id
```
| 平台 | 示例 |
|------|------|
| `webchat` | `webchat:FriendMessage:openapi_probe` |
| `telegram` | `telegram:private:123456789` |
| `telegram` | `telegram:group:-123456789` |
| `discord` | `discord:channel:987654321` |
**消息类型**
| 类型 | 说明 |
|------|------|
| `private` | 私聊 |
| `group` | 群聊 |
| `FriendMessage` | WebChat 好友消息 |
**响应**
```json
{
"status": "ok",
"message": "Message sent successfully"
}
```
---
### GET /api/v1/configs
获取可用的聊天配置文件列表。
**请求**
```
GET /api/v1/configs
X-API-Key: sk-xxxxx
```
**响应**
```json
{
"status": "ok",
"data": {
"configs": [
{
"id": "default",
"name": "默认配置",
"path": "/var/lib/astrbot/data/configs/default.yaml",
"is_default": true
},
{
"id": "coding-assistant",
"name": "编程助手",
"path": "/var/lib/astrbot/data/configs/coding-assistant.yaml",
"is_default": false
}
]
}
}
```
---
## 速率限制
| 等级 | 请求数 | 窗口 |
|------|--------|------|
| 默认 | 100 | 60 秒 |
| 高级 | 1000 | 60 秒 |
**限流响应**
```
429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1742992060
{
"status": "error",
"code": 429,
"message": "Rate limit exceeded. Try again in 30 seconds."
}
```
---
## Webhook 回调
### 配置
```yaml
# system.yaml
callback_api_base: "https://your-server.com/callback"
```
### 回调事件
| 事件 | 说明 |
|------|------|
| `message.received` | 收到消息 |
| `message.sent` | 消息发送成功 |
| `session.created` | 会话创建 |
| `session.updated` | 会话更新 |
| `error` | 错误发生 |
### 回调签名
```
X-AstrBot-Signature: sha256=xxxxx
X-AstrBot-Timestamp: 1742992060
```
验证签名:
```python
import hmac
import hashlib
def verify_signature(payload: bytes, timestamp: str, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
f"{timestamp}.".encode() + payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
```
---
## 健康检查
### GET /health
```
GET /health
```
**响应**
```json
{
"status": "healthy",
"version": "4.16.0",
"uptime": 3600
}
```
### GET /ready
```
GET /ready
```
**响应**
```json
{
"status": "ready",
"components": {
"api": "ok",
"platforms": {
"telegram": "ok",
"discord": "ok"
},
"llm_providers": {
"openai": "ok"
}
}
}
```
---
## 客户端 SDK
### Python
```python
from astrbot.api.http import AstrBotClient
client = AstrBotClient(api_key="sk-xxxxx", base_url="http://localhost:6185")
# 发送消息
async def main():
async for event in client.chat("Hello!", username="alice", session_id="test"):
print(event)
# 或非流式
result = await client.chat_one("Hello!", username="alice")
print(result)
```
### JavaScript
```javascript
import { AstrBotClient } from "@astrbot/sdk";
const client = new AstrBotClient({
apiKey: "sk-xxxxx",
baseUrl: "http://localhost:6185"
});
// 流式
for await (const event of client.chat({ message: "Hello!", username: "alice" })) {
console.log(event);
}
// 非流式
const result = await client.chatOne({ message: "Hello!", username: "alice" });
```
---
## 目录结构
```
$XDG_CONFIG_HOME/astrbot/
├── config.yaml # 主配置入口
├── secrets.yaml # 安全配置(包含 API Keys
└── ...
```
---
## 安全建议
1. **API Key 安全**
- API Key 仅通过 HTTPS 传输
- 定期轮换 API Key
- 不同应用使用不同 Key
2. **网络隔离**
- 生产环境使用 SSL/TLS
- 限制 API 服务器访问范围
- 使用防火墙或 VPN
3. **权限控制**
- 按最小权限原则分配 API Key 权限
- 避免使用 `admin` 权限
- 记录 API 调用日志
4. **限流配置**
- 根据业务需求调整限流参数
- 监控限流触发情况
- 合理设置不同等级的限流策略

565
openspec/config.md Normal file
View File

@@ -0,0 +1,565 @@
# 配置规范
## 概述
AstrBot 配置系统遵循以下原则:
| 原则 | 说明 |
|------|------|
| **分离关注点** | 按功能域分离配置 |
| **敏感信息隔离** | 密钥、密码存储在独立的安全文件中 |
| **环境变量优先** | 密钥通过环境变量注入,不写入配置文件 |
| **分层配置** | 系统配置 → 平台配置 → 插件配置 |
## 配置分类
### 配置域
| 域 | 配置文件 | 说明 |
|---|----------|------|
| **安全配置** | `secrets.yaml` | API keys、密码、JWT secrets、GPG 密钥 |
| **平台配置** | `platform.yaml` | 平台适配器设置 |
| **提供商配置** | `providers.yaml` | LLM 提供商设置(不含密钥) |
| **Agent 配置** | `agent.yaml` | Agent 行为参数 |
| **系统配置** | `system.yaml` | 日志、调试、代理、API 服务器等 |
| **GPG 配置** | `gpg.yaml` | GPG 安全配置(可选) |
| **插件配置** | `plugins/` | 各插件独立配置 |
## GPG 安全配置(可选)
### 概述
AstrBot 提供可选的 GPG 安全功能,用于配置文件的签名与验签,防止配置被篡改。
| 功能 | 说明 |
|------|------|
| **配置签名** | 对配置文件进行签名,确保完整性 |
| **配置验签** | 启动时验证配置文件签名,检测篡改 |
| **插件签名** | 验证插件包的 GPG 签名 |
### gpg.yamlGPG 配置)
```yaml
# GPG 安全配置
# 可选功能,不启用时不影响正常功能
security:
# GPG 功能开关
enable: false
# 验签模式
# - none: 不验签
# - warn: 验签失败仅警告
# - enforce: 验签失败阻止启动
verify_mode: "warn"
# 信任的公钥指纹列表
trusted_keys:
- "ABCDEF1234567890..."
- "FEDCBA0987654321..."
# 签名的配置文件模式
signed_configs:
- "secrets.yaml"
- "agent.yaml"
- "platform.yaml"
# 签名文件后缀
signature_suffix: ".sig"
# GPG 主目录(默认使用系统配置)
gnupg_home: ""
```
### secrets.yaml 中的 GPG 密钥
```yaml
# GPG 密钥配置
gpg:
# 私钥(用于签名)
# 使用空字符串表示不配置
private_key: ""
passphrase: ""
# 或使用 GPG Keybox 格式
# keybox: "/path/to/s.gpg-keybox"
# 公钥指纹(用于验签)
fingerprint: "ABCDEF1234567890..."
```
### 配置签名与验签流程
#### 签名流程
```bash
# 1. 首次签名
astrbot gpg sign --config secrets.yaml
astrbot gpg sign --config agent.yaml
# 2. 生成签名文件
# secrets.yaml.sig
# agent.yaml.sig
# 3. 签名文件内容JSON 格式)
{
"config_file": "secrets.yaml",
"signature": "-----BEGIN PGP SIGNATURE-----...",
"signed_at": "2026-03-26T10:00:00Z",
"issuer": "AstrBot/1.0"
}
```
#### 验签流程
```
启动时:
1. 检查 gpg.yaml 中 verify_mode
2. 对 signed_configs 中的每个文件:
a. 读取配置文件
b. 读取对应的 .sig 签名文件
c. 使用 trusted_keys 中的公钥验签
3. 根据 verify_mode 处理结果:
- none: 跳过验签
- warn: 验签失败记录警告日志
- enforce: 验签失败阻止启动并报错
```
### 目录结构
```
$XDG_CONFIG_HOME/astrbot/
├── gpg.yaml # GPG 安全配置
├── secrets.yaml # 包含 GPG 密钥
├── secrets.yaml.sig # 配置文件签名
├── agent.yaml
├── agent.yaml.sig
└── ...
```
### 命令行接口
```bash
# 签名配置文件
astrbot gpg sign --config <file> [--output <dir>]
# 验签配置文件
astrbot gpg verify --config <file>
# 验签所有已签名配置
astrbot gpg verify --all
# 导出公钥
astrbot gpg export --keyring <fingerprint> [--output <file>]
# 生成新密钥对
astrbot gpg generate --name "AstrBot" --email "astrbot@example.com"
```
### 安全原则
| 原则 | 说明 |
|------|------|
| **私钥保护** | 私钥仅存储在 secrets.yaml 中,不提交到版本控制 |
| **密钥分离** | GPG 签名密钥与 API 密钥分离存储 |
| **验签优先** | 生产环境建议使用 `verify_mode: enforce` |
| **密钥轮换** | 定期更换签名密钥并重新签名配置文件 |
## 敏感信息隔离
### 原则
| 原则 | 说明 |
|------|------|
| **密钥不上传** | `secrets.yaml` 必须在 `.gitignore` 中 |
| **环境变量注入** | 生产环境通过环境变量注入密钥 |
| **不记录日志** | 配置值不写入日志 |
### secrets.yaml
```yaml
# 安全配置 - 请勿提交到版本控制
# Security config - DO NOT commit to version control
# Dashboard 凭证
dashboard:
password: "your-hashed-password"
jwt_secret: "your-jwt-secret"
# LLM Provider API Keys
providers:
openai:
api_key: "sk-..."
anthropic:
api_key: "sk-ant-..."
dashscope:
api_key: "..."
# 平台凭证
platforms:
telegram:
bot_token: "12345:ABC..."
discord:
bot_token: "..."
# 第三方服务
third_party:
baidu_aip:
app_id: ""
api_key: ""
secret_key: ""
moonshotai:
api_key: ""
coze:
api_key: ""
bot_id: ""
# API KeysHTTP API 服务认证)
api:
keys:
- id: "my-app"
key: "sk-xxxxxxxxxxxx"
name: "My Application"
permissions:
- "chat"
- "file"
- "message"
rate_limit:
requests: 100
period: 60
```
### 环境变量注入
敏感配置通过环境变量覆盖:
```bash
# .env 或系统环境变量
ASTRBOT_DASHBOARD_PASSWORD=xxx
ASTRBOT_DASHBOARD_JWT_SECRET=xxx
ASTRBOT_PROVIDER_OPENAI_API_KEY=sk-xxx
ASTRBOT_PROVIDER_ANTHROPIC_API_KEY=sk-ant-xxx
ASTRBOT_PLATFORM_TELEGRAM_BOT_TOKEN=xxx
```
## 配置文件结构
### 1. system.yaml系统配置
```yaml
# 系统配置
log:
level: INFO # DEBUG, INFO, WARNING, ERROR
file_enable: false
file_path: logs/astrbot.log
file_max_mb: 20
disable_access_log: true
proxy:
http_proxy: ""
https_proxy: ""
no_proxy:
- localhost
- 127.0.0.1
trace:
enable: false
log_enable: false
log_path: logs/astrbot.trace.log
temp:
dir_max_size: 1024 # MB
timezone: Asia/Shanghai
pypi_index_url: https://mirrors.aliyun.com/pypi/simple/
pip_install_arg: ""
# 回调配置
callback_api_base: ""
# API 服务器配置
api:
host: "0.0.0.0"
port: 6185
timeout: 60
max_body_size: 100
rate_limit:
default:
requests: 100
period: 60
high:
requests: 1000
period: 60
```
### 2. platform.yaml平台配置
```yaml
# 平台配置
# 通用平台设置
platform_settings:
unique_session: false
rate_limit:
time: 60
count: 30
strategy: "stall" # stall, reject
reply_prefix: ""
forward_threshold: 1500
enable_id_white_list: true
id_whitelist: []
id_whitelist_log: true
wl_ignore_admin_on_group: true
wl_ignore_admin_on_friend: true
reply_with_mention: false
reply_with_quote: false
path_mapping: []
segmented_reply:
enable: false
only_llm_result: true
interval_method: "random"
interval: "1.5,3.5"
words_count_threshold: 150
split_mode: "regex"
regex: ".*?[。?!~…]+|.+$"
split_words: ["。", "", "", "~", "…"]
no_permission_reply: true
empty_mention_waiting: true
empty_mention_waiting_need_reply: true
friend_message_needs_wake_prefix: false
ignore_bot_self_message: false
ignore_at_all: false
# 平台列表
platforms: []
# 平台特定配置
platform_specific:
lark:
pre_ack_emoji:
enable: false
emojis: ["Typing"]
telegram:
pre_ack_emoji:
enable: false
emojis: ["✍️"]
discord:
pre_ack_emoji:
enable: false
emojis: ["🤔"]
```
### 3. providers.yaml提供商配置
```yaml
# LLM 提供商配置
provider_settings:
enable: true
default_provider_id: ""
fallback_chat_models: []
default_image_caption_provider_id: ""
image_caption_prompt: "Please describe the image using Chinese."
provider_pool: ["*"]
# API Keys 在 secrets.yaml 中配置
# providers:
# openai:
# api_key: "sk-..." # 在 secrets.yaml 中
# model: "gpt-4"
# base_url: "" # 可选,自定义 endpoint
```
### 4. agent.yamlAgent 配置)
```yaml
# Agent 配置
agent_settings:
wake_prefix: ["/"]
default_personality: "default"
persona_pool: ["*"]
prompt_prefix: "{{prompt}}"
# 上下文配置
context_limit_reached_strategy: "truncate_by_turns"
llm_compress_instruction: "..."
llm_compress_keep_recent: 6
llm_compress_provider_id: ""
max_context_length: -1
dequeue_context_length: 1
# 输出配置
streaming_response: false
show_tool_use_status: false
show_tool_call_result: false
sanitize_context_by_modalities: false
max_quoted_fallback_images: 20
# Web Search
web_search: false
websearch_provider: "default"
# websearch_tavily_key: 在 secrets.yaml 中
# websearch_bocha_key: 在 secrets.yaml 中
web_search_link: false
# 标识
identifier: false
group_name_display: false
datetime_system_prompt: true
# Agent Runner
agent_runner_type: "local" # local, dify, coze, dashscope, deerflow
dify_agent_runner_provider_id: ""
coze_agent_runner_provider_id: ""
dashscope_agent_runner_provider_id: ""
deerflow_agent_runner_provider_id: ""
# 工具配置
unsupported_streaming_strategy: "realtime_segmenting"
reachability_check: false
max_agent_step: 30
tool_call_timeout: 60
tool_schema_mode: "full"
# 安全模式
llm_safety_mode: true
safety_mode_strategy: "system_prompt"
# 主动能力
proactive_capability:
add_cron_tools: true
# Computer Use
computer_use_runtime: "none"
computer_use_require_admin: true
# 图片压缩
image_compress_enabled: true
image_compress_options:
max_size: 1024
quality: 95
# 子 Agent 编排
subagent_orchestrator:
main_enable: false
remove_main_duplicate_tools: false
router_system_prompt: "..."
agents: []
# STT/TTS
provider_stt_settings:
enable: false
provider_id: ""
provider_tts_settings:
enable: false
provider_id: ""
dual_output: false
use_file_service: false
trigger_probability: 1.0
# LTM
provider_ltm_settings:
group_icl_enable: false
group_message_max_cnt: 300
image_caption: false
image_caption_provider_id: ""
active_reply:
enable: false
method: "possibility_reply"
possibility_reply: 0.1
whitelist: []
```
### 5. plugins/(插件配置)
各插件配置在 `plugins/` 目录下,文件名与插件名对应:
```
plugins/
├── _conf_schema.json # 插件配置 schema
├── my_plugin.yaml # my_plugin 插件配置
└── another_plugin.yaml # another_plugin 插件配置
```
## 目录结构
```
$XDG_CONFIG_HOME/astrbot/
├── config.yaml # 主配置入口
├── secrets.yaml # 安全配置(不提交)
├── system.yaml # 系统配置
├── platform.yaml # 平台配置
├── providers.yaml # 提供商配置
├── agent.yaml # Agent 配置
├── gpg.yaml # GPG 安全配置(可选)
├── plugins/ # 插件配置
│ ├── _conf_schema.json # 插件 schema
│ └── *.yaml # 各插件配置
└── mcp_servers.json # MCP 服务器配置
```
## 配置加载优先级
```
环境变量 > secrets.yaml > config.yaml > 默认值
```
| 优先级 | 来源 | 说明 |
|--------|------|------|
| 最高 | 环境变量 | `ASTRBOT_*` 前缀 |
| 高 | secrets.yaml | 敏感信息 |
| 中 | config.yaml | 用户配置 |
| 低 | 默认值 | 代码中的默认值 |
## 配置迁移
旧版 `cmd_config.json` 迁移到新版:
| 旧字段 | 新配置文件 | 说明 |
|--------|------------|------|
| `dashboard.*` | `secrets.yaml` | 密码、JWT secret |
| `provider_settings.websearch_*_key` | `secrets.yaml` | API keys |
| `platform_settings.*` | `platform.yaml` | 平台设置 |
| `provider_settings.*` | `providers.yaml` + `agent.yaml` | 提供商和 Agent |
| `provider_stt/tts_settings.*` | `agent.yaml` | STT/TTS |
| `content_safety.baidu_aip.*` | `secrets.yaml` | 安全密钥 |
| `log_level`, `http_proxy` | `system.yaml` | 系统配置 |
| `plugin_set`, `kb_*` | `plugins/` | 插件配置 |
## 不规范示例
**旧方式** - 所有配置混在一起:
```json
{
"dashboard": {
"password": "plain-text-or-hashed",
"jwt_secret": "secret-in-config"
},
"provider_settings": {
"openai_api_key": "sk-xxx"
}
}
```
**新方式** - 分离关注点:
```
config/
├── secrets.yaml # 密码、密钥
├── system.yaml # 日志、代理
├── platform.yaml # 平台设置
├── providers.yaml # 提供商配置
└── agent.yaml # Agent 配置
```
## 实施建议
1. **新增配置**:新功能应按域分类到对应文件
2. **敏感信息**:绝不写入 `config.yaml`,使用 `secrets.yaml` 或环境变量
3. **默认值**:在代码中设置默认值,配置文件仅覆盖需要修改的项
4. **Schema 验证**:使用 JSON Schema 验证配置文件格式

View File

@@ -155,6 +155,58 @@
---
## API 服务器配置 / API Server
### `ASTRBOT_API_ENABLE`
| 属性 | 值 |
|------|-----|
| 说明 | 启用 HTTP API 服务器 |
| 类型 | boolean |
| 默认 | `true` |
### `ASTRBOT_API_TIMEOUT`
| 属性 | 值 |
|------|-----|
| 说明 | API 请求超时时间(秒) |
| 类型 | integer |
| 默认 | `60` |
### `ASTRBOT_API_MAX_BODY_SIZE`
| 属性 | 值 |
|------|-----|
| 说明 | API 最大请求体大小MB |
| 类型 | integer |
| 默认 | `100` |
### `ASTRBOT_API_KEY`
| 属性 | 值 |
|------|-----|
| 说明 | 默认 API Key用于开发环境 |
| 类型 | string |
| 注意 | 建议通过 secrets.yaml 配置 |
### `ASTRBOT_CALLBACK_API_BASE`
| 属性 | 值 |
|------|-----|
| 说明 | Webhook 回调基础 URL |
| 类型 | url |
| 示例 | `https://your-server.com/callback` |
### `ASTRBOT_CALLBACK_SECRET`
| 属性 | 值 |
|------|-----|
| 说明 | Webhook 回调签名密钥 |
| 类型 | string |
| 注意 | 用于验证回调请求签名 |
---
## SSL/TLS 配置
### `ASTRBOT_SSL_ENABLE`
@@ -268,6 +320,67 @@
---
## GPG 安全配置 / GPG Security
### `ASTRBOT_GPG_ENABLE`
| 属性 | 值 |
|------|-----|
| 说明 | 启用 GPG 配置签名与验签功能 |
| 类型 | boolean |
| 默认 | `false` |
### `ASTRBOT_GPG_VERIFY_MODE`
| 属性 | 值 |
|------|-----|
| 说明 | 验签模式 |
| 类型 | enum |
| 默认 | `warn` |
| 可选值 | `none` (不验签), `warn` (仅警告), `enforce` (阻止启动) |
### `ASTRBOT_GPG_TRUSTED_KEYS`
| 属性 | 值 |
|------|-----|
| 说明 | 信任的 GPG 公钥指纹列表(逗号分隔) |
| 类型 | string |
| 示例 | `ABCDEF1234567890...,FEDCBA0987654321...` |
### `ASTRBOT_GPG_SIGNED_CONFIGS`
| 属性 | 值 |
|------|-----|
| 说明 | 需要验签的配置文件列表(逗号分隔) |
| 类型 | string |
| 示例 | `secrets.yaml,agent.yaml,platform.yaml` |
### `ASTRBOT_GPG_GNUPG_HOME`
| 属性 | 值 |
|------|-----|
| 说明 | GPG 主目录路径(默认使用系统配置) |
| 类型 | path |
| 示例 | `/home/user/.gnupg` |
### `ASTRBOT_GPG_PRIVATE_KEY`
| 属性 | 值 |
|------|-----|
| 说明 | GPG 私钥ASCII Armor 格式),用于签名 |
| 类型 | string |
| 注意 | 建议通过 secrets.yaml 配置,优先使用环境变量 |
### `ASTRBOT_GPG_PASSPHRASE`
| 属性 | 值 |
|------|-----|
| 说明 | GPG 私钥密码短语 |
| 类型 | string |
| 注意 | 建议通过 secrets.yaml 配置,优先使用环境变量 |
---
## 命名规范
### 前缀规则
@@ -276,6 +389,7 @@
|------|------|
| `ASTRBOT_` | AstrBot 核心配置 |
| `ASTRBOT_SSL_` | SSL/TLS 配置 |
| `ASTRBOT_GPG_` | GPG 安全配置 |
| `ASTRBOT_INSTANCE_` | 实例相关 |
| `http_proxy`, `https_proxy`, `no_proxy` | 标准代理变量(无前缀) |

View File

@@ -42,6 +42,7 @@ AstrBot 遵循 [XDG Base Directory Specification](https://specifications.freedes
```
/var/lib/astrbot/
├── config/ # 配置文件
│ └── mcp_servers.json # MCP 服务器配置
├── data/ # 插件、skills 等
│ ├── plugins/
│ ├── plugin_data/
@@ -62,6 +63,36 @@ AstrBot 遵循 [XDG Base Directory Specification](https://specifications.freedes
└── pid # PID 文件
```
### MCP 服务器配置
MCP 服务器配置位于 `config/mcp_servers.json`
```json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
"env": {}
},
"http-server": {
"url": "http://localhost:3000/mcp",
"transport": "sse"
}
}
}
```
**配置字段**
| 字段 | 类型 | 说明 |
|------|------|------|
| `command` | string | 启动命令stdio 传输) |
| `args` | array | 命令参数 |
| `env` | object | 环境变量 |
| `url` | string | 服务器 URLHTTP 传输) |
| `transport` | string | 传输类型:`stdio` / `sse` / `streamable_http` |
## 平台差异
| 平台 | 配置目录 | 数据目录 | 缓存目录 | 运行时目录 |
@@ -86,7 +117,6 @@ AstrBot 遵循 [XDG Base Directory Specification](https://specifications.freedes
```
$XDG_DATA_HOME/astrbot/
├── data/ # 应用数据
│ ├── config/ # 配置文件
│ ├── plugins/ # 插件目录
│ ├── plugin_data/ # 插件数据
│ ├── skills/ # Skills 目录
@@ -101,7 +131,8 @@ $XDG_DATA_HOME/astrbot/
$XDG_CONFIG_HOME/astrbot/
├── .env # 环境变量
── config.yaml # 主配置文件
── config.yaml # 主配置文件
└── mcp_servers.json # MCP 服务器配置
```
## 路径映射
@@ -283,6 +314,47 @@ $XDG_DATA_HOME/astrbot/<instance_name>/
所有平台统一使用 `ASTRBOT_*` 环境变量覆盖默认路径,优先级最高。
## 与 Claude Code 目录对比
Claude Code 使用 `~/.claude/` 作为根目录,**不遵循 XDG 规范**
```
~/.claude/ # Claude Code 根目录
├── backups/ # 备份
├── cache/ # 缓存
├── downloads/ # 下载
├── file-history/ # 文件历史
├── paste-cache/ # 粘贴缓存
├── plans/ # 计划
├── plugins/ # 插件
├── projects/ # 项目会话
├── session-env/ # 会话环境
├── sessions/ # 会话
├── shell-snapshots/ # Shell 快照
├── skills/ # Skills
├── tasks/ # 任务
├── teams/ # 团队
├── history.jsonl # 对话历史
├── settings.json # 设置
└── settings.local.json # 本地设置
```
### 关键差异
| 维度 | Claude Code | AstrBot |
|------|-------------|---------|
| 规范 | 非标准(固定 `~/.claude/` | XDG 规范 |
| 配置 | 根目录直接放配置文件 | 分离到 `$XDG_CONFIG_HOME/` |
| 缓存 | 根目录下 `cache/` | `$XDG_CACHE_HOME/` |
| 数据 | 根目录直接放数据文件 | 分离到 `$XDG_DATA_HOME/` |
| 会话 | `sessions/`, `session-env/` | `$XDG_STATE_HOME/` |
| 项目 | `projects/` | 内嵌在数据目录 |
### 设计哲学
- **Claude Code**:简单直接,所有内容在 `~/.claude/`
- **AstrBot**:遵循 XDG数据分类清晰配置/数据/缓存/状态分离)
## 已知限制
- Windows 平台**不遵循** XDG 规范,使用 Windows 标准路径