merge: 合并 master 分支 (webui 改进和 attachment recovery)

- 采用 master 的 README 多语言版本和文档更新
- 采用 dev 版本号 (4.25.0) + master 的 Python 版本限制 (<3.14)
- 采用 master 的 _image_ref_to_data_url 图片处理实现
- 从 git 中移除 MDI 字体二进制文件,改由脚本生成
- 其他冲突均采用 master 版本
This commit is contained in:
LIghtJUNction
2026-03-27 00:27:03 +08:00
33 changed files with 2038 additions and 1256 deletions
+112 -142
View File
@@ -2,12 +2,14 @@
<div align="center">
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">简体中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh-TW.md">繁體中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_ja.md">日本語</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_fr.md">Français</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_ru.md">Русский</a>
<br>
<div>
<a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://hellogithub.com/repository/AstrBotDevs/AstrBot" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=d127d50cd5e54c5382328acc3bb25483&claim_uid=ZO9by7qCXgSd6Lp&t=2" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
@@ -19,43 +21,42 @@
<img src="https://img.shields.io/github/v/release/AstrBotDevs/AstrBot?color=76bad9" href="https://github.com/AstrBotDevs/AstrBot/releases/latest">
<img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="python">
<img src="https://deepwiki.com/badge.svg" href="https://deepwiki.com/AstrBotDevs/AstrBot">
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTk0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
<a href="https://hub.docker.com/r/soulter/astrbot"><img alt="Docker pull" src="https://img.shields.io/docker/pulls/soulter/astrbot.svg?color=76bad9"/></a>
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%20Plugins&label=Marketplace&cacheSeconds=3600">
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%20plugins&label=Marketplace&cacheSeconds=3600">
<img src="https://gitcode.com/Soulter/AstrBot/star/badge.svg" href="https://gitcode.com/Soulter/AstrBot">
</div>
<br>
<a href="https://astrbot.app/">Home</a> |
<a href="https://astrbot.app/">Docs</a> |
<a href="https://astrbot.app/">Documentation</a> |
<a href="https://blog.astrbot.app/">Blog</a> |
<a href="https://astrbot.featurebase.app/roadmap">Roadmap</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/issues">Issue Tracker</a> |
<a href="mailto:community@astrbot.app">Email Support</a>
</div>
AstrBot is an open-source, all-in-one Agentic personal and group chat assistant that can be deployed on dozens of mainstream instant messaging platforms such as QQ, Telegram, WeCom, Lark, DingTalk, Slack, and more. It also features a built-in lightweight ChatUI similar to OpenWebUI, creating a reliable and scalable conversational AI infrastructure for individuals, developers, and teams. Whether it's a personal AI companion, smart customer service, automated assistant, or enterprise knowledge base, AstrBot enables you to quickly build AI applications within the workflow of your instant messaging platforms.
AstrBot is an open-source all-in-one Agent chatbot platform that integrates with mainstream instant messaging apps. It provides reliable and scalable conversational AI infrastructure for individuals, developers, and teams. Whether you're building a personal AI companion, intelligent customer service, automation assistant, or enterprise knowledge base, AstrBot enables you to quickly build production-ready AI applications within your IM platform workflows.
![landingpage](https://github.com/user-attachments/assets/45fc5699-cddf-4e21-af35-13040706f6c0)
![screenshot_1 5x_postspark_2026-02-27_22-37-45](https://github.com/user-attachments/assets/f17cdb90-52d7-4773-be2e-ff64b566af6b)
## Key Features
1. 💯 Free & Open Source.
2. ✨ Large Language Model (LLM) dialogue, Multimodal, Agent, MCP, Skills, Knowledge Base, Persona settings, automatic dialogue compression.
3. 🤖 Supports integration with agent platforms such as Dify, Alibaba Bailian, Coze, etc.
4. 🌐 Multi-platform support: QQ, WeCom, Lark, DingTalk, WeChat Official Account, Telegram, Slack, and [more](#supported-message-platforms).
5. 📦 Plugin extension: 1000+ plugins available for one-click installation.
6. 🛡️ [Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html): Isolated environment for safely executing any code, calling Shell commands, and reusing session-level resources.
7. 💻 WebUI support.
8. 🌈 Web ChatUI support: Built-in proxy sandbox, web search, etc. within ChatUI.
9. 🌐 Internationalization (i18n) support.
2. ✨ AI LLM Conversations, Multimodal, Agent, MCP, Skills, Knowledge Base, Persona Settings, Auto Context Compression.
3. 🤖 Supports integration with Dify, Alibaba Cloud Bailian, Coze, and other agent platforms.
4. 🌐 Multi-Platform: QQ, WeChat Work, Feishu, DingTalk, WeChat Official Accounts, Telegram, Slack, and [more](#supported-messaging-platforms).
5. 📦 Plugin Extensions with 1000+ plugins available for one-click installation.
6. 🛡️ [Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html) for isolated, safe execution of code, shell calls, and session-level resource reuse.
7. 💻 WebUI Support.
8. 🌈 Web ChatUI Support with built-in agent sandbox and web search.
9. 🌐 Internationalization (i18n) Support.
<br>
<table align="center">
<tr align="center">
<th>💙 Roleplay & Companionship</th>
<th>💙 Role-playing & Emotional Companionship</th>
<th>✨ Proactive Agent</th>
<th>🚀 General Agentic Capabilities</th>
<th>🧩 1000+ Community Plugins</th>
@@ -72,21 +73,18 @@ AstrBot is an open-source, all-in-one Agentic personal and group chat assistant
### One-Click Deployment
For users who want to experience AstrBot quickly, are familiar with the command line, and can install the `uv` environment themselves, we recommend using `uv` for one-click deployment ⚡️.
For users who want to quickly experience AstrBot, are familiar with command-line usage, and can install a `uv` environment on their own, we recommend the `uv` one-click deployment method ⚡️:
```bash
uv tool install astrbot
astrbot init # Execute this command only for the first time to initialize the environment
astrbot run # astrbot run --backend-only starts only the backend service
# Install development version (more fixes and new features, but less stable; suitable for developers)
uv tool install git+https://github.com/AstrBotDevs/AstrBot@dev
astrbot init # Only execute this command for the first time to initialize the environment
astrbot run
```
> Requires [uv](https://docs.astral.sh/uv/) installed.
> Requires [uv](https://docs.astral.sh/uv/) to be installed.
> [!NOTE]
> For macOS users: Due to macOS security checks, the first execution of the `astrbot` command may take a longer time (about 10-20 seconds).
> For macOS user: due to macOS security checks, the first run of the `astrbot` command may take longer (about 10-20s).
Update `astrbot`:
@@ -96,107 +94,106 @@ uv tool upgrade astrbot
### Docker Deployment
For users familiar with containers who prefer a more stable deployment suitable for production environments, we recommend using Docker / Docker Compose to deploy AstrBot.
For users familiar with containers and looking for a more stable, production-ready deployment method, we recommend deploying AstrBot with Docker / Docker Compose.
Please refer to the official documentation [Deploy AstrBot with Docker](https://astrbot.app/deploy/astrbot/docker.html).
Please refer to the official documentation: [Deploy AstrBot with Docker](https://astrbot.app/deploy/astrbot/docker.html#%E4%BD%BF%E7%94%A8-docker-%E9%83%A8%E7%BD%B2-astrbot).
### Deploy on RainYun
For users who want to deploy AstrBot with one click and do not want to manage servers themselves, we recommend RainYun's one-click cloud deployment service ☁️:
For users who want one-click deployment and do not want to manage servers themselves, we recommend RainYun's one-click cloud deployment service ☁️:
[![Deploy on RainYun](https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-en.svg)](https://app.rainyun.com/apps/rca/store/5994?ref=NjU1ODg0)
### Desktop Client Deployment
### Desktop Application Deployment
For users who wish to use AstrBot on the desktop with ChatUI as the main interface, we recommend using the AstrBot App.
For users who want to use AstrBot on desktop and mainly use ChatUI, we recommend AstrBot App.
Go to [AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) to download and install; this method is intended for desktop use and is not recommended for server scenarios.
Visit [AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) to download and install; this method is designed for desktop usage and is not recommended for server scenarios.
### Launcher Deployment
Also for desktop, users who want quick deployment and isolated environments for multiple instances can use the AstrBot Launcher.
For desktop users who also want fast deployment and isolated multi-instance usage, we recommend AstrBot Launcher.
Go to [AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher) to download and install.
Visit [AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher) to download and install.
### Deploy on Replit
Replit deployment is maintained by the community, suitable for online demos and lightweight trials.
Replit deployment is maintained by the community and is suitable for online demos and lightweight trials.
[![Run on Repl.it](https://repl.it/badge/github/AstrBotDevs/AstrBot)](https://repl.it/github/AstrBotDevs/AstrBot)
### AUR
The AUR method is for Arch Linux users who wish to install AstrBot via the system package manager.
AUR deployment targets Arch Linux users who prefer installing AstrBot through the system package workflow.
Execute the following command in the terminal to install the `astrbot-git` package. You can start using it after installation completes.
Run the command below to install `astrbot-git`, then start AstrBot in your local environment.
```bash
yay -S astrbot-git
```
**More Deployment Methods**
**More deployment methods**
If you need panel-based or highly customized deployment, you can refer to [BT Panel](https://astrbot.app/deploy/astrbot/btpanel.html) (BT Panel App Store), [1Panel](https://astrbot.app/deploy/astrbot/1panel.html) (1Panel App Store), [CasaOS](https://astrbot.app/deploy/astrbot/casaos.html) (NAS / Home Server visual deployment), and [Manual Deployment](https://astrbot.app/deploy/astrbot/cli.html) (Full custom installation based on source code and `uv`).
If you need panel-based management or deeper customization, see [BT-Panel Deployment](https://astrbot.app/deploy/astrbot/btpanel.html) for BT Panel app-store setup, [1Panel Deployment](https://astrbot.app/deploy/astrbot/1panel.html) for 1Panel app-market deployment, [CasaOS Deployment](https://astrbot.app/deploy/astrbot/casaos.html) for NAS/home-server visual deployment, and [Manual Deployment](https://astrbot.app/deploy/astrbot/cli.html) for fully custom source-based installation with `uv`.
## Supported Message Platforms
## Supported Messaging Platforms
Connect AstrBot to your favorite chat platforms.
Connect AstrBot to your favorite chat platform.
| Platform | Maintainer |
|---------|---------------|
| **QQ** | Official |
| **OneBot v11** | Official |
| **Telegram** | Official |
| **WeCom App & Bot** | Official |
| **WeChat Customer Service & Official Account** | Official |
| **Lark (Feishu)** | Official |
| **DingTalk** | Official |
| **Slack** | Official |
| **Discord** | Official |
| **LINE** | Official |
| **Satori** | Official |
| **Misskey** | Official |
| **Whatsapp (Coming Soon)** | Official |
| [**Matrix**](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | Community |
| [**KOOK**](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | Community |
| [**VoceChat**](https://github.com/HikariFroya/astrbot_plugin_vocechat) | Community |
| QQ | Official |
| OneBot v11 protocol implementation | Official |
| Telegram | Official |
| Wecom & Wecom AI Bot | Official |
| WeChat Official Accounts | Official |
| Feishu (Lark) | Official |
| DingTalk | Official |
| Slack | Official |
| Discord | Official |
| LINE | Official |
| Satori | Official |
| Misskey | Official |
| WhatsApp (Coming Soon) | Official |
| [Matrix](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | Community |
| [KOOK](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | Community |
| [VoceChat](https://github.com/HikariFroya/astrbot_plugin_vocechat) | Community |
## Supported Model Providers
## Supported Model Services
| Provider | Type |
| Service | Type |
|---------|---------------|
| Custom | Any OpenAI API compatible service |
| OpenAI | LLM |
| Anthropic | LLM |
| Google Gemini | LLM |
| Moonshot AI | LLM |
| Zhipu AI | LLM |
| DeepSeek | LLM |
| Ollama (Local) | LLM |
| LM Studio (Local) | LLM |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | LLM (API Gateway, supports all models) |
| [Compshare](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | LLM (API Gateway, supports all models) |
| [SiliconFlow](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | LLM (API Gateway, supports all models) |
| [PPIO](https://ppio.com/user/register?invited_by=AIOONE) | LLM (API Gateway, supports all models) |
| [302.AI](https://share.302.ai/rr1M3l) | LLM (API Gateway, supports all models)|
| [TokenPony](https://www.tokenpony.cn/3YPyf) | LLM (API Gateway, supports all models)|
| ModelScope | LLM |
| OneAPI | LLM |
| Dify | LLMOps Platform |
| Alibaba Bailian | LLMOps Platform |
| Coze | LLMOps Platform |
| OpenAI Whisper | Speech-to-Text |
| SenseVoice | Speech-to-Text |
| OpenAI TTS | Text-to-Speech |
| Gemini TTS | Text-to-Speech |
| GPT-Sovits-Inference | Text-to-Speech |
| GPT-Sovits | Text-to-Speech |
| FishAudio | Text-to-Speech |
| Edge TTS | Text-to-Speech |
| Alibaba Bailian TTS | Text-to-Speech |
| Azure TTS | Text-to-Speech |
| Minimax TTS | Text-to-Speech |
| Volcano Engine TTS | Text-to-Speech |
| OpenAI and Compatible Services | LLM Services |
| Anthropic | LLM Services |
| Google Gemini | LLM Services |
| Moonshot AI | LLM Services |
| Zhipu AI | LLM Services |
| DeepSeek | LLM Services |
| Ollama (Self-hosted) | LLM Services |
| LM Studio (Self-hosted) | LLM Services |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | LLM Services (API Gateway, supports all models) |
| [CompShare](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | LLM Services |
| [302.AI](https://share.302.ai/rr1M3l) | LLM Services |
| [TokenPony](https://www.tokenpony.cn/3YPyf) | LLM Services |
| [SiliconFlow](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | LLM Services |
| [PPIO Cloud](https://ppio.com/user/register?invited_by=AIOONE) | LLM Services |
| ModelScope | LLM Services |
| OneAPI | LLM Services |
| Dify | LLMOps Platforms |
| Alibaba Cloud Bailian Applications | LLMOps Platforms |
| Coze | LLMOps Platforms |
| OpenAI Whisper | Speech-to-Text Services |
| SenseVoice | Speech-to-Text Services |
| OpenAI TTS | Text-to-Speech Services |
| Gemini TTS | Text-to-Speech Services |
| GPT-Sovits-Inference | Text-to-Speech Services |
| GPT-Sovits | Text-to-Speech Services |
| FishAudio | Text-to-Speech Services |
| Edge TTS | Text-to-Speech Services |
| Alibaba Cloud Bailian TTS | Text-to-Speech Services |
| Azure TTS | Text-to-Speech Services |
| Minimax TTS | Text-to-Speech Services |
| Volcano Engine TTS | Text-to-Speech Services |
## ❤️ Sponsors
@@ -205,63 +202,47 @@ Connect AstrBot to your favorite chat platforms.
</p>
## ❤️ Contribution
## ❤️ Contributing
Welcome any Issues/Pull Requests! Just submit your changes to this project :)
Issues and Pull Requests are always welcome! Feel free to submit your changes to this project :)
### How to Contribute
You can contribute by viewing issues or helping to review PRs (Pull Requests). Any issues or PRs are welcome to promote community contribution. Of course, these are just suggestions; you can contribute in any way. For new feature additions, please discuss via Issue first.
It is recommended to merge functional PRs into the `dev` branch, which will be merged into the main branch and released as a new version after testing.
To reduce conflicts, we suggest:
1. Create your working branch based on the `dev` branch, avoid working directly on the `main` branch.
2. When submitting a PR, select the `dev` branch as the target.
3. Regularly sync the `dev` branch to your local environment; use `git pull` frequently.
You can contribute by reviewing issues or helping with pull request reviews. Any issues or PRs are welcome to encourage community participation. Of course, these are just suggestions—you can contribute in any way you like. For adding new features, please discuss through an Issue first.
### Development Environment
AstrBot uses `ruff` for code formatting and checking.
AstrBot uses `ruff` for code formatting and linting.
```bash
git clone https://github.com/AstrBotDevs/AstrBot
git switch dev # Switch to dev branch
pip install pre-commit # or uv tool install pre-commit
pip install pre-commit
pre-commit install
```
We recommend using `uv` for local installation and testing:
```bash
uv tool install -e . --force
astrbot init
astrbot run
```
Frontend Debugging:
```bash
astrbot run --backend-only
cd dashboard
bun install # or pnpm, etc.
bun dev
```
## 🌍 Community
### QQ Groups
- Group 9: 1076659624 (New)
- Group 10: 1078079676 (New)
- Group 1: 322154837
- Group 3: 630166526
- Group 5: 822130018
- Group 6: 753075035
- Group 7: 743746109
- Group 8: 1030353265
- Developer Group (Casual): 975206796
- Developer Group (Official): 1039761811
- Group 12: 916228568 (New)
- Group 9: 1076659624 (Full)
- Group 10: 1078079676 (Full)
- Group 11: 704659519 (Full)
- Group 1: 322154837 (Full)
- Group 3: 630166526 (Full)
- Group 4: 1077826412 (Full)
- Group 5: 822130018 (Full)
- Group 6: 753075035 (Full)
- Group 7: 743746109 (Full)
- Group 8: 1030353265 (Full)
### Discord Channel
- Developer Group(Chit-chat): 975206796
- Developer Group(Formal): 1039761811
- [Discord](https://discord.gg/hAVk6tgV36)
### Discord Server
<a href="https://discord.gg/hAVk6tgV36"><img alt="Discord_community" src="https://img.shields.io/badge/Discord-AstrBot-purple?style=for-the-badge&color=76bad9"></a>
## ❤️ Special Thanks
@@ -271,24 +252,14 @@ Special thanks to all Contributors and plugin developers for their contributions
<img src="https://contrib.rocks/image?repo=AstrBotDevs/AstrBot&max=200&columns=14" />
</a>
In addition, the birth of this project cannot be separated from the help of the following open-source projects:
Additionally, the birth of this project would not have been possible without the help of the following open-source projects:
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - Great Cat Framework
Open Source Project Friendly Links:
- [NoneBot2](https://github.com/nonebot/nonebot2) - Excellent Python Asynchronous ChatBot Framework
- [Koishi](https://github.com/koishijs/koishi) - Excellent Node.js ChatBot Framework
- [MaiBot](https://github.com/Mai-with-u/MaiBot) - Excellent Anthropomorphic AI ChatBot
- [nekro-agent](https://github.com/KroMiose/nekro-agent) - Excellent Agent ChatBot
- [LangBot](https://github.com/langbot-app/LangBot) - Excellent Multi-platform AI ChatBot
- [ChatLuna](https://github.com/ChatLunaLab/chatluna) - Excellent Multi-platform AI ChatBot Koishi Plugin
- [Operit AI](https://github.com/AAswordman/Operit) - Excellent AI Assistant Android APP
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - The amazing cat framework
## ⭐ Star History
> [!TIP]
> If this project helps your life/work, or you are concerned about the future development of this project, please Star the project. This is our motivation to maintain this open-source project <3
> If this project has helped you in your life or work, or if you're interested in its future development, please give the project a Star. It's the driving force behind maintaining this open-source project <3
<div align="center">
@@ -298,10 +269,9 @@ Open Source Project Friendly Links:
<div align="center">
_Companionship and capability should never be opposites. We hope to create a robot that can both understand emotions, provide companionship, and reliably complete tasks._
_Companionship and capability should never be at odds. What we aim to create is a robot that can understand emotions, provide genuine companionship, and reliably accomplish tasks._
_私は、高性能ですから!_
<img src="https://files.astrbot.app/watashiwa-koseino-desukara.gif" width="100"/>
</div>
+122 -157
View File
@@ -2,12 +2,14 @@
<div align="center">
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">简体中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README.md">English</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh-TW.md">繁體中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_ja.md">日本語</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">简体中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_ru.md">Русский</a>
<br>
<div>
<a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://hellogithub.com/repository/AstrBotDevs/AstrBot" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=d127d50cd5e54c5382328acc3bb25483&claim_uid=ZO9by7qCXgSd6Lp&t=2" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
@@ -19,47 +21,45 @@
<img src="https://img.shields.io/github/v/release/AstrBotDevs/AstrBot?color=76bad9" href="https://github.com/AstrBotDevs/AstrBot/releases/latest">
<img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="python">
<img src="https://deepwiki.com/badge.svg" href="https://deepwiki.com/AstrBotDevs/AstrBot">
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTk4IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFZIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
<a href="https://hub.docker.com/r/soulter/astrbot"><img alt="Docker pull" src="https://img.shields.io/docker/pulls/soulter/astrbot.svg?color=76bad9"/></a>
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%20Plugins&label=Marketplace&cacheSeconds=3600">
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%20&label=Marketplace&cacheSeconds=3600">
<img src="https://gitcode.com/Soulter/AstrBot/star/badge.svg" href="https://gitcode.com/Soulter/AstrBot">
</div>
<br>
<a href="https://astrbot.app/">Accueil</a> |
<a href="https://astrbot.app/">Documentation</a> |
<a href="https://blog.astrbot.app/">Blog</a> |
<a href="https://astrbot.featurebase.app/roadmap">Feuille de route</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/issues">Signaler un problème</a>
<a href="mailto:community@astrbot.app">Email</a>
<a href="mailto:community@astrbot.app">Email Support</a>
</div>
AstrBot est un assistant de chat personnel et de groupe Agentic tout-en-un et open-source, qui peut être déployé sur des dizaines de logiciels de messagerie instantanée grand public tels que QQ, Telegram, WeCom (WeChat Entreprise), Lark (Feishu), DingTalk, Slack, etc. Il intègre également une interface de chat légère similaire à OpenWebUI, créant ainsi une infrastructure conversationnelle intelligente fiable et extensible pour les particuliers, les développeurs et les équipes. Qu'il s'agisse d'un compagnon IA personnel, d'un service client intelligent, d'un assistant automatisé ou d'une base de connaissances d'entreprise, AstrBot vous permet de construire rapidement des applications IA au sein du flux de travail de vos plateformes de messagerie instantanée.
AstrBot est une plateforme de chatbot Agent tout-en-un open source qui s'intègre aux principales applications de messagerie instantanée. Elle fournit une infrastructure d'IA conversationnelle fiable et évolutive pour les particuliers, les développeurs et les équipes. Que vous construisiez un compagnon IA personnel, un service client intelligent, un assistant d'automatisation ou une base de connaissances d'entreprise, AstrBot vous permet de créer rapidement des applications d'IA prêtes pour la production dans les flux de travail de votre plateforme de messagerie.
![landingpage](https://github.com/user-attachments/assets/45fc5699-cddf-4e21-af35-13040706f6c0)
![521771166-00782c4c-4437-4d97-aabc-605e3738da5c (1)](https://github.com/user-attachments/assets/61e7b505-f7db-41aa-a75f-4ef8f079b8ba)
## Fonctionnalités Principales
## Fonctionnalités principales
1. 💯 Gratuit & Open Source.
2. ✨ Dialogue avec de grands modèles d'IA (LLM), multimodal, Agent, MCP, Compétences (Skills), base de connaissances, définition de persona, compression automatique des dialogues.
3. 🤖 Prend en charge l'intégration avec des plateformes d'agents comme Dify, Alibaba Bailian, Coze, etc.
4. 🌐 Multiplateforme, prend en charge QQ, WeCom, Lark, DingTalk, Compte Officiel WeChat, Telegram, Slack et [plus encore](#plateformes-de-messagerie-prises-en-charge).
5. 📦 Extension par plugins, plus de 1000 plugins disponibles pour une installation en un clic.
6. 🛡️ [Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html) : environnement isolé pour exécuter n'importe quel code, appeler le Shell et réutiliser les ressources au niveau de la session en toute sécurité.
2. ✨ Dialogue avec de grands modèles d'IA, multimodal, Agent, MCP, Skills, Base de connaissances, Paramétrage de personnalité, compression automatique des dialogues.
3. 🤖 Prise en charge de l'accès aux plateformes d'Agents telles que Dify, Alibaba Cloud Bailian, Coze, etc.
4. 🌐 Multiplateforme : supporte QQ, WeChat Enterprise, Feishu, DingTalk, Comptes officiels WeChat, Telegram, Slack et [plus encore](#plateformes-de-messagerie-prises-en-charge).
5. 📦 Extension par plugins, avec plus de 1000 plugins déjà disponibles pour une installation en un clic.
6. 🛡️ Environnement isolé [Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html) : exécution sécurisée de code, appels Shell et réutilisation des ressources au niveau de la session.
7. 💻 Support WebUI.
8. 🌈 Support Web ChatUI, avec sandbox de proxy intégré, recherche web, etc.
8. 🌈 Support Web ChatUI, avec sandbox d'agent intégrée, recherche web, etc.
9. 🌐 Support de l'internationalisation (i18n).
<br>
<table align="center">
<tr align="center">
<th>💙 Jeu de rôle & Accompagnement émotionnel</th>
<th>✨ Agent Proactif</th>
<th>🚀 Capacités Agentic Génériques</th>
<th>🧩 1000+ Plugins Communautaires</th>
<th>💙 Jeux de rôle & Accompagnement émotionnel</th>
<th>✨ Agent proactif</th>
<th>🚀 Capacités agentiques générales</th>
<th>🧩 1000+ Plugins de communauté</th>
</tr>
<tr>
<td align="center"><p align="center"><img width="984" height="1746" alt="99b587c5d35eea09d84f33e6cf6cfd4f" src="https://github.com/user-attachments/assets/89196061-3290-458d-b51f-afa178049f84" /></p></td>
@@ -69,25 +69,22 @@ AstrBot est un assistant de chat personnel et de groupe Agentic tout-en-un et op
</tr>
</table>
## Démarrage Rapide
## Démarrage rapide
### Déploiement en un clic
Pour les utilisateurs qui souhaitent essayer AstrBot rapidement, qui sont familiers avec la ligne de commande et capables d'installer l'environnement `uv` par eux-mêmes, nous recommandons la méthode de déploiement en un clic avec `uv` ⚡️.
Pour les utilisateurs qui veulent découvrir AstrBot rapidement, qui sont familiers avec la ligne de commande et peuvent installer eux-mêmes l'environnement `uv`, nous recommandons la méthode de déploiement en un clic avec `uv` ⚡️ :
```bash
uv tool install astrbot
astrbot init # Exécutez cette commande uniquement la première fois pour initialiser l'environnement
astrbot run # astrbot run --backend-only démarre uniquement le service backend
# Installer la version de développement (plus de correctifs, nouvelles fonctionnalités, mais moins stable, adapté aux développeurs)
uv tool install git+https://github.com/AstrBotDevs/AstrBot@dev
astrbot run
```
> Nécessite l'installation de [uv](https://docs.astral.sh/uv/).
> [uv](https://docs.astral.sh/uv/) doit être installé.
> [!NOTE]
> Pour les utilisateurs de macOS : en raison des contrôles de sécurité de macOS, la première exécution de la commande `astrbot` peut prendre un certain temps (environ 10-20 secondes).
> Pour les utilisateurs macOS : en raison des vérifications de sécurité de macOS, la première exécution de la commande `astrbot` peut prendre plus de temps (environ 10-20s).
Mettre à jour `astrbot` :
@@ -97,172 +94,150 @@ uv tool upgrade astrbot
### Déploiement Docker
Pour les utilisateurs familiers avec les conteneurs et souhaitant une méthode de déploiement plus stable et adaptée aux environnements de production, nous recommandons d'utiliser Docker / Docker Compose pour déployer AstrBot.
Pour les utilisateurs familiers avec les conteneurs et qui souhaitent une méthode plus stable et adaptée à la production, nous recommandons de déployer AstrBot avec Docker / Docker Compose.
Veuillez vous référer à la documentation officielle [Déployer AstrBot avec Docker](https://astrbot.app/deploy/astrbot/docker.html).
Veuillez consulter la documentation officielle [Déployer AstrBot avec Docker](https://astrbot.app/deploy/astrbot/docker.html#%E4%BD%BF%E7%94%A8-docker-%E9%83%A8%E7%BD%B2-astrbot).
### Déploiement sur RainYun
### Déployer sur RainYun
Pour les utilisateurs souhaitant déployer AstrBot en un clic sans gérer de serveur, nous recommandons le service de déploiement cloud en un clic de RainYun ☁️ :
Pour les utilisateurs qui souhaitent déployer AstrBot en un clic sans gérer le serveur eux-mêmes, nous recommandons le service de déploiement cloud en un clic de RainYun ☁️ :
[![Deploy on RainYun](https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-en.svg)](https://app.rainyun.com/apps/rca/store/5994?ref=NjU1ODg0)
### Déploiement Client Bureau
### Déploiement de l'application de bureau
Pour les utilisateurs souhaitant utiliser AstrBot sur ordinateur de bureau et utiliser principalement ChatUI comme point d'entrée, nous recommandons l'application AstrBot App.
Pour les utilisateurs qui veulent utiliser AstrBot sur desktop et passer principalement par ChatUI, nous recommandons AstrBot App.
Rendez-vous sur [AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) pour télécharger et installer ; cette méthode est destinée à un usage bureautique et n'est pas recommandée pour les scénarios serveur.
Accédez à [AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) pour télécharger et installer l'application ; cette méthode est conçue pour un usage desktop et n'est pas recommandée pour les scénarios serveur.
### Déploiement Launcher
### Déploiement avec le lanceur
Également pour une utilisation sur bureau, pour les utilisateurs souhaitant un déploiement rapide et une isolation de l'environnement pour plusieurs instances, nous recommandons AstrBot Launcher.
Également sur desktop, pour les utilisateurs qui souhaitent un déploiement rapide avec isolation d'environnement et multi-instances, nous recommandons AstrBot Launcher.
Rendez-vous sur [AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher) pour télécharger et installer.
Accédez à [AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher) pour télécharger et installer.
### Déploiement sur Replit
### Déployer sur Replit
Le déploiement sur Replit est maintenu par la communauté et convient aux démonstrations en ligne et aux scénarios d'essai légers.
Le déploiement sur Replit est maintenu par la communauté et convient aux démonstrations en ligne et aux essais légers.
[![Run on Repl.it](https://repl.it/badge/github/AstrBotDevs/AstrBot)](https://repl.it/github/AstrBotDevs/AstrBot)
### AUR
La méthode AUR est destinée aux utilisateurs d'Arch Linux souhaitant installer AstrBot via le gestionnaire de paquets du système.
Le mode AUR s'adresse aux utilisateurs Arch Linux qui préfèrent installer AstrBot via le gestionnaire de paquets système.
Exécutez la commande ci-dessous dans le terminal pour installer le paquet `astrbot-git`. Une fois l'installation terminée, vous pouvez le lancer.
Exécutez la commande ci-dessous pour installer `astrbot-git`, puis lancez AstrBot localement.
```bash
yay -S astrbot-git
```
**Plus de méthodes de déploiement**
**Autres méthodes de déploiement**
Si vous avez besoin d'un déploiement via panneau de contrôle ou hautement personnalisé, vous pouvez consulter [BT Panel](https://astrbot.app/deploy/astrbot/btpanel.html) (installation via le magasin d'applications BT Panel), [1Panel](https://astrbot.app/deploy/astrbot/1panel.html) (installation via le magasin d'applications 1Panel), [CasaOS](https://astrbot.app/deploy/astrbot/casaos.html) (déploiement visuel pour NAS / serveur domestique) et [Déploiement Manuel](https://astrbot.app/deploy/astrbot/cli.html) (installation personnalisée complète basée sur le code source et `uv`).
Si vous avez besoin d'une gestion par panneau ou d'une personnalisation plus poussée, consultez [Déploiement BT-Panel](https://astrbot.app/deploy/astrbot/btpanel.html) pour une installation via BT Panel, [Déploiement 1Panel](https://astrbot.app/deploy/astrbot/1panel.html) pour le marketplace 1Panel, [Déploiement CasaOS](https://astrbot.app/deploy/astrbot/casaos.html) pour un déploiement visuel sur NAS/serveur domestique, et [Déploiement manuel](https://astrbot.app/deploy/astrbot/cli.html) pour une installation complète depuis les sources avec `uv`.
## Plateformes de Messagerie Prises en Charge
## Plateformes de messagerie prises en charge
Connectez AstrBot à vos plateformes de chat préférées.
| Plateforme | Mainteneur |
| Plateforme | Maintenance |
|---------|---------------|
| **QQ** | Officiel |
| **OneBot v11** | Officiel |
| **Telegram** | Officiel |
| **WeCom (App & Smart Bot)** | Officiel |
| **WeChat (Service Client & Compte Officiel)** | Officiel |
| **Lark (Feishu)** | Officiel |
| **DingTalk** | Officiel |
| **Slack** | Officiel |
| **Discord** | Officiel |
| **LINE** | Officiel |
| **Satori** | Officiel |
| **Misskey** | Officiel |
| **Whatsapp (Bientôt)** | Officiel |
| [**Matrix**](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | Communauté |
| [**KOOK**](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | Communauté |
| [**VoceChat**](https://github.com/HikariFroya/astrbot_plugin_vocechat) | Communauté |
| QQ | Officielle |
| Implémentation du protocole OneBot v11 | Officielle |
| Telegram | Officielle |
| Application WeChat Work & Bot intelligent WeChat Work | Officielle |
| Service client WeChat & Comptes officiels WeChat | Officielle |
| Feishu (Lark) | Officielle |
| DingTalk | Officielle |
| Slack | Officielle |
| Discord | Officielle |
| LINE | Officielle |
| Satori | Officielle |
| Misskey | Officielle |
| WhatsApp (Bientôt disponible) | Officielle |
| [Matrix](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | Communauté |
| [KOOK](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | Communauté |
| [VoceChat](https://github.com/HikariFroya/astrbot_plugin_vocechat) | Communauté |
## Fournisseurs de Modèles Pris en Charge
## Services de modèles pris en charge
| Fournisseur | Type |
| Service | Type |
|---------|---------------|
| Personnalisé | Tout service compatible avec l'API OpenAI |
| OpenAI | LLM |
| Anthropic | LLM |
| Google Gemini | LLM |
| Moonshot AI | LLM |
| Zhipu AI | LLM |
| DeepSeek | LLM |
| Ollama (Local) | LLM |
| LM Studio (Local) | LLM |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | LLM (Passerelle API, supporte tous les modèles) |
| [Uyun AI](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | LLM (Passerelle API, supporte tous les modèles) |
| [SiliconFlow](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | LLM (Passerelle API, supporte tous les modèles) |
| [PPIO](https://ppio.com/user/register?invited_by=AIOONE) | LLM (Passerelle API, supporte tous les modèles) |
| [302.AI](https://share.302.ai/rr1M3l) | LLM (Passerelle API, supporte tous les modèles)|
| [TokenPony](https://www.tokenpony.cn/3YPyf) | LLM (Passerelle API, supporte tous les modèles)|
| ModelScope | LLM |
| OneAPI | LLM |
| Dify | Plateforme LLMOps |
| Alibaba Bailian | Plateforme LLMOps |
| Coze | Plateforme LLMOps |
| OpenAI Whisper | Synthèse vocale (Speech-to-Text) |
| SenseVoice | Synthèse vocale (Speech-to-Text) |
| OpenAI TTS | Synthèse vocale (Text-to-Speech) |
| Gemini TTS | Synthèse vocale (Text-to-Speech) |
| GPT-Sovits-Inference | Synthèse vocale (Text-to-Speech) |
| GPT-Sovits | Synthèse vocale (Text-to-Speech) |
| FishAudio | Synthèse vocale (Text-to-Speech) |
| Edge TTS | Synthèse vocale (Text-to-Speech) |
| Alibaba Bailian TTS | Synthèse vocale (Text-to-Speech) |
| Azure TTS | Synthèse vocale (Text-to-Speech) |
| Minimax TTS | Synthèse vocale (Text-to-Speech) |
| Volcengine TTS | Synthèse vocale (Text-to-Speech) |
| OpenAI et services compatibles | Services LLM |
| Anthropic | Services LLM |
| Google Gemini | Services LLM |
| Moonshot AI | Services LLM |
| Zhipu AI | Services LLM |
| DeepSeek | Services LLM |
| Ollama (Auto-hébergé) | Services LLM |
| LM Studio (Auto-hébergé) | Services LLM |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | Services LLM (Passerelle API, prend en charge tous les modèles) |
| [CompShare](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | Services LLM |
| [302.AI](https://share.302.ai/rr1M3l) | Services LLM |
| [TokenPony](https://www.tokenpony.cn/3YPyf) | Services LLM |
| [SiliconFlow](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | Services LLM |
| [PPIO Cloud](https://ppio.com/user/register?invited_by=AIOONE) | Services LLM |
| ModelScope | Services LLM |
| OneAPI | Services LLM |
| Dify | Plateformes LLMOps |
| Applications Alibaba Cloud Bailian | Plateformes LLMOps |
| Coze | Plateformes LLMOps |
| OpenAI Whisper | Services de reconnaissance vocale |
| SenseVoice | Services de reconnaissance vocale |
| OpenAI TTS | Services de synthèse vocale |
| Gemini TTS | Services de synthèse vocale |
| GPT-Sovits-Inference | Services de synthèse vocale |
| GPT-Sovits | Services de synthèse vocale |
| FishAudio | Services de synthèse vocale |
| Edge TTS | Services de synthèse vocale |
| Alibaba Cloud Bailian TTS | Services de synthèse vocale |
| Azure TTS | Services de synthèse vocale |
| Minimax TTS | Services de synthèse vocale |
| Volcano Engine TTS | Services de synthèse vocale |
## ❤️ Sponsors
## ❤️ Contribuer
<p align="center">
<img alt="sponsors" src="https://sponsors.astrbot.app/?v=1">
</p>
Les Issues et Pull Requests sont toujours les bienvenues ! N'hésitez pas à soumettre vos modifications à ce projet :)
### Comment contribuer
## ❤️ Contribution
Vous pouvez contribuer en examinant les issues ou en aidant à la revue des pull requests. Toutes les issues ou PRs sont les bienvenues pour encourager la participation de la communauté. Bien sûr, ce ne sont que des suggestions - vous pouvez contribuer de la manière que vous souhaitez. Pour l'ajout de nouvelles fonctionnalités, veuillez d'abord en discuter via une Issue.
Les Issues et Pull Requests sont les bienvenus ! Soumettez simplement vos modifications à ce projet :)
### Environnement de développement
### Comment Contribuer
Vous pouvez contribuer en examinant les problèmes ou en aidant à réviser les PR (Pull Requests). Tout problème ou PR est le bienvenu pour promouvoir la contribution communautaire. Bien sûr, ce ne sont que des suggestions, vous pouvez contribuer de n'importe quelle manière. Pour l'ajout de nouvelles fonctionnalités, veuillez d'abord en discuter via une Issue.
Il est recommandé de fusionner les PR fonctionnels dans la branche `dev`, qui sera fusionnée dans la branche principale et publiée en tant que nouvelle version après test des modifications.
Pour réduire les conflits, nous suggérons :
1. Créez votre branche de travail basée sur la branche `dev`, évitez de travailler directement sur la branche `main`.
2. Lors de la soumission d'une PR, sélectionnez la branche `dev` comme cible.
3. Synchronisez régulièrement la branche `dev` en local, utilisez souvent `git pull`.
### Environnement de Développement
AstrBot utilise `ruff` pour le formatage et la vérification du code.
AstrBot utilise `ruff` pour le formatage et le linting du code.
```bash
git clone https://github.com/AstrBotDevs/AstrBot
git switch dev # Basculer vers la branche de développement
pip install pre-commit # ou uv tool install pre-commit
pip install pre-commit
pre-commit install
```
Il est recommandé d'utiliser `uv` pour l'installation locale et les tests.
```bash
uv tool install -e . --force
astrbot init
astrbot run
```
Débogage frontend
```bash
astrbot run --backend-only
cd dashboard
bun install # ou pnpm, etc.
bun dev
```
## 🌍 Communauté
### Groupes QQ
- Groupe 9 : 1076659624 (Nouveau)
- Groupe 10 : 1078079676 (Nouveau)
- Groupe 1 : 322154837
- Groupe 3 : 630166526
- Groupe 5 : 822130018
- Groupe 6 : 753075035
- Groupe 7 : 743746109
- Groupe 8 : 1030353265
- Groupe Développeurs (Discussion libre) : 975206796
- Groupe Développeurs (Officiel) : 1039761811
- Groupe 12 : 916228568 (nouveau)
- Groupe 9 : 1076659624 (complet)
- Groupe 10 : 1078079676 (complet)
- Groupe 11 : 704659519 (complet)
- Groupe 1 : 322154837 (complet)
- Groupe 3 : 630166526 (complet)
- Groupe 4 : 1077826412 (complet)
- Groupe 5 : 822130018 (complet)
- Groupe 6 : 753075035 (complet)
- Groupe 7 : 743746109 (complet)
- Groupe 8 : 1030353265 (complet)
- Groupe développeurs : 975206796
- Groupe développeurs (officiel) : 1039761811
### Canal Discord
### Serveur Discord
- [Discord](https://discord.gg/hAVk6tgV36)
<a href="https://discord.gg/hAVk6tgV36"><img alt="Discord_community" src="https://img.shields.io/badge/Discord-AstrBot-purple?style=for-the-badge&color=76bad9"></a>
## ❤️ Remerciements Spéciaux
## ❤️ Remerciements spéciaux
Un grand merci à tous les Contributeurs et développeurs de plugins pour leur contribution à AstrBot ❤️
Un grand merci à tous les contributeurs et développeurs de plugins pour leurs contributions à AstrBot ❤️
<a href="https://github.com/AstrBotDevs/AstrBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=AstrBotDevs/AstrBot&max=200&columns=14" />
@@ -270,22 +245,12 @@ Un grand merci à tous les Contributeurs et développeurs de plugins pour leur c
De plus, la naissance de ce projet n'aurait pas été possible sans l'aide des projets open source suivants :
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - Le grand framework félin
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - L'incroyable framework chat
Liens amicaux vers des projets open source :
- [NoneBot2](https://github.com/nonebot/nonebot2) - Excellent framework de ChatBot asynchrone en Python
- [Koishi](https://github.com/koishijs/koishi) - Excellent framework de ChatBot en Node.js
- [MaiBot](https://github.com/Mai-with-u/MaiBot) - Excellent ChatBot IA anthropomorphe
- [nekro-agent](https://github.com/KroMiose/nekro-agent) - Excellent ChatBot Agent
- [LangBot](https://github.com/langbot-app/LangBot) - Excellent ChatBot IA multiplateforme
- [ChatLuna](https://github.com/ChatLunaLab/chatluna) - Excellent plugin Koishi de ChatBot IA multiplateforme
- [Operit AI](https://github.com/AAswordman/Operit) - Excellente application Android d'assistant intelligent IA
## ⭐ Historique des Étoiles
## ⭐ Historique des étoiles
> [!TIP]
> Si ce projet vous a été utile dans votre vie ou votre travail, ou si vous vous intéressez à son développement futur, merci de lui donner une Étoile. C'est notre motivation pour maintenir ce projet open source <3
> Si ce projet vous a aidé dans votre vie ou votre travail, ou si vous êtes intéressé par son développement futur, veuillez donner une étoile au projet. C'est la force motrice derrière la maintenance de ce projet open source <3
<div align="center">
@@ -295,9 +260,9 @@ Liens amicaux vers des projets open source :
<div align="center">
_La compagnie et la compétence ne devraient jamais être opposées. Nous espérons créer un robot capable à la fois de comprendre les émotions, d'offrir de la compagnie et d'accomplir des tâches de manière fiable._
_La compagnie et la capacité ne devraient jamais être des opposés. Nous souhaitons créer un robot capable à la fois de comprendre les émotions, d'offrir de la présence, et d'accomplir des tâches de manière fiable._
_私は、高性能ですから!_ (Je suis performant !)
_私は、高性能ですから!_
<img src="https://files.astrbot.app/watashiwa-koseino-desukara.gif" width="100"/>
+125 -159
View File
@@ -2,12 +2,14 @@
<div align="center">
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">简体中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README.md">English</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh-TW.md">繁體中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">简体中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_fr.md">Français</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_ru.md">Русский</a>
<br>
<div>
<a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://hellogithub.com/repository/AstrBotDevs/AstrBot" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=d127d50cd5e54c5382328acc3bb25483&claim_uid=ZO9by7qCXgSd6Lp&t=2" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
@@ -19,46 +21,44 @@
<img src="https://img.shields.io/github/v/release/AstrBotDevs/AstrBot?color=76bad9" href="https://github.com/AstrBotDevs/AstrBot/releases/latest">
<img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="python">
<img src="https://deepwiki.com/badge.svg" href="https://deepwiki.com/AstrBotDevs/AstrBot">
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFZIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0LjYxNTZDNS4zMTUwMiAxNC4zOTk5IDUuNjAxNTYgMTQuMTEzNCA1LjYwMTU2IDEzLjc1OTlWMTEuMDM5OUM1LjYwMTU2IDEwLjY4NjQgNS4zMTUwMiAxMC4zOTk5IDQuOTYxNTYgMTAuMzk5OVoiIGZpbGw9IiNmZmYiLz4KPHBhdGggZD0iTTEzLjc1ODQgMS42MDAxSDExLjAzODRDMTAuNjg1IDEuNjAwMSAxMC4zOTg0IDEuODg2NjQgMTAuMzk4NCAyLjI0MDFWNC45NjAxQzEwLjM5ODQgNS4zMTM1NiAxMC42ODUgNS42MDAxIDExLjAzODQgNS42MDAxSDEzLjc1ODRDMTQuMTExOSA1LjYwMDEgMTQuMzk4NCA1LjMxMzU2IDE0LjM5ODQgNC45NjAxVjIuMjQwMUMxNC4zOTg0IDEuODg2NjQgMTQuMTExOSAxLjYwMDEgMTMuNzU4NCAxLjYwMDFZIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDRMNCAxMlpFIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
<a href="https://hub.docker.com/r/soulter/astrbot"><img alt="Docker pull" src="https://img.shields.io/docker/pulls/soulter/astrbot.svg?color=76bad9"/></a>
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%E5%80%8B&label=%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%82%A2&cacheSeconds=3600">
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%20&label=%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%83%9E%E3%83%BC%E3%82%B1%E3%83%83%E3%83%88&cacheSeconds=3600">
<img src="https://gitcode.com/Soulter/AstrBot/star/badge.svg" href="https://gitcode.com/Soulter/AstrBot">
</div>
<br>
<a href="https://astrbot.app/">ホーム</a> |
<a href="https://astrbot.app/">ドキュメント</a> |
<a href="https://blog.astrbot.app/">ブログ</a> |
<a href="https://blog.astrbot.app/">Blog</a> |
<a href="https://astrbot.featurebase.app/roadmap">ロードマップ</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/issues">課題の提出</a>
<a href="mailto:community@astrbot.app">Email</a>
<a href="https://github.com/AstrBotDevs/AstrBot/issues">Issue</a>
<a href="mailto:community@astrbot.app">Email Support</a>
</div>
AstrBotは、オープンソースのオールインワンAgentic個人およびグループチャットアシスタントです。QQ、Telegram、WeCom(企業微信)、Lark(飛書)、DingTalk(釘釘)、Slackなど、数十種類の主要なインスタントメッセージングソフトウェアに導入できます。さらに、OpenWebUIに似た軽量のChatUIも組み込まれており、個人、開発者、チーム向けに信頼性が高く拡張可能な会話型AIインフラストラクチャを提供します。個人のAIパートナー、インテリジェントなカスタマーサービス、自動化アシスタント、または企業のナレッジベースであっても、AstrBotはインスタントメッセージングプラットフォームのワークフロー内でAIアプリケーションを迅速に構築することを可能にします。
AstrBot は、主要なインスタントメッセージングアプリと統合できるオープンソースのオールインワン Agent チャットボットプラットフォームです。個人、開発者、チームに信頼性が高くスケーラブルな会話型 AI インフラストラクチャを提供します。パーソナル AI コンパニオン、インテリジェントカスタマーサービス、オートメーションアシスタント、エンタープライズナレッジベースなど、AstrBot を使用すると、IM プラットフォームのワークフロー内で本番環境対応の AI アプリケーションを迅速に構築できます。
![landingpage](https://github.com/user-attachments/assets/45fc5699-cddf-4e21-af35-13040706f6c0)
![screenshot_1 5x_postspark_2026-02-27_22-37-45](https://github.com/user-attachments/assets/f17cdb90-52d7-4773-be2e-ff64b566af6b)
## 主な機能
1. 💯 無料 & オープンソース。
2. ✨ AI大規模モデル対話、マルチモーダル、エージェント、MCP、スキル、ナレッジベース、人格設定、対話の自動圧縮。
3. 🤖 Dify、Alibaba Bailian(阿里雲百煉)、Cozeなどのエージェントプラットフォームとの連携をサポート。
4. 🌐 マルチプラットフォーム対応:QQ、WeCom、Lark、DingTalk、WeChat公式アカウント、Telegram、Slack、その他[多数](#対応メッセージングプラットフォーム)。
5. 📦 プラグイン拡張:1000以上のプラグインがワンクリックでインストール可能。
6. 🛡️ [Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html):隔離された環境で、あらゆるコードの安全な実行、シェル呼び出し、セッションレベルのリソース再利用が可能。
7. 💻 WebUIサポート。
8. 🌈 Web ChatUIサポート:ChatUIにはプロキシサンドボックス、Web検索などが組み込まれています。
9. 🌐 国際化(i18n)サポート。
2. ✨ AI大規模言語モデル対話、マルチモーダル、Agent、MCP、Skills、ナレッジベース、ペルソナ設定、対話の自動圧縮。
3. 🤖 Dify、Alibaba Cloud Bailian(百煉)、Coze などのAgentプラットフォームへの接続をサポート。
4. 🌐 マルチプラットフォーム:QQ、企業微信(WeCom)、飛書(Lark)、釘釘(DingTalk)、WeChat公式アカウント、Telegram、Slack、[その他](#サポートされているメッセージプラットフォーム)に対応。
5. 📦 プラグイン拡張:1000を超える既存プラグインをワンクリックでインストール可能。
6. 🛡️ 隔離環境[Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html):コードの安全な実行、Shell呼び出し、セッションレベルのリソース再利用。
7. 💻 WebUI 対応。
8. 🌈 Web ChatUI 対応:ChatUI内にAgent Sandboxやウェブ検索などを内蔵。
9. 🌐 多言語対応(i18n)。
<br>
<table align="center">
<tr align="center">
<th>💙 ロールプレイ & 感情的な付き添い</th>
<th>✨ 能動的エージェント</th>
<th>🚀 汎用Agentic能力</th>
<th>💙 ロールプレイ & 感情的な対話</th>
<th>✨ プロアクティブ・エージェント (Proactive Agent)</th>
<th>🚀 汎用 エージェント的能力</th>
<th>🧩 1000+ コミュニティプラグイン</th>
</tr>
<tr>
@@ -73,63 +73,60 @@ AstrBotは、オープンソースのオールインワンAgentic個人および
### ワンクリックデプロイ
AstrBotをすぐに試してみたい方で、コマンドラインに慣れており、`uv`環境を自分でインストールできる方には、`uv`を使用したワンクリックデプロイをお勧めします⚡️。
AstrBot を素早く試したいユーザーで、コマンドラインに慣れており `uv` 環境を自分でインストールできる場合は、`uv` のワンクリックデプロイをおすすめします ⚡️:
```bash
uv tool install astrbot
astrbot init # 初回のみ環境初期化のために実行
astrbot run # astrbot run --backend-only バックエンドサービスのみ起動
# 開発版のインストール(修正や新機能が多いですが、不安定な場合があります。開発者向け)
uv tool install git+https://github.com/AstrBotDevs/AstrBot@dev
astrbot init # 初回のみ実行して環境を初期化します
astrbot run
```
> [uv](https://docs.astral.sh/uv/)のインストールが必要です。
> [uv](https://docs.astral.sh/uv/) のインストールが必要です。
> [!NOTE]
> macOSユーザーの場合:macOSのセキュリティチェックにより、`astrbot`コマンドの初回実行に時間がかかる場合があります(約10〜20秒)。
> macOS ユーザーの場合:macOS のセキュリティチェックにより、`astrbot` コマンドの初回実行に時間がかかる場合があります(約 10〜20 秒)。
`astrbot`の更新:
`astrbot` の更新:
```bash
uv tool upgrade astrbot
```
### Dockerデプロイ
### Docker デプロイ
コンテナに精通しており、より安定的で本番環境に適したデプロイ方法を好むユーザーには、Docker / Docker Composeを使用したAstrBotのデプロイをお勧めします。
コンテナ運用に慣れており、より安定した本番向けのデプロイ方法を求めるユーザーには、Docker / Docker Compose での AstrBot デプロイをおすすめします。
公式ドキュメントの[Dockerを使用してAstrBotをデプロイする](https://astrbot.app/deploy/astrbot/docker.html)を参照してください。
公式ドキュメント [Docker を使用した AstrBot のデプロイ](https://astrbot.app/deploy/astrbot/docker.html#%E4%BD%BF%E7%94%A8-docker-%E9%83%A8%E7%BD%B2-astrbot) をご参照ください。
### RainYun(雨云)でのデプロイ
### 雨云でのデプロイ
サーバーを自分で管理せずにAstrBotをワンクリックでデプロイしたいユーザーには、RainYunのワンクリッククラウドデプロイサービスをお勧めします☁️:
AstrBot をワンクリックでデプロイしたく、サーバーを自分で管理したくないユーザーには、雨云のワンクリッククラウドデプロイサービスをおすすめします ☁️:
[![Deploy on RainYun](https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-en.svg)](https://app.rainyun.com/apps/rca/store/5994?ref=NjU1ODg0)
### デスクトップクライアントデプロイ
### デスクトップアプリのデプロイ
デスクトップでAstrBotを使用し、主にChatUIを入り口として使用したいユーザーには、AstrBot Appをお勧めします。
デスクトップで AstrBot を使い、主に ChatUI を入口として利用するユーザーには、AstrBot App をおすすめします。
[AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop)にアクセスしてダウンロードおよびインストールしてください。この方法はデスクトップ利用向けであり、サーバーシナリオには推奨されません。
[AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) からダウンロードしてインストールしてください。この方式はデスクトップ向けであり、サーバー用途には推奨されません。
### ランチャーデプロイ
### ランチャーのデプロイ
同じくデスクトップ向けで、迅速にデプロイし、環境を分離して複数起動したいユーザーには、AstrBot Launcherをお勧めします。
同じくデスクトップで、素早くデプロイしつつ環境を分離して多重起動したいユーザーには、AstrBot Launcher をおすすめします。
[AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher)にアクセスしてダウンロードおよびインストールしてください。
[AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher) からダウンロードしてインストールしてください。
### Replitでのデプロイ
### Replit でのデプロイ
Replitデプロイはコミュニティによって維持されており、オンラインデモや軽量な試用シナリオに適しています。
Replit デプロイはコミュニティ提供の方式で、オンラインデモや軽量な試用に向いています。
[![Run on Repl.it](https://repl.it/badge/github/AstrBotDevs/AstrBot)](https://repl.it/github/AstrBotDevs/AstrBot)
### AUR
AUR方式はArch Linuxユーザー向けで、システムパッケージマネージャーを通じてAstrBotをインストールしたい場合に適しています。
AUR 方式は Arch Linux ユーザー向けで、システムのパッケージ運用に合わせて AstrBot を導入したい場合に適しています。
ターミナルで以下のコマンドを実行して`astrbot-git`パッケージをインストールすると、起動して使用できます。
次のコマンドで `astrbot-git` をインストールし、ローカル環境で AstrBot を起動してください。
```bash
yay -S astrbot-git
@@ -137,155 +134,124 @@ yay -S astrbot-git
**その他のデプロイ方法**
パネル化や高度なカスタマイズデプロイが必要な場合は、[BT Panel(宝塔パネル)](https://astrbot.app/deploy/astrbot/btpanel.html)(BT Panelアプリストアインストール)、[1Panel](https://astrbot.app/deploy/astrbot/1panel.html)(1Panelアプリストアインストール)、[CasaOS](https://astrbot.app/deploy/astrbot/casaos.html)(NAS / ホームサーバーの視覚的デプロイ)、および[手動デプロイ](https://astrbot.app/deploy/astrbot/cli.html)(ソースコードと`uv`に基づく完全なカスタムインストール)を参照してください。
パネル操作での導入やより高度なカスタマイズが必要な場合は、[宝塔パネルデプロイ](https://astrbot.app/deploy/astrbot/btpanel.html)(BT Panel 経由の導入)、[1Panel デプロイ](https://astrbot.app/deploy/astrbot/1panel.html)(1Panel アプリマーケット経由)、[CasaOS デプロイ](https://astrbot.app/deploy/astrbot/casaos.html)(NAS / ホームサーバー向け可視化導入)、[手動デプロイ](https://astrbot.app/deploy/astrbot/cli.html)(`uv` とソースベースのフルカスタム導入)を参照してください。
## 対応メッセージングプラットフォーム
## サポートされているメッセージプラットフォーム
AstrBotを普段使用しているチャットプラットフォームに接続しましょう。
AstrBot をよく使うチャットプラットフォームに接続できます。
| プラットフォーム | 管理者 |
| プラットフォーム | 保守 |
|---------|---------------|
| **QQ** | 公式管理 |
| **OneBot v11** | 公式管理 |
| **Telegram** | 公式管理 |
| **WeComアプリ & WeComボット** | 公式管理 |
| **WeChatカスタマーサービス & WeChat公式アカウント** | 公式管理 |
| **Lark (飛書)** | 公式管理 |
| **DingTalk (釘釘)** | 公式管理 |
| **Slack** | 公式管理 |
| **Discord** | 公式管理 |
| **LINE** | 公式管理 |
| **Satori** | 公式管理 |
| **Misskey** | 公式管理 |
| **Whatsapp (対応予定)** | 公式管理 |
| [**Matrix**](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | コミュニティ管理 |
| [**KOOK**](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | コミュニティ管理 |
| [**VoceChat**](https://github.com/HikariFroya/astrbot_plugin_vocechat) | コミュニティ管理 |
| QQ | 公式 |
| OneBot v11 プロトコル実装 | 公式 |
| Telegram | 公式 |
| WeChat Work アプリケーション & WeChat Work インテリジェントボット | 公式 |
| WeChat カスタマーサービス & WeChat 公式アカウント | 公式 |
| Feishu (Lark) | 公式 |
| DingTalk | 公式 |
| Slack | 公式 |
| Discord | 公式 |
| LINE | 公式 |
| Satori | 公式 |
| Misskey | 公式 |
| WhatsApp (近日対応予定) | 公式 |
| [Matrix](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | コミュニティ |
| [KOOK](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | コミュニティ |
| [VoceChat](https://github.com/HikariFroya/astrbot_plugin_vocechat) | コミュニティ |
## 対応モデルプロバイダー
| プロバイダー | タイプ |
## サポートされているモデルサービス
| サービス | 種類 |
|---------|---------------|
| カスタム | OpenAI API互換の任意のサービス |
| OpenAI | LLM |
| Anthropic | LLM |
| Google Gemini | LLM |
| Moonshot AI | LLM |
| Zhipu AI (智譜AI) | LLM |
| DeepSeek | LLM |
| Ollama (ローカル) | LLM |
| LM Studio (ローカル) | LLM |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | LLM (APIゲートウェイ, 全モデル対応) |
| [Uyun AI (優雲智算)](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | LLM (APIゲートウェイ, 全モデル対応) |
| [SiliconFlow (硅基流動)](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | LLM (APIゲートウェイ, 全モデル対応) |
| [PPIO](https://ppio.com/user/register?invited_by=AIOONE) | LLM (APIゲートウェイ, 全モデル対応) |
| [302.AI](https://share.302.ai/rr1M3l) | LLM (APIゲートウェイ, 全モデル対応)|
| [TokenPony (小馬算力)](https://www.tokenpony.cn/3YPyf) | LLM (APIゲートウェイ, 全モデル対応)|
| ModelScope | LLM |
| OneAPI | LLM |
| Dify | LLMOpsプラットフォーム |
| Alibaba Bailian (阿里雲百煉) | LLMOpsプラットフォーム |
| Coze | LLMOpsプラットフォーム |
| OpenAI Whisper | 音声認識 (STT) |
| SenseVoice | 音声認識 (STT) |
| OpenAI TTS | 音声合成 (TTS) |
| Gemini TTS | 音声合成 (TTS) |
| GPT-Sovits-Inference | 音声合成 (TTS) |
| GPT-Sovits | 音声合成 (TTS) |
| FishAudio | 音声合成 (TTS) |
| Edge TTS | 音声合成 (TTS) |
| Alibaba Bailian TTS | 音声合成 (TTS) |
| Azure TTS | 音声合成 (TTS) |
| Minimax TTS | 音声合成 (TTS) |
| Volcengine TTS (火山エンジン) | 音声合成 (TTS) |
| OpenAI および互換サービス | 大規模言語モデルサービス |
| Anthropic | 大規模言語モデルサービス |
| Google Gemini | 大規模言語モデルサービス |
| Moonshot AI | 大規模言語モデルサービス |
| 智谱 AI | 大規模言語モデルサービス |
| DeepSeek | 大規模言語モデルサービス |
| Ollama (セルフホスト) | 大規模言語モデルサービス |
| LM Studio (セルフホスト) | 大規模言語モデルサービス |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | 大規模言語モデルサービス(APIゲートウェイ、全モデル対応) |
| [優云智算](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | 大規模言語モデルサービス |
| [302.AI](https://share.302.ai/rr1M3l) | 大規模言語モデルサービス |
| [小馬算力](https://www.tokenpony.cn/3YPyf) | 大規模言語モデルサービス |
| [硅基流動](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | 大規模言語モデルサービス |
| [PPIO 派欧云](https://ppio.com/user/register?invited_by=AIOONE) | 大規模言語モデルサービス |
| ModelScope | 大規模言語モデルサービス |
| OneAPI | 大規模言語モデルサービス |
| Dify | LLMOps プラットフォーム |
| Alibaba Cloud 百炼アプリケーション | LLMOps プラットフォーム |
| Coze | LLMOps プラットフォーム |
| OpenAI Whisper | 音声認識サービス |
| SenseVoice | 音声認識サービス |
| OpenAI TTS | 音声合成サービス |
| Gemini TTS | 音声合成サービス |
| GPT-Sovits-Inference | 音声合成サービス |
| GPT-Sovits | 音声合成サービス |
| FishAudio | 音声合成サービス |
| Edge TTS | 音声合成サービス |
| Alibaba Cloud 百炼 TTS | 音声合成サービス |
| Azure TTS | 音声合成サービス |
| Minimax TTS | 音声合成サービス |
| Volcano Engine TTS | 音声合成サービス |
## ❤️ Sponsors
## ❤️ コントリビューション
<p align="center">
<img alt="sponsors" src="https://sponsors.astrbot.app/?v=1">
</p>
Issue や Pull Request は大歓迎です!このプロジェクトに変更を送信してください :)
### コントリビュート方法
## ❤️ 貢献
IssueやPull Requestは大歓迎です!変更をこのプロジェクトに送信してください :)
### 貢献方法
問題の確認やPR(プルリクエスト)のレビューを通じて貢献できます。コミュニティの貢献を促進するために、あらゆる問題やPRへの参加を歓迎します。もちろん、これらは提案に過ぎず、どのような方法で貢献しても構いません。新機能の追加については、まずIssueで議論してください。
機能的なPRは`dev`ブランチにマージすることをお勧めします。テスト修正後にメインブランチにマージされ、新しいバージョンとしてリリースされます。
コンフリクトを減らすために、以下のことを推奨します:
1. 作業ブランチは`dev`ブランチに基づいて作成し、`main`ブランチで直接作業することは避けてください。
2. PRを送信する際は、ターゲットブランチとして`dev`ブランチを選択してください。
3. 定期的に`dev`ブランチをローカルに同期し、`git pull`を頻繁に使用してください。
Issue を確認したり、PR(プルリクエスト)のレビューを手伝うことで貢献できます。どんな Issue や PR への参加も歓迎され、コミュニティ貢献を促進します。もちろん、これらは提案に過ぎず、どんな方法でも貢献できます。新機能の追加については、まず Issue で議論してください。
### 開発環境
AstrBotはコードのフォーマットとチェックに`ruff`を使用しています。
AstrBot はコードのフォーマットとチェックに `ruff` を使用しています。
```bash
git clone https://github.com/AstrBotDevs/AstrBot
git switch dev # 開発ブランチに切り替え
pip install pre-commit # または uv tool install pre-commit
pip install pre-commit
pre-commit install
```
ローカルでのインストールとテストには`uv`の使用をお勧めします。
```bash
uv tool install -e . --force
astrbot init
astrbot run
```
フロントエンドのデバッグ
```bash
astrbot run --backend-only
cd dashboard
bun install # または pnpm など
bun dev
```
### QQグループ
## 🌍 コミュニティ
- 9群: 1076659624 (新)
- 10群: 1078079676 (新)
- 1群:322154837
- 3群:630166526
- 5群:822130018
- 6群:753075035
- 7群:743746109
- 8群:1030353265
- 開発者群(雑談):975206796
- 開発者群(公式):1039761811
### QQ グループ
### Discordチャンネル
- 12群: 916228568 (新)
- 9群: 1076659624 (満員)
- 10群: 1078079676 (満員)
- 11群: 704659519 (満員)
- 1群: 322154837 (満員)
- 3群: 630166526 (満員)
- 4群: 1077826412 (満員)
- 5群: 822130018 (満員)
- 6群: 753075035 (満員)
- 7群: 743746109 (満員)
- 8群: 1030353265 (満員)
- 開発者群: 975206796
- 開発者群(正式): 1039761811
- [Discord](https://discord.gg/hAVk6tgV36)
### Discord サーバー
<a href="https://discord.gg/hAVk6tgV36"><img alt="Discord_community" src="https://img.shields.io/badge/Discord-AstrBot-purple?style=for-the-badge&color=76bad9"></a>
## ❤️ Special Thanks
AstrBotに貢献してくださったすべてのコントリビューターとプラグイン開発者に感謝します ❤️
AstrBot への貢献をしていただいたすべてのコントリビューターとプラグイン開発者に特別な感謝を ❤️
<a href="https://github.com/AstrBotDevs/AstrBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=AstrBotDevs/AstrBot&max=200&columns=14" />
</a>
さらに、このプロジェクトの誕生は、以下のオープンソースプロジェクトの助けなしにはあり得ませんでした:
また、このプロジェクトの誕生は以下のオープンソースプロジェクトの助けなしには実現できませんでした:
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - 偉大な猫フレームワーク
オープンソースプロジェクトのフレンドリーリンク:
- [NoneBot2](https://github.com/nonebot/nonebot2) - 優れたPython非同期チャットボットフレームワーク
- [Koishi](https://github.com/koishijs/koishi) - 優れたNode.jsチャットボットフレームワーク
- [MaiBot](https://github.com/Mai-with-u/MaiBot) - 優れた擬人化AIチャットボット
- [nekro-agent](https://github.com/KroMiose/nekro-agent) - 優れたエージェントチャットボット
- [LangBot](https://github.com/langbot-app/LangBot) - 優れたマルチプラットフォームAIチャットボット
- [ChatLuna](https://github.com/ChatLunaLab/chatluna) - 優れたマルチプラットフォームAIチャットボットKoishiプラグイン
- [Operit AI](https://github.com/AAswordman/Operit) - 優れたAIインテリジェントアシスタントAndroidアプリ
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - 素晴らしい猫猫フレームワーク
## ⭐ Star History
> [!TIP]
> もしこのプロジェクトがあなたの生活や仕事の助けになったなら、あるいはこのプロジェクトの将来の発展に関心があるなら、プロジェクトにStarを付けてください。これは私たちがこのオープンソースプロジェクトを維持するための原動力となります <3
> このプロジェクトがあなたの生活や仕事に役立ったり、このプロジェクトの今後の発展に関心がある場合は、プロジェクトに Star をください。これがこのオープンソースプロジェクトを維持する原動力です <3
<div align="center">
@@ -295,7 +261,7 @@ AstrBotに貢献してくださったすべてのコントリビューターと
<div align="center">
_付き添いと能力は決して対立するものであってはなりません。私たちが創造したいのは、感情を理解し、寄り添いながらも、確実に仕事を遂行できるロボットです。_
_共感力と能力は決して対立するものではありません。私たちが目指すのは、感情を理解し、心の支えとなるだけでなく、確実に仕事をこなせるロボットの創造です。_
_私は、高性能ですから!_
+122 -156
View File
@@ -2,11 +2,13 @@
<div align="center">
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">简体中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README.md">English</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh-TW.md">繁體中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_ja.md">日本語</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_fr.md">Français</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">简体中文</a>
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_fr.md">Français</a>
<br>
<div>
<a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
@@ -19,47 +21,45 @@
<img src="https://img.shields.io/github/v/release/AstrBotDevs/AstrBot?color=76bad9" href="https://github.com/AstrBotDevs/AstrBot/releases/latest">
<img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="python">
<img src="https://deepwiki.com/badge.svg" href="https://deepwiki.com/AstrBotDevs/AstrBot">
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFZIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjczODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
<a href="https://hub.docker.com/r/soulter/astrbot"><img alt="Docker pull" src="https://img.shields.io/docker/pulls/soulter/astrbot.svg?color=76bad9"/></a>
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%20Plugins&label=%D0%9C%D0%B0%D1%80%D0%BA%D0%B5%D1%82%D0%BF%D0%BB%D0%B5%D0%B9%D1%81&cacheSeconds=3600">
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%20&label=%D0%9C%D0%B0%D1%80%D0%BA%D0%B5%D1%82%D0%BF%D0%BB%D0%B5%D0%B9%D1%81&cacheSeconds=3600">
<img src="https://gitcode.com/Soulter/AstrBot/star/badge.svg" href="https://gitcode.com/Soulter/AstrBot">
</div>
<br>
<a href="https://astrbot.app/">Главная</a> |
<a href="https://astrbot.app/">Документация</a> |
<a href="https://blog.astrbot.app/">Блог</a> |
<a href="https://astrbot.featurebase.app/roadmap">Roadmap</a> |
<a href="https://astrbot.featurebase.app/roadmap">Дорожная карта</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/issues">Сообщить о проблеме</a>
<a href="mailto:community@astrbot.app">Email</a>
<a href="mailto:community@astrbot.app">Email Support</a>
</div>
AstrBot — это универсальный агентский помощник для личных и групповых чатов с открытым исходным кодом. Он может быть развернут в десятках популярных мессенджеров, таких как QQ, Telegram, WeCom (Enterprise WeChat), Lark (Feishu), DingTalk, Slack и других. Кроме того, он имеет встроенный легковесный веб-интерфейс чата (ChatUI), похожий на OpenWebUI, создавая надежную и масштабируемую диалоговую интеллектуальную инфраструктуру для частных лиц, разработчиков и команд. Будь то личный AI-компаньон, интеллектуальная служба поддержки, автоматизированный помощник или корпоративная база знаний, AstrBot позволяет быстро создавать AI-приложения в рабочем процессе ваших платформ обмена мгновенными сообщениями.
AstrBot — это универсальная платформа Agent-чатботов с открытым исходным кодом, которая интегрируется с основными приложениями для обмена мгновенными сообщениями. Она предоставляет надёжную и масштабируемую инфраструктуру разговорного ИИ для частных лиц, разработчиков и команд. Будь то персональный ИИ-компаньон, интеллектуальная служба поддержки, автоматизированный помощник или корпоративная база знаний — AstrBot позволяет быстро создавать готовые к использованию ИИ-приложения в рабочих процессах вашей платформы обмена сообщениями.
![landingpage](https://github.com/user-attachments/assets/45fc5699-cddf-4e21-af35-13040706f6c0)
![521771166-00782c4c-4437-4d97-aabc-605e3738da5c (1)](https://github.com/user-attachments/assets/61e7b505-f7db-41aa-a75f-4ef8f079b8ba)
## Основные возможности
1. 💯 Бесплатно и с открытым исходным кодом.
2. ✨ Поддержка диалога с большими языковыми моделями (LLM), мультимодальность, Агенты, MCP, Навыки (Skills), База знаний, Персонализация, автоматическое сжатие диалога.
3. 🤖 Поддержка интеграции с платформами агентов, такими как Dify, Alibaba Bailian, Coze и др.
4. 🌐 Мультиплатформенность: поддержка QQ, WeCom, Lark, DingTalk, WeChat Official Account, Telegram, Slack и [других](#поддерживаемые-платформы-сообщений).
1. 💯 Бесплатно & Открытый исходный код.
2. ✨ Диалоги с ИИ-моделями, мультимодальность, Agent, MCP, Skills, База знаний, Настройка личности, автоматическое сжатие диалогов.
3. 🤖 Поддержка интеграции с платформами Agents, такими как Dify, Alibaba Cloud Bailian, Coze и др.
4. 🌐 Мультиплатформенность: поддержка QQ, WeChat для предприятий, Feishu, DingTalk, публичных аккаунтов WeChat, Telegram, Slack и [других](#Поддерживаемые-платформы-обмена-сообщениями).
5. 📦 Расширение плагинами: доступно более 1000 плагинов для установки в один клик.
6. 🛡️ [Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html): Изолированная среда для безопасного выполнения любого кода, вызова Shell и повторного использования ресурсов на уровне сессии.
6. 🛡️ Изолированная среда[Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html): безопасное выполнение любого кода, вызов Shell, повторное использование ресурсов на уровне сессии.
7. 💻 Поддержка WebUI.
8. 🌈 Поддержка Web ChatUI: встроенная прокси-песочница, веб-поиск и многое другое внутри ChatUI.
8. 🌈 Поддержка Web ChatUI: встроенная песочница агента, веб-поиск и др.
9. 🌐 Поддержка интернационализации (i18n).
<br>
<table align="center">
<tr align="center">
<th>💙 Ролевые игры и Эмоциональное общение</th>
<th>✨ Проактивный Агент</th>
<th>🚀 Общие агентские возможности</th>
<th>🧩 1000+ Плагинов сообщества</th>
<th>💙 Ролевые игры & Эмоциональная поддержка</th>
<th>✨ Проактивный Агент (Agent)</th>
<th>🚀 Универсальные возможности Агента</th>
<th>🧩 1000+ плагинов сообщества</th>
</tr>
<tr>
<td align="center"><p align="center"><img width="984" height="1746" alt="99b587c5d35eea09d84f33e6cf6cfd4f" src="https://github.com/user-attachments/assets/89196061-3290-458d-b51f-afa178049f84" /></p></td>
@@ -71,194 +71,169 @@ AstrBot — это универсальный агентский помощни
## Быстрый старт
### Развертывание в один клик
### Развёртывание в один клик
Для пользователей, которые хотят быстро протестировать AstrBot, знакомы с командной строкой и могут самостоятельно установить среду `uv`, мы рекомендуем метод развертывания в один клик с помощью `uv` ⚡️.
Для пользователей, которые хотят быстро попробовать AstrBot, знакомы с командной строкой и могут самостоятельно установить окружение `uv`, мы рекомендуем использовать развёртывание в один клик через `uv` ⚡️:
```bash
uv tool install astrbot
astrbot init # Выполните эту команду только в первый раз для инициализации среды
astrbot run # astrbot run --backend-only запускает только бэкенд сервис
# Установка версии для разработчиков (больше исправлений и новых функций, но менее стабильна; подходит для разработчиков)
uv tool install git+https://github.com/AstrBotDevs/AstrBot@dev
astrbot init # Выполните эту команду только при первом запуске для инициализации окружения
astrbot run
```
> Требуется установленный [uv](https://docs.astral.sh/uv/).
> [!NOTE]
> Для пользователей macOS: Из-за проверок безопасности macOS первый запуск команды `astrbot` может занять длительное время (около 10-20 секунд).
> Для пользователей macOS: из-за проверок безопасности macOS первый запуск команды `astrbot` может занять больше времени (около 10-20 секунд).
Обновление `astrbot`:
Обновить `astrbot`:
```bash
uv tool upgrade astrbot
```
### Развертывание через Docker
### Развёртывание Docker
Для пользователей, знакомых с контейнерами и предпочитающих более стабильный метод развертывания, подходящий для производственных сред, мы рекомендуем использовать Docker / Docker Compose для развертывания AstrBot.
Для пользователей, знакомых с контейнерами и которым нужен более стабильный и подходящий для production способ, мы рекомендуем разворачивать AstrBot через Docker / Docker Compose.
Пожалуйста, обратитесь к официальной документации [Развертывание AstrBot с помощью Docker](https://astrbot.app/deploy/astrbot/docker.html).
См. официальную документацию [Развёртывание AstrBot с Docker](https://astrbot.app/deploy/astrbot/docker.html#%E4%BD%BF%E7%94%A8-docker-%E9%83%A8%E7%BD%B2-astrbot).
### Развертывание на RainYun
### Развёртывание на RainYun
Для пользователей, которые хотят развернуть AstrBot в один клик и не хотят самостоятельно управлять серверами, мы рекомендуем облачный сервис развертывания в один клик от RainYun ☁️:
Для пользователей, которые хотят развернуть AstrBot в один клик и не хотят самостоятельно управлять сервером, мы рекомендуем облачный сервис развёртывания в один клик от RainYun ☁️:
[![Deploy on RainYun](https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-en.svg)](https://app.rainyun.com/apps/rca/store/5994?ref=NjU1ODg0)
### Развертывание настольного клиента
### Развёртывание десктопного приложения
Для пользователей, желающих использовать AstrBot на рабочем столе и использовать ChatUI в качестве основного интерфейса, мы рекомендуем приложение AstrBot App.
Для пользователей, которые хотят использовать AstrBot на десктопе и в основном работают через ChatUI, мы рекомендуем AstrBot App.
Перейдите на [AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) для загрузки и установки; этот метод предназначен для использования на рабочем столе и не рекомендуется для серверных сценариев.
Перейдите в [AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop), скачайте и установите приложение; этот вариант предназначен для десктопа и не рекомендуется для серверных сценариев.
### Развертывание через лаунчер
### Развёртывание через лаунчер
Также для настольных компьютеров, для пользователей, которым требуется быстрое развертывание и изоляция среды для нескольких экземпляров, мы рекомендуем AstrBot Launcher.
Также на десктопе, для пользователей, которым нужен быстрый запуск и мультиинстанс с изоляцией окружений, мы рекомендуем AstrBot Launcher.
Перейдите на [AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher) для загрузки и установки.
Перейдите в [AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher), чтобы скачать и установить.
### Развертывание на Replit
### Развёртывание на Replit
Развертывание на Replit поддерживается сообществом и подходит для онлайн-демонстраций и легких тестовых сценариев.
Развёртывание через Replit поддерживается сообществом и подходит для онлайн-демо и лёгких тестовых запусков.
[![Run on Repl.it](https://repl.it/badge/github/AstrBotDevs/AstrBot)](https://repl.it/github/AstrBotDevs/AstrBot)
### AUR
Метод AUR предназначен для пользователей Arch Linux, желающих установить AstrBot через системный менеджер пакетов.
AUR-вариант предназначен для пользователей Arch Linux, которым удобна установка через системный менеджер пакетов.
Выполните приведенную ниже команду в терминале, чтобы установить пакет `astrbot-git`. После завершения установки вы сможете запустить его.
Выполните команду ниже для установки `astrbot-git`, затем запустите AstrBot локально.
```bash
yay -S astrbot-git
```
**Другие методы развертывания**
**Другие способы развёртывания**
Если вам требуется панельное управление или более кастомизированное развертывание, вы можете обратиться к [BT Panel](https://astrbot.app/deploy/astrbot/btpanel.html) (установка через магазин приложений BT Panel), [1Panel](https://astrbot.app/deploy/astrbot/1panel.html) (установка через магазин приложений 1Panel), [CasaOS](https://astrbot.app/deploy/astrbot/casaos.html) (визуальное развертывание для NAS / домашнего сервера) и [Ручное развертывание](https://astrbot.app/deploy/astrbot/cli.html) (полная пользовательская установка на основе исходного кода и `uv`).
Если вам нужна панельная установка или более глубокая кастомизация, смотрите [Развёртывание BT-Panel](https://astrbot.app/deploy/astrbot/btpanel.html) (установка через BT Panel), [Развёртывание 1Panel](https://astrbot.app/deploy/astrbot/1panel.html) (развёртывание через маркетплейс 1Panel), [Развёртывание CasaOS](https://astrbot.app/deploy/astrbot/casaos.html) (визуальный вариант для NAS и домашних серверов) и [Ручное развёртывание](https://astrbot.app/deploy/astrbot/cli.html) (полностью настраиваемая установка из исходников через `uv`).
## Поддерживаемые платформы сообщений
## Поддерживаемые платформы обмена сообщениями
Подключите AstrBot к вашим любимым платформам чата.
Подключите AstrBot к вашим любимым чат-платформам.
| Платформа | Поддержка |
|---------|---------------|
| **QQ** | Официальная |
| **OneBot v11** | Официальная |
| **Telegram** | Официальная |
| **WeCom (Приложение & Смарт-бот)** | Официальная |
| **WeChat (Служба поддержки & Официальный аккаунт)** | Официальная |
| **Lark (Feishu)** | Официальная |
| **DingTalk** | Официальная |
| **Slack** | Официальная |
| **Discord** | Официальная |
| **LINE** | Официальная |
| **Satori** | Официальная |
| **Misskey** | Официальная |
| **Whatsapp (Скоро)** | Официальная |
| [**Matrix**](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | Сообщество |
| [**KOOK**](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | Сообщество |
| [**VoceChat**](https://github.com/HikariFroya/astrbot_plugin_vocechat) | Сообщество |
| QQ | Официальная |
| Реализация протокола OneBot v11 | Официальная |
| Telegram | Официальная |
| Приложение WeChat Work и интеллектуальный бот WeChat Work | Официальная |
| Служба поддержки WeChat и официальные аккаунты WeChat | Официальная |
| Feishu (Lark) | Официальная |
| DingTalk | Официальная |
| Slack | Официальная |
| Discord | Официальная |
| LINE | Официальная |
| Satori | Официальная |
| Misskey | Официальная |
| WhatsApp (Скоро) | Официальная |
| [Matrix](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | Сообщество |
| [KOOK](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | Сообщество |
| [VoceChat](https://github.com/HikariFroya/astrbot_plugin_vocechat) | Сообщество |
## Поддерживаемые провайдеры моделей
## Поддерживаемые сервисы моделей
| Провайдер | Тип |
| Сервис | Тип |
|---------|---------------|
| Пользовательский | Любой сервис, совместимый с OpenAI API |
| OpenAI | LLM |
| Anthropic | LLM |
| Google Gemini | LLM |
| Moonshot AI | LLM |
| Zhipu AI | LLM |
| DeepSeek | LLM |
| Ollama (Локально) | LLM |
| LM Studio (Локально) | LLM |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | LLM (API шлюз, поддерживает все модели) |
| [Uyun AI](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | LLM (API шлюз, поддерживает все модели) |
| [SiliconFlow](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | LLM (API шлюз, поддерживает все модели) |
| [PPIO](https://ppio.com/user/register?invited_by=AIOONE) | LLM (API шлюз, поддерживает все модели) |
| [302.AI](https://share.302.ai/rr1M3l) | LLM (API шлюз, поддерживает все модели)|
| [TokenPony](https://www.tokenpony.cn/3YPyf) | LLM (API шлюз, поддерживает все модели)|
| ModelScope | LLM |
| OneAPI | LLM |
| Dify | Платформа LLMOps |
| Alibaba Bailian | Платформа LLMOps |
| Coze | Платформа LLMOps |
| OpenAI Whisper | Распознавание речи (STT) |
| SenseVoice | Распознавание речи (STT) |
| OpenAI TTS | Синтез речи (TTS) |
| Gemini TTS | Синтез речи (TTS) |
| GPT-Sovits-Inference | Синтез речи (TTS) |
| GPT-Sovits | Синтез речи (TTS) |
| FishAudio | Синтез речи (TTS) |
| Edge TTS | Синтез речи (TTS) |
| Alibaba Bailian TTS | Синтез речи (TTS) |
| Azure TTS | Синтез речи (TTS) |
| Minimax TTS | Синтез речи (TTS) |
| Volcengine TTS | Синтез речи (TTS) |
## ❤️ Sponsors
<p align="center">
<img alt="sponsors" src="https://sponsors.astrbot.app/?v=1">
</p>
| OpenAI и совместимые сервисы | Сервисы LLM |
| Anthropic | Сервисы LLM |
| Google Gemini | Сервисы LLM |
| Moonshot AI | Сервисы LLM |
| Zhipu AI | Сервисы LLM |
| DeepSeek | Сервисы LLM |
| Ollama (Самостоятельное размещение) | Сервисы LLM |
| LM Studio (Самостоятельное размещение) | Сервисы LLM |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | Сервисы LLM (API-шлюз, поддерживает все модели) |
| [CompShare](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | Сервисы LLM |
| [302.AI](https://share.302.ai/rr1M3l) | Сервисы LLM |
| [TokenPony](https://www.tokenpony.cn/3YPyf) | Сервисы LLM |
| [SiliconFlow](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | Сервисы LLM |
| [PPIO Cloud](https://ppio.com/user/register?invited_by=AIOONE) | Сервисы LLM |
| ModelScope | Сервисы LLM |
| OneAPI | Сервисы LLM |
| Dify | Платформы LLMOps |
| Приложения Alibaba Cloud Bailian | Платформы LLMOps |
| Coze | Платформы LLMOps |
| OpenAI Whisper | Сервисы распознавания речи |
| SenseVoice | Сервисы распознавания речи |
| OpenAI TTS | Сервисы синтеза речи |
| Gemini TTS | Сервисы синтеза речи |
| GPT-Sovits-Inference | Сервисы синтеза речи |
| GPT-Sovits | Сервисы синтеза речи |
| FishAudio | Сервисы синтеза речи |
| Edge TTS | Сервисы синтеза речи |
| Alibaba Cloud Bailian TTS | Сервисы синтеза речи |
| Azure TTS | Сервисы синтеза речи |
| Minimax TTS | Сервисы синтеза речи |
| Volcano Engine TTS | Сервисы синтеза речи |
## ❤️ Вклад в проект
Мы приветствуем любые Issues и Pull Requests! Просто отправьте свои изменения в этот проект :)
Issues и Pull Request всегда приветствуются! Не стесняйтесь отправлять свои изменения в этот проект :)
### Как внести вклад
Вы можете внести свой вклад, просматривая проблемы (Issues) или помогая проверять PR (Pull Requests). Любая проблема или PR приветствуются для поощрения участия сообщества. Конечно, это всего лишь предложения, вы можете внести свой вклад любым способом. Для добавления новых функций, пожалуйста, сначала обсудите это через Issue.
Рекомендуется объединять функциональные PR в ветку `dev`, которая будет объединена с основной веткой (`main`) и выпущена как новая версия после тестирования изменений.
Для уменьшения конфликтов мы рекомендуем:
1. Создавайте рабочую ветку на основе ветки `dev`, избегайте работы напрямую в ветке `main`.
2. При отправке PR выбирайте ветку `dev` в качестве целевой.
3. Регулярно синхронизируйте ветку `dev` с локальной средой, чаще используйте `git pull`.
Вы можете внести вклад, просматривая issues или помогая с ревью pull request. Любые issues или PR приветствуются для поощрения участия сообщества. Конечно, это лишь предложения — вы можете вносить вклад любым удобным для вас способом. Для добавления новых функций сначала обсудите это через Issue.
### Среда разработки
AstrBot использует `ruff` для форматирования и проверки кода.
AstrBot использует `ruff` для форматирования и линтинга кода.
```bash
git clone https://github.com/AstrBotDevs/AstrBot
git switch dev # Переключиться на ветку разработки
pip install pre-commit # или uv tool install pre-commit
pip install pre-commit
pre-commit install
```
Рекомендуется использовать `uv` для локальной установки и тестирования:
```bash
uv tool install -e . --force
astrbot init
astrbot run
```
Отладка фронтенда:
```bash
astrbot run --backend-only
cd dashboard
bun install # или pnpm и т.д.
bun dev
```
## 🌍 Сообщество
### Группы QQ
- Группа 9: 1076659624 (Новая)
- Группа 10: 1078079676 (Новая)
- Группа 1: 322154837
- Группа 3: 630166526
- Группа 5: 822130018
- Группа 6: 753075035
- Группа 7: 743746109
- Группа 8: 1030353265
- Группа разработчиков (Неформальное общение): 975206796
- Группа разработчиков (Официальная): 1039761811
- Группа 12: 916228568 (новая)
- Группа 9: 1076659624 (полная)
- Группа 10: 1078079676 (полная)
- Группа 11: 704659519 (полная)
- Группа 1: 322154837 (полная)
- Группа 3: 630166526 (полная)
- Группа 4: 1077826412 (полная)
- Группа 5: 822130018 (полная)
- Группа 6: 753075035 (полная)
- Группа 7: 743746109 (полная)
- Группа 8: 1030353265 (полная)
- Группа разработчиков: 975206796
- Группа разработчиков (официальная): 1039761811
### Канал Discord
### Сервер Discord
- [Discord](https://discord.gg/hAVk6tgV36)
<a href="https://discord.gg/hAVk6tgV36"><img alt="Discord_community" src="https://img.shields.io/badge/Discord-AstrBot-purple?style=for-the-badge&color=76bad9"></a>
## ❤️ Особая благодарность
@@ -268,24 +243,15 @@ bun dev
<img src="https://contrib.rocks/image?repo=AstrBotDevs/AstrBot&max=200&columns=14" />
</a>
Кроме того, рождение этого проекта было бы невозможным без помощи следующих проектов с открытым исходным кодом:
Кроме того, рождение этого проекта было бы невозможно без помощи следующих проектов с открытым исходным кодом:
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - Великий кошачий фреймворк
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - Замечательный кошачий фреймворк
Дружественные ссылки на проекты с открытым исходным кодом:
- [NoneBot2](https://github.com/nonebot/nonebot2) - Отличный асинхронный фреймворк ChatBot на Python
- [Koishi](https://github.com/koishijs/koishi) - Отличный фреймворк ChatBot на Node.js
- [MaiBot](https://github.com/Mai-with-u/MaiBot) - Отличный антропоморфный AI ChatBot
- [nekro-agent](https://github.com/KroMiose/nekro-agent) - Отличный агентский ChatBot
- [LangBot](https://github.com/langbot-app/LangBot) - Отличный мультиплатформенный AI ChatBot
- [ChatLuna](https://github.com/ChatLunaLab/chatluna) - Отличный плагин мультиплатформенного AI ChatBot для Koishi
- [Operit AI](https://github.com/AAswordman/Operit) - Отличное Android-приложение интеллектуального AI-помощника
## ⭐ История звезд
## ⭐ История звёзд
> [!TIP]
> Если этот проект помог вам в жизни или работе, или если вы заинтересованы в будущем развитии этого проекта, пожалуйста, поставьте проекту звезду (Star). Это наша мотивация поддерживать этот проект с открытым исходным кодом <3
> Если этот проект помог вам в жизни или работе, или если вас интересует его будущее развитие, пожалуйста, поставьте проекту звезду. Это движущая сила поддержки этого проекта с открытым исходным кодом <3
<div align="center">
@@ -295,9 +261,9 @@ bun dev
<div align="center">
_Компаньонство и способности никогда не должны быть противоположностями. Мы надеемся создать робота, который сможет одновременно понимать эмоции, быть компаньоном и надежно выполнять работу._
_Сопровождение и способности никогда не должны быть противоположностями. Мы стремимся создать робота, который сможет как понимать эмоции, оказывать душевную поддержку, так и надёжно выполнять работу._
_私は、高性能ですから!_ (Я высокопроизводительный!)
_私は、高性能ですから!_
<img src="https://files.astrbot.app/watashiwa-koseino-desukara.gif" width="100"/>
+98 -133
View File
@@ -2,12 +2,14 @@
<div align="center">
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README.md">English</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_zh.md">简体中文</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README.md">English</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_ja.md">日本語</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_fr.md">Français</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/blob/master/README_ru.md">Русский</a>
<br>
<div>
<a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://hellogithub.com/repository/AstrBotDevs/AstrBot" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=d127d50cd5e54c5382328acc3bb25483&claim_uid=ZO9by7qCXgSd6Lp&t=2" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
@@ -27,30 +29,28 @@
<br>
<a href="https://astrbot.app/">首頁</a> |
<a href="https://astrbot.app/">文檔</a> |
<a href="https://blog.astrbot.app/">博客</a> |
<a href="https://astrbot.app/">文件</a> |
<a href="https://blog.astrbot.app/">Blog</a> |
<a href="https://astrbot.featurebase.app/roadmap">路線圖</a> |
<a href="https://github.com/AstrBotDevs/AstrBot/issues">問題提交</a>
<a href="https://github.com/AstrBotDevs/AstrBot/issues">問題回報</a>
<a href="mailto:community@astrbot.app">Email</a>
</div>
AstrBot 是一個開源的一站式 Agentic 個人和群聊助手,可在 QQ、Telegram、企業微信、飛書、釘钉、Slack 等數十款主流即時通訊軟件上部署,此外還內置類似 OpenWebUI 的輕量化 ChatUI,為個人、開發者和團隊打造可靠、可擴展的對話式智能基礎設施。無論是個人 AI 夥伴、智能客服、自動化助手,還是企業知識庫,AstrBot 都能在你的即時通訊軟件平台的工作流中快速構建 AI 應用。
AstrBot 是一個開源的一站式 Agent 聊天機器人平台,可接入主流即時通訊軟體,為個人、開發者和團隊打造可靠、可擴展的對話式智慧基礎設施。無論是個人 AI 夥伴、智慧客服、自動化助手,還是企業知識庫,AstrBot 都能在您的即時通訊軟體平台的工作流程中快速構建生產可用的 AI 應用程式。
![landingpage](https://github.com/user-attachments/assets/45fc5699-cddf-4e21-af35-13040706f6c0)
![screenshot_1 5x_postspark_2026-02-27_22-37-45](https://github.com/user-attachments/assets/f17cdb90-52d7-4773-be2e-ff64b566af6b)
## 主要功能
1. 💯 免費 & 開源。
2. ✨ AI 大模型對話,多模態,Agent,MCP,Skills,知識庫,人格設定,自動壓縮對話。
3. 🤖 支持接入 Dify、阿里雲百煉、Coze 等智能體平台。
4. 🌐 多平台,支持 QQ、企業微信、飛書、釘釘、微信公眾號、Telegram、Slack 以及[更多](#支持的消息平台)。
3. 🤖 支援接入 Dify、阿里雲百煉、Coze 等智慧體 (Agent) 平台。
4. 🌐 多平台,支援 QQ、企業微信、飛書、釘釘、微信公眾號、Telegram、Slack 以及[更多](#支援的訊息平台)。
5. 📦 插件擴展,已有 1000+ 個插件可一鍵安裝。
6. 🛡️ [Agent Sandbox](https://docs.astrbot.app/use/astrbot-agent-sandbox.html) 隔離化環境,安全地執行任何代碼、調用 Shell、會話級資源複用。
7. 💻 WebUI 支持。
8. 🌈 Web ChatUI 支持,ChatUI 內置代理沙盒、網頁搜索等。
9. 🌐 國際化(i18n)支持。
7. 💻 WebUI 支援。
8. 🌈 Web ChatUI 支援,ChatUI 內置代理沙盒 (Agent Sandbox)、網頁搜尋等。
9. 🌐 國際化(i18n)支援。
<br>
@@ -59,7 +59,7 @@ AstrBot 是一個開源的一站式 Agentic 個人和群聊助手,可在 QQ、
<th>💙 角色扮演 & 情感陪伴</th>
<th>✨ 主動式 Agent</th>
<th>🚀 通用 Agentic 能力</th>
<th>🧩 1000+ 社區插件</th>
<th>🧩 1000+ 社區外掛程式</th>
</tr>
<tr>
<td align="center"><p align="center"><img width="984" height="1746" alt="99b587c5d35eea09d84f33e6cf6cfd4f" src="https://github.com/user-attachments/assets/89196061-3290-458d-b51f-afa178049f84" /></p></td>
@@ -73,21 +73,18 @@ AstrBot 是一個開源的一站式 Agentic 個人和群聊助手,可在 QQ、
### 一鍵部署
對於想快速體驗 AstrBot、且熟悉命令行並能夠自行安裝 `uv` 環境的用戶,我們推薦使用 `uv` 一鍵部署方式 ⚡️。
對於想快速體驗 AstrBot、且熟悉命令列並能自行安裝 `uv` 環境的使用者,我們推薦使用 `uv` 一鍵部署方式 ⚡️。
```bash
uv tool install astrbot
astrbot init # 僅首次執行此命令以初始化環境
astrbot run # astrbot run --backend-only 僅啟動後端服務
# 安裝開發版本(更多修復,新功能,但不夠穩定,適合開發者)
uv tool install git+https://github.com/AstrBotDevs/AstrBot@dev
astrbot run
```
> 需要安裝 [uv](https://docs.astral.sh/uv/)。
> [!NOTE]
> 對於 macOS 用戶:由於 macOS 安全檢查,首次運行 `astrbot` 命令可能需要較長時間(約 10-20 秒)。
> 對於 macOS 使用者:由於 macOS 安全性檢查,首次執行 `astrbot` 指令可能需要較長時間(約 10-20 秒)。
更新 `astrbot`:
@@ -97,39 +94,39 @@ uv tool upgrade astrbot
### Docker 部署
對於熟悉容器、希望獲得更穩定且更適合生產環境部署方式的用戶,我們推薦使用 Docker / Docker Compose 部署 AstrBot。
對於熟悉容器、希望獲得更穩定且更適合正式環境部署方式的使用者,我們推薦使用 Docker / Docker Compose 部署 AstrBot。
請參考官方文檔 [使用 Docker 部署 AstrBot](https://astrbot.app/deploy/astrbot/docker.html#%E4%BD%BF%E7%94%A8-docker-%E9%83%A8%E7%BD%B2-astrbot)。
請參考官方文件 [使用 Docker 部署 AstrBot](https://astrbot.app/deploy/astrbot/docker.html#%E4%BD%BF%E7%94%A8-docker-%E9%83%A8%E7%BD%B2-astrbot)。
### 在 雨雲 上部署
### 在雨雲上部署
對於希望一鍵部署 AstrBot 且不想自行管理服務器的用戶,我們推薦使用雨雲的一鍵雲部署服務 ☁️:
對於希望一鍵部署 AstrBot 且不想自行管理伺服器的使用者,我們推薦使用雨雲的一鍵雲端部署服務 ☁️:
[![Deploy on RainYun](https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-en.svg)](https://app.rainyun.com/apps/rca/store/5994?ref=NjU1ODg0)
### 桌面客戶端部署
對於希望在桌面端使用 AstrBot、並以 ChatUI 為主要入口的用戶,我們推薦使用 AstrBot App。
對於希望在桌面端使用 AstrBot、並以 ChatUI 為主要入口的使用者,我們推薦使用 AstrBot App。
前往 [AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) 下載並安裝;該方式面向桌面使用,不推薦服務器場景。
前往 [AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) 下載並安裝;此方式面向桌面使用,不建議伺服器場景。
### 啟動器部署
同樣在桌面端,希望快速部署並實現環境隔離多開的用戶,我們推薦使用 AstrBot Launcher。
同樣在桌面端,對於希望快速部署並實現環境隔離多開的使用者,我們推薦使用 AstrBot Launcher。
前往 [AstrBot Launcher](https://github.com/Raven95676/astrbot-launcher) 下載並安裝。
### 在 Replit 上部署
Replit 部署由社區維護,適合在線演示和輕量試用場景。
Replit 部署由社群維護,適合線上示範與輕量試用情境。
[![Run on Repl.it](https://repl.it/badge/github/AstrBotDevs/AstrBot)](https://repl.it/github/AstrBotDevs/AstrBot)
### AUR
AUR 方式面向 Arch Linux 用戶,適合希望通過系統包管理器安裝 AstrBot 的場景。
AUR 方式面向 Arch Linux 使用者,適合希望透過系統套件管理器安裝 AstrBot 的場景。
在終端執行下方命令安裝 `astrbot-git` 包,安裝完成後即可啟動使用。
在終端執行下方命令安裝 `astrbot-git` 套件,安裝完成後即可啟動使用。
```bash
yay -S astrbot-git
@@ -137,155 +134,123 @@ yay -S astrbot-git
**更多部署方式**
若你需要面板化或更高自定義部署,可參考 [寶塔面板](https://astrbot.app/deploy/astrbot/btpanel.html)(BT Panel 應用商店安裝)、[1Panel](https://astrbot.app/deploy/astrbot/1panel.html)(1Panel 應用商店安裝)、[CasaOS](https://astrbot.app/deploy/astrbot/casaos.html)(NAS / 家庭服務器可視化部署)和 [手動部署](https://astrbot.app/deploy/astrbot/cli.html)(基於源碼與 `uv` 的完整自定義安裝)。
若你需要面板化或更高自訂程度的部署,可參考 [寶塔面板](https://astrbot.app/deploy/astrbot/btpanel.html)(BT Panel 應用商店安裝)、[1Panel](https://astrbot.app/deploy/astrbot/1panel.html)(1Panel 應用商店安裝)、[CasaOS](https://astrbot.app/deploy/astrbot/casaos.html)(NAS / 家用伺服器可視化部署)與 [手動部署](https://astrbot.app/deploy/astrbot/cli.html)(基於原始碼與 `uv` 的完整自訂安裝)。
## 支持的消息平台
## 支援的訊息平台
將 AstrBot 連接到你常用的聊天平台。
| 平台 | 維護方 |
|---------|---------------|
| **QQ** | 官方維護 |
| **OneBot v11** | 官方維護 |
| **Telegram** | 官方維護 |
| **企微應用 & 企微智能機器人** | 官方維護 |
| **微信客服 & 微信公眾號** | 官方維護 |
| **飛書** | 官方維護 |
| **釘釘** | 官方維護 |
| **Slack** | 官方維護 |
| **Discord** | 官方維護 |
| **LINE** | 官方維護 |
| **Satori** | 官方維護 |
| **Misskey** | 官方維護 |
| **Whatsapp (將支持)** | 官方維護 |
| [**Matrix**](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | 社區維護 |
| [**KOOK**](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | 社區維護 |
| [**VoceChat**](https://github.com/HikariFroya/astrbot_plugin_vocechat) | 社區維護 |
| QQ | 官方維護 |
| OneBot v11 協議實作 | 官方維護 |
| Telegram | 官方維護 |
| 企微應用 & 企微智慧機器人 | 官方維護 |
| 微信客服 & 微信公眾號 | 官方維護 |
| 飛書 | 官方維護 |
| 釘釘 | 官方維護 |
| Slack | 官方維護 |
| Discord | 官方維護 |
| LINE | 官方維護 |
| Satori | 官方維護 |
| Misskey | 官方維護 |
| Whatsapp(即將支援) | 官方維護 |
| [Matrix](https://github.com/stevessr/astrbot_plugin_matrix_adapter) | 社群維護 |
| [KOOK](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | 社群維護 |
| [VoceChat](https://github.com/HikariFroya/astrbot_plugin_vocechat) | 社群維護 |
## 支持的模型提供商
## 支援的模型服務
| 提供商 | 類型 |
| 服務 | 類型 |
|---------|---------------|
| 自定義 | 任何 OpenAI API 兼容的服務 |
| OpenAI | LLM |
| Anthropic | LLM |
| Google Gemini | LLM |
| Moonshot AI | LLM |
| 智譜 AI | LLM |
| DeepSeek | LLM |
| Ollama (本地部署) | LLM |
| LM Studio (本地部署) | LLM |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | LLM (API 網關, 支持所有模型) |
| [優雲智算](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | LLM (API 網關, 支持所有模型) |
| [硅基流動](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | LLM (API 網關, 支持所有模型) |
| [PPIO 派歐雲](https://ppio.com/user/register?invited_by=AIOONE) | LLM (API 網關, 支持所有模型) |
| [302.AI](https://share.302.ai/rr1M3l) | LLM (API 網關, 支持所有模型)|
| [小馬算力](https://www.tokenpony.cn/3YPyf) | LLM (API 網關, 支持所有模型)|
| ModelScope | LLM |
| OneAPI | LLM |
| OpenAI 及相容服務 | 大型模型服務 |
| Anthropic | 大型模型服務 |
| Google Gemini | 大型模型服務 |
| Moonshot AI | 大型模型服務 |
| 智譜 AI | 大型模型服務 |
| DeepSeek | 大型模型服務 |
| Ollama(本機部署) | 大型模型服務 |
| LM Studio(本機部署) | 大型模型服務 |
| [AIHubMix](https://aihubmix.com/?aff=4bfH) | 大型模型服務(API 閘道,支援所有模型) |
| [優雲智算](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | 大型模型服務 |
| [302.AI](https://share.302.ai/rr1M3l) | 大型模型服務 |
| [小馬算力](https://www.tokenpony.cn/3YPyf) | 大型模型服務 |
| [矽基流動](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot) | 大型模型服務 |
| [PPIO 派歐雲](https://ppio.com/user/register?invited_by=AIOONE) | 大型模型服務 |
| ModelScope | 大型模型服務 |
| OneAPI | 大型模型服務 |
| Dify | LLMOps 平台 |
| 阿里雲百煉應用 | LLMOps 平台 |
| Coze | LLMOps 平台 |
| OpenAI Whisper | 語音轉文本 |
| SenseVoice | 語音轉文本 |
| OpenAI TTS | 文本轉語音 |
| Gemini TTS | 文本轉語音 |
| GPT-Sovits-Inference | 文本轉語音 |
| GPT-Sovits | 文本轉語音 |
| FishAudio | 文本轉語音 |
| Edge TTS | 文本轉語音 |
| 阿里雲百煉 TTS | 文本轉語音 |
| Azure TTS | 文本轉語音 |
| Minimax TTS | 文本轉語音 |
| 火山引擎 TTS | 文本轉語音 |
## ❤️ Sponsors
<p align="center">
<img alt="sponsors" src="https://sponsors.astrbot.app/?v=1">
</p>
| OpenAI Whisper | 語音轉文字服務 |
| SenseVoice | 語音轉文字服務 |
| OpenAI TTS | 文字轉語音服務 |
| Gemini TTS | 文字轉語音服務 |
| GPT-Sovits-Inference | 文字轉語音服務 |
| GPT-Sovits | 文字轉語音服務 |
| FishAudio | 文字轉語音服務 |
| Edge TTS | 文字轉語音服務 |
| 阿里雲百煉 TTS | 文字轉語音服務 |
| Azure TTS | 文字轉語音服務 |
| Minimax TTS | 文字轉語音服務 |
| 火山引擎 TTS | 文字轉語音服務 |
## ❤️ 貢獻
歡迎任何 Issues/Pull Requests!只需要將你的更改提交到此項目 :)
歡迎任何 Issues/Pull Requests!只需要將您的變更提交到此專案 :)
### 如何貢獻
你可以通過查看問題或幫助審核 PR(拉取請求)來貢獻。任何問題或 PR 都歡迎參與,以促進社區貢獻。當然,這些只是建議,你可以以任何方式進行貢獻。對於新功能的添加,請先通過 Issue 討論。
建議將功能性PR合併至dev分支,將在測試修改後合併到主分支並發布新版本。
為了減少衝突,建議如下:
1. 工作分支最好基於 `dev` 分支創建,避免直接在 `main` 分支上工作。
2. 提交 PR 時,選擇 `dev` 分支作為目標分支。
3. 定期同步 `dev` 分支到本地,多使用git pull。
您可以透過檢視問題或協助審核 PR(拉取請求)來貢獻。任何問題或 PR 都歡迎參與,以促進社群貢獻。當然,這些只是建議,您可以以任何方式進行貢獻。對於新功能的新增,請先透過 Issue 討論。
### 開發環境
AstrBot 使用 `ruff` 進行代碼格式化和檢查。
AstrBot 使用 `ruff` 進行程式碼格式化和檢查。
```bash
git clone https://github.com/AstrBotDevs/AstrBot
git switch dev # 切換到開發分支
pip install pre-commit # 或者uv tool install pre-commit
pip install pre-commit
pre-commit install
```
推薦使用uv本地安裝,進行測試
```bash
uv tool install -e . --force
astrbot init
astrbot run
```
調試前端
```bash
astrbot run --backend-only
cd dashboard
bun install # 或者pnpm 等
bun dev
```
## 🌍 社群
### QQ 群組
- 9 群: 1076659624 (新)
- 10 群: 1078079676 (新)
- 1 群:322154837
- 3 群:630166526
- 5 群:822130018
- 6 群:753075035
- 7 群:743746109
- 8 群:1030353265
- 開發者群(偏閒聊吹水):975206796
- 12 群:916228568 (新)
- 9 群:1076659624 (人滿)
- 10 群:1078079676 (人滿)
- 11 群:704659519 (人滿)
- 1 群:322154837 (人滿)
- 3 群:630166526 (人滿)
- 4 群:1077826412 (人滿)
- 5 群:822130018 (人滿)
- 6 群:753075035 (人滿)
- 7 群:743746109 (人滿)
- 8 群:1030353265 (人滿)
- 開發者群(闲聊吹水):975206796
- 開發者群(正式):1039761811
### Discord 頻道
### Discord 群組
- [Discord](https://discord.gg/hAVk6tgV36)
<a href="https://discord.gg/hAVk6tgV36"><img alt="Discord_community" src="https://img.shields.io/badge/Discord-AstrBot-purple?style=for-the-badge&color=76bad9"></a>
## ❤️ Special Thanks
特別感謝所有 Contributors 和插件開發者對 AstrBot 的貢獻 ❤️
特別感謝所有 Contributors 和外掛開發者對 AstrBot 的貢獻 ❤️
<a href="https://github.com/AstrBotDevs/AstrBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=AstrBotDevs/AstrBot&max=200&columns=14" />
</a>
此外,本項目的誕生離不開以下開源項目的幫助:
此外,本專案的誕生離不開以下開源專案的幫助:
- [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) - 偉大的貓貓框架
開源項目友情鏈接:
- [NoneBot2](https://github.com/nonebot/nonebot2) - 優秀的 Python 異步 ChatBot 框架
- [Koishi](https://github.com/koishijs/koishi) - 優秀的 Node.js ChatBot 框架
- [MaiBot](https://github.com/Mai-with-u/MaiBot) - 優秀的擬人化 AI ChatBot
- [nekro-agent](https://github.com/KroMiose/nekro-agent) - 優秀的 Agent ChatBot
- [LangBot](https://github.com/langbot-app/LangBot) - 優秀的多平台 AI ChatBot
- [ChatLuna](https://github.com/ChatLunaLab/chatluna) - 優秀的多平台 AI ChatBot Koishi 插件
- [Operit AI](https://github.com/AAswordman/Operit) - 優秀的 AI 智能助手 Android APP
## ⭐ Star History
> [!TIP]
> 如果本項目對您的生活 / 工作產生了幫助,或者您關注本項目的未來發展,請給項目 Star,這是我們維護這個開源項目的動力 <3
> 如果本專案對您的生活 / 工作產生了幫助,或者您關注本專案的未來發展,請給專案 Star,這是我們維護這個開源專案的動力 <3
<div align="center">
+11 -8
View File
@@ -245,14 +245,17 @@ bun dev
### QQ 群组
- 9 群: 1076659624 (新)
- 10 群: 1078079676 (新)
- 1 群:322154837
- 3 群:630166526
- 5 群:822130018
- 6 群:753075035
- 7 群:743746109
- 8 群:1030353265
- 12 群:916228568 (新)
- 9 群:1076659624 (人满)
- 10 群:1078079676 (人满)
- 11 群:704659519 (人满)
- 1 群:322154837 (人满)
- 3 群:630166526 (人满)
- 4 群:1077826412 (人满)
- 5 群:822130018 (人满)
- 6 群:753075035 (人满)
- 7 群:743746109 (人满)
- 8 群:1030353265 (人满)
- 开发者群(偏闲聊吹水):975206796
- 开发者群(正式):1039761811
+4 -12
View File
@@ -8,21 +8,13 @@ from astrbot.core.agent.tool import ToolExecResult
from astrbot.core.astr_agent_context import AstrAgentContext
from ..computer_client import get_booter
from .permissions import check_admin_permission
def _to_json(data: Any) -> str:
return json.dumps(data, ensure_ascii=False, default=str)
def _ensure_admin(context: ContextWrapper[AstrAgentContext]) -> str | None:
if context.context.event.role != "admin":
return (
"error: Permission denied. Browser and skill lifecycle tools are only allowed "
"for admin users."
)
return None
async def _get_browser_component(context: ContextWrapper[AstrAgentContext]) -> Any:
booter = await get_booter(
context.context.context,
@@ -78,7 +70,7 @@ class BrowserExecTool(FunctionTool):
include_trace: bool = False,
**kwargs: Any,
) -> ToolExecResult:
if err := _ensure_admin(context):
if err := check_admin_permission(context, "Using browser tools"):
return err
try:
browser = await _get_browser_component(context)
@@ -142,7 +134,7 @@ class BrowserBatchExecTool(FunctionTool):
include_trace: bool = False,
**kwargs: Any,
) -> ToolExecResult:
if err := _ensure_admin(context):
if err := check_admin_permission(context, "Using browser tools"):
return err
try:
browser = await _get_browser_component(context)
@@ -190,7 +182,7 @@ class RunBrowserSkillTool(FunctionTool):
tags: str | None = None,
**kwargs: Any,
) -> ToolExecResult:
if err := _ensure_admin(context):
if err := check_admin_permission(context, "Using browser tools"):
return err
try:
browser = await _get_browser_component(context)
+3 -8
View File
@@ -10,6 +10,7 @@ from astrbot.core.astr_agent_context import AstrAgentContext
from astrbot.core.skills.neo_skill_sync import NeoSkillSyncManager
from ..computer_client import get_booter
from .permissions import check_admin_permission
def _to_jsonable(model_like: Any) -> Any:
@@ -26,12 +27,6 @@ def _to_json_text(data: Any) -> str:
return json.dumps(_to_jsonable(data), ensure_ascii=False, default=str)
def _ensure_admin(context: ContextWrapper[AstrAgentContext]) -> str | None:
if context.context.event.role != "admin":
return "error: Permission denied. Skill lifecycle tools are only allowed for admin users."
return None
async def _get_neo_context(
context: ContextWrapper[AstrAgentContext],
) -> tuple[Any, Any]:
@@ -59,7 +54,7 @@ class NeoSkillToolBase(FunctionTool):
neo_call: Callable[[Any, Any], Awaitable[Any]],
error_action: str,
) -> ToolExecResult:
if err := _ensure_admin(context):
if err := check_admin_permission(context, "Using skill lifecycle tools"):
return err
try:
client, sandbox = await _get_neo_context(context)
@@ -392,7 +387,7 @@ class PromoteSkillCandidateTool(NeoSkillToolBase):
stage: str = "canary",
sync_to_local: bool = True,
) -> ToolExecResult:
if err := _ensure_admin(context):
if err := check_admin_permission(context, "Using skill lifecycle tools"):
return err
if stage not in {"canary", "stable"}:
return "Error promoting skill candidate: stage must be canary or stable."
+1 -1
View File
@@ -5,7 +5,7 @@ from typing import Any, TypedDict
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
VERSION = "4.22.0"
VERSION = "4.22.1"
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
PERSONAL_WECHAT_CONFIG_METADATA = {
"weixin_oc_base_url": {
@@ -516,7 +516,7 @@ class ProviderAnthropic(Provider):
model = model or self.get_model()
payloads = {"messages": new_messages, "model": model}
payloads = {**kwargs, "messages": new_messages, "model": model}
# Anthropic has a different way of handling system prompts
if system_prompt:
@@ -572,7 +572,7 @@ class ProviderAnthropic(Provider):
model = model or self.get_model()
payloads = {"messages": new_messages, "model": model}
payloads = {**kwargs, "messages": new_messages, "model": model}
# Anthropic has a different way of handling system prompts
if system_prompt:
@@ -758,7 +758,7 @@ class ProviderGoogleGenAI(Provider):
model = model or self.get_model()
payloads = {"messages": context_query, "model": model}
payloads = {**kwargs, "messages": context_query, "model": model}
retry = 10
keys = self.api_keys.copy()
@@ -813,7 +813,7 @@ class ProviderGoogleGenAI(Provider):
model = model or self.get_model()
payloads = {"messages": context_query, "model": model}
payloads = {**kwargs, "messages": context_query, "model": model}
retry = 10
keys = self.api_keys.copy()
+215 -22
View File
@@ -1,11 +1,15 @@
import asyncio
import base64
import copy
import inspect
import json
import random
import re
from collections.abc import AsyncGenerator
from typing import Any
from io import BytesIO
from pathlib import Path
from typing import Any, Literal
from urllib.parse import unquote, urlparse
import aiofiles
import httpx
@@ -15,6 +19,8 @@ from openai.lib.streaming.chat._completions import ChatCompletionStreamState
from openai.types.chat.chat_completion import ChatCompletion
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
from openai.types.completion_usage import CompletionUsage
from PIL import Image as PILImage
from PIL import UnidentifiedImageError
import astrbot.core.message.components as Comp
from astrbot import logger
@@ -134,6 +140,186 @@ class ProviderOpenAIOfficial(Provider):
return True
return False
def _is_invalid_attachment_error(self, error: Exception) -> bool:
body = getattr(error, "body", None)
code: str | None = None
message: str | None = None
if isinstance(body, dict):
err_obj = body.get("error")
if isinstance(err_obj, dict):
raw_code = err_obj.get("code")
raw_message = err_obj.get("message")
code = raw_code.lower() if isinstance(raw_code, str) else None
message = raw_message.lower() if isinstance(raw_message, str) else None
if code == "invalid_attachment":
return True
text_sources: list[str] = []
if message:
text_sources.append(message)
if code:
text_sources.append(code)
text_sources.extend(map(str, self._extract_error_text_candidates(error)))
error_text = " ".join(text.lower() for text in text_sources if text)
if "invalid_attachment" in error_text:
return True
if "download attachment" in error_text and "404" in error_text:
return True
return False
@classmethod
def _encode_image_file_to_data_url(
cls,
image_path: str,
*,
mode: Literal["safe", "strict"],
) -> str | None:
try:
image_bytes = Path(image_path).read_bytes()
except OSError:
if mode == "strict":
raise
return None
try:
with PILImage.open(BytesIO(image_bytes)) as image:
image.verify()
image_format = str(image.format or "").upper()
except (OSError, UnidentifiedImageError):
if mode == "strict":
raise ValueError(f"Invalid image file: {image_path}")
return None
mime_type = {
"JPEG": "image/jpeg",
"PNG": "image/png",
"GIF": "image/gif",
"WEBP": "image/webp",
"BMP": "image/bmp",
}.get(image_format, "image/jpeg")
image_bs64 = base64.b64encode(image_bytes).decode("utf-8")
return f"data:{mime_type};base64,{image_bs64}"
@staticmethod
def _file_uri_to_path(file_uri: str) -> str:
"""Normalize file URIs to paths.
`file://localhost/...` and drive-letter forms are treated as local paths.
Other non-empty hosts are preserved as UNC-style paths.
"""
parsed = urlparse(file_uri)
if parsed.scheme != "file":
return file_uri
netloc = unquote(parsed.netloc or "")
path = unquote(parsed.path or "")
if re.fullmatch(r"[A-Za-z]:", netloc):
return str(Path(f"{netloc}{path}"))
if re.match(r"^/[A-Za-z]:/", path):
path = path[1:]
if netloc and netloc != "localhost":
path = f"//{netloc}{path}"
return str(Path(path))
async def _image_ref_to_data_url(
self,
image_ref: str,
*,
mode: Literal["safe", "strict"] = "safe",
) -> str | None:
if image_ref.startswith("base64://"):
return image_ref.replace("base64://", "data:image/jpeg;base64,")
if image_ref.startswith("http"):
image_path = await download_image_by_url(image_ref)
elif image_ref.startswith("file://"):
image_path = self._file_uri_to_path(image_ref)
else:
image_path = image_ref
return self._encode_image_file_to_data_url(
image_path,
mode=mode,
)
async def _resolve_image_part(
self,
image_url: str,
*,
image_detail: str | None = None,
) -> dict | None:
if image_url.startswith("data:"):
image_payload = {"url": image_url}
else:
image_data = await self._image_ref_to_data_url(image_url, mode="safe")
if not image_data:
logger.warning(f"图片 {image_url} 得到的结果为空,将忽略。")
return None
image_payload = {"url": image_data}
if image_detail:
image_payload["detail"] = image_detail
return {
"type": "image_url",
"image_url": image_payload,
}
def _extract_image_part_info(self, part: dict) -> tuple[str | None, str | None]:
if not isinstance(part, dict) or part.get("type") != "image_url":
return None, None
image_url_data = part.get("image_url")
if not isinstance(image_url_data, dict):
logger.warning("图片内容块格式无效,将保留原始内容。")
return None, None
url = image_url_data.get("url")
if not isinstance(url, str) or not url:
logger.warning("图片内容块缺少有效 URL,将保留原始内容。")
return None, None
image_detail = image_url_data.get("detail")
if not isinstance(image_detail, str):
image_detail = None
return url, image_detail
async def _transform_content_part(self, part: dict) -> dict:
url, image_detail = self._extract_image_part_info(part)
if not url:
return part
try:
resolved_part = await self._resolve_image_part(
url, image_detail=image_detail
)
except Exception as exc:
logger.warning(
"图片 %s 预处理失败,将保留原始内容。错误: %s",
url,
exc,
)
return part
return resolved_part or part
async def _materialize_message_image_parts(self, message: dict) -> dict:
content = message.get("content")
if not isinstance(content, list):
return {**message}
new_content = [await self._transform_content_part(part) for part in content]
return {**message, "content": new_content}
async def _materialize_context_image_parts(
self, context_query: list[dict]
) -> list[dict]:
return [
await self._materialize_message_image_parts(message)
for message in context_query
]
async def _fallback_to_text_only_and_retry(
self,
payloads: dict,
@@ -608,7 +794,7 @@ class ProviderOpenAIOfficial(Provider):
new_record = await self.assemble_context(
prompt, image_urls, extra_user_content_parts
)
context_query = self._ensure_message_to_dicts(contexts)
context_query = copy.deepcopy(self._ensure_message_to_dicts(contexts))
if new_record:
context_query.append(new_record)
if system_prompt:
@@ -626,9 +812,11 @@ class ProviderOpenAIOfficial(Provider):
for tcr in tool_calls_result:
context_query.extend(tcr.to_openai_messages())
model = model or self.get_model()
if self._context_contains_image(context_query):
context_query = await self._materialize_context_image_parts(context_query)
payloads = {"messages": context_query, "model": model}
model = model or self.get_model()
payloads = {**kwargs, "messages": context_query, "model": model}
self._finally_convert_payload(payloads)
@@ -726,6 +914,18 @@ class ProviderOpenAIOfficial(Provider):
"image_content_moderated",
image_fallback_used=True,
)
if self._is_invalid_attachment_error(e):
if image_fallback_used or not self._context_contains_image(context_query):
raise e
return await self._fallback_to_text_only_and_retry(
payloads,
context_query,
chosen_key,
available_api_keys,
func_tool,
"invalid_attachment",
image_fallback_used=True,
)
if (
"Function calling is not enabled" in str(e)
@@ -927,22 +1127,6 @@ class ProviderOpenAIOfficial(Provider):
) -> dict:
"""组装成符合 OpenAI 格式的 role 为 user 的消息段"""
async def resolve_image_part(image_url: str) -> dict | None:
if image_url.startswith("http"):
image_path = await download_image_by_url(image_url)
image_data = await self.encode_image_bs64(image_path)
elif image_url.startswith("file:///"):
image_path = image_url.replace("file:///", "")
image_data = await self.encode_image_bs64(image_path)
else:
image_data = await self.encode_image_bs64(image_url)
if not image_data:
logger.warning(f"图片 {image_url} 得到的结果为空,将忽略。")
return None
return {
"type": "image_url",
"image_url": {"url": image_data},
}
# 构建内容块列表
content_blocks = []
@@ -963,7 +1147,9 @@ class ProviderOpenAIOfficial(Provider):
if isinstance(part, TextPart):
content_blocks.append({"type": "text", "text": part.text})
elif isinstance(part, ImageURLPart):
image_part = await resolve_image_part(part.image_url.url)
image_part = await self._resolve_image_part(
part.image_url.url,
)
if image_part:
content_blocks.append(image_part)
else:
@@ -972,7 +1158,7 @@ class ProviderOpenAIOfficial(Provider):
# 3. 图片内容
if image_urls:
for image_url in image_urls:
image_part = await resolve_image_part(image_url)
image_part = await self._resolve_image_part(image_url)
if image_part:
content_blocks.append(image_part)
@@ -991,11 +1177,18 @@ class ProviderOpenAIOfficial(Provider):
async def encode_image_bs64(self, image_url: str) -> str:
"""将图片转换为 base64"""
<<<<<<< ours
if image_url.startswith("base64://"):
return image_url.replace("base64://", "data:image/jpeg;base64,")
async with aiofiles.open(image_url, "rb") as f:
image_bs64 = base64.b64encode(await f.read()).decode("utf-8")
return "data:image/jpeg;base64," + image_bs64
=======
image_data = await self._image_ref_to_data_url(image_url, mode="strict")
if image_data is None:
raise RuntimeError(f"Failed to encode image data: {image_url}")
return image_data
>>>>>>> theirs
async def terminate(self):
if self.client:
+70 -17
View File
@@ -23,7 +23,12 @@ SANDBOX_SKILLS_ROOT = "skills"
SANDBOX_WORKSPACE_ROOT = "/workspace"
_SANDBOX_SKILLS_CACHE_VERSION = 1
_SKILL_NAME_RE = re.compile(r"^[A-Za-z0-9._-]+$")
_SKILL_NAME_RE = re.compile(r"^[\w.-]+$")
def _normalize_skill_name(name: str | None) -> str:
raw = str(name or "")
return re.sub(r"\s+", "_", raw.strip())
def _default_sandbox_skill_path(name: str) -> str:
@@ -548,7 +553,13 @@ class SkillManager:
config["skills"].pop(name, None)
self._save_config(config)
def install_skill_from_zip(self, zip_path: str, *, overwrite: bool = True) -> str:
def install_skill_from_zip(
self,
zip_path: str,
*,
overwrite: bool = True,
skill_name_hint: str | None = None,
) -> str:
zip_path_obj = Path(zip_path)
if not zip_path_obj.exists():
raise FileNotFoundError(f"Zip file not found: {zip_path}")
@@ -565,15 +576,48 @@ class SkillManager:
if not file_names:
raise ValueError("Zip archive is empty.")
top_dirs = {
PurePosixPath(name).parts[0] for name in file_names if name.strip()
}
has_root_skill_md = any(
len(parts := PurePosixPath(name).parts) == 1
and parts[0] in {"SKILL.md", "skill.md"}
for name in file_names
)
root_mode = has_root_skill_md
if len(top_dirs) != 1:
raise ValueError("Zip archive must contain a single top-level folder.")
skill_name = next(iter(top_dirs))
if skill_name in {".", "..", ""} or not _SKILL_NAME_RE.match(skill_name):
raise ValueError("Invalid skill folder name.")
archive_skill_name = None
if skill_name_hint is not None:
archive_skill_name = _normalize_skill_name(skill_name_hint)
if archive_skill_name and not _SKILL_NAME_RE.fullmatch(
archive_skill_name
):
raise ValueError("Invalid skill name.")
if root_mode:
archive_hint = _normalize_skill_name(
archive_skill_name or zip_path_obj.stem
)
if not archive_hint or not _SKILL_NAME_RE.fullmatch(archive_hint):
raise ValueError("Invalid skill name.")
skill_name = archive_hint
else:
top_dirs = {
PurePosixPath(name).parts[0] for name in file_names if name.strip()
}
if len(top_dirs) != 1:
raise ValueError(
"Zip archive must contain a single top-level folder."
)
archive_root_name = next(iter(top_dirs))
archive_root_name_normalized = _normalize_skill_name(archive_root_name)
if archive_root_name in {".", "..", ""} or not _SKILL_NAME_RE.fullmatch(
archive_root_name_normalized
):
raise ValueError("Invalid skill folder name.")
if archive_skill_name:
if not _SKILL_NAME_RE.fullmatch(archive_skill_name):
raise ValueError("Invalid skill name.")
skill_name = archive_skill_name
else:
skill_name = archive_root_name_normalized
for name in names:
if not name:
@@ -583,16 +627,20 @@ class SkillManager:
parts = PurePosixPath(name).parts
if ".." in parts:
raise ValueError("Zip archive contains invalid relative paths.")
if parts and parts[0] != skill_name:
if (not root_mode) and parts and parts[0] != archive_root_name:
raise ValueError(
"Zip archive contains unexpected top-level entries."
)
if (
f"{skill_name}/SKILL.md" not in file_names
and f"{skill_name}/skill.md" not in file_names
):
raise ValueError("SKILL.md not found in the skill folder.")
if root_mode:
if "SKILL.md" not in file_names and "skill.md" not in file_names:
raise ValueError("SKILL.md not found in the skill folder.")
else:
if (
f"{archive_root_name}/SKILL.md" not in file_names
and f"{archive_root_name}/skill.md" not in file_names
):
raise ValueError("SKILL.md not found in the skill folder.")
with tempfile.TemporaryDirectory(dir=str(astrbot_paths.temp)) as tmp_dir:
for member in zf.infolist():
@@ -600,7 +648,12 @@ class SkillManager:
if not member_name or _is_ignored_zip_entry(member_name):
continue
zf.extract(member, tmp_dir)
src_dir = Path(tmp_dir) / skill_name
src_dir = (
Path(tmp_dir) if root_mode else Path(tmp_dir) / archive_root_name
)
normalized_path = _normalize_skill_markdown_path(src_dir)
if normalized_path is None:
raise ValueError("SKILL.md not found in the skill folder.")
_normalize_skill_markdown_path(src_dir)
if not src_dir.exists():
raise ValueError("Skill folder not found after extraction.")
+84 -12
View File
@@ -2,7 +2,6 @@ import os
import re
import shutil
import traceback
import uuid
from collections.abc import Awaitable, Callable
from pathlib import Path
from typing import Any
@@ -45,6 +44,17 @@ def _to_bool(value: Any, default: bool = False) -> bool:
_SKILL_NAME_RE = re.compile(r"^[A-Za-z0-9._-]+$")
def _next_available_temp_path(temp_dir: str, filename: str) -> str:
stem = Path(filename).stem
suffix = Path(filename).suffix
candidate = filename
index = 1
while os.path.exists(os.path.join(temp_dir, candidate)):
candidate = f"{stem}_{index}{suffix}"
index += 1
return os.path.join(temp_dir, candidate)
class SkillsRoute(Route):
def __init__(self, context: RouteContext, core_lifecycle) -> None:
super().__init__(context)
@@ -165,11 +175,24 @@ class SkillsRoute(Route):
temp_dir = get_astrbot_temp_path()
os.makedirs(temp_dir, exist_ok=True)
temp_path = os.path.join(temp_dir, filename)
skill_mgr = SkillManager()
temp_path = _next_available_temp_path(temp_dir, filename)
await file.save(temp_path)
skill_mgr = SkillManager()
skill_name = skill_mgr.install_skill_from_zip(temp_path, overwrite=True)
try:
try:
skill_name = skill_mgr.install_skill_from_zip(
temp_path, overwrite=False, skill_name_hint=Path(filename).stem
)
except TypeError:
# Backward compatibility for callers that do not accept skill_name_hint
skill_name = skill_mgr.install_skill_from_zip(
temp_path, overwrite=False
)
except Exception:
# Keep behavior consistent with previous implementation
# and bubble up install errors (including duplicates).
raise
try:
await sync_skills_to_active_sandboxes()
@@ -209,6 +232,7 @@ class SkillsRoute(Route):
succeeded = []
failed = []
skipped = []
skill_mgr = SkillManager()
temp_dir = get_astrbot_temp_path()
os.makedirs(temp_dir, exist_ok=True)
@@ -227,14 +251,42 @@ class SkillsRoute(Route):
)
continue
temp_path = os.path.join(
temp_dir, f"batch_{uuid.uuid4().hex}_{filename}"
)
temp_path = _next_available_temp_path(temp_dir, filename)
await file.save(temp_path)
skill_name = skill_mgr.install_skill_from_zip(
temp_path, overwrite=True
)
try:
skill_name = skill_mgr.install_skill_from_zip(
temp_path,
overwrite=False,
skill_name_hint=Path(filename).stem,
)
except TypeError:
# Backward compatibility for monkeypatched implementations in tests
try:
skill_name = skill_mgr.install_skill_from_zip(
temp_path, overwrite=False
)
except FileExistsError:
skipped.append(
{
"filename": filename,
"name": Path(filename).stem,
"error": "Skill already exists.",
}
)
skill_name = None
except FileExistsError:
skipped.append(
{
"filename": filename,
"name": Path(filename).stem,
"error": "Skill already exists.",
}
)
skill_name = None
if skill_name is None:
continue
succeeded.append({"filename": filename, "name": skill_name})
except Exception as e:
@@ -256,8 +308,10 @@ class SkillsRoute(Route):
total = len(file_list)
success_count = len(succeeded)
skipped_count = len(skipped)
failed_count = len(failed)
if success_count == total:
if failed_count == 0 and success_count == total:
message = f"All {total} skill(s) uploaded successfully."
return (
Response()
@@ -266,18 +320,35 @@ class SkillsRoute(Route):
"total": total,
"succeeded": succeeded,
"failed": failed,
"skipped": skipped,
},
message,
)
.__dict__
)
if success_count == 0:
if failed_count == 0 and success_count == 0:
message = f"All {total} file(s) were skipped."
return (
Response()
.ok(
{
"total": total,
"succeeded": succeeded,
"failed": failed,
"skipped": skipped,
},
message,
)
.__dict__
)
if success_count == 0 and skipped_count == 0:
message = f"Upload failed for all {total} file(s)."
resp = Response().error(message)
resp.data = {
"total": total,
"succeeded": succeeded,
"failed": failed,
"skipped": skipped,
}
return resp.__dict__
@@ -289,6 +360,7 @@ class SkillsRoute(Route):
"total": total,
"succeeded": succeeded,
"failed": failed,
"skipped": skipped,
},
message,
)
+49
View File
@@ -0,0 +1,49 @@
## What's Changed
### 新增
- 增强 Skills 安装流程,不再限制上传的压缩包顶级必须是一个目录。并支持中文技能名称显示。([#6952](https://github.com/AstrBotDevs/AstrBot/pull/6952))
- OpenAI Embedding 模型配置支持自动补齐 `/v1` 基础路径。([#6863](https://github.com/AstrBotDevs/AstrBot/pull/6863))
- 为 `/api/file` 新增 GET 端点并支持多种请求方式。([#6874](https://github.com/AstrBotDevs/AstrBot/pull/6874))
- WebUI 设置页新增日志与缓存清理能力。([#6822](https://github.com/AstrBotDevs/AstrBot/pull/6822))
- Lark 平台新增可折叠 Thinking 面板能力与消息处理优化。([#6831](https://github.com/AstrBotDevs/AstrBot/pull/6831))
### 修复
- 修复 QQ 官方机器人中,在 Cron Job 或者主动发送消息时的 `msg_id` 相关负载处理问题。([#6604](https://github.com/AstrBotDevs/AstrBot/pull/6604))
- 修复 个人微信 在轮询超时后停止轮询的问题。([#6915](https://github.com/AstrBotDevs/AstrBot/pull/6915))
- 修复 硅基流动 提供商无法正确使用工具调用能力的问题。([#6829](https://github.com/AstrBotDevs/AstrBot/pull/6829))
- 修复部分提供商工具调用流式增量返回缺少 index 导致的异常。([#6661](https://github.com/AstrBotDevs/AstrBot/pull/6661)
- 修复 WebUI 中 `ObjectEditor` 的 `updateKey` 错误索引导致的“键已存在”误判。([#6825](https://github.com/AstrBotDevs/AstrBot/pull/6825))
- 修复 UI 图标集合及测试一致性导致的展示异常。([#6894](https://github.com/AstrBotDevs/AstrBot/pull/6894)、[#6892](https://github.com/AstrBotDevs/AstrBot/pull/6892))
- 修复 T2I 配置间未同步生效模板的问题。([#6824](https://github.com/AstrBotDevs/AstrBot/pull/6824))
- 修复 MIMO TTS 样式参数以对齐官方文档约定。([#6814](https://github.com/AstrBotDevs/AstrBot/pull/6814))
## What's Changed (EN)
### New Features
- Enhanced skill installation to support multiple top-level folders, duplicate handling, and Chinese skill names.([#6952](https://github.com/AstrBotDevs/AstrBot/pull/6952))
- Automatically append `/v1` to `embedding_api_base` for OpenAI embedding compatibility.([#6863](https://github.com/AstrBotDevs/AstrBot/pull/6863))
- Added plugin author display and pinned plugin card support in WebUI.([#6875](https://github.com/AstrBotDevs/AstrBot/pull/6875))
- Added GET endpoint for `/api/file` and support for multiple HTTP methods.([#6874](https://github.com/AstrBotDevs/AstrBot/pull/6874))
- Added log and cache cleanup in Dashboard settings.([#6822](https://github.com/AstrBotDevs/AstrBot/pull/6822))
- Added collapsible reasoning panel and message handling improvements for Lark.([#6831](https://github.com/AstrBotDevs/AstrBot/pull/6831))
### Improvements
- Validate `config_path` before existence checks to avoid false negatives.([#6722](https://github.com/AstrBotDevs/AstrBot/pull/6722))
- Improved Provider batch reset behavior and "follow" configuration handling in WebUI.([#6825](https://github.com/AstrBotDevs/AstrBot/pull/6825))
- Updated OpenAI-related guidance in i18n docs for clearer compatibility hints.([adc252a3](https://github.com/AstrBotDevs/AstrBot/commit/adc252a3a2f9f6a4b3fcf6f7d5f4c7d5b9d9a1))
### Bug Fixes
- Fixed missing index field in streaming `tool_call` deltas.([#6661](https://github.com/AstrBotDevs/AstrBot/pull/6661))
- Fixed `msg_id` payload handling for QQ API.([#6604](https://github.com/AstrBotDevs/AstrBot/pull/6604))
- Kept Weixin OC polling active after inbound timeout.([#6915](https://github.com/AstrBotDevs/AstrBot/pull/6915))
- Fixed `updateKey` index bug in WebUI `ObjectEditor` that caused false “key exists” errors.([#6825](https://github.com/AstrBotDevs/AstrBot/pull/6825))
- Fixed icon regressions in UI and related icon scan tests.([#6894](https://github.com/AstrBotDevs/AstrBot/pull/6894)、[#6892](https://github.com/AstrBotDevs/AstrBot/pull/6892))
- Fixed SiliconFlow provider tools compatibility issue.([#6829](https://github.com/AstrBotDevs/AstrBot/pull/6829))
- Synchronized active T2I template across all configs.([#6824](https://github.com/AstrBotDevs/AstrBot/pull/6824))
- Aligned MIMO TTS payload style with official docs.([#6814](https://github.com/AstrBotDevs/AstrBot/pull/6814))
- Removed privacy-sensitive data left in tests.([#6803](https://github.com/AstrBotDevs/AstrBot/pull/6803))
+34 -1
View File
@@ -34,9 +34,42 @@ const UTILITY_CLASSES = new Set([
]);
// Icons used indirectly by Vuetify internals, so they won't appear in src/ static scans.
const REQUIRED_ICONS = new Set([
export const REQUIRED_ICONS = new Set([
"mdi-radiobox-blank",
"mdi-radiobox-marked",
"mdi-menu-down",
"mdi-menu-right",
"mdi-check-circle",
"mdi-information",
"mdi-alert-circle",
"mdi-close-circle",
"mdi-chevron-down",
"mdi-chevron-up",
"mdi-chevron-left",
"mdi-chevron-right",
"mdi-check",
"mdi-close",
"mdi-checkbox-marked",
"mdi-checkbox-blank-outline",
"mdi-minus-box",
"mdi-circle",
"mdi-arrow-up",
"mdi-arrow-down",
"mdi-menu",
"mdi-pencil",
"mdi-star-outline",
"mdi-star",
"mdi-star-half-full",
"mdi-cached",
"mdi-page-first",
"mdi-page-last",
"mdi-unfold-more-horizontal",
"mdi-paperclip",
"mdi-plus",
"mdi-minus",
"mdi-calendar",
"mdi-eyedropper",
"mdi-cloud-upload",
]);
// Regex to match individual icon class definitions in MDI CSS
@@ -1,4 +1,4 @@
/* Auto-generated MDI subset – 238 icons */
/* Auto-generated MDI subset – 248 icons */
/* Do not edit manually. Run: pnpm run subset-icons */
@font-face {
@@ -120,6 +120,10 @@
content: "\F00E4";
}
.mdi-cached::before {
content: "\F00E8";
}
.mdi-calendar::before {
content: "\F00ED";
}
@@ -364,6 +368,10 @@
content: "\F06D0";
}
.mdi-eyedropper::before {
content: "\F020A";
}
.mdi-file::before {
content: "\F0214";
}
@@ -620,6 +628,14 @@
content: "\F035C";
}
.mdi-menu-down::before {
content: "\F035D";
}
.mdi-menu-right::before {
content: "\F035F";
}
.mdi-message-off-outline::before {
content: "\F164E";
}
@@ -644,6 +660,10 @@
content: "\F0374";
}
.mdi-minus-box::before {
content: "\F0375";
}
.mdi-note-text-outline::before {
content: "\F11D7";
}
@@ -676,6 +696,18 @@
content: "\F03D6";
}
.mdi-page-first::before {
content: "\F0600";
}
.mdi-page-last::before {
content: "\F0601";
}
.mdi-paperclip::before {
content: "\F03E2";
}
.mdi-pause::before {
content: "\F03E4";
}
@@ -804,10 +836,6 @@
content: "\F0832";
}
.mdi-share-variant::before {
content: "\F0497";
}
.mdi-shield-check::before {
content: "\F0565";
}
@@ -852,6 +880,14 @@
content: "\F1C55";
}
.mdi-star-half-full::before {
content: "\F04D0";
}
.mdi-star-outline::before {
content: "\F04D2";
}
.mdi-stop::before {
content: "\F04DB";
}
@@ -908,6 +944,10 @@
content: "\F0A7A";
}
.mdi-unfold-more-horizontal::before {
content: "\F054F";
}
.mdi-update::before {
content: "\F06B0";
}
@@ -1026,6 +1026,7 @@ export default {
const applyUploadResults = (attemptedItems, payload) => {
const succeededMap = buildResultMap(payload?.succeeded);
const failedMap = buildResultMap(payload?.failed);
const skippedMap = buildResultMap(payload?.skipped);
for (const item of attemptedItems) {
const successEntry = takeFirstMatch(succeededMap, item.filenameKey);
@@ -1037,6 +1038,14 @@ export default {
continue;
}
const skippedEntry = takeFirstMatch(skippedMap, item.filenameKey);
if (skippedEntry) {
item.status = STATUS_SKIPPED;
item.validationMessage =
skippedEntry.error || tm("skills.validationDuplicate");
continue;
}
const failedEntry = takeFirstMatch(failedMap, item.filenameKey);
if (failedEntry) {
item.status = STATUS_ERROR;
@@ -242,7 +242,7 @@
"emptyHint": "Upload a Skills zip to get started",
"uploadDialogTitle": "Upload Skills",
"uploadHint": "Upload multiple zip skill packages or drag them in. The system validates the structure automatically and shows a result for each file.",
"structureRequirement": "The most common failure is an invalid archive structure. Each zip must contain exactly one top-level folder such as `skillname/`, and that folder must include `SKILL.md`.",
"structureRequirement": "The archive supports multiple skills folders.",
"abilityMultiple": "Upload multiple zip files at once",
"abilityValidate": "Validate `SKILL.md` automatically",
"abilitySkip": "Automatically skip duplicate files.",
@@ -241,7 +241,7 @@
"emptyHint": "Пожалуйста, загрузите архив с навыками",
"uploadDialogTitle": "Загрузка навыков",
"uploadHint": "Поддерживается массовая загрузка zip-архивов. Вы также можете перетащить файлы в это окно. Система автоматически проверит структуру каждого архива.",
"structureRequirement": "Архив должен содержать одну корневую папку (например, `skillname/`), внутри которой обязательно должен находиться файл `SKILL.md`.",
"structureRequirement": "Поддерживаются архивы с несколькими папками skills.",
"abilityMultiple": "Поддержка массовой загрузки",
"abilityValidate": "Автопроверка `SKILL.md`",
"abilitySkip": "Пропуск дубликатов",
@@ -245,7 +245,7 @@
"emptyHint": "请上传 Skills 压缩包",
"uploadDialogTitle": "上传 Skills",
"uploadHint": "支持批量上传 zip 技能包,也支持拖拽批量上传 zip 技能包。系统会自动校验目录结构,并给出逐个文件的结果。",
"structureRequirement": "常见失败原因是压缩包结构不正确。每个 zip 必须只包含一个顶层目录,例如 `skillname/`,且该目录下必须存在 `SKILL.md`。",
"structureRequirement": "支持压缩包内含多个 skills 文件夹。",
"abilityMultiple": "支持一次上传多个zip文件",
"abilityValidate": "自动校验 `SKILL.md`",
"abilitySkip": "自动跳过重复文件",
+12
View File
@@ -17,6 +17,11 @@ html {
flex: unset;
}
.v-overlay.v-snackbar {
--v-layout-left: 0px !important;
--v-layout-right: 0px !important;
}
.customizer-btn .icon {
animation: progress-circular-rotate 1.4s linear infinite;
transform-origin: center center;
@@ -34,3 +39,10 @@ html {
transform: rotate(270deg);
}
}
pre, code, .markdown pre, .markdown code, .release-notes pre, .release-notes code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, "Roboto Mono", "Helvetica Neue", monospace;
color: var(--astrbot-code-color);
}
+2
View File
@@ -11,10 +11,12 @@ $font-size-root: 1rem;
$border-radius-root: 8px;
$cjk-sans-fallback: 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans CJK SC', 'Microsoft YaHei' !default;
$cjk-mono-fallback: 'PingFang SC', 'PingFang TC', 'Hiragino Sans GB', 'Noto Sans CJK SC', 'Microsoft YaHei' !default;
$code-text-color: #111827 !default;
:root {
--astrbot-font-cjk-sans: #{$cjk-sans-fallback};
--astrbot-font-cjk-mono: #{$cjk-mono-fallback};
--astrbot-code-color: #{$code-text-color};
}
$body-font-family: 'Roboto', $cjk-sans-fallback, sans-serif !default;
+154 -380
View File
@@ -154,14 +154,8 @@ const {
<template>
<v-row>
<v-col
cols="12"
md="12"
>
<v-card
variant="flat"
style="background-color: transparent"
>
<v-col cols="12" md="12">
<v-card variant="flat" style="background-color: transparent">
<!-- 标签页 -->
<v-card-text style="padding: 0px 12px">
<!-- 已安装插件标签页内容 -->
@@ -170,13 +164,8 @@ const {
<!-- 指令面板标签页内容 -->
<v-tab-item v-show="activeTab === 'components'">
<div class="mb-4 pt-4 pb-4">
<div
class="d-flex align-center flex-wrap"
style="gap: 12px"
>
<h2 class="text-h2 mb-0">
{{ tm("tabs.handlersOperation") }}
</h2>
<div class="d-flex align-center flex-wrap" style="gap: 12px">
<h2 class="text-h2 mb-0">{{ tm("tabs.handlersOperation") }}</h2>
</div>
</div>
<v-card
@@ -193,13 +182,8 @@ const {
<!-- 已安装的 MCP 服务器标签页内容 -->
<v-tab-item v-show="activeTab === 'mcp'">
<div class="mb-4 pt-4 pb-4">
<div
class="d-flex align-center flex-wrap"
style="gap: 12px"
>
<h2 class="text-h2 mb-0">
{{ tm("tabs.installedMcpServers") }}
</h2>
<div class="d-flex align-center flex-wrap" style="gap: 12px">
<h2 class="text-h2 mb-0">{{ tm("tabs.installedMcpServers") }}</h2>
</div>
</div>
<v-card
@@ -216,13 +200,8 @@ const {
<!-- Skills 标签页内容 -->
<v-tab-item v-show="activeTab === 'skills'">
<div class="mb-4 pt-4 pb-4">
<div
class="d-flex align-center flex-wrap"
style="gap: 12px"
>
<h2 class="text-h2 mb-0">
{{ tm("tabs.skills") }}
</h2>
<div class="d-flex align-center flex-wrap" style="gap: 12px">
<h2 class="text-h2 mb-0">{{ tm("tabs.skills") }}</h2>
</div>
</div>
<v-card
@@ -238,20 +217,18 @@ const {
<!-- 插件市场标签页内容 -->
<MarketPluginsTab :state="pageState" />
</v-card-text>
</v-card>
</v-col>
<v-col
v-if="activeTab === 'market'"
cols="12"
md="12"
>
<v-col v-if="activeTab === 'market'" cols="12" md="12">
<div class="d-flex align-center justify-center mt-4 mb-4 gap-4">
<v-btn
variant="text"
prepend-icon="mdi-book-open-variant"
href="https://astrbot.app/dev/plugin.html"
href="https://docs.astrbot.app/dev/star/plugin-new.html"
rel="noopener noreferrer"
target="_blank"
color="primary"
class="text-none"
@@ -264,7 +241,7 @@ const {
width: 1px;
background-color: rgba(var(--v-theme-on-surface), 0.12);
"
/>
></div>
<v-btn
variant="text"
prepend-icon="mdi-github"
@@ -280,74 +257,51 @@ const {
</v-row>
<!-- 配置对话框 -->
<v-dialog
v-model="configDialog"
max-width="900"
>
<v-dialog v-model="configDialog" max-width="900">
<v-card>
<v-card-title class="text-h2 pa-4 pl-6 pb-0">
{{
tm("dialogs.config.title")
}}
</v-card-title>
<v-card-title class="text-h2 pa-4 pl-6 pb-0">{{
tm("dialogs.config.title")
}}</v-card-title>
<v-card-text>
<div style="max-height: 60vh; overflow-y: auto; padding-right: 8px">
<AstrBotConfig
v-if="extension_config.metadata"
:metadata="extension_config.metadata"
:iterable="extension_config.config"
:metadata-key="curr_namespace"
:plugin-name="curr_namespace"
:metadataKey="curr_namespace"
:pluginName="curr_namespace"
/>
<p v-else>
{{ tm("dialogs.config.noConfig") }}
</p>
<p v-else>{{ tm("dialogs.config.noConfig") }}</p>
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="blue-darken-1"
variant="text"
@click="updateConfig"
>
{{
tm("buttons.saveAndClose")
}}
</v-btn>
<v-spacer></v-spacer>
<v-btn color="blue-darken-1" variant="text" @click="updateConfig">{{
tm("buttons.saveAndClose")
}}</v-btn>
<v-btn
color="blue-darken-1"
variant="text"
@click="configDialog = false"
>{{ tm("buttons.close") }}</v-btn
>
{{ tm("buttons.close") }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 加载对话框 -->
<v-dialog
v-model="loadingDialog.show"
width="700"
persistent
>
<v-dialog v-model="loadingDialog.show" width="700" persistent>
<v-card>
<v-card-title class="text-h5">
{{ loadingDialog.title }}
</v-card-title>
<v-card-title class="text-h5">{{ loadingDialog.title }}</v-card-title>
<v-card-text style="max-height: calc(100vh - 200px); overflow-y: auto">
<v-progress-linear
v-if="loadingDialog.statusCode === 0"
indeterminate
color="primary"
class="mb-4"
/>
></v-progress-linear>
<div
v-if="loadingDialog.statusCode !== 0"
class="py-8 text-center"
>
<div v-if="loadingDialog.statusCode !== 0" class="py-8 text-center">
<v-icon
class="mb-6"
:color="loadingDialog.statusCode === 1 ? 'success' : 'error'"
@@ -357,45 +311,40 @@ const {
: 'mdi-alert-circle-outline'
"
size="128"
/>
<div class="text-h4 font-weight-bold">
{{ loadingDialog.result }}
</div>
></v-icon>
<div class="text-h4 font-weight-bold">{{ loadingDialog.result }}</div>
</div>
<div style="margin-top: 32px">
<h3>{{ tm("dialogs.loading.logs") }}</h3>
<ConsoleDisplayer
history-num="10"
historyNum="10"
style="height: 200px; margin-top: 16px; margin-bottom: 24px"
/>
>
</ConsoleDisplayer>
</div>
</v-card-text>
<v-divider />
<v-divider></v-divider>
<v-card-actions class="pa-4">
<v-spacer />
<v-spacer></v-spacer>
<v-btn
color="blue-darken-1"
variant="text"
@click="resetLoadingDialog"
>{{ tm("buttons.close") }}</v-btn
>
{{ tm("buttons.close") }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 插件信息对话框 -->
<v-dialog
v-model="showPluginInfoDialog"
width="1200"
>
<v-dialog v-model="showPluginInfoDialog" width="1200">
<v-card>
<v-card-title class="text-h5">
{{ selectedPlugin.name }} {{ tm("buttons.viewInfo") }}
</v-card-title>
<v-card-title class="text-h5"
>{{ selectedPlugin.name }} {{ tm("buttons.viewInfo") }}</v-card-title
>
<v-card-text>
<v-data-table
style="font-size: 17px"
@@ -403,45 +352,43 @@ const {
:items="selectedPlugin.handlers"
item-key="name"
>
<template #header.id="{ column }">
<p style="font-weight: bold">
{{ column.title }}
</p>
<template v-slot:header.id="{ column }">
<p style="font-weight: bold">{{ column.title }}</p>
</template>
<template #item.event_type="{ item }">
<template v-slot:item.event_type="{ item }">
{{ item.event_type }}
</template>
<template #item.desc="{ item }">
<template v-slot:item.desc="{ item }">
{{ item.desc }}
</template>
<template #item.type="{ item }">
<template v-slot:item.type="{ item }">
<v-chip color="success">
{{ item.type }}
</v-chip>
</template>
<template #item.cmd="{ item }">
<template v-slot:item.cmd="{ item }">
<span style="font-weight: bold">{{ item.cmd }}</span>
</template>
</v-data-table>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-spacer></v-spacer>
<v-btn
color="blue-darken-1"
variant="text"
@click="showPluginInfoDialog = false"
>{{ tm("buttons.close") }}</v-btn
>
{{ tm("buttons.close") }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-snackbar
v-model="snack_show"
:timeout="2000"
elevation="24"
:color="snack_success"
v-model="snack_show"
location="bottom center"
>
{{ snack_message }}
</v-snackbar>
@@ -467,18 +414,10 @@ const {
/>
<!-- 更新全部插件确认对话框 -->
<v-dialog
v-model="updateAllConfirmDialog.show"
max-width="420"
>
<v-dialog v-model="updateAllConfirmDialog.show" max-width="420">
<v-card class="rounded-lg">
<v-card-title class="d-flex align-center pa-4">
<v-icon
color="warning"
class="mr-2"
>
mdi-update
</v-icon>
<v-icon color="warning" class="mr-2">mdi-update</v-icon>
{{ tm("dialogs.updateAllConfirm.title") }}
</v-card-title>
<v-card-text>
@@ -487,38 +426,26 @@ const {
</p>
</v-card-text>
<v-card-actions class="pa-4">
<v-spacer />
<v-spacer></v-spacer>
<v-btn
variant="text"
@click="cancelUpdateAll"
>
{{ tm("buttons.cancel") }}
</v-btn>
>{{ tm("buttons.cancel") }}</v-btn>
<v-btn
color="warning"
variant="flat"
@click="confirmUpdateAll"
>
{{ tm("dialogs.updateAllConfirm.confirm") }}
</v-btn>
>{{ tm("dialogs.updateAllConfirm.confirm") }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 指令冲突提示对话框 -->
<v-dialog
v-model="conflictDialog.show"
max-width="420"
>
<v-dialog v-model="conflictDialog.show" max-width="420">
<v-card class="rounded-lg">
<v-card-title class="d-flex align-center pa-4">
<v-icon
color="warning"
class="mr-2"
>
mdi-alert-circle
</v-icon>
<v-icon color="warning" class="mr-2">mdi-alert-circle</v-icon>
{{ tm("conflicts.title") }}
</v-card-title>
<v-card-text class="px-4 pb-2">
@@ -541,20 +468,11 @@ const {
</p>
</v-card-text>
<v-card-actions class="pa-4 pt-2">
<v-spacer />
<v-btn
variant="text"
@click="conflictDialog.show = false"
>
{{
tm("conflicts.later")
}}
</v-btn>
<v-btn
color="warning"
variant="flat"
@click="handleConflictConfirm"
>
<v-spacer></v-spacer>
<v-btn variant="text" @click="conflictDialog.show = false">{{
tm("conflicts.later")
}}</v-btn>
<v-btn color="warning" variant="flat" @click="handleConflictConfirm">
{{ tm("conflicts.goToManage") }}
</v-btn>
</v-card-actions>
@@ -562,36 +480,21 @@ const {
</v-dialog>
<!-- 危险插件确认对话框 -->
<v-dialog
v-model="dangerConfirmDialog"
width="500"
persistent
>
<v-dialog v-model="dangerConfirmDialog" width="500" persistent>
<v-card>
<v-card-title class="text-h5 d-flex align-center">
<v-icon
color="warning"
class="mr-2"
>
mdi-alert-circle
</v-icon>
<v-icon color="warning" class="mr-2">mdi-alert-circle</v-icon>
{{ tm("dialogs.danger_warning.title") }}
</v-card-title>
<v-card-text>
<div>{{ tm("dialogs.danger_warning.message") }}</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="grey"
@click="cancelDangerInstall"
>
<v-spacer></v-spacer>
<v-btn color="grey" @click="cancelDangerInstall">
{{ tm("dialogs.danger_warning.cancel") }}
</v-btn>
<v-btn
color="warning"
@click="confirmDangerInstall"
>
<v-btn color="warning" @click="confirmDangerInstall">
{{ tm("dialogs.danger_warning.confirm") }}
</v-btn>
</v-card-actions>
@@ -599,41 +502,24 @@ const {
</v-dialog>
<!-- 版本不兼容警告对话框 -->
<v-dialog
v-model="versionCompatibilityDialog.show"
width="520"
persistent
>
<v-dialog v-model="versionCompatibilityDialog.show" width="520" persistent>
<v-card>
<v-card-title class="text-h5 d-flex align-center">
<v-icon
color="warning"
class="mr-2"
>
mdi-alert
</v-icon>
<v-icon color="warning" class="mr-2">mdi-alert</v-icon>
{{ tm("dialogs.versionCompatibility.title") }}
</v-card-title>
<v-card-text>
<div class="mb-2">
{{ tm("dialogs.versionCompatibility.message") }}
</div>
<div class="mb-2">{{ tm("dialogs.versionCompatibility.message") }}</div>
<div class="text-medium-emphasis">
{{ versionCompatibilityDialog.message }}
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="grey"
@click="cancelInstallOnVersionWarning"
>
<v-spacer></v-spacer>
<v-btn color="grey" @click="cancelInstallOnVersionWarning">
{{ tm("dialogs.versionCompatibility.cancel") }}
</v-btn>
<v-btn
color="warning"
@click="continueInstallIgnoringVersionWarning"
>
<v-btn color="warning" @click="continueInstallIgnoringVersionWarning">
{{ tm("dialogs.versionCompatibility.confirm") }}
</v-btn>
</v-card-actions>
@@ -641,10 +527,7 @@ const {
</v-dialog>
<!-- 上传插件对话框 -->
<v-dialog
v-model="dialog"
width="500"
>
<v-dialog v-model="dialog" width="500">
<div
class="v-card v-card--density-default rounded-lg v-card--variant-elevated"
>
@@ -654,7 +537,7 @@ const {
color="primary"
height="2"
:active="loading_"
/>
></v-progress-linear>
</div>
<v-card-title class="text-h3 pa-4 pb-0 pl-6">
@@ -662,22 +545,12 @@ const {
</v-card-title>
<div class="v-card-text">
<v-tabs
v-model="uploadTab"
color="primary"
>
<v-tab value="file">
{{ tm("dialogs.install.fromFile") }}
</v-tab>
<v-tab value="url">
{{ tm("dialogs.install.fromUrl") }}
</v-tab>
<v-tabs v-model="uploadTab" color="primary">
<v-tab value="file">{{ tm("dialogs.install.fromFile") }}</v-tab>
<v-tab value="url">{{ tm("dialogs.install.fromUrl") }}</v-tab>
</v-tabs>
<v-window
v-model="uploadTab"
class="mt-4"
>
<v-window v-model="uploadTab" class="mt-4">
<v-window-item value="file">
<div class="d-flex flex-column align-center justify-center pa-4">
<v-file-input
@@ -688,14 +561,14 @@ const {
hide-details
hide-input
class="d-none"
/>
></v-file-input>
<v-btn
color="primary"
size="large"
prepend-icon="mdi-upload"
elevation="2"
@click="$refs.fileInput.click()"
elevation="2"
>
{{ tm("buttons.selectFile") }}
</v-btn>
@@ -704,10 +577,7 @@ const {
{{ tm("messages.supportedFormats") }}
</div>
<div
v-if="upload_file"
class="mt-4 text-center"
>
<div v-if="upload_file" class="mt-4 text-center">
<v-chip
color="primary"
size="large"
@@ -715,8 +585,10 @@ const {
@click:close="upload_file = null"
>
{{ upload_file.name }}
<template #append>
<span class="text-caption ml-2">({{ (upload_file.size / 1024).toFixed(1) }}KB)</span>
<template v-slot:append>
<span class="text-caption ml-2"
>({{ (upload_file.size / 1024).toFixed(1) }}KB)</span
>
</template>
</v-chip>
</div>
@@ -733,12 +605,9 @@ const {
hide-details
class="rounded-lg mb-4"
placeholder="https://github.com/username/repo"
/>
></v-text-field>
<div
v-if="selectedInstallPlugin"
class="mb-3"
>
<div v-if="selectedInstallPlugin" class="mb-3">
<v-chip
v-if="selectedInstallPlugin.astrbot_version"
size="small"
@@ -766,8 +635,8 @@ const {
<v-alert
v-if="
selectedInstallPlugin.astrbot_version &&
installCompat.checked &&
!installCompat.compatible
installCompat.checked &&
!installCompat.compatible
"
type="warning"
variant="tonal"
@@ -778,65 +647,46 @@ const {
</v-alert>
</div>
<ProxySelector />
<ProxySelector></ProxySelector>
</div>
</v-window-item>
</v-window>
</div>
<div class="v-card-actions">
<v-spacer />
<v-btn
color="grey"
variant="text"
@click="dialog = false"
>
{{
tm("buttons.cancel")
}}
</v-btn>
<v-btn
color="primary"
variant="text"
@click="newExtension"
>
{{
tm("buttons.install")
}}
</v-btn>
<v-spacer></v-spacer>
<v-btn color="grey" variant="text" @click="dialog = false">{{
tm("buttons.cancel")
}}</v-btn>
<v-btn color="primary" variant="text" @click="newExtension">{{
tm("buttons.install")
}}</v-btn>
</div>
</div>
</v-dialog>
<!-- 插件源管理对话框 -->
<v-dialog
v-model="showSourceManagerDialog"
width="640"
>
<v-dialog v-model="showSourceManagerDialog" width="640">
<v-card>
<v-card-title class="text-h3 pa-4 pl-6">
{{
tm("market.sourceManagement")
}}
</v-card-title>
<v-card-title class="text-h3 pa-4 pl-6">{{
tm("market.sourceManagement")
}}</v-card-title>
<v-card-text>
<v-select
:model-value="selectedSource || '__default__'"
@update:model-value="
selectPluginSource($event === '__default__' ? null : $event)
"
:items="sourceSelectItems"
:label="tm('market.currentSource')"
variant="outlined"
prepend-inner-icon="mdi-source-branch"
hide-details
class="mb-4"
@update:model-value="
selectPluginSource($event === '__default__' ? null : $event)
"
/>
></v-select>
<div class="d-flex align-center justify-space-between mb-2">
<div class="text-subtitle-2">
{{ tm("market.availableSources") }}
</div>
<div class="text-subtitle-2">{{ tm("market.availableSources") }}</div>
<v-btn
size="small"
color="primary"
@@ -848,23 +698,15 @@ const {
</v-btn>
</div>
<v-list
density="compact"
nav
class="pa-0"
>
<v-list density="compact" nav class="pa-0">
<v-list-item
rounded="md"
color="primary"
:active="selectedSource === null"
@click="selectPluginSource(null)"
>
<template #prepend>
<v-icon
icon="mdi-shield-check"
size="small"
class="mr-2"
/>
<template v-slot:prepend>
<v-icon icon="mdi-shield-check" size="small" class="mr-2"></v-icon>
</template>
<v-list-item-title>{{ tm("market.defaultSource") }}</v-list-item-title>
</v-list-item>
@@ -877,64 +719,47 @@ const {
:active="selectedSource === source.url"
@click="selectPluginSource(source.url)"
>
<template #prepend>
<v-icon
icon="mdi-link-variant"
size="small"
class="mr-2"
/>
<template v-slot:prepend>
<v-icon icon="mdi-link-variant" size="small" class="mr-2"></v-icon>
</template>
<v-list-item-title>{{ source.name }}</v-list-item-title>
<v-list-item-subtitle class="text-caption">
{{
source.url
}}
</v-list-item-subtitle>
<template #append>
<v-list-item-subtitle class="text-caption">{{
source.url
}}</v-list-item-subtitle>
<template v-slot:append>
<v-btn
icon="mdi-pencil-outline"
size="small"
variant="text"
color="medium-emphasis"
@click.stop="editCustomSource(source)"
/>
></v-btn>
<v-btn
icon="mdi-trash-can-outline"
size="small"
variant="text"
color="error"
@click.stop="removeCustomSource(source)"
/>
></v-btn>
</template>
</v-list-item>
</v-list>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="primary"
variant="text"
@click="showSourceManagerDialog = false"
>
{{
tm("buttons.close")
}}
</v-btn>
<v-spacer></v-spacer>
<v-btn color="primary" variant="text" @click="showSourceManagerDialog = false">{{
tm("buttons.close")
}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 添加/编辑自定义插件源对话框 -->
<v-dialog
v-model="showSourceDialog"
width="500"
>
<v-dialog v-model="showSourceDialog" width="500">
<v-card>
<v-card-title class="text-h5">
{{
editingSource ? tm("market.editSource") : tm("market.addSource")
}}
</v-card-title>
<v-card-title class="text-h5">{{
editingSource ? tm("market.editSource") : tm("market.addSource")
}}</v-card-title>
<v-card-text>
<div class="pa-2">
<v-text-field
@@ -945,7 +770,7 @@ const {
hide-details
class="mb-4"
placeholder="我的插件源"
/>
></v-text-field>
<v-text-field
v-model="sourceUrl"
@@ -954,7 +779,7 @@ const {
prepend-inner-icon="mdi-link"
hide-details
placeholder="https://example.com/plugins.json"
/>
></v-text-field>
<div class="text-caption text-medium-emphasis mt-2">
{{ tm("messages.enterJsonUrl") }}
@@ -962,115 +787,64 @@ const {
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="grey"
variant="text"
@click="showSourceDialog = false"
>
{{
tm("buttons.cancel")
}}
</v-btn>
<v-btn
color="primary"
variant="text"
@click="saveCustomSource"
>
{{
tm("buttons.save")
}}
</v-btn>
<v-spacer></v-spacer>
<v-btn color="grey" variant="text" @click="showSourceDialog = false">{{
tm("buttons.cancel")
}}</v-btn>
<v-btn color="primary" variant="text" @click="saveCustomSource">{{
tm("buttons.save")
}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 删除插件源确认对话框 -->
<v-dialog
v-model="showRemoveSourceDialog"
width="400"
>
<v-dialog v-model="showRemoveSourceDialog" width="400">
<v-card>
<v-card-title class="text-h5 d-flex align-center">
<v-icon
color="warning"
class="mr-2"
>
mdi-alert-circle
</v-icon>
<v-icon color="warning" class="mr-2">mdi-alert-circle</v-icon>
{{ tm("dialogs.uninstall.title") }}
</v-card-title>
<v-card-text>
<div>{{ tm("market.confirmRemoveSource") }}</div>
<div
v-if="sourceToRemove"
class="mt-2"
>
<div v-if="sourceToRemove" class="mt-2">
<strong>{{ sourceToRemove.name }}</strong>
<div class="text-caption">
{{ sourceToRemove.url }}
</div>
<div class="text-caption">{{ sourceToRemove.url }}</div>
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-spacer></v-spacer>
<v-btn
color="grey"
variant="text"
@click="showRemoveSourceDialog = false"
>{{ tm("buttons.cancel") }}</v-btn
>
{{ tm("buttons.cancel") }}
</v-btn>
<v-btn
color="error"
variant="text"
@click="confirmRemoveSource"
>
{{
tm("buttons.deleteSource")
}}
</v-btn>
<v-btn color="error" variant="text" @click="confirmRemoveSource">{{
tm("buttons.deleteSource")
}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 强制更新确认对话框 -->
<v-dialog
v-model="forceUpdateDialog.show"
max-width="420"
>
<v-dialog v-model="forceUpdateDialog.show" max-width="420">
<v-card class="rounded-lg">
<v-card-title class="text-h6 d-flex align-center">
<v-icon
color="info"
class="mr-2"
>
mdi-information-outline
</v-icon>
<v-icon color="info" class="mr-2">mdi-information-outline</v-icon>
{{ tm("dialogs.forceUpdate.title") }}
</v-card-title>
<v-card-text>
{{ tm("dialogs.forceUpdate.message") }}
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
variant="text"
@click="forceUpdateDialog.show = false"
>
{{
tm("buttons.cancel")
}}
</v-btn>
<v-btn
color="primary"
variant="flat"
@click="confirmForceUpdate"
>
{{
tm("dialogs.forceUpdate.confirm")
}}
</v-btn>
<v-spacer></v-spacer>
<v-btn variant="text" @click="forceUpdateDialog.show = false">{{
tm("buttons.cancel")
}}</v-btn>
<v-btn color="primary" variant="flat" @click="confirmForceUpdate">{{
tm("dialogs.forceUpdate.confirm")
}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
+18 -11
View File
@@ -11,6 +11,7 @@ import {
resolveUsedIcons,
extractUtilityCss,
ICON_CLASS_PATTERN,
REQUIRED_ICONS,
} from '../scripts/subset-mdi-font.mjs';
// ── Helper: create a temporary directory tree for file-system tests ─────────
@@ -83,9 +84,11 @@ test('scanUsedIcons extracts mdi-* icon names from files', () => {
assert.ok(icons instanceof Set);
assert.ok(icons.has('mdi-home'));
assert.ok(icons.has('mdi-close'));
assert.ok(icons.has('mdi-radiobox-blank'));
assert.ok(icons.has('mdi-radiobox-marked'));
assert.equal(icons.size, 4); // source icons + required radio icons
for (const requiredIcon of REQUIRED_ICONS) {
assert.ok(icons.has(requiredIcon));
}
const expectedIcons = new Set([...REQUIRED_ICONS, 'mdi-home', 'mdi-close']);
assert.deepEqual(icons, expectedIcons);
rmSync(tmp, { recursive: true });
});
@@ -103,25 +106,29 @@ test('scanUsedIcons excludes utility classes', () => {
rmSync(tmp, { recursive: true });
});
test('scanUsedIcons includes required radio icons even when no mdi-* icons are found in source', () => {
test('scanUsedIcons includes all required icons even when no mdi-* icons are found in source', () => {
const tmp = makeTmpDir();
writeFileSync(join(tmp, 'A.vue'), '<div>Hello</div>');
const icons = scanUsedIcons(collectFiles(tmp, ['.vue']));
assert.ok(icons.has('mdi-radiobox-blank'));
assert.ok(icons.has('mdi-radiobox-marked'));
assert.equal(icons.size, 2);
for (const requiredIcon of REQUIRED_ICONS) {
assert.ok(icons.has(requiredIcon));
}
assert.equal(icons.size, REQUIRED_ICONS.size);
rmSync(tmp, { recursive: true });
});
test('scanUsedIcons deduplicates required radio icons when source already references them', () => {
test('scanUsedIcons deduplicates required icons when source already references them', () => {
const tmp = makeTmpDir();
writeFileSync(join(tmp, 'A.vue'), '<v-icon>mdi-radiobox-marked</v-icon><v-icon>mdi-home</v-icon>');
const requiredIcon = [...REQUIRED_ICONS][0];
writeFileSync(join(tmp, 'A.vue'), `<v-icon>${requiredIcon}</v-icon><v-icon>mdi-home</v-icon>`);
const icons = [...scanUsedIcons(collectFiles(tmp, ['.vue']))];
assert.equal(icons.filter(icon => icon === 'mdi-radiobox-marked').length, 1);
assert.ok(icons.includes('mdi-radiobox-blank'));
assert.equal(icons.filter(icon => icon === requiredIcon).length, 1);
for (const builtInRequiredIcon of REQUIRED_ICONS) {
assert.ok(icons.includes(builtInRequiredIcon));
}
assert.ok(icons.includes('mdi-home'));
rmSync(tmp, { recursive: true });
+11 -9
View File
@@ -16,15 +16,17 @@ Welcome to submit Issues or Pull Requests:
### Tencent QQ Groups
> - All groups are available to join. If you find that the group size is below the limit, please feel free to join.
- Group 1: 322154837 (2000-member group)
- Group 3: 630166526 (2000-member group)
- Group 4: 1077826412 (1000-member group)
- Group 5: 822130018 (2000-member group)
- Group 6: 753075035 (2000-member group)
- Group 7: 743746109 (500-member group)
- Group 8: 1030353265 (500-member group)
- Group 12: 916228568 (New)
- Group 9: 1076659624 (Full)
- Group 10: 1078079676 (Full)
- Group 11: 704659519 (Full)
- Group 1: 322154837 (Full)
- Group 3: 630166526 (Full)
- Group 4: 1077826412 (Full)
- Group 5: 822130018 (Full)
- Group 6: 753075035 (Full)
- Group 7: 743746109 (Full)
- Group 8: 1030353265 (Full)
- **AstrBot Core Development Group: 975206796** (AstrBot development members are usually active here. Welcome to anyone interested in programming/AI technology~)
## Become an AstrBot Organization Member
+11 -11
View File
@@ -6,17 +6,17 @@
### QQ 群
> 所有群都可以插空加入,如果您发现群人数小于上限,请尝试加入。
- 9 群: 1076659624 (500 人群, 优先加此群)
- 10 群: 1078079676 (500 人群, 优先加此群)
- 1 群: 322154837 (2000 人群, 人满)
- 3 群: 630166526 (2000 人群, 人满)
- 4 群: 1077826412 (1000 人群, 人满)
- 5 群: 822130018 (2000 人群, 人满)
- 6 群: 753075035 (2000 人群, 人满)
- 7 群: 743746109 (500 人群, 人满)
- 8 群: 1030353265 (500 人群, 人满)
- 12 群: 916228568 (新)
- 9 群: 1076659624 (人满)
- 10 群: 1078079676 (人满)
- 11 群: 704659519 (人满)
- 1 群: 322154837 (人满)
- 3 群: 630166526 (人满)
- 4 群: 1077826412 (人满)
- 5 群: 822130018 (人满)
- 6 群: 753075035 (人满)
- 7 群: 743746109 (人满)
- 8 群: 1030353265 (人满)
- **AstrBot 核心开发交流群: 975206796**(AstrBot 开发成员通常活跃于此,欢迎任何对编程/AI 技术感兴趣的同学加入~)
### Discord
+1 -1
View File
@@ -3,7 +3,7 @@
随着插件功能的增加,可能需要定义一些配置以让用户自定义插件的行为。
AstrBot 提供了”强大“的配置解析和可视化功能。能够让用户在管理面板上直接配置插件,而不需要修改代码。
AstrBot 提供了“强大”的配置解析和可视化功能。能够让用户在管理面板上直接配置插件,而不需要修改代码。
## 配置定义
+98
View File
@@ -0,0 +1,98 @@
import json
from types import SimpleNamespace
import pytest
from astrbot.core.agent.run_context import ContextWrapper
from astrbot.core.computer.tools.browser import BrowserExecTool
from astrbot.core.computer.tools.neo_skills import GetExecutionHistoryTool
class _FakeBrowser:
async def exec(self, **kwargs):
return {
"ok": True,
"cmd": kwargs["cmd"],
}
class _FakeSandbox:
async def get_execution_history(self, **kwargs):
return {
"items": [],
"limit": kwargs["limit"],
}
def _make_run_context(require_admin: bool, role: str = "member") -> ContextWrapper:
config_holder = SimpleNamespace(
get_config=lambda umo: { # noqa: ARG005
"provider_settings": {
"computer_use_require_admin": require_admin,
}
}
)
event = SimpleNamespace(
role=role,
unified_msg_origin="qq_official:friend:user-1",
get_sender_id=lambda: "user-1",
)
astr_ctx = SimpleNamespace(context=config_holder, event=event)
return ContextWrapper(context=astr_ctx)
@pytest.mark.asyncio
async def test_browser_tool_allows_non_admin_when_admin_requirement_disabled(
monkeypatch,
):
async def _fake_get_booter(_ctx, _session_id):
return SimpleNamespace(browser=_FakeBrowser())
monkeypatch.setattr(
"astrbot.core.computer.tools.browser.get_booter",
_fake_get_booter,
)
result = await BrowserExecTool().call(
_make_run_context(require_admin=False),
cmd="open https://example.com",
)
assert json.loads(result)["ok"] is True
@pytest.mark.asyncio
async def test_neo_skill_tool_allows_non_admin_when_admin_requirement_disabled(
monkeypatch,
):
async def _fake_get_booter(_ctx, _session_id):
return SimpleNamespace(
bay_client=object(),
sandbox=_FakeSandbox(),
)
monkeypatch.setattr(
"astrbot.core.computer.tools.neo_skills.get_booter",
_fake_get_booter,
)
result = await GetExecutionHistoryTool().call(
_make_run_context(require_admin=False),
limit=5,
)
payload = json.loads(result)
assert payload["items"] == []
assert payload["limit"] == 5
@pytest.mark.asyncio
async def test_browser_tool_still_denies_non_admin_when_admin_requirement_enabled():
result = await BrowserExecTool().call(
_make_run_context(require_admin=True),
cmd="open https://example.com",
)
assert "Permission denied" in result
assert "Using browser tools is only allowed for admin users" in result
assert "User's ID is: user-1" in result
+15 -2
View File
@@ -54,8 +54,21 @@ def test_promote_stable_sync_failure_auto_rolls_back(monkeypatch):
_fake_sync_release,
)
event = SimpleNamespace(role="admin", unified_msg_origin="session-1")
astr_ctx = SimpleNamespace(context=SimpleNamespace(), event=event)
event = SimpleNamespace(
role="admin",
unified_msg_origin="session-1",
get_sender_id=lambda: "admin-user",
)
astr_ctx = SimpleNamespace(
context=SimpleNamespace(
get_config=lambda umo: { # noqa: ARG005
"provider_settings": {
"computer_use_require_admin": True,
}
}
),
event=event,
)
run_ctx = ContextWrapper(context=astr_ctx)
tool = PromoteSkillCandidateTool()
+605 -2
View File
@@ -2,6 +2,7 @@ from types import SimpleNamespace
import pytest
from openai.types.chat.chat_completion import ChatCompletion
from PIL import Image as PILImage
from astrbot.core.provider.sources.groq_source import ProviderGroq
from astrbot.core.provider.sources.openai_source import ProviderOpenAIOfficial
@@ -234,7 +235,9 @@ async def test_openai_payload_keeps_reasoning_content_in_assistant_history():
provider._finally_convert_payload(payloads)
assistant_message = payloads["messages"][0]
assert assistant_message["content"] == [{"type": "text", "text": "final answer"}]
assert assistant_message["content"] == [
{"type": "text", "text": "final answer"}
]
assert assistant_message["reasoning_content"] == "step 1"
finally:
await provider.terminate()
@@ -259,7 +262,9 @@ async def test_groq_payload_drops_reasoning_content_from_assistant_history():
provider._finally_convert_payload(payloads)
assistant_message = payloads["messages"][0]
assert assistant_message["content"] == [{"type": "text", "text": "final answer"}]
assert assistant_message["content"] == [
{"type": "text", "text": "final answer"}
]
assert "reasoning_content" not in assistant_message
assert "reasoning" not in assistant_message
finally:
@@ -450,6 +455,604 @@ async def test_handle_api_error_unknown_image_error_raises():
await provider.terminate()
@pytest.mark.asyncio
async def test_handle_api_error_invalid_attachment_removes_images_and_retries_text_only():
provider = _make_provider()
try:
payloads = {
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "hello"},
{
"type": "image_url",
"image_url": {"url": "data:image/jpeg;base64,abcd"},
},
],
}
]
}
context_query = payloads["messages"]
err = _ErrorWithBody(
"upstream error",
{
"error": {
"code": "INVALID_ATTACHMENT",
"message": "download attachment: unexpected status 404",
}
},
)
success, *_rest = await provider._handle_api_error(
err,
payloads=payloads,
context_query=context_query,
func_tool=None,
chosen_key="test-key",
available_api_keys=["test-key"],
retry_cnt=0,
max_retries=10,
)
assert success is False
assert payloads["messages"][0]["content"] == [{"type": "text", "text": "hello"}]
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_handle_api_error_invalid_attachment_without_images_raises():
provider = _make_provider()
try:
payloads = {
"messages": [
{
"role": "user",
"content": [{"type": "text", "text": "hello"}],
}
]
}
context_query = payloads["messages"]
err = _ErrorWithBody(
"upstream error",
{
"error": {
"code": "INVALID_ATTACHMENT",
"message": "download attachment: unexpected status 404",
}
},
)
with pytest.raises(_ErrorWithBody, match="upstream error"):
await provider._handle_api_error(
err,
payloads=payloads,
context_query=context_query,
func_tool=None,
chosen_key="test-key",
available_api_keys=["test-key"],
retry_cnt=0,
max_retries=10,
)
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_handle_api_error_invalid_attachment_after_fallback_raises():
provider = _make_provider()
try:
payloads = {
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "hello"},
{
"type": "image_url",
"image_url": {"url": "data:image/jpeg;base64,abcd"},
},
],
}
]
}
context_query = payloads["messages"]
err = _ErrorWithBody(
"upstream error",
{
"error": {
"code": "INVALID_ATTACHMENT",
"message": "download attachment: unexpected status 404",
}
},
)
with pytest.raises(_ErrorWithBody, match="upstream error"):
await provider._handle_api_error(
err,
payloads=payloads,
context_query=context_query,
func_tool=None,
chosen_key="test-key",
available_api_keys=["test-key"],
retry_cnt=1,
max_retries=10,
image_fallback_used=True,
)
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_prepare_chat_payload_materializes_context_http_image_urls(monkeypatch):
provider = _make_provider()
try:
async def fake_download(url: str) -> str:
assert url == "https://example.com/quoted.png"
return "/tmp/quoted.png"
def fake_encode(image_path: str, **_kwargs) -> str:
assert image_path == "/tmp/quoted.png"
return "data:image/png;base64,abcd"
monkeypatch.setattr(
"astrbot.core.provider.sources.openai_source.download_image_by_url",
fake_download,
)
monkeypatch.setattr(provider, "_encode_image_file_to_data_url", fake_encode)
contexts = [
{
"role": "user",
"metadata": {"source": "quoted"},
"content": [
{"type": "text", "text": "look"},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/quoted.png",
"id": "ctx-img",
"detail": "high",
},
},
],
}
]
payloads, _ = await provider._prepare_chat_payload(
prompt=None,
contexts=contexts,
)
assert payloads["messages"][0]["content"] == [
{"type": "text", "text": "look"},
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,abcd",
"detail": "high",
},
},
]
assert payloads["messages"][0]["content"][1]["image_url"].get("id") is None
assert contexts[0]["content"][1]["image_url"] == {
"url": "https://example.com/quoted.png",
"id": "ctx-img",
"detail": "high",
}
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_prepare_chat_payload_skips_materialization_for_text_only_context(
monkeypatch,
):
provider = _make_provider()
try:
async def fail_if_called(_context_query):
raise AssertionError("materialization should be skipped")
monkeypatch.setattr(
provider, "_materialize_context_image_parts", fail_if_called
)
payloads, _ = await provider._prepare_chat_payload(
prompt=None,
contexts=[{"role": "user", "content": "hello"}],
)
assert payloads["messages"] == [{"role": "user", "content": "hello"}]
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_prepare_chat_payload_skips_materialization_for_text_only_parts(
monkeypatch,
):
provider = _make_provider()
try:
async def fail_if_called(_context_query):
raise AssertionError("materialization should be skipped")
monkeypatch.setattr(
provider, "_materialize_context_image_parts", fail_if_called
)
payloads, _ = await provider._prepare_chat_payload(
prompt=None,
contexts=[
{
"role": "user",
"content": [{"type": "text", "text": "hello"}],
}
],
)
assert payloads["messages"] == [
{
"role": "user",
"content": [{"type": "text", "text": "hello"}],
}
]
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_prepare_chat_payload_materializes_context_http_image_urls_with_detected_mime(
monkeypatch, tmp_path
):
provider = _make_provider()
try:
image_path = tmp_path / "quoted-image.png"
PILImage.new("RGBA", (1, 1), (255, 0, 0, 255)).save(image_path)
async def fake_download(url: str) -> str:
assert url == "https://example.com/quoted.png"
return str(image_path)
monkeypatch.setattr(
"astrbot.core.provider.sources.openai_source.download_image_by_url",
fake_download,
)
payloads, _ = await provider._prepare_chat_payload(
prompt=None,
contexts=[
{
"role": "user",
"content": [
{"type": "text", "text": "look"},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/quoted.png",
},
},
],
}
],
)
image_payload = payloads["messages"][0]["content"][1]["image_url"]
assert image_payload["url"].startswith("data:image/png;base64,")
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_prepare_chat_payload_materializes_context_file_uri_image_urls(tmp_path):
provider = _make_provider()
try:
image_path = tmp_path / "quoted-image.png"
PILImage.new("RGBA", (1, 1), (255, 0, 0, 255)).save(image_path)
payloads, _ = await provider._prepare_chat_payload(
prompt=None,
contexts=[
{
"role": "user",
"content": [
{"type": "text", "text": "look"},
{
"type": "image_url",
"image_url": {
"url": image_path.as_uri(),
},
},
],
}
],
)
image_payload = payloads["messages"][0]["content"][1]["image_url"]
assert image_payload["url"].startswith("data:image/png;base64,")
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_file_uri_to_path_preserves_windows_drive_letter():
provider = _make_provider()
try:
assert provider._file_uri_to_path("file:///C:/tmp/quoted-image.png") == (
"C:/tmp/quoted-image.png"
)
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_file_uri_to_path_preserves_windows_netloc_drive_letter():
provider = _make_provider()
try:
assert provider._file_uri_to_path("file://C:/tmp/quoted-image.png") == (
"C:/tmp/quoted-image.png"
)
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_file_uri_to_path_preserves_remote_netloc_as_unc_path():
provider = _make_provider()
try:
assert provider._file_uri_to_path("file://server/share/quoted-image.png") == (
"//server/share/quoted-image.png"
)
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_resolve_image_part_rejects_invalid_local_file(tmp_path):
provider = _make_provider()
try:
invalid_file = tmp_path / "not-image.txt"
invalid_file.write_text("not an image")
assert await provider._resolve_image_part(str(invalid_file)) is None
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_resolve_image_part_rejects_invalid_file_uri(tmp_path):
provider = _make_provider()
try:
invalid_file = tmp_path / "not-image.txt"
invalid_file.write_text("not an image")
assert await provider._resolve_image_part(invalid_file.as_uri()) is None
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_image_ref_to_data_url_mode_controls_invalid_file_behavior(tmp_path):
provider = _make_provider()
try:
invalid_file = tmp_path / "not-image.txt"
invalid_file.write_text("not an image")
assert (
await provider._image_ref_to_data_url(str(invalid_file), mode="safe")
is None
)
with pytest.raises(ValueError, match="Invalid image file"):
await provider._image_ref_to_data_url(str(invalid_file), mode="strict")
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_materialize_context_image_parts_returns_new_messages(monkeypatch):
provider = _make_provider()
try:
context_query = [
{
"role": "user",
"metadata": {"source": "quoted"},
"content": [
{"type": "text", "text": "look"},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/quoted.png",
"detail": "high",
},
},
],
},
{"role": "assistant", "content": "plain text"},
]
async def fake_resolve(image_url: str, *, image_detail: str | None = None):
assert image_url == "https://example.com/quoted.png"
assert image_detail == "high"
return {
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,abcd",
"detail": "high",
},
}
monkeypatch.setattr(provider, "_resolve_image_part", fake_resolve)
materialized = await provider._materialize_context_image_parts(context_query)
assert materialized is not context_query
assert materialized[0] is not context_query[0]
assert materialized[0]["metadata"] is context_query[0]["metadata"]
assert materialized[0]["content"][0] is context_query[0]["content"][0]
assert (
materialized[0]["content"][1]["image_url"]["url"]
== "data:image/png;base64,abcd"
)
assert (
context_query[0]["content"][1]["image_url"]["url"]
== "https://example.com/quoted.png"
)
assert materialized[1] is not context_query[1]
assert materialized[1]["content"] == "plain text"
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_encode_image_bs64_missing_file_raises(tmp_path):
provider = _make_provider()
try:
missing_path = tmp_path / "missing-image.png"
with pytest.raises(FileNotFoundError):
await provider.encode_image_bs64(str(missing_path))
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_encode_image_bs64_invalid_file_raises(tmp_path):
provider = _make_provider()
try:
invalid_file = tmp_path / "not-image.txt"
invalid_file.write_text("not an image")
with pytest.raises(ValueError, match="Invalid image file"):
await provider.encode_image_bs64(str(invalid_file))
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_encode_image_bs64_supports_base64_scheme():
provider = _make_provider()
try:
image_data = await provider.encode_image_bs64("base64://abcd")
assert image_data == "data:image/jpeg;base64,abcd"
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_encode_image_bs64_supports_file_uri(tmp_path):
provider = _make_provider()
try:
image_path = tmp_path / "quoted-image.png"
PILImage.new("RGBA", (1, 1), (255, 0, 0, 255)).save(image_path)
image_data = await provider.encode_image_bs64(image_path.as_uri())
assert image_data.startswith("data:image/png;base64,")
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_resolve_image_part_supports_base64_scheme():
provider = _make_provider()
try:
assert await provider._resolve_image_part("base64://abcd") == {
"type": "image_url",
"image_url": {"url": "data:image/jpeg;base64,abcd"},
}
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_prepare_chat_payload_materializes_context_localhost_file_uri_image_urls(
tmp_path,
):
provider = _make_provider()
try:
image_path = tmp_path / "quoted-image.png"
PILImage.new("RGBA", (1, 1), (255, 0, 0, 255)).save(image_path)
localhost_uri = f"file://localhost{image_path.as_posix()}"
payloads, _ = await provider._prepare_chat_payload(
prompt=None,
contexts=[
{
"role": "user",
"content": [
{"type": "text", "text": "look"},
{
"type": "image_url",
"image_url": {
"url": localhost_uri,
},
},
],
}
],
)
image_payload = payloads["messages"][0]["content"][1]["image_url"]
assert image_payload["url"].startswith("data:image/png;base64,")
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_prepare_chat_payload_keeps_original_context_image_when_materialization_fails(
monkeypatch,
):
provider = _make_provider()
try:
async def fake_download(url: str) -> str:
assert url == "https://example.com/expired.png"
return "/tmp/not-an-image"
monkeypatch.setattr(
"astrbot.core.provider.sources.openai_source.download_image_by_url",
fake_download,
)
monkeypatch.setattr(
provider,
"_encode_image_file_to_data_url",
lambda _image_path, **_kwargs: None,
)
payloads, _ = await provider._prepare_chat_payload(
prompt=None,
contexts=[
{
"role": "user",
"content": [
{"type": "text", "text": "look"},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/expired.png",
},
},
],
}
],
)
assert payloads["messages"][0]["content"] == [
{"type": "text", "text": "look"},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/expired.png",
},
},
]
finally:
await provider.terminate()
@pytest.mark.asyncio
async def test_apply_provider_specific_extra_body_overrides_disables_ollama_thinking():
provider = _make_provider(