# Create a model provider
Source: https://docs.langbot.app/en/api-reference/model-providers/create-a-model-provider
/openapi/service-api-en.json post /api/v1/provider/providers
Create a new model provider.
# Get a model provider
Source: https://docs.langbot.app/en/api-reference/model-providers/get-a-model-provider
/openapi/service-api-en.json get /api/v1/provider/providers/{provider_uuid}
# List all model providers
Source: https://docs.langbot.app/en/api-reference/model-providers/list-all-model-providers
/openapi/service-api-en.json get /api/v1/provider/providers
List all model providers, including the number of LLM and embedding models associated with each provider.
# Update a model provider
Source: https://docs.langbot.app/en/api-reference/model-providers/update-a-model-provider
/openapi/service-api-en.json put /api/v1/provider/providers/{provider_uuid}
# Get system information
Source: https://docs.langbot.app/en/api-reference/system/get-system-information
/openapi/service-api-en.json get /api/v1/system/info
Get basic information about the LangBot instance, including its version and release type. This endpoint does not require authentication.
# How to Connect DeepSeek R1 to WeChat, Discord & Telegram in 5 Minutes (FREE)
Source: https://docs.langbot.app/en/articles/connect-deepseek-to-wechat
A step-by-step guide to connecting DeepSeek R1/V3 API to WeChat (Personal/Enterprise), Discord, Telegram, and more using LangBot. No coding required.
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/connect-deepseek-to-wechat).
Published 2026-02-21 · Author: LangBot Team
DeepSeek has taken the AI world by storm. Its R1 reasoning model rivals OpenAI's o1 but is open-source and significantly cheaper.
However, the official DeepSeek app currently only supports "AI Search" in WeChat, and direct integration into group chats or private workflows is limited.
**What if you could have a fully functional DeepSeek R1 bot in your WeChat groups, Discord servers, and Telegram chats right now?**
With **LangBot**, you can. And it takes less than 5 minutes.
## Why LangBot?
LangBot is an open-source, production-grade IM bot platform. Unlike simple scripts or single-platform bots, LangBot gives you:
* **Unified Platform:** Connect once, deploy to WeChat, Discord, Telegram, Slack, Lark, and more.
* **Model Agnostic:** Use DeepSeek R1, V3, Claude 3.5, GPT-4, or local Ollama models.
* **No Coding:** robust WebUI for configuration.
* **Enterprise Features:** Knowledge base (RAG), Plugin system, and multi-user management.
## Prerequisites
1. **A Server or PC:** Docker installed (VPS, local computer, or Synology/NAS).
2. **DeepSeek API Key:**
* **Official:** [platform.deepseek.com](https://platform.deepseek.com)
* **SiliconFlow (Recommended for speed):** [cloud.siliconflow.cn](https://cloud.siliconflow.cn) (Faster R1 inference).
## Step 1: Deploy LangBot
If you haven't deployed LangBot yet, run this one command:
```bash theme={null}
git clone https://github.com/langbot-app/LangBot
cd LangBot/docker
docker compose up -d
```
Access your dashboard at `http://localhost:5300` (or your server IP).
## Step 2: Configure DeepSeek Model
1. Go to **Models** -> **Provider List**.
2. Find **DeepSeek** (or **SiliconFlow** / **OpenAI Compatible**).
3. Enter your API Key.
4. Click **Save**.
Now create a **Model Instance**:
* **Name:** `DeepSeek-R1-Bot`
* **Model:** `deepseek-reasoner` (for R1) or `deepseek-chat` (for V3).
## Step 3: Create a Chat Pipeline
LangBot uses "Pipelines" to manage bot logic.
1. Go to **Pipelines** -> **New Pipeline**.
2. Choose **Chat Pipeline**.
3. Select your `DeepSeek-R1-Bot` model.
4. (Optional) Add a System Prompt:
> "You are a helpful assistant powered by DeepSeek R1. You think deeply before answering."
## Step 4: Connect to WeChat (or any platform)
### For WeChat (Personal / Wechaty)
*Note: Personal WeChat access relies on third-party libraries and carries some risk. For business use, we recommend Enterprise WeChat (WeCom).*
1. Go to **Bots** -> **New Bot**.
2. Select **WeChat**.
3. Choose the **GeweChat** or **Wechaty** adapter (depending on your preference).
4. Click **Save**.
5. Scan the QR code that appears in the logs or UI.
### For Discord / Telegram
1. Select **Discord** or **Telegram**.
2. Paste your **Bot Token** (from Discord Developer Portal or BotFather).
3. Click **Save**.
## Step 5: Test It Out!
Open your chat app and send a message. You should see DeepSeek R1 "thinking" (if supported) and replying with high-quality reasoning.
## Advanced: Using Search & Tools
DeepSeek is great, but DeepSeek with **Internet Access** is better.
In LangBot:
1. Go to **Plugins**.
2. Install **Tavily Search** or **Google Search**.
3. Add the plugin to your DeepSeek Pipeline.
Now your bot can search the web for real-time info *before* reasoning with DeepSeek R1!
***
**Ready to build?**
[Star us on GitHub](https://github.com/langbot-app/LangBot) or [Read the Docs](https://docs.langbot.app).
# Deploy Your Own AI Bot to Discord, Telegram & WeChat in 5 Minutes
Source: https://docs.langbot.app/en/articles/deploy-ai-bot-in-5-minutes
A step-by-step guide to deploying LangBot — the open-source platform that connects 20+ LLMs to 13+ messaging platforms with a single Docker command.
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/deploy-ai-bot-in-5-minutes).
Published 2026-02-17 · Author: LangBot Team
What if you could have GPT-5, Claude, DeepSeek, and Gemini all answering questions in your Discord server, Telegram group, and WeChat — at the same time?
No API wrangling. No weeks of development. Just one Docker command.
That's **LangBot** — and it just crossed **15,000 stars** on GitHub.
## The Problem Everyone Faces
You want an AI assistant in your team's chat. Maybe for customer support on Telegram. Maybe for a coding helper in Discord. Maybe for a knowledge base bot in your company's WeChat or Lark group.
But then reality hits:
* Each platform has its own bot API, webhook format, and auth flow
* You need to handle message queuing, session management, and error recovery
* Switching LLM providers means rewriting your integration layer
* Adding RAG or tool calling is yet another project
LangBot solves all of this with a single, unified platform.
## What Makes LangBot Different
### 13+ Messaging Platforms, One Codebase
Deploy a single LangBot instance and connect it to:
**Global:** Discord, Telegram, Slack, LINE, WhatsApp
**Asia:** WeChat (Official Account), WeCom, QQ, Lark, DingTalk, Feishu, KOOK
Each platform gets its own adapter — you just fill in your bot token in the WebUI and you're live.
### 20+ LLM Models, Zero Lock-in
Through **LangBot Space**, you get instant access to 20 cloud models out of the box — no API keys to manage:
* **Claude** (Opus 4.6, Sonnet 4.5, Haiku 4.5)
* **GPT** (GPT-5.2, GPT-5-mini, GPT-4.1-mini)
* **Gemini** (3 Pro, 2.5 Pro, 2.5 Flash)
* **DeepSeek** (R1, V3)
* **Grok** (4, 4.1)
* **Qwen** (3 Max)
Or add your own providers — OpenAI-compatible endpoints, Ollama for local models, any provider you want.
### Built-in Agent with Tool Calling
LangBot's Local Agent isn't just a chat wrapper — it's a full agent runtime:
* **Multi-round conversations** with configurable memory
* **Function calling / tool use** for LLM-driven actions
* **MCP (Model Context Protocol)** support for connecting to 100+ pre-built tools
* **Knowledge base (RAG)** with built-in vector search
### Plugin Marketplace
37+ community plugins and growing — install with one click:
* **WebSearch** — Let your bot search the web
* **AI Image Generator** — Generate images from text
* **LinkAnaly** — Auto-preview links in chat
* **ScheNotify** — Schedule reminders with natural language
* **Google Search**, **Tavily Search**, **RAGFlow Retriever**, and more
## Deploy in 5 Minutes — For Real
### Step 1: Run Docker Compose
```bash theme={null}
git clone https://github.com/langbot-app/LangBot
cd LangBot/docker
docker compose up -d
```
That's it. LangBot is now running at `http://localhost:5300`.
### Step 2: Initialize with LangBot Space
Open the WebUI and click **"Initialize with Space"**. This connects your instance to LangBot Space, giving you:
* 20 cloud models ready to use (with free credits)
* One-click plugin installation
* Managed API keys
### Step 3: Configure Your Pipeline
Go to **Pipelines** and edit the default `ChatPipeline`:
1. Select your model (e.g., `deepseek-v3`, `gpt-5-mini`, `claude-sonnet-4-5`)
2. Customize the system prompt
3. Optionally attach a knowledge base or enable tools
### Step 4: Connect a Platform
Go to **Bots** → click **+** → choose your platform (Discord, Telegram, etc.) → enter your bot token.
Done. Your bot is live.
### Step 5: Test It
Use the built-in **Debug Chat** to test your pipeline before going live:
## Real Conversations, Real Value
Here's what it looks like when LangBot is running in a QQ group — users asking technical questions and getting instant, accurate answers:
And in private chat:
## Architecture That Scales
LangBot is built for production:
* **Pipeline architecture** — each bot binds to a pipeline; pipelines handle AI logic, triggers, safety controls, and output formatting
* **Cross-process plugin isolation** — a bad plugin can't crash your bot
* **Multiple runner backends** — use LangBot's Local Agent, or connect to Dify, n8n, Langflow, Coze for complex workflows
* **Database flexibility** — SQLite for dev, PostgreSQL for production
* **Vector DB options** — Chroma, Qdrant, Milvus, pgvector, SeekDB
## Why 15,000+ Developers Choose LangBot
| Feature | LangBot | Building from Scratch |
| ------------- | ------------------- | --------------------- |
| Platforms | 13+ ready | Weeks per platform |
| LLM Providers | 20+ models | Manual integration |
| Agent Runtime | Built-in | Build your own |
| RAG | Native + external | Separate project |
| Plugin System | Marketplace | DIY |
| Deployment | `docker compose up` | Days of setup |
| WebUI | Included | Build your own |
## Get Started
* **GitHub:** [github.com/langbot-app/LangBot](https://github.com/langbot-app/LangBot) — give us a star!
* **Documentation:** [docs.langbot.app](https://docs.langbot.app)
* **Plugin Market:** [space.langbot.app](https://space.langbot.app)
```bash theme={null}
git clone https://github.com/langbot-app/LangBot
cd LangBot/docker
docker compose up -d
```
Your AI bot empire starts with one command.
# Finally Got My Dify Agent Working in Discord, Telegram and Slack
Source: https://docs.langbot.app/en/articles/dify-agent-discord-telegram-slack
Finally Got My Dify Agent Working in Discord, Telegram and Slack
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/dify-agent-discord-telegram-slack).
Published 2025-12-11 · Author: LangBot Team
Want your Dify Agent to break free from the browser and chat directly in WeChat, QQ, or Telegram? Combine LangBot with Dify and you can set it up in under 10 minutes.
## Why This Combo?
[**LangBot**](https://github.com/langbot-app/LangBot) is the most powerful open-source multi-platform chatbot framework available. It supports major Chinese IMs (QQ, WeChat, Feishu, DingTalk) and international platforms (Telegram, Discord, Slack, LINE). Its core strength is connecting AI backends to messaging apps.
**Dify** is one of the hottest AI app development platforms, offering visual Agent orchestration, 50+ built-in tools, and RAG knowledge bases. Together, they let you build a multi-platform AI assistant fast.
## Deploy LangBot
Three commands with uvx:
```bash theme={null}
mkdir langbot-instance
cd langbot-instance
uvx langbot@latest
```
Visit `http://127.0.0.1:5300` and register an admin account:
## Create a Dify Agent
Log in to [cloud.dify.ai](https://cloud.dify.ai) and create a new app. Choose **Agent** type - unlike basic chat apps, Agents can reason autonomously and call tools for complex tasks.
### Configure the Agent
In the orchestration interface, set up:
**System Instructions** - Define the Agent's role:
```
You are an intelligent assistant that helps users complete various tasks. You have the following capabilities:
1. Answer various user questions
2. Query weather information
3. Retrieve webpage content
Please always maintain a friendly and professional attitude. If you need to use tools to get information, proactively call the relevant tools.
```
**Add Tools** - Pick from Dify's 50+ built-in tools, like Weather and Web Scraper:
**Select Model** - Supports GPT, Claude, Gemini, DeepSeek, and other major LLMs.
Final configuration:
### Get the API Key
Click "Publish", then go to "Access API" to create a key:
Save the API key and server address `https://api.dify.ai/v1`.
## Connect Dify to LangBot
Back in LangBot, go to Pipelines > ChatPipeline > AI Capability:
Select **Dify Service API** as the Runner:
Enter the configuration:
* **Base URL**: `https://api.dify.ai/v1`
* **API Key**: The key you just created
* **App Type**: Chat (including Chatflow)
Save and click "Debug Conversation" to test:
If the Agent responds correctly and calls tools, the integration is working.
## Why This Architecture Rocks
* **Decoupled Design**: Agent capabilities live in Dify, LangBot handles message routing - each does what it's best at
* **One Config, Multi-Platform**: Same Agent serves QQ, WeChat, Telegram, and more simultaneously
* **Easy to Extend**: Add RAG knowledge bases, Workflow orchestration, or more tools in Dify later
## Next Steps
* Configure messaging platforms in LangBot (QQ bot, WeChat personal account, etc.)
* Add more tools in Dify (database queries, API calls)
* Try Dify Chatflow for complex conversation flows
If you're using n8n, FastGPT, Coze, or Langflow, LangBot supports those too.
***
**Resources**
* [LangBot Docs](https://docs.langbot.app) | [GitHub](https://github.com/langbot-app/LangBot)
* [Dify Docs](https://docs.dify.ai) | [Cloud Platform](https://cloud.dify.ai)
# LangBot 4.6.0 External Knowledge Base Tutorial: Integrating Dify with LangBot for RAG-powered Conversations
Source: https://docs.langbot.app/en/articles/dify-langbot-rag-knowledge-base
LangBot 4.6.0 External Knowledge Base Tutorial: Integrating Dify with LangBot for RAG-powered Conversations
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/dify-langbot-rag-knowledge-base).
Published 2025-12-02 · Author: LangBot Team
LangBot 4.6.0 introduces external knowledge base functionality, allowing users to integrate external knowledge retrieval services such as Dify and RAGFlow into conversation pipelines. This tutorial demonstrates how to combine Dify knowledge base with LangBot to enable intelligent conversations based on domain-specific knowledge.
## Feature Overview
The external knowledge base feature enables LangBot to:
* Connect to various external knowledge retrieval services (Dify, RAGFlow, etc.)
* Provide professional answers based on domain-specific knowledge
* Flexibly extend knowledge retrieval capabilities through the plugin system
* Configure conveniently through WebUI without manual configuration file editing
## Prerequisites
Before starting, you need:
1. Deploy LangBot 4.6.0 or higher
2. Have a Dify account (register at [https://cloud.dify.ai](https://cloud.dify.ai))
3. Configure a conversation model (this tutorial uses claude-opus-4-1-20250805)
## Step 1: Deploy LangBot
### 1.1 View LangBot Repository
First, visit the [LangBot GitHub repository](https://github.com/langbot-app/LangBot) to understand the project. LangBot is a production-grade instant messaging bot development platform supporting multiple messaging platforms and LLM services.
### 1.2 Learn About External Knowledge Base Feature
Visit the [official LangBot documentation](https://docs.langbot.app) to view the external knowledge base usage instructions. The documentation provides detailed explanations on how to build built-in knowledge bases and connect to external knowledge bases.
### 1.3 Start LangBot
Use the `uvx langbot` command to quickly start LangBot, then visit [http://127.0.0.1:5300](http://127.0.0.1:5300) for initialization. Fill in your email and password to complete registration, then log in.
### 1.4 Dashboard After Login
After successfully logging in, you will see the LangBot WebUI dashboard.
## Step 2: Configure Conversation Model
Before using the knowledge base, you need to configure a conversation model.
### 2.1 Navigate to Model Configuration Page
Click "Models" in the left navigation bar to enter the model configuration page.
### 2.2 Add New Model
Click the "+" button to open the model configuration dialog.
### 2.3 Fill in Model Information
Fill in the following information:
* **Model Name**: claude-opus-4-1-20250805
* **Model Provider**: New API
* **Request URL**: Your API endpoint
* **API Key**: Your API Key
* **Abilities**: Check Vision Ability and Function Call
Click "Submit" to save the configuration.
## Step 3: Create Knowledge Base in Dify
### 3.1 Log in to Dify Platform
Visit [https://cloud.dify.ai](https://cloud.dify.ai) and log in to your account.
### 3.2 Navigate to Knowledge Page
Click "Knowledge" in the top navigation bar to enter the knowledge base management page.
### 3.3 Create New Knowledge Base
Click "Create Knowledge" to start creating a knowledge base.
### 3.4 Select Data Source
You can choose from the following methods to import data:
* **Import from file**: Upload document files
* **Sync from Notion**: Sync from Notion
* **Sync from website**: Crawl website content
This tutorial selects "Import from file" and uploads prepared LangBot documentation.
### 3.5 Upload Document
After uploading the document, the system will display file information. Click "Next" to proceed.
### 3.6 Configure Document Processing Parameters
On the document processing page, you can configure chunk settings, index method, and retrieval settings:
* **Chunk Settings**: General mode, maximum chunk length 1024 characters
* **Index Method**: High Quality (uses embedding model)
* **Retrieval Setting**: Vector Search
Click "Save & Process" to start processing the document.
### 3.7 Knowledge Base Processing
The system starts processing the document and generating vector embeddings.
### 3.8 Embedding Completed
After processing is complete, the knowledge base is ready to use.
## Step 4: Get Dify API Information
### 4.1 Open Service API Panel
Click the "Service API" button in the bottom right. Record the following information:
* **Service API Endpoint**: [https://api.dify.ai/v1](https://api.dify.ai/v1)
* **Dataset ID**: Get from the URL
### 4.2 Get API Key
Click the "API Key" button to view the API key. Record your API Key for use in subsequent configuration.
## Step 5: Configure External Knowledge Base in LangBot
### 5.1 Navigate to Knowledge Page
In the LangBot WebUI, click "Knowledge" in the left navigation bar.
### 5.2 Switch to External Knowledge Base Tab
Click the "External" tab.
### 5.3 Add External Knowledge Base
Click the "+" button to open the add external knowledge base dialog. In this dialog, you need to:
1. **Knowledge Base Name**: Give the knowledge base a name
2. **Knowledge Base Description**: Add a description (optional)
3. **Retriever**: Select a knowledge retriever plugin
**Note**: Using an external knowledge base requires installing the corresponding knowledge retriever plugin first. You can search for and install the Dify knowledge retriever plugin in the [plugin marketplace](https://space.langbot.app/market?category=KnowledgeRetriever).
After installing the plugin, select the corresponding plugin from the Retriever dropdown, then fill in the configuration information obtained from Dify (API Endpoint, API Key, Dataset ID).
## Step 6: Configure Pipeline
After configuring the external knowledge base, you need to enable it in the pipeline:
1. Navigate to the "Pipelines" page
2. Edit or create a new pipeline
3. On the "AI Capabilities" page, select "Built-in Agent" as the runner
4. In the knowledge base selection, check the external knowledge base you just configured
5. Save the pipeline configuration
## Step 7: Test the Effect
Test in the pipeline's "Conversation Testing" page, or bind the pipeline to a bot for actual conversation testing. When users ask questions, LangBot will automatically retrieve relevant content from the Dify knowledge base and generate answers combined with the retrieved knowledge.
## Frequently Asked Questions
### 1. How to Install Knowledge Retriever Plugins?
Visit the [LangBot plugin marketplace](https://space.langbot.app/market), search for "Knowledge Retriever" or "Dify", find the corresponding plugin and click install.
### 2. Which External Knowledge Retrieval Services Are Supported?
LangBot currently supports multiple external knowledge retrieval services through the plugin system, including:
* Dify
* RAGFlow
* Other custom knowledge retrieval services
Specific supported services can be viewed in the plugin marketplace.
### 3. What's the Difference Between External and Built-in Knowledge Bases?
* **Built-in Knowledge Base**: Data is stored locally in LangBot, with LangBot handling vectorization and retrieval
* **External Knowledge Base**: Data is stored in external services (like Dify), with retrieval performed through API calls
The advantage of external knowledge bases is the ability to leverage the capabilities of professional LLMOps platforms, such as Dify's advanced document processing and multiple retrieval strategies.
### 4. Can Multiple Knowledge Bases Be Used Simultaneously?
Yes. In the pipeline configuration, you can check multiple knowledge bases (built-in or external) simultaneously, and LangBot will comprehensively utilize the content from these knowledge bases to generate answers.
## Summary
Through this tutorial, you learned how to:
1. Create a knowledge base and upload documents on the Dify platform
2. Obtain Dify API key and related configuration information
3. Configure a conversation model in LangBot
4. Add an external knowledge base in LangBot
5. Configure pipelines to use external knowledge bases
The external knowledge base feature provides LangBot with more powerful and flexible knowledge management capabilities. Combined with professional LLMOps platforms like Dify, you can build more intelligent and professional conversational bots.
## Related Resources
* [LangBot Official Website](https://langbot.app)
* [LangBot Documentation](https://docs.langbot.app)
* [LangBot GitHub](https://github.com/langbot-app/LangBot)
* [Dify Official Website](https://dify.ai)
* [LangBot Plugin Marketplace](https://space.langbot.app/market)
# LangBot Articles
Source: https://docs.langbot.app/en/articles/index
Product updates, engineering deep dives, tutorials, integrations, and announcements from the LangBot Blog.
Product updates, engineering deep dives, tutorials, integrations, and announcements from the LangBot Blog.
## Product Updates
* [The new LangBot Cloud is here — and multi-user Workspaces are now in open source](/en/articles/langbot-cloud-multi-user-workspaces) — 2026-08-25
## Engineering
* [LangBot v4.10.0: Agentic Sandbox & Skills — Letting Your Agent Work Like a Developer](/en/articles/langbot-v4100-sandbox-skills) — 2026-06-04
* [LangBot v4.9.0: Full RAG Plugin Architecture — Knowledge Without Borders](/en/articles/langbot-v490-rag-plugin-architecture) — 2026-03-10
* [Deep Dive into the LangBot Plugin System: Process Isolation, Event-Driven Hooks, and Component Architecture](/en/articles/langbot-plugin-system-deep-dive) — 2026-02-23
## Tutorials & Integrations
* [LangTARS: Open-Source AI Agent for Remote PC Control — Works with Dify, n8n & 10+ Messaging Platforms](/en/articles/langtars-remote-pc-control-dify-n8n) — 2026-03-23
* [How to Connect DeepSeek R1 to WeChat, Discord & Telegram in 5 Minutes (FREE)](/en/articles/connect-deepseek-to-wechat) — 2026-02-21
* [Deploy Your Own AI Bot to Discord, Telegram & WeChat in 5 Minutes](/en/articles/deploy-ai-bot-in-5-minutes) — 2026-02-17
* [Finally Got My Dify Agent Working in Discord, Telegram and Slack](/en/articles/dify-agent-discord-telegram-slack) — 2025-12-11
* [How I Built a Multi-Platform AI Bot with Langflow's Drag-and-Drop Workflows](/en/articles/langflow-drag-and-drop-ai-bot) — 2025-12-05
* [How I Built a Multi-Platform AI Chatbot with n8n and LangBot](/en/articles/n8n-multi-platform-ai-chatbot) — 2025-12-03
* [LangBot 4.6.0 External Knowledge Base Tutorial: Integrating Dify with LangBot for RAG-powered Conversations](/en/articles/dify-langbot-rag-knowledge-base) — 2025-12-02
## Announcements
* [Introducing the LangBot Blog](/en/articles/welcome) — 2026-02-17
# The new LangBot Cloud is here — and multi-user Workspaces are now in open source
Source: https://docs.langbot.app/en/articles/langbot-cloud-multi-user-workspaces
LangBot is an open-source AI bot platform connecting LLMs with messaging apps. The new Cloud and open-source editions now support multi-user Workspaces, invitations, and role-based access.
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/langbot-cloud-multi-user-workspaces).
Published 2026-08-25 · Author: LangBot Team
The new [LangBot Cloud](https://cloud.langbot.app) is live. This release tackles a practical question: when a team uses LangBot together, who can access which resources?
The same multi-user foundation is now available in the open-source edition. Cloud and self-hosted LangBot still have different operating models, but they share the same basic approach to members, invitations, and permissions.
## What is LangBot?
LangBot is an open-source platform for building and operating AI bots across messaging apps. A single dashboard manages bots, message pipelines, knowledge bases, models, and extensions, then connects them to Discord, Telegram, Slack, LINE, QQ, WeChat, WeCom, Lark, DingTalk, and KOOK. LangBot also supports Satori and Matrix; Matrix bridges can extend a deployment to networks such as WhatsApp, Signal, Messenger, Mattermost, Google Chat, IRC, XMPP, and Zulip.
The model layer is provider-neutral. LangBot connects to OpenAI, Anthropic Claude, DeepSeek, Google Gemini, xAI Grok, Moonshot Kimi, and Zhipu GLM, while Ollama and LM Studio cover local models. Gateway options include SiliconFlow, Aliyun Bailian, Volcengine Ark, ModelScope, and GiteeAI. Existing agent workflows can also be connected through Dify and MCP.
A team can therefore run AI assistants on several messaging platforms from one place and choose a hosted model, a local model, or an existing agent workflow for each use case.
## Cloud: start in your own Workspace
LangBot Cloud now uses LangBot Account for sign-in. New users start with a personal Workspace and can accept invitations to join team Workspaces. If an account belongs to several Workspaces, the current one can be switched from the interface.
Bots, pipelines, models, knowledge bases, and extensions belong to the active Workspace. Switching Workspaces does not mix one team's configuration or data with another's.
Cloud continues to provide the hosting, updates, and runtime environment. It is the simpler option for teams that want to configure a bot without maintaining a server first.
## Invite people instead of sharing an admin account
An Owner, or another role with member-management permission, can send a one-time invitation. The recipient accepts it with their own account and enters the Workspace with the assigned role.
That is a better long-term setup than sharing one administrator password: each person has an identity, and access can match the work they actually do.
Five fixed roles are available today:
* **Owner** — owns the Workspace and its highest-level membership controls.
* **Admin** — manages members and most Workspace settings.
* **Developer** — configures bots, pipelines, models, and extensions.
* **Operator** — handles day-to-day operations.
* **Viewer** — read-only access.
## Open source: one instance, one Workspace, multiple users
The self-hosted edition is not turning into a multi-Workspace SaaS. One LangBot instance still has one Workspace, but that Workspace can now include several users.
The account created during initialization becomes the Owner. It can invite other people later. When SMTP is configured, LangBot can deliver the invitation by email; otherwise it shows a one-time link that can be shared through a trusted channel.
Members still use the familiar LangBot dashboard. The difference is that resource access is checked against both the Workspace and the member's role, rather than treating every signed-in user as a full administrator.
## Which edition should you use?
| | LangBot Cloud | Open-source self-hosted |
| ----------------------- | -------------------------------- | --------------------------------------------- |
| Deployment | Hosted and maintained by LangBot | You operate the server and data |
| Workspaces | Own or join multiple | One per instance |
| Members and invitations | Included | Included |
| Fixed role-based access | Included | Included |
| Best for | Getting a team running quickly | Private deployment and infrastructure control |
To start with the hosted edition, visit [cloud.langbot.app](https://cloud.langbot.app). To keep the runtime and data on your own infrastructure, deploy LangBot from [GitHub](https://github.com/langbot-app/LangBot).
# Deep Dive into the LangBot Plugin System: Process Isolation, Event-Driven Hooks, and Component Architecture
Source: https://docs.langbot.app/en/articles/langbot-plugin-system-deep-dive
A source-level deep dive into LangBot 4.0's plugin architecture — process-isolated communication, event-driven hooks, a component-based extension model, and comparisons with Dify and MCP.
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/langbot-plugin-system-deep-dive).
Published 2026-02-23 · Author: LangBot Team
Most chatbot frameworks call their "plugin system" a glorified dynamic import of Python modules. LangBot 4.0 takes a harder but more principled approach — **every plugin runs in its own process**, communicating with the host through a structured JSON-RPC-style protocol.
This article dissects the system from source code, end to end.
## Overall Architecture: A Three-Layer Process Model
LangBot's plugin system consists of three cooperating process layers:
**Each layer has a distinct responsibility:**
1. **LangBot Main Process**: Runs business logic (message pipelines, platform adapters, model invocations), connects to Runtime via `PluginRuntimeConnector`.
2. **Plugin Runtime**: The orchestration layer — discovers, launches, and manages all plugin subprocesses, routes requests from the main process to the appropriate plugin.
3. **Plugin Subprocesses**: Each plugin runs in its own Python process, communicating with Runtime via stdio pipes.
### Why Three Layers Instead of Two?
The intuitive design would have the main process manage plugin processes directly. LangBot adds the Runtime layer for **deployment flexibility**:
* **Local development**: Main process spawns Runtime as a child via stdio (zero config)
* **Docker production**: Runtime runs as a separate container, connected via WebSocket
* **Windows compatibility**: Since Windows asyncio has incomplete stdio subprocess support, it automatically falls back to WebSocket
The same codebase — no config changes — adapts from development to production.
## Communication Protocol: JSON-RPC-Style Request/Response
All cross-process communication runs on a unified protocol layer. The core data structures are minimal:
```python theme={null}
# Request
class ActionRequest(pydantic.BaseModel):
seq_id: int # Sequence number for matching request/response
action: str # Action name
data: dict # Payload
# Response
class ActionResponse(pydantic.BaseModel):
seq_id: int
code: int # 0 = success
message: str
data: dict
chunk_status: str # "continue" | "end" (streaming support)
```
The `Handler` class is the system's core abstraction, acting as both RPC client and server:
```python theme={null}
class Handler:
async def call_action(self, action, data, timeout=15.0) -> dict:
"""Actively call an action provided by the peer, wait for response"""
self.seq_id_index += 1
request = ActionRequest.make_request(self.seq_id_index, action.value, data)
future = asyncio.Future()
self.resp_waiters[self.seq_id_index] = future
await self.conn.send(json.dumps(request.model_dump()))
response = await asyncio.wait_for(future, timeout)
return response.data
@action(SomeAction.DO_SOMETHING)
async def handle_something(data: dict) -> ActionResponse:
"""Register an action for the peer to call"""
return ActionResponse.success({"result": "ok"})
```
**Key design points:**
* `seq_id`-based request/response matching enables full-duplex concurrent calls
* Streaming responses via `chunk_status` for long-running operations like command execution
* Large messages auto-chunk (stdio: 16KB / WebSocket: 64KB per chunk)
* File transfer uses a separate base64 chunking mechanism
### Action Enums: Clear API Contracts
The system defines all cross-process calls through four enum groups:
```python theme={null}
# Plugin → Runtime (plugin-initiated requests)
class PluginToRuntimeAction:
REGISTER_PLUGIN = "register_plugin"
SEND_MESSAGE = "send_message" # Send message to a platform
INVOKE_LLM = "invoke_llm" # Call an LLM
SET_PLUGIN_STORAGE = "set_plugin_storage" # Persistent storage
# ...
# Runtime → Plugin (runtime-dispatched commands)
class RuntimeToPluginAction:
INITIALIZE_PLUGIN = "initialize_plugin"
EMIT_EVENT = "emit_event"
CALL_TOOL = "call_tool"
EXECUTE_COMMAND = "execute_command"
SHUTDOWN = "shutdown"
# ...
# LangBot Main → Runtime
class LangBotToRuntimeAction:
INSTALL_PLUGIN = "install_plugin"
EMIT_EVENT = "emit_event"
LIST_TOOLS = "list_tools"
# ...
# Runtime → LangBot Main
class RuntimeToLangBotAction:
GET_PLUGIN_SETTINGS = "get_plugin_settings"
SET_BINARY_STORAGE = "set_binary_storage"
# ...
```
This makes API boundaries crystal clear — what a plugin can and cannot do is defined entirely by these enums.
## Plugin Lifecycle
A plugin goes through these stages from installation to execution:
### 1. Discovery
On startup, Runtime scans the `data/plugins/` directory:
```python theme={null}
async def launch_all_plugins(self):
for plugin_path in glob.glob("data/plugins/*"):
if not os.path.isdir(plugin_path):
continue
task = self.launch_plugin(plugin_path)
self.plugin_run_tasks.append(task)
```
Directory names follow the `{author}__{name}` convention, each containing a `manifest.yaml` and plugin code.
### 2. Launch
Runtime spawns an independent subprocess for each plugin:
```python theme={null}
async def launch_plugin(self, plugin_path: str):
python_path = sys.executable
args = ["-m", "langbot_plugin.cli.__init__", "run", "-s", "--prod"]
ctrl = StdioClientController(
command=python_path,
args=args,
working_dir=plugin_path, # Each plugin runs in its own directory
)
await ctrl.run(new_plugin_connection_callback)
```
**Key detail**: The subprocess working directory is set to the plugin's own directory — natural filesystem isolation.
### 3. Registration
After starting, the plugin process actively registers itself with Runtime:
```python theme={null}
# Runtime-side registration handler
async def register_plugin(self, handler, container_data, debug_plugin=False):
plugin_container = PluginContainer.from_dict(container_data)
# Fetch plugin settings from the main process
plugin_settings = await self.context.control_handler.call_action(
RuntimeToLangBotAction.GET_PLUGIN_SETTINGS, {...}
)
# Initialize the plugin (send config)
await handler.initialize_plugin(plugin_settings)
# Store the plugin container
self.plugins.append(plugin_container)
```
### 4. Running
Once in `INITIALIZED` state, the plugin can receive events, tool calls, and command executions.
### 5. Shutdown
```python theme={null}
async def shutdown_plugin(self, plugin_container):
# 1. Notify the plugin to shut down gracefully
await plugin_container._runtime_plugin_handler.shutdown_plugin()
# 2. Close the communication connection
await plugin_container._runtime_plugin_handler.conn.close()
# 3. Kill the subprocess
if handler.stdio_process is not None:
handler.stdio_process.kill()
await asyncio.wait_for(handler.stdio_process.wait(), timeout=2)
```
## Component System: Four Extension Types
A LangBot plugin isn't a single hook function — it's a **component container**. A single plugin can provide multiple component types simultaneously:
### EventListener
The most fundamental extension — listen for events in the message pipeline:
```python theme={null}
from langbot_plugin.api.definition.components.common.event_listener import EventListener
from langbot_plugin.api.entities.events import PersonNormalMessageReceived
from langbot_plugin.api.entities.context import EventContext
class MyListener(EventListener):
@EventListener.handler(PersonNormalMessageReceived)
async def on_person_message(self, ctx: EventContext):
event = ctx.event
# Modify the user message before it reaches the LLM
event.user_message_alter = "Answer in poetry: " + event.text_message
# Or block further processing
# ctx.prevent_default()
# ctx.prevent_postorder()
```
Supported events cover the full message lifecycle:
| Event | Trigger |
| ----------------------------- | ---------------------------------- |
| `PersonMessageReceived` | Any private message received |
| `GroupMessageReceived` | Any group message received |
| `PersonNormalMessageReceived` | Private message deemed processable |
| `GroupNormalMessageReceived` | Group message deemed processable |
| `NormalMessageResponded` | LLM response completed |
| `PromptPreProcessing` | Prompt preprocessing stage |
Event propagation supports two interruption modes:
* `prevent_default()`: Skip default behavior (e.g., skip the LLM call)
* `prevent_postorder()`: Stop subsequent plugins from running
### Tool
Tools for LLM Function Calling:
```python theme={null}
from langbot_plugin.api.definition.components.tool.tool import Tool
class WeatherTool(Tool):
async def call(self, params: dict, session, query_id: int) -> str:
city = params.get("city", "Beijing")
# Call weather API...
return f"{city}: Sunny, 25°C"
```
Tool metadata (name, description, parameter schema) is defined in a companion YAML manifest file. LangBot automatically converts this into the Function definition that LLMs understand.
### Command
User-triggered commands via `!command`, with subcommand support:
```python theme={null}
from langbot_plugin.api.definition.components.command.command import Command
class MyCommand(Command):
def __init__(self):
super().__init__()
@self.subcommand("hello", help="Say hello")
async def hello(self, ctx):
yield CommandReturn(text="Hello from plugin!")
@self.subcommand("status", help="Show status")
async def status(self, ctx):
yield CommandReturn(text="All systems operational.")
```
Command results are returned via `AsyncGenerator`, providing natural streaming output.
### KnowledgeRetriever
A multi-instance component for connecting external knowledge bases:
```python theme={null}
from langbot_plugin.api.definition.components.knowledge_retriever.retriever import KnowledgeRetriever
class MyRetriever(KnowledgeRetriever):
async def retrieve(self, context) -> list:
results = await self.search_external_db(context.query)
return [RetrievalResultEntry(content=r) for r in results]
```
KnowledgeRetriever is a **polymorphic component** — a single retriever class can spawn multiple instances, each with independent configuration. This allows users to connect multiple different external knowledge bases.
## SDK API: What Plugins Can Do
Plugins gain rich capabilities through the `LangBotAPIProxy` inherited by `BasePlugin`:
```python theme={null}
class LangBotAPIProxy:
# Message operations
async def send_message(self, bot_uuid, target_type, target_id, message_chain)
# Model invocation
async def get_llm_models(self) -> list[str]
async def invoke_llm(self, model_uuid, messages, funcs=[], extra_args={})
# Persistent storage (plugin-level isolation)
async def set_plugin_storage(self, key, value: bytes)
async def get_plugin_storage(self, key) -> bytes
# Workspace storage (cross-plugin shared)
async def set_workspace_storage(self, key, value: bytes)
async def get_workspace_storage(self, key) -> bytes
# System info
async def get_langbot_version(self) -> str
async def get_bots(self) -> list[str]
async def list_plugins_manifest(self) -> list
```
**The storage API design is worth noting**: Two levels of KV storage — `plugin_storage` (plugin-private) and `workspace_storage` (globally shared), storing data as bytes (base64-serialized in transit). Simple but flexible enough.
## Event Dispatch Mechanism
The complete path from main process to plugin:
Key source code:
```python theme={null}
async def emit_event(self, event_context, include_plugins=None):
for plugin in self.plugins:
if plugin.status != RuntimeContainerStatus.INITIALIZED:
continue
if not plugin.enabled:
continue
# Pipeline-level plugin filtering
if include_plugins is not None:
plugin_id = f"{plugin.manifest.metadata.author}/{plugin.manifest.metadata.name}"
if plugin_id not in include_plugins:
continue
resp = await plugin._runtime_plugin_handler.emit_event(
event_context.model_dump()
)
event_context = EventContext.model_validate(resp["event_context"])
# Plugin requested propagation stop
if event_context.is_prevented_postorder():
break
return emitted_plugins, event_context
```
The `include_plugins` parameter enables **pipeline-level plugin binding** — different message processing pipelines can use different subsets of plugins.
## Installation & Distribution
Plugins support three installation sources:
1. **Local upload**: `.lbpkg` files (actually zip archives containing manifest.yaml and code)
2. **Marketplace**: Install from LangBot Space online
3. **GitHub Release**: Download from a GitHub repository's Release assets
The installation flow:
```python theme={null}
async def install_plugin(self, source, install_info):
yield {"current_action": "downloading plugin package"}
# 1. Fetch and extract the plugin package (unzip)
plugin_path, author, name, version = await self.install_plugin_from_file(plugin_file)
yield {"current_action": "installing dependencies"}
# 2. Install dependencies (pip install -r requirements.txt)
pkgmgr_helper.install_requirements(requirements_file)
yield {"current_action": "initializing plugin settings"}
# 3. Initialize configuration
await self.context.control_handler.call_action(
RuntimeToLangBotAction.INITIALIZE_PLUGIN_SETTINGS, {...}
)
yield {"current_action": "launching plugin"}
# 4. Launch the plugin process
task = self.launch_plugin(plugin_path)
```
The entire process reports progress via `AsyncGenerator`, enabling real-time installation status in the frontend.
## Developer Experience
The SDK provides a complete developer toolchain:
```bash theme={null}
# Initialize a new plugin
lbp init
# Add a component
lbp component add
# Run locally for debugging
lbp run
# Package for publication
lbp publish
```
**Debug mode** has a particularly clever design: the developer's plugin connects to the running Runtime via WebSocket (instead of stdio), meaning you can hot-reload plugin code without restarting LangBot. Debug plugins are specially marked in the UI and protected from accidental deletion.
## Comparisons with Other Systems
### vs Dify Plugins
Dify's plugin system (`dify-plugin-daemon`) shares the process isolation philosophy with LangBot, but the focus differs:
* **Dify**: Plugins extend workflow node types (Tool, Model, Extension) — designed for AI application orchestration
* **LangBot**: Plugins extend the message processing pipeline (Event, Tool, Command, KnowledgeRetriever) — designed for instant messaging scenarios
LangBot's `EventListener` component provides a capability Dify lacks — injecting logic at any stage of message processing.
### vs MCP (Model Context Protocol)
MCP is a standardized protocol for AI tool invocation. LangBot's Tool component and MCP services overlap functionally, but serve different purposes:
* **MCP**: A universal "AI calls external capabilities" protocol, usable by any LLM application
* **LangBot Tool**: Deeply integrated with message processing context, with access to session info, user identity, etc.
In practice, LangBot natively supports MCP — users can configure MCP servers directly in LangBot without writing plugins. LangBot's Tool component is for scenarios requiring access to LangBot's internal context.
## Design Decisions Explained
**Why process isolation instead of threads/coroutines?**
* Plugin code quality is unpredictable; a segfault shouldn't crash the entire service
* Dependency isolation: different plugins may depend on different versions of the same library
* Resource control: you can set per-plugin process resource limits
**Why JSON instead of Protobuf/MessagePack?**
* Debug-friendly: developers can directly read communication logs
* Natively supported in Python, no extra dependencies
* The performance bottleneck isn't serialization (plugin call frequency is far below database queries)
**Why stdio over WebSocket by default?**
* stdio requires no network stack — lower latency
* Simpler process lifecycle management (child processes auto-cleanup when parent exits)
* WebSocket is only used where stdio isn't supported (Docker, Windows)
## Conclusion
LangBot's plugin system is a **production-grade, process-isolated, event-driven component framework for extensibility**.
Its core design principles:
1. **Safety first**: Process isolation ensures plugins can't destabilize the main service
2. **Deployment flexibility**: Dual stdio/WebSocket modes adapt to all environments
3. **Developer-friendly**: Complete SDK, CLI, and debug support
4. **Component-based**: Four component types cover the major extension needs
If you're interested in developing LangBot plugins, start with the [plugin development docs](https://docs.langbot.app/en/plugin/dev/tutor), or browse existing plugins on the [marketplace](https://space.langbot.app/market) for inspiration.
# LangBot v4.10.0: Agentic Sandbox & Skills — Letting Your Agent Work Like a Developer
Source: https://docs.langbot.app/en/articles/langbot-v4100-sandbox-skills
LangBot v4.10.0 brings a real sandbox (Box Runtime) and an on-demand Skills system to the built-in Agent. It can now run commands and edit files in an isolated environment, and pull in specialized know-how exactly when a task needs it.
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/langbot-v4100-sandbox-skills).
Published 2026-06-04 · Author: LangBot Team
LangBot v4.10.0 is our biggest Agent update yet. It ships two features that point in the same direction — **letting the built-in Agent work like a real developer**:
1. **Sandbox**: the Agent now runs in a genuinely isolated environment where it can execute commands and work with files.
2. **Skills**: the Agent can load specialized know-how on demand, instead of cramming everything into its context.
Both capabilities were led by community contributor [@huanghuoguoguo](https://github.com/huanghuoguoguo). Let's dig in.
> **New to LangBot?** [LangBot](https://github.com/langbot-app/LangBot) is an open-source LLM bot platform (15k+ GitHub stars) that lets you deploy AI chatbots across **Discord, Telegram, WeChat (个人微信 / 企业微信), Slack, Lark (Feishu), DingTalk, QQ** and more — all from a single instance. It connects 20+ large language models (OpenAI GPT, Anthropic Claude, Google Gemini, DeepSeek, and others) to 13+ messaging platforms, with built-in Agent, RAG knowledge bases, MCP support, and a plugin ecosystem. You can self-host it with one Docker command or run it managed on [LangBot Cloud](https://space.langbot.app/cloud).
## 📦 Sandbox: a real workspace for your Agent
Until now, LangBot's built-in Agent could only call predefined tools. It couldn't "open a terminal and run a command," nor "write an intermediate result to a file and read it back later" — which is exactly what human developers do all day.
v4.10.0 introduces the new **Box Runtime**. It gives the Agent an isolated sandbox environment with six native tools built in:
| Tool | Purpose |
| ------- | ---------------------------- |
| `exec` | Run shell commands |
| `read` | Read file contents |
| `write` | Write files |
| `edit` | Make targeted edits to files |
| `glob` | Find files by pattern |
| `grep` | Search inside file contents |
With this toolkit, the Agent can work like a developer: run a script, save the output to a file, read it back to analyze, and post-process the result. All of it happens inside an isolated environment that never touches the host.
### Pluggable backends
The sandbox's execution backend is **pluggable**, and LangBot selects one automatically based on your deployment:
* **Docker**: spins up a dedicated `langbot_box` container that talks to the main process over WebSocket. Great for containerized deployments.
* **nsjail**: lightweight isolation based on Linux namespaces, no extra container needed. Great for bare-metal or single-container setups.
* **E2B**: cloud sandbox that runs execution in a remote isolated environment.
You don't have to pick manually — `box.enabled` is the master switch. Once on, LangBot probes for the most suitable backend and degrades gracefully if probing fails.
### It also hosts MCP Servers
Beyond native tools, the Box Runtime takes on another important job: **hosting stdio-mode MCP Servers**.
Previously, stdio-mode MCP Servers had to be spawned as subprocesses directly inside the main process — neither safe nor easy to manage. Now they all run inside the sandbox, sharing the same isolation boundary as the Agent's execution environment. `http` / `sse` mode MCP is unaffected and works as before.
## 🎯 Skills: know-how, loaded on demand
The second big update is **Skills**.
As the Agent's capabilities grow, an old problem surfaces: **the context window is finite**. If you stuff every possible instruction, procedure, and reference doc into the system prompt, the context fills up fast — and most of it is irrelevant to the task at hand.
The design idea behind Skills is simple: **keep the table of contents in view, and open the book only when you need it.**
A Skill is an **on-demand instruction pack** containing:
* **A prompt**: tells the Agent what the skill does and when to use it
* **A procedure**: the steps to accomplish a class of tasks
* **Scripts**: ready-to-run helper scripts
* **Reference files**: API docs, templates, examples, and so on
By default the Agent only sees a **list of available skills** (name + summary), which costs almost nothing in context. When it decides a task needs a particular skill, it pulls the full content just in time via the `activate` action.
The benefits are clear:
1. **Lean context**: irrelevant skill content never takes up the window.
2. **Extensible capabilities**: want to teach the Agent a new trick? Write a skill pack — no core code changes.
3. **Instant effect**: skills load dynamically during execution, no restart required.
If you've used Claude's Skills or a similar mechanism, this pattern will feel familiar — it decouples "what the Agent knows" from "what the Agent needs right now."
## 🧩 Shipped early across 4.9.x
These were planned for v4.10.0 but shipped early in the 4.9.x line:
* **Plugin Extension Pages**: plugins can render custom UI via iframe + a Page SDK ([@TyperBody](https://github.com/TyperBody), #2107)
* **Monitoring**: feedback linked to message IDs, plus feedback export ([@6mvp6](https://github.com/6mvp6), #2120)
* **Security hardening**: path-traversal fixes and removal of an authenticated RCE endpoint ([@sebastiondev](https://github.com/sebastiondev), #2087 / #2178)
## 🚀 Getting started
The sandbox needs an execution backend, depending on how you deploy:
* **Docker**: run `docker compose --profile all up`, which also launches the `langbot_box` container.
* **Manual / `uvx`**: runs as a stdio subprocess automatically, nothing to do.
* **LangBot Cloud**: ready out of the box — the sandbox is already set up for you, nothing to install.
To turn the sandbox off, set `box.enabled: false` — built-in tools, Skill create/edit, and stdio-mode MCP are all disabled; `http` / `sse` mode MCP is unaffected. See the [Sandbox docs](https://docs.langbot.app/en/usage/sandbox/readme) for details.
This release also upgrades the plugin SDK to **`langbot-plugin 0.4.1`**.
## Closing thoughts
Sandbox and Skills are, at their core, answering the same question: **how do we let an Agent handle genuinely complex, open-ended tasks?**
The sandbox solves the "**hands**" problem — giving the Agent the ability to execute and room to experiment. Skills solve the "**brain**" problem — letting the Agent reach for the right knowledge at the right moment, without drowning in irrelevant information.
The fastest way to try it is to spin up a managed instance on [LangBot Cloud](https://space.langbot.app/cloud), where the sandbox is ready to go. You can also read the [full release notes](https://github.com/langbot-app/LangBot/releases/tag/v4.10.0) for every change.
Thank you to everyone building with LangBot. We'll keep making the cloud Agent experience more stable and more capable.
# LangBot v4.9.0: Full RAG Plugin Architecture — Knowledge Without Borders
Source: https://docs.langbot.app/en/articles/langbot-v490-rag-plugin-architecture
LangBot v4.9.0 refactors the entire knowledge base system from a built-in implementation to a plugin-driven architecture, introducing KnowledgeEngine and Parser component types for boundless extensibility.
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/langbot-v490-rag-plugin-architecture).
Published 2026-03-10 · Author: LangBot Team
LangBot v4.9.0, codenamed **"Knowledge Without Borders,"** does exactly what the name says: **the entire knowledge base capability has been refactored from a built-in implementation to a plugin-driven architecture.**
This isn't a minor tweak — it redefines what "knowledge" means in LangBot.
## The Problem with the Old Approach
Before v4.9.0, LangBot's knowledge base was split into two separate systems:
* **Built-in Knowledge Base**: Used Chroma as the vector database, with embedding models managed by LangBot directly. Document parsing, chunking, and indexing were all hardcoded.
* **External Knowledge Base**: Bridged services like Dify, RAGFlow, and FastGPT through the `KnowledgeRetriever` plugin component — retrieval only, no document ingestion.
These two systems lived behind separate UI tabs, with completely different data models and management flows.
**Why this was painful:**
1. **Poor extensibility**: Want a different vector database? A custom chunking strategy? Sorry, that's hardcoded.
2. **High maintenance cost**: Every RAG improvement required changes to LangBot's core code and a new release.
3. **Fragmented UX**: Two completely different knowledge base management flows meant a steep learning curve.
v4.9.0 solves this decisively: **extract RAG capabilities from LangBot's core and hand them to plugins.**
## What Changed
### 1. Unified Knowledge Base Model
The `internal` / `external` distinction is gone. All knowledge bases are managed through a single interface, differentiated only by their `rag_engine_plugin_id`. One list, one creation flow — just pick your engine.
### 2. KnowledgeEngine Component
This is the headline addition. `KnowledgeEngine` replaces the old `KnowledgeRetriever` and takes ownership of the full knowledge base lifecycle:
* **Document Ingestion**: The complete pipeline from file parsing to vector indexing
* **Knowledge Retrieval**: Returning relevant chunks at query time
* **Document Deletion**: Cleaning up documents and their associated vector data
* **Lifecycle Hooks**: Callbacks when knowledge bases are created or deleted
A KnowledgeEngine plugin has full control over indexing and retrieval strategies — not just retrieval.
### 3. Parser Component
Document parsing has been extracted into its own plugin component type. A Parser converts binary files (PDF, Word, Markdown, etc.) into structured text, which is then handed to the RAG engine for chunking and indexing.
**The data flow:**
If a RAG engine declares `DOC_PARSING` capability, it can handle parsing internally and skip the external Parser.
### 4. Host RAG API
LangBot's core no longer executes RAG operations directly, but it still provides essential infrastructure through `RAGRuntimeService`, accessible to plugins via RPC:
* **Embedding invocation**: `invoke_embedding()` — plugins don't need to manage model connections
* **Vector database operations**: `vector_upsert()` / `vector_search()` / `vector_delete()`
* **File access**: `get_knowledge_file_stream()` — read raw files from storage
This means plugins can focus on RAG strategy (chunking algorithms, retrieval logic, re-ranking) while the host handles the "heavy" operations like vector storage and embedding models.
### 5. KnowledgeRetriever Deprecated
The old `KnowledgeRetriever` component has been removed. If you had external knowledge base plugins, they'll need to migrate to `KnowledgeEngine`. The good news: the new API is cleaner and migration is straightforward.
## Building a RAG Engine Plugin
### Scaffold the Component
```bash theme={null}
lbp comp KnowledgeEngine
```
This generates the directory structure:
### Define Configuration Schemas
The YAML manifest defines two configuration schemas:
* **`creation_schema`**: Parameters filled when creating a knowledge base (e.g., chunk size, embedding model)
* **`retrieval_schema`**: Parameters adjustable at retrieval time (e.g., score threshold, top-K)
```yaml theme={null}
spec:
creation_schema:
- name: chunk_size
label:
en_US: Chunk Size
type: integer
default: 500
- name: chunk_overlap
label:
en_US: Chunk Overlap
type: integer
default: 50
retrieval_schema:
- name: score_threshold
label:
en_US: Score Threshold
type: float
default: 0.5
```
LangBot **dynamically renders** creation and retrieval forms based on these schemas — different engines show different configuration fields.
### Declare Capabilities
```python theme={null}
class SimpleRag(KnowledgeEngine):
@classmethod
def get_capabilities(cls) -> list[str]:
return [
KnowledgeEngineCapability.DOC_INGESTION, # Supports document upload
# KnowledgeEngineCapability.DOC_PARSING, # Optional: built-in parsing
]
```
| Capability | Description |
| --------------- | --------------------------------------------------------------------------------------- |
| `DOC_INGESTION` | Supports document upload and processing. UI shows "Documents" tab |
| `DOC_PARSING` | Supports built-in document parsing. Without this, an external Parser plugin is required |
### Implement Core Methods
The three essential methods:
**Document Ingestion:**
```python theme={null}
async def ingest(self, context: IngestionContext) -> IngestionResult:
# 1. Get file content (or use Parser's pre-parsed result)
if context.parsed_content:
text = context.parsed_content.text
else:
file_bytes = await self.plugin.get_knowledge_file_stream(
context.file_object.storage_path
)
text = file_bytes.decode('utf-8')
# 2. Chunk the text
chunks = self._split_text(text, chunk_size=500)
# 3. Call host embedding model
vectors = await self.plugin.invoke_embedding(
embedding_model_uuid, chunks
)
# 4. Write to host vector database
await self.plugin.vector_upsert(
collection_id, vectors, ids, metadata
)
return IngestionResult(
document_id=context.file_object.metadata.document_id,
status=DocumentStatus.COMPLETED,
chunks_created=len(chunks),
)
```
**Knowledge Retrieval:**
```python theme={null}
async def retrieve(self, context: RetrievalContext) -> RetrievalResponse:
# 1. Generate query vector
query_vectors = await self.plugin.invoke_embedding(
embedding_model_uuid, [context.query]
)
# 2. Vector search
results = await self.plugin.vector_search(
collection_id, query_vectors[0], top_k=5
)
# 3. Convert and return
return RetrievalResponse(results=entries, total_found=len(entries))
```
**Document Deletion:**
```python theme={null}
async def delete_document(self, kb_id: str, document_id: str) -> bool:
deleted = await self.plugin.vector_delete(
collection_id=kb_id, file_ids=[document_id]
)
return deleted > 0
```
### Bridging External Services
If your goal is to bridge Dify, RAGFlow, FastGPT, or other external services rather than building a custom RAG pipeline, the implementation is even simpler — **don't declare `DOC_INGESTION` capability** and only implement `retrieve`:
```python theme={null}
class DifyRAGEngine(KnowledgeEngine):
@classmethod
def get_capabilities(cls) -> list[str]:
return [] # No document upload — managed externally
async def retrieve(self, context: RetrievalContext) -> RetrievalResponse:
# Call Dify/RAGFlow/FastGPT retrieval API
...
```
The knowledge base won't show a "Documents" tab — all content management happens in the external service.
## Building a Parser Plugin
Parser development is even more concise:
```bash theme={null}
lbp comp Parser
```
Declare supported MIME types in the manifest:
```yaml theme={null}
spec:
supported_mime_types:
- application/pdf
- application/vnd.openxmlformats-officedocument.wordprocessingml.document
```
Implement the `parse` method:
```python theme={null}
class PdfParser(Parser):
async def parse(self, context: ParseContext) -> ParseResult:
# context.file_content: raw file bytes
# context.mime_type: detected MIME type
# context.filename: original filename
text = extract_text_from_pdf(context.file_content)
return ParseResult(
text=text,
sections=[
TextSection(content=page_text, heading=f"Page {i}", page=i)
for i, page_text in enumerate(pages)
],
)
```
Parsers also support **cross-plugin invocation** — a RAG engine plugin can call another plugin's Parser:
```python theme={null}
result = await self.plugin.invoke_parser(
plugin_author="author_name",
plugin_name="plugin_name",
storage_path=context.file_object.storage_path,
mime_type=context.file_object.metadata.mime_type,
filename=context.file_object.metadata.filename,
)
```
## Upgrade Notes
* Knowledge bases created in previous versions are **automatically migrated**. After updating, visit the Knowledge Base page to verify.
* The `KnowledgeRetriever` component is deprecated. Existing plugins need to migrate to `KnowledgeEngine`.
* Browse the [Plugin Marketplace](https://space.langbot.app/market?category=KnowledgeEngine) for available RAG engine plugins.
## The Bigger Picture
v4.9.0's knowledge base refactoring is the latest step in LangBot's plugin-first evolution. From event handlers and tools in v4.0, to knowledge retrievers, to now full RAG engines and parsers — LangBot's core capabilities are progressively moving from "built-in" to "pluggable."
The endgame: **LangBot's core provides pipeline orchestration and infrastructure; all business capabilities are plugin-driven.**
Custom chunking strategy? Write a KnowledgeEngine plugin. PDF parsing? Write a Parser plugin. Bridge your company's internal knowledge service? Also a plugin.
Knowledge, without borders.
***
**Links:**
* [v4.9.0 Release Notes](https://github.com/langbot-app/LangBot/releases/tag/v4.9.0)
* [KnowledgeEngine Development Guide](https://docs.langbot.app/en/plugin/dev/components/knowledge-engine)
* [Parser Development Guide](https://docs.langbot.app/en/plugin/dev/components/parser)
* [Plugin Marketplace](https://space.langbot.app/market)
* [LangBot Cloud](https://space.langbot.app/cloud)
# How I Built a Multi-Platform AI Bot with Langflow's Drag-and-Drop Workflows
Source: https://docs.langbot.app/en/articles/langflow-drag-and-drop-ai-bot
How I Built a Multi-Platform AI Bot with Langflow's Drag-and-Drop Workflows
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/langflow-drag-and-drop-ai-bot).
Published 2025-12-05 · Author: LangBot Team
Drive chatbots across QQ, WeChat, Telegram, Discord, and more using visual workflows - no coding required.
***
[LangBot](https://github.com/langbot-app/LangBot) is an open-source instant messaging bot platform that connects AI workflow engines like Langflow, n8n, Dify, FastGPT, and Coze to platforms including WeChat, QQ, Feishu, DingTalk, Telegram, Discord, Slack, and LINE. This tutorial demonstrates how to use Langflow's visual workflows as LangBot's conversation engine.
## Why This Approach Works
* **True Multi-Platform**: One workflow powering 8+ messaging platforms simultaneously
* **Visual Orchestration**: Drag-and-drop conversation design with conditional branches, multi-turn dialogs, and external API calls
* **Flexible AI Models**: Support for OpenAI, Claude, Gemini, DeepSeek, and local models
* **Fully Open Source**: Both LangBot and Langflow are open-source projects for free deployment and customization
## Prerequisites
* Python 3.10+
* Docker (recommended for quick deployment)
* OpenAI API Key or API keys for other LLM services
## Step 1: Deploy LangBot
Launch with uvx in one command:
```bash theme={null}
uvx langbot
```
First run auto-initializes and opens your browser to [http://127.0.0.1:5300](http://127.0.0.1:5300).
After registration, log in to access the dashboard:
## Step 2: Deploy Langflow
Deploy quickly with Docker:
```bash theme={null}
docker run -d --name langflow -p 7860:7860 langflowai/langflow:latest
```
Visit [http://localhost:7860](http://localhost:7860) to access Langflow:
## Step 3: Create a Langflow Workflow
In Langflow, select the "Basic Prompting" template to get started quickly:
This template includes four basic components:
* **Chat Input**: Receives user messages
* **Prompt**: Sets system instructions
* **Language Model**: Calls LLM to generate responses
* **Chat Output**: Returns results
### Configure Language Model
Click the Language Model component and configure:
1. **Model Provider**: Select OpenAI (or other compatible providers like SiliconFlow, New API)
2. **Model Name**: Select gpt-4o-mini or deepseek-chat
3. **OpenAI API Key**: Enter your API Key
> Tip: You can use OpenAI-compatible API services like SiliconFlow or New API by simply modifying the Base URL.
Save the workflow after configuration.
## Step 4: Get Langflow API Information
### Generate API Key
In Langflow's upper right: Settings → API Keys, navigate to the API Keys page:
Click Create New Key:
Generate and save the API Key:
Format: `sk-xxxxxxxxxxxxxxxxxxxxxxxx`
### Get Flow ID
Extract from the flow editor's URL:
```
http://localhost:7860/flow/{flow-id}
```
Record this `flow-id`.
## Step 5: Configure Langflow in LangBot
Return to LangBot dashboard and go to **Pipelines** page.
Click ChatPipeline to edit, in the AI tab:
Configure **Runner**, select **Langflow API**:
Fill in the Langflow configuration:
Configuration items:
* **Base URL**: `http://localhost:7860` (local) or `http://langflow:7860` (Docker network)
* **API Key**: The API Key generated in Langflow
* **Flow ID**: The Flow ID recorded earlier
> Docker Tip: If both services run in containers, ensure they're on the same network and use the container name for Base URL.
Click **Save** to save the configuration.
## Step 6: Test the Conversation
Click **Debug Chat** on the Pipelines page to open the debug chat interface:
Enter a test message like "Hello" and view the AI response:
## How It Works
1. User sends a message on a messaging platform
2. LangBot receives and passes it to the Pipeline
3. Pipeline calls Langflow API
4. Langflow executes the workflow: receives input → adds prompt → calls LLM → returns result
5. LangBot sends the response back to the user
## Common Issues
**Cannot connect to Langflow?**
Check Base URL. For Docker deployment, ensure containers are on the same network:
```bash theme={null}
docker network create langbot_network
docker network connect langbot_network langflow
docker network connect langbot_network langbot
```
Use container name: `http://langflow:7860`
**API call fails?**
* Confirm API Key and Flow ID are correct
* Verify the Language Model in Langflow has a valid LLM API Key configured
## Advanced Use Cases
Langflow's power lies in visually orchestrating complex AI workflows:
* **Multi-Turn Memory**: Add Memory components for contextual understanding
* **Conditional Branches**: Execute different logic based on user input
* **External API Integration**: Connect databases, search engines, third-party services
* **Multi-Agent Collaboration**: Multiple LLM models working together
* **RAG Applications**: Integrate vector databases for knowledge base Q\&A
All achievable through drag-and-drop without writing code.
## Summary
With LangBot + Langflow, you can rapidly build powerful multi-platform AI chatbots. Langflow provides visual workflow orchestration, LangBot handles messaging platform integration - together they create a complete loop from workflow design to multi-platform deployment.
This approach is ideal for:
* Scenarios requiring the same AI capabilities across multiple platforms
* Teams wanting rapid iteration and testing of different conversation flows
* Developers wanting to build complex AI applications without deep coding
## Related Resources
* [LangBot Official Site](https://langbot.app) | [Documentation](https://docs.langbot.app) | [GitHub](https://github.com/langbot-app/LangBot)
* [Langflow Official Site](https://www.langflow.org) | [Documentation](https://docs.langflow.org) | [GitHub](https://github.com/langflow-ai/langflow)
***
*This article is based on the latest version of LangBot. LangBot supports integration with Dify, n8n, FastGPT, Coze, and other AI platforms - choose the workflow engine that best fits your needs.*
# LangTARS: Open-Source AI Agent for Remote PC Control — Works with Dify, n8n & 10+ Messaging Platforms
Source: https://docs.langbot.app/en/articles/langtars-remote-pc-control-dify-n8n
LangTARS is a LangBot native plugin that lets you remotely control Mac/Windows/Linux from Telegram, Discord & WeChat — with Dify and n8n workflow support.
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/langtars-remote-pc-control-dify-n8n).
Published 2026-03-23 · Author: LangBot Team
**LangBot Native Plugin | OpenClaw-like Experience | Ready to Use with One-Click Deployment**
***
## ✨ What is LangTARS?
**LangTARS** is a powerful **OpenClaw alternative** and native plugin for LangBot, inspired by the loyal and reliable robot TARS from the movie *Interstellar*.
As a versatile **AI agent** for **remote computer control**, LangTARS allows you to remotely command your **Mac, Windows, or Linux** machine directly through your favorite messaging apps like **Telegram, Discord, DingTalk, and WeChat**! If you've been looking for an **OpenClaw-like** computer use agent that integrates seamlessly into your existing **chatbot framework**, LangTARS is the perfect solution.
> 💡 **Deploy LangBot, and you can experience LangTARS instantly!** No extra standalone agent installation needed—just get it with one click from the plugin marketplace.
***
## 🤖 The LangBot Ecosystem
LangTARS is just ONE plugin in the massive LangBot ecosystem. **LangBot** is a leading **open source chatbot** framework designed for connecting LLMs to messaging platforms.
* **Multi-platform bot integration**: Native support for Telegram, Discord, WeChat, DingTalk, Lark, LINE, Slack, and more.
* **Multi-model LLM chatbot**: Works flawlessly with OpenAI, Claude, Gemini, DeepSeek, and local models.
* **Rich Plugin Marketplace**: Access over 30+ plugins for endless functionality.
* **Advanced Features**: Pipeline-based message processing, RAG support, and multi-agent workflows.
* **Self-hosted AI**: Total control over your data and deployments.
* **AI Workflow Integrations**: Connect LangBot to platforms like **Dify**, **n8n**, **Langflow**, and **Coze**. LangBot can act as the messaging frontend for your complex AI workflows, meaning LangTARS + Dify/n8n = powerful automation!
Get started with LangBot:
* **Easiest**: [LangBot Cloud](https://cloud.langbot.app)
* Self-host: `uvx langbot@latest`
* Documentation: [docs.langbot.app](https://docs.langbot.app)
* GitHub: [github.com/langbot-app/LangBot](https://github.com/langbot-app/LangBot) (15k+ stars)
***
## 🎯 What Can It Do?
As a comprehensive **automation tool**, just send a message, and the AI will handle your **task automation** and **browser automation**:
| You Say | AI Does |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `!tars open Reddit in Chrome, post a thread with the title "Hello from LangTARS" and say hi` | 🌐 Automatically controls Chrome to open Reddit and publish a post |
| `!tars organize my desktop files` | 📁 Automatically categorizes and moves files |
| `!tars open the browser and search for today's AI news` | 🌐 Opens a browser, searches the web, and summarizes the news |
| `!tars create a meeting note for our marketing sync` | 📝 Automatically creates a markdown note file |
| `!tars check system memory and CPU` | 💻 Uses MCP tools to get computer status |
***
## 🔗 Supercharge with Dify & n8n
LangTARS becomes even more powerful when combined with **AI workflow platforms**:
* **Dify Integration**: Connect LangBot to [Dify](https://dify.ai) as a messaging frontend. Build complex AI workflows in Dify's visual editor, then trigger them from Telegram/Discord via LangTARS.
* **n8n Integration**: Use [n8n](https://n8n.io) workflows with LangBot for advanced automation chains. Combine LangTARS's computer control with n8n's 400+ integrations for unlimited possibilities.
* **Also supports**: Langflow, Coze, and any OpenAI-compatible API.
> 💡 **Example**: "Hey TARS, run my Dify workflow to analyze today's sales data and save the report to my desktop" — LangTARS handles the entire chain.
***
## 🚀 Why Choose LangTARS?
### 1️⃣ LangBot Native Plugin, Out of the Box
Instead of deploying a complex standalone agent, you can easily **deploy AI bot** capabilities right where you already chat.
```bash theme={null}
# Deploy LangBot with one command
uvx langbot@latest
# Install LangTARS from the plugin market, configure your LLM, and start using!
```
**No complex configuration, no extra deployment. LangBot users can experience OpenClaw-style computer control directly!**
***
### 2️⃣ Intelligent Task Planning, Autonomous Execution
Powered by an AI planning engine based on the **ReAct agent** loop:
```
Your Command → Understand Needs → Make a Plan → Execute Step-by-Step → Feedback Results
```
The AI assistant thinks like a human and automatically breaks down complex tasks using integrated **MCP tools**!
***
### 3️⃣ Multi-Browser Support for Web Automation
| Browser | macOS | Windows | Linux |
| --------------------- | ----- | ------- | ----- |
| Playwright (Headless) | ✅ | ✅ | ✅ |
| Safari | ✅ | - | - |
| Chrome | ✅ | ✅ | ✅ |
| Edge | - | ✅ | - |
| Firefox | - | - | ✅ |
***
### 4️⃣ Security-First Design
* 🚫 **Dangerous Command Interception** — Automatically blocks commands like `rm -rf /`
* 📂 **Workspace Isolation** — Restricts file operations to safe directories
* 📋 **Command Whitelist** — Configurable allowed commands
* 👤 **User Access Control** — Restricts usage to specific authorized users
***
## 📱 Quick Start
### Step 1: Deploy LangBot
> 🚀 **Fastest way to start:** Use [LangBot Cloud](https://cloud.langbot.app) — deploy your LangBot instance in one click, no server needed! Install LangTARS from the plugin marketplace and start controlling your computer immediately.
*Alternative for power users (Self-hosted):*
```bash theme={null}
uvx langbot@latest
```
### Step 2: Configure Chat Platform
Follow the documentation to configure your **Telegram bot**, **WeChat bot**, or **Discord bot**.
### Step 3: Install LangTARS
Search for **LangTARS** in the **plugin marketplace** and install it.
### Step 4: Start Chatting
```
!tars Hello, tell me what you can do
```
> 📱 **Recommendation**: Use it on Telegram or Discord for the best interactive experience!
***
## 🎮 Common Commands
| Command | Function |
| -------------------------- | ---------------------- |
| `!tars ` | Execute a task |
| `!tars stop` | Stop the current task |
| `!tars what` | Check execution status |
| `!tars reset` | Reset the conversation |
| `!tars help` | Show help |
***
## 🌟 Project Information
| Item | Information |
| ------------- | -------------------------------------------------------------------------- |
| **GitHub** | [github.com/langbot-app/LangTARS](https://github.com/langbot-app/LangTARS) |
| **Language** | Python 100% |
| **License** | CC BY-NC-ND 4.0 |
| **Platforms** | macOS / Windows / Linux |
***
## 🔗 Related Links
* 📖 **LangBot Documentation**: [docs.langbot.app](https://docs.langbot.app)
* 💬 **Issue Feedback**: [GitHub Issues](https://github.com/langbot-app/LangTARS/issues)
* ⭐ **Support with a Star**: [GitHub](https://github.com/langbot-app/LangTARS)
***
## 🎉 Deploy LangBot and Experience LangTARS!
**Let AI be your loyal digital assistant, just like TARS.**
> 🚀 **Fastest way to start:** Use [LangBot Cloud](https://cloud.langbot.app) — deploy your LangBot instance in one click, no server needed! Install LangTARS from the plugin marketplace and start controlling your computer immediately.
*Alternative for power users (Self-hosted):*
```bash theme={null}
uvx langbot@latest
```
# How I Built a Multi-Platform AI Chatbot with n8n and LangBot
Source: https://docs.langbot.app/en/articles/n8n-multi-platform-ai-chatbot
How I Built a Multi-Platform AI Chatbot with n8n and LangBot
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/n8n-multi-platform-ai-chatbot).
Published 2025-12-03 · Author: LangBot Team
Connecting n8n's visual workflow automation with LangBot's multi-platform bot framework creates a powerful, code-free way to deploy AI chatbots across QQ, WeChat, Discord, Telegram, Slack, and more. This tutorial shows you how to integrate these tools in minutes.
## What You'll Need
* Python 3.8+ installed
* Node.js 18+ installed
* npm or npx available
* 15 minutes of your time
## Deploy LangBot in 3 Commands
LangBot is a production-ready bot framework that connects to multiple messaging platforms and AI services including Dify, FastGPT, Coze, OpenAI, Claude, and Gemini. Deploy it instantly:
```bash theme={null}
cd your-workspace
mkdir -p langbot-instance && cd langbot-instance
uvx langbot@latest
```
On first launch, LangBot initializes automatically. Open `http://127.0.0.1:5300` in your browser.
Register your admin account when prompted. You'll land on the dashboard where you can manage bots, models, pipelines, and integrations.
## Set Up n8n Workflow Automation
n8n is an open-source automation platform with 400+ integrations and powerful AI capabilities. Launch it locally:
```bash theme={null}
cd your-workspace
mkdir -p n8n-data
export N8N_USER_FOLDER=$(pwd)/n8n-data
npx n8n
```
Visit `http://127.0.0.1:5678` and create your owner account.
## Build Your AI Workflow
Create a new workflow in n8n. You'll need two essential nodes:
### Add the Webhook Trigger
Click "+" on the canvas and add a **Webhook** node. Configure it:
* **HTTP Method**: POST
* **Response Mode**: Streaming (enables real-time chat responses)
* **Authentication**: None (adjust for production)
### Add the AI Agent
Press Tab, navigate to the "AI" category, and select **AI Agent**.
**Configure the Chat Model**: Click "Chat Model" and choose "OpenAI Chat Model". Add your credentials:
* **API Key**: Your OpenAI API key (or compatible service key)
* **Base URL**: For OpenAI alternatives like Claude, Gemini, or local models, update to your provider's endpoint
**Critical Step - Fix the Prompt Source**: By default, the AI Agent expects a Chat Trigger node, which won't work with webhooks. Here's how to fix it:
1. Find "Source for Prompt (User Message)" in the AI Agent settings
2. Change from "Connected Chat Trigger Node" to "Define below"
3. Switch to "Expression" mode
4. Enter: `{{ $json.body }}`
This expression pulls the user's message from the webhook request body.
### Activate and Get Your Webhook URL
Save the workflow and toggle the activation switch (top-right). Switch to the "Production URL" tab and copy the webhook URL:
```
http://localhost:5678/webhook/{your-webhook-id}
```
## Connect LangBot to n8n
Back in the LangBot dashboard, navigate to **Pipelines** and click the default "ChatPipeline".
Switch to the **AI** tab and select "n8n Workflow API" from the Runner dropdown. Configure:
* **Webhook URL**: Paste your n8n production webhook URL
* **Authentication Type**: None (match your n8n webhook settings)
* **Timeout**: 120 seconds
* **Output Key**: response
Click **Save**.
## Test It Out
In the Pipeline editor, click "Debug Chat" on the left sidebar. Send a test message like "What is LangBot?"
If everything works, you'll see LangBot send the message to n8n, where the AI Agent processes it and streams back a response.
## Troubleshooting
**Error: "Expected to find the prompt in an input field called 'chatInput'"**
This means the AI Agent is still configured for a Chat Trigger node. Fix it:
1. Open the AI Agent configuration
2. Set "Source for Prompt (User Message)" to "Define below"
3. Switch to Expression mode
4. Enter: `{{ $json.body }}`
**Test Your Webhook Directly**
Verify the webhook works with curl:
```bash theme={null}
curl -X POST http://localhost:5678/webhook/your-webhook-id \
-H "Content-Type: application/json" \
-d '{"body": "Hello, can you introduce yourself?"}'
```
You should receive streaming JSON with the AI's response.
## How the Integration Works
Here's the complete flow:
1. User sends a message via QQ, WeChat, Discord, Telegram, Slack, LINE, or any LangBot-supported platform
2. LangBot's Pipeline receives the message and calls the n8n Workflow API
3. n8n's Webhook node captures the request and passes it to the AI Agent
4. The AI Agent uses OpenAI, Claude, Gemini, or your configured LLM to generate a response
5. n8n streams the response back to LangBot
6. LangBot delivers the response to the user on their original platform
## Why This Combination Works
**LangBot + n8n** unlocks powerful capabilities:
1. **No-Code AI Logic**: Design conversation flows visually in n8n without touching code
2. **Multi-Platform Reach**: Deploy the same AI across QQ, WeChat, Discord, Telegram, Slack, LINE, DingTalk, and Lark simultaneously
3. **Flexible AI Models**: Swap between OpenAI GPT, Anthropic Claude, Google Gemini, Coze, Dify, local models, and more
4. **Rich Integrations**: Connect n8n's 400+ integrations - databases, APIs, Notion, Airtable, Google Sheets, Slack, and beyond
5. **Tool-Calling Agents**: AI Agent can trigger n8n tools like database queries, API calls, or custom functions
6. **Workflow Extensions**: Add preprocessing, content moderation, logging, or custom business logic
**Perfect For**:
* Enterprise customer support bots
* Knowledge base Q\&A systems
* Multi-platform community management
* Task automation assistants
* Unified chat interfaces for teams
## Next Steps
Extend your bot further:
* Integrate Dify or FastGPT for advanced RAG (retrieval-augmented generation)
* Add vector database nodes (Pinecone, Qdrant, Weaviate) for knowledge retrieval
* Connect business APIs for real-time data
* Implement conversation memory and context tracking
* Add content filtering and moderation workflows
* Use Langflow or Coze for additional AI orchestration
This integration gives you the flexibility of code-based AI frameworks like LangChain with the simplicity of visual workflow builders - all while reaching users across every major messaging platform.
Ready to deploy your multi-platform AI assistant? Start with LangBot and n8n today.
# Introducing the LangBot Blog
Source: https://docs.langbot.app/en/articles/welcome
Welcome to the official LangBot blog. Stay up to date with product updates, new features, and milestones.
This article is synchronized from the LangBot Blog. [Read the canonical version](https://langbot.app/en/blog/welcome).
Published 2026-02-17 · Author: LangBot Team
We're excited to launch the official LangBot blog! This is where we'll share product updates, new features, milestones, and insights from the LangBot team.
## What is LangBot?
LangBot is an open-source LLM-native instant messaging bot platform. It supports multiple messaging platforms and model providers, with integrations for Dify, Alibaba Cloud Bailian, and other LLMOps platforms. LangBot also provides a WebUI for quick deployment and management.
## What to Expect
On this blog, you'll find:
* **Release Notes** - Detailed breakdowns of each new version
* **Feature Spotlights** - Deep dives into new capabilities
* **Milestones** - Project growth and community achievements
* **Guides & Tips** - Best practices for getting the most out of LangBot
## Stay Connected
* [GitHub](https://github.com/langbot-app/LangBot) - Star us and follow development
* [Documentation](https://docs.langbot.app) - Full guides and API reference
* [LangBot Home](https://langbot.app) - Main website
Stay tuned for more updates!
# Command
Source: https://docs.langbot.app/en/deploy/command
LangBot command-line reference: manage your AI bot instance, configuration, and runtime from the terminal.
LangBot will regard messages starting with `!` as commands. Official provides [`EssentialCommands` plugin](https://space.langbot.app/market?author=langbot-team\&plugin=EssentialCommands), which includes commonly used commands.
You can also develop your own command plugin, please refer to [Command Plugin Development](/en/plugin/dev/components/command).
# Docker Deployment
Source: https://docs.langbot.app/en/deploy/langbot/docker
Deploy LangBot with Docker and Docker Compose. Run an open-source multi-platform AI chatbot for Discord, Telegram, WeChat and more in minutes.
Before you start, you may learn about in advanced:
* Usage of `Docker` and `Docker Compose`
* Docker container network communication configuration methods
* Please ensure Git, Docker, and Docker Compose are installed
Clone the project:
```bash theme={null}
git clone https://github.com/langbot-app/LangBot
cd LangBot/docker
```
Start the containers with the recommended `all` profile, which enables Box Runtime-dependent features by default, including the sandbox, stdio MCP hosting, and Skill add/edit:
```bash theme={null}
docker compose --profile all up
```
## Runtime tokens for Internet-accessible deployments
The open-source edition skips token verification only when **both LangBot and the corresponding Runtime leave the token unset**. If the deployment is accessible from the Internet, strongly protect Plugin Runtime and Box Runtime with separate strong tokens:
```bash theme={null}
export LANGBOT_PLUGIN_RUNTIME_CONTROL_TOKEN="$(openssl rand -hex 32)"
export LANGBOT_BOX_CONTROL_TOKEN="$(openssl rand -hex 32)"
docker compose --profile all up -d
```
The official Compose file passes each variable to both ends of its connection. Setting only one end, using different values, or using a weak token causes the connection to be rejected. Store tokens in your deployment platform's secret manager and never commit them.
This starts `langbot`, `langbot_plugin_runtime`, and `langbot_box`. If you only need the basic services and do not want to enable Box Runtime, run:
```bash theme={null}
docker compose up
```
## SeekDB support
The official Docker image already includes the optional SeekDB dependencies;
no additional build argument is required. The container uses the matching
Linux `pylibseekdb` wheel, so the host macOS version does not limit embedded
mode inside the container. Keep the default `vdb.use: chroma` when SeekDB is
not needed.
`langbot_box` creates sandbox containers through the host Docker socket, so the Box root path must be identical on the host and inside the container:
```yaml theme={null}
services:
langbot:
environment:
- BOX__LOCAL__HOST_ROOT=${LANGBOT_BOX_ROOT:-${PWD}/data/box}
- BOX__LOCAL__DEFAULT_WORKSPACE=default
- BOX__LOCAL__SKILLS_ROOT=skills
- BOX__LOCAL__ALLOWED_MOUNT_ROOTS=${LANGBOT_BOX_ROOT:-${PWD}/data/box}
langbot_box:
profiles: ["box", "all"]
volumes:
- ${LANGBOT_BOX_ROOT:-${PWD}/data/box}:${LANGBOT_BOX_ROOT:-${PWD}/data/box}
- /var/run/docker.sock:/var/run/docker.sock
```
**The Box control plane's transport depends on the deployment method.** In Docker deployments it runs as the standalone `langbot_box` **container**, and `langbot` connects to it over WebSocket (`ws://langbot_box:5410`). In manual / `uvx` deployments, `langbot` instead spawns a stdio subprocess as the Box control plane. `langbot_box` creates sandbox sibling containers on the host Docker through the mounted `docker.sock`; the LangBot image already bundles the `docker` client, so no extra installation is required.
If you see `No sandbox backend (Docker/nsjail/E2B) is ready`, first make sure the current user can access Docker:
```bash theme={null}
sudo usermod -aG docker $USER
newgrp docker
docker info
```
If `docker info` works, restart LangBot.
`langbot_box` does not read `LANGBOT_BOX_*` or `BOX__*` variables directly. Set `BOX__LOCAL__*` on `langbot`; LangBot forwards the effective Box config to Box Runtime through INIT RPC. To change the Box root, set `LANGBOT_BOX_ROOT` to an absolute path, for example `LANGBOT_BOX_ROOT=/var/lib/langbot/box docker compose --profile all up`.
The container maps port `5300` for WebUI access. You can visit `http://127.0.0.1:5300` to view the WebUI.\
It also maps ports `2280-2285` reserved for message platform adapters.
After completing the LangBot deployment, please continue reading:
* [Bot Configuration Documentation](/en/usage/platforms/readme).
* [Model Configuration Documentation](/en/usage/models/readme).
* [Sandbox Documentation](/en/usage/sandbox/readme).
* [Skills Documentation](/en/usage/skills/readme).
# Kubernetes Deployment
Source: https://docs.langbot.app/en/deploy/langbot/kubernetes
Deploy LangBot on Kubernetes for scalable, production-grade AI bot hosting across messaging platforms.
Before you start, you should be familiar with:
* Basic Kubernetes usage (`kubectl`, Namespace, Deployment, Service, PVC)
* How to configure inter-container networking and persistent storage
The LangBot repository ships a ready-to-use Kubernetes manifest at [`docker/kubernetes.yaml`](https://github.com/langbot-app/LangBot/blob/master/docker/kubernetes.yaml). This page is the deployment guide for that manifest, focusing on the **Box sandbox runtime** under Kubernetes — the part that differs most from the Docker Compose deployment.
## Components
The manifest creates the following components in the `langbot` namespace:
| Component | Description | Ports |
| ------------------------ | ------------------------------ | ---------------- |
| `langbot` | Main app (WebUI + webhooks) | 5300 / 2280-2290 |
| `langbot-plugin-runtime` | Plugin runtime | 5400 |
| `langbot-box` | Box sandbox runtime (optional) | 5410 |
Persistent storage: three PVCs (`langbot-data`, `langbot-plugins`, `langbot-plugin-runtime-data`). The Box workspace root uses a node `hostPath` (`/app/data/box`) rather than a PVC.
## Quick Start
```bash theme={null}
# Clone the repository
git clone https://github.com/langbot-app/LangBot
cd LangBot/docker
# Deploy all components
kubectl apply -f kubernetes.yaml
# Check status
kubectl get all -n langbot
```
Access the WebUI via port-forward:
```bash theme={null}
kubectl port-forward -n langbot svc/langbot 5300:5300
# Visit http://localhost:5300
```
For production, expose via Ingress + TLS (the manifest already includes commented Ingress / LoadBalancer / NodePort examples — uncomment as needed).
### Using SeekDB
The official image already includes the optional SeekDB dependencies, so no
dedicated image or additional build argument is required. Set `vdb.use` to
`seekdb` to enable it, or keep the default `chroma` when it is not needed.
## Box Sandbox Runtime
`langbot-box` provides the code-sandbox capability backing the sandbox tools (`exec` / `read` / `write` / `edit` / `glob` / `grep`), the skill `activate` tool and skill add/edit, and stdio-mode MCP servers. It is **optional**: without it, LangBot still runs and the dashboard / skills list stay visible (read-only), but those features are disabled — in that case set `BOX__ENABLED=false` on `langbot`.
### How it works & the key constraint
The official LangBot image ships **only the Docker CLI** (no dockerd, no nsjail). The Box runtime therefore creates sandbox containers by talking to the node's Docker daemon over the mounted socket (`/var/run/docker.sock`) — it does not run dockerd inside the Pod.
This imposes a **key constraint**: the daemon that actually creates sandbox containers is the **node's Docker daemon**, which resolves bind-mount paths against the **node filesystem**. So the Box workspace root must be the **same absolute path** in all three places:
1. The actual path on the node
2. The mount path inside the `langbot-box` container
3. The mount path inside every sandbox container it spawns
This is exactly why the manifest uses a `hostPath` (fixed at `/app/data/box`) instead of a regular PVC: a PVC path only exists inside the Pod's mount namespace, which the node's dockerd cannot see. `langbot` and `langbot-box` are also pinned to the **same node** via `podAffinity` so they share this hostPath.
Because it relies on hostPath + same-node affinity, `langbot` does not support multi-replica horizontal scaling (same limitation as `ReadWriteOnce` PVCs). High availability requires a different architecture.
### Connection & configuration
* `langbot` connects to the Box runtime over WebSocket, using the endpoint from the ConfigMap: `BOX__RUNTIME__ENDPOINT: ws://langbot-box:5410`.
The in-container default hostname is `langbot_box` (with an underscore), which is **not** a valid Kubernetes DNS name. The endpoint must therefore be set **explicitly** to the valid Service name `langbot-box` — you cannot rely on the in-container default.
* The Box runtime does **not** read its own `box.local.*` / `BOX__*` environment variables; its configuration is read from `BOX__LOCAL__*` by `langbot` and pushed via the INIT RPC. So `BOX__LOCAL__HOST_ROOT` / `DEFAULT_WORKSPACE` / `SKILLS_ROOT` / `ALLOWED_MOUNT_ROOTS` are set on the `langbot` Deployment, where `HOST_ROOT` must match the `box-root` mountPath on both sides (`/app/data/box`).
### Security note
Mounting the node's Docker socket grants the Box runtime (and any code executed in the sandbox) effective root on the node. Only deploy Box on nodes you trust for this workload — ideally a dedicated node pool isolated with taints/tolerations. For a stronger isolation boundary, switch `box.backend` to `e2b` (set `E2B_API_KEY`) and drop the `docker.sock` mount + hostPath. See [Sandbox configuration](/en/usage/sandbox/config).
### Verify Box is ready
```bash theme={null}
# Box runtime logs (should show the selected backend, e.g. "using backend: docker")
kubectl logs -n langbot -l app=langbot-box -f
# Confirm langbot connected to the Box runtime
kubectl logs -n langbot -l app=langbot | grep -i "box runtime"
```
## When you don't need the sandbox
If you do not need the sandbox, remove the `langbot-box` Deployment / Service from the manifest and set `BOX__ENABLED=false` on the `langbot` Deployment to avoid connection-failure warnings in the logs.
## Further reading
* [Docker Deployment](/en/deploy/langbot/docker)
* [Sandbox docs](/en/usage/sandbox/readme) and [Sandbox configuration](/en/usage/sandbox/config)
* [Skills docs](/en/usage/skills/readme)
* Manifest source file: [`docker/kubernetes.yaml`](https://github.com/langbot-app/LangBot/blob/master/docker/kubernetes.yaml) in the repo
# Manual Deployment
Source: https://docs.langbot.app/en/deploy/langbot/manual
Manually deploy LangBot from source. Step-by-step setup for the open-source AI bot platform on Linux, Windows and macOS.
## Installing the Main Program
1. Go to the [Release](https://github.com/langbot-app/LangBot/releases) page to download the latest version of the zip package (recommended) `langbot-xxx-all.zip` (do not download Source Code as it does not include WebUI). Extract it and open a command line (terminal) in the extracted directory.
2. Install dependencies
We use [uv](https://docs.astral.sh/uv/) to manage dependencies.
```bash theme={null}
pip install uv
uv sync
```
The default installation does not include optional SeekDB support. If you need
the SeekDB vector store or built-in embedding model, use:
```bash theme={null}
uv sync --extra seekdb
```
Embedded-mode platform support depends on the available `pylibseekdb` wheels;
Apple Silicon currently requires macOS 15 or later. All other LangBot features
can use the default `uv sync` installation.
3. Run the main program once to generate configuration files
```bash theme={null}
uv run main.py
```
You will see the following message:
```
_ ___ _
| | __ _ _ _ __ _| _ ) ___| |_
| |__/ _` | ' \/ _` | _ \/ _ \ _|
|____\__,_|_||_\__, |___/\___/\__|
|___/
⭐️Open Source: https://github.com/langbot-app/LangBot
📖Documentation: https://docs.langbot.app
The following files do not exist and have been automatically generated. Please modify the configuration files as needed and restart:
- data/config.yaml
...
```
You can now use Ctrl+C to exit the program and continue to the [Configure Bots](/en/usage/platforms/readme) page.
# 1Panel Deployment
Source: https://docs.langbot.app/en/deploy/langbot/one-click/1panel
One-click LangBot deployment with 1Panel. Install the open-source AI chatbot platform without touching the command line.
Deploy LangBot with one click using [1Panel](https://www.bt.cn/new/download.html?r=dk_LangBot).
## Install 1Panel
> Recommended server systems (from 1Panel): Debian 12, Ubuntu 22, etc.
This article assumes that the server has a public IPv4 address by default. If you are using a home machine without a public IP, you can access the relevant pages through the internal network described below.
Visit [1Panel](https://www.bt.cn/new/download.html?r=dk_LangBot) and use the Linux panel installation script provided officially by 1Panel. Select the corresponding script according to your operating system.
Copy the corresponding script, paste it into the server, and execute it.
After waiting for a period of time, the installation of 1Panel will be completed.
Then copy the `External Address` and open it in a browser.
## Install LangBot
Open the `App Store`, select the `AI` category, and click to install LangBot.
Check the box for `External Port Access` and click Confirm.
Wait for a moment, and the installation will be completed.
Click `Containers` on the left side, then select the `Containers` category, and you will see the LangBot container bar.
Copy the public IP address of the server, for example, `http://xxx.xxx.xxx.xxx:5300` (where xxx.xxx.xxx.xxx is the IP address of the server where 1Panel is located, and 5300 is the port number).
Open the address, enter the administrator email and password, then click Initialize.
After that, you can configure LangBot in the visual interface.
Next, please continue to read the [Configure Messaging Platforms](/en/usage/platforms/readme) page.
# aaPanel Deployment
Source: https://docs.langbot.app/en/deploy/langbot/one-click/bt
One-click LangBot deployment with aaPanel (BT Panel). Install the open-source AI bot platform through a web control panel.
Deploy LangBot with one click using [aaPanel](https://www.bt.cn/new/download.html?r=dk_LangBot).
If you already have aaPanel installed, please update to version **9.2.0** or higher
## Installing aaPanel
> Recommended server systems (from aaPanel): Debian 12, Ubuntu 22, etc.
>
> This document assumes you have a server with a public IPv4 address. If you're using a home machine without a public IP, you can access the relevant pages through the internal network mentioned below.
Open [aaPanel](https://www.bt.cn/new/download.html?r=dk_LangBot), use the Linux panel installation script provided by aaPanel official, and select the corresponding script based on your system.
Copy the corresponding script and paste it into your server to execute.
## Installing LangBot
After waiting for a while, aaPanel installation will be complete.
You'll see a message similar to this, copy the external panel address and open it in your browser.
When you first open it, you should see a popup for `Recommended Installation Suite`. You can skip this by closing it.
Then click on `docker` in the sidebar. If it's your first time, you'll be prompted to install Docker and Docker Compose services. Click "Install Now" if prompted, or ignore if already installed.
Then go to `Docker -> App Store`, search for `LangBot` (note the case sensitivity), click `Install`, and use the default configuration.
After a short wait, you'll see a container named `langbot_XXXX` showing as `Running`.
Click the red circle in the image above to view the running logs.
You can see the external and internal access paths for the WebUI configuration page (visual configuration page). Copy the public address, for example `http://xxx.xxx.xxx.xxx:5300` (where xxx.xxx.xxx.xxx is your aaPanel IP, and 5300 is the port number, which may vary as shown in the image).
After opening it, enter your admin email and password, then click initialize.
You can then proceed with configuration in the visual interface.
If you prefer to directly modify JSON configuration files, you can look at the local mapping directory shown in the image above. Go to the installation directory, where the `data` folder is used to store configuration files, and the `plugins` folder is used for plugin-related files.
Next, please continue reading the [Configure Message Platform](/en/usage/platforms/readme) page.
# Deploy with Package Manager
Source: https://docs.langbot.app/en/deploy/langbot/package
Deploy LangBot with a package manager (uv/uvx). Quick install for the open-source LLM chatbot platform.
Supports Windows, Linux, Mac OS, etc. Recommended for testing environments and personal use scenarios; production-level environments please use [Docker Deployment](/en/deploy/langbot/docker.html).
LangBot is packaged and released to PyPI. Please install [uv](https://docs.astral.sh/uv/getting-started/installation/), and then run the following command in an empty directory:
```bash theme={null}
uvx langbot@latest
```
This will set the current directory as the working directory, and you can access `http://localhost:5300` to start using it.
## Optional SeekDB support
The default installation uses Chroma and does not include the SeekDB Python
SDK or native components. Install the `seekdb` extra only when you want to use
SeekDB as the vector store or use its built-in embedding model:
```bash theme={null}
uvx --from 'langbot[seekdb]@latest' langbot
```
SeekDB embedded mode also requires a compatible `pylibseekdb` wheel. Apple
Silicon currently requires macOS 15 or later; follow
[oceanbase/seekdb#1324](https://github.com/oceanbase/seekdb/issues/1324) for the
macOS 14 ARM64 support plan. Because of the current `pyseekdb` package
metadata, the extra also fails to resolve on macOS 14 ARM64 for server-only
use. These platform limits do not affect LangBot when SeekDB is not installed.
# System Environment Settings
Source: https://docs.langbot.app/en/deploy/settings
Complete reference for LangBot config.yaml: API, concurrency, databases, storage, plugins, MCP, monitoring, and Box.
LangBot runtime configuration lives in `data/config.yaml`. The first startup generates it from the default template. The example below is kept aligned with [`src/langbot/templates/config.yaml`](https://github.com/langbot-app/LangBot/blob/master/src/langbot/templates/config.yaml).
Most self-hosted deployments only need to change public URLs, databases/vector stores, object storage, and Box Runtime. Keep capacity and Cloud safety limits at their defaults unless you understand their operational impact.
## Complete default configuration
```yaml theme={null}
api:
port: 5300
webhook_prefix: 'http://127.0.0.1:5300'
extra_webhook_prefix: ''
# Canonical browser origin when WebUI and API use different origins in
# development (for example http://localhost:3000). Production bundled UI
# may leave this empty when webhook_prefix already has the browser origin.
# OAuth redirects trust only these server-side values, never request Host
# or Origin headers.
webui_url: ''
# Global API key for the HTTP service API and the MCP server. When set to a
# non-empty string, this key is accepted anywhere a web-UI-created API key is
# accepted (X-API-Key header or "Authorization: Bearer "), WITHOUT any
# login session and without a database record. Leave empty to disable.
# Keep this value secret; only enable it on trusted/internal deployments.
global_api_key: ''
workspace:
invitations:
# Public WebUI origin used to build invitation links. Leave empty to
# use api.webui_url, then api.webhook_prefix. Set via
# WORKSPACE__INVITATIONS__PUBLIC_WEB_URL in container deployments.
public_web_url: ''
email:
# Optional invitation email delivery. Empty provider keeps
# invitations link-only. Supported: resend, smtp.
provider: ''
from: ''
timeout_seconds: 10
resend:
api_url: 'https://api.resend.com/emails'
# Secret. Set via WORKSPACE__INVITATIONS__EMAIL__RESEND__API_KEY.
api_key: ''
smtp:
host: ''
port: 587
username: ''
# Secret. Set via WORKSPACE__INVITATIONS__EMAIL__SMTP__PASSWORD.
password: ''
starttls: true
ssl: false
command:
enable: true
prefix:
- '!'
- !
privilege: {}
concurrency:
pipeline: 20
session: 1
# Hard admission limits for queued + running pipeline queries.
pending_queries: 1000
pending_queries_per_workspace: 100
webhooks:
# Bound database materialization and per-message outbound fan-out.
# Existing rows above this limit remain deletable through the management
# API, but only this many enabled destinations are dispatched.
# Supports WEBHOOKS__MAX_PER_WORKSPACE (hard cap: 64).
max_per_workspace: 16
# Instance-wide request admission. Delivery fails open when every slot is
# occupied instead of retaining an unbounded queue of webhook tasks.
# Supports WEBHOOKS__MAX_INFLIGHT_REQUESTS (hard cap: 128).
max_inflight_requests: 16
cloud:
# Operational safety ceilings for the one logical Cloud instance. These
# are not subscription entitlements. An authoritative directory update
# that would exceed them is rejected atomically rather than truncated.
directory:
# Tune downward from the measured production capacity curve. Core has
# an absolute safety ceiling of 5,000 active Workspaces.
max_active_workspaces: 1000
# Full snapshots contain current Workspaces only. Archived tombstones
# are delivered through bounded per-Workspace deltas.
max_snapshot_workspaces: 1000
# Aggregate memberships accepted in one signed snapshot or delta.
max_snapshot_memberships: 20000
# Signed control-plane envelope buffered by the closed adapter before
# JSON/JWS verification (32 MiB; absolute maximum 64 MiB).
max_response_bytes: 33554432
proxy:
http: ''
https: ''
system:
instance_id: ''
edition: community
recovery_key: ''
allow_modify_login_info: true
disabled_adapters: []
blocking_executor:
# All asyncio.to_thread work shares this process-wide bounded pool.
# Both running threads and queued calls are capped to prevent tenant
# bursts from creating an unbounded queue of retained request objects.
max_workers: 8
max_pending: 128
# One trusted Workspace can occupy at most this many running + queued
# slots. This must not exceed half of max_workers.
max_inflight_per_scope: 4
# Public outbound IP addresses of this LangBot deployment. Some platforms
# (e.g. WeCom, WeChat Official Account, QQ Official API) require the
# caller's IPs to be added to their trusted-IP / IP-whitelist settings.
# When set, the web UI shows these IPs on the bot config form of such
# adapters. Also settable via the SYSTEM__OUTBOUND_IPS env var
# (comma-separated). Empty list = hidden in the web UI.
outbound_ips: []
limitation:
max_bots: -1
max_pipelines: -1
max_extensions: -1
max_knowledge_bases: -1
# When set to a non-empty string, every pipeline is forced to use this
# Box sandbox-scope template regardless of its own configuration, and
# the per-pipeline "Sandbox Scope" selector is locked in the web UI.
# Used by SaaS deployments to confine a tenant to a single shared
# sandbox (set to '{global}'). Empty string = no restriction.
force_box_session_id_template: ''
task_retention:
# Keep at most this many completed async task records in memory
completed_limit: 200
# Bound progress output retained by one task, including running tasks.
max_log_chars: 200000
# Protect the shared process from user-triggered operation storms.
max_active_user_tasks: 256
max_active_user_tasks_per_workspace: 8
session_retention:
# Process-local conversation sessions are a cache, not durable history.
max_entries: 2000
max_entries_per_workspace: 200
idle_ttl_seconds: 86400
max_conversations_per_session: 20
max_messages_per_conversation: 100
websocket_retention:
# Bound live browser sockets and per-Workspace fan-out in the shared process.
max_connections: 1024
max_connections_per_workspace: 32
# Idle proxy runtimes are evicted when this process-local cache fills.
max_workspace_proxies: 1024
max_conversations_per_workspace: 200
max_messages_per_conversation: 100
conversation_idle_ttl_seconds: 86400
send_queue_size: 100
response_limits:
# Defense in depth for tenant-configured upstream providers.
max_generated_chars: 1048576
max_stream_chunks: 100000
jwt:
expire: 604800
secret: ''
database:
use: sqlite
sqlite:
path: 'data/langbot.db'
postgresql:
# Optional SQLAlchemy URL (postgresql[+asyncpg]://...). When set, it
# overrides the structured fields and preserves TLS/query options.
url: ''
host: '127.0.0.1'
port: 5432
user: 'postgres'
password: 'postgres'
database: 'postgres'
# One bounded pool is shared by business data and Cloud pgvector.
pool_size: 10
max_overflow: 10
pool_timeout_seconds: 30
pool_recycle_seconds: 1800
# Applied only to Cloud runtime connections. The one-shot release
# migration uses its operator connection without these short limits.
statement_timeout_ms: 60000
lock_timeout_ms: 5000
idle_in_transaction_session_timeout_ms: 60000
cloud_migration:
# `langbot migrate --cloud` reads an operator-only PostgreSQL DSN from
# this environment variable. The operator role must differ from the
# runtime role above; never put its password in this file or CLI args.
operator_dsn_env: 'LANGBOT_CLOUD_MIGRATION_DSN'
vdb:
use: chroma
# Bound process-local collection/index handles across all Workspaces.
runtime_cache_limit: 1024
qdrant:
url: ''
host: localhost
port: 6333
api_key: ''
# SeekDB is optional. Source/package deployments need the `seekdb` extra.
# The official Docker/Kubernetes image already includes it.
# Embedded-mode platform support depends on the native pylibseekdb wheels.
seekdb:
mode: embedded # 'embedded' or 'server'
# Embedded mode options:
path: './data/seekdb'
database: 'langbot'
# Server mode options (used when mode='server'):
host: 'localhost'
port: 2881
user: 'root'
password: ''
tenant: '' # Optional, for OceanBase server
milvus:
uri: 'http://127.0.0.1:19530'
token: ''
db_name: ''
pgvector:
# SaaS/shared-schema deployments reuse database.postgresql. OSS can
# keep this false when deliberately using an external pgvector DB.
use_business_database: false
# Release migrations create one partial ANN index per enabled value.
allowed_dimensions: [384, 512, 768, 1024, 1536]
host: '127.0.0.1'
port: 5433
database: 'langbot'
user: 'postgres'
password: 'postgres'
valkey_search:
host: 'localhost'
port: 6379 # integration tests use 6380 -> valkey/valkey-bundle:9.1.0
db: 0
password: '' # optional (toB auth)
username: '' # optional (ACL user, toB)
tls: false # optional (toB/SaaS)
index_algorithm: 'HNSW' # HNSW | FLAT
distance_metric: 'COSINE' # COSINE | L2 | IP
request_timeout: 5000 # per-request timeout in ms (glide default 250ms is too low for KNN)
storage:
use: local
# Bound every object materialized into Core memory. Built-in Local/S3
# providers enforce this while reading (hard cap: 64 MiB).
max_object_read_bytes: 10485760
cleanup:
# Enable periodic cleanup of local/S3 uploaded files and old log files
enabled: true
# Cleanup check interval in hours
check_interval_hours: 1
# Root-level uploaded files older than this will be deleted
uploaded_file_retention_days: 7
# LangBot log files older than this many days will be deleted
log_retention_days: 3
# Bound per-Workspace file cleanup and diagnostic candidate lists.
# Supports STORAGE__CLEANUP__MAX_FILES_PER_RUN (hard cap: 10000).
max_files_per_run: 1000
s3:
endpoint_url: ''
access_key_id: ''
secret_access_key: ''
region: 'us-east-1'
bucket: 'langbot-storage'
# boto3 is synchronous; bound the number of operations delegated to
# worker threads so an S3 slowdown cannot saturate the process.
max_concurrency: 16
plugin:
enable: true
runtime_ws_url: 'ws://langbot_plugin_runtime:5400/control/ws'
enable_marketplace: true
display_plugin_debug_url: 'ws://localhost:5401/plugin/debug/ws'
worker:
# Instance-wide maximum for every plugin installation. Plugin
# manifests cannot raise or override these limits.
max_cpus: 1.0
max_memory_mb: 512
max_pids: 128
max_open_files: 256
max_file_size_mb: 512
# Instance-wide admission budgets. The effective worker count is the
# lowest of max_workers, max_total_cpus/max_cpus and
# max_total_memory_mb/max_memory_mb.
max_workers: 16
max_total_cpus: 8.0
max_total_memory_mb: 8192
# Includes disabled and historical installation fences retained to
# reject stale desired-state replay.
max_installations: 10000
# Restart storms are globally serialized by default. Repeated
# unexpected exits within the configured window open a Runtime-wide
# circuit; one half-open probe must remain stable before other
# installations may restart.
max_concurrent_restarts: 1
restart_failure_threshold: 8
restart_failure_window_seconds: 30.0
restart_circuit_open_seconds: 60.0
# Cloud shared Runtime sets this to true and fails closed unless
# delegated cgroup v2 controllers are available.
require_hard_limits: false
binary_storage:
# Max bytes for a single plugin binary storage value
max_value_bytes: 10485760
mcp:
# Bound instance-wide MCP startup and shutdown bursts. Supports
# MCP__LIFECYCLE_CONCURRENCY and is clamped to a maximum of 128.
lifecycle_concurrency: 16
stdio:
# Independent gate for local stdio MCP transports. Cloud v2 sets
# MCP__STDIO__ENABLED=false even when Box Runtime is available.
enabled: true
monitoring:
query_limits:
# Maximum records materialized by one paginated monitoring request.
# Supports MONITORING__QUERY_LIMITS__PAGE_ROWS (hard cap: 5000).
page_rows: 1000
# CSV exports are currently assembled in memory. Keep this lower than
# the historical 100000-row default (hard cap: 50000).
export_rows: 10000
# Maximum related records returned by one session/message detail view
# (hard cap: 10000). Aggregate statistics remain database-computed.
detail_rows: 2000
# Token charts are grouped in SQL and return only the newest buckets
# (hard cap: 10000). Supports an environment variable override.
timeseries_buckets: 1000
# Bound high-offset scans that can otherwise monopolize PostgreSQL CPU
# (hard cap: 10000000).
max_offset: 1000000
auto_cleanup:
# Enable automatic cleanup of expired monitoring records
enabled: true
# Retention period in days, records older than this will be deleted
retention_days: 30
# Cleanup check interval in hours
check_interval_hours: 1
# Number of expired rows to delete per table batch
delete_batch_size: 1000
# Prevent one large Workspace backlog from monopolizing PostgreSQL.
# Supports MONITORING__AUTO_CLEANUP__MAX_BATCHES_PER_TABLE_PER_RUN.
max_batches_per_table_per_run: 4
box:
# Master switch for the Box sandbox runtime. When false, LangBot does NOT
# attempt to connect to a remote Box runtime nor start a local stdio Box
# subprocess. Disabling Box also disables every feature that depends on it:
# the native sandbox tools (exec/read/write/edit/glob/grep), the activate
# skill tool, skill add/edit, and stdio-mode MCP servers. Skills can still
# be listed read-only and http/sse MCP servers continue to work.
enabled: true
backend: 'local' # 'local' (Docker/nsjail), 'docker', 'nsjail', or 'e2b'. Can be written via BOX__BACKEND.
runtime:
# LANGBOT_BOX_CONTROL_TOKEN is optional for OSS external WebSocket
# runtimes. To protect an exposed endpoint, set the same strong secret
# in both LangBot and Box. Keep it out of this config file.
endpoint: '' # External Box Runtime base URL, e.g. 'ws://127.0.0.1:5410'. Leave empty for local auto-managed runtime.
limits:
max_sessions: 64
max_managed_processes: 64
max_completed_processes: 256
# Core scans a Workspace before and after quota-enforced executions.
# Fail closed instead of repeatedly walking an inode bomb.
# Supports BOX__LIMITS__MAX_WORKSPACE_ENTRIES (hard cap: 1000000).
max_workspace_entries: 100000
# Retained admission fences prevent replay after entitlement expiry or
# revocation. Fail closed before that monotonic state can grow without
# bound; Cloud may override this with BOX__LIMITS__MAX_ADMISSION_RECORDS.
max_admission_records: 100000
max_rpc_file_bytes: 20971520
# Cloud v2 overrides these values through the instance config/environment.
# OSS keeps admission disabled and preserves the existing multi-session
# local behavior. These limits are Runtime-owned and cannot be relaxed by
# a pipeline, Workspace entitlement, or tool call.
admission:
required: false
logical_session_id: 'global'
required_backend: 'nsjail'
max_sessions: 1
max_managed_processes: 0
max_grant_ttl_sec: 300
max_timeout_sec: 120
cpus: 1.0
memory_mb: 512
pids_limit: 128
read_only_rootfs: true
# OSS admission-disabled mode uses 0 for unlimited compatibility.
# Cloud bootstrap requires a positive hard quota.
workspace_quota_mb: 0
readiness_cache_sec: 15
local:
profile: 'default'
image: '' # Custom local sandbox image. Leave empty to use the profile default.
host_root: './data/box' # Base host directory for local workspace mounts. Docker deployments should override this with an absolute host path.
default_workspace: '' # Defaults to '/default'. Relative paths are resolved under host_root.
skills_root: 'skills' # Box-owned skill package directory. Relative paths are resolved under host_root.
allowed_mount_roots: # Defaults to [''] when left empty.
- './data/box'
- '/tmp'
workspace_quota_mb: null # Optional disk quota override (>= 0). null = profile default.
# Default nsjail cgroup memory limit for each MCP stdio server process, in MB.
# Node.js MCP servers (npx/bunx) need more memory than Python ones because V8
# and WebAssembly modules (e.g. undici llhttp) reserve large virtual address
# space at startup. Setting this too low causes processes to be killed with
# return_code=137 (OOM kill); the symptom is "Box managed process exited
# unexpectedly" in the logs. Raise on machines with ample RAM; lower only if
# you run exclusively Python (uvx) MCP servers.
# Can also be set via BOX__DEFAULT_MEMORY_MB. Default: 1536.
default_memory_mb: 1536
docker:
cpu_limit_enabled: true # When false, Docker sandbox containers are started without --cpus. Memory and PID limits still apply.
e2b:
api_key: '' # Can also be set via E2B_API_KEY env var.
api_url: '' # Custom API URL for self-hosted deployments.
template: '' # Default template ID (e.g. 'base', 'python-3.11').
space:
# Space service URL for OAuth and API
url: 'https://space.langbot.app'
# Space API URL for model requests (MaaS)
models_gateway_api_url: 'https://api.langbot.cloud/v1'
# OAuth authorization page URL (user will be redirected here)
oauth_authorize_url: 'https://space.langbot.app/auth/authorize'
disable_models_service: false
disable_telemetry: false
```
## Configuration groups
### API, WebUI, and invitations
* `api.port` is the HTTP API and WebUI port. `api.webhook_prefix` is the public base URL used to generate platform callback URLs; production deployments normally set it to the HTTPS reverse-proxy origin.
* Set `api.webui_url` when the browser UI and API use different origins. OAuth redirects trust only this server-side value and `webhook_prefix`, never request `Host` or `Origin` headers.
* `api.global_api_key` authenticates the HTTP Service API and built-in MCP server through `X-API-Key` or `Authorization: Bearer`, without a login session or a database-backed `lbk_` key. Empty means disabled.
* `workspace.invitations.public_web_url` controls invitation links and falls back to `api.webui_url`, then `api.webhook_prefix`. Email delivery is optional; choose `resend` or `smtp`, or leave `provider` empty for link-only invitations.
Treat the global API key, JWT secret, database credentials, S3 credentials, email-provider secrets, and E2B key as secrets. Prefer environment variables in production, never commit real values, and expose authenticated endpoints only over HTTPS.
### Admission and capacity limits
* `concurrency` bounds running and queued pipeline work globally and per Workspace.
* `webhooks` bounds enabled destinations per Workspace and instance-wide outbound requests. Full request admission fails open rather than retaining an unbounded task queue.
* `cloud.directory` contains operational safety ceilings for one logical Cloud instance, not subscription entitlements. Oversized authoritative directory updates are rejected atomically instead of being truncated. Most self-hosted deployments should keep these defaults.
* `system.blocking_executor`, `task_retention`, `session_retention`, `websocket_retention`, and `response_limits` bound process-local workers, cached records, sockets, and upstream output.
* Values under `system.limitation` are instance limits; `-1` means unlimited. `force_box_session_id_template` is intended for SaaS sandbox confinement and should remain empty for normal self-hosting.
### Databases and vector stores
* `database.use` selects SQLite or PostgreSQL. A non-empty `database.postgresql.url` overrides the structured connection fields and preserves TLS/query options.
* PostgreSQL pool and timeout settings bound shared runtime resources. `database.cloud_migration.operator_dsn_env` names the environment variable containing the operator-only migration DSN; keep that role separate from the runtime role.
* `vdb.use` selects the vector backend. Configure only the selected backend. `runtime_cache_limit` bounds process-local collection/index handles.
* `vdb.use: seekdb` requires `uv sync --extra seekdb` for a source deployment or `uvx --from 'langbot[seekdb]@latest' langbot` for a package deployment. The official Docker/Kubernetes image already includes the dependency and needs no additional build argument.
* `vdb.pgvector.use_business_database` reuses `database.postgresql`; `allowed_dimensions` controls the partial ANN indexes created by release migrations.
* Valkey Search requires a Valkey server with the Search module, such as `valkey/valkey-bundle:9.1.0`.
### Storage, plugins, MCP, and monitoring
* `storage.max_object_read_bytes` caps objects materialized into Core memory. Cleanup limits bound file scans, and `s3.max_concurrency` bounds synchronous boto3 operations delegated to worker threads.
* `plugin.worker` defines hard per-installation and instance-wide budgets. Plugin manifests cannot raise them. Restart-window settings suppress Runtime restart storms.
* `mcp.lifecycle_concurrency` bounds MCP startup/shutdown bursts. `mcp.stdio.enabled` can disable local stdio transports without disabling HTTP/SSE MCP servers.
* Monitoring query and cleanup limits prevent large pages, exports, offsets, or backlogs from monopolizing memory or PostgreSQL.
### Box sandbox
* `box.enabled` is the master switch. Disabling it also disables native sandbox tools, Skill add/edit, and stdio MCP, while read-only Skill listing and HTTP/SSE MCP remain available.
* `box.backend` selects `local`, `docker`, `nsjail`, or `e2b`; `runtime.endpoint` connects an external WebSocket Runtime.
* `box.limits` bounds sessions, processes, workspace scans, retained admission fences, and RPC file size.
* `box.admission` is the Cloud v2 hard-admission policy. OSS defaults to `required: false`; pipelines, Workspace entitlements, and tool calls cannot relax Runtime-owned limits.
* `box.local` controls workspace roots and mount allowlists. In Docker deployments, use an absolute `host_root` that the Box container can mount.
* `box.default_memory_mb` is the default nsjail cgroup limit for each stdio MCP process. Node.js MCP servers usually need more memory than Python servers; too little commonly produces exit code `137`.
### LangBot Space
`space.url`, `models_gateway_api_url`, and `oauth_authorize_url` control Space OAuth/API and MaaS endpoints. The two `disable_*` flags independently disable model service use and telemetry.
## Environment-variable overrides
Convert a nested key to uppercase and join levels with double underscores:
* `API__PORT` → `api.port`
* `WORKSPACE__INVITATIONS__PUBLIC_WEB_URL` → `workspace.invitations.public_web_url`
* `CONCURRENCY__PENDING_QUERIES_PER_WORKSPACE` → `concurrency.pending_queries_per_workspace`
* `DATABASE__POSTGRESQL__POOL_SIZE` → `database.postgresql.pool_size`
* `STORAGE__CLEANUP__MAX_FILES_PER_RUN` → `storage.cleanup.max_files_per_run`
* `PLUGIN__WORKER__MAX_TOTAL_MEMORY_MB` → `plugin.worker.max_total_memory_mb`
* `MCP__STDIO__ENABLED` → `mcp.stdio.enabled`
* `BOX__DEFAULT_MEMORY_MB` → `box.default_memory_mb`
At startup, LangBot applies these overrides and writes the resulting configuration to `data/config.yaml`.
In Docker deployments, set unified `BOX__*` variables on the `langbot` service. LangBot sends Box configuration to `langbot_box` through INIT RPC; variables set directly on `langbot_box` are not read.
# Update LangBot
Source: https://docs.langbot.app/en/deploy/update
Update LangBot to the latest version. Upgrade your self-hosted AI chatbot platform safely with these steps.
## Using Package Manager
Please execute in the directory you deployed LangBot again:
```bash theme={null}
uvx langbot@latest
```
This command will download and start the latest version of LangBot.
## Using Docker (or one-click deployment based on Docker)
Please execute in the directory you cloned LangBot:
```bash theme={null}
git pull
```
Please execute in the `docker/` directory:
```bash theme={null}
docker compose up --force-recreate --pull always -d
```
This command will pull the latest LangBot image and rebuild the container.
4.3.0 version changed the content and location of the `docker-compose.yaml` file, if you updated from a previous version, please [redploy according to the documentation](/en/deploy/langbot/docker).
## Manual Deployment
Download the latest release's `langbot--all.zip` from the Assets section on the [Releases](https://github.com/langbot-app/LangBot/releases) page, extract it to the original deployment directory, and restart.
# AI Assistant Context
Source: https://docs.langbot.app/en/develop/agent-context
A single, copy-paste-the-whole-page context for AI coding assistants: LangBot's directory layout, how components are organized, how they connect at runtime, and how to set up the dev environment.
Copy the **entire content** of this page and paste it to your AI assistant. It will then understand LangBot's overall architecture, how each component is organized, how they connect at runtime, and how to configure the dev environment — without you having to explain it piece by piece.
This page mirrors the `AGENTS.md` (with `CLAUDE.md` as a symlink) at each repository root, and serves as LangBot's "one-page onboarding" for developers and AI assistants.
## Project Overview
LangBot is an open-source, LLM-native instant-messaging bot development platform. It provides an out-of-the-box IM bot development experience with built-in Agent, RAG, MCP and other LLM application capabilities, supports mainstream global IM platforms, and exposes rich APIs for custom development.
LangBot has a comprehensive web frontend — almost every operation can be performed through it.
* **Backend**: Python (`>=3.11,<4.0`), dependencies managed by `uv`; the web framework is Quart (the async flavour of Flask). Both the HTTP API and the pre-built frontend are served by the backend on `http://127.0.0.1:5300`.
* **Frontend**: `web/` is a **Vite + React Router 7 + shadcn/ui + Tailwind CSS** SPA, managed by `pnpm` (note: this is **not** Next.js — the `dev` script is literally `vite`).
* **Plugin system**: The Plugin SDK, CLI (`lbp`), Plugin Runtime, Box (sandbox) Runtime, and the entity/API definitions shared between LangBot and plugins all live in a separate repository: [`langbot-plugin-sdk`](https://github.com/langbot-app/langbot-plugin-sdk). LangBot depends on it via the version-pinned `langbot-plugin` package in `pyproject.toml`.
## Repository Layout
```
LangBot/
├── main.py # Entrypoint shim -> langbot.__main__.main()
├── pyproject.toml # Python project + deps (uv), pins langbot-plugin==
├── src/langbot/
│ ├── __main__.py # Real entrypoint, CLI args (--standalone-runtime / --standalone-box / --debug)
│ ├── pkg/ # Core backend package
│ │ ├── api/ # HTTP API controllers + services (Quart)
│ │ ├── core/ # App bootstrap, stages, task manager
│ │ ├── platform/ # IM platform adapters, bot managers, session managers
│ │ ├── provider/ # LLM providers, requesters, tool providers
│ │ ├── pipeline/ # Pipelines, stages, query pool
│ │ ├── plugin/ # Bridge to the plugin runtime (connector.py / handler.py)
│ │ ├── box/ # Code-sandbox subsystem (Docker / nsjail / E2B backends)
│ │ ├── skill/ # Skill subsystem
│ │ ├── rag/ , vector/ # RAG + vector store
│ │ ├── command/ # Built-in commands
│ │ ├── persistence/ # ORM models + Alembic migrations (SQLite & PostgreSQL)
│ │ ├── storage/ # Object/file storage abstractions
│ │ ├── config/, entity/, discover/, utils/, telemetry/, survey/
│ ├── libs/ # Vendored SDKs (qq_official_api, wecom_api, etc.)
│ └── templates/ # Config/component templates (e.g. templates/config.yaml)
├── web/ # Frontend SPA (Vite + React Router 7 + shadcn + Tailwind)
└── docker/ # docker-compose deployment files
```
## How Components Are Organized
The core backend package `src/langbot/pkg/` is split into loosely-coupled submodules by responsibility:
| Module | Responsibility |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `core` | Application lifecycle: startup stages load config, connect the DB, bring up subsystems in order, and manage background tasks. |
| `platform` | Adapters for each IM platform (Discord, Telegram, QQ, WeCom, Lark, etc.); send/receive messages, manage bots and sessions. |
| `provider` | LLM providers and requesters (OpenAI-compatible and native APIs), plus tool providers callable by the Agent. |
| `pipeline` | The message-processing pipeline: routes one message through trigger, AI processing, output, and safety stages. |
| `plugin` | Bridge between the LangBot main process and the plugin runtime — connect, exchange actions, forward events. |
| `box` | Code-sandbox subsystem providing isolated code execution for skills/tools; selects Docker / nsjail / E2B by availability. |
| `skill` / `rag` / `vector` | Skill, retrieval-augmented generation, and vector-store capabilities. |
| `persistence` | ORM entity definitions and Alembic migrations; a single set of scripts works on both SQLite and PostgreSQL. |
On the plugin side (defined in `langbot-plugin-sdk`, generated by `lbp comp`), components are organized around a single `BasePlugin`. Six component types are currently supported:
* **Command**: user-triggered actions (e.g. `!weather tokyo`).
* **Tool**: functions the LLM calls during Agent execution (e.g. fetch weather, query a database).
* **EventListener**: handlers for events in the message pipeline (e.g. auto-reply, content filtering).
* **KnowledgeEngine**: a custom knowledge-base retrieval/integration implementation used by RAG.
* **Parser**: custom parsing of messages/content.
* **Page**: a custom web page provided by the plugin, embeddable in the LangBot admin panel.
Each plugin runs in its own process, with its lifecycle managed by the plugin runtime (discover → install deps → load → initialize → register components → ready → terminate).
## How Things Connect at Runtime
```
┌──────────────┐ HTTP/API ┌──────────────┐
│ Browser/Web │ ────────────► │ │
│ (Vite SPA) │ :3000/origin │ LangBot │
└──────────────┘ │ backend │
│ (Quart) │
┌───────────────────────────┤ :5300 ├─────────────────────────┐
│ stdio or WebSocket └──────────────┘ stdio or WebSocket │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Plugin │ separate process, │ Box │ code sandbox
│ Runtime │ launches each plugin │ Runtime │ Docker/nsjail/E2B
│ :5400 / :5401│ │ :5410 │
└──────────────┘ └──────────────┘
```
* **Frontend ↔ Backend**: In dev, the frontend runs standalone on `:3000` and reaches the backend `:5300` via `VITE_API_BASE_URL` in `web/.env`; in production the frontend is pre-built into static files served by the backend on the same origin.
* **Backend ↔ Plugin Runtime**:
* When LangBot is **started directly** (not in a container), the backend **spawns** the runtime itself and talks over **stdio** (lightweight/personal use). stdio **cannot auto-reconnect** — after a disconnect you must restart LangBot; a common failure is an **orphan runtime process** from a previous backend still holding `5400`/`5401` — kill it and restart.
* When LangBot runs in a **container**, it connects to a standalone runtime over **WebSocket** (production). Control port defaults to `5400`, debug port to `5401`. Config: `plugin.runtime_ws_url` in `data/config.yaml` (e.g. `ws://langbot_plugin_runtime:5400/control/ws`).
* **Backend ↔ Box Runtime**: The Box subsystem connects to the Box runtime over a control channel (default port `5410`), which executes sandboxed code in Docker / nsjail / E2B. Config (`box:` section of `data/config.yaml`): `box.enabled` (master switch), `box.backend` (`'local'`/`'docker'`/`'nsjail'`/`'e2b'`), `box.runtime.endpoint` (external Box runtime URL, e.g. `ws://127.0.0.1:5410`; empty = local auto-managed). As with the plugin runtime, set that endpoint and start with `--standalone-box` to connect to an external Box runtime.
* The full guide for debugging the runtime, CLI and SDK is in [Debugging Plugin Runtime, CLI, SDK](/en/develop/plugin-runtime); detailed flags and architecture are in the `langbot-plugin-sdk` repo's `AGENTS.md`.
## Development Environment Setup
Full guide: [Development Configuration](/en/develop/dev-config). Summary:
### Backend
```bash theme={null}
pip install uv
uv sync --dev # uv creates a .venv/ for you; point your editor's interpreter at it
uv run main.py # serves API + frontend on http://127.0.0.1:5300
```
On first run the config file is generated at `data/config.yaml`. SQLite is the default (zero setup); PostgreSQL is supported. Migrations run automatically on startup.
### Frontend
Requires Node.js and [pnpm](https://pnpm.io/installation).
```bash theme={null}
cd web
cp .env.example .env # Windows: copy .env.example .env
pnpm install
pnpm dev # http://127.0.0.1:3000 (npm install / npm run dev also work)
```
`pnpm dev` reads `VITE_API_BASE_URL` from `web/.env` so the dev frontend can reach the backend on `:5300`.
### Code Formatting
CI runs lint + format checks. Install the pre-commit hooks so the same checks run locally before each commit:
```bash theme={null}
uv run pre-commit install
```
### Database Migrations
After changing ORM models, generate a migration:
```bash theme={null}
# Run from the project root (requires data/config.yaml)
uv run python -m langbot.pkg.persistence.alembic_runner autogenerate "describe your change"
```
`autogenerate` detects schema changes (add/drop columns and tables, type changes), but **data migrations** (e.g. mutating JSON field contents) must be hand-written into the generated script. `env.py` sets `render_as_batch=True`, so SQLite's ALTER TABLE limits are handled automatically — no per-database branching needed. Migrations execute automatically on startup.
## Development Standards
* LangBot is a global project: **all code comments and docstrings must be in English**, and every user-facing string must support **i18n** (`en_US` + `zh_Hans` at minimum, plus `ja_JP` where the repo already has it).
* LangBot is adopted in both toC and toB scenarios — always consider compatibility and security.
* **Commit message format**: `(): `
* `type`: one of `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, etc.
* `scope`: the affected package/module/file/class.
* `subject`: a concise description of the change.
## Agent-Facing Surfaces (MCP + Skills)
LangBot is built to be **agent-friendly**, with three agent-facing surfaces that
are kept in lockstep with the HTTP API:
1. **MCP server** — `src/langbot/pkg/api/mcp/` exposes a curated subset of the API
as MCP tools at `/mcp` (API-key authenticated, including `api.global_api_key`
from config.yaml). `server.py` defines the tools (calling the service layer
directly); `mount.py` is the ASGI dispatcher.
2. **In-repo skills** — the `skills/` directory is the **single source of truth**
for all LangBot agent skills (plugin dev, core dev, deployment, e2e, MCP
operations). Docs and the landing page link here rather than copying content.
3. **API-key auth** — `api.global_api_key` (config.yaml) authenticates the API and
MCP without a login session; see `docs/API_KEY_AUTH.md`.
> **Maintenance rule (important).** When you add, remove, or change an HTTP API
> endpoint that should be agent-accessible, you MUST update **both** the matching
> MCP tool in `src/langbot/pkg/api/mcp/server.py` **and** the relevant skill under
> `skills/` (especially `skills/skills/langbot-mcp-ops`). API, MCP tools, and
> skills are one system — drift between them is a bug.
## Some Principles
* Keep it simple, stupid.
* Entities should not be multiplied unnecessarily.
* 八荣八耻 (Eight Honors and Eight Shames):
Shame in guessing interfaces; honor in carefully checking them.
Shame in vague execution; honor in seeking confirmation.
Shame in assuming business logic; honor in human confirmation.
Shame in inventing interfaces; honor in reusing existing ones.
Shame in skipping verification; honor in proactive testing.
Shame in breaking architecture; honor in following conventions.
Shame in pretending to understand; honor in honest ignorance.
Shame in blind changes; honor in careful refactoring.
# Component Architecture
Source: https://docs.langbot.app/en/develop/comp-arch
# Development Configuration
Source: https://docs.langbot.app/en/develop/dev-config
LangBot is divided into frontend and backend. The frontend is developed using Vite + React Router + shadcn, and the backend is developed using Quart (an asynchronous version of Flask).
## Backend
The code is located in the `pkg` directory and is started by the `main.py` file in the root directory.
Install dependencies, we use uv to manage dependencies.
```bash theme={null}
pip install uv
uv sync --dev
```
Start the backend
```bash theme={null}
uv run main.py
```
At this point, the configuration file will be automatically generated in the `data/config.yaml` file.
## Frontend
The code is located in the `web` directory and requires Node.js and [pnpm](https://pnpm.io/installation).
Copy `.env.example` to `.env` and fill in the values.
* Usage in Linux and other environments
```bash theme={null}
cp .env.example .env
```
* Usage in Windows environments
```bash theme={null}
copy .env.example .env
```
Install dependencies and start the frontend
```bash theme={null}
pnpm install
pnpm dev
# If pnpm is not installed, you can also use npm to solve dependencies and start
npm install
npm run dev
```
Then according to the output information, visit `http://127.0.0.1:3000` to view the standalone frontend page.
In production environments, the frontend will be precompiled into static files and provided by the backend, and the frontend will automatically access the backend address on the same domain.
## Code Formatting
The repository contains lint and format check workflows, and your code will be automatically checked when you push it. Please configure pre-commit to check your code locally before submitting.
```bash theme={null}
uv run pre-commit install
```
## API Documentation
We will write API documentation in APIFox before developing each interface. Please refer to [API Documentation(Chinese)](https://ok52vhsenr.apifox.cn/).
## Database Migrations
LangBot uses [Alembic](https://alembic.sqlalchemy.org/) to manage database migrations, supporting both SQLite and PostgreSQL seamlessly without database-specific branching.
### Migration File Location
```
src/langbot/pkg/persistence/alembic/versions/
```
### Creating a New Migration
The recommended way is to use **autogenerate**, which compares ORM models against the database schema and generates a migration script:
```bash theme={null}
# Run from the project root (requires data/config.yaml)
uv run python -m langbot.pkg.persistence.alembic_runner autogenerate "describe your changes"
```
The generated file will appear in `src/langbot/pkg/persistence/alembic/versions/`. Review and edit the generated script to confirm the changes are correct before committing.
Autogenerate can automatically detect column additions/removals, table changes, type changes, etc. However, for **data migrations** (e.g., modifying JSON field content), you need to manually add code to the generated script.
You can also create migration files manually, following the naming convention `NNNN_description.py`:
```python theme={null}
"""Describe your migration
Revision ID: 0003_add_bot_description
Revises: 0002_sample
Create Date: 2026-04-08
"""
from alembic import op
import sqlalchemy as sa
revision = '0003_add_bot_description'
down_revision = '0002_sample' # Points to the previous migration's revision
branch_labels = None
depends_on = None
def upgrade() -> None:
# Schema change (works on both SQLite and PostgreSQL)
op.add_column('bots', sa.Column('description', sa.String(512), server_default=''))
def downgrade() -> None:
op.drop_column('bots', 'description')
```
### Migration Patterns
```python theme={null}
def upgrade() -> None:
# Add a column
op.add_column('table_name', sa.Column('new_col', sa.String(255), server_default=''))
# Create a new table
op.create_table(
'new_table',
sa.Column('id', sa.Integer, primary_key=True, autoincrement=True),
sa.Column('name', sa.String(255), nullable=False),
)
```
`env.py` sets `render_as_batch=True`, which makes Alembic automatically handle SQLite's ALTER TABLE limitations (via temporary table rebuild). No need to branch on database type.
```python theme={null}
import json
def upgrade() -> None:
conn = op.get_bind()
rows = conn.execute(sa.text("SELECT uuid, config FROM pipelines")).fetchall()
for row in rows:
config = json.loads(row[1]) if isinstance(row[1], str) else row[1]
config.setdefault('ai', {})['new_key'] = 'default_value'
conn.execute(
sa.text("UPDATE pipelines SET config = :cfg WHERE uuid = :uuid"),
{"cfg": json.dumps(config), "uuid": row[0]}
)
```
### How It Works
Migrations run automatically when LangBot starts — no manual commands needed:
1. On first startup, the baseline version is automatically stamped (marking the existing database)
2. On subsequent startups, all pending migrations are applied (`alembic upgrade head`)
### CI Testing
The repository includes a `test-migrations.yml` workflow that automatically tests migration scripts on both SQLite and PostgreSQL when `persistence/` related files change.
### CLI Tool
```bash theme={null}
# Autogenerate migration (compare ORM models against DB)
uv run python -m langbot.pkg.persistence.alembic_runner autogenerate "description"
# Upgrade database
uv run python -m langbot.pkg.persistence.alembic_runner upgrade
# Show current revision
uv run python -m langbot.pkg.persistence.alembic_runner current
# Stamp revision (without running migrations)
uv run python -m langbot.pkg.persistence.alembic_runner stamp head
```
# Debugging Plugin Runtime, CLI, SDK
Source: https://docs.langbot.app/en/develop/plugin-runtime
Plugin Runtime, CLI, SDK are open sourced at: [https://github.com/langbot-app/langbot-plugin-sdk](https://github.com/langbot-app/langbot-plugin-sdk)
Since LangBot depends on entities defined in langbot-plugin-sdk, we recommend opening VS Code in a new directory, placing both LangBot and langbot-plugin-sdk (git clone [https://github.com/langbot-app/langbot-plugin-sdk](https://github.com/langbot-app/langbot-plugin-sdk)) as subdirectories within it. The directory structure should look like:
```bash theme={null}
langbot-projects
├── LangBot
├── langbot-plugin-sdk
```
Enter the LangBot directory and install dependencies:
```bash theme={null}
cd LangBot
uv sync --dev
```
At this point, uv will automatically create a virtual environment (.venv) for you. If your editor asks whether to use this virtual environment, please select `Yes`.
If the prompt does not appear, please manually set the Python interpreter path to the interpreter in the venv from the bottom-right corner.
Then open the terminal at the bottom of VS Code, which will automatically activate the venv.
Or you can manually activate this virtual environment:
```bash theme={null}
# Please modify the command according to your .venv path
source .venv/bin/activate
```
## Start Plugin Runtime
```bash theme={null}
python -m langbot_plugin.cli.__init__ rt
```
Plugin Runtime accepts the following parameters:
* `--debug-only`: Do not start plugins in the `data/plugins` directory, only allow loading plugins through debug connections.
* `--ws-debug-port`: Debug port to listen on, default is `5401`.
* `--ws-control-port`: Control port to listen on (for LangBot main program connection), default is `5400`.
* `-s`: Use `stdio` to accept control connections. **Use only in production environment**.
* `--skip-deps-check`: To ensure that all plugin dependencies are installed, the Runtime will check and install all installed plugin dependencies on every startup. Use this parameter to disable this check.
### Make LangBot Use Your Locally Modified langbot-plugin-sdk
If you have modified things like message entities or plugin data definitions, you need to update them in the LangBot environment to ensure data format compatibility during runtime.
In a **terminal with the LangBot directory's virtual environment (.venv) activated**, switch to the langbot-plugin-sdk directory and run:
```bash theme={null}
uv pip install .
```
This will install your modified langbot-plugin-sdk into LangBot's environment.
### Make LangBot Connect to This Runtime
Configure `plugin.runtime_ws_url` to `ws://localhost:5400/control/ws` in LangBot's `data/config.yaml`.
```yaml theme={null}
plugin:
runtime_ws_url: ws://localhost:5400/control/ws
```
In a terminal with LangBot's virtual environment activated, start the main program directly with Python and add `--standalone-runtime` (for example, `python main.py --standalone-runtime`). Calling the current virtual environment's Python directly does not resync dependencies, so it does not overwrite the local langbot-plugin-sdk you just installed with the remote version.\
Restart LangBot, and it will connect to this runtime using WebSocket.
`LANGBOT_PLUGIN_RUNTIME_CONTROL_TOKEN` is optional by default. When it is unset on both LangBot and Runtime, the local OSS control connection is established without a token. To protect an exposed port 5400, configure the same high-entropy value of at least 32 characters on both sides. Once the Runtime configures a token, it rejects LangBot clients without the same value; configuring only the LangBot side does not enable authentication on the Runtime.
### Debug a plugin with `lbp run`
Multi-Workspace versions no longer allow a debug plugin to join the Runtime merely by reaching port `5401`. Every Workspace has a separate expiring debug key:
1. Make sure LangBot and Plugin Runtime are running and connected as described above.
2. On the LangBot WebUI Plugins page, open “Debug Info” and copy the debug URL and debug key. This requires resource-management permission in the current Workspace.
3. Add the values to the plugin project's `.env`:
```bash theme={null}
DEBUG_RUNTIME_WS_URL=ws://localhost:5401/plugin/debug/ws
PLUGIN_DEBUG_KEY=
```
4. Start the plugin from its project directory:
```bash theme={null}
python -m langbot_plugin.cli.__init__ run
```
Instead of storing the key in `.env`, you may run `python -m langbot_plugin.cli.__init__ run --plugin-debug-key ''`. Keys are scoped to one Workspace and expire after two hours. Fetch a new key after it expires, the Runtime restarts, or you switch Workspaces. A client that configures only `DEBUG_RUNTIME_WS_URL` is rejected.
## Start Box Runtime in standalone mode
Box Runtime follows the same control-connection rules as Plugin Runtime: a token is optional for OSS standalone development, and the connection works when `LANGBOT_BOX_CONTROL_TOKEN` is unset on both sides:
```bash theme={null}
# Terminal 1: langbot-plugin-sdk directory
python -m langbot_plugin.cli.__init__ box
```
Point LangBot's `data/config.yaml` at the local Box Runtime:
```yaml theme={null}
box:
enabled: true
backend: local
runtime:
endpoint: ws://127.0.0.1:5410
```
```bash theme={null}
# Terminal 2: LangBot directory
python main.py --standalone-runtime --standalone-box
```
To protect an exposed port 5410, set the same high-entropy, non-whitespace value of at least 32 characters before starting each process:
```bash theme={null}
export LANGBOT_BOX_CONTROL_TOKEN=''
```
Once Box Runtime configures a token, it rejects LangBot clients without the same value. Configuring only the LangBot side does not enable authentication on Box Runtime; an explicitly configured value shorter than 32 characters is still rejected by both sides. Do not commit the real value to configuration or Git.
## langbot-plugin-sdk Architecture
This codebase contains the following:
* `langbot_plugin.api`: Plugin-related entities and API definitions.
* `langbot_plugin.assets`: Plugin templates.
* `langbot_plugin.cli`: Plugin development CLI tools.
* `langbot_plugin.entities`: Plugin system-related entities not defined in API.
* `langbot_plugin.runtime`: Plugin runtime and underlying communication (stdio and websocket) implementation.
## `lbp` CLI Tool
The CLI tool provides Runtime startup, plugin initialization, plugin component management, Marketplace interaction, and other functions.
For detailed program entry points, please see [`langbot_plugin.cli.__init__`](https://github.com/langbot-app/langbot-plugin-sdk/blob/main/src/langbot_plugin/cli/__init__.py).
# Community Resources
Source: https://docs.langbot.app/en/insight/community
LangBot community resources: GitHub, Discord, documentation and support for the open-source AI bot platform.
## Simplified Tutorials
> Simplified tutorials: such as video tutorials, simpler text tutorials, suitable for beginners
Collected at: [https://blog.thelazy.top/tags/langbot](https://blog.thelazy.top/tags/langbot)
## 🥰 Community
Join our community on Discord: [](https://discord.gg/wdNEHETs87)
# Data Collection Policy
Source: https://docs.langbot.app/en/insight/data-collection-policy
What data LangBot collects and how it is used
LangBot collects minimal, anonymized telemetry data to help improve the product. This document explains what data is collected, how it is used, and how you can opt out.
## What We Collect
When telemetry is enabled (via LangBot Space integration), the following data is sent **per conversation query**:
| Field | Description |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id` | A randomly generated unique ID for the query (not linked to user identity) |
| `adapter` | The messaging platform adapter in use (e.g., WeChat, QQ, Telegram) — only the adapter type name, not your account info |
| `runner` | The AI runner type (e.g., `local-agent`, `dify-service-api`) |
| `model_name` | The model name used for the query (e.g., `gpt-4o`, `deepseek-chat`) |
| `duration_ms` | How long the query took to process (in milliseconds) |
| `version` | The LangBot version you are running |
| `workspace_uuid` | A stable UUID identifying the Workspace where the query ran. For the default self-hosted Workspace it is aligned with the installation UUID and is not linked to personal identity. |
| `runtime_instance_id` | For managed SaaS query events only, the runtime deployment identifier used to refresh idle-timeout activity. It is not stored in the analytics event record. |
| `pipeline_plugins` | Names of pipeline plugins involved in the query |
| `features` | Anonymous feature-usage counters for the query (see below) |
| `error` | Error traceback if the query failed (no user content included) |
| `timestamp` | When the query occurred (UTC) |
#### The `features` object
To understand which capabilities are actually used, each query event may include the following counters and category names — **never the content** of tool calls, retrieved documents, or executed code:
| Key | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool_calls` | Number of tool invocations during the query, grouped by tool source (`native` / `plugin` / `mcp` / `skill`) |
| `tool_call_rounds` | How many agent tool-call rounds the query took |
| `kb` | Knowledge base usage: number of knowledge bases queried, knowledge engine plugin IDs in use, and the number of retrieved entries — not the retrieved text |
| `sandbox` | Number of sandbox code executions and sandbox errors — not the executed code or its output |
| `activated_skills` | Names of skills activated during the query |
| `mcp_servers` | Names of MCP servers bound to the pipeline (omitted when "all enabled" is used) |
### Workspace heartbeat
In addition to per-query events, LangBot sends one anonymous **Workspace heartbeat** for each active Workspace shortly after startup and then once per day. It describes that Workspace's configuration profile — never its data:
| Field | Description |
| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `deploy_platform` | How LangBot is deployed (`docker`, `linux`, `darwin`, `win32`, ...) |
| `database` | Database kind in use (`sqlite` or `postgresql`) — no connection details |
| `vdb` | Vector database kind in use (e.g., `chroma`, `milvus`, `qdrant`) — no connection details |
| `box` | Sandbox configuration: enabled flag, availability, backend kind (`docker` / `nsjail` / `e2b`), deployment topology — no workspace contents |
| `adapters` | Adapter type names of enabled bots |
| `bot_count`, `pipeline_count`, `mcp_server_count`, `knowledge_base_count`, `plugin_count`, `skill_count` | Object counts only — no names or contents of these resources |
### Surveys
LangBot may occasionally present in-app surveys (e.g., product-market fit surveys). Participation is **optional**. When a survey is submitted, the following is sent:
* Survey ID and your answers
* Your instance ID and LangBot version
* Whether you completed or dismissed the survey
## What We Do NOT Collect
* ❌ **Message content** — We never collect the content of your conversations
* ❌ **User identities** — No usernames, emails, phone numbers, or account IDs from your messaging platforms
* ❌ **API keys or credentials** — Your model provider API keys are never transmitted
* ❌ **IP addresses** — We do not log or store your IP address for analytics purposes
* ❌ **File or media content** — No images, documents, or other media you exchange with the bot
## How We Use the Data
The collected telemetry data is used to:
1. **Understand usage patterns** — Which adapters, runners, and models are most popular
2. **Improve reliability** — Identify common errors and failure modes
3. **Guide development** — Prioritize features and platform support based on real usage
4. **Monitor performance** — Track response times and identify bottlenecks
## Data Storage & Retention
* Telemetry data is sent to **LangBot Space** (`space.langbot.app`)
* Data is stored on servers operated by the LangBot team
* Data is retained for product analytics purposes and may be aggregated for public statistics (e.g., total user count)
## How to Opt Out
You can disable telemetry at any time by setting `disable_telemetry: true` in the `space` section of your `data/config.yaml` configuration file:
```yaml theme={null}
space:
url: "https://space.langbot.app"
disable_telemetry: true
```
When telemetry is disabled:
* No query data will be sent
* No Workspace heartbeats will be sent
* No surveys will be fetched or displayed
* All other LangBot features continue to work normally
## Changes to This Policy
We may update this policy as LangBot evolves. Changes will be documented in our release notes and reflected in this page.
*Last updated: July 2026*
# Features
Source: https://docs.langbot.app/en/insight/features
LangBot is an open-source, production-grade, LLM-native instant messaging bot development platform. Build AI agents for Discord, Telegram, Slack, QQ, WeChat, WeCom, Lark, DingTalk and more — with built-in Agent, RAG knowledge base, MCP, Skills and a code sandbox, plus deep integration with Dify, Coze, n8n and Langflow.
LangBot is an **open-source, production-grade**, LLM-native instant messaging bot development platform. With a single codebase you can bring AI agents to Discord, Telegram, Slack, QQ, WeChat, WeCom, Lark, DingTalk and more — with built-in **Agent, RAG knowledge base, MCP, Skills and a code sandbox**, deep integration with Agent platforms like Dify, Coze, n8n and Langflow, and everything configured and monitored from a browser-based web panel — no hand-written config files.
**Don't want to self-host?** Try [LangBot Cloud](https://space.langbot.app/cloud) — no server required, sign up and start instantly.
## Core Capabilities at a Glance
* **AI Conversation & Agent** — multi-turn dialogue, tool calling (function calling), multimodal (image/audio/file) and streaming output, ready out of the box.
* **RAG Knowledge Base** — a built-in knowledge-base engine (vector retrieval + Rerank re-scoring) so your bot answers from your private knowledge, extensible via plugins to seamlessly connect external knowledge-base services like RAGFlow, Dify and FastGPT.
* **All-Platform Coverage** — one codebase reaches Discord, Telegram, Slack, LINE, KOOK, Matrix, QQ, WeChat, WeCom, Lark, DingTalk and more.
* **Deep Agent Platform Integration** — native connectors for Dify, Coze, n8n, Langflow, Deerflow, Aliyun Bailian, Ant TBox and other orchestration & workflow platforms.
* **Skills & Code Sandbox** — on-demand instruction packs paired with a securely isolated code-execution sandbox, so the agent actually *gets things done*.
* **MCP Protocol** — acts as a client into the Model Context Protocol tool ecosystem, supporting both Stdio and HTTP transports.
* **Web Management Panel** — configure, manage and monitor bots visually in the browser, no manual config editing.
* **Production-grade Plugin System** — hundreds of community plugins on a cross-process, event-driven architecture that's safe and hot-pluggable.
* **Multi-pipeline Architecture** — run multiple pipelines per instance so different bots serve different scenarios, with access control, rate limiting, sensitive-word filtering and comprehensive monitoring.
## Instant Messaging Platform Support
LangBot covers mainstream global IM platforms through a unified adapter layer — develop once, reach everywhere.
| Platform | Status | Remarks |
| ----------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Personal QQ | ✅ | Supported via third-party apps implementing the OneBot v11 protocol; private and group chats |
| QQ Official Bot | ✅ | Channels, private chats, and group chats |
| WeChat | ✅ | Personal WeChat |
| WeChat Official Account | ✅ | |
| WeCom | ✅ | Internal application messages |
| WeCom Intelligent Bot | ✅ | Supports streaming output |
| WeCom Customer Service | ✅ | |
| Lark | ✅ | Supports streaming output |
| DingTalk | ✅ | Supports streaming output |
| Discord | ✅ | |
| Telegram | ✅ | Supports streaming output |
| Slack | ✅ | |
| LINE | ✅ | |
| KOOK | ✅ | |
| Satori | ✅ | [Satori](https://satori.chat/zh-CN/introduction.html) is a universal chat protocol that theoretically supports QQ, Zulip, Miyoushe and 15+ platforms |
| Matrix | ✅ | Reaches Signal, WhatsApp, Messenger, iMessage, Mattermost, Google Chat, IRC, XMPP, Zulip and more via bridges |
| Email | ✅ | Use email as a conversation channel |
| Web Page Bot | ✅ | Embeddable chat widget for web pages |
| WhatsApp | 🚧 | In development (also usable via Matrix bridge) |
🚧: In development
For fine-grained, per-platform feature support (sending/receiving images, audio, files, rich text, streaming responses, etc.), see the [platform features document](/en/insight/platform-features).
## Model and Agent Platform Support
LangBot uses **LiteLLM as a unified model request backend**, so beyond the built-in providers below you can also connect any OpenAI / Anthropic API-format or LiteLLM-supported model.
### LLM, Aggregation & GPU Platforms
| Provider/Service | Status | Remarks |
| ----------------------------------------------------------------------------------- | ------ | ------------------------------------ |
| [OpenAI](https://platform.openai.com/) | ✅ | Works with any OpenAI-format model |
| [Anthropic](https://www.anthropic.com/) | ✅ | |
| [DeepSeek](https://www.deepseek.com/) | ✅ | |
| [Google Gemini](https://aistudio.google.com/prompts/new_chat) | ✅ | |
| [xAI](https://x.ai/) | ✅ | |
| [Moonshot](https://www.moonshot.cn/) | ✅ | |
| [Zhipu AI](https://open.bigmodel.cn/) | ✅ | |
| [Mistral](https://mistral.ai/) | ✅ | |
| [Groq](https://groq.com/) | ✅ | |
| [OpenRouter](https://openrouter.ai/) | ✅ | Global LLM gateway |
| [Ollama](https://ollama.com/) | ✅ | Local LLM running platform |
| [LMStudio](https://lmstudio.ai/) | ✅ | Local LLM running platform |
| [SiliconFlow](https://siliconflow.cn/) | ✅ | LLM gateway (MaaS) |
| [Aliyun Bailian](https://bailian.console.aliyun.com/) | ✅ | LLM gateway (MaaS) / Agent platform |
| [Volc Engine Ark](https://console.volcengine.com/ark) | ✅ | LLM gateway (MaaS) / Agent platform |
| [ModelScope](https://modelscope.cn/docs/model-service/API-Inference/intro) | ✅ | LLM gateway (MaaS) |
| [GiteeAI](https://ai.gitee.com/) | ✅ | LLM interface gateway (MaaS) |
| [ShengSuanYun](https://www.shengsuanyun.com/?from=CH_KYIPP758) | ✅ | Global LLM aggregation (recommended) |
| [CompShare](https://www.compshare.cn/?ytag=GPU_YY-gh_langbot) | ✅ | LLM and GPU resource platform |
| [PPIO](https://ppio.com/user/register?invited_by=QJKFYD\&utm_source=github_langbot) | ✅ | LLM and GPU resource platform |
| [302.AI](https://share.302ai.cn/SuTG99) | ✅ | LLM gateway (MaaS) |
| [Jiekou AI](https://jiekou.ai/) | ✅ | LLM gateway (MaaS) |
| [TokenPony](https://www.tokenpony.cn/453z1) | ✅ | LLM gateway (MaaS) |
| [Qiniu](https://www.qiniu.com/ai/agent) | ✅ | LLM gateway (MaaS) |
### Agent Platforms
Plug your existing workflows or agents straight into IM platforms — no need to rewrite your business logic.
| Platform | Status | Remarks |
| ---------------------------------------- | ------ | ----------------------------- |
| [Dify](https://dify.ai) | ✅ | Agent platform |
| [Coze](https://coze.cn) | ✅ | Agent orchestration platform |
| [n8n](https://n8n.io/) | ✅ | Workflow automation platform |
| [Langflow](https://langflow.org/) | ✅ | Visual orchestration platform |
| [Deerflow](https://deerflow.tech) | ✅ | Deep research framework |
| [Weknora](https://weknora.weixin.qq.com) | ✅ | WeChat knowledge-base Q\&A |
| [Ant TBox](https://tbox.cn/) | ✅ | Agent platform |
### Rerank Models
| Provider | Status | Remarks |
| ---------------------------------- | ------ | ------------- |
| [Cohere](https://cohere.com/) | ✅ | Rerank models |
| [Jina](https://jina.ai/) | ✅ | Rerank models |
| [Voyage AI](https://voyageai.com/) | ✅ | Rerank models |
## LLM Application Paradigm Implementation
LangBot turns mainstream LLM application paradigms into out-of-the-box capabilities, covering the full path from "can chat" to "can get work done."
| Paradigm | Status | Notes |
| -------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Agent | ✅ | Built-in agent with tool calling, multi-turn reasoning, multimodal and streaming output |
| RAG (Knowledge Base) | ✅ | Built-in knowledge-base engine (default Chroma vector store + Rerank re-scoring), pluggable into external knowledge bases like RAGFlow, Dify and FastGPT |
| MCP | ✅ | Acts as an MCP client into the tool ecosystem; supports Stdio and HTTP |
| Skills | ✅ | On-demand instruction packs, pulled into context only when matched — zero extra overhead |
| Code Sandbox | ✅ | Securely isolated code-execution environment with Docker / nsjail / E2B backends |
* **Skills**: package a professional workflow, template or script into an on-demand instruction pack — the agent pulls it into context only when the user's request matches, keeping the System Prompt clean. See the [Skills doc](/en/usage/skills/readme).
* **Code Sandbox**: gives the agent securely isolated code-execution capability, automatically picking the best of the Docker / nsjail / E2B backends — the runtime foundation for Skills and native tools. See the [Sandbox doc](/en/usage/sandbox/readme).
## System Extensibility
LangBot offers a complete extension surface for secondary development and enterprise integration.
* **HTTP Service API**: access LangBot's entities through HTTP interfaces with API-key authentication, for easy integration with existing systems.
* **Outgoing Webhook**: push LangBot's internal events to external systems in real time, enabling event-driven automation.
* **Production-grade Plugin System**: built on cross-process communication and async technology, supporting various component extensions — safe, stable and hot-pluggable; hundreds of community plugins install in one click from the [extension marketplace](https://space.langbot.app).
* **Web Management Panel**: do all your bot, model, pipeline, knowledge-base and plugin configuration and monitoring visually.
## Deployment Options
LangBot supports everything from a one-line command to a Kubernetes cluster:
* **LangBot Cloud (recommended)**: [a hosted, deploy-free, out-of-the-box version](https://space.langbot.app/cloud).
* **One-line start**: `uvx langbot`, then open `http://localhost:5300`.
* **Docker Compose**, **aaPanel**, **Kubernetes**, **one-click cloud deploy on Zeabur / Railway**, and more.
See the [deployment docs](/en/deploy/langbot/kubernetes) and [quick start](/en/insight/guide).
## Case Screenshots
# Welcome to LangBot Documentation
Source: https://docs.langbot.app/en/insight/guide
LangBot is an open-source LLM bot platform connecting Discord, Telegram, WeChat, Slack and more to GPT, Claude, Gemini, DeepSeek and Dify. Start here.
LangBot is an **open-source, production-grade**, LLM-native instant messaging bot platform. It connects large language models — OpenAI, Anthropic, Gemini, DeepSeek and more — and Agent platforms like Dify, Coze and n8n to mainstream messaging platforms including Discord, Telegram, Slack, QQ, WeChat, WeCom, Lark and DingTalk. With built-in Agent, RAG knowledge base, MCP, Skills and a code sandbox, plus an out-of-the-box web management panel, you can get your AI bot online in minutes — no hand-written config files required.
## What You Can Build
* **AI customer service / enterprise assistant**: deploy a knowledge-base-backed AI agent to Slack, Discord, Lark, WeCom or DingTalk to answer customer and employee questions automatically.
* **Community operations**: provide AI-powered Q\&A, content moderation and interaction in Discord, Telegram and QQ groups.
* **Workflow integration**: plug your existing Dify / n8n / Coze workflows straight into chat platforms — no need to rewrite business logic.
* **Multi-platform reach**: one bot covers every platform, managed centrally from a unified panel.
## Documentation Structure
The documentation is divided into the following sections:
* Deployment and Usage (Must Read): Detailed steps for deploying and using LangBot
* Plugins: Information related to plugin usage and development
* Practice: Many excellent use cases for LangBot
* Development: Information related to participating in LangBot development
## Quick Start Guide
**Don't want to self-host?** Try [LangBot Cloud](https://space.langbot.app/cloud) — get started instantly with zero infrastructure. Just sign up, pay, and go.
> Two ways to get started quickly:
>
> * Use the simplified tutorial from community resources (see left sidebar)
> * Read the documentation to deploy it yourself (method described below)
### 1. Deploy LangBot
Read the **Deploy LangBot** section on the left and choose a method to deploy LangBot
After deployment, open `http://127.0.0.1:5300` or `http://your-server-public-ip:5300`
Complete the initialization by entering an email and password, and make sure to save them
### 2. Configure Bots
Check [Configure Bots](/en/usage/platforms/readme) in the left sidebar
### 3. Configure Models
Check [Configure Dialogue Models](/en/usage/models/readme) in the left sidebar
After completing the above two steps, you can start using LangBot. If you need to configure more features, you can set them in the [Pipeline](/en/usage/pipelines/readme) section
# Platform Feature Support
Source: https://docs.langbot.app/en/insight/platform-features
Compare LangBot feature support across messaging platforms: Discord, Telegram, WeChat, Slack, QQ, Lark, DingTalk and more.
The following is a list of features we have implemented and tested for each platform. If you encounter unexpected behavior, please feel free to report it to [GitHub Issues](https://github.com/langbot-app/LangBot/issues) or directly submit a Pull Request to help fix it.
## OneBot v11
### Receive
| Feature | Status | Remarks |
| ----------------------------------- | ------ | ----------------------- |
| Receive private messages | ✅ | |
| Receive group mention messages | ✅ | |
| Receive group regular messages | ✅ | |
| Receive image messages | ✅ | |
| Receive voice messages | ✅ | |
| Receive file messages | ✅ | |
| Receive rich text messages | ✅ | Text and image combined |
| Get private message event user name | ✅ | |
| Get group message event group name | ✅ | |
| Get group message event sender name | ✅ | |
### Send
| Feature | Status | Remarks |
| ----------------------------- | ------ | ----------------------- |
| Reply to private messages | ✅ | |
| Reply to group messages | ✅ | |
| Reply with image messages | ✅ | |
| Reply with voice messages | ✅ | |
| Reply with file messages | ✅ | |
| Reply with rich text messages | ✅ | Text and image combined |
| Send proactive messages | ✅ | |
| Send streaming response | ❌ | |
## QQ Official Bot
## WeCom (Internal Application)
### Receive
| Feature | Status | Remarks |
| ----------------------------------- | ------ | ------------------------------------------------------------ |
| Receive private messages | ✅ | |
| Receive group mention messages | ❌ | WeCom internal application does not support adding to groups |
| Receive group regular messages | ❌ | |
| Receive image messages | ✅ | |
| Receive voice messages | ✅ | |
| Receive file messages | ✅ | |
| Receive rich text messages | ❌ | |
| Get private message event user name | ✅ | |
| Get group message event group name | ❌ | |
| Get group message event sender name | ❌ | |
### Send
| Feature | Status | Remarks |
| ----------------------------- | ------ | ------- |
| Reply to private messages | ✅ | |
| Reply to group messages | ❌ | |
| Reply with image messages | ✅ | |
| Reply with voice messages | ✅ | |
| Reply with file messages | ✅ | |
| Reply with rich text messages | ❌ | |
| Send proactive messages | ✅ | |
| Send streaming response | ❌ | |
## Satori
### Receive
| Feature | Status | Remarks |
| ----------------------------------- | ------ | ----------------------- |
| Receive private messages | ✅ | |
| Receive group mention messages | ✅ | |
| Receive group regular messages | ✅ | |
| Receive image messages | ✅ | |
| Receive voice messages | ✅ | |
| Receive file messages | ✅ | |
| Receive rich text messages | ✅ | Text and image combined |
| Get private message event user name | ✅ | |
| Get group message event group name | ✅ | |
| Get group message event sender name | ✅ | |
### Send
| Feature | Status | Remarks |
| ----------------------------- | ------ | ----------------------- |
| Reply to private messages | ✅ | |
| Reply to group messages | ✅ | |
| Reply with image messages | ✅ | |
| Reply with voice messages | ✅ | |
| Reply with file messages | ✅ | |
| Reply with rich text messages | ✅ | Text and image combined |
| Send proactive messages | ✅ | |
| Send streaming response | ❌ | |
## WeCom (Customer Service)
## WeCom Intelligent Bot
## WeChat Official Account
## Lark
### Receive
| Feature | Status | Remarks |
| ----------------------------------- | ------ | ---------------------------------------------------------- |
| Receive private messages | ✅ | |
| Receive group mention messages | ✅ | |
| Receive group regular messages | ❌ | Lark only supports receiving messages that mention the bot |
| Receive image messages | ✅ | |
| Receive voice messages | ❌ | Lark does not have voice messages |
| Receive file messages | ✅ | |
| Receive rich text messages | ✅ | Text and image combined |
| Get private message event user name | ❌ | Lark does not support getting user name |
| Get group message event group name | ❌ | Lark does not support getting group name |
| Get group message event sender name | ❌ | Lark does not support getting sender name |
### Send
| Feature | Status | Remarks |
| ----------------------------- | ------ | ------- |
| Reply to private messages | ✅ | |
| Reply to group messages | ✅ | |
| Reply with image messages | ✅ | |
| Reply with voice messages | ❌ | |
| Reply with file messages | ❌ | |
| Reply with rich text messages | ❌ | |
| Send proactive messages | ✅ | |
| Send streaming response | ✅ | |
## DingTalk
### Receive
| Feature | Status | Remarks |
| ----------------------------------- | ------ | -------------------------------------------------------------- |
| Receive private messages | ✅ | |
| Receive group mention messages | ✅ | |
| Receive group regular messages | ❌ | DingTalk only supports receiving messages that mention the bot |
| Receive image messages | ✅ | |
| Receive voice messages | ✅ | |
| Receive file messages | ✅ | |
| Receive rich text messages | ✅ | Text and image combined |
| Get private message event user name | ✅ | |
| Get group message event group name | ✅ | |
| Get group message event sender name | ✅ | |
### Send
| Feature | Status | Remarks |
| ----------------------------- | ------ | ----------------------- |
| Reply to private messages | ✅ | |
| Reply to group messages | ✅ | |
| Reply with image messages | ✅ | |
| Reply with voice messages | ❌ | |
| Reply with file messages | ❌ | |
| Reply with rich text messages | ✅ | Text and image combined |
| Send proactive messages | ✅ | |
| Send streaming response | ✅ | |
## KOOK
P.S. Sending voice and file messages has errors not fixed yet
### Receive
| Feature | Status | Remarks |
| ----------------------------------- | ------ | ----------------------- |
| Receive private messages | ✅ | |
| Receive group mention messages | ✅ | |
| Receive group regular messages | ✅ | |
| Receive image messages | ✅ | |
| Receive voice messages | ✅ | |
| Receive file messages | ✅ | |
| Receive rich text messages | ✅ | Text and image combined |
| Get private message event user name | ✅ | |
| Get group message event group name | ✅ | |
| Get group message event sender name | ✅ | |
### Send
| Feature | Status | Remarks |
| ----------------------------- | ------ | -------------------------------------------------- |
| Reply to private messages | ✅ | |
| Reply to group messages | ✅ | |
| Reply with image messages | ✅ | |
| Reply with voice messages | ❌ | KOOK does not support replying with voice messages |
| Reply with file messages | ❌ | |
| Reply with rich text messages | ✅ | Text and image combined |
| Send proactive messages | ✅ | |
| Send streaming response | ❌ | |
## Discord
### Receive
| Feature | Status | Remarks |
| ----------------------------------- | ------ | ----------------------- |
| Receive private messages | ✅ | |
| Receive group mention messages | ✅ | |
| Receive group regular messages | ✅ | |
| Receive image messages | ✅ | |
| Receive voice messages | ❌ | |
| Receive file messages | ✅ | |
| Receive rich text messages | ✅ | Text and image combined |
| Get private message event user name | ✅ | |
| Get group message event group name | ✅ | |
| Get group message event sender name | ✅ | |
### Send
| Feature | Status | Remarks |
| ----------------------------- | ------ | ----------------------- |
| Reply to private messages | ✅ | |
| Reply to group messages | ✅ | |
| Reply with image messages | ✅ | Sent as file attachment |
| Reply with voice messages | ✅ | Sent as file attachment |
| Reply with file messages | ✅ | |
| Reply with rich text messages | ✅ | Text and file combined |
| Send proactive messages | ✅ | |
| Send streaming response | ❌ | |
## Telegram
P.S. Not tested
### Receive
| Feature | Status | Remarks |
| ----------------------------------- | ------ | ------- |
| Receive private messages | ✅ | |
| Receive group mention messages | ✅ | |
| Receive group regular messages | ✅ | |
| Receive image messages | ✅ | |
| Receive voice messages | ❌ | |
| Receive file messages | ✅ | |
| Receive rich text messages | ✅ | |
| Get private message event user name | ✅ | |
| Get group message event group name | ✅ | |
| Get group message event sender name | ✅ | |
### Send
| Feature | Status | Remarks |
| ----------------------------- | ------ | ------- |
| Reply to private messages | ✅ | |
| Reply to group messages | ✅ | |
| Reply with image messages | ✅ | |
| Reply with voice messages | ✅ | |
| Reply with file messages | ✅ | |
| Reply with rich text messages | ✅ | |
| Send proactive messages | ✅ | |
| Send streaming response | ❌ | |
## Slack
## LINE
# Troubleshooting
Source: https://docs.langbot.app/en/insight/troubleshooting
Common LangBot local deployment and runtime issues, with symptoms, causes, and fixes. Currently covers Docker deployment issues.
This page collects practical troubleshooting paths for common issues. It currently covers Docker deployment problems. More sections for models, bot adapters, MCP, and Skills can be added later.
The commands below assume you are running them from `LangBot/docker`.
## Docker Deployment
### Preflight Checks
If you need the sandbox, stdio MCP hosting, or Skill add/edit features, start LangBot with the `all` profile:
```bash theme={null}
docker compose --profile all up -d
```
Check service status:
```bash theme={null}
docker compose --profile all ps
```
You should see:
* `langbot`: WebUI and backend service, exposing `5300` and `2280-2285` by default.
* `langbot_plugin_runtime`: plugin runtime, exposing `5401` by default.
* `langbot_box`: Box Runtime control plane, used to create sandbox containers.
If you only run `docker compose up`, `langbot_box` is not started. stdio MCP, Skill add/edit, and some sandbox-dependent features will be unavailable.
### Port Conflicts
#### Symptoms
* `docker compose up` fails with a port binding error.
* The WebUI cannot be opened at `http://127.0.0.1:5300`.
* Logs contain `port is already allocated` or `bind: address already in use`.
#### Fix
Find the process using the port. Linux/macOS:
```bash theme={null}
lsof -i :5300
lsof -i :5401
```
Windows PowerShell:
```powershell theme={null}
Get-NetTCPConnection -LocalPort 5300,5401 | Select-Object LocalPort,OwningProcess
Get-Process -Id
```
If it is an old LangBot, plugin runtime, or test process, stop it and start LangBot again:
```bash theme={null}
docker compose --profile all up -d
```
If the process cannot be stopped, change the matching port mapping in `docker-compose.yaml`.
### Box Runtime Is Not Available
#### Symptoms
* The UI or logs show `No sandbox backend (Docker/nsjail/E2B) is ready`.
* stdio MCP servers are unavailable.
* Skills cannot be installed, activated, or edited.
#### Common Causes
* LangBot was not started with `--profile all` or `--profile box`.
* `langbot_box` is not running.
* Docker Desktop or the current user cannot access Docker.
* On Linux, the current user does not have permission to access the Docker socket.
#### Fix
Make sure `langbot_box` is running:
```bash theme={null}
docker compose --profile all ps
docker logs langbot_box --tail 100
```
Make sure Docker itself works:
```bash theme={null}
docker info
```
On Linux, add the current user to the `docker` group if needed, then log in again:
```bash theme={null}
sudo usermod -aG docker $USER
newgrp docker
docker info
```
Then recreate the containers:
```bash theme={null}
docker compose --profile all up -d --force-recreate
```
### Box Root Mount Fails
#### Symptoms
* Logs contain `host_path is outside allowed_mount_roots`.
* Logs contain `host_path must point to an existing directory on the host`.
* On Docker Desktop, the Box root is resolved to `/run/desktop/...` and is rejected.
* stdio MCP or Skill sandbox containers cannot be created.
#### Cause
`langbot_box` creates sandbox containers through the host Docker socket. Those sandbox containers see host paths, so the Box root path must be identical on the host and inside the container, and it must be under `allowed_mount_roots`.
#### Fix
Set an absolute path that Docker can mount and that Box security checks can accept in `LangBot/docker/.env`:
```env theme={null}
LANGBOT_BOX_ROOT=/var/lib/langbot/box
```
On Windows Docker Desktop, use a path visible to the Docker VM, for example:
```env theme={null}
LANGBOT_BOX_ROOT=/host_mnt/c/Users//code/projects/langbot/LangBot/docker/data/box
```
Make sure the directory exists, then recreate the containers:
```bash theme={null}
docker compose --profile all up -d --force-recreate
```
If you previously edited `docker/data/config.yaml` manually, check that it points to the same root:
```yaml theme={null}
box:
local:
host_root: /host_mnt/c/Users//code/projects/langbot/LangBot/docker/data/box
allowed_mount_roots:
- /host_mnt/c/Users//code/projects/langbot/LangBot/docker/data/box
```
### Many stdio MCP Servers Fail or Stay Connecting
#### Symptoms
* MCP status stays at `connecting` or becomes `error`.
* `docker logs langbot_box` contains `Cannot fork`.
* After installing many stdio MCP servers, previously working MCP servers also fail to start.
#### Cause
stdio MCP servers run inside the Box sandbox. The default Box profile has a relatively small PID limit. Starting many `npx` or `uvx` based MCP servers at the same time can hit that process limit.
#### Fix
Use the larger built-in Box profile in `docker/data/config.yaml`:
```yaml theme={null}
box:
local:
profile: network_extended
```
Then stop LangBot and Box, remove the old shared sandbox, and recreate the containers.
Linux/macOS:
```bash theme={null}
docker compose --profile all stop langbot langbot_box
docker rm -f $(docker ps -aq --filter "name=langbot-box-mcp-shared")
docker compose --profile all up -d --force-recreate
```
Windows PowerShell:
```powershell theme={null}
docker compose --profile all stop langbot langbot_box
docker ps -a --filter "name=langbot-box-mcp-shared" --format "{{.Names}}" | ForEach-Object { docker rm -f $_ }
docker compose --profile all up -d --force-recreate
```
After the MCP servers finish cold starting, check the MCP management page. Target servers should show `connected` and a non-zero tool count.
### MCP Is Installed but Has No Tools or Cannot Connect
#### Symptoms
* The MCP server is listed, but its status is `error` or stays at `connecting`.
* Tool count is `0`.
* Remote MCP logs show `401 Unauthorized`, connection timeout, or handshake failure.
#### Common Causes
* The remote MCP requires an API key or OAuth authorization that is not configured.
* The MCP package is cold starting through `npx` or `uvx`, and dependency download takes time.
* The current Docker network cannot reach the MCP service or package registry.
* The stdio MCP sandbox does not have enough resources.
#### Fix
Prefer MCP servers from LangBot Space that do not require credentials, or make sure credentials are configured correctly. For credentialed MCP servers, fill in the required environment variables, headers, or URL parameters during installation.
Check runtime logs:
```bash theme={null}
docker logs langbot --tail 200
docker logs langbot_box --tail 200
```
If a server cannot connect in the current environment, disable or delete it from the MCP management page so it does not confuse later checks.
### Image Tag or Registry Is Inconsistent
#### Symptoms
* `docker compose ps` still shows an old image.
* Image registry or tag changes do not take effect after restart.
* `langbot`, `langbot_plugin_runtime`, and `langbot_box` use different image tags.
#### Fix
Keep the three LangBot service image tags consistent, then pull and recreate:
```bash theme={null}
docker pull rockchin/langbot:latest
docker compose --profile all up -d --force-recreate
```
If you intentionally deploy a non-default tag, update all three image entries in `docker-compose.yaml`: `langbot`, `langbot_plugin_runtime`, and `langbot_box`.
### Post-Fix Verification
After applying a fix, run a full check:
```bash theme={null}
docker compose --profile all ps
docker inspect langbot langbot_plugin_runtime langbot_box --format '{{.Name}} {{.Config.Image}} {{.State.Status}}'
```
Check the WebUI:
```bash theme={null}
curl -I http://127.0.0.1:5300/
```
Then confirm in the UI:
* Login works.
* Box / sandbox features are available.
* Target MCP servers show `connected`.
* stdio MCP servers show the expected tool count.
# System Compatibility
Source: https://docs.langbot.app/en/plugin/compatibility
LangBot's plugin system utilizes mechanisms such as `Asyncio` and `Subprocess`, which work well on Unix-like systems (such as Linux, macOS), but have some issues on Windows, preventing plugins from running properly.
Python's asynchronous event loop on Windows defaults to using `ProactorEventLoop`, but this event loop lacks support for Stdio, while LangBot in non-Docker environments relies on Stdio to communicate with Plugin Runtime, and Plugin Runtime also depends on Stdio to communicate with plugins.
If we switch to using `SelectorEventLoop`, it would prevent Plugin Runtime and plugins from starting properly, as `SelectorEventLoop` does not support `Subprocess`.
For detailed information about these issues, please refer to: [Python Official Documentation](https://docs.python.org/3.13/library/asyncio-platforms.html)
## Solution
Unlike the tight coupling between subprocess management and Stdio communication on Unix-like systems, LangBot and the Plugin Runtime automatically switch to a hybrid subprocess + WebSocket communication strategy on Windows. LangBot still launches the Plugin Runtime via a subprocess, but communicates with it over WebSocket; similarly, the Plugin Runtime starts each installed plugin via subprocesses while communicating with them through WebSocket.
This behavior is automatically selected based on the OS, so no manual configuration is required.
# Plugin Common APIs
Source: https://docs.langbot.app/en/plugin/dev/apis/common
LangBot provides a series of APIs for plugins to operate various LangBot modules and control message contexts.
## Request API
Operations for the currently processing user request (message). Only available in `EventListener` and `Command` components. Access methods:
* In event handler methods of `EventListener`: Internal methods of `event_context: context.EventContext` object
* In subcommand handler methods of `Command`: Internal methods of `context: ExecuteContext` object
### Direct Reply Message
Directly reply with a message chain to the session where the current request is located.
For message chain construction methods, please refer to [Message Platform Entities](/en/plugin/dev/apis/messages).
```python theme={null}
async def reply(
self, message_chain: platform_message.MessageChain, quote_origin: bool = False
):
"""Reply to the message request
Args:
message_chain (platform.types.MessageChain): LangBot message chain
quote_origin (bool): Whether to quote the original message
"""
# Usage example
await event_context.reply(
platform_message.MessageChain([
platform_message.Plain(text="Hello, world!"),
]),
)
```
### Get Bot UUID
Get the UUID of the bot that originated the current request.
```python theme={null}
async def get_bot_uuid(self) -> str:
"""Get the bot uuid"""
# Usage example
bot_uuid = await event_context.get_bot_uuid()
```
### Set Request Variables
Some information in a single request is stored in request variables. When using external Agent platforms like Dify, [these variables are explicitly passed to Agent platforms](/en/deploy/pipelines/readme.html#request-variables).
```python theme={null}
async def set_query_var(self, key: str, value: Any):
"""Set a query variable"""
# Usage example
await event_context.set_query_var("key", "value")
```
### Get Request Variables
Get a single request variable.
```python theme={null}
async def get_query_var(self, key: str) -> Any:
"""Get a query variable"""
# Usage example
query_var = await event_context.get_query_var("key")
```
### Get All Request Variables
```python theme={null}
async def get_query_vars(self) -> dict[str, Any]:
"""Get all query variables"""
# Usage example
query_vars = await event_context.get_query_vars()
```
### List Pipeline Knowledge Bases
Get the list of knowledge bases configured for the pipeline used by the current request.
```python theme={null}
async def list_pipeline_knowledge_bases(self) -> list[dict[str, Any]]:
"""List knowledge bases configured for the current pipeline"""
# Usage example
knowledge_bases = await event_context.list_pipeline_knowledge_bases()
# Return example
[
{
"uuid": "kb_uuid",
"name": "Product Docs",
"description": "Internal product documentation",
}
]
```
Only returns knowledge bases bound in the `Local Agent` configuration of the pipeline corresponding to the current request. If the current pipeline has no knowledge bases configured, or does not use `Local Agent`, an empty list is returned.
## LangBot API
These APIs can be called in any plugin component. Access methods:
* In the plugin root directory `main.py`: Internal methods of the `self` object, these APIs are all provided by the plugin class parent class `BasePlugin`.
* In any plugin component class: Internal methods of the `self.plugin` object.
### Get Plugin Configuration
Plugin configuration format can be written in `manifest.yaml`, and users need to fill it out according to the plugin configuration format in LangBot's plugin management. Plugin code can then call this API to get plugin configuration information.
```python theme={null}
def get_config(self) -> dict[str, typing.Any]:
"""Get the config of the plugin."""
# Usage example
config = self.plugin.get_config()
```
### Get LangBot Version
Get the LangBot version number, returned as a string in format `v..`.
```python theme={null}
async def get_langbot_version(self) -> str:
"""Get the langbot version"""
# Usage example
langbot_version = await self.plugin.get_langbot_version()
```
### Get Configured Bot List
Returns a list of all bot UUIDs.
```python theme={null}
async def get_bots(self) -> list[str]:
"""Get all bots"""
# Usage example
bots = await self.plugin.get_bots()
```
### Get Bot Information
Get bot information.
```python theme={null}
async def get_bot_info(self, bot_uuid: str) -> dict[str, Any]:
"""Get a bot info"""
# Usage example
bot_info = await self.plugin.get_bot_info("de639861-be05-4018-859b-c2e2d3e0d603")
# Return example
{
"uuid": "de639861-be05-4018-859b-c2e2d3e0d603",
"name": "aiocqhttp",
"description": "Migrated from LangBot v3",
"adapter": "aiocqhttp",
"enable": true,
"use_pipeline_name": "ChatPipeline",
"use_pipeline_uuid": "c30a1dca-e91c-452b-83ec-84d635a30028",
"created_at": "2025-05-10T13:53:08",
"updated_at": "2025-08-12T11:27:30",
"adapter_runtime_values": { # Present if the bot is currently running
"bot_account_id": 960164003 # Bot account ID
}
}
```
### Send Proactive Message
Send proactive messages through bot UUID and target session ID.
For message chain construction methods, please refer to [Message Platform Entities](/en/plugin/dev/apis/messages).
```python theme={null}
async def send_message(
self,
bot_uuid: str,
target_type: str,
target_id: str,
message_chain: platform_message.MessageChain,
) -> None:
"""Send a message to a session"""
# Usage example
await self.plugin.send_message(
bot_uuid="de639861-be05-4018-859b-c2e2d3e0d603",
target_type="person",
target_id="1010553892",
message_chain=platform_message.MessageChain([platform_message.Plain(text="Hello, world!")]),
)
```
### Get Configured LLM Model List
Returns a list of UUIDs for all configured LLM models.
```python theme={null}
async def get_llm_models(self) -> list[str]:
"""Get all LLM models"""
# Usage example
llm_models = await self.plugin.get_llm_models()
```
### Invoke LLM Model
Invoke an LLM model, returns an LLM message. Non-streaming.
```python theme={null}
async def invoke_llm(
self,
llm_model_uuid: str,
messages: list[provider_message.Message],
funcs: list[resource_tool.LLMTool] = [],
extra_args: dict[str, Any] = {},
) -> provider_message.Message:
"""Invoke an LLM model"""
# Usage example
llm_message = await self.plugin.invoke_llm(
llm_model_uuid="llm_model_uuid",
messages=[provider_message.Message(role="user", content="Hello, world!")],
funcs=[],
extra_args={},
)
```
### List Available Parsers
List Parser plugins currently available on the host, optionally filtered by MIME type.
```python theme={null}
async def list_parsers(self, mime_type: str | None = None) -> list[dict[str, Any]]:
"""List available Parser plugins"""
# Usage example
parsers = await self.plugin.list_parsers(mime_type="application/pdf")
# Each item includes plugin_id, plugin_author, plugin_name, name, description, supported_mime_types
```
### List All Available Tools
List all available tools in the current LangBot instance (including plugin tools and MCP tools).
```python theme={null}
async def list_tools(self) -> list[dict[str, Any]]:
"""List all available tools
Returns:
A list of tool dicts, each containing:
- name: Tool name
- label: Display label (i18n)
- description: Tool description (i18n)
- icon: Tool icon
- spec: Tool specification (includes llm_prompt and parameters)
"""
# Example
tools = await self.plugin.list_tools()
for tool in tools:
print(f"Tool: {tool['name']}")
```
### Get Tool Detail
Get detailed information about a specific tool.
```python theme={null}
async def get_tool_detail(self, tool_name: str) -> dict[str, Any]:
"""Get detailed information about a specific tool
Args:
tool_name: Tool name (metadata.name, e.g. "get_weather_alerts")
Returns:
Tool detail dict containing name, label, description, spec (with parameters and llm_prompt)
"""
# Example
detail = await self.plugin.get_tool_detail("get_weather_alerts")
print(detail['spec']['parameters'])
```
### Call Tool
Call a specific tool.
```python theme={null}
async def call_tool(
self,
tool_name: str,
parameters: dict[str, Any],
session: dict[str, Any],
query_id: int,
) -> dict[str, Any]:
"""Call a specific tool
Args:
tool_name: Tool name (metadata.name)
parameters: Tool parameters
session: Session info
query_id: Query ID
Returns:
Tool response dict
"""
# Example
result = await self.plugin.call_tool(
tool_name="get_weather_alerts",
parameters={"state": "CA"},
session={},
query_id=0,
)
print(result)
```
Tool names use `metadata.name` (e.g. `echo_tool`, `get_weather_alerts`), without author or plugin name prefix.
### Set Plugin Persistent Data
Persistently store plugin data. Data stored through this interface can only be accessed by this plugin. Values need to be converted to bytes manually.
```python theme={null}
async def set_plugin_storage(self, key: str, value: bytes) -> None:
"""Set a plugin storage value"""
# Usage example
await self.plugin.set_plugin_storage("key", b"value")
```
### Get Plugin Persistent Data
```python theme={null}
async def get_plugin_storage(self, key: str) -> bytes:
"""Get a plugin storage value"""
# Usage example
plugin_storage = await self.plugin.get_plugin_storage("key")
```
### Get All Plugin Persistent Data Keys
```python theme={null}
async def get_plugin_storage_keys(self) -> list[str]:
"""Get all plugin storage keys"""
# Usage example
plugin_storage_keys = await self.plugin.get_plugin_storage_keys()
```
### Delete Plugin Persistent Data
```python theme={null}
async def delete_plugin_storage(self, key: str) -> None:
"""Delete a plugin storage value"""
# Usage example
await self.plugin.delete_plugin_storage("key")
```
### Get Workspace Persistent Data
Data stored through this interface can be accessed by all plugins. Values need to be converted to bytes manually.
```python theme={null}
async def set_workspace_storage(self, key: str, value: bytes) -> None:
"""Set a workspace storage value"""
# Usage example
await self.plugin.set_workspace_storage("key", b"value")
```
### Get Workspace Persistent Data
```python theme={null}
async def get_workspace_storage(self, key: str) -> bytes:
"""Get a workspace storage value"""
# Usage example
workspace_storage = await self.plugin.get_workspace_storage("key")
```
### Get All Workspace Persistent Data Keys
```python theme={null}
async def get_workspace_storage_keys(self) -> list[str]:
"""Get all workspace storage keys"""
# Usage example
workspace_storage_keys = await self.plugin.get_workspace_storage_keys()
```
### Delete Workspace Persistent Data
```python theme={null}
async def delete_workspace_storage(self, key: str) -> None:
"""Delete a workspace storage value"""
# Usage example
await self.plugin.delete_workspace_storage("key")
```
### Get Plugin File-typed Config Field Data
```python theme={null}
async def get_config_file(self, file_key: str) -> bytes:
"""Get a config file value"""
# Usage example
file_bytes = await self.plugin.get_config_file("key")
```
Use this in conjunction with configuration fields of type [`file or array[file`](/en/plugin/dev/basic-info.html#type-file).
## Knowledge Base API
These APIs are accessible via `self.plugin` in any component, allowing you to list and retrieve from all knowledge bases in the LangBot instance without pipeline restrictions.
### List All Knowledge Bases
List all available knowledge bases in the LangBot instance.
```python theme={null}
async def list_knowledge_bases(self) -> list[dict[str, Any]]:
"""List all knowledge bases
Returns:
List of knowledge base dicts, each containing:
- uuid: Knowledge base UUID
- name: Knowledge base name
- description: Knowledge base description
"""
# Usage example
knowledge_bases = await self.plugin.list_knowledge_bases()
for kb in knowledge_bases:
print(f"KB: {kb['name']} ({kb['uuid']})")
```
### Retrieve from Knowledge Base
Retrieve relevant documents from any knowledge base.
```python theme={null}
async def retrieve_knowledge(
self,
kb_id: str,
query_text: str,
top_k: int = 5,
filters: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
"""Retrieve from a knowledge base
Args:
kb_id: Knowledge base UUID (from list_knowledge_bases)
query_text: Search query text
top_k: Number of results to return (default: 5)
filters: Optional metadata filters for retrieval
Returns:
List of retrieval result entries
"""
# Usage example
results = await self.plugin.retrieve_knowledge(
kb_id="kb-uuid-here",
query_text="How to configure the system?",
top_k=3,
)
for entry in results:
print(entry)
```
These APIs do not require a `query_id` and can be used in any component (including Tool components). They can access all knowledge bases without being restricted to the current pipeline's configuration.
## RAG API
These APIs are available for `KnowledgeEngine` components to access the LangBot host's embedding models, vector database, and file storage. Access method:
* In `KnowledgeEngine` component classes: Internal methods of the `self.plugin` object.
### Invoke Embedding Model
Generate text embeddings using the host's configured embedding model.
```python theme={null}
async def invoke_embedding(
self,
embedding_model_uuid: str,
texts: list[str],
) -> list[list[float]]:
"""Generate embeddings using host's embedding model
Args:
embedding_model_uuid: Embedding model UUID
texts: List of texts to embed
Returns:
List of embedding vectors, one per input text
"""
# Usage example
vectors = await self.plugin.invoke_embedding("model_uuid", ["Hello", "World"])
```
### Vector Upsert
Upsert vectors to the host's vector database.
```python theme={null}
async def vector_upsert(
self,
collection_id: str,
vectors: list[list[float]],
ids: list[str],
metadata: list[dict[str, Any]] | None = None,
documents: list[str] | None = None,
) -> None:
"""Upsert vectors
Args:
collection_id: Target collection ID
vectors: List of vectors
ids: List of unique IDs for vectors
metadata: Optional list of metadata dicts
documents: Optional raw text documents. Required for full-text
and hybrid search in backends that support them.
"""
# Usage example
await self.plugin.vector_upsert(
collection_id="kb_uuid",
vectors=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
ids=["chunk_0", "chunk_1"],
metadata=[{"document_id": "doc1"}, {"document_id": "doc1"}],
documents=["chunk text 0", "chunk text 1"],
)
```
### Vector Search
Search similar vectors in the host's vector database.
```python theme={null}
async def vector_search(
self,
collection_id: str,
query_vector: list[float],
top_k: int = 5,
filters: dict[str, Any] | None = None,
search_type: str = "vector",
query_text: str = "",
) -> list[dict[str, Any]]:
"""Vector search
Args:
collection_id: Target collection ID
query_vector: Query vector for similarity search
top_k: Number of results to return
filters: Optional metadata filters
search_type: One of 'vector', 'full_text', 'hybrid'
query_text: Raw query text, used for full_text and hybrid search
Returns:
List of search results (dict with id, score, metadata, etc.)
"""
# Usage example
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
top_k=5,
search_type="hybrid",
query_text="search query",
)
# Return format: [{"id": "chunk_0", "score": 0.123, "metadata": {"document_id": "doc1", ...}}, ...]
```
Each result returned by `vector_search` is a dict containing `id` (vector ID), `score` (distance score), and `metadata` (metadata provided during upsert). If you need text content in retrieval results, store the text in metadata during ingestion.
### Vector Delete
Delete vectors from the host's vector database.
```python theme={null}
async def vector_delete(
self,
collection_id: str,
file_ids: list[str] | None = None,
filters: dict[str, Any] | None = None,
) -> int:
"""Vector delete
Args:
collection_id: Target collection ID
file_ids: File IDs whose vectors should be deleted
filters: Optional metadata filters for deletion
Returns:
Number of deleted items
"""
# Usage example
deleted = await self.plugin.vector_delete(
collection_id="kb_uuid",
file_ids=["doc_001"],
)
```
The `filters` parameter supports Chroma-style `where` syntax for metadata filtering. Multiple top-level keys are AND-ed. Supported operators: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`.
```python theme={null}
# Implicit $eq
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
filters={"file_id": "abc"},
)
# Comparison operator
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
filters={"created_at": {"$gte": 1700000000}},
)
# In-list operator
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
filters={"file_type": {"$in": ["pdf", "docx"]}},
)
# Delete by filter
deleted = await self.plugin.vector_delete(
collection_id="kb_uuid",
filters={"file_type": {"$eq": "pdf"}},
)
```
**Note:** Chroma, Qdrant, and SeekDB store full metadata and can filter on any field. Milvus and pgvector only store `text`, `file_id`, and `chunk_uuid` — filters on other fields will be silently ignored.
Get uploaded file content from the host's storage.
```python theme={null}
async def get_knowledge_file_stream(self, storage_path: str) -> bytes:
"""Get file content
Args:
storage_path: File storage path (from FileObject.storage_path)
Returns:
File content as bytes
"""
# Usage example
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
```
# Message Platform Entities
Source: https://docs.langbot.app/en/plugin/dev/apis/messages
LangBot supports multiple messaging platforms, and each platform has different `message entity` formats. To abstract away these differences, we have implemented a unified standard. Plugin developers only need to master and use the message entities described on this page in plugins, and LangBot's internal message processing logic can automatically handle message parsing and conversion.
## Message Chain
`Messages` on messaging platforms are different from messages used for AI interaction. Messages on messaging platforms are described in the form of `message chains`. Each independent message displayed on QQ is a message chain, which can contain various `message chain components` such as `text`, `images`, `@mentions`, etc. For example:
This is a message chain containing one Plain component (Hello World) and one Image component (Usagi).
The definitions of message chains and message chain components are located in `langbot_plugin.api.entities.builtin.platform.message`.
### Constructing Message Chains
Please first import the `langbot_plugin.api.entities.builtin.platform.message` package to use the message components within it.
```python theme={null}
from langbot_plugin.api.entities.builtin.platform.message import *
# Build a message containing text "Hello LangBot" and an image (from URL)
msg_chain = MessageChain([
Plain(text="Hello LangBot"),
Image(url='https://qchatgpt.rockchin.top/langbot-logo.png')
])
# Build a message containing @everyone and text "Hello LangBot"
msg_chain = MessageChain([
AtAll(),
Plain(text="Hello LangBot")
])
# Build a message containing @specific member and text "Hello LangBot"
msg_chain = MessageChain([
At(target=123456),
Plain(text="Hello LangBot")
])
```
Currently supported message chain components:
* `Source` - Source message chain information. Messages received from messaging platforms will include this component at the front of the message chain to record message information
* `Plain` - Plain text message
* `Quote` - Quote message
* `Image` - Image message
* `AtAll` - @everyone message
* `At` - @specific member message
* `Voice` - Voice message
* Need to check messaging platform support
* `Forward` - Forward message
* Not supported on many platforms, not recommended
* `File` - File message
When creating components, named parameters must be used. For specific usage, you can check the definitions in the source code `langbot_plugin.api.entities.builtin.platform.message`.
## Other Entities
In addition to the above entities, there are also entities like `Friend` and `Group`. These entities are defined in `langbot_plugin.api.entities.platform.entities`.
## Accessing Underlying Platform APIs
Not applicable for 4.x.
To abstract away platform differences, LangBot provides an abstraction layer above messaging platform APIs, which includes the content described above and various platform adapters in the `pkg/platform/sources` directory. However, due to significant platform differences, the entity and API encapsulation in the abstraction layer is not complete. If your plugin needs to access platform-specific functionality, you can access underlying APIs according to the following instructions.
```python theme={null}
# For example in a plugin
@handler(GroupMessageReceived)
async def _(self, ctx: EventContext):
# Get adapter object
adapter = ctx.query.adapter
```
| Platform | Adapter | Access Method | Description |
| ------------------------- | --------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OneBot v11 | aiocqhttp | adapter.bot | bot object corresponds to [aiocqhttp](https://github.com/nonebot/aiocqhttp) CQHttp object, please refer to aiocqhttp documentation for detailed usage |
| QQ Official API | qqofficial | adapter.bot | bot object is the SDK object under libs/qq\_official\_api, for detailed usage please refer to the `send_group_text_msg` method in `QQOfficialClient` in libs/qq\_official\_api/api.py for HTTP request methods, please refer to QQ Official API documentation for specific interface documentation |
| WeCom (Enterprise WeChat) | wecom | adapter.bot | Refer to the `send_image` method in `WecomClient` in libs/wecom\_api/api.py for HTTP request methods, please refer to WeCom API documentation for specific interface documentation |
| WeChat Official Account | officialaccount | - | Please refer to WeChat Official Account API documentation for interface documentation, credential information can be obtained from adapter.bot |
| Lark | lark | adapter.api\_client | Lark SDK's API Client object, please refer to [oapi-sdk-python](https://github.com/larksuite/oapi-sdk-python) for details |
| DingTalk | dingtalk | - | Please refer to DingTalk API documentation, credential information can be obtained from adapter.bot |
| Discord | discord | adapter.bot | bot object is [Rapptz/discord.py](https://github.com/Rapptz/discord.py) Discord object, please refer to documentation for detailed usage |
| Telegram | telegram | adapter.bot | bot object is [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) Telegram object, please refer to documentation for detailed usage |
| Slack | slack | adapter.bot | bot object in libs/slack\_api/api.py, please refer to its code and Slack SDK for detailed usage |
# Pipeline Events and APIs
Source: https://docs.langbot.app/en/plugin/dev/apis/pipeline-events
LangBot plugins can register and handle pipeline events. For usage instructions, see [Component: Event Listener](/en/plugin/dev/components/event-listener).
## List of Events
Some events have `settable attributes`, which can be modified by plugin code and used in subsequent LangBot processing.
### \*MessageReceived
Triggered when any message is received in group chat or private chat.
```python theme={null}
class PersonMessageReceived(BaseEventModel):
"""When any private chat message is received"""
event_name: str = "PersonMessageReceived"
launcher_type: str
"""Launcher object type (person)"""
launcher_id: typing.Union[int, str]
"""Sender ID"""
sender_id: typing.Union[int, str]
"""Sender ID, same as launcher_id in private chat"""
message_event: platform_events.PersonMessage
"""Original message event object. Contains sender information."""
message_chain: platform_message.MessageChain = pydantic.Field(
serialization_alias="message_chain"
)
"""Message chain"""
class GroupMessageReceived(BaseEventModel):
"""When any group chat message is received"""
event_name: str = "GroupMessageReceived"
launcher_type: str
"""Launcher object type (group)"""
launcher_id: typing.Union[int, str]
"""Group ID"""
sender_id: typing.Union[int, str]
"""Sender ID"""
message_event: platform_events.GroupMessage
"""Original message event object. Contains group and sender information."""
message_chain: platform_message.MessageChain = pydantic.Field(
serialization_alias="message_chain"
)
"""Message chain"""
```
### \*NormalMessageReceived
Triggered when a group chat or private chat message is received and determined to be a message that needs to be processed by LLM (non-command message).
```python theme={null}
class PersonNormalMessageReceived(BaseEventModel):
"""Triggered when a private chat normal message that should be processed is determined"""
event_name: str = "PersonNormalMessageReceived"
launcher_type: str
"""Launcher object type (person)"""
launcher_id: typing.Union[int, str]
"""Launcher object ID"""
sender_id: typing.Union[int, str]
"""Sender ID, same as launcher_id in private chat"""
text_message: str
"""Message text"""
message_event: platform_events.PersonMessage
"""Original message event object. Contains sender information."""
message_chain: platform_message.MessageChain = pydantic.Field(
serialization_alias="message_chain"
)
"""Message chain"""
# ========== Settable Attributes ==========
user_message_alter: typing.Optional[provider_message.ContentElement] = None
"""Modified message text, langbot_plugin.api.entities.builtin.provider.message.ContentElement type"""
reply_message_chain: typing.Optional[platform_message.MessageChain] = None
"""Direct reply message chain, only effective when preventing default behavior"""
class GroupNormalMessageReceived(BaseEventModel):
"""Triggered when a group chat normal message that should be processed is determined"""
event_name: str = "GroupNormalMessageReceived"
launcher_type: str
"""Launcher object type (group)"""
launcher_id: typing.Union[int, str]
"""Group ID"""
sender_id: typing.Union[int, str]
"""Sender ID"""
text_message: str
"""Message text"""
message_event: platform_events.GroupMessage
"""Original message event object. Contains group and sender information."""
message_chain: platform_message.MessageChain = pydantic.Field(
serialization_alias="message_chain"
)
"""Message chain"""
# ========== Settable Attributes ==========
user_message_alter: typing.Optional[provider_message.ContentElement] = None
"""Modified message text, langbot_plugin.api.entities.builtin.provider.message.ContentElement type"""
reply_message_chain: typing.Optional[platform_message.MessageChain] = None
"""Direct reply message chain, only effective when preventing default behavior"""
```
### \*CommandSent
No longer recommended for use, please use [Component: Command](/en/plugin/dev/components/command) instead.
Triggered when a group chat or private chat command is received.
```python theme={null}
class PersonCommandSent(BaseEventModel):
"""Triggered when a private chat command that should be processed is determined"""
event_name: str = "PersonCommandSent"
launcher_type: str
launcher_id: typing.Union[int, str]
sender_id: typing.Union[int, str]
command: str
"""Command text"""
params: list[str]
"""Command parameters"""
text_message: str
"""Message text"""
is_admin: bool
"""Whether it's an administrator"""
class GroupCommandSent(BaseEventModel):
"""Triggered when a group chat command that should be processed is determined"""
event_name: str = "GroupCommandSent"
launcher_type: str
launcher_id: typing.Union[int, str]
sender_id: typing.Union[int, str]
command: str
"""Command text"""
params: list[str]
"""Command parameters"""
text_message: str
"""Message text"""
is_admin: bool
"""Whether it's an administrator"""
```
### NormalMessageResponded
Triggered when a message receives an LLM response.
```python theme={null}
class NormalMessageResponded(BaseEventModel):
"""Triggered when replying to a normal message"""
event_name: str = "NormalMessageResponded"
launcher_type: str
launcher_id: typing.Union[int, str]
sender_id: typing.Union[int, str]
session: provider_session.Session
"""Session object"""
prefix: str
"""Reply message prefix"""
response_text: str
"""Reply message text"""
finish_reason: str
"""Response end reason"""
funcs_called: list[str]
"""List of called functions"""
# ========== Settable Attributes ==========
reply_message_chain: typing.Optional[platform_message.MessageChain] = None
"""Reply message component list, only effective when preventing default behavior"""
```
### PromptPreProcessing
Triggered when building the LLM response context (prompt).
```python theme={null}
class PromptPreProcessing(BaseEventModel):
"""Triggered when preprocessing prompt in session"""
event_name: str = "PromptPreProcessing"
session_name: str
"""Session name, format is person_1234567890 or group_1234567890"""
# ========== Settable Attributes ==========
default_prompt: list[typing.Union[provider_message.Message, provider_message.MessageChunk]]
"""Scenario preset (system prompt) for this conversation, can be modified, langbot_plugin.api.entities.builtin.provider.message.Message or langbot_plugin.api.entities.builtin.provider.message.MessageChunk type"""
prompt: list[typing.Union[provider_message.Message, provider_message.MessageChunk]]
"""Existing message records for this conversation, can be modified, langbot_plugin.api.entities.builtin.provider.message.Message or langbot_plugin.api.entities.builtin.provider.message.MessageChunk type"""
```
## Event Context APIs
```python theme={null}
...
@self.handler(events.PersonMessageReceived)
async def handler(event_context: context.EventContext):
...
```
Event processing methods will be passed the `EventContext` object, which contains event context information, and the object has both [Request API](/en/plugin/dev/apis/common) and event context specific APIs. The following is a list of event context specific APIs:
### Get Original Event Attributes
`event_context.event` attribute is the original event object, which can get the attributes of the original event. The type is the type of the event being listened to.
For example, when the event being listened to is `GroupMessageReceived`:
```python theme={null}
event = event_context.event
print(event.launcher_type) # Launcher object type (group)
print(event.launcher_id) # Group ID
print(event.sender_id) # Sender account ID
print(event.message_chain) # Message chain
```
### Prevent Default Behavior
```python theme={null}
def prevent_default(self):
"""Prevent default behavior"""
# Usage example
event_context.prevent_default()
```
Calling this method will prevent the default subsequent behavior of this event, and the pipeline will end directly.
Only the following events can prevent default behavior:
* PersonMessageReceived
* GroupMessageReceived
* PersonNormalMessageReceived
* GroupNormalMessageReceived
* PersonCommandSent
* GroupCommandSent
* NormalMessageResponded
### Prevent Postorder Execution
```python theme={null}
def prevent_postorder(self):
"""Prevent postorder execution"""
# Usage example
event_context.prevent_postorder()
```
Calling this method will prevent the subsequent plugins from executing this time.
# Plugin Technical Details
Source: https://docs.langbot.app/en/plugin/dev/apis/tech-details
## Architecture
In version 4.0, we introduced a high-security, high-flexibility production-grade plugin system and provided developers with rich APIs and easy-to-use supporting tools.
Plugin Runtime is used to manage plugin lifecycles and coordinate interactions between LangBot and plugins. It has two operating modes: `stdio` and `websocket`. When LangBot is started directly by users (not running in a container), it uses `stdio` mode, which is common for personal users or lightweight environments. When LangBot runs in a container, it uses `websocket` mode, designed specifically for production environments.
Plugin Runtime automatically starts each installed plugin and interacts through stdio. In plugin development scenarios, developers can use the `lbp` command-line tool to start plugins and connect to the running Runtime via WebSocket for debugging.
# Completing Plugin Configuration Information
Source: https://docs.langbot.app/en/plugin/dev/basic-info
## Modifying the Manifest File
The `manifest.yaml` file in the plugin directory declares the basic information about the plugin, which will be displayed in the LangBot UI or plugin marketplace interfaces.
```yaml theme={null}
apiVersion: v1 # Do not modify
kind: Plugin # Do not modify
metadata:
author: RockChinQ # Author, must match regex ^[a-zA-Z0-9_-]+$
name: HelloPlugin # Plugin name, used to distinguish plugins, must match regex ^[a-zA-Z0-9-]+$
repository: 'https://github.com/langbot-app/HelloPlugin' # Plugin repository URL
version: 0.1.0 # Plugin version
description:
en_US: 'Hello LangBot Plugin' # Plugin description, multilingual
zh_Hans: 'Hello LangBot Plugin'
label:
en_US: HelloPlugin # Plugin label, displayed in the interface, multilingual
zh_Hans: HelloPlugin
icon: assets/icon.svg # Plugin icon, defaults to assets/icon.svg, can be replaced with various image formats
spec:
config: [] # Plugin configuration item format
components: {} # Plugin component list, no need to modify manually
execution:
python:
path: main.py # Do not modify
attr: HelloPlugin # Do not modify
```
Plugin multilingual support follows the [RFC 4646](https://datatracker.ietf.org/doc/html/rfc4646) standard. Currently supported languages include:
* `en_US` English (required)
* `zh_Hans` Simplified Chinese
* `zh_Hant` Traditional Chinese
* `ja_JP` Japanese
* `vi_VN` Vietnamese
* `th_TH` Thai
* `es_ES` Spanish
## Plugin Configuration Item Format
In the `manifest.yaml` file, fields declared in `spec.config` will be rendered by LangBot as configuration item forms for users to fill out. Plugins can retrieve user-filled configuration items through APIs provided by the SDK (see later sections).
For example:
```yaml theme={null}
spec:
config:
- name: github_token # Required; configuration item name, used for retrieval in the plugin
type: string # Required; configuration item type, supports string, integer, float, boolean, select, prompt-editor, llm-model-selector, bot-selector, tools-selector, etc.
label: # Required; configuration item display name, supports multilingual. Language codes follow RFC 4646 standard.
en_US: Github Token
zh_Hans: Github Token
description: # Configuration item description, supports multilingual. Optional.
en_US: Image downloading requires a Github token
zh_Hans: 如果不填的话,图片可能会下载失败
default: '' # Configuration item default value, optional.
required: false # Whether the configuration item is required, optional.
show_if: # Conditional rendering (shows when conditions are met), optional.
field: mode
operator: eq
value: mode1
- name: mode
type: select
...
```
The supported types and fields for configuration items are as follows:
### Conditional Rendering (`show_if`)
All types of configuration items support conditional rendering using the `show_if` field. You can decide whether the current field is rendered based on the value of another field in the same (or an associated) form.
```yaml theme={null}
- name: advanced_setting
type: string
show_if:
field: mode # The name of the dependent field
operator: eq # The operator, supports 'eq' (equals), 'neq' (not equals), 'in' (in a list)
value: advanced # The value to check against
```
> **Note:** Cross-form cascading relationships (e.g. `creation_schema` values reading from `retrieval_schema` values and vice versa in a Knowledge Engine) are supported in the latest LangBot frontend.
### type: string
String type. Optionally supports `options` for preset values — when provided, a dropdown button appears next to the input allowing users to quickly fill in a preset value. Users can still type freely.
```yaml theme={null}
- name: api_key
type: string
...
```
With preset options:
```yaml theme={null}
- name: api_url
type: string
options: # Optional preset values, displayed as a dropdown button next to the input.
- name: "https://api.openai.com/v1" # The value to fill in when selected.
label: # Display name, supports multilingual.
en_US: OpenAI Official
zh_Hans: OpenAI 官方
- name: "https://api.deepseek.com/v1"
label:
en_US: DeepSeek
zh_Hans: DeepSeek
...
```
### type: array\[string]
String array.
```yaml theme={null}
- name: tags
type: array[string]
...
```
### type: integer
Integer type.
```yaml theme={null}
- name: progress
type: integer
...
```
### type: float
Float type.
```yaml theme={null}
- name: temperature
type: float
...
```
### type: boolean
Boolean type.
```yaml theme={null}
- name: is_enabled
type: boolean
...
```
### type: select
Dropdown menu. Requires configuring `options`, which represents the dropdown menu options. Each option displays its `label` as the primary text, with the `name` (value) shown in smaller text below.
```yaml theme={null}
- name: mode
type: select
options: # Dropdown options, supports multilingual.
- name: mode1 # Value, displayed as secondary text in the dropdown.
label: # Display name, supports multilingual.
en_US: Mode 1
zh_Hans: 模式 1
- name: mode2
label: # Display name, supports multilingual.
en_US: Mode 2
zh_Hans: 模式 2
...
```
### type: prompt-editor
Prompt editor. Displays a prompt editor from the pipeline configuration page, with the final result represented in OpenAI's `messages` format.
```yaml theme={null}
- name: prompt
type: prompt-editor
...
```
### type: text
Large text input. Rendered as a textarea in the frontend for user input, and passed to the plugin as a string.
```yaml theme={null}
- name: prompt
type: text
...
```
### type: file
File upload. Supports file uploads up to 10MB, passed to the plugin in the format `{"file_key": "xxxxx.xxx", "mimetype": "xxxxx"}`. The plugin can use the `get_config_file` API to retrieve the file content.
```yaml theme={null}
- name: config_file
type: file
accept: 'application/json' # Optional, specify accepted file MIME types
...
```
Retrieving the file in the plugin:
```python theme={null}
# Get file information from config
file_config = self.get_config()['config_file']
file_key = file_config['file_key']
mimetype = file_config['mimetype']
# Example output: {'file_input': {'file_key': 'plugin_config_d2234fd802054faf80babe0679a97fa9.json', 'mimetype': 'application/json'}}
print(file_config)
# Get file content
file_bytes = await self.get_config_file(file_key)
```
Common MIME type and file extension reference: [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types)
### type: array\[file]
Multiple file upload. Similar to the file type, but supports uploading multiple files, passed to the plugin in the format `[{"file_key": "xxxxx.xxx", "mimetype": "xxxxx"}]`.
```yaml theme={null}
- name: resource_files
type: array[file]
accept: 'image/*' # Optional, specify accepted file MIME types
...
```
Retrieving files in the plugin:
```python theme={null}
# Get file list from config
files_config = self.get_config()['resource_files']
for file_config in files_config:
file_key = file_config['file_key']
mimetype = file_config['mimetype']
# Get file content
file_bytes = await self.get_config_file(file_key)
```
### type: llm-model-selector
LLM model selector. Displays an LLM model selector where you can choose configured LLM models, with the final result represented as the LLM model UUID.
```yaml theme={null}
- name: model
type: llm-model-selector
...
```
### type: bot-selector
Bot selector. Displays a Bot selector where you can choose configured Bots, with the final result represented as the Bot UUID.
```yaml theme={null}
- name: bot
type: bot-selector
...
```
Using the Bot UUID in the plugin:
```python theme={null}
# Get Bot UUID from config
bot_uuid = self.get_config()['bot']
print(bot_uuid) # Output: '550e8400-e29b-41d4-a716-446655440000'
```
### type: tools-selector
Tool selector. Displays a multi-select tool selector where you can choose registered tools (plugin tools and MCP tools), with the final result represented as an array of tool names.
```yaml theme={null}
- name: selected_tools
type: tools-selector
...
```
Using the selected tool names in the plugin:
```python theme={null}
# Get selected tool names from config
selected_tools = self.get_config()['selected_tools']
print(selected_tools) # Output: ['get_weather_alerts', 'web_search']
# Call selected tools
for tool_name in selected_tools:
result = await self.plugin.call_tool(
tool_name=tool_name,
parameters={"query": "hello"},
session={},
query_id=0,
)
```
## What's Next
This tutorial will guide you through step-by-step completion of plugin functionality.
* Adding Components: Plugin components are the core functional units of plugins. You can [add components](/en/plugin/dev/components/add) based on your needs.
# Adding Components
Source: https://docs.langbot.app/en/plugin/dev/components/add
Plugins consist of one or more components that provide different functionalities to LangBot. Currently supported component types include:
* Event Listeners (EventListener): Listen to events during pipeline execution and modify context or pipelines.
* Commands (Command): Triggered by user command messages starting with `!` (or other configured prefixes).
* Tools (Tool): Called by LLMs during execution of LangBot's built-in Local Agent.
* Knowledge Engines (KnowledgeEngine): Provide knowledge base indexing and retrieval capabilities for LangBot, managing document ingestion, retrieval, and deletion.
* Parsers (Parser): Parse binary files such as PDF, Word, etc. into structured text before document ingestion, for use by Knowledge Engines.
* Pages (Page): Register custom visual pages in the LangBot WebUI sidebar, providing admin panels, data displays, and interactive interfaces.
## Adding Components
Execute the command in the plugin directory:
```bash theme={null}
lbp comp
```
For example, to add an event listener:
```bash theme={null}
lbp comp EventListener
```
Follow the prompts to enter the component configuration (if any).
```bash theme={null}
➜ HelloPlugin > lbp comp EventListener
Generating component EventListener...
Component EventListener generated successfully.
组件 EventListener 生成成功。
➜ HelloPlugin >
```
The CLI will generate a `components` directory in the plugin directory and create corresponding component directories within it.
```
.
├── assets
│ └── icon.svg
├── components
│ ├── __init__.py
│ └── event_listener
│ ├── __init__.py
│ ├── default.py
│ └── default.yaml
├── main.py
├── manifest.yaml
├── README.md
└── requirements.txt
```
At the same time, component discovery configuration will be added to the plugin's `manifest.yaml`:
```yaml theme={null}
...
components:
EventListener:
fromDirs:
- path: components/event_listener/
...
```
To remove components, you can delete the corresponding information manually.
## Component Types
For detailed usage of each component, please refer to:
* [EventListener](./event-listener)
* [Command](./command)
* [Tool](./tool)
* [KnowledgeEngine](./knowledge-engine)
* [Parser](./parser)
* [Page](./page)
# Component: Command
Source: https://docs.langbot.app/en/plugin/dev/components/command
Command components are triggered by user command messages starting with `!` (or other configured prefixes). Here's an example of triggering the `!help` command:
## Adding Command Components
A single plugin can contain any number of commands. Execute the command `lbp comp Command` in the plugin directory and follow the prompts to enter the command configuration.
```bash theme={null}
➜ HelloPlugin > lbp comp Command
Generating component Command...
Command name: info
Command description: Show information of the query
Component Command generated successfully.
组件 Command 生成成功。
```
This will generate `info.yaml` and `info.py` files in the `components/commands/` directory. The `.yaml` file defines the basic information for the `!info` command, and the `.py` file is the command handler:
```bash theme={null}
➜ HelloPlugin > tree
...
├── components
│ ├── __init__.py
│ ├── commands
│ │ ├── __init__.py
│ │ ├── info.py
│ │ └── info.yaml
...
```
## Manifest File: Command Component
```yaml theme={null}
apiVersion: v1 # Do not modify
kind: Command # Do not modify
metadata:
name: info # Command name, users will trigger this command with !info
label: # Command display name, shown in LangBot UI, supports multilingual
en_US: Info
zh_Hans: Info
description: # Command description, shown in LangBot UI, supports multilingual. Optional.
en_US: 'Show information of the query'
zh_Hans: '发送此次消息的详细信息'
spec:
execution:
python:
path: info.py # Command handler, do not modify
attr: Info # Command handler class name, matches the class name in info.py
```
## Plugin Processing
The following code is generated by default (`components/command/.py`). You need to register and implement subcommand processing logic in the `initialize` method of the `Info` class.
```python theme={null}
# Auto generated by LangBot Plugin SDK.
# Please refer to https://docs.langbot.app/en/plugin/dev/tutor.html for more details.
from __future__ import annotations
from typing import Any, AsyncGenerator
from langbot_plugin.api.definition.components.command.command import Command, Subcommand
from langbot_plugin.api.entities.builtin.command.context import ExecuteContext, CommandReturn
class Info(Command):
async def initialize(self):
await super().initialize()
"Fill with your code here"
```
Adding subcommands:
```python theme={null}
...
class Info(Command):
async def initialize(self):
await super().initialize()
@self.subcommand(
name="", # Empty string represents root command
help="Show information of the query", # Command help information
usage="info", # Command usage example, displayed in command help
aliases=["i"], # Command aliases
)
async def send(self, context: ExecuteContext) -> AsyncGenerator[CommandReturn, None]:
print(context)
reply_text = f"Query ID: {context.query_id}\n"
reply_text += f"command: {context.command}\n"
reply_text += f"command_text: {context.command_text}\n"
reply_text += f"params: {context.params}\n"
reply_text += f"crt_params: {context.crt_params}\n"
reply_text += f"privilege: {context.privilege}\n"
reply_text += f"session: {context.session.launcher_type.value}_{context.session.launcher_id}\n"
yield CommandReturn(
text=reply_text,
)
@self.subcommand(
name="field",
help="Show information of the field",
usage="info field",
aliases=["f"],
)
async def field(self, context: ExecuteContext) -> AsyncGenerator[CommandReturn, None]:
print(context)
field_name = context.crt_params[0]
field_value = getattr(context, field_name)
yield CommandReturn(
text=f"{field_name}: {field_value}",
)
```
In this code, the `send` function is registered as a subcommand through the `@self.subcommand` decorator, prints the command context (ExecuteContext) information, and concatenates it into a reply message.
### Subcommand Registration
`name` is the subcommand name. Leaving it empty means handling the main command. If not empty, it matches the second parameter as the command. For example:
* `!info` matches the subcommand with name=""
* `!info field` matches the subcommand with name="field"
* `!info field value` matches the subcommand with name="field", with `value` as the subcommand parameter
* Specialy, `name="*"` matches all unmatched first-level subcommands, and passes each section after `info` as parameters, for example `!info 123`, `!info abc`. You can get `['123']` or `['abc']` from `context.crt_params`, depending on the user's input.
In subcommand functions, you can read command parameters through the `context` variable.
The effect of the above command is shown in the image:
### Command Context
```python theme={null}
class ExecuteContext(pydantic.BaseModel):
"""Single command execution context"""
query_id: int
"""Request ID"""
session: provider_session.Session
"""Session object for this message"""
command_text: str
"""Complete command text"""
command: str
"""Command name"""
crt_command: str
"""Current command
In multi-level commands, crt_command is the current command, command is the root command.
Example: !plugin on Webwlkr
When processing plugin, command is plugin, crt_command is plugin
When processing on, command is plugin, crt_command is on
"""
params: list[str]
"""Command parameters
Parameter list after splitting the entire command by spaces
"""
crt_params: list[str]
"""Current command parameters
In multi-level commands, crt_params are current command parameters, params are root command parameters.
Example: !plugin on Webwlkr
When processing plugin, params is ['on', 'Webwlkr'], crt_params is ['on', 'Webwlkr']
When processing on, params is ['on', 'Webwlkr'], crt_params is ['Webwlkr']
"""
privilege: int
"""Session privilege level"""
...
```
### Command Return Values
Command return values (CommandReturn) currently support returning text, images (image\_url, image links), and errors.
```python theme={null}
yield CommandReturn(
text=reply_text,
)
yield CommandReturn(
image_url=image_url,
)
```
Command processing supports returning multiple messages, so please use `yield` statements to return messages.
For specific return values, please refer to the definition of `CommandReturn`: [langbot\_plugin.api.entities.builtin.command.context.CommandReturn](https://github.com/langbot-app/langbot-plugin-sdk/tree/main/src/langbot_plugin/api/entities/builtin/command/context.py)
## What's Next
You have learned the basic information about command registration and command execution. Next, you can:
* Check out [Plugin Common APIs](/en/plugin/dev/apis/common)
# Component: Event Listener
Source: https://docs.langbot.app/en/plugin/dev/components/event-listener
LangBot's pipeline generates events during runtime for plugins to hook into and process. Each plugin can only have one event listener (components/event\_listener/default.yaml), but can register any number of events within it.
## Adding Event Listener Component
Execute the command `lbp comp EventListener` in the plugin directory. Creating an event listener component doesn't require any configuration.
```bash theme={null}
➜ HelloPlugin > lbp comp EventListener
Generating component EventListener...
Component EventListener generated successfully.
组件 EventListener 生成成功。
```
This will generate `default.yaml` and `default.py` files in the `components/event_listener/` directory. The `.yaml` file defines the basic information for the event listener, and the `.py` file is the event listener handler:
```bash theme={null}
➜ HelloPlugin > tree
...
├── components
│ ├── __init__.py
│ └── event_listener
│ ├── __init__.py
│ ├── default.py
│ └── default.yaml
...
```
## Manifest File: Event Listener
Since each plugin can only have one event listener, no modifications are needed in the manifest file.
## Plugin Processing
The following code is generated by default (components/event\_listener/default.py). You need to register and implement event processing logic in the `initialize` method of the `DefaultEventListener` class.
```python theme={null}
# Auto generated by LangBot Plugin SDK.
# Please refer to https://docs.langbot.app/en/plugin/dev/tutor.html for more details.
from __future__ import annotations
from langbot_plugin.api.definition.components.common.event_listener import EventListener
from langbot_plugin.api.entities import events, context
class DefaultEventListener(EventListener):
async def initialize(self):
await super().initialize()
"Fill with your code here"
```
Adding listeners for specific events:
```python theme={null}
...
class DefaultEventListener(EventListener):
async def initialize(self):
await super().initialize()
@self.handler(events.PersonMessageReceived)
async def handler(event_context: context.EventContext):
print("Hello LangBot Plugin!")
print(event_context)
await event_context.reply(
platform_message.MessageChain([
platform_message.Plain(text=f"Hello from Nahida Plugin!"),
])
)
```
This code registers a listener for the `PersonMessageReceived` (receiving any message from private chat) event, prints the event context (EventContext) information when the event is triggered, and calls the event context API to reply with a message.
EventContext stores common context information for this event trigger, while EventContext.event is an object of PersonMessageReceived (or other corresponding event types), storing detailed information about the event.
```python theme={null}
# Summary of EventContext definition
class EventContext(pydantic.BaseModel):
"""Event context, stores information for this event execution"""
...
event: pydantic.SerializeAsAny[BaseEventModel]
"""Event object, specific type is the type specified when registering the handler, can be found in events.py"""
...
```
## Event Registration
Event registration is implemented through the `@self.handler` decorator, with the decorator parameter being the event type. All monitorable events are defined in `langbot_plugin.api.entities.builtin.events`.
## What's Next
You have learned the basic information about event listeners. Next, you can:
* Check the `List of Monitorable Events` and `Event Context APIs` in the documentation [Pipeline Events](/en/plugin/dev/apis/pipeline-events)
* Check out [Plugin Common APIs](/en/plugin/dev/apis/common)
# Component: Knowledge Engine
Source: https://docs.langbot.app/en/plugin/dev/components/knowledge-engine
The Knowledge Engine component allows plugins to provide full knowledge base indexing and retrieval capabilities for LangBot. When users create a knowledge base in LangBot, they can choose a Knowledge Engine provided by a plugin to manage document ingestion, retrieval, and deletion. Plugins can also bridge advanced knowledge base services such as Dify, RAGFlow, FastGPT, and more.
## Adding a Knowledge Engine Component
A single plugin can add any number of Knowledge Engines. Execute the command `lbp comp KnowledgeEngine` in the plugin directory and follow the prompts to enter the Knowledge Engine configuration.
```bash theme={null}
➜ MyRAGPlugin > lbp comp KnowledgeEngine
Generating component KnowledgeEngine...
Knowledge Engine name: simple_rag
Knowledge Engine description: A simple Knowledge Engine with built-in chunking and retrieval
Component KnowledgeEngine generated successfully.
```
This will generate `simple_rag.yaml` and `simple_rag.py` files in the `components/knowledge_engine/` directory. The `.yaml` file defines the Knowledge Engine's basic information and configuration schemas, and the `.py` file is the handler for this engine:
```bash theme={null}
➜ MyRAGPlugin > tree
...
├── components
│ ├── __init__.py
│ └── knowledge_engine
│ ├── __init__.py
│ ├── simple_rag.py
│ └── simple_rag.yaml
...
```
## Manifest File: Knowledge Engine
```yaml theme={null}
apiVersion: v1 # Do not modify
kind: KnowledgeEngine # Do not modify
metadata:
name: simple_rag # Knowledge Engine name, used to identify this engine
label:
en_US: Simple Knowledge Engine # Engine display name, shown in LangBot's UI, supports multiple languages
zh_Hans: 简易 RAG 引擎
ja_JP: シンプル RAG エンジン
description:
en_US: 'A simple Knowledge Engine with built-in chunking and retrieval'
zh_Hans: '内置分块和检索能力的简易 RAG 引擎'
ja_JP: 'チャンキングと検索を内蔵したシンプルなRAGエンジン'
spec:
creation_schema: # Configuration parameters users fill in when creating a knowledge base
- name: chunk_size
label:
en_US: Chunk Size
zh_Hans: 分块大小
type: integer
required: false
default: 500
- name: chunk_overlap
label:
en_US: Chunk Overlap
zh_Hans: 分块重叠
type: integer
required: false
default: 50
retrieval_schema: # Configurable parameters during retrieval
- name: score_threshold
label:
en_US: Score Threshold
zh_Hans: 分数阈值
type: float
required: false
default: 0.5
execution:
python:
path: simple_rag.py # Engine handler, do not modify
attr: SimpleRag # Class name of the engine handler, consistent with the class name in simple_rag.py
```
For configuration item format reference, see: [Plugin Manifest Configuration Format](/en/plugin/dev/basic-info)
### creation\_schema and retrieval\_schema
Unlike the old `KnowledgeRetriever` component which used a single `spec.config`, KnowledgeEngine uses two separate schemas:
* **creation\_schema**: Parameters users fill in when creating a knowledge base. These are passed to the plugin via `creation_settings` during ingestion and retrieval.
* **retrieval\_schema**: Parameters users can adjust when querying the knowledge base. These are passed to the plugin via `retrieval_settings`.
## Capability Declaration
KnowledgeEngine can declare its supported capabilities. LangBot uses these capability declarations to determine UI behavior and available operations:
```python theme={null}
from langbot_plugin.api.definition.components.knowledge_engine.engine import KnowledgeEngine, KnowledgeEngineCapability
class SimpleRag(KnowledgeEngine):
@classmethod
def get_capabilities(cls) -> list[str]:
return [
KnowledgeEngineCapability.DOC_INGESTION, # Supports document upload and processing
]
```
Available capability constants:
| Capability | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DOC_INGESTION` | Supports document upload and processing. When declared, LangBot shows a "Documents" tab in the knowledge base details. If the plugin is used to bridge other knowledge base services or does not require users to upload documents, this capability declaration can be omitted. |
| `DOC_PARSING` | Supports native document parsing (file-to-text extraction). When declared, users can choose to use the Knowledge Engine's built-in parsing when uploading documents, without needing an external [Parser plugin](./parser). If not declared, an external Parser plugin must be installed to upload documents. |
Other retrieval behaviors (such as reranking, hybrid search, etc.) are controlled by the plugin's `retrieval_schema` and do not need capability flags.
## Plugin Handler
The following code will be generated by default (`components/knowledge_engine/.py`). You need to implement the three core methods: `ingest`, `retrieve`, and `delete_document`. Complete code can be found in the `SimpleKnowledgeEngine` example in [langbot-plugin-demo](https://github.com/langbot-app/langbot-plugin-demo).
```python theme={null}
from langbot_plugin.api.definition.components.knowledge_engine.engine import KnowledgeEngine, KnowledgeEngineCapability
from langbot_plugin.api.entities.builtin.rag.models import (
IngestionContext,
IngestionResult,
)
from langbot_plugin.api.entities.builtin.rag.context import (
RetrievalContext,
RetrievalResponse,
RetrievalResultEntry,
)
from langbot_plugin.api.entities.builtin.rag.enums import DocumentStatus
from langbot_plugin.api.entities.builtin.provider.message import ContentElement
class SimpleRag(KnowledgeEngine):
@classmethod
def get_capabilities(cls) -> list[str]:
return [KnowledgeEngineCapability.DOC_INGESTION]
async def on_knowledge_base_create(self, kb_id: str, config: dict) -> None:
"""Callback when a knowledge base is created, can be used to initialize resources"""
pass
async def on_knowledge_base_delete(self, kb_id: str) -> None:
"""Callback when a knowledge base is deleted, can be used to clean up resources"""
pass
async def ingest(self, context: IngestionContext) -> IngestionResult:
"""Ingest a document into the knowledge base"""
# 1. Get file content
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
# 2. Parse document and split into chunks
text = file_bytes.decode('utf-8')
chunk_size = context.creation_settings.get('chunk_size', 500)
chunk_overlap = context.creation_settings.get('chunk_overlap', 50)
chunks = self._split_text(text, chunk_size, chunk_overlap)
# 3. Generate embeddings using host embedding model
embedding_model_uuid = context.creation_settings.get('embedding_model_uuid', '')
vectors = await self.plugin.invoke_embedding(embedding_model_uuid, chunks)
# 4. Write to host vector database
collection_id = context.get_collection_id()
ids = [f"{context.file_object.metadata.document_id}_{i}" for i in range(len(chunks))]
metadata = [{"document_id": context.file_object.metadata.document_id, "chunk_index": i, "text": chunks[i]} for i in range(len(chunks))]
await self.plugin.vector_upsert(collection_id, vectors, ids, metadata)
return IngestionResult(
document_id=context.file_object.metadata.document_id,
status=DocumentStatus.COMPLETED,
chunks_created=len(chunks),
)
async def retrieve(self, context: RetrievalContext) -> RetrievalResponse:
"""Retrieve relevant content from the knowledge base"""
# 1. Generate query vector
embedding_model_uuid = context.creation_settings.get('embedding_model_uuid', '')
query_vectors = await self.plugin.invoke_embedding(embedding_model_uuid, [context.query])
query_vector = query_vectors[0]
# 2. Vector search
collection_id = context.get_collection_id()
results = await self.plugin.vector_search(collection_id, query_vector, top_k=context.retrieval_settings.get('top_k', 5))
# 3. Convert to retrieval results
entries = []
for r in results:
entry = RetrievalResultEntry(
id=r.get('id', ''),
content=[ContentElement.from_text(r.get('metadata', {}).get('text', ''))],
metadata=r.get('metadata', {}),
distance=r.get('score', 0.0),
)
entries.append(entry)
return RetrievalResponse(
results=entries,
total_found=len(entries),
metadata={},
)
async def delete_document(self, kb_id: str, document_id: str) -> bool:
"""Delete a document and its associated data from the knowledge base"""
collection_id = kb_id
deleted = await self.plugin.vector_delete(collection_id, file_ids=[document_id])
return deleted > 0
```
### Lifecycle Hooks
KnowledgeEngine provides two lifecycle hooks that are called when knowledge bases are created and deleted:
```python theme={null}
async def on_knowledge_base_create(self, kb_id: str, config: dict) -> None:
"""Callback when a knowledge base using this engine is created
Args:
kb_id: Knowledge base UUID
config: User-provided configuration (fields from creation_schema)
"""
async def on_knowledge_base_delete(self, kb_id: str) -> None:
"""Callback when a knowledge base using this engine is deleted
Args:
kb_id: Knowledge base UUID
"""
```
### Document Ingestion
The `ingest` method is called when a user uploads a document to the knowledge base:
```python theme={null}
async def ingest(self, context: IngestionContext) -> IngestionResult:
```
**IngestionContext** contains the following information:
```python theme={null}
class IngestionContext(pydantic.BaseModel):
file_object: FileObject # File object to ingest
knowledge_base_id: str # Target knowledge base ID
collection_id: str | None # Vector collection ID (falls back to knowledge_base_id)
creation_settings: dict # Configuration from knowledge base creation
parsed_content: ParseResult | None # Pre-parsed content from external Parser plugin (if any)
```
**FileObject** contains file metadata:
```python theme={null}
class FileObject(pydantic.BaseModel):
metadata: FileMetadata # Contains filename, file_size, mime_type, document_id, knowledge_base_id, etc.
storage_path: str # File path in the storage system
```
**IngestionResult** should return the ingestion result:
```python theme={null}
class IngestionResult(pydantic.BaseModel):
document_id: str # Document ID
status: DocumentStatus # Processing status: COMPLETED / FAILED
chunks_created: int = 0 # Number of chunks created
error_message: str | None = None # Error message on failure
metadata: dict = {} # Additional metadata
```
### Knowledge Retrieval
The `retrieve` method is called when the knowledge base is queried:
```python theme={null}
async def retrieve(self, context: RetrievalContext) -> RetrievalResponse:
```
**RetrievalContext** contains the following information:
```python theme={null}
class RetrievalContext(pydantic.BaseModel):
query: str # Query text
knowledge_base_id: str | None # Knowledge base ID
collection_id: str | None # Vector collection ID
retrieval_settings: dict # Retrieval configuration (fields from retrieval_schema)
creation_settings: dict # Knowledge base creation configuration
filters: dict # Metadata filter conditions (Chroma-style where syntax)
```
The LangBot host automatically injects a default `top_k` value (default 5) into `retrieval_settings` before passing it to the plugin. Plugins can access it via `context.retrieval_settings.get('top_k', 5)`.
The `filters` field contains Chroma-style `where` filter conditions extracted from `retrieval_settings`. When the caller provides `filters` in the retrieval settings (e.g., filtering by time range, file type, or custom metadata fields), the host populates this field so plugins can apply them during retrieval. If no filters are provided, this field is an empty dict.
**RetrievalResponse** should return the retrieval results:
```python theme={null}
class RetrievalResponse(pydantic.BaseModel):
results: list[RetrievalResultEntry] # List of retrieval results
total_found: int # Total number of matches
metadata: dict # Additional metadata
```
**RetrievalResultEntry** represents a single retrieval result:
```python theme={null}
class RetrievalResultEntry(pydantic.BaseModel):
id: str # Result ID
content: list[ContentElement] # Content, create using ContentElement.from_text()
metadata: dict # Metadata
distance: float # Distance score (smaller is more relevant)
score: float | None = None # Similarity score (larger is more relevant)
```
### Document Deletion
The `delete_document` method is called when a user deletes a document from the knowledge base:
```python theme={null}
async def delete_document(self, kb_id: str, document_id: str) -> bool:
"""Delete a document and its associated data
Args:
kb_id: Knowledge base ID
document_id: Document ID
Returns:
Whether the deletion was successful
"""
```
## Host RAG APIs
KnowledgeEngine components can call LangBot host-provided RAG APIs via `self.plugin`, including embedding model invocation, vector database operations, and file retrieval.
### Invoke Embedding Model
```python theme={null}
async def invoke_embedding(
self,
embedding_model_uuid: str,
texts: list[str],
) -> list[list[float]]:
"""Generate embeddings using host's embedding model
Args:
embedding_model_uuid: Embedding model UUID
texts: List of texts to embed
Returns:
List of embedding vectors, one per input text
"""
# Usage example
vectors = await self.plugin.invoke_embedding("model_uuid", ["Hello", "World"])
```
### Vector Upsert
```python theme={null}
async def vector_upsert(
self,
collection_id: str,
vectors: list[list[float]],
ids: list[str],
metadata: list[dict] | None = None,
) -> None:
"""Upsert vectors to host's vector database
Args:
collection_id: Target collection ID
vectors: List of vectors
ids: List of unique IDs for vectors
metadata: Optional list of metadata dicts
"""
# Usage example
await self.plugin.vector_upsert(
collection_id="kb_uuid",
vectors=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
ids=["chunk_0", "chunk_1"],
metadata=[{"document_id": "doc1"}, {"document_id": "doc1"}],
)
```
### Vector Search
```python theme={null}
async def vector_search(
self,
collection_id: str,
query_vector: list[float],
top_k: int = 5,
filters: dict | None = None,
) -> list[dict]:
"""Search similar vectors in host's vector database
Args:
collection_id: Target collection ID
query_vector: Query vector for similarity search
top_k: Number of results to return
filters: Optional metadata filters
Returns:
List of search results (dict with id, score, metadata, etc.)
"""
# Usage example
results = await self.plugin.vector_search(
collection_id="kb_uuid",
query_vector=[0.1, 0.2, ...],
top_k=5,
)
# Return format: [{"id": "chunk_0", "score": 0.123, "metadata": {"document_id": "doc1", ...}}, ...]
```
Each result returned by `vector_search` is a dict containing `id` (vector ID), `score` (distance score), and `metadata` (metadata provided during upsert). If you need text content in retrieval results, store the text in metadata during ingestion.
### Vector Delete
```python theme={null}
async def vector_delete(
self,
collection_id: str,
file_ids: list[str] | None = None,
filters: dict | None = None,
) -> int:
"""Delete vectors from host's vector database
Args:
collection_id: Target collection ID
file_ids: File IDs whose vectors should be deleted
filters: Optional metadata filters for deletion
Returns:
Number of deleted items
"""
# Usage example
deleted = await self.plugin.vector_delete(
collection_id="kb_uuid",
file_ids=["doc_001"],
)
```
The `filters` parameter supports Chroma-style `where` syntax for metadata filtering. Multiple top-level keys are AND-ed. Supported operators: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`. Example: `{"file_id": {"$eq": "abc"}}`.
**Note:** Chroma, Qdrant, and SeekDB store full metadata and can filter on any field. Milvus and pgvector only store `text`, `file_id`, and `chunk_uuid` — filters on other fields will be silently ignored.
```python theme={null}
async def get_knowledge_file_stream(self, storage_path: str) -> bytes:
"""Get file content from host storage
Args:
storage_path: File storage path (from FileObject.storage_path)
Returns:
File content as bytes
"""
# Usage example
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
```
## Testing the Knowledge Engine
After creation, execute the command `lbp run` in the plugin directory to start debugging. Then in LangBot:
1. Go to the "Knowledge Base" page
2. Click "Create Knowledge Base"
3. Select the Knowledge Engine provided by your plugin and fill in the configuration based on the engine's `creation_schema`
4. After creation, upload documents to test ingestion capabilities (if the engine declares `DOC_INGESTION` capability)
5. Bind the knowledge base to a pipeline and test retrieval capabilities
# Component: Page
Source: https://docs.langbot.app/en/plugin/dev/components/page
The Page component allows plugins to register custom visual pages in the LangBot WebUI sidebar. Pages run inside an iframe sandbox, communicate with the host via the Page SDK, and can call the plugin's backend API.
## Use Cases
* **Admin panels**: Provide visual configuration and data management interfaces (e.g., FAQ management, analytics dashboards)
* **Data displays**: Show runtime statistics, logs, charts, etc.
* **Interactive tools**: Provide forms, editors, and other interactive interfaces
## Adding a Page Component
Run the following command in the plugin directory:
```bash theme={null}
lbp comp Page
```
After entering the page name, the CLI will generate page files under `components/pages/`:
```bash theme={null}
├── components
│ └── pages
│ └── dashboard
│ ├── dashboard.yaml # Page manifest
│ ├── dashboard.py # Backend handler
│ ├── index.html # Page entry point
│ └── i18n # Translation files (optional)
│ ├── en_US.json
│ └── zh_Hans.json
```
The plugin's `manifest.yaml` will also be updated with the component discovery config:
```yaml theme={null}
spec:
components:
Page:
fromDirs:
- path: components/pages/
maxDepth: 2
```
## Manifest File: Page
```yaml theme={null}
apiVersion: v1 # Do not modify
kind: Page # Do not modify
metadata:
name: dashboard # Unique page ID within the plugin
label:
en_US: Dashboard # Display name shown in the WebUI sidebar, supports i18n
zh_Hans: 仪表盘
spec:
path: index.html # HTML entry file, relative to this YAML file's directory
execution:
python:
path: dashboard.py # Backend handler file
attr: DashboardPage # Handler class name
```
## Backend Handler
The Page component's backend handler extends the `Page` base class and implements `handle_api` to process API requests from the frontend page.
```python theme={null}
from langbot_plugin.api.definition.components.page import Page, PageRequest, PageResponse
class DashboardPage(Page):
async def handle_api(self, request: PageRequest) -> PageResponse:
# request.endpoint: API endpoint path, e.g. '/stats'
# request.method: HTTP method (GET, POST, PUT, DELETE)
# request.body: Request body (parsed JSON, or None)
if request.endpoint == '/stats' and request.method == 'GET':
# Access shared plugin state via self.plugin
return PageResponse.ok({
'total': len(self.plugin.entries),
})
return PageResponse.fail(f'Unknown endpoint: {request.endpoint}')
```
### PageRequest Fields
| Field | Type | Description |
| ---------- | ----- | -------------------------------------------- |
| `endpoint` | `str` | API endpoint path (e.g. `'/entries'`) |
| `method` | `str` | HTTP method (`GET`, `POST`, `PUT`, `DELETE`) |
| `body` | `Any` | Request body (parsed JSON, or `None`) |
### PageResponse Construction
| Method | Description |
| -------------------------- | ----------------------------------------------------------- |
| `PageResponse.ok(data)` | Success response. `data` can be any JSON-serializable value |
| `PageResponse.fail(error)` | Error response. `error` is a human-readable error string |
## Frontend Page Development
Include the Page SDK in your HTML file to communicate with the plugin backend:
```html theme={null}
Dashboard
```
### Page SDK API
| Method | Description |
| --------------------------------------- | ------------------------------------------------------------------------------ |
| `langbot.onReady(callback)` | Fires when SDK is ready. `callback` receives `ctx` with `theme` and `language` |
| `langbot.api(endpoint, body?, method?)` | Calls the plugin's `handle_api`. Returns a Promise |
| `langbot.t(key, fallback?)` | Gets a translated string |
| `langbot.onThemeChange(callback)` | Fires when the theme changes |
| `langbot.onLanguageChange(callback)` | Fires when the language changes |
| `langbot.applyI18n()` | Manually re-apply `data-i18n` translations |
### Dark Mode
The SDK automatically sets CSS custom properties on the page. Use them directly:
| CSS Variable | Purpose |
| ---------------------- | -------------------- |
| `--langbot-bg` | Page background |
| `--langbot-bg-card` | Card background |
| `--langbot-text` | Primary text color |
| `--langbot-text-muted` | Secondary text color |
| `--langbot-border` | Border color |
| `--langbot-accent` | Accent color |
### Page i18n
Create an `i18n/` directory inside your page directory with JSON translation files:
```
pages/dashboard/
├── index.html
└── i18n/
├── en_US.json
└── zh_Hans.json
```
Translation files are flat JSON key-value pairs:
```json theme={null}
{
"title": "Dashboard",
"totalEntries": "Total Entries"
}
```
Add the `data-i18n` attribute to HTML elements for automatic translation:
```html theme={null}
Dashboard
```
## Full Example: FAQ Manager
Here is a complete Page component example that implements CRUD operations for FAQ entries.
**Backend handler** (`components/pages/manager/manager.py`):
```python theme={null}
from langbot_plugin.api.definition.components.page import Page, PageRequest, PageResponse
class ManagerPage(Page):
async def handle_api(self, request: PageRequest) -> PageResponse:
plugin = self.plugin
if request.endpoint == '/entries' and request.method == 'GET':
return PageResponse.ok({'entries': plugin.entries})
if request.endpoint == '/entries' and request.method == 'POST':
question = (request.body or {}).get('question', '').strip()
answer = (request.body or {}).get('answer', '').strip()
if not question or not answer:
return PageResponse.fail('question and answer are required')
entry = plugin.add_entry(question, answer)
await plugin.persist()
return PageResponse.ok({'entry': entry})
if request.endpoint == '/entries' and request.method == 'DELETE':
entry_id = (request.body or {}).get('id', '')
if plugin.delete_entry(entry_id):
await plugin.persist()
return PageResponse.ok({'deleted': entry_id})
return PageResponse.fail('entry not found')
return PageResponse.fail(f'Unknown: {request.method} {request.endpoint}')
```
**Frontend page** (`components/pages/manager/index.html`) calls the backend via `langbot.api()`:
```javascript theme={null}
// Load entries
var data = await langbot.api('/entries', null, 'GET');
// Add entry
await langbot.api('/entries', { question: '...', answer: '...' }, 'POST');
// Delete entry
await langbot.api('/entries', { id: '...' }, 'DELETE');
```
Full example code is available at [FAQManager plugin](https://github.com/langbot-app/langbot-plugin-demo/tree/main/FAQManager).
## Notes
* Pages run inside a `sandbox="allow-scripts allow-forms"` iframe and cannot open popups or navigate the parent page
* Access shared plugin state via `self.plugin` — Page and Tool components can share data
* Use `PageResponse.ok()` and `PageResponse.fail()` to construct responses for consistent formatting
* The Page SDK `
```
The embed code URL defaults to `localhost`. If your LangBot is deployed on a server, replace `localhost:5300` with your actual address.
## Anti-Abuse (Optional)
To protect against abuse, you can enable Cloudflare Turnstile:
1. Go to [Cloudflare Turnstile](https://dash.cloudflare.com/) > Add Site
2. Get the **Site Key** and **Secret Key**
3. Fill them into the corresponding fields in the LangBot Web Page Bot configuration
4. Save to take effect
## Local Preview / Demo
Want to try the embed widget before wiring it into your own site? The LangBot
main repo ships a self-contained single-file demo page `index.html` under
[`examples/web-page-bot/`](https://github.com/langbot-app/LangBot/tree/master/examples/web-page-bot):
open it in a browser, fill in your LangBot address and Page Bot UUID, and it
loads the live embed widget so you can chat with the bot just like a site
visitor — and generates the exact `