From 0a7945a2a03ab4515a06729c1223d567f681b240 Mon Sep 17 00:00:00 2001 From: xunxiing <110362831+xunxiing@users.noreply.github.com> Date: Thu, 23 Apr 2026 08:06:58 +0000 Subject: [PATCH] docs: automated sync from main repo --- docs/snapshots/index.md | 5 + docs/snapshots/v4.23.3/SKILL.md | 130 +++++ .../v4.23.3/Storage & Utils/file_storage.md | 29 ++ .../v4.23.3/Storage & Utils/kv_storage.md | 21 + .../v4.23.3/Storage & Utils/text_to_image.md | 109 +++++ docs/snapshots/v4.23.3/agent/Invoke-llm.md | 86 ++++ .../v4.23.3/agent/agent-related-hooks.md | 88 ++++ docs/snapshots/v4.23.3/agent/agent-runner.md | 17 + .../v4.23.3/agent/context-compression.md | 50 ++ docs/snapshots/v4.23.3/agent/conversation.md | 52 ++ docs/snapshots/v4.23.3/agent/cron.md | 57 +++ docs/snapshots/v4.23.3/agent/index.md | 56 +++ .../v4.23.3/agent/offical-tool-list/tools.md | 43 ++ .../v4.23.3/agent/persona-resolution.md | 53 ++ docs/snapshots/v4.23.3/agent/persona-sets.md | 72 +++ docs/snapshots/v4.23.3/agent/registe tools.md | 111 +++++ docs/snapshots/v4.23.3/agent/sandbox.md | 48 ++ docs/snapshots/v4.23.3/agent/subagents.md | 70 +++ .../design_standards/architecture_overview.md | 21 + .../design_standards/best_practices.md | 43 ++ .../v4.23.3/design_standards/context_usage.md | 30 ++ .../v4.23.3/design_standards/core_concepts.md | 128 +++++ .../v4.23.3/design_standards/event_flow.md | 20 + .../v4.23.3/design_standards/sandbox.md | 20 + .../v4.23.3/design_standards/visual_utils.md | 89 ++++ .../v4.23.3/en/deploy/astrbot/package.md | 24 + .../dev/star/guides/listen-message-event.md | 436 +++++++++++++++++ .../v4.23.3/en/dev/star/guides/storage.md | 32 ++ docs/snapshots/v4.23.3/en/use/command.md | 110 +++++ docs/snapshots/v4.23.3/en/use/computer.md | 139 ++++++ docs/snapshots/v4.23.3/index.md | 16 + docs/snapshots/v4.23.3/messages/components.md | 41 ++ docs/snapshots/v4.23.3/messages/events.md | 33 ++ docs/snapshots/v4.23.3/messages/model.md | 31 ++ docs/snapshots/v4.23.3/messages/umo.md | 22 + .../platform_adapters/adapter_interface.md | 374 +++++++++++++++ .../platform_adapters/message_conversion.md | 29 ++ .../platform_adapters/telegram_media_group.md | 40 ++ .../plugin_config/command_management.md | 34 ++ .../v4.23.3/plugin_config/decorators.md | 30 ++ .../v4.23.3/plugin_config/file_config.md | 30 ++ docs/snapshots/v4.23.3/plugin_config/hooks.md | 93 ++++ .../v4.23.3/plugin_config/lifecycle.md | 32 ++ .../snapshots/v4.23.3/plugin_config/schema.md | 81 ++++ .../v4.23.3/plugin_config/session_control.md | 31 ++ .../providers/agent-runners/deerflow.md | 57 +++ .../v4.23.3/zh/deploy/astrbot/package.md | 24 + .../dev/star/guides/listen-message-event.md | 452 ++++++++++++++++++ .../v4.23.3/zh/dev/star/guides/storage.md | 32 ++ docs/snapshots/v4.23.3/zh/use/command.md | 106 ++++ docs/snapshots/v4.23.3/zh/use/computer.md | 137 ++++++ scripts/state.json | 4 +- 52 files changed, 3916 insertions(+), 2 deletions(-) create mode 100644 docs/snapshots/index.md create mode 100644 docs/snapshots/v4.23.3/SKILL.md create mode 100644 docs/snapshots/v4.23.3/Storage & Utils/file_storage.md create mode 100644 docs/snapshots/v4.23.3/Storage & Utils/kv_storage.md create mode 100644 docs/snapshots/v4.23.3/Storage & Utils/text_to_image.md create mode 100644 docs/snapshots/v4.23.3/agent/Invoke-llm.md create mode 100644 docs/snapshots/v4.23.3/agent/agent-related-hooks.md create mode 100644 docs/snapshots/v4.23.3/agent/agent-runner.md create mode 100644 docs/snapshots/v4.23.3/agent/context-compression.md create mode 100644 docs/snapshots/v4.23.3/agent/conversation.md create mode 100644 docs/snapshots/v4.23.3/agent/cron.md create mode 100644 docs/snapshots/v4.23.3/agent/index.md create mode 100644 docs/snapshots/v4.23.3/agent/offical-tool-list/tools.md create mode 100644 docs/snapshots/v4.23.3/agent/persona-resolution.md create mode 100644 docs/snapshots/v4.23.3/agent/persona-sets.md create mode 100644 docs/snapshots/v4.23.3/agent/registe tools.md create mode 100644 docs/snapshots/v4.23.3/agent/sandbox.md create mode 100644 docs/snapshots/v4.23.3/agent/subagents.md create mode 100644 docs/snapshots/v4.23.3/design_standards/architecture_overview.md create mode 100644 docs/snapshots/v4.23.3/design_standards/best_practices.md create mode 100644 docs/snapshots/v4.23.3/design_standards/context_usage.md create mode 100644 docs/snapshots/v4.23.3/design_standards/core_concepts.md create mode 100644 docs/snapshots/v4.23.3/design_standards/event_flow.md create mode 100644 docs/snapshots/v4.23.3/design_standards/sandbox.md create mode 100644 docs/snapshots/v4.23.3/design_standards/visual_utils.md create mode 100644 docs/snapshots/v4.23.3/en/deploy/astrbot/package.md create mode 100644 docs/snapshots/v4.23.3/en/dev/star/guides/listen-message-event.md create mode 100644 docs/snapshots/v4.23.3/en/dev/star/guides/storage.md create mode 100644 docs/snapshots/v4.23.3/en/use/command.md create mode 100644 docs/snapshots/v4.23.3/en/use/computer.md create mode 100644 docs/snapshots/v4.23.3/index.md create mode 100644 docs/snapshots/v4.23.3/messages/components.md create mode 100644 docs/snapshots/v4.23.3/messages/events.md create mode 100644 docs/snapshots/v4.23.3/messages/model.md create mode 100644 docs/snapshots/v4.23.3/messages/umo.md create mode 100644 docs/snapshots/v4.23.3/platform_adapters/adapter_interface.md create mode 100644 docs/snapshots/v4.23.3/platform_adapters/message_conversion.md create mode 100644 docs/snapshots/v4.23.3/platform_adapters/telegram_media_group.md create mode 100644 docs/snapshots/v4.23.3/plugin_config/command_management.md create mode 100644 docs/snapshots/v4.23.3/plugin_config/decorators.md create mode 100644 docs/snapshots/v4.23.3/plugin_config/file_config.md create mode 100644 docs/snapshots/v4.23.3/plugin_config/hooks.md create mode 100644 docs/snapshots/v4.23.3/plugin_config/lifecycle.md create mode 100644 docs/snapshots/v4.23.3/plugin_config/schema.md create mode 100644 docs/snapshots/v4.23.3/plugin_config/session_control.md create mode 100644 docs/snapshots/v4.23.3/providers/agent-runners/deerflow.md create mode 100644 docs/snapshots/v4.23.3/zh/deploy/astrbot/package.md create mode 100644 docs/snapshots/v4.23.3/zh/dev/star/guides/listen-message-event.md create mode 100644 docs/snapshots/v4.23.3/zh/dev/star/guides/storage.md create mode 100644 docs/snapshots/v4.23.3/zh/use/command.md create mode 100644 docs/snapshots/v4.23.3/zh/use/computer.md diff --git a/docs/snapshots/index.md b/docs/snapshots/index.md new file mode 100644 index 0000000..2104e4f --- /dev/null +++ b/docs/snapshots/index.md @@ -0,0 +1,5 @@ +# 文档快照 + +这里存放按 AstrBot Tag 归档的文档快照。 + +- [v4.23.3](/snapshots/v4.23.3/) diff --git a/docs/snapshots/v4.23.3/SKILL.md b/docs/snapshots/v4.23.3/SKILL.md new file mode 100644 index 0000000..a6e5862 --- /dev/null +++ b/docs/snapshots/v4.23.3/SKILL.md @@ -0,0 +1,130 @@ +--- +name: skill-astrbot-dev +description: Reference + workflow notes for AstrBot plugin development (messages, platform adapters, plugin config, agent system). +metadata: + short-description: AstrBot dev reference +--- +# skill-astrbot-dev + +This skill is the source-of-truth index for AstrBot developer docs in this repo (`docs/`). + +Goal: when this skill is selected, immediately ground on the minimum required docs + code entrypoints, +avoid duplicated reading, and always prefer code as the final authority. + +## When to use + +Use this skill when you ask for help with: + +- AstrBot plugin structure, decorators/hooks, lifecycle, schema, sessions +- Message model/event flow and message-chain conversion +- Platform adapter interface and message conversion patterns +- Agent topics (tools/providers/personas/subagents/sandbox/cron/context compression) + +## Mandatory workflow (use this every time) + +1. Start from a single entrypoint (avoid broad loading): + - Site index: `docs/index.md` + - Core concepts: `docs/design_standards/core_concepts.md` +2. Pick one topic folder and stay focused: + - Agent system: `docs/agent/` + - Plugin config: `docs/plugin_config/` + - Messages: `docs/messages/` + - Platform adapters: `docs/platform_adapters/` +3. For Agent Runner (v4.7.0+): `docs/agent/agent-runner.md` +3. If the user targets a specific AstrBot version, cross-check: + - `docs/snapshots//` +4. If docs and code disagree, treat code as truth: + - Core code lives under `astrbotcore/astrbot/core/` (read only the needed files) + +## STRONGLY ADVISED: use AstrBot SDK while writing plugins + +When writing plugin code, strongly advised to install AstrBot SDK locally and use it for API reference, +signature lookup, and IDE auto-completion. + +```powershell +python -m pip install -U astrbot +``` + +Use SDK symbols first when implementing hooks, provider/context calls, and agent runner integration. +This helps reduce guesswork and signature mismatch. + +If AstrBot source code in this repo is available, still treat repo code as higher priority than package docs. + +## Plugin project structure (strongly advised) + +A standard AstrBot plugin project should include: + +- `main.py`: entrypoint. Implement plugin startup and primary features here. +- `metadata.yaml`: plugin metadata (name, version, author, repo, description). +- `README.md`: installation, usage, feature overview, and dev links. +- `.gitignore`: ignore Python cache (`__pycache__`) and IDE config files. +- `LICENSE`: open-source license file. + +## `metadata.yaml` minimal template + +```yaml +name: astrbot_plugin_helloworld # 插件唯一识别名,最好以 astrbot_plugin_ 前缀开头 +display_name: helloworld # 展示名(v4.5.0+) +desc: AstrBot 插件示例。 # 插件简短描述 +version: v1.3.0 # 版本号:v1.1.1 或 v1.1 +author: Soulter # 作者 +repo: https://github.com/Soulter/helloworld # 插件的仓库地址 +``` + +## Code rules for plugin implementation + +- Use `async def` for handlers/hooks/tool functions. +- Keep `main.py` focused on plugin entry and orchestration; extract complex logic into submodules. +- Add type hints for public methods and hook signatures. +- Do not hardcode provider IDs or secrets; expose configurable fields in `_conf_schema.json`. +- Prefer small, testable functions over large monolithic handler bodies. +- Keep README and metadata consistent with actual plugin behavior and version. +-If you are writing AstrBot core code instead of plugins, you must submit a PR to https://github.com/AstrBotDevs/AstrBot-docs if the changes require doc updates (for instance: new hooks, new APIs, new features, platform adapter changes, and so on). If you don't see the docs repo, please remind the user to clone the docs-repo and add it to the workspace. +## Hooks: avoid missing / outdated references + +There are two different "hook" layers you must not mix up: + +- Plugin event hooks (decorators): `docs/plugin_config/hooks.md` +- Agent runner hooks (`BaseAgentRunHooks`): `docs/agent/agent-related-hooks.md` + +If you need a complete hook inventory (because context may be truncated), generate it locally: + +```powershell +python scripts/generate_hook_inventory.py +``` + +This writes to `docs/.tmp/hook_inventory/` (gitignored). Use it as a scratchpad for writing/updating docs; +do not reference `.tmp` paths as public documentation URLs. + +## High-signal code entrypoints (open only when needed) + +- Event hooks registration + signatures: `astrbotcore/astrbot/core/star/register/star_handler.py` +- Event types: `astrbotcore/astrbot/core/star/star_handler.py` +- Agent runners + hook call order: `astrbotcore/astrbot/core/agent/runners/` +- Agent hook interface: `astrbotcore/astrbot/core/agent/hooks.py` +- Main agent build (sandbox/cron/tools): `astrbotcore/astrbot/core/astr_main_agent.py` +- Skills system (AstrBot runtime skills): `astrbotcore/astrbot/core/skills/skill_manager.py` +- Subagents config loading: `astrbotcore/astrbot/core/subagent_orchestrator.py` + +## v4.5.7+ New Tool Definition Pattern + +推荐使用 dataclass 模式定义 Tool(见 `docs/design_standards/core_concepts.md` 第7节): + +```python +from pydantic.dataclasses import dataclass +from astrbot.core.agent.tool import FunctionTool + +@dataclass +class MyTool(FunctionTool): + name: str = "my_tool" + description: str = "工具描述" + parameters: dict = {...} + + async def call(self, context, **kwargs) -> str: + return "结果" +``` + +注册:`self.context.add_llm_tools(MyTool())` + +装饰器方式仍然支持,但推荐新项目使用 dataclass 模式。 + diff --git a/docs/snapshots/v4.23.3/Storage & Utils/file_storage.md b/docs/snapshots/v4.23.3/Storage & Utils/file_storage.md new file mode 100644 index 0000000..a248d0d --- /dev/null +++ b/docs/snapshots/v4.23.3/Storage & Utils/file_storage.md @@ -0,0 +1,29 @@ +--- +category: storage +--- + +# 文件存储规范 + +对于大文件、日志或插件特有的资源文件,AstrBot 建议遵循以下存储规范。 + +### 目录规范 + +所有插件特有的文件应存储在以下目录: +`data/plugin_data/{plugin_name}/` + +### 获取存储路径 + +建议在插件中使用以下方式获取路径,以确保兼容性: + +```python +from astrbot.core.utils.astrbot_path import get_astrbot_data_path + +# 获取插件专属数据目录 +plugin_data_path = get_astrbot_data_path() / "plugin_data" / self.name +plugin_data_path.mkdir(parents=True, exist_ok=True) # 确保目录存在 +``` + +### 注意事项 + +- 不要将大文件直接存储在 `docs/` 或插件根目录下。 +- 建议定期清理不再使用的临时文件。 diff --git a/docs/snapshots/v4.23.3/Storage & Utils/kv_storage.md b/docs/snapshots/v4.23.3/Storage & Utils/kv_storage.md new file mode 100644 index 0000000..62e98f3 --- /dev/null +++ b/docs/snapshots/v4.23.3/Storage & Utils/kv_storage.md @@ -0,0 +1,21 @@ +--- +category: storage +--- + +# 键值对存储 (KV Storage) + +AstrBot 为插件提供了简单易用的 KV 存储接口,适合存储配置、轻量级状态或用户数据。 + +### 核心接口 (>= v4.9.2) + +这些方法在插件类(继承自 `Star`)中可以直接调用: + +- `await self.put_kv_data(key: str, value: Any)`: 存储数据。 +- `await self.get_kv_data(key: str, default: Any = None) -> Any`: 获取数据。 +- `await self.delete_kv_data(key: str)`: 删除数据。 + +### 特点 + +- **隔离性**: 数据按插件 ID 隔离,不同插件之间的 Key 不会冲突。 +- **持久化**: 数据会自动持久化到 `data/metadata/kv_storage.db`(或相应目录)。 +- **异步**: 接口均为异步方法。 diff --git a/docs/snapshots/v4.23.3/Storage & Utils/text_to_image.md b/docs/snapshots/v4.23.3/Storage & Utils/text_to_image.md new file mode 100644 index 0000000..a3203a9 --- /dev/null +++ b/docs/snapshots/v4.23.3/Storage & Utils/text_to_image.md @@ -0,0 +1,109 @@ +# 文转图 (Text to Image) + +将文本或 HTML 模板渲染为图片。 + +## 插件方法(Star) + +### `text_to_image` + +```python +async def text_to_image(self, text: str, return_url: bool = True) -> str +``` + +- 内部调用:`html_renderer.render_t2i(...)` +- 使用当前激活模板:`t2i_active_template` +- `return_url=True` 返回可发送的 URL;`False` 返回本地文件路径 +- **网络渲染失败会自动 fallback 到本地渲染** + +```python +url = await self.text_to_image("你好,AstrBot") +yield event.image_result(url) +``` + +### `html_render` + +```python +async def html_render(self, tmpl: str, data: dict, return_url: bool = True, options: dict | None = None) -> str +``` + +- 内部调用:`html_renderer.render_custom_template(...)` +- 适合自定义 HTML + Jinja2 模板渲染 + +```python +tmpl = """ +
+

{{ title }}

+ +
+""" +url = await self.html_render(tmpl, {"title": "Todo", "items": ["吃饭", "睡觉"]}) +yield event.image_result(url) +``` + +## SDK 方法(`html_renderer`) + +```python +from astrbot.api import html_renderer +``` + +### 初始化 + +```python +await html_renderer.initialize() +``` + +### 默认文转图 + +```python +await html_renderer.render_t2i( + text: str, + use_network: bool = True, + return_url: bool = False, + template_name: str | None = None, +) +``` + +- `use_network=True` 先走网络渲染;失败时 fallback 到本地渲染 +- `return_url=False` 时返回本地路径 + +### 自定义模板渲染 + +```python +await html_renderer.render_custom_template( + tmpl_str: str, + tmpl_data: dict, + return_url: bool = False, + options: dict | None = None, +) +``` + +## 渲染选项(`html_render` / `render_custom_template`) + +`options` 透传给截图参数(Playwright 风格): + +- `timeout` +- `type`: `"jpeg" | "png"` +- `quality`(仅 jpeg) +- `omit_background`(仅 png) +- `full_page` +- `clip` +- `animations`: `"allow" | "disabled"` +- `caret`: `"hide" | "initial"` +- `scale`: `"css" | "device"` + +默认值(未传 `options` 时): + +```python +{"full_page": True, "type": "jpeg", "quality": 40} +``` + +## 模板管理方法 + +`TemplateManager` 提供模板 CRUD: + +- `list_templates()` +- `get_template(name)` +- `create_template(name, content)` +- `update_template(name, content)` +- `delete_template(name)` +- `reset_default_template()` diff --git a/docs/snapshots/v4.23.3/agent/Invoke-llm.md b/docs/snapshots/v4.23.3/agent/Invoke-llm.md new file mode 100644 index 0000000..6229426 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/Invoke-llm.md @@ -0,0 +1,86 @@ +Provider 是模型能力入口(Chat/STT/TTS/Embedding) + +### 获取当前会话使用的 Chat Provider ID + +```python +prov_id = await ctx.get_current_chat_provider_id(umo) +``` +- `await get_current_chat_provider_id(umo: str) -> str`:返回当前会话的 chat provider ID + +### 简化 LLM 调用 + +```python +llm_resp = await ctx.llm_generate( + chat_provider_id=prov_id, + prompt="Hello!", + system_prompt="You are a helpful assistant.", +) +print(llm_resp.completion_text) +``` +- `await llm_generate(chat_provider_id, prompt, contexts=None, image_urls=None, system_prompt=None, tools=None) -> LLMResponse`: 简化的 LLM 调用接口,不自动执行 tool call + +### 工具循环 Agent + +```python +llm_resp = await ctx.tool_loop_agent( + event=event, + chat_provider_id=prov_id, + prompt="搜索 AstrBot 相关信息", + tools=ToolSet([SearchTool()]), + max_steps=30, + tool_call_timeout=60, +) +``` +- `await tool_loop_agent(event, chat_provider_id, prompt, contexts=None, image_urls=None, tools=None, system_prompt=None, max_steps=30, tool_call_timeout=120, **kwargs) -> LLMResponse` + - `event`: AstrMessageEvent,会话上下文来源 + - `chat_provider_id`: chat provider ID + - `prompt`: 用户 prompt + - `contexts`: 消息历史上下文(可选,追加到 prompt 后) + - `image_urls`: 图片 URL 列表(追加到 prompt) + - `tools`: ToolSet,AI 可调用的工具集 + - `system_prompt`: 系统提示(插到上下文最前面) + - `max_steps`: 最大 tool call 轮次,默认 30 + - `tool_call_timeout`: 单次工具调用超时(秒),默认 120 + - **`**kwargs`**: 扩展参数: + - `stream: bool` — 是否流式输出 + - `agent_hooks: BaseAgentRunHooks` — Agent 运行期钩子 + - `agent_context: AstrAgentContext` — 复用已有 agent 上下文 + - 其他 kwargs — 直接透传给 `runner.reset()` + +## 传统方法 + +### 当前会话正在使用的 Provider + +- `get_using_provider(umo: str | None = None) -> Provider | None`: 拿 chat provider 实例 +- `get_using_stt_provider(umo: str | None = None) -> STTProvider | None` +- `get_using_tts_provider(umo: str | None = None) -> TTSProvider | None` + +### 按 ID 读取 Provider + +- `get_provider_by_id(provider_id: str)`: 按 ID 获取 provider(可能是 chat/stt/tts/embedding/rerank) + +```python +prov = ctx.get_provider_by_id("your_provider_id") +``` +### 列表查询(用于配置页或校验) + +- `get_all_providers() -> list[Provider]` +- `get_all_stt_providers() -> list[STTProvider]` +- `get_all_tts_providers() -> list[TTSProvider]` +- `get_all_embedding_providers() -> list[EmbeddingProvider]` + +## Agent Runner 相关 + +```python +# 获取当前会话使用的 Agent Runner +runner = ctx.get_using_agent_runner(umo=event.unified_msg_origin) + +# 或者通过 ID 获取 +runner = ctx.get_agent_runner_by_id(runner_id="your_runner_id") +``` +```## + +- 会话内调用必须优先传 `umo`,否则会回退到默认配置,可能拿到错误 provider +- `get_provider_by_id` 返回的不一定是 chat provider,传给 `tool_loop_agent` 前要确保是 chat provider id +- 不要把 provider id 硬编码在代码里,优先从 `_conf_schema.json` 配置读取 +``` diff --git a/docs/snapshots/v4.23.3/agent/agent-related-hooks.md b/docs/snapshots/v4.23.3/agent/agent-related-hooks.md new file mode 100644 index 0000000..6db27f7 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/agent-related-hooks.md @@ -0,0 +1,88 @@ +--- +category: agent +--- +# Agent Related Hooks + + Agent 请求/工具循环直接相关的 hooks。 + +## Plugin Hooks + +### LLM 请求阶段 + +- `@filter.on_waiting_llm_request()` +- `@filter.on_llm_request()` +- `@filter.on_llm_response()` + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import ProviderRequest, LLMResponse + +@filter.on_waiting_llm_request() +async def on_waiting(self, event: AstrMessageEvent) -> None: ... + +@filter.on_llm_request() +async def on_req(self, event: AstrMessageEvent, request: ProviderRequest) -> None: ... + +@filter.on_llm_response() +async def on_resp(self, event: AstrMessageEvent, response: LLMResponse) -> None: ... +``` + +### Tool 调用阶段 + +- `@filter.on_using_llm_tool()` +- `@filter.on_llm_tool_respond()` + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool +from mcp.types import CallToolResult + +@filter.on_using_llm_tool() +async def on_tool_start(self, event: AstrMessageEvent, tool: FunctionTool, tool_args: dict | None) -> None: ... + +@filter.on_llm_tool_respond() +async def on_tool_end(self, event: AstrMessageEvent, tool: FunctionTool, tool_args: dict | None, tool_result: CallToolResult | None) -> None: ... +``` + +### 结果发送阶段 + +- `@filter.on_decorating_result()` +- `@filter.after_message_sent()` + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_decorating_result() +async def on_decorating(self, event: AstrMessageEvent) -> None: ... + +@filter.after_message_sent() +async def after_sent(self, event: AstrMessageEvent) -> None: ... +``` + +## Agent Runner Hooks + +用于 `context.tool_loop_agent(..., agent_hooks=...)` 的运行期扩展。 + +```python +from astrbot.core.agent.hooks import BaseAgentRunHooks +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.agent.tool import FunctionTool +from astrbot.core.provider.entities import LLMResponse +import mcp + +class MyAgentHooks(BaseAgentRunHooks): + async def on_agent_begin(self, run_context: ContextWrapper) -> None: ... + async def on_tool_start(self, run_context: ContextWrapper, tool: FunctionTool, tool_args: dict | None) -> None: ... + async def on_tool_end(self, run_context: ContextWrapper, tool: FunctionTool, tool_args: dict | None, tool_result: mcp.types.CallToolResult | None) -> None: ... + async def on_agent_done(self, run_context: ContextWrapper, llm_response: LLMResponse) -> None: ... +``` + +## 主 Agent 默认映射关系 + +- `on_tool_start` -> `@filter.on_using_llm_tool()` +- `on_tool_end` -> `@filter.on_llm_tool_respond()` +- `on_agent_done` -> `@filter.on_llm_response()` + +## MUST + +- Hook 处理函数必须使用 `async def`。 diff --git a/docs/snapshots/v4.23.3/agent/agent-runner.md b/docs/snapshots/v4.23.3/agent/agent-runner.md new file mode 100644 index 0000000..803b389 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/agent-runner.md @@ -0,0 +1,17 @@ +Agent Runner 是 AstrBot 中用于执行 Agent 的组件。 + +## 插件侧使用 + +```python +# 获取当前会话使用的 Agent Runner +runner = self.context.get_using_agent_runner(umo=event.unified_msg_origin) + +# 或者通过 provider_id 获取 +runner = self.context.get_agent_runner_by_id(runner_id="your_runner_id") +``` + +## 注意事项 + +- Agent Runner 会调用 Chat Provider 接口 +- 切换 Agent Runner 后,部分 AstrBot 功能(MCP、知识库、网页搜索)可能不可用(取决于 Runner 实现) +- AstrBot 内置 Agent Runner 支持全部功能 diff --git a/docs/snapshots/v4.23.3/agent/context-compression.md b/docs/snapshots/v4.23.3/agent/context-compression.md new file mode 100644 index 0000000..086ff43 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/context-compression.md @@ -0,0 +1,50 @@ + +# 上下文控制与压缩 + + +## 1 `context.tool_loop_agent(...)` + +用于运行 Agent 工具循环,同时传入上下文压缩参数。 + +```python +await self.context.tool_loop_agent(event=event, chat_provider_id=prov_id, prompt="...", enforce_max_turns=20, truncate_turns=2, llm_compress_keep_recent=6) +``` + +可用压缩参数(都可选): + +- `enforce_max_turns: int`:最多保留多少轮对话(`-1` 不限制)。 +- `truncate_turns: int`:触发截断时一次丢弃多少轮。 +- `llm_compress_instruction: str | None`:LLM 压缩时的摘要指令。 +- `llm_compress_keep_recent: int`:LLM 压缩时保留最近多少条消息不摘要。 +- `llm_compress_provider: Provider | None`:用于压缩摘要的模型 provider。 +- `custom_token_counter: TokenCounter | None`:自定义 token 计数器。 +- `custom_compressor: ContextCompressor | None`:自定义压缩器。 + +## 2`context.get_provider_by_id(provider_id)` + +用于拿到压缩模型实例,再传给 `llm_compress_provider`。 + +```python +compress_prov = self.context.get_provider_by_id("openai/gpt-4o-mini") +``` + +## 3 `context.get_current_chat_provider_id(umo)` + +用于获取当前会话正在使用的对话 provider id,常用于给 `tool_loop_agent` 传 `chat_provider_id`。 + +```python +chat_provider_id = await self.context.get_current_chat_provider_id(event.unified_msg_origin) +``` + +## 4 `context.get_config(umo)` +用于读取当前会话配置,按需决定压缩参数。 +```python +cfg = self.context.get_config(event.unified_msg_origin) +``` +## 示例 +```python +umo = event.unified_msg_origin +chat_prov = await self.context.get_current_chat_provider_id(umo) +compress_prov = self.context.get_provider_by_id("your_compress_provider_id") +resp = await self.context.tool_loop_agent(event=event, chat_provider_id=chat_prov, prompt="总结最近讨论并给出下一步", enforce_max_turns=24, truncate_turns=2, llm_compress_instruction="保留任务结论、待办、关键约束", llm_compress_keep_recent=8, llm_compress_provider=compress_prov) +``` \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/agent/conversation.md b/docs/snapshots/v4.23.3/agent/conversation.md new file mode 100644 index 0000000..2da96ab --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/conversation.md @@ -0,0 +1,52 @@ +--- +category: agent +--- + +# 会话与对话分支(Conversation) + +插件侧通过 `self.context.conversation_manager` 管理会话分支;会话标识使用 `event.unified_msg_origin`(`umo`)。 + +## 插件可用入口 + +```python +conv_mgr = self.context.conversation_manager +umo = event.unified_msg_origin +``` + +## ConversationManager 可用方法 + +- `register_on_session_deleted(callback: Callable[[str], Awaitable[None]]) -> None`:注册会话删除后的级联清理回调。 +- `new_conversation(unified_msg_origin: str, platform_id: str | None = None, content: list[dict] | None = None, title: str | None = None, persona_id: str | None = None) -> str`:新建分支并切换为当前分支。 +- `switch_conversation(unified_msg_origin: str, conversation_id: str) -> None`:切换当前分支。 +- `delete_conversation(unified_msg_origin: str, conversation_id: str | None = None) -> None`:删除指定分支;不传 `conversation_id` 时删除当前分支。 +- `delete_conversations_by_user_id(unified_msg_origin: str) -> None`:删除该会话下全部分支。 +- `get_curr_conversation_id(unified_msg_origin: str) -> str | None`:读取当前分支 ID。 +- `get_conversation(unified_msg_origin: str, conversation_id: str, create_if_not_exists: bool = False) -> Conversation | None`:读取分支对象。 +- `get_conversations(unified_msg_origin: str | None = None, platform_id: str | None = None) -> list[Conversation]`:列出分支。 +- `get_filtered_conversations(page: int = 1, page_size: int = 20, platform_ids: list[str] | None = None, search_query: str = "", **kwargs) -> tuple[list[Conversation], int]`:分页 + 条件过滤。 +- `update_conversation(unified_msg_origin: str, conversation_id: str | None = None, history: list[dict] | None = None, title: str | None = None, persona_id: str | None = None, token_usage: int | None = None) -> None`:更新历史/标题/persona/token_usage。 +- `add_message_pair(cid: str, user_message: UserMessageSegment | dict, assistant_message: AssistantMessageSegment | dict) -> None`:向指定分支追加一组 user/assistant 消息。 +- `get_human_readable_context(unified_msg_origin: str, conversation_id: str, page: int = 1, page_size: int = 10) -> tuple[list[str], int]`:获取分页后的可读上下文。 + +## 最小示例 + +```python +cid = await self.context.conversation_manager.get_curr_conversation_id(event.unified_msg_origin) +``` + +```python +cid = await self.context.conversation_manager.new_conversation(event.unified_msg_origin, title="新分支") +``` + +```python +await self.context.conversation_manager.update_conversation(event.unified_msg_origin, conversation_id=cid, title="重命名", persona_id="assistant_default") +``` + +```python +contexts, total_pages = await self.context.conversation_manager.get_human_readable_context(event.unified_msg_origin, cid, page=1, page_size=10) +``` + +## MUST + +- 所有分支操作必须使用当前会话的 `umo`,不要跨会话复用 `conversation_id`。 +- 更新历史时必须传 OpenAI 风格 `list[dict]` 消息结构。 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/agent/cron.md b/docs/snapshots/v4.23.3/agent/cron.md new file mode 100644 index 0000000..e9ead65 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/cron.md @@ -0,0 +1,57 @@ +--- +category: agent +--- + +# Cron + +定时执行逻辑或唤醒 AI。AI 任务触发生成 `CronMessageEvent`(继承自 AstrMessageEvent)。 + +通过 `self.context.cron_manager` 调用。 + +## 注册 Python 函数(Basic Job) + +```python +await cron_mgr.add_basic_job( + name="任务名", + cron_expression="*/5 * * * *", + handler=self.your_method, + payload={"key": "value"}, + persistent=False, + description="任务描述", + handler_params={"extra": "data"}, + enabled=True, +) +``` + +- `name: str`: 任务唯一标识名 +- `cron_expression: str`: 标准 cron 表达式(5 段,`分 时 日 月 周`) +- `handler: Callable`: Python 异步处理函数 +- `payload: dict`: 传给 handler 的上下文数据 +- `persistent: bool`: 是否持久化(重启后保留,依赖 DB) +- `description: str`: 任务描述(v4.22.2 新增) +- `handler_params: dict`: 额外参数,合并到 payload(v4.22.2 新增) +- `enabled: bool`: 是否启用(v4.22.2 新增) + +## 注册 AI 唤醒(Active Agent Job) + +```python +await cron_mgr.add_active_job( + name="AI 定时任务", + cron_expression="0 8 * * *", + payload={"session": "UMO", "note": "指令"}, + run_once=False, + description="每日早报", +) +``` + +- `name: str`: 任务唯一标识名 +- `cron_expression: str`: 标准 cron 表达式 +- `payload: dict`: 包含 `session`(UMO)、`note`(唤醒指令) +- `run_once: bool`: 是否只执行一次 +- `description: str`: 任务描述(v4.22.2 新增) + +## 维护方法 + +- `delete_job(job_id: str)`: 删除任务 +- `list_jobs(job_type: str = None) -> list[CronJob]`: 列出任务(可选过滤 basic/active) +- `update_job(job_id: str, **kwargs) -> CronJob | None`: 更新任务(只支持部分字段) diff --git a/docs/snapshots/v4.23.3/agent/index.md b/docs/snapshots/v4.23.3/agent/index.md new file mode 100644 index 0000000..1c80977 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/index.md @@ -0,0 +1,56 @@ +--- +category: agent +--- + +# Agent 系统概览 + +在 AstrBot 中,"Agent"指的是:**指令/系统提示(instructions)+ 工具(tools)+ 模型提供商(providers)+ 运行时能力(上下文管理 / 子智能体 / 沙盒 / 定时任务)** 的组合。 + +本目录把原先以 "LLM" 为中心的内容重组为 "Agent" 视角:LLM/VLM/Embedding 等都被视为 Provider 能力的一部分,工具与运行时能力决定了 Agent 的上限与安全边界。 + +## 你大概率会从这里开始 + +- 需要让模型调用工具:`docs/agent/registe tools.md` +- 需要选模型/Embedding/STT/TTS:`docs/agent/providers.md` +- 需要控制上下文与压缩:`docs/agent/context-compression.md` +- 需要 Hook(事件钩子/Agent 钩子):`docs/agent/agent-related-hooks.md` +- 需要子智能体:`docs/agent/subagents.md` +- 需要代码方式注册子智能体:`docs/agent/agent-registration.md` +- 需要沙盒(computer use):`docs/agent/sandbox.md` +- 需要定时任务(主动能力):`docs/agent/cron.md` +- **v4.7.0+ Agent Runner 架构(Dify/Coze/DeerFlow)**:`docs/agent/agent-runner.md` + +## 最短示例:工具循环 Agent + +```python +llm_resp = await self.context.tool_loop_agent( + event=event, + chat_provider_id=prov_id, + prompt="把这段需求拆成 3 个可执行步骤,并给出每步输出。", + tools=ToolSet([MyTool()]), + max_steps=10, + tool_call_timeout=60, + system_prompt="你是一个严谨的工程助手。", +) +``` + +### 关键参数(只记这几个就够用) + +- `chat_provider_id`:对话模型 provider id(LLM/VLM 的入口通常在这里) +- `tools`:可用工具集合(`FunctionTool` / handoff tool / 运行时注入工具) +- `max_steps`:限制循环次数,避免无限工具调用 +- `tool_call_timeout`:单个工具调用超时 +- `system_prompt`:定义 Agent 角色、边界与输出格式 + +### v4.22.2 扩展参数 + +`tool_loop_agent` 的 `**kwargs` 支持: +- `stream: bool` — 流式输出 +- `agent_hooks: BaseAgentRunHooks` — Agent 运行期钩子 +- `agent_context: AstrAgentContext` — 复用已有 agent 上下文 + +## 相关源码入口(以代码为准) + +- Agent runner(工具循环):`astrbotcore/astrbot/core/agent/runners/tool_loop_agent_runner.py` +- Agent hooks 接口:`astrbotcore/astrbot/core/agent/hooks.py` +- 主 Agent 构建(沙盒/定时工具注入/安全模式等):`astrbotcore/astrbot/core/astr_main_agent.py` diff --git a/docs/snapshots/v4.23.3/agent/offical-tool-list/tools.md b/docs/snapshots/v4.23.3/agent/offical-tool-list/tools.md new file mode 100644 index 0000000..595d9cd --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/offical-tool-list/tools.md @@ -0,0 +1,43 @@ +--- +category: agent +--- +# AstrBot 官方 Tool 列表 + + AstrBot Core 内置工具 + +## Computer Use + +- `astrbot_execute_shell`(`computer_use_runtime=sandbox|local`):执行 Shell 命令。 + - 示例参数:`{"command":"pwd","background":false}` +- `astrbot_execute_ipython`(`computer_use_runtime=sandbox`):在沙盒 IPython 执行代码。 + - 示例参数:`{"code":"print(1+1)","silent":false}` +- `astrbot_execute_python`(`computer_use_runtime=local`):在本地 Python 执行代码(仅管理员)。 + - 示例参数:`{"code":"print(1+1)","silent":false}` +- `astrbot_upload_file`(`computer_use_runtime=sandbox`):上传本地文件到沙盒。 + - 示例参数:`{"local_path":"C:/tmp/a.txt"}` +- `astrbot_download_file`(`computer_use_runtime=sandbox`):从沙盒下载文件。 + - 示例参数:`{"remote_path":"/workspace/out.txt","also_send_to_user":true}` + +## Knowledge Base + +- `astr_kb_search`(`kb_agentic_mode=true`):检索知识库内容。 + - 示例参数:`{"query":"AstrBot provider isolation"}` + +## Cron / Proactive Task + +- `create_future_task`(`add_cron_tools=true`):创建未来任务(周期或一次性)。 + - 示例参数:`{"note":"明早提醒我同步日报","cron_expression":"0 9 * * *"}` +- `delete_future_task`(`add_cron_tools=true`):删除未来任务。 + - 示例参数:`{"job_id":"cron_xxx"}` +- `list_future_tasks`(`add_cron_tools=true`):列出未来任务。 + - 示例参数:`{"job_type":"active_agent"}` + +## Proactive Message + +- `send_message_to_user`(平台支持主动消息时注入):主动向用户发送消息。 + - 示例参数:`{"messages":[{"type":"plain","text":"任务已完成"}]}` + +## Dynamic Handoff Tool + +- `transfer_to_`(`subagent_orchestrator.main_enable=true`):将任务移交给子智能体。 + - 示例参数:`{"input":"请处理这段文本并给出结构化结论"}` diff --git a/docs/snapshots/v4.23.3/agent/persona-resolution.md b/docs/snapshots/v4.23.3/agent/persona-resolution.md new file mode 100644 index 0000000..4a83a36 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/persona-resolution.md @@ -0,0 +1,53 @@ +--- +category: agent +--- + +# 人格解析与优先级(Persona Resolution) +系统按以下顺序解析 `persona_id`,命中即停止: + +1. 会话级:`session_service_config.persona_id`(`umo` 作用域) +2. 对话分支级:`conversation.persona_id` +3. 全局默认:`provider_settings.default_personality` + +## 插件可用入口 + +```python +umo = event.unified_msg_origin +conv_mgr = self.context.conversation_manager +``` + +## 1设置会话级 persona(最高优先级) + +使用 SDK `sp` 读写 `session_service_config`: + +```python +from astrbot.api import sp + +cfg = await sp.get_async(scope="umo", scope_id=umo, key="session_service_config", default={}) or {} +cfg["persona_id"] = "assistant_default" +await sp.put_async(scope="umo", scope_id=umo, key="session_service_config", value=cfg) +``` + +## 2设置对话分支级 persona + +```python +cid = await conv_mgr.get_curr_conversation_id(umo) +await conv_mgr.update_conversation(umo, conversation_id=cid, persona_id="assistant_default") +``` + +## 3 显式禁用人格注入 + +将 `persona_id` 设置为 `"[%None]"`(会话级或分支级都可): + +```python +await conv_mgr.update_conversation(umo, conversation_id=cid, persona_id="[%None]") +``` + +## 运行时行为要点 + +- 命中 persona 后会注入: + - `persona.prompt` -> `system_prompt` + - `persona._begin_dialogs_processed` -> 上下文前置消息 +- `webchat` 平台下,若未命中 persona 且 `persona_id != "[%None]"`,会追加 ChatUI 默认人格提示词。 +- 读写 `session_service_config` 时必须先读后改再写回,避免覆盖掉同键下其他字段(如 `llm_enabled` / `tts_enabled`)。 +- 会话操作必须使用当前 `umo`,不要跨会话复用 `conversation_id`。 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/agent/persona-sets.md b/docs/snapshots/v4.23.3/agent/persona-sets.md new file mode 100644 index 0000000..12de20d --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/persona-sets.md @@ -0,0 +1,72 @@ +--- +category: agent +--- + +# Persona 集管理(插件可用) + +Persona 用于控制系统提示词、开场对话、可用 tools/skills。插件侧入口:`self.context.persona_manager`。 + +## 快速入口 + +```python +pm = self.context.persona_manager +umo = event.unified_msg_origin +``` + +## 核心操作(高频) + +### 读取 + +- `get_persona(persona_id: str)`:读取单个 persona。 +- `get_all_personas() -> list[Persona]`:列出全部 persona。 +- `get_default_persona_v3(umo: str | MessageSession | None = None) -> Personality`:读取默认 persona(按会话配置解析)。 + +### 新建 + +- `create_persona(persona_id, system_prompt, begin_dialogs=None, tools=None, skills=None, folder_id=None, sort_order=0) -> Persona` + +```python +await pm.create_persona( + persona_id="astrbot_plugin_writer", + system_prompt="你是一个技术写作助手。", + begin_dialogs=["你是谁?", "我是你的写作助手。"], + tools=None, + skills=None, +) +``` + +### 更新 + +- `update_persona(persona_id, system_prompt=None, begin_dialogs=None, tools=None, skills=None)` + +```python +# 只改 prompt 时,先读旧值再回填 tools/skills,避免被重置 +old = await pm.get_persona("astrbot_plugin_writer") +await pm.update_persona( + persona_id="astrbot_plugin_writer", + system_prompt="你是一个精炼的技术写作助手。", + tools=old.tools, + skills=old.skills, +) +``` + +### 删除 + +- `delete_persona(persona_id: str) -> None` + +## 文件夹管理(按需) + +- `create_folder / get_folder / get_folders / get_all_folders` +- `update_folder / delete_folder / get_folder_tree` +- `move_persona_to_folder / get_personas_by_folder / batch_update_sort_order` + + + + +## tips + +- `create_persona` 和 `update_persona` 参数不完全一致: + - `create` 有 `folder_id`、`sort_order`;`update` 没有。 +- `tools` / `skills` 语义:`None` = 全部可用,`[]` = 全部禁用 +- `begin_dialogs` 必须是偶数条 +- `_conf_schema.json`中有选择人设的配置 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/agent/registe tools.md b/docs/snapshots/v4.23.3/agent/registe tools.md new file mode 100644 index 0000000..539a0f9 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/registe tools.md @@ -0,0 +1,111 @@ +--- +category: agent +--- + +# Tools(函数调用) + +Tool 是让大语言模型调用外部能力(检索、计算、执行命令、文件处理)的机制。 + +## 两种定义方式 + +- 类方式:继承 FunctionTool +- 装饰器方式:@filter.llm_tool(...) + +## 方式一:类定义 Tool(推荐,v4.5.7+) + +`python +from pydantic import Field +from pydantic.dataclasses import dataclass + +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.agent.tool import FunctionTool, ToolExecResult +from astrbot.core.astr_agent_context import AstrAgentContext + + +@dataclass +class BilibiliTool(FunctionTool[AstrAgentContext]): + name: str = "bilibili_videos" + description: str = "搜索 Bilibili 视频" + parameters: dict = Field( + default_factory=lambda: { + "type": "object", + "properties": { + "keywords": { + "type": "string", + "description": "搜索关键词", + } + }, + "required": ["keywords"], + } + ) + + async def call( + self, + context: ContextWrapper[AstrAgentContext], + **kwargs, + ) -> ToolExecResult: + return ToolExecResult(result="搜索结果...") +` + +**ToolExecResult 返回值格式(v4.22.2):** + +`python +from astrbot.core.agent.tool import ToolExecResult + +return ToolExecResult(result="文本结果") +return ToolExecResult(is_error=True, result="错误信息") +return ToolExecResult(result="", image_url="https://...") # 图片结果 +` + +## 注册到全局工具池 + +`python +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + self.context.add_llm_tools(BilibiliTool()) +` + +注册后主对话模型自动感知并调用该 Tool。 + +## 方式二:装饰器(兼容旧版) + +`python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.llm_tool(name="get_weather") +async def get_weather(self, event: AstrMessageEvent, location: str): + """获取天气信息。 + + Args: + location(string): 地点 + """ + resp = self.get_weather_from_api(location) + yield event.plain_result("天气信息: " + resp) +` + +Docstring 中 Args 格式必须是 参数名(类型): 描述。 + +支持的类型:string、 +umber、object、oolean、rray、rray[string](v4.5.7+)。 + +## 内部 Tool(不注册全局) + +仅在单次 ool_loop_agent 调用中可见,不进入全局工具池: + +`python +from astrbot.core.agent.tool import ToolSet + +llm_resp = await self.context.tool_loop_agent( + event=event, + chat_provider_id=await self.context.get_current_chat_provider_id(event.unified_msg_origin), + prompt="请调用 bilibili_videos 工具搜索 AstrBot 教程", + tools=ToolSet([BilibiliTool()]), +) +` + +## Tips + +- parameters 必须是合法 JSON Schema +- 装饰器方式必须写规范 docstring(尤其 Args),否则 schema 解析失败 +- 推荐新项目使用类定义方式,参数类型检查更严格 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/agent/sandbox.md b/docs/snapshots/v4.23.3/agent/sandbox.md new file mode 100644 index 0000000..ba1e833 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/sandbox.md @@ -0,0 +1,48 @@ +--- +category: agent +--- + +# Sandbox(插件可用) + +Sandbox 是 Agent 的计算机使用运行时(shell/python/文件上传下载)。 + +## 快速入口 + +```python +ctx = self.context +umo = event.unified_msg_origin +``` +## 底层方法(给 booter/工具实现使用) + +- `get_booter(context, session_id)` +- `get_local_booter()` +- `booter.shell.exec(command, cwd=None, env=None, timeout=30, shell=True, background=False)` +- `booter.python.exec(code, kernel_id=None, timeout=30, silent=False)` +- `booter.upload_file(path, file_name)` +- `booter.download_file(remote_path, local_path)` +- `booter.available()` + +## UMO 与当前 Sandbox 的绑定规则 + +- 当前会话标识使用 `event.unified_msg_origin`。 +- 工具执行时用 `event.unified_msg_origin` 调用 `get_booter(...)` 获取当前会话 booter。 +- `get_booter` 内部按 `session_id` 缓存:`session_booter[session_id]`。 +- 若缓存实例 `available()` 为 false,会先移除再重建。 +- `get_booter` 会读取 `context.get_config(umo=session_id)`,因此会话级配置可生效。 + +## 配置键(常用) + +- `provider_settings.computer_use_runtime`: `none | local | sandbox` +- `provider_settings.sandbox.booter`: `shipyard | boxlite` +- `provider_settings.sandbox.shipyard_endpoint` +- `provider_settings.sandbox.shipyard_access_token` +- `provider_settings.sandbox.shipyard_ttl` +- `provider_settings.sandbox.shipyard_max_sessions` + +## 易翻车点 + +- `shipyard_endpoint` 或 `shipyard_access_token` 缺失时,sandbox 工具不会注入。 +- `astrbot_execute_shell` 要求 `admin` 角色,否则返回 permission denied。 +- `astrbot_download_file(..., also_send_to_user=True)` 会发送后删除本地临时文件。 +- `local` 与 `sandbox` 是两套运行时:`local` 走 `get_local_booter()`,`sandbox` 走 `get_booter(..., umo)`。 + diff --git a/docs/snapshots/v4.23.3/agent/subagents.md b/docs/snapshots/v4.23.3/agent/subagents.md new file mode 100644 index 0000000..2ad5ee3 --- /dev/null +++ b/docs/snapshots/v4.23.3/agent/subagents.md @@ -0,0 +1,70 @@ +--- +category: agent +--- + +# Subagents(子智能体 / Handoff) + +Subagent 是给主 Agent 使用的 handoff 工具。主模型通过 `transfer_to_` 把任务转交给子智能体执行。 +`from astrbot.api import agent`(详见 `docs/agent/agent-registration.md`) + +## 配置式(推荐) + +### 最小配置 + +```json +{ + "subagent_orchestrator": { + "main_enable": true, + "remove_main_duplicate_tools": false, + "router_system_prompt": "You are a task router...", + "agents": [ + { + "enabled": true, + "name": "writer", + "public_description": "负责技术文档整理与重写", + "persona_id": null, + "system_prompt": "你是文档子智能体,输出精简且结构化。", + "provider_id": "openai_gpt4o_mini", + "tools": ["search_docs", "rewrite_text"] + } + ] + } +} +``` + +### `agents[]` 字段(源码对齐) + +- `enabled`: 是否启用 +- `name`: 子智能体名;工具名会生成为 `transfer_to_` +- `public_description`: 暴露给主模型的工具描述(决定主模型是否愿意调用) +- `persona_id`: 可选;存在时优先使用 persona 的 `system_prompt/begin_dialogs/tools` +- `system_prompt`: 未命中 persona 时使用 +- `provider_id`: 可选;子智能体专用 chat provider 覆盖 +- `tools`: 子智能体可用工具名列表(字符串) + +## 运行规则 + +- `main_enable=true` 时,主 Agent 会把所有 handoff 工具加入工具集。 +- `remove_main_duplicate_tools=true` 时,会把“已分配给子智能体”的同名工具从主 Agent 工具集移除。 +- `router_system_prompt` 会拼接到主 Agent 的 `system_prompt`。 +- `provider_id` 不为空时,handoff 执行优先用该 provider;否则回退当前会话 provider。 + +## SDK/代码式(高级) + +```python +from astrbot.api import agent + +@agent(name="writer", instruction="你是写作子智能体。") +async def writer_agent(event): + return None +``` + +> 代码式注册、`run_hooks`、专属工具挂载见:`docs/agent/agent-registration.md` + +## MUST + +- `name` 必须非空,且在同一实例中保持唯一。 +- `public_description` 必须写“适用任务”,不要写空泛人设。 +- `tools` 必须显式写成字符串列表(不要依赖隐式行为)。 + + diff --git a/docs/snapshots/v4.23.3/design_standards/architecture_overview.md b/docs/snapshots/v4.23.3/design_standards/architecture_overview.md new file mode 100644 index 0000000..4c6eec1 --- /dev/null +++ b/docs/snapshots/v4.23.3/design_standards/architecture_overview.md @@ -0,0 +1,21 @@ +--- +category: design_standards +--- + +# 核心架构综述 + +AstrBot 采用基于**插件化 (Plugin-based)** 和 **事件驱动 (Event-driven)** 的架构。其核心(Core)负责协调各个管理器(Manager),并通过 `Context` 对象向插件(Star)暴露能力。 + +### 核心管理器分工 + +- **`PluginManager`**: 负责插件的加载、卸载、重载以及元数据管理。 +- **`PlatformManager`**: 管理所有已接入的消息平台适配器,负责分发事件。 +- **`ProviderManager`**: 管理大语言模型(LLM)、语音识别(STT)、语音合成(TTS)等服务提供商。 +- **`ConversationManager`**: 管理用户会话历史、上下文存储及切换。 +- **`PersonaManager`**: 管理人格设定(Persona),包括系统提示词(System Prompt)和工具配置。 + +### 核心设计原则 + +1. **解耦**: 核心系统与平台适配器、AI 提供商、插件之间高度解耦。 +2. **统一模型**: 所有的平台消息都被转化为统一的 `AstrBotMessage` 模型。 +3. **插件化**: 功能尽可能通过插件实现,核心仅提供基础调度能力。 diff --git a/docs/snapshots/v4.23.3/design_standards/best_practices.md b/docs/snapshots/v4.23.3/design_standards/best_practices.md new file mode 100644 index 0000000..845cc5e --- /dev/null +++ b/docs/snapshots/v4.23.3/design_standards/best_practices.md @@ -0,0 +1,43 @@ +--- +category: design_standards +--- + +# AI 插件开发最佳实践 + +为了确保插件的稳定性、安全性和易用性,建议遵循以下实践方案。 + +### 1. 异常处理 + +务必捕获可能的异常,并给用户明确的反馈。 + +```python +try: + # 逻辑代码 +except TimeoutError: + yield event.plain_result("⌛ 会话已超时,请重新开始。") +except Exception as e: + logger.error(f"插件执行出错: {e}") + yield event.plain_result(f"❌ 发生错误: {e}") +finally: + event.stop_event() # 已经处理过错误,通常建议停止事件继续传播 +``` + +### 2. 平台差异化 + +虽然 AstrBot 提供了统一模型,但在调用底层 SDK 功能(如 `call_action`)时,需进行环境检查: + +```python +if event.get_platform_name() == "aiocqhttp": + # 调用 OneBot 特有 API + pass +``` + +### 3. 工具 (Tools) 开发 + +- 推荐使用 `agent-as-tool` 模式。 +- 完善 Docstring,这直接决定了大模型对工具的理解能力。 +- 尽量保持工具功能的单一性。 + +### 4. 资源清理 + +在插件卸载时,应在 `terminate()` 方法中清理定时器、数据库连接或文件句柄。 diff --git a/docs/snapshots/v4.23.3/design_standards/context_usage.md b/docs/snapshots/v4.23.3/design_standards/context_usage.md new file mode 100644 index 0000000..22dc222 --- /dev/null +++ b/docs/snapshots/v4.23.3/design_standards/context_usage.md @@ -0,0 +1,30 @@ +--- +category: design_standards +--- + +# Context 对象使用规范 + +`Context` 对象是 AstrBot 的能力中枢,在插件初始化时通过 `__init__` 注入。它是插件与系统核心交互的唯一桥梁。 + +### 重要属性 + +通过 `self.context` 可以访问各个管理器: + +- `self.context.conversation_manager`: 会话管理器。 +- `self.context.persona_manager`: 人格管理器。 +- `self.context.platform_manager`: 平台管理器。 +- `self.context.provider_manager`: 提供商管理器。 + +### 核心方法 + +#### 消息与平台相关 +- `send_message(umo: str, message_chain: MessageChain)`: 向指定源主动发送消息。 +- `get_platform(platform_type: PlatformAdapterType)`: 获取指定类型的平台实例。 + +#### AI 与工具相关 +- `add_llm_tools(*tools)`: 动态注册函数工具。 +- `get_using_provider(umo)`: 获取当前使用的 LLM 提供商。 + +#### 配置与插件 +- `get_config(umo=None)`: 获取当前配置。 +- `get_all_stars()`: 获取所有已加载插件的元数据。 diff --git a/docs/snapshots/v4.23.3/design_standards/core_concepts.md b/docs/snapshots/v4.23.3/design_standards/core_concepts.md new file mode 100644 index 0000000..58549b4 --- /dev/null +++ b/docs/snapshots/v4.23.3/design_standards/core_concepts.md @@ -0,0 +1,128 @@ +# AstrBot 核心概念 API 清单 + +本文档仅列出 AstrBot 插件开发的核心功能 API。 + +### 1. 装饰器 (Decorators) + +- `@register(id, author, description, version, repo_url)`: 注册插件。 +- `@filter.command(name, alias, priority)`: 注册指令。支持带参函数。 +- `@filter.command_group(name)`: 注册指令组。 +- `@filter.event_message_type(type)`: 过滤消息类型 (`ALL`, `PRIVATE_MESSAGE`, `GROUP_MESSAGE`)。 +- `@filter.platform_adapter_type(type)`: 过滤平台类型 (如 `AIOCQHTTP`, `TELEGRAM`)。 +- `@filter.permission_type(type)`: 校验权限 (如 `ADMIN`)。 +- `@filter.regex(pattern)`: 正则匹配。 +- `@filter.llm_tool(name)`: 注册为 AI 可调用的工具。 +- `@session_waiter(timeout, record_history_chains)`: 等待下一条用户消息。 + +### 2. 消息组件 (Message Components) + +- `Plain(text)`: 纯文本。 +- `At(user_id)`: 提及用户。 +- `Image.fromFileSystem(path)` / `Image.fromURL(url)`: 图片。 +- `Record.fromFileSystem(path)`: 语音。 +- `Video.fromFileSystem(path)` / `Video.fromURL(url)`: 视频。 +- `File.fromFileSystem(path, name)`: 文件。 +- `Face(id)`: 系统表情。 +- `Reply(message_id)`: 回复特定消息。 +- `Node(uin, name, content)` / `Nodes(nodes)`: 合并转发节点 (部分平台支持)。 + +### 3. 核心对象与方法 + +**AstrMessageEvent (事件对象)** + +- 消息事件对象,包含消息内容、发送者信息、群组信息等。 +- 提供消息发送和结果构建方法。 + +**Context (核心枢纽)** + +- `context.send_message(umo, chain)`: 向指定源主动发送消息。 +- `context.get_platform(type)`: 获取指定类型的平台实例。 +- `context.get_using_provider(umo)`: 获取当前 LLM 提供商。 +- `context.add_llm_tools(*tools)`: 动态注册 AI 工具。 + +**v4.5.7+ 新增 LLM API** + +```python +umo = event.unified_msg_origin +prov_id = await self.context.get_current_chat_provider_id(umo) +``` + +- `await context.get_current_chat_provider_id(umo) -> str`: 获取当前会话使用的 chat provider ID。 +- `await context.llm_generate(chat_provider_id, prompt, contexts=None, system_prompt=None, tools=None) -> LLMResponse`: 简化的 LLM 调用。 +- `await context.tool_loop_agent(event, chat_provider_id, prompt, tools, system_prompt=None, max_steps=30, tool_call_timeout=60) -> LLMResponse`: 工具循环 Agent。 + +**MessageChain (消息链构建器)** + +- `MessageChain().message(text)`: 添加文本。 +- `MessageChain().file_image(path)`: 添加图片文件。 +- `MessageChain().at(user_id)`: 添加 At。 + +### 4. 存储与工具 (Storage & Utils) + +- `await self.get_kv_data(key, default)`: 获取插件隔离的 KV 数据。 +- `await self.put_kv_data(key, value)`: 存储插件隔离的 KV 数据。 +- `await self.delete_kv_data(key)`: 删除 KV 数据。 +- `await self.html_render(html_text=None, url=None, data=None, options=None)`: 将 HTML 字符串或网页渲染为图片。基于 Playwright。 +- `text_to_image(text)`: 将文字转为图片。 + +### 5. 系统钩子 (Hooks) + +Hooks 分为两层,不建议在"概念清单"里重复列举具体 hook 名单(容易过时): + +- 插件事件钩子(`@filter.on_*`):见 `docs/plugin_config/hooks.md` +- Agent 运行钩子(`BaseAgentRunHooks`):见 `docs/agent/agent-related-hooks.md` + +### 6. Agent 智能体 + +- Agent 相关能力(tools / providers / persona / sandbox / cron / subagents):见 `docs/agent/` +- `context.tool_loop_agent(...)`: 调用工具循环 Agent(可结合子智能体 handoff) +- v4.7.0+ Agent Runner 架构:见 `docs/agent/agent-runner.md` + +### 7. Tool 定义 (v4.5.7+ 推荐) + +推荐使用 dataclass 模式定义 Tool: + +```python +from pydantic import Field +from pydantic.dataclasses import dataclass +from astrbot.core.agent.tool import FunctionTool +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@dataclass +class MyTool(FunctionTool[AstrAgentContext]): + name: str = "my_tool" + description: str = "工具描述" + parameters: dict = Field(default_factory=lambda: { + "type": "object", + "properties": {"query": {"type": "string", "description": "参数描述"}}, + "required": ["query"], + }) + + async def call(self, context: ContextWrapper[AstrAgentContext], **kwargs) -> str: + return "结果" +``` + +### 8. 多智能体 (Multi-Agent) v4.5.7+ + +使用 agent-as-tool 模式实现多智能体: + +```python +@dataclass +class SubAgentTool(FunctionTool[AstrAgentContext]): + name: str = "sub_agent" + description: str = "子智能体描述" + parameters: dict = Field(default_factory=lambda: {...}) + + async def call(self, context: ContextWrapper[AstrAgentContext], **kwargs) -> str: + ctx = context.context.context + event = context.context.event + llm_resp = await ctx.tool_loop_agent( + event=event, + chat_provider_id=await ctx.get_current_chat_provider_id(event.unified_msg_origin), + prompt=kwargs["query"], + tools=ToolSet([SomeTool()]), + max_steps=30, + ) + return llm_resp.completion_text +``` diff --git a/docs/snapshots/v4.23.3/design_standards/event_flow.md b/docs/snapshots/v4.23.3/design_standards/event_flow.md new file mode 100644 index 0000000..16389ba --- /dev/null +++ b/docs/snapshots/v4.23.3/design_standards/event_flow.md @@ -0,0 +1,20 @@ +--- +category: design_standards +--- + +# 消息流转模型 + +AstrBot 的消息处理遵循一个清晰的流转过程。 + +### 核心流程图 + +1. **接收**: 平台适配器(Platform)接收原始消息。 +2. **转换**: 调用 `convert_message` 将其封装为 `AstrBotMessage`。 +3. **提交**: 封装为 `AstrMessageEvent` 后通过 `self.commit_event(event)` 提交到事件队列。 +4. **分发**: `PlatformManager` 按优先级将事件分发给所有插件的 Handler。 +5. **处理**: 插件执行业务逻辑。 + - 若调用 `event.stop_event()`,流程在此终止。 +6. **LLM 交互**: 若消息未被拦截,且符合 AI 触发条件,调用配置的 LLM。 +7. **结果装饰**: 发送前调用 `on_decorating_result` 钩子。 +8. **回复**: 调用 `event.send()` 或 `yield`,触发适配器的 `send` 方法。 +9. **发送**: 适配器调用平台 SDK 发送消息。 diff --git a/docs/snapshots/v4.23.3/design_standards/sandbox.md b/docs/snapshots/v4.23.3/design_standards/sandbox.md new file mode 100644 index 0000000..faf01d3 --- /dev/null +++ b/docs/snapshots/v4.23.3/design_standards/sandbox.md @@ -0,0 +1,20 @@ +--- +title: Sandbox 存储挂载与文件共享 (Sandbox Storage Mounting & File Sharing) +type: improvement +status: stable +last_updated: 2025-02-10 +related_base: agent/sandbox.md +--- + +## 概述 +在基于 Shipyard 的 Sandbox 运行时中,系统通过 Docker Volume 建立了宿主机与沙盒环境之间的共享临时目录。这一变更明确了文件在宿主机与沙盒之间流转的物理路径契约,是 `astrbot_upload_file` 等文件操作工具正常运行的基础设施保障。 + +## 存储映射契约 +为了实现宿主机与沙盒环境的高效文件交换,系统建立了以下挂载关系: +- **宿主机源路径**: `${PWD}/data/temp` (即 AstrBot 运行根目录下的临时文件夹) +- **沙盒目标路径**: `/AstrBot/data/temp` (沙盒环境内的绝对路径) + +## 变更影响分析 +1. **文件访问一致性**:AI 开发者在编写涉及沙盒文件操作的工具(Tools)时,应知晓 `/AstrBot/data/temp` 是预设的共享交换区。上传到宿主机临时目录的文件将直接映射至此路径,无需通过网络流重复传输。 +2. **底层实现透明化**:此变更解释了 `ShipyardBooter` 如何在物理层面处理文件可见性。如果开发者在非标准 Docker 环境下部署,需手动配置类似的卷挂载以维持 `astrbot_upload_file` 和 `astrbot_download_file` 的功能兼容性。 +3. **边界情况**:宿主机对 `data/temp` 的清理操作会同步反映在沙盒内。在执行长时间运行的 Agent 任务时,需注意临时文件的生命周期管理,避免因宿主机清理导致沙盒内路径失效。 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/design_standards/visual_utils.md b/docs/snapshots/v4.23.3/design_standards/visual_utils.md new file mode 100644 index 0000000..1a18162 --- /dev/null +++ b/docs/snapshots/v4.23.3/design_standards/visual_utils.md @@ -0,0 +1,89 @@ +--- +category: design_standards +--- + +# 视觉与渲染工具 (Visual Utils) + +AstrBot 提供了一些工具函数,帮助插件实现更丰富的视觉表现,如 HTML 渲染图片。 + +### 1. HTML 渲染 (html_render) + +AstrBot 内置了基于 **Playwright** 的 HTML 渲染引擎,支持将 HTML 字符串(支持 Jinja2 模板)或远程网页渲染为图片。 + +#### `await self.html_render(html_text: str = None, url: str = None, data: dict = None, options: dict = None) -> str` + +- **参数**: + - `html_text`: Jinja2 格式的 HTML 模板字符串。 + - `url`: 目标网页的 URL。如果提供了 `url`,将优先使用 `url` 而忽略 `html_text`。 + - `data`: 传入模板的变量字典(仅在提供 `html_text` 时有效)。 + - `options`: 渲染选项(映射自 Playwright API)。 + - `viewport`: 视口大小,例如 `{"width": 800, "height": 600}`。 + - `selector`: 等待并截图指定的 CSS 选择器对应的元素。 + - `wait_until`: 等待页面加载的状态。可选值:`"commit"`, `"domcontentloaded"`, `"load"`, `"networkidle"` (默认)。 + - `timeout`: 截图超时时间(毫秒)。 + - `type`: 图片格式,`"jpeg"` 或 `"png"`。 + - `quality`: 仅 JPEG 有效 (0-100)。 + - `omit_background`: 是否透明背景 (仅 PNG)。 + - `full_page`: 是否截取整页 (默认为 True,如果指定了 `selector` 则失效)。 + - `clip`: 裁切区域 `{"x": 0, "y": 0, "width": 100, "height": 100}`。 + - `animations`: `"allow"` 或 `"disabled"`。 + - `scale`: `"css"` 或 `"device"`。 +- **返回值**: 渲染后的图片本地路径。 + +#### 使用示例 + +##### 渲染 HTML 模板 + +```python +TMPL = """ +
+

Hello {{ name }}!

+

This is rendered via AstrBot HTML Render.

+
+""" + +@filter.command("hello_render") +async def hello_render(self, event: AstrMessageEvent): + # 渲染 HTML 字符串并传入数据 + image_path = await self.html_render(html_text=TMPL, data={"name": event.get_sender_id()}) + + # 将结果作为图片发送 + yield event.image_result(image_path) +``` + +##### 渲染远程网页 + +```python +@filter.command("screenshot") +async def screenshot(self, event: AstrMessageEvent, site_url: str): + # 渲染指定 URL,并设置视口大小 + image_path = await self.html_render( + url=site_url, + options={"viewport": {"width": 1280, "height": 720}, "wait_until": "networkidle"} + ) + yield event.image_result(image_path) +``` + +#### 转换为 Image 组件 + +`html_render` 返回的是图片的本地路径。你可以使用 `event.image_result(path)` 快速发送,也可以手动构建 `Image` 组件: + +```python +from astrbot.api.message_components import Image + +image_path = await self.html_render(url="...") +image_comp = Image.fromFileSystem(image_path) + +# 放入消息链发送 +# yield event.chain_result([Plain("这是截图:"), image_comp]) +``` + +### 2. 文字转图片 (text_to_image) + +#### `text_to_image(text: str, return_url: bool = True) -> str` + +- **说明**: 简单的文字转图片工具。 +- **参数**: + - `text`: 要转换的文字内容。 + - `return_url`: 是否返回 URL 格式。 +- **返回值**: 图片的路径或 URL。 diff --git a/docs/snapshots/v4.23.3/en/deploy/astrbot/package.md b/docs/snapshots/v4.23.3/en/deploy/astrbot/package.md new file mode 100644 index 0000000..c921f20 --- /dev/null +++ b/docs/snapshots/v4.23.3/en/deploy/astrbot/package.md @@ -0,0 +1,24 @@ +# Package Manager Deployment (uv) + +Use `uv` to install and run AstrBot quickly. + +## Before You Start + +If `uv` is not installed, install it first by following the official guide: + + +`uv` supports Linux, Windows, and macOS. + +## Important Notes + +> [!WARNING] +> AstrBot deployed via `uv` **does not support upgrading through the WebUI**. To update, run `uv tool upgrade astrbot --python 3.12` from the command line. + +AstrBot requires Python 3.12 or later. Use `--python 3.12` to ensure that `uv` creates the tool environment with Python 3.12; if Python downloads are enabled, `uv` will download Python 3.12 automatically when it is missing. + +## Install and Start + +```bash +uv tool install astrbot --python 3.12 +astrbot +``` diff --git a/docs/snapshots/v4.23.3/en/dev/star/guides/listen-message-event.md b/docs/snapshots/v4.23.3/en/dev/star/guides/listen-message-event.md new file mode 100644 index 0000000..a789bb1 --- /dev/null +++ b/docs/snapshots/v4.23.3/en/dev/star/guides/listen-message-event.md @@ -0,0 +1,436 @@ + +# Handling Message Events + +Event listeners can receive message content delivered by the platform and implement features such as commands, command groups, and event listening. + +Event listener decorators are located in `astrbot.api.event.filter` and must be imported first. Please make sure to import it, otherwise it will conflict with Python's built-in `filter` higher-order function. + +```py +from astrbot.api.event import filter, AstrMessageEvent +``` + +## Messages and Events + +AstrBot receives messages delivered by messaging platforms and encapsulates them as `AstrMessageEvent` objects, which are then passed to plugins for processing. + +![message-event](https://files.astrbot.app/docs/en/dev/star/guides/message-event.svg) + +### Message Events + +`AstrMessageEvent` is AstrBot's message event object, which stores information about the message sender, message content, etc. + +### Message Object + +`AstrBotMessage` is AstrBot's message object, which stores the specific content of messages delivered by the messaging platform. The `AstrMessageEvent` object contains a `message_obj` attribute to retrieve this message object. + +```py{11} +class AstrBotMessage: + '''AstrBot's message object''' + type: MessageType # Message type + self_id: str # Bot's identification ID + session_id: str # Session ID. Depends on the unique_session setting. + message_id: str # Message ID + group_id: str = "" # Group ID, empty if it's a private chat + sender: MessageMember # Sender + message: List[BaseMessageComponent] # Message chain. For example: [Plain("Hello"), At(qq=123456)] + message_str: str # The most straightforward plain text message string, concatenating Plain messages (text messages) from the message chain + raw_message: object + timestamp: int # Message timestamp +``` + +Here, `raw_message` is the **raw message object** from the messaging platform adapter. + +### Message Chain + +![message-chain](https://files.astrbot.app/docs/en/dev/star/guides/message-chain.svg) + +A `message chain` describes the structure of a message. It's an ordered list where each element is called a `message segment`. + +Common message segment types include: + +- `Plain`: Text message segment +- `At`: Mention message segment +- `Image`: Image message segment +- `Record`: Audio message segment +- `Video`: Video message segment +- `File`: File message segment + +Most messaging platforms support the above message segment types. + +Additionally, the OneBot v11 platform (QQ personal accounts, etc.) also supports the following common message segment types: + +- `Face`: Emoji message segment +- `Node`: A node in a forward message +- `Nodes`: Multiple nodes in a forward message +- `Poke`: Poke message segment + +In AstrBot, message chains are represented as lists of type `List[BaseMessageComponent]`. + +## Commands + +![message-event-simple-command](https://files.astrbot.app/docs/en/dev/star/guides/message-event-simple-command.svg) + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.star import Context, Star + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + + @filter.command("helloworld") # from astrbot.api.event.filter import command + async def helloworld(self, event: AstrMessageEvent): + '''This is a hello world command''' + user_name = event.get_sender_name() + message_str = event.message_str # Get the plain text content of the message + yield event.plain_result(f"Hello, {user_name}!") +``` + +> [!TIP] +> Commands cannot contain spaces, otherwise AstrBot will parse them as a second parameter. You can use the command group feature below, or use a listener to parse the message content yourself. + +## Commands with Parameters + +![command-with-param](https://files.astrbot.app/docs/en/dev/star/guides/command-with-param.svg) + +AstrBot will automatically parse command parameters for you. + +```python +@filter.command("add") +def add(self, event: AstrMessageEvent, a: int, b: int): + # /add 1 2 -> Result is: 3 + yield event.plain_result(f"Wow! The answer is {a + b}!") +``` + +## Command Groups + +Command groups help you organize commands. + +```python +@filter.command_group("math") +def math(self): + pass + +@math.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + # /math add 1 2 -> Result is: 3 + yield event.plain_result(f"Result is: {a + b}") + +@math.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + # /math sub 1 2 -> Result is: -1 + yield event.plain_result(f"Result is: {a - b}") +``` + +The command group function doesn't need to implement any logic; just use `pass` directly or add comments within the function. Subcommands of the command group are registered using `command_group_name.command`. + +When a user doesn't input a subcommand, an error will be reported and the tree structure of the command group will be rendered. + +![image](https://files.astrbot.app/docs/source/images/plugin/image-1.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/898a169ae7ed0478f41c0a7d14cb4d64.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/image-2.png) + +Theoretically, command groups can be nested infinitely! + +```py +''' +math +├── calc +│ ├── add (a(int),b(int),) +│ ├── sub (a(int),b(int),) +│ ├── help (command with no parameters) +''' + +@filter.command_group("math") +def math(): + pass + +@math.group("calc") # Note: this is group, not command_group +def calc(): + pass + +@calc.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"Result is: {a + b}") + +@calc.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"Result is: {a - b}") + +@calc.command("help") +def calc_help(self, event: AstrMessageEvent): + # /math calc help + yield event.plain_result("This is a calculator plugin with add and sub commands.") +``` + +## Command Aliases + +> Available after v3.4.28 + +You can add different aliases for commands or command groups: + +```python +@filter.command("help", alias={'帮助', 'helpme'}) +def help(self, event: AstrMessageEvent): + yield event.plain_result("This is a calculator plugin with add and sub commands.") +``` + +### Event Type Filtering + +#### Receive All + +This will receive all events. + +```python +@filter.event_message_type(filter.EventMessageType.ALL) +async def on_all_message(self, event: AstrMessageEvent): + yield event.plain_result("Received a message.") +``` + +#### Group Chat and Private Chat + +```python +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def on_private_message(self, event: AstrMessageEvent): + message_str = event.message_str # Get the plain text content of the message + yield event.plain_result("Received a private message.") +``` + +`EventMessageType` is an `Enum` type that contains all event types. Current event types are `PRIVATE_MESSAGE` and `GROUP_MESSAGE`. + +#### Messaging Platform + +```python +@filter.platform_adapter_type(filter.PlatformAdapterType.AIOCQHTTP | filter.PlatformAdapterType.QQOFFICIAL) +async def on_aiocqhttp(self, event: AstrMessageEvent): + '''Only receive messages from AIOCQHTTP and QQOFFICIAL''' + yield event.plain_result("Received a message") +``` + +In the current version, `PlatformAdapterType` includes `AIOCQHTTP`, `QQOFFICIAL`, `GEWECHAT`, and `ALL`. + +#### Admin Commands + +```python +@filter.permission_type(filter.PermissionType.ADMIN) +@filter.command("test") +async def test(self, event: AstrMessageEvent): + pass +``` + +Only admins can use the `test` command. + +### Multiple Filters + +Multiple filters can be used simultaneously by adding multiple decorators to a function. Filters use `AND` logic, meaning the function will only execute if all filters pass. + +```python +@filter.command("helloworld") +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") +``` + +### Event Hooks + +> [!TIP] +> Event hooks do not support being used together with @filter.command, @filter.command_group, @filter.event_message_type, @filter.platform_adapter_type, or @filter.permission_type. + +#### On Bot Initialization Complete + +> Available after v3.4.34 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_astrbot_loaded() +async def on_astrbot_loaded(self): + print("AstrBot initialization complete") + +``` + +#### On LLM Request + +In AstrBot's default execution flow, the `on_llm_request` hook is triggered before calling the LLM. + +You can obtain the `ProviderRequest` object and modify it. + +The ProviderRequest object contains all information about the LLM request, including the request text, system prompt, etc. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import ProviderRequest + +@filter.on_llm_request() +async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # Note there are three parameters + print(req) # Print the request text + req.system_prompt += "Custom system_prompt" + +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### On LLM Response Complete + +After the LLM request completes, the `on_llm_response` hook is triggered. + +You can obtain the `ProviderResponse` object and modify it. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse + +@filter.on_llm_response() +async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # Note there are three parameters + print(resp) +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### On Agent Begin + +> Requires AstrBot version > v4.23.1 + +When the Agent starts running, the `on_agent_begin` hook is triggered. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_begin() +async def on_agent_begin(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext]): # Note there are three parameters + print("Agent started") +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### Before LLM Tool Call + +> Requires AstrBot version > v4.23.1 + +When the Agent is about to call an LLM tool, the `on_using_llm_tool` hook is triggered. + +You can obtain the `FunctionTool` object and tool call arguments. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_using_llm_tool() +async def on_using_llm_tool( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, +): + print(tool.name, tool_args) +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### After LLM Tool Call + +> Requires AstrBot version > v4.23.1 + +After the LLM tool call completes, the `on_llm_tool_respond` hook is triggered. + +You can obtain the `FunctionTool` object, tool call arguments, and tool call result. + +```python +from mcp.types import CallToolResult + +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_llm_tool_respond() +async def on_llm_tool_respond( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, + tool_result: CallToolResult | None, +): + print(tool.name, tool_args, tool_result) +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### On Agent Done + +> Requires AstrBot version > v4.23.1 + +After the Agent finishes running, the `on_agent_done` hook is triggered. This hook is triggered after `on_llm_response`. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_done() +async def on_agent_done(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext], resp: LLMResponse): # Note there are four parameters + print(resp) +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### Before Sending Message + +Before sending a message, the `on_decorating_result` hook is triggered. + +You can implement some message decoration here, such as converting to voice, converting to image, adding prefixes, etc. + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_decorating_result() +async def on_decorating_result(self, event: AstrMessageEvent): + result = event.get_result() + chain = result.chain + print(chain) # Print the message chain + chain.append(Plain("!")) # Add an exclamation mark at the end of the message chain +``` + +> You cannot use yield to send messages here. This hook is only for decorating event.get_result().chain. If you need to send, please use the `event.send()` method directly. + +#### After Message Sent + +After a message is sent to the messaging platform, the `after_message_sent` hook is triggered. + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.after_message_sent() +async def after_message_sent(self, event: AstrMessageEvent): + pass +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +### Priority + +Commands, event listeners, and event hooks can have priority set to execute before other commands, listeners, or hooks. The default priority is `0`. + +```python +@filter.command("helloworld", priority=1) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") +``` + +## Controlling Event Propagation + +```python{6} +@filter.command("check_ok") +async def check_ok(self, event: AstrMessageEvent): + ok = self.check() # Your own logic + if not ok: + yield event.plain_result("Check failed") + event.stop_event() # Stop event propagation +``` + +When event propagation is stopped, all subsequent steps will not be executed. + +Assuming there's a plugin A, after A terminates event propagation, all subsequent operations will not be executed, such as executing other plugins' handlers or requesting the LLM. diff --git a/docs/snapshots/v4.23.3/en/dev/star/guides/storage.md b/docs/snapshots/v4.23.3/en/dev/star/guides/storage.md new file mode 100644 index 0000000..286d238 --- /dev/null +++ b/docs/snapshots/v4.23.3/en/dev/star/guides/storage.md @@ -0,0 +1,32 @@ +# Plugin Storage + +## Simple KV Storage + +> [!TIP] +> Requires AstrBot version >= 4.9.2. + +Plugins can use AstrBot's simple key-value store to persist configuration or temporary data. The storage is scoped per plugin, so each plugin has its own isolated space. + +```py +class Main(star.Star): + @filter.command("hello") + async def hello(self, event: AstrMessageEvent): + """Aloha!""" + await self.put_kv_data("greeted", True) + greeted = await self.get_kv_data("greeted", False) + await self.delete_kv_data("greeted") +``` + + +## Large File Storage Convention + +To keep large file handling consistent, store large files under `data/plugin_data/{plugin_name}/`. + +You can fetch the plugin data directory with: + +```py +from pathlib import Path +from astrbot.core.utils.astrbot_path import get_astrbot_data_path + +plugin_data_path = Path(get_astrbot_data_path()) / "plugin_data" / self.name # self.name is the plugin name; available in v4.9.2 and above. For lower versions, specify the plugin name yourself. +``` diff --git a/docs/snapshots/v4.23.3/en/use/command.md b/docs/snapshots/v4.23.3/en/use/command.md new file mode 100644 index 0000000..74c9e0c --- /dev/null +++ b/docs/snapshots/v4.23.3/en/use/command.md @@ -0,0 +1,110 @@ +# Built-in Commands + +AstrBot commands are registered through the plugin system. To keep the core lightweight, only a small set of basic commands are loaded with AstrBot itself. Other management and extended commands have been moved into a separate plugin. + +Use `/help` to view currently enabled commands. + +> [!NOTE] +> 1. `/help`, `/set`, and `/unset` are not shown in the `/help` command list by default, but they are still available. +> 2. If you change the wake prefix and remove the default `/`, commands must use the new wake prefix as well. For example, after changing the wake prefix to `!`, use `!help` and `!reset` instead of `/help` and `/reset`. + +## Core Built-in Commands + +The following commands are shipped with AstrBot and loaded by default: + +- `/help`: View currently enabled commands and AstrBot version information. +- `/sid`: View current message source information, including UMO, user ID, platform ID, message type, and session ID. This is commonly used when configuring admins, allowlists, or routing rules. +- `/reset`: Reset the current conversation's LLM context. +- `/stop`: Stop Agent tasks currently running in the current session. +- `/new`: Create and switch to a new conversation. +- `/dashboard_update`: Update AstrBot WebUI. This command requires admin permission. +- `/set`: Set a session variable, commonly used for Agent Runner input variables such as Dify, Coze, or DashScope. +- `/unset`: Remove a session variable. + +These commands are located in: + +```text +astrbot/builtin_stars/builtin_commands +``` + +## Core Command Details + +### `/sid` + +`/sid` shows information about the current message source. It mainly returns: + +- `UMO`: The unified message origin of the current message. It is commonly used for allowlists and per-session config routing. +- `UID`: The sender's user ID. It is commonly used when adding AstrBot admins. +- `Bot ID`: The platform instance ID of the current bot. +- `Message Type`: The message type, such as private chat or group chat. +- `Session ID`: The platform-side session ID. + +In group chats, if `unique_session` is enabled, `/sid` also shows the current group ID. This group ID can be used to allowlist the entire group. + +Common uses: + +- Add an admin: run `/sid` to get the `UID`, then add it in WebUI under `Config -> Other Config -> Admin ID`. +- Configure allowlists: use `UMO` or group ID to control which sessions can use the bot. +- Configure routing rules: use `UMO` to distinguish different platforms, groups, or private chats. + +### `/reset` + +`/reset` resets the LLM context of the current session. + +For AstrBot's built-in Agent Runner, it: + +- Stops running tasks in the current session. +- Clears the context messages of the current conversation. +- Notifies long-term memory to clear the current session state. + +For third-party Agent Runners such as `dify`, `coze`, `dashscope`, and `deerflow`, it: + +- Stops running tasks in the current session. +- Removes the saved third-party conversation ID for this session, so the next turn starts a new conversation. + +Permission notes: + +- In private chat, regular users can use it by default. +- In group chat with `unique_session` enabled, regular users can use it by default. +- In group chat without `unique_session`, admin permission is required by default. +- If command permission settings have been customized, the actual configuration takes precedence. + +### `/stop` + +`/stop` stops Agent tasks currently running in the current session. + +It does not clear conversation history and does not create a new conversation. It only sends a stop request to tasks currently executing in this session. + +For the built-in Agent Runner, `/stop` asks the Agent Runner to stop the current task. +For third-party Agent Runners such as `dify`, `coze`, `dashscope`, and `deerflow`, `/stop` directly stops registered running tasks in the current session. + +If there are no running tasks in the current session, AstrBot will report that no task is running. + +## Built-in Commands Extension + +Other commands that were previously shipped with the core have been moved to a separate plugin: + +- [builtin_commands_extension](https://github.com/AstrBotDevs/builtin_commands_extension) + +This plugin provides extended commands for plugin management, Provider management, model switching, Persona management, and conversation management. Examples include: + +- `/plugin`: View, enable, disable, or install plugins. +- `/op`, `/deop`: Add or remove admins. +- `/provider`: View or switch LLM Providers. +- `/model`: View or switch models. +- `/history`: View current conversation history. +- `/ls`: View the conversation list. +- `/groupnew`: Create a new conversation for a specified group. +- `/switch`: Switch to a specified conversation. +- `/rename`: Rename the current conversation. +- `/del`: Delete the current conversation. +- `/persona`: View or switch Persona. +- `/llm`: Enable or disable LLM chat. + +Install or enable the `builtin_commands_extension` plugin if you need these extended commands. + +## Permission Notes + +Some commands require AstrBot admin permission, such as `/dashboard_update`, `/op`, `/deop`, `/provider`, `/model`, and `/persona`. + +You can use `/sid` to get a user ID, then add it in WebUI under `Config -> Other Config -> Admin ID`. diff --git a/docs/snapshots/v4.23.3/en/use/computer.md b/docs/snapshots/v4.23.3/en/use/computer.md new file mode 100644 index 0000000..c128165 --- /dev/null +++ b/docs/snapshots/v4.23.3/en/use/computer.md @@ -0,0 +1,139 @@ +# Computer Use + +Computer Use controls whether an Agent can execute code, access files, run Shell commands. + +## Mode Selection + +In WebUI, open: + +- `Config -> General Config -> Use Computer Capabilities` + +The key option is `Computer Use Runtime`: + +- `none`: disables Computer Use; Shell, Python, filesystem, and related tools are not mounted. +- `local`: executes on the host machine where AstrBot is running. Use this when the Agent needs local files, command-line tools, or local dependencies. +- `sandbox`: executes inside an isolated sandbox. Use this when you want to reduce host risk or provide automation capabilities to multiple users. + +If you are not sure which mode to choose, prefer `sandbox`. Use `local` only when direct host access is required. + +## Local Mode + +`local` mode mounts Computer Use tools into the host environment where AstrBot runs. The Agent can call the host Shell, host Python, and host filesystem tools. + +This means the Agent's boundary is close to the AstrBot process itself. What it can access depends on the system permissions, runtime user, working directory, and operating-system restrictions of the AstrBot process. + +### Workspace + +In `local` mode, AstrBot prepares a workspace for each session: + +```text +data/workspaces/{normalized_umo} +``` + +`{normalized_umo}` is derived from the current session's `unified_msg_origin`; characters unsuitable for filenames are replaced with `_`. + +Relative paths passed to local filesystem tools are resolved under this workspace. For example: + +```text +notes/todo.txt +``` + +is resolved as: + +```text +data/workspaces/{normalized_umo}/notes/todo.txt +``` + +The local Shell tool also runs with this workspace as its current working directory. + +> [!NOTE] +> The local Python tool executes code through AstrBot's current Python environment. When Python code reads or writes files, use explicit absolute paths or prepare files through filesystem tools in the workspace first. + +### Local Tools + +`local` mode mainly provides: + +- `Shell`: executes host shell commands. Windows follows `cmd.exe` semantics; Linux/macOS follow Unix-like shell semantics. +- `Python`: executes Python code in AstrBot's current Python environment. +- `File read`: reads text, image, spreadsheet, and other supported files. +- `File write`: writes UTF-8 text files; relative paths default to the current workspace. +- `File edit`: replaces exact text in files. +- `Grep search`: searches file contents through ripgrep. + +`local` mode does not mount sandbox upload/download tools, and it does not provide browser automation. Browser automation belongs to the sandbox runtime and requires a sandbox profile with the `browser` capability. + +The local Shell tool includes basic blocking for dangerous commands such as `rm -rf`, `sudo`, `shutdown`, `reboot`, and `kill -9`. This is not a complete security sandbox and should not be treated as one. + +### Permission Model + +Computer Use has a separate option: + +- `Require AstrBot admin permission` + +This option is enabled by default. + +When enabled: + +- Admin users can use Shell, Python, file read, file write, file edit, and Grep search in `local` mode. +- Non-admin users cannot use Shell or Python. +- Non-admin users can only use file read, write, edit, and search inside restricted directories. + +Allowed directories for non-admin users in `local` mode include: + +- `data/skills` +- Current session's `data/workspaces/{normalized_umo}` +- AstrBot temporary directories +- `.astrbot` under the system temporary directory + +If `Require AstrBot admin permission` is disabled, regular users behave much closer to admins for Computer Use tools. Do not disable it unless you understand the risk. + +Admin IDs can be configured in: + +- `Config -> Other Config -> Admin ID` + +Users can get their own ID with `/sid`. + +## Sandbox Mode + +`sandbox` mode runs execution actions inside an isolated environment instead of directly on the AstrBot host. + +Inside the sandbox, the Agent can still use Shell, Python, and filesystem tools. If the selected sandbox profile supports the `browser` capability, AstrBot also mounts browser automation tools. + +With Shipyard Neo, the sandbox workspace root is usually: + +```text +/workspace +``` + +Filesystem tools should usually receive relative paths, for example: + +```text +result.txt +``` + +instead of: + +```text +/workspace/result.txt +``` + +For sandbox deployment, profiles, TTL, persistence, and browser capabilities, see [Agent Sandbox Environment](/agent/sandbox). + +> [!NOTE] +> Even in `sandbox` mode, `Require AstrBot admin permission` still affects access to Shell, Python, browser, upload/download, and related tools. The exact behavior depends on your configuration. + +## Skills + +Skills are reusable instruction bundles for Agents. They are usually stored under `data/skills`, and each Skill contains a `SKILL.md`. + +The relationship between Skills and Computer Use is: + +- Skills tell the Agent what to do. +- Computer Use decides whether the Agent can execute those steps. + +For example, a Skill may ask the Agent to read files, run scripts, and generate a report. If `Computer Use Runtime` is `none`, the Agent may see the Skill instructions, but it cannot call Shell or Python to execute them. + +In `local` mode, the Agent reads local Skills. +In `sandbox` mode, AstrBot attempts to sync local Skills into the sandbox so the Agent can execute them there. + +For more details, see [Anthropic Skills](/agent/skills). diff --git a/docs/snapshots/v4.23.3/index.md b/docs/snapshots/v4.23.3/index.md new file mode 100644 index 0000000..39e00da --- /dev/null +++ b/docs/snapshots/v4.23.3/index.md @@ -0,0 +1,16 @@ +# AstrBot 开发文档 + +本仓库的文档用于给 **AI 开发者 / RAG** 提供结构化的上下文。 + +## 快速入口 + +- [核心概念](/design_standards/core_concepts) +- [架构总览](/design_standards/architecture_overview) +- [Agent(工具 / 子智能体 / 沙盒 / 定时任务)](/agent/) +- [v4.7.0+ Agent Runner 架构](/agent/agent-runner) +- [消息模型](/messages/model) +- [插件配置 Schema](/plugin_config/schema) +- [事件钩子(Hooks)](/plugin_config/hooks) +- [平台适配器接口](/platform_adapters/adapter_interface) + + diff --git a/docs/snapshots/v4.23.3/messages/components.md b/docs/snapshots/v4.23.3/messages/components.md new file mode 100644 index 0000000..ce7f2dd --- /dev/null +++ b/docs/snapshots/v4.23.3/messages/components.md @@ -0,0 +1,41 @@ +--- +category: messages +--- + +# 消息链组件 (Message Components) + +AstrBot 使用消息链(MessageChain)来描述消息结构,它是一个由多个消息段(MessagePart/Component)组成的有序列表。 + +### 核心组件及其兼容性 + +| 组件类型 | 描述 | 参数示例 | 平台兼容性建议 | +| :--- | :--- | :--- | :--- | +| `Plain` | 纯文本 | `text="Hello"` | 所有平台支持。 | +| `At` | 提及/艾特 | `user_id="xxx"` | 大多数平台支持。 | +| `Image` | 图片 | `fromFileSystem(path)`, `fromURL(url)` | 所有平台支持。URL 必须以 `http` 或 `https` 开头。 | +| `Record` | 语音 | `file="path/to/wav"` | 广泛支持。目前主要支持 `wav` 格式。 | +| `Video` | 视频 | `fromFileSystem(path)`, `fromURL(url)` | 广泛支持。常用格式为 `mp4` | +| `File` | 文件 | `file="path"`, `name="a.txt"` | 部分平台不支持。 | +| `Face` | 表情 | `id="123"` | 主要在 OneBot v11 (QQ) 平台支持。 | +| `Node/Nodes` | 合并转发节点 | `uin`, `name`, `content` | 仅 OneBot v11 支持。 | +| `Poke` | 戳一戳 | - | 主要在 OneBot v11 支持。 | +| `Reply` | 回复特定消息 | `message_id="xxx"` | 广泛支持。 | + +### 消息构建示例 + +```python +import astrbot.api.message_components as Comp + +# 方式 1:手动构建列表 +chain = [ + Comp.At(user_id=event.get_sender_id()), + Comp.Plain(" 来看这张图:"), + Comp.Image.fromURL("https://example.com/image.jpg") +] +yield event.chain_result(chain) + +# 方式 2:使用 MessageChain 流式构建 +from astrbot.api.event import MessageChain +message_chain = MessageChain().message("Hello!").file_image("path/to/image.jpg") +await self.context.send_message(event.unified_msg_origin, message_chain) +``` diff --git a/docs/snapshots/v4.23.3/messages/events.md b/docs/snapshots/v4.23.3/messages/events.md new file mode 100644 index 0000000..c8681f9 --- /dev/null +++ b/docs/snapshots/v4.23.3/messages/events.md @@ -0,0 +1,33 @@ +--- +title: 消息事件 (AstrMessageEvent) +type: improvement +status: stable +last_updated: 2024-05-22 +related_base: messages/events.md +--- + +## 概述 +`AstrMessageEvent` 是插件处理逻辑的核心上下文对象。在最新版本中,该对象的会话标识属性(`session_id` 与 `unified_msg_origin`)已重构为基于 `MessageSession` 对象的动态属性(Property),增强了会话管理的一致性。 + +## 核心属性与 Setter 契约 + +这些属性现在不仅支持读取,还支持通过 Setter 进行动态修改,且修改会自动同步到底层的 `MessageSession` 状态: + +- **`event.unified_msg_origin` (UMO)**: + - **Getter**: 返回格式为 `platform_name:message_type:session_id` 的统一标识符。 + - **Setter**: 允许通过赋值 UMO 字符串来重置事件的会话上下文。内部通过 `MessageSession.from_str(value)` 重新解析并覆盖当前 session 对象。 +- **`event.session_id`**: + - **Getter**: 获取当前会话的唯一 ID。 + - **Setter**: 直接修改当前会话 ID,此变更会立即反映在 `unified_msg_origin` 的输出中。 + +## 内部实现逻辑 + +`AstrMessageEvent` 不再在 `__init__` 中静态存储 `session_id` 和 `unified_msg_origin` 字符串,而是统一维护一个 `self.session` (`MessageSession` 类实例)。 +- **初始化**: 修正了 `MessageSession` 的拼写错误并确保其在事件创建时被正确初始化。 +- **响应式更新**: 通过 Python `@property` 装饰器,确保了 UMO 和 Session ID 始终指向同一个数据源,消除了状态不一致的风险。 + +## 变更影响分析 + +1. **动态会话切换**: 插件开发者现在可以在事件处理过程中,通过修改 `event.unified_msg_origin` 动态地将事件“重定向”到另一个会话上下文。这对于实现跨群指令触发或会话劫持逻辑至关重要。 +2. **副作用警示**: 修改 `unified_msg_origin` 会导致底层的 `platform_name` 和 `message_type` 同时发生变化。如果仅需修改用户 ID,应优先使用 `event.session_id` 的 setter。 +3. **最佳实践**: 在编写需要持久化或比对会话的逻辑时,应始终依赖 `event.unified_msg_origin` 属性,因为它现在是经过 `MessageSession` 校验的权威来源。 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/messages/model.md b/docs/snapshots/v4.23.3/messages/model.md new file mode 100644 index 0000000..4394c9d --- /dev/null +++ b/docs/snapshots/v4.23.3/messages/model.md @@ -0,0 +1,31 @@ +--- +category: messages +--- + +# 消息模型 (AstrBotMessage) + +`AstrBotMessage` 是适配器层生成的标准化消息对象,它屏蔽了不同平台(QQ、飞书等)的差异,使插件可以“一次编写,到处运行”。 + +### AstrBotMessage 结构 + +在适配器中,必须填充 `AstrBotMessage` 的以下字段: + +```python +class AstrBotMessage: + type: MessageType # 消息类型(GROUP_MESSAGE 或 FRIEND_MESSAGE) + self_id: str # 机器人 ID + session_id: str # 会话 ID,决定了上下文隔离 + message_id: str # 消息 ID + group_id: str # 群组 ID(如果是私聊则为空) + sender: MessageMember # 发送者信息(含 user_id 和 nickname) + message: List[BaseMessageComponent] # 消息链(组件列表) + message_str: str # 纯文本汇总内容 + raw_message: object # 原始平台消息对象(用于 Debug 或特殊处理) + timestamp: int # 时间戳 +``` + +### 属性详解 + +- **`session_id`**: 核心字段,用于决定 LLM 对话的上下文隔离。 +- **`message_str`**: 插件处理逻辑中常用的纯文本内容。 +- **`message`**: 结构化消息内容,由各种消息组件组成。 diff --git a/docs/snapshots/v4.23.3/messages/umo.md b/docs/snapshots/v4.23.3/messages/umo.md new file mode 100644 index 0000000..3c432a9 --- /dev/null +++ b/docs/snapshots/v4.23.3/messages/umo.md @@ -0,0 +1,22 @@ +--- +category: messages +--- + +# 统一消息源 (Unified Message Origin) + +统一消息源(Unified Message Origin,简称 **UMO**)是 AstrBot 识别跨平台会话的核心标识。 + +### 格式 + +UMO 是一个格式如下的字符串: +`platform_id:message_type:session_id` + +- **`platform_id`**: 平台 ID(如 `aiocqhttp`, `qqofficial`)。 +- **`message_type`**: 消息类型(`group` 或 `private`)。 +- **`session_id`**: 会话 ID(群号或用户 ID)。 + +### 用途 + +1. **会话识别**: 唯一标识一个消息来源。 +2. **主动发送**: 通过 `context.send_message(umo, message_chain)` 向指定的源发送消息。 +3. **获取方式**: 在插件中通过 `event.unified_msg_origin` 获取。 diff --git a/docs/snapshots/v4.23.3/platform_adapters/adapter_interface.md b/docs/snapshots/v4.23.3/platform_adapters/adapter_interface.md new file mode 100644 index 0000000..ca0b211 --- /dev/null +++ b/docs/snapshots/v4.23.3/platform_adapters/adapter_interface.md @@ -0,0 +1,374 @@ +# Platform Adapter + +平台适配器将外部消息平台接入 AstrBot。插件可注册自定义适配器。 + +## 注册适配器 + +`@register_platform_adapter(adapter_name="id", desc="描述", default_config_tmpl={"key": "value"}, adapter_display_name="显示名", logo_path="logo.png", support_streaming_message=True)` + +## Platform 基类 + +继承 `Platform` 并实现以下方法: + +### 必须实现 + +- `run() -> Coroutine`: 异步阻塞方法,启动客户端 SDK 并持续监听消息。 +- `meta() -> PlatformMetadata`: 返回适配器元数据。 +- `send_by_session(session: MessageSession, message_chain: MessageChain)`: 通过会话发送消息。 + +### 可选重写 + +- `terminate()`: 终止平台运行。 +- `get_client() -> object`: 获取平台客户端对象。 +- `webhook_callback(request) -> Any`: 统一 Webhook 回调入口。 + +### 辅助方法 + +- `commit_event(event: AstrMessageEvent)`: 提交事件到事件队列。 +- `unified_webhook() -> bool`: 是否使用统一 Webhook 模式。 +- `get_stats() -> dict`: 获取平台统计信息。 +- `record_error(message: str, traceback_str: str | None)`: 记录错误。 +- `clear_errors()`: 清除错误记录。 + +### 属性 + +- `config: dict`: 平台配置(用户填写的 default_config_tmpl)。 +- `status: PlatformStatus`: 运行状态(PENDING/RUNNING/ERROR/STOPPED)。 +- `errors: list[PlatformError]`: 错误列表。 +- `last_error: PlatformError | None`: 最近错误。 + +## PlatformMetadata + +```python +PlatformMetadata( + name="adapter_id", # 平台类型标识 + description="适配器描述", + id="adapter_id", # 唯一标识符 + default_config_tmpl={}, # 默认配置模板 + adapter_display_name="显示名", # WebUI 显示名称 + logo_path="logo.png", # Logo 路径(相对于插件目录) + support_streaming_message=True, # 是否支持流式消息 + support_proactive_message=True, # 是否支持主动消息 +) +``` + +## AstrBotMessage + +适配器必须填充以下字段: + +```python +AstrBotMessage( + type=MessageType.GROUP_MESSAGE, # GROUP_MESSAGE / FRIEND_MESSAGE / OTHER_MESSAGE + self_id="bot_id", # 机器人 ID + session_id="session_id", # 会话 ID(决定上下文隔离) + message_id="msg_id", # 消息 ID + group=Group(group_id="123"), # 群组信息(私聊为 None) + sender=MessageMember(user_id="uid", nickname="昵称"), + message=[Plain(text="内容")], # 消息链 + message_str="纯文本内容", # 纯文本汇总 + raw_message=original_data, # 原始平台消息 + timestamp=1234567890, # 时间戳 +) +``` + +### 属性 + +- `group_id: str`: 群组 ID(私聊返回空字符串)。 + +## MessageType 枚举 + +- `MessageType.GROUP_MESSAGE`: 群组消息 +- `MessageType.FRIEND_MESSAGE`: 私聊消息 +- `MessageType.OTHER_MESSAGE`: 其他消息 + +## MessageMember + +```python +MessageMember(user_id="uid", nickname="昵称") +``` + +## Group + +```python +Group( + group_id="123", + group_name="群名", + group_avatar="头像URL", + group_owner="群主ID", + group_admins=["admin1", "admin2"], + members=[MessageMember(...)], +) +``` + +## MessageSession + +```python +MessageSession( + platform_name="adapter_id", # 平台 ID + message_type=MessageType.GROUP_MESSAGE, + session_id="session_id", +) +# 字符串格式: "platform_id:message_type:session_id" +``` + +### 方法 + +- `MessageSession.from_str(session_str)`: 从字符串解析。 + +## AstrMessageEvent + +事件基类,平台适配器需继承并实现 `send()` 方法。 + +### 核心属性 + +- `message_str: str`: 纯文本消息。 +- `message_obj: AstrBotMessage`: 完整消息对象。 +- `platform_meta: PlatformMetadata`: 平台元数据。 +- `session: MessageSession`: 会话对象。 +- `unified_msg_origin: str`: UMO(格式: `platform_id:message_type:session_id`)。 +- `session_id: str`: 会话 ID。 +- `role: str`: 用户角色("member" / "admin")。 +- `is_wake: bool`: 是否唤醒。 +- `is_at_or_wake_command: bool`: 是否 At 或唤醒词。 +- `call_llm: bool`: 是否调用 LLM。 + +### 获取信息方法 + +- `get_platform_name() -> str`: 获取平台类型。 +- `get_platform_id() -> str`: 获取平台 ID。 +- `get_message_str() -> str`: 获取消息文本。 +- `get_message_outline() -> str`: 获取消息概要(图片转 `[图片]`)。 +- `get_messages() -> list[BaseMessageComponent]`: 获取消息链。 +- `get_message_type() -> MessageType`: 获取消息类型。 +- `get_session_id() -> str`: 获取会话 ID。 +- `get_group_id() -> str`: 获取群组 ID。 +- `get_self_id() -> str`: 获取机器人 ID。 +- `get_sender_id() -> str`: 获取发送者 ID。 +- `get_sender_name() -> str`: 获取发送者昵称。 +- `is_private_chat() -> bool`: 是否私聊。 +- `is_wake_up() -> bool`: 是否唤醒。 +- `is_admin() -> bool`: 是否管理员。 + +### 消息发送方法 + +- `send(message: MessageChain)`: 发送消息到平台。 +- `send_streaming(generator: AsyncGenerator, use_fallback: bool)`: 发送流式消息。 +- `react(emoji: str)`: 添加表情回应。 +- `get_group(group_id: str | None) -> Group | None`: 获取群组数据。 + +### 结果设置方法 + +- `set_result(result: MessageEventResult | str)`: 设置事件结果。 +- `stop_event()`: 终止事件传播。 +- `continue_event()`: 继续事件传播。 +- `is_stopped() -> bool`: 是否已终止。 +- `should_call_llm(call_llm: bool)`: 是否调用 LLM。 +- `get_result() -> MessageEventResult | None`: 获取结果。 +- `clear_result()`: 清除结果。 + +### 快捷构建结果 + +- `make_result() -> MessageEventResult`: 创建空结果。 +- `plain_result(text: str) -> MessageEventResult`: 文本结果。 +- `image_result(url_or_path: str) -> MessageEventResult`: 图片结果。 +- `chain_result(chain: list) -> MessageEventResult`: 消息链结果。 + +### LLM 请求 + +- `request_llm(prompt: str, func_tool_manager=None, tool_set=None, session_id="", image_urls=None, contexts=None, system_prompt="", conversation=None) -> ProviderRequest`: 创建 LLM 请求。 + +### 额外信息 + +- `set_extra(key, value)`: 设置额外信息。 +- `get_extra(key: str | None, default=None) -> Any`: 获取额外信息。 +- `clear_extra()`: 清除额外信息。 + +## MessageChain + +消息链,用于构建和发送消息。 + +### 构建方法 + +- `message(text: str)`: 添加文本。 +- `at(name: str, qq: str | int)`: 添加 At。 +- `at_all()`: 添加 AtAll。 +- `url_image(url: str)`: 添加网络图片。 +- `file_image(path: str)`: 添加本地图片。 +- `base64_image(base64_str: str)`: 添加 base64 图片。 +- `use_t2i(use_t2i: bool)`: 设置是否使用文本转图片。 + +### 工具方法 + +- `get_plain_text(with_other_comps_mark: bool) -> str`: 获取纯文本。 +- `squash_plain()`: 合并所有 Plain 消息段。 + +## MessageEventResult + +继承 MessageChain,增加事件控制。 + +### 方法 + +- `stop_event()`: 终止事件传播。 +- `continue_event()`: 继续事件传播。 +- `is_stopped() -> bool`: 是否终止。 +- `set_async_stream(stream: AsyncGenerator)`: 设置异步流。 +- `set_result_content_type(typ: ResultContentType)`: 设置结果类型。 +- `is_llm_result() -> bool`: 是否 LLM 结果。 + +## 消息组件 + +### Plain + +`Plain(text="文本内容")` + +### Image + +```python +Image.fromURL("https://example.com/img.jpg") +Image.fromFileSystem("/path/to/image.jpg") +Image.fromBase64("base64_data") +Image.fromBytes(bytes_data) +``` + +- `convert_to_file_path() -> str`: 转换为本地路径。 +- `convert_to_base64() -> str`: 转换为 base64。 +- `register_to_file_service() -> str`: 注册到文件服务。 + +### Record + +```python +Record.fromFileSystem("/path/to/audio.wav") +Record.fromURL("https://example.com/audio.wav") +Record.fromBase64("base64_data") +``` + +- `convert_to_file_path() -> str`: 转换为本地路径。 +- `convert_to_base64() -> str`: 转换为 base64。 +- `register_to_file_service() -> str`: 注册到文件服务。 + +### Video + +```python +Video.fromFileSystem("/path/to/video.mp4") +Video.fromURL("https://example.com/video.mp4") +``` + +- `convert_to_file_path() -> str`: 转换为本地路径。 +- `register_to_file_service() -> str`: 注册到文件服务。 + +### File + +`File(name="文件名", file="/path/to/file", url="https://...")` + +- `get_file(allow_return_url: bool) -> str`: 异步获取文件。 +- `register_to_file_service() -> str`: 注册到文件服务。 + +### At / AtAll + +```python +At(qq="user_id", name="昵称") +AtAll() +``` + +### Reply + +`Reply(id="message_id", chain=[...], sender_id="uid", sender_nickname="昵称", time=timestamp, message_str="文本")` + +### Face + +`Face(id=123)` + +### Node / Nodes + +```python +Node(uin="qq号", name="昵称", content=[Plain("内容")]) +Nodes(nodes=[Node(...), Node(...)]) +``` + +### Forward + +`Forward(id="forward_id")` + +### Poke + +`Poke(type="poke_type")` + +### Json + +`Json(data={"key": "value"})` + +### WechatEmoji + +`WechatEmoji(md5="md5值", md5_len=长度, cdnurl="CDN链接")` + +## 完整示例 + +```python +from astrbot.api.platform import ( + Platform, AstrBotMessage, MessageMember, MessageType, + PlatformMetadata, register_platform_adapter +) +from astrbot.api.event import AstrMessageEvent, MessageChain +from astrbot.core.platform.astr_message_event import MessageSesion + +@register_platform_adapter("myplatform", "我的平台适配器", default_config_tmpl={ + "token": "", + "enable": False, +}) +class MyPlatformAdapter(Platform): + def __init__(self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue): + super().__init__(platform_config, event_queue) + self.settings = platform_settings + + def meta(self) -> PlatformMetadata: + return PlatformMetadata(name="myplatform", description="我的平台", id=self.config.get("id", "myplatform")) + + async def run(self): + async def on_message(data): + abm = await self.convert_message(data) + await self.handle_msg(abm) + # 启动客户端监听... + + async def convert_message(self, data: dict) -> AstrBotMessage: + abm = AstrBotMessage() + abm.type = MessageType.GROUP_MESSAGE + abm.session_id = data["session_id"] + abm.message_id = data["message_id"] + abm.sender = MessageMember(user_id=data["user_id"], nickname=data["nickname"]) + abm.message_str = data["content"] + abm.message = [Plain(text=data["content"])] + abm.raw_message = data + return abm + + async def handle_msg(self, message: AstrBotMessage): + event = MyPlatformEvent( + message_str=message.message_str, + message_obj=message, + platform_meta=self.meta(), + session_id=message.session_id, + client=self.client, + ) + self.commit_event(event) + + async def send_by_session(self, session: MessageSesion, message_chain: MessageChain): + # 实现发送逻辑... + await super().send_by_session(session, message_chain) + +class MyPlatformEvent(AstrMessageEvent): + def __init__(self, message_str, message_obj, platform_meta, session_id, client): + super().__init__(message_str, message_obj, platform_meta, session_id) + self.client = client + + async def send(self, message: MessageChain): + for comp in message.chain: + if isinstance(comp, Plain): + await self.client.send_text(self.get_sender_id(), comp.text) + await super().send(message) +``` + +## 注意事项 + +- `run()` 必须是阻塞方法,持续监听消息。 +- `convert_message()` 必须正确设置 `session_id`,它决定 LLM 上下文隔离。 +- `commit_event()` 用于提交事件到队列,不可遗漏。 +- 事件类必须实现 `send()` 方法,并在最后调用 `await super().send(message)`。 diff --git a/docs/snapshots/v4.23.3/platform_adapters/message_conversion.md b/docs/snapshots/v4.23.3/platform_adapters/message_conversion.md new file mode 100644 index 0000000..4d56d3c --- /dev/null +++ b/docs/snapshots/v4.23.3/platform_adapters/message_conversion.md @@ -0,0 +1,29 @@ +--- +category: platform_adapters +--- + +# 消息转换逻辑 (Message Conversion) + +`convert_message` 是适配器中最关键的方法,它负责将平台原始的消息格式映射到 AstrBot 的统一模型。 + +### 转换要求 + +在 `convert_message` 中,必须填充 `AstrBotMessage` 的以下核心字段: + +1. **`type`**: 识别是 `GROUP_MESSAGE` 还是 `FRIEND_MESSAGE`。 +2. **`session_id`**: 设置会话隔离。 +3. **`message_str`**: 提取纯文本内容。 +4. **`message`**: 将平台各段消息(如图片、表情)映射为 AstrBot 的 `MessageComponent` 列表。 +5. **`sender`**: 提取发送者的 ID 和昵称。 +6. **`raw_message`**: 保存原始对象。 + +### 提交事件 + +转换完成后,需将其封装为 `AstrMessageEvent` 并提交: + +```python +async def handle_raw_message(self, data): + bot_msg = self.convert_message(data) + event = AstrMessageEvent(bot_msg, self) # 或子类 + self.commit_event(event) +``` diff --git a/docs/snapshots/v4.23.3/platform_adapters/telegram_media_group.md b/docs/snapshots/v4.23.3/platform_adapters/telegram_media_group.md new file mode 100644 index 0000000..823c09e --- /dev/null +++ b/docs/snapshots/v4.23.3/platform_adapters/telegram_media_group.md @@ -0,0 +1,40 @@ +--- +title: Telegram 媒体组处理机制 (Telegram Media Group Handling) +type: feature +status: stable +last_updated: 2025-02-08 +related_base: platform_adapters/adapter_interface.md +--- + +## 概述 + +Telegram 平台在发送包含多张图片或视频的“相册”(Media Group)时,会将其拆分为多个独立的 Update 发送。AstrBot 的 Telegram 适配器实现了缓存与防抖机制,将这些碎片化的消息合并为单个 `AstrMessageEvent`,从而保证插件逻辑的一致性。 + +## 核心逻辑与参数 + +### 1. 收集与防抖机制 +适配器通过 `media_group_id` 识别属于同一相册的消息,并使用 `APScheduler` 进行异步调度处理: +- **`telegram_media_group_timeout` (默认 2.5s)**: 防抖延迟。每收到该组内的一条新消息,计时器都会重置。这是收集所有媒体项的窗口期。 +- **`telegram_media_group_max_wait` (默认 10.0s)**: 硬性超时上限。防止因消息流持续不断导致的无限延迟,达到此时间后将强制触发合并处理。 + +### 2. 消息合并策略 +在 `process_media_group` 方法中,系统执行以下合并逻辑: +- **基础元数据**: 以媒体组的第一条消息作为基准,保留其 `message_str`(通常是相册的 Caption)、回复关系(Reply Chain)和会话上下文。 +- **组件聚合**: 遍历组内所有后续消息,调用 `convert_message` 提取其媒体组件(如 `Image`, `Video`, `File`),并将其 `extend` 到基准消息的 `message` 列表(MessageChain)中。 +- **事件分发**: 合并完成后,仅提交一个封装了完整 `MessageChain` 的 `AstrMessageEvent` 到事件循环。 + +## 关键方法签名 + +- `handle_media_group_message(update, context)`: 拦截带有 `media_group_id` 的消息并管理缓存与调度任务。 +- `process_media_group(media_group_id)`: 核心合并函数,负责从缓存提取数据、重组 `AstrBotMessage` 并触发 `handle_msg`。 + +## 变更影响分析 + +- **插件开发者**: + - **事件密度变化**: 针对 Telegram 平台的相册消息,插件现在只会接收到一个 `AstrMessageEvent`。开发者应预期 `event.message` 列表中可能包含多个 `Image` 或 `Video` 组件。 + - **响应延迟**: 处理 Telegram 相册消息时会有至少 2.5s 的固有延迟,这是为了确保媒体收集完整,属于预期行为。 +- **适配器开发者**: + - 此机制展示了处理“流式/碎片化”平台消息的标准范式:`缓存 -> 防抖调度 -> 组件合并 -> 统一分发`。在接入类似具有媒体组概念的平台(如 Discord)时应参考此实现。 +- **边界情况**: + - 如果相册中的不同图片带有不同的文字说明(虽然 Telegram UI 通常只允许一个 Caption),目前逻辑仅保留第一条消息的文本。 + - 超过 `max_wait` 后到达的消息将被视为独立消息处理。 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/plugin_config/command_management.md b/docs/snapshots/v4.23.3/plugin_config/command_management.md new file mode 100644 index 0000000..912cf14 --- /dev/null +++ b/docs/snapshots/v4.23.3/plugin_config/command_management.md @@ -0,0 +1,34 @@ +--- +title: 动态指令管理与权限覆盖 (Dynamic Command Management) +type: feature +status: stable +last_updated: 2024-05-22 +related_base: plugin_config/decorators.md +--- + +## 概述 +AstrBot 引入了动态指令管理机制,允许在运行时通过 Dashboard 或 API 修改指令的元数据(如名称、启用状态)和执行权限。这意味着插件代码中通过装饰器(如 `@filter.permission_type`)定义的静态配置现在仅作为“初始默认值”,系统支持持久化的运行时覆盖。 + +## 核心逻辑与 API + +### 1. 权限动态更新 (`update_command_permission`) +该服务函数负责修改特定指令的处理权限。其核心流程如下: +- **定位处理函数**:通过 `handler_full_name` 检索指令描述符。 +- **持久化配置**:将权限变更写入全局 KV 存储中的 `alter_cmd` 字典。存储结构为 `alter_cmd -> {plugin_name} -> {handler_name} -> { "permission": "admin" | "member" }`。 +- **运行时滤镜注入**: + - 遍历指令关联的 `event_filters`。 + - 如果存在 `PermissionTypeFilter`,则直接更新其 `permission_type` 属性。 + - 如果不存在,则在滤镜列表首位插入一个新的 `PermissionTypeFilter` 实例。 + +### 2. 权限类型映射 +- `admin`: 映射为 `PermissionType.ADMIN`。 +- `member`: 映射为 `PermissionType.MEMBER`。 + +## 数据流向 +1. **配置加载**:插件加载时,系统会读取 `alter_cmd` 中的持久化设置并应用到指令实例。 +2. **实时生效**:通过 Dashboard 修改权限后,后端会同步更新内存中的 `handler.event_filters`,无需重启插件即可生效。 + +## 变更影响分析 +- **权限判定优先级**:动态配置(`alter_cmd`) > 装饰器静态定义。AI 开发者在调试权限问题时,应优先检查全局配置而非仅查看源码中的 `@filter.permission_type`。 +- **滤镜链可变性**:指令的 `event_filters` 列表现在是动态可变的。插件开发者不应假设滤镜列表在插件生命周期内保持不变。 +- **副作用**:修改权限会直接影响 `AstrMessageEvent` 的分发逻辑。如果一个指令被动态设为 `ADMIN`,则非管理员发送的消息将在 `PermissionTypeFilter` 阶段被拦截,不会进入插件业务逻辑。 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/plugin_config/decorators.md b/docs/snapshots/v4.23.3/plugin_config/decorators.md new file mode 100644 index 0000000..489dbb0 --- /dev/null +++ b/docs/snapshots/v4.23.3/plugin_config/decorators.md @@ -0,0 +1,30 @@ +--- +category: plugin_config +--- + +# 常用装饰器 (Decorators) + +AstrBot 提供了一系列基于 `astrbot.api.event.filter` 的装饰器,用于注册插件和控制消息处理逻辑。 + +### 注册装饰器 + +- **`@register(id, author, description, version, repo_url)`** + - 标记插件类,提供基础元数据(若存在 `metadata.yaml` 则优先级较低)。 + +### 消息过滤器装饰器 + +过滤器遵循 **AND 逻辑**,即所有条件均满足时才触发。 + +| 装饰器 | 参数说明 | +| :--- | :--- | +| `@filter.command(name, alias, priority)` | 注册指令。支持带参函数,如 `def add(self, event, a: int, b: int)`。 | +| `@filter.command_group(name)` | 注册指令组。子指令通过 `@组名.command` 注册。 | +| `@filter.event_message_type(type)` | 过滤消息来源类型(`ALL`, `PRIVATE_MESSAGE`, `GROUP_MESSAGE`)。 | +| `@filter.platform_adapter_type(type)` | 过滤平台类型(`AIOCQHTTP`, `TELEGRAM` 等)。支持按位或 `|`。 | +| `@filter.permission_type(type)` | 校验权限(如 `ADMIN`)。 | +| `@filter.regex(pattern)` | 正则表达式匹配。 | + +### 注意事项 + +- 指令名不应包含空格。 +- 优先级 `priority` 默认为 0,数值越大优先级越高。 diff --git a/docs/snapshots/v4.23.3/plugin_config/file_config.md b/docs/snapshots/v4.23.3/plugin_config/file_config.md new file mode 100644 index 0000000..8337071 --- /dev/null +++ b/docs/snapshots/v4.23.3/plugin_config/file_config.md @@ -0,0 +1,30 @@ +--- +title: 插件文件配置系统 (Plugin File Config) +type: feature +status: stable +last_updated: 2024-05-22 +related_base: plugin_config/schema.md +--- + +## 概述 +AstrBot 引入了原生的文件配置支持,允许插件开发者在 `_conf_schema.json` 中定义文件类型的配置项。该系统集成了 Dashboard 文件上传、路径安全清洗及自动化存储管理,使插件能够以标准化的方式管理模型权重、静态资源或本地数据库文件。 + +## Schema 定义 +在插件的 `_conf_schema.json` 中,可以通过以下字段定义文件配置项: +- **`type`**: 必须设置为 `"file"`。 +- **`file_types`**: (可选) 字符串数组,定义允许的文件后缀名白名单(例如 `[".jpg", ".png", ".onnx"]`)。 +- **`description`**: 配置项描述,将显示在 Dashboard 的上传控件旁。 + +## 存储与路径逻辑 +1. **物理存储**: 上传的文件统一存储在 `data/plugins//files//` 目录下。其中 `` 是配置项在 Schema 中的点号路径(dot-path),确保了不同配置项间的文件隔离。 +2. **路径安全**: 系统通过 `sanitize_filename` 强制清洗文件名以防止路径穿越攻击,并使用 `normalize_rel_path` 确保跨平台路径的一致性。 +3. **配置引用**: 存入插件 `config.json` 的值为相对于插件数据目录的规范化路径(始终以 `files/` 开头)。 + +## 核心校验机制 +- **`validate_config`**: 在配置保存前,核心系统会强制执行校验逻辑,确保所有 `file` 类型的路径均指向合法的插件内部存储区域,并符合后缀名白名单。 +- **`MAX_FILE_BYTES`**: 系统级限制上传文件大小,默认为 500MB。 + +## 变更影响分析 +- **资源管理标准化**: 开发者不再需要手动编写文件上传接口或处理复杂的 `multipart/form-data` 逻辑,只需通过 `self.config` 即可获取已就绪的本地文件路径。 +- **安全性增强**: 统一的路径清洗和校验机制消除了插件开发者自行处理文件路径时可能引入的任意文件读写漏洞。 +- **AI 开发者适配**: 在为 AstrBot 编写插件 Schema 时,AI 助手应优先推荐使用 `type: "file"` 来处理外部资源依赖,而非要求用户手动填写绝对路径。注意,保存配置时必须通过 `validate_config` 校验,否则配置将无法持久化。 \ No newline at end of file diff --git a/docs/snapshots/v4.23.3/plugin_config/hooks.md b/docs/snapshots/v4.23.3/plugin_config/hooks.md new file mode 100644 index 0000000..0ae6d6b --- /dev/null +++ b/docs/snapshots/v4.23.3/plugin_config/hooks.md @@ -0,0 +1,93 @@ +--- +category: plugin_config +--- + +# 事件钩子 (Hooks) + +事件钩子用于在 AstrBot 核心执行流程的关键节点介入(例如:LLM 请求前后、工具调用前后、发送消息前后)。 + +> 事件钩子是“插件事件系统”的一部分,和 Agent 运行钩子(`BaseAgentRunHooks`)不是同一套机制。 +> Agent 运行钩子见:`docs/agent/agent-related-hooks.md` + +## 使用方式 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_astrbot_loaded() +async def on_loaded(self): + ... +``` + +### 重要限制与最佳实践 + +- 事件钩子通常不支持与指令/过滤器混用(例如:`@filter.command`、`@filter.command_group`、`@filter.event_message_type`、`@filter.platform_adapter_type`、`@filter.permission_type`)。 +- 大多数事件钩子不建议用 `yield` 发送消息:如果要发消息,请直接调用 `await event.send(...)`。 +- 事件钩子应当短小、幂等、可失败(失败只影响当前 hook,不应导致整个机器人崩溃)。 + +## 核心钩子清单 + +下面列出对外暴露的常用事件钩子(按执行流程排序)。签名写法以核心实现为准(参考源码:`astrbotcore/astrbot/core/star/register/star_handler.py`)。 + +### 1) 生命周期 + +- `@filter.on_astrbot_loaded()` + - 触发:AstrBot 加载完成 + - 建议签名:`async def on_astrbot_loaded(self) -> None` + +- `@filter.on_platform_loaded()` + - 触发:平台加载完成 + - 建议签名:`async def on_platform_loaded(self) -> None` + +### 2) LLM 请求前后 + +- `@filter.on_waiting_llm_request()` + - 触发:确定要调用 LLM,但尚未获取会话锁之前(适合提示“思考中/排队中”) + - 建议签名:`async def on_waiting_llm(self, event: AstrMessageEvent) -> None` + +- `@filter.on_llm_request()` + - 触发:LLM 请求发送前(可修改请求内容) + - 建议签名:`async def on_llm_request(self, event: AstrMessageEvent, request: ProviderRequest) -> None` + - 常见用途: + - 注入/调整 `system_prompt` + - 根据平台/会话动态切换模型或工具策略(请保持可解释、可回滚) + +- `@filter.on_llm_response()` + - 触发:LLM 请求完成后(可读取/修饰返回结果) + - 建议签名:`async def on_llm_response(self, event: AstrMessageEvent, response: LLMResponse) -> None` + +### 3) 工具调用前后(Function Calling / Tools Use) + +- `@filter.on_using_llm_tool()` + - 触发:函数工具调用前 + - 建议签名:`async def on_using_tool(self, event: AstrMessageEvent, tool: FunctionTool, tool_args: dict | None) -> None` + - 常见用途: + - 记录审计日志 / 埋点 + - 根据会话状态拒绝某些工具(需配合工具层做硬限制) + +- `@filter.on_llm_tool_respond()` + - 触发:函数工具调用后 + - 建议签名:`async def on_tool_respond(self, event: AstrMessageEvent, tool: FunctionTool, tool_args: dict | None, tool_result: CallToolResult | None) -> None` + - 常见用途: + - 对工具结果做脱敏/裁剪 + - 追加提示,让 LLM 更好地总结工具输出 + +### 4) 发送消息前后 + +- `@filter.on_decorating_result()` + - 触发:发送消息前(用于“装饰”即将发送的消息链) + - 建议签名:`async def on_decorating_result(self, event: AstrMessageEvent) -> None` + - 常见用途: + - 文转图、追加后缀、统一格式化输出 + - 注意:这里的职责是“改 `event.get_result().chain`”,不是发消息;如需主动发送请使用 `event.send()`。 + +- `@filter.after_message_sent()` + - 触发:消息成功发送到平台后 + - 建议签名:`async def after_message_sent(self, event: AstrMessageEvent) -> None` + +## 相关文档与源码 + +- Agent 运行钩子:`docs/agent/agent-related-hooks.md` +- `filter` 对外导出位置:`astrbotcore/astrbot/api/event/filter/__init__.py` +- Hook 注册实现:`astrbotcore/astrbot/core/star/register/star_handler.py` +- 事件类型枚举:`astrbotcore/astrbot/core/star/star_handler.py` diff --git a/docs/snapshots/v4.23.3/plugin_config/lifecycle.md b/docs/snapshots/v4.23.3/plugin_config/lifecycle.md new file mode 100644 index 0000000..67807d3 --- /dev/null +++ b/docs/snapshots/v4.23.3/plugin_config/lifecycle.md @@ -0,0 +1,32 @@ +--- +category: plugin_config +--- + +# 插件生命周期 (Lifecycle) + +AstrBot 的插件系统基于**运行时注入**和**动态加载**机制。 + +### 插件结构规范 + +一个标准的 AstrBot 插件(Star)通常包含: +- `main.py`: 插件入口,包含继承自 `Star` 的类。 +- `metadata.yaml`: 插件元数据(ID、名称、版本、作者等)。 +- `_conf_schema.json`: 可选,配置 Schema。 +- `requirements.txt`: 可选,依赖定义。 +- `logo.png`: 可选,图标。 + +### 加载流程 + +1. **扫描**: 扫描 `data/plugins` 目录。 +2. **解析**: 读取元数据。 +3. **依赖校验**: 检查并提示缺失依赖。 +4. **实例化**: + - 解析 `_conf_schema.json` 并加载配置实体 `AstrBotConfig`。 + - 实例化插件类,注入 `Context` 和 `AstrBotConfig`。 +5. **注册**: 扫描 `@filter` 装饰器方法并注册到事件分发中心。 + +### 卸载与重载 + +- 调用插件实例的 `terminate()` 异步方法。 +- 清除事件分发中心中该插件的所有 Handler。 +- 允许在不重启 Bot 的情况下动态重载。 diff --git a/docs/snapshots/v4.23.3/plugin_config/schema.md b/docs/snapshots/v4.23.3/plugin_config/schema.md new file mode 100644 index 0000000..9daca24 --- /dev/null +++ b/docs/snapshots/v4.23.3/plugin_config/schema.md @@ -0,0 +1,81 @@ +--- +category: plugin_config +--- + +# 配置 Schema (`_conf_schema.json`) + +AstrBot 通过 Schema 实现配置的自动解析与 WebUI 可视化渲染。 + +### 配置定义 + +在插件目录下添加 `_conf_schema.json` 文件,定义配置项的 Schema。 + +### 字段说明 + +| 字段名 | 说明 | +| :--- | :--- | +| `type` | **必填**。支持 `string`, `text`, `int`, `float`, `bool`, `object`, `list`, `dict`, `template_list` | +| `description` | 配置描述 | +| `hint` | 提示语,右侧问号图标悬浮显示 | +| `obvious_hint` | 是否醒目显示 | +| `default` | 默认值 | +| `options` | 下拉列表可选项 | +| `items` | `object` 类型的子 Schema | +| `editor_mode` | 启用代码编辑器 (Monaco Editor) | +| `editor_language` | 代码编辑器语言,默认 `json` | +| `editor_theme` | 代码编辑器主题,`vs-light` 或 `vs-dark` | +| `_special` | 调用内置数据:`select_provider`, `select_provider_tts`, `select_provider_stt`, `select_persona` | +| `invisible` | 是否隐藏,默认 `false` | + +### 高级类型 + +- **`text`**: 多行文本输入 +- **`dict`**: 键值对配置,支持 `template_schema` 定义子项 +- **`template_list`**: 多组重复配置(v4.10.4+) + +### `template_list` 类型 + +用于保存多组重复配置,如多个 API 供应商或多套人设。 + +```json +{ + "providers": { + "type": "template_list", + "description": "API 供应商列表", + "templates": { + "openai": { + "name": "OpenAI", + "items": { + "api_key": {"description": "API Key", "type": "string", "default": "sk-xxxx"}, + "model": {"description": "模型名称", "type": "string", "default": "gpt-3.5-turbo"} + } + } + } + } +} +``` + +存储格式(包含 `__template_key` 字段): + +```json +{ + "providers": [ + {"__template_key": "openai", "api_key": "sk-xxxx", "model": "gpt-3.5-turbo"} + ] +} +``` + +### 在插件中使用 + +```python +from astrbot.api import AstrBotConfig + +@register("config", "Soulter", "一个配置示例", "1.0.0") +class ConfigPlugin(Star): + def __init__(self, context: Context, config: AstrBotConfig): + super().__init__(context) + self.config = config + # self.config.save_config() # 保存配置 +``` + +配置更新时,AstrBot 会自动添加缺失的默认值、移除不存在的配置项。 diff --git a/docs/snapshots/v4.23.3/plugin_config/session_control.md b/docs/snapshots/v4.23.3/plugin_config/session_control.md new file mode 100644 index 0000000..bdf93f3 --- /dev/null +++ b/docs/snapshots/v4.23.3/plugin_config/session_control.md @@ -0,0 +1,31 @@ +--- +category: plugin_config +--- + +# 会话控制 (Session Control) + +`session_waiter` 是实现多轮对话状态机的核心机制,常用于问答、验证或连续操作。 + +### 核心装饰器 + +- **`@session_waiter(timeout: float, record_history_chains: bool = False)`** + - 用于定义一个等待用户进一步输入的异步函数。 + - 超时会抛出 `TimeoutError`。 + +### SessionController 接口 + +Waiter 函数通常接收一个 `controller: SessionController` 参数: + +- `keep(timeout, reset_timeout)`: 保持会话拦截。`reset_timeout=True` 将重置计时。 +- `stop()`: 立即终止拦截,恢复正常消息分发。 +- `get_history_chains()`: 获取拦截期间的消息历史。 + +### SessionFilter (自定义会话隔离) + +通过继承 `SessionFilter` 并重写 `filter` 方法,可以自定义拦截的范围(如按群组拦截): + +```python +class GroupFilter(SessionFilter): + def filter(self, event: AstrMessageEvent) -> str: + return event.get_group_id() or event.unified_msg_origin +``` diff --git a/docs/snapshots/v4.23.3/providers/agent-runners/deerflow.md b/docs/snapshots/v4.23.3/providers/agent-runners/deerflow.md new file mode 100644 index 0000000..46dd216 --- /dev/null +++ b/docs/snapshots/v4.23.3/providers/agent-runners/deerflow.md @@ -0,0 +1,57 @@ +# 接入 DeerFlow + +在 v4.19.2 及之后,AstrBot 支持接入 [DeerFlow](https://github.com/bytedance/deer-flow) Agent Runner。 + +当前适配面向 DeerFlow **2.0 `main` 分支**。DeerFlow 官方已将原始 Deep Research 框架迁移到 `main-1.x` 分支持续维护,因此如果你使用的是 2.0,请以 `main` 分支文档和后端 API 为准。 + +## 预备工作:部署 DeerFlow + +如果你还没有部署 DeerFlow,请先参考 DeerFlow 官方文档完成安装和启动: + +- [DeerFlow GitHub 仓库](https://github.com/bytedance/deer-flow) +- [DeerFlow 官方网站](https://deerflow.tech/) +- [DeerFlow 配置文档](https://github.com/bytedance/deer-flow/blob/main/backend/docs/CONFIGURATION.md) + +请确认 DeerFlow 已正常启动,并且 AstrBot 可以访问 DeerFlow 的网关地址。默认情况下,DeerFlow 网关地址为 `http://127.0.0.1:2026`。 + +> [!TIP] +> - `API Base URL` 必须以 `http://` 或 `https://` 开头。 +> - 如果 AstrBot 与 DeerFlow 运行在不同容器或主机上,请将 `127.0.0.1` 替换为 DeerFlow 实际可访问的内网地址、主机名或域名。 + +## 在 AstrBot 中配置 DeerFlow + +在 WebUI 中,点击「模型提供商」->「新增提供商」,选择「Agent 执行器」,选择「DeerFlow」,进入 DeerFlow 的配置页面。 + +填写以下配置项: + +- `API Base URL`:DeerFlow API 网关地址,默认为 `http://127.0.0.1:2026` +- `DeerFlow API Key`:可选。若你的 DeerFlow 网关使用 Bearer 鉴权,可在此填写 +- `Authorization Header`:可选。自定义 Authorization 请求头,优先级高于 `DeerFlow API Key` +- `Assistant ID`:对应 DeerFlow 2.0 LangGraph 的 `assistant_id`,默认为 `lead_agent` +- `模型名称覆盖`:可选。覆盖 DeerFlow 默认模型 +- `启用思考模式`:是否启用 DeerFlow 的思考模式 +- `启用计划模式`:对应 DeerFlow 2.0 运行时 `config.configurable.is_plan_mode` +- `启用子智能体`:对应 DeerFlow 2.0 运行时 `config.configurable.subagent_enabled` +- `子智能体最大并发数`:对应 DeerFlow 2.0 运行时 `config.configurable.max_concurrent_subagents`,仅在启用子智能体时生效,默认 `3` +- `递归深度上限`:对应 LangGraph 的 `recursion_limit`,默认 `1000` + +填写完成后点击「保存」。 + +> [!TIP] +> - 如果 DeerFlow 侧已经配置了默认模型,可以将 `模型名称覆盖` 留空。 +> - 只有在 DeerFlow 侧已经启用了相应能力时,才建议开启 `计划模式` 或 `子智能体` 相关选项。 +> - AstrBot 会同时发送 DeerFlow 2.0 推荐的 `config.configurable` 运行时参数,并保留兼容字段,便于对接上游近期版本。 + +## 选择 Agent 执行器 + +进入左边栏配置页面,点击「Agent 执行方式」,选择「DeerFlow」,然后在下方出现的新的配置项中选择你刚刚创建的 DeerFlow Agent 执行器提供商 ID,点击右下角「保存」,即可完成配置。 + +## 常见检查项 + +如果请求没有正常通过 DeerFlow 执行,请优先检查以下内容: + +- DeerFlow 服务是否已经正常启动 +- `API Base URL` 是否能从 AstrBot 所在环境访问 +- 鉴权配置是否填写正确 +- `Assistant ID` 是否与 DeerFlow 中实际可用的 assistant 一致 +- 如果通过 `/reset`、`/new`、`/del` 重置 DeerFlow 会话,AstrBot 会尝试同步清理 DeerFlow 远端 thread;若 DeerFlow 网关不可达,则只会清理 AstrBot 本地会话标识 diff --git a/docs/snapshots/v4.23.3/zh/deploy/astrbot/package.md b/docs/snapshots/v4.23.3/zh/deploy/astrbot/package.md new file mode 100644 index 0000000..96a50f5 --- /dev/null +++ b/docs/snapshots/v4.23.3/zh/deploy/astrbot/package.md @@ -0,0 +1,24 @@ +# 包管理器部署(uv) + +使用 `uv` 可以快速安装并启动 AstrBot。 + +## 前置条件 + +如果尚未安装 `uv`,请先按照官方文档安装: + +`uv` 支持 Linux、Windows、macOS。 + +## 注意事项 + +> [!WARNING] +> 通过 `uv` 部署的 AstrBot **不支持在 WebUI 中进行版本升级**。如需更新,请在命令行中执行 `uv tool upgrade astrbot --python 3.12`。 + +AstrBot 需要 Python 3.12 或更高版本。使用 `--python 3.12` 可以确保 `uv` 使用 Python 3.12 创建 tool 环境;如果启用了 Python 自动下载,`uv` 会在缺少 Python 3.12 时自动下载。 + +## 安装并启动 + +```bash +uv tool install astrbot --python 3.12 +astrbot init # 只需要在第一次部署时执行,后续启动不需要执行 +astrbot run +``` diff --git a/docs/snapshots/v4.23.3/zh/dev/star/guides/listen-message-event.md b/docs/snapshots/v4.23.3/zh/dev/star/guides/listen-message-event.md new file mode 100644 index 0000000..3503086 --- /dev/null +++ b/docs/snapshots/v4.23.3/zh/dev/star/guides/listen-message-event.md @@ -0,0 +1,452 @@ + +# 处理消息事件 + +事件监听器可以收到平台下发的消息内容,可以实现指令、指令组、事件监听等功能。 + +事件监听器的注册器在 `astrbot.api.event.filter` 下,需要先导入。请务必导入,否则会和 python 的高阶函数 filter 冲突。 + +```py +from astrbot.api.event import filter, AstrMessageEvent +``` + +## 消息与事件 + +AstrBot 接收消息平台下发的消息,并将其封装为 `AstrMessageEvent` 对象,传递给插件进行处理。 + +![message-event](https://files.astrbot.app/docs/zh/dev/star/guides/message-event.svg) + +### 消息事件 + +`AstrMessageEvent` 是 AstrBot 的消息事件对象,其中存储了消息发送者、消息内容等信息。 + +### 消息对象 + +`AstrBotMessage` 是 AstrBot 的消息对象,其中存储了消息平台下发的消息具体内容,`AstrMessageEvent` 对象中包含一个 `message_obj` 属性用于获取该消息对象。 + +```py{11} +class AstrBotMessage: + '''AstrBot 的消息对象''' + type: MessageType # 消息类型 + self_id: str # 机器人的识别id + session_id: str # 会话id。取决于 unique_session 的设置。 + message_id: str # 消息id + group_id: str = "" # 群组id,如果为私聊,则为空 + sender: MessageMember # 发送者 + message: List[BaseMessageComponent] # 消息链。比如 [Plain("Hello"), At(qq=123456)] + message_str: str # 最直观的纯文本消息字符串,将消息链中的 Plain 消息(文本消息)连接起来 + raw_message: object + timestamp: int # 消息时间戳 +``` + +其中,`raw_message` 是消息平台适配器的**原始消息对象**。 + +### 消息链 + +![message-chain](https://files.astrbot.app/docs/zh/dev/star/guides/message-chain.svg) + +`消息链`描述一个消息的结构,是一个有序列表,列表中每一个元素称为`消息段`。 + +常见的消息段类型有: + +- `Plain`:文本消息段 +- `At`:提及消息段 +- `Image`:图片消息段 +- `Record`:语音消息段 +- `Video`:视频消息段 +- `File`:文件消息段 + +大多数消息平台都支持上面的消息段类型。 + +此外,OneBot v11 平台(QQ 个人号等)还支持以下较为常见的消息段类型: + +- `Face`:表情消息段 +- `Node`:合并转发消息中的一个节点 +- `Nodes`:合并转发消息中的多个节点 +- `Poke`:戳一戳消息段 + +在 AstrBot 中,消息链表示为 `List[BaseMessageComponent]` 类型的列表。 + +## 指令 + +![message-event-simple-command](https://files.astrbot.app/docs/zh/dev/star/guides/message-event-simple-command.svg) + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.star import Context, Star + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + + @filter.command("helloworld") # from astrbot.api.event.filter import command + async def helloworld(self, event: AstrMessageEvent): + '''这是 hello world 指令''' + user_name = event.get_sender_name() + message_str = event.message_str # 获取消息的纯文本内容 + yield event.plain_result(f"Hello, {user_name}!") +``` + +> [!TIP] +> 指令不能带空格,否则 AstrBot 会将其解析到第二个参数。可以使用下面的指令组功能,或者也使用监听器自己解析消息内容。 + +## 带参指令 + +![command-with-param](https://files.astrbot.app/docs/zh/dev/star/guides/command-with-param.svg) + +AstrBot 会自动帮你解析指令的参数。 + +```python +@filter.command("add") +def add(self, event: AstrMessageEvent, a: int, b: int): + # /add 1 2 -> 结果是: 3 + yield event.plain_result(f"Wow! The anwser is {a + b}!") +``` + +## 指令组 + +指令组可以帮助你组织指令。 + +```python +@filter.command_group("math") +def math(self): + pass + +@math.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + # /math add 1 2 -> 结果是: 3 + yield event.plain_result(f"结果是: {a + b}") + +@math.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + # /math sub 1 2 -> 结果是: -1 + yield event.plain_result(f"结果是: {a - b}") +``` + +指令组函数内不需要实现任何函数,请直接 `pass` 或者添加函数内注释。指令组的子指令使用 `指令组名.command` 来注册。 + +当用户没有输入子指令时,会报错并,并渲染出该指令组的树形结构。 + +![image](https://files.astrbot.app/docs/source/images/plugin/image-1.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/898a169ae7ed0478f41c0a7d14cb4d64.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/image-2.png) + +理论上,指令组可以无限嵌套! + +```py +''' +math +├── calc +│ ├── add (a(int),b(int),) +│ ├── sub (a(int),b(int),) +│ ├── help (无参数指令) +''' + +@filter.command_group("math") +def math(): + pass + +@math.group("calc") # 请注意,这里是 group,而不是 command_group +def calc(): + pass + +@calc.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"结果是: {a + b}") + +@calc.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"结果是: {a - b}") + +@calc.command("help") +def calc_help(self, event: AstrMessageEvent): + # /math calc help + yield event.plain_result("这是一个计算器插件,拥有 add, sub 指令。") +``` + +## 指令别名 + +> v3.4.28 后 + +可以为指令或指令组添加不同的别名: + +```python +@filter.command("help", alias={'帮助', 'helpme'}) +def help(self, event: AstrMessageEvent): + yield event.plain_result("这是一个计算器插件,拥有 add, sub 指令。") +``` + +### 事件类型过滤 + +#### 接收所有 + +这将接收所有的事件。 + +```python +@filter.event_message_type(filter.EventMessageType.ALL) +async def on_all_message(self, event: AstrMessageEvent): + yield event.plain_result("收到了一条消息。") +``` + +#### 群聊和私聊 + +```python +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def on_private_message(self, event: AstrMessageEvent): + message_str = event.message_str # 获取消息的纯文本内容 + yield event.plain_result("收到了一条私聊消息。") +``` + +`EventMessageType` 是一个 `Enum` 类型,包含了所有的事件类型。当前的事件类型有 `PRIVATE_MESSAGE` 和 `GROUP_MESSAGE`。 + +#### 消息平台 + +```python +@filter.platform_adapter_type(filter.PlatformAdapterType.AIOCQHTTP | filter.PlatformAdapterType.QQOFFICIAL) +async def on_aiocqhttp(self, event: AstrMessageEvent): + '''只接收 AIOCQHTTP 和 QQOFFICIAL 的消息''' + yield event.plain_result("收到了一条信息") +``` + +当前版本下,`PlatformAdapterType` 有 `AIOCQHTTP`, `QQOFFICIAL`, `GEWECHAT`, `ALL`。 + +#### 管理员指令 + +```python +@filter.permission_type(filter.PermissionType.ADMIN) +@filter.command("test") +async def test(self, event: AstrMessageEvent): + pass +``` + +仅管理员才能使用 `test` 指令。 + +### 多个过滤器 + +支持同时使用多个过滤器,只需要在函数上添加多个装饰器即可。过滤器使用 `AND` 逻辑。也就是说,只有所有的过滤器都通过了,才会执行函数。 + +```python +@filter.command("helloworld") +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("你好!") +``` + +### 事件钩子 + +> [!TIP] +> 事件钩子不支持与上面的 @filter.command, @filter.command_group, @filter.event_message_type, @filter.platform_adapter_type, @filter.permission_type 一起使用。 + +#### Bot 初始化完成时 + +> v3.4.34 后 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_astrbot_loaded() +async def on_astrbot_loaded(self): + print("AstrBot 初始化完成") + +``` + +#### 等待 LLM 请求时 + +在 AstrBot 准备调用 LLM 但还未获取会话锁时,会触发 `on_waiting_llm_request` 钩子。 + +这个钩子适合用于发送"正在等待请求..."等用户反馈提示,亦或是在锁外及时获取LLM请求而不用等到锁被释放。 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_waiting_llm_request() +async def on_waiting_llm(self, event: AstrMessageEvent): + await event.send("🤔 正在等待请求...") +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### LLM 请求时 + +在 AstrBot 默认的执行流程中,在调用 LLM 前,会触发 `on_llm_request` 钩子。 + +可以获取到 `ProviderRequest` 对象,可以对其进行修改。 + +ProviderRequest 对象包含了 LLM 请求的所有信息,包括请求的文本、系统提示等。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import ProviderRequest + +@filter.on_llm_request() +async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # 请注意有三个参数 + print(req) # 打印请求的文本 + req.system_prompt += "自定义 system_prompt" + +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### LLM 请求完成时 + +在 LLM 请求完成后,会触发 `on_llm_response` 钩子。 + +可以获取到 `ProviderResponse` 对象,可以对其进行修改。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse + +@filter.on_llm_response() +async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # 请注意有三个参数 + print(resp) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### Agent 开始运行时 + +> 适用于 AstrBot 版本 > v4.23.1 + +在 Agent 开始运行时,会触发 `on_agent_begin` 钩子。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_begin() +async def on_agent_begin(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext]): + print("Agent 开始运行") +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### LLM 工具调用前 + +> 适用于 AstrBot 版本 > v4.23.1 + +在 Agent 准备调用 LLM 工具时,会触发 `on_using_llm_tool` 钩子。 + +可以获取到 `FunctionTool` 对象和工具调用参数。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_using_llm_tool() +async def on_using_llm_tool( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, +): + print(tool.name, tool_args) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### LLM 工具调用后 + +> 适用于 AstrBot 版本 > v4.23.1 + +在 LLM 工具调用完成后,会触发 `on_llm_tool_respond` 钩子。 + +可以获取到 `FunctionTool` 对象、工具调用参数和工具调用结果。 + +```python +from mcp.types import CallToolResult + +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_llm_tool_respond() +async def on_llm_tool_respond( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, + tool_result: CallToolResult | None, +): + print(tool.name, tool_args, tool_result) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### Agent 运行完成时 + +> 适用于 AstrBot 版本 > v4.23.1 + +在 Agent 运行完成后,会触发 `on_agent_done` 钩子。这个钩子会在 `on_llm_response` 之后触发。本质上和 `on_llm_response` 一样。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_done() +async def on_agent_done(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext], resp: LLMResponse): + print(resp) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### 发送消息前 + +在发送消息前,会触发 `on_decorating_result` 钩子。 + +可以在这里实现一些消息的装饰,比如转语音、转图片、加前缀等等 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_decorating_result() +async def on_decorating_result(self, event: AstrMessageEvent): + result = event.get_result() + chain = result.chain + print(chain) # 打印消息链 + chain.append(Plain("!")) # 在消息链的最后添加一个感叹号 +``` + +> 这里不能使用 yield 来发送消息。这个钩子只是用来装饰 event.get_result().chain 的。如需发送,请直接使用 `event.send()` 方法。 + +#### 发送消息后 + +在发送消息给消息平台后,会触发 `after_message_sent` 钩子。 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.after_message_sent() +async def after_message_sent(self, event: AstrMessageEvent): + pass +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +### 优先级 + +指令、事件监听器、事件钩子可以设置优先级,先于其他指令、监听器、钩子执行。默认优先级是 `0`。 + +```python +@filter.command("helloworld", priority=1) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") +``` + +## 控制事件传播 + +```python{6} +@filter.command("check_ok") +async def check_ok(self, event: AstrMessageEvent): + ok = self.check() # 自己的逻辑 + if not ok: + yield event.plain_result("检查失败") + event.stop_event() # 停止事件传播 +``` + +当事件停止传播,后续所有步骤将不会被执行。 + +假设有一个插件 A,A 终止事件传播之后所有后续操作都不会执行,比如执行其它插件的 handler、请求 LLM。 diff --git a/docs/snapshots/v4.23.3/zh/dev/star/guides/storage.md b/docs/snapshots/v4.23.3/zh/dev/star/guides/storage.md new file mode 100644 index 0000000..a03ac29 --- /dev/null +++ b/docs/snapshots/v4.23.3/zh/dev/star/guides/storage.md @@ -0,0 +1,32 @@ +# 插件存储 + +## 简单 KV 存储 + +> [!TIP] +> 该功能需要 AstrBot 版本 >= 4.9.2。 + +插件可以使用 AstrBot 提供的简单 KV 存储功能来存储一些配置信息或临时数据。该存储是基于插件维度的,每个插件有独立的存储空间,互不干扰。 + +```py +class Main(star.Star): + @filter.command("hello") + async def hello(self, event: AstrMessageEvent): + """Aloha!""" + await self.put_kv_data("greeted", True) + greeted = await self.get_kv_data("greeted", False) + await self.delete_kv_data("greeted") +``` + + +## 存储大文件规范 + +为了规范插件存储大文件的行为,请将大文件存储于 `data/plugin_data/{plugin_name}/` 目录下。 + +你可以通过以下代码获取插件数据目录: + +```py +from pathlib import Path +from astrbot.core.utils.astrbot_path import get_astrbot_data_path + +plugin_data_path = Path(get_astrbot_data_path()) / "plugin_data" / self.name # self.name 为插件名称,在 v4.9.2 及以上版本可用,低于此版本请自行指定插件名称 +``` diff --git a/docs/snapshots/v4.23.3/zh/use/command.md b/docs/snapshots/v4.23.3/zh/use/command.md new file mode 100644 index 0000000..410b96e --- /dev/null +++ b/docs/snapshots/v4.23.3/zh/use/command.md @@ -0,0 +1,106 @@ +# 内置指令 + +AstrBot 的指令通过插件机制注册。为了保持主程序轻量,当前只有少量基础指令随 AstrBot 主程序内置加载;更多管理类、扩展类指令已经迁移到独立插件中维护。 + +使用 `/help` 可以查看当前已经启用的指令。 + +> [!NOTE] +> 1. `/help`、`/set`、`/unset` 默认不会显示在 `/help` 输出的指令清单中,但这些指令仍然可用。 +> 2. 如果您修改了唤醒前缀,去掉了默认的 `/`,那么指令也需要使用新的唤醒前缀触发。例如将唤醒前缀改为 `!` 后,应使用 `!help`、`!reset`,而不是 `/help`、`/reset`。 + +## 主程序内置指令 + +以下指令由 AstrBot 主程序自带,默认随 AstrBot 加载: + +- `/help`:查看当前启用的指令和 AstrBot 版本信息。 +- `/sid`:查看当前消息来源信息,包括 UMO、用户 ID、平台 ID、消息类型和会话 ID。常用于配置管理员、白名单或路由规则。 +- `/reset`:重置当前会话的 LLM 上下文。 +- `/stop`:停止当前会话中正在运行的 Agent 任务。 +- `/new`:创建并切换到一个新对话。 +- `/dashboard_update`:更新 AstrBot WebUI。该指令需要管理员权限。 +- `/set`:设置当前会话变量,常用于 Dify、Coze、DashScope 等 Agent 执行器的输入变量。 +- `/unset`:移除当前会话变量。 + +## 核心指令详解 + +### `/sid` + +`/sid` 用于查看当前消息来源信息,主要输出: + +- `UMO`:当前消息来源的统一标识。它通常用于白名单、配置文件路由等按会话生效的配置。 +- `UID`:当前发送者的用户 ID。它通常用于添加 AstrBot 管理员。 +- `Bot ID`:当前机器人所在平台实例的 ID。 +- `Message Type`:消息类型,例如私聊或群聊。 +- `Session ID`:平台侧会话 ID。 + +在群聊中,如果开启了 `unique_session`(会话隔离),`/sid` 还会额外提示当前群 ID。这个群 ID 可用于把整个群加入白名单。 + +常见用途: + +- 添加管理员:先发送 `/sid` 获取 `UID`,再在 WebUI 的 `配置 -> 其他配置 -> 管理员 ID` 中添加。 +- 配置白名单:使用 `UMO` 或群 ID 控制哪些会话可以使用机器人。 +- 配置路由规则:使用 `UMO` 区分不同平台、群聊或私聊来源。 + +### `/reset` + +`/reset` 用于重置当前会话的 LLM 上下文。 + +对于 AstrBot 内置 Agent Runner,它会: + +- 停止当前会话中正在运行的任务。 +- 清空当前对话的上下文消息。 +- 通知长期记忆会话清理当前上下文状态。 + +对于第三方 Agent Runner,例如 `dify`、`coze`、`dashscope`、`deerflow`,它会: + +- 停止当前会话中正在运行的任务。 +- 删除当前会话保存的第三方会话 ID,让下一轮对话重新开始。 + +权限说明: + +- 私聊中默认普通用户可使用。 +- 群聊开启会话隔离时,默认普通用户可使用。 +- 群聊未开启会话隔离时,默认需要管理员权限。 +- 如果管理员修改过指令权限配置,则以实际配置为准。 + +### `/stop` + +`/stop` 用于停止当前会话中正在运行的 Agent 任务。 + +它不会清空对话历史,也不会创建新对话。它只对当前会话正在执行的任务发出停止请求。 + +对于内置 Agent Runner,`/stop` 会请求 Agent Runner 停止当前任务。 +对于第三方 Agent Runner,例如 `dify`、`coze`、`dashscope`、`deerflow`,`/stop` 会直接停止当前会话中登记的运行任务。 + +如果当前会话没有正在运行的任务,AstrBot 会提示当前会话没有运行中的任务。 + +## 内置指令扩展 + +除上述基础指令外,其他原本随主程序提供的内置指令已经迁移到独立插件: + +- [builtin_commands_extension](https://github.com/AstrBotDevs/builtin_commands_extension) + +可直接在插件市场搜索安装。 + +该插件提供插件管理、Provider 管理、模型切换、Persona 管理、对话列表管理等扩展指令,例如: + +- `/plugin`:查看、启用、停用或安装插件。 +- `/op`、`/deop`:添加或移除管理员。 +- `/provider`:查看或切换 LLM Provider。 +- `/model`:查看或切换模型。 +- `/history`:查看当前对话历史。 +- `/ls`:查看对话列表。 +- `/groupnew`:为指定群聊创建新对话。 +- `/switch`:切换到指定对话。 +- `/rename`:重命名当前对话。 +- `/del`:删除当前对话。 +- `/persona`:查看或切换 Persona。 +- `/llm`:开启或关闭 LLM 聊天功能。 + +如果你需要这些扩展指令,请安装或启用 `builtin_commands_extension` 插件。 + +## 权限说明 + +部分指令需要 AstrBot 管理员权限,例如 `/dashboard_update`、`/op`、`/deop`、`/provider`、`/model`、`/persona` 等。 + +可以通过 `/sid` 获取用户 ID,然后在 WebUI 的 `配置 -> 其他配置 -> 管理员 ID` 中添加管理员。 diff --git a/docs/snapshots/v4.23.3/zh/use/computer.md b/docs/snapshots/v4.23.3/zh/use/computer.md new file mode 100644 index 0000000..2ab5025 --- /dev/null +++ b/docs/snapshots/v4.23.3/zh/use/computer.md @@ -0,0 +1,137 @@ +# 使用电脑能力 + +电脑能力(Computer Use)决定 Agent 是否可以在 AstrBot 运行环境中执行代码、访问文件、调用 Shell。 + +## 模式选择 + +在 WebUI 中进入: + +- `配置 -> 普通配置 -> 使用电脑能力` + +核心配置项是 `Computer Use Runtime`: + +- `none`:不启用电脑能力,不给 Agent 挂载 Shell、Python、文件系统等工具。 +- `local`:在 AstrBot 所在机器上执行,适合需要访问本机文件、命令行工具或本地依赖的场景。 +- `sandbox`:在隔离沙盒中执行,适合希望降低本机风险、或让多用户使用自动化能力的场景。 + +## Local 模式 + +`local` 模式会把电脑能力挂载到 AstrBot 所在的主机环境。Agent 可以调用本机 Shell、本机 Python,以及本机文件系统工具。 + +这意味着 Agent 的能力边界接近 AstrBot 进程本身:它能访问什么,取决于 AstrBot 进程的系统权限、运行用户、工作目录和操作系统限制。 + +### Workspace + +在 `local` 模式下,AstrBot 会为每个会话准备一个 workspace: + +```text +data/workspaces/{normalized_umo} +``` + +其中 `{normalized_umo}` 来自当前会话的 `unified_msg_origin`,并会将不适合文件名的字符替换为 `_`。 + +本地文件工具的相对路径会解析到这个 workspace 下。例如: + +```text +notes/todo.txt +``` + +会被解析为: + +```text +data/workspaces/{normalized_umo}/notes/todo.txt +``` + +本地 Shell 工具执行时,也会把当前工作目录设置为这个 workspace。 + +> [!NOTE] +> 本地 Python 工具会调用 AstrBot 当前 Python 环境执行代码。编写会读写文件的 Python 代码时,建议使用明确的绝对路径,或先通过文件工具在 workspace 中准备文件。 + +### 本地工具 + +`local` 模式主要提供以下工具: + +- `Shell`:执行本机 shell 命令。Windows 下使用 `cmd.exe` 语义,Linux/macOS 下使用类 Unix shell 语义。 +- `Python`:使用 AstrBot 当前 Python 环境执行 Python 代码。 +- `文件读取`:读取 workspace 或允许路径中的文本、图片、表格等文件。 +- `文件写入`:写入 UTF-8 文本文件;相对路径默认落在当前 workspace。 +- `文件编辑`:按精确字符串替换文件内容。 +- `Grep 搜索`:使用 ripgrep 能力搜索文件内容。 + +`local` 模式不会挂载沙盒上传/下载工具,也不会提供浏览器自动化工具。浏览器能力属于沙盒运行时,需要使用支持 `browser` capability 的沙盒 profile。 + +本地 Shell 内置了基础危险命令拦截,例如 `rm -rf`、`sudo`、`shutdown`、`reboot`、`kill -9` 等。但这不是完整安全沙箱,不能把它当作安全边界。 + +### 权限模型 + +电脑能力还有一个独立开关: + +- `需要 AstrBot 管理员权限` + +默认情况下这个开关是开启的。 + +开启后: + +- 管理员可以使用 `local` 模式下的 Shell、Python、文件读取、文件写入、文件编辑和 Grep 搜索。 +- 非管理员不能使用 Shell 和 Python。 +- 非管理员只能在受限目录内使用文件读取、写入、编辑和搜索。 + +非管理员在 `local` 模式下允许访问的目录包括: + +- `data/skills` +- 当前会话的 `data/workspaces/{normalized_umo}` +- AstrBot 的临时目录 +- 系统临时目录中的 `.astrbot` + +关闭“需要 AstrBot 管理员权限”后,普通用户在电脑能力工具上的行为会接近管理员。除非你非常清楚风险,否则不建议关闭。 + +管理员 ID 可在: + +- `配置 -> 其他配置 -> 管理员 ID` + +中配置。用户可通过 `/sid` 获取自己的 ID。 + +## Sandbox 模式 + +`sandbox` 模式会把执行动作放到隔离环境中,而不是直接在 AstrBot 主机上运行。 + +在沙盒中,Agent 仍然可以使用 Shell、Python、文件系统工具;如果所选沙盒 profile 支持 `browser` capability,还会挂载浏览器自动化工具。 + +使用 Shipyard Neo 时,沙盒 workspace 根目录通常是: + +```text +/workspace +``` + +文件工具一般应传入相对路径,例如: + +```text +result.txt +``` + +而不是: + +```text +/workspace/result.txt +``` + +沙盒部署、profile、TTL、数据持久化、浏览器能力等内容请参考:[Agent 沙盒环境](/agent/sandbox)。 + +> [!NOTE] +> 即使在 `sandbox` 模式下,“需要 AstrBot 管理员权限”仍会影响 Shell、Python、浏览器、上传下载等工具的调用权限。具体权限取决于你的配置。 + +## Skills + +Skills 是给 Agent 使用的“任务说明书”,通常存放在 `data/skills` 下,每个 Skill 都包含一个 `SKILL.md`。 + +电脑能力和 Skills 的关系可以理解为: + +- Skills 告诉 Agent 应该怎么做。 +- 电脑能力决定 Agent 能不能执行这些步骤。 + +例如,一个 Skill 可能要求 Agent 读取文件、运行脚本、生成报告。如果 `Computer Use Runtime` 是 `none`,Agent 可以看到 Skill 的说明,但无法真正调用 Shell 或 Python 完成执行。 + +在 `local` 模式下,Agent 会读取本地 Skills。 +在 `sandbox` 模式下,AstrBot 会尝试把本地 Skills 同步到沙盒中,让 Agent 在沙盒内按 Skill 指令执行。 + +更多内容请参考:[技能 Skills](/agent/skills)。 diff --git a/scripts/state.json b/scripts/state.json index 5a19011..6f73bf6 100644 --- a/scripts/state.json +++ b/scripts/state.json @@ -1,4 +1,4 @@ { - "last_commit_sha": "06fd2d2428dd1333eca30447f6c5329fa7686144", - "last_tag": "v4.20.0" + "last_commit_sha": "2f479b52043fade09db94649047d84ee4de6ce0f", + "last_tag": "v4.23.3" } \ No newline at end of file