[verified] feat: synchronize Blog articles into Wiki (#113)
Co-authored-by: dadachann <[email protected]>
@@ -0,0 +1,52 @@
|
||||
name: Sync LangBot Blog articles
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "17 2 * * *"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: sync-langbot-blog-articles
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out Wiki
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check out LangBot landing page
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: langbot-app/langbot-landing-page
|
||||
path: .article-source
|
||||
persist-credentials: false
|
||||
|
||||
- name: Synchronize articles and images
|
||||
run: node scripts/sync-blog-articles.mjs --source .article-source
|
||||
|
||||
- name: Validate generated articles
|
||||
run: |
|
||||
node scripts/sync-blog-articles.mjs --check --source .article-source
|
||||
node scripts/sync-blog-articles.mjs --check
|
||||
npm test
|
||||
npx --yes [email protected] validate
|
||||
|
||||
- name: Commit synchronized content
|
||||
run: |
|
||||
rm -rf .article-source
|
||||
if git diff --quiet && test -z "$(git status --short)"; then
|
||||
echo "LangBot Blog articles are already synchronized."
|
||||
exit 0
|
||||
fi
|
||||
git config user.name "langbot-wiki-bot"
|
||||
git config user.email "[email protected]"
|
||||
git add docs.json en/articles zh/articles ja/articles images/articles scripts/blog-articles-manifest.json
|
||||
git commit -m "docs(articles): sync LangBot Blog"
|
||||
git push
|
||||
@@ -710,6 +710,49 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Articles",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Browse",
|
||||
"pages": [
|
||||
"en/articles/index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Product Updates",
|
||||
"pages": [
|
||||
"en/articles/langbot-cloud-multi-user-workspaces"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Engineering",
|
||||
"pages": [
|
||||
"en/articles/langbot-v4100-sandbox-skills",
|
||||
"en/articles/langbot-v490-rag-plugin-architecture",
|
||||
"en/articles/langbot-plugin-system-deep-dive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Tutorials & Integrations",
|
||||
"pages": [
|
||||
"en/articles/langtars-remote-pc-control-dify-n8n",
|
||||
"en/articles/connect-deepseek-to-wechat",
|
||||
"en/articles/deploy-ai-bot-in-5-minutes",
|
||||
"en/articles/dify-agent-discord-telegram-slack",
|
||||
"en/articles/langflow-drag-and-drop-ai-bot",
|
||||
"en/articles/n8n-multi-platform-ai-chatbot",
|
||||
"en/articles/dify-langbot-rag-knowledge-base"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Announcements",
|
||||
"pages": [
|
||||
"en/articles/welcome"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API Reference",
|
||||
"groups": [
|
||||
@@ -949,6 +992,49 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "文章",
|
||||
"groups": [
|
||||
{
|
||||
"group": "浏览",
|
||||
"pages": [
|
||||
"zh/articles/index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "产品动态",
|
||||
"pages": [
|
||||
"zh/articles/langbot-cloud-multi-user-workspaces"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "技术解析",
|
||||
"pages": [
|
||||
"zh/articles/langbot-v4100-sandbox-skills",
|
||||
"zh/articles/langbot-v490-rag-plugin-architecture",
|
||||
"zh/articles/langbot-plugin-system-deep-dive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "教程与集成",
|
||||
"pages": [
|
||||
"zh/articles/langtars-remote-pc-control-dify-n8n",
|
||||
"zh/articles/connect-deepseek-to-wechat",
|
||||
"zh/articles/deploy-ai-bot-in-5-minutes",
|
||||
"zh/articles/dify-agent-discord-telegram-slack",
|
||||
"zh/articles/langflow-drag-and-drop-ai-bot",
|
||||
"zh/articles/n8n-multi-platform-ai-chatbot",
|
||||
"zh/articles/dify-langbot-rag-knowledge-base"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "公告",
|
||||
"pages": [
|
||||
"zh/articles/welcome"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API 参考",
|
||||
"groups": [
|
||||
@@ -1163,6 +1249,49 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "記事",
|
||||
"groups": [
|
||||
{
|
||||
"group": "一覧",
|
||||
"pages": [
|
||||
"ja/articles/index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "製品アップデート",
|
||||
"pages": [
|
||||
"ja/articles/langbot-cloud-multi-user-workspaces"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "エンジニアリング",
|
||||
"pages": [
|
||||
"ja/articles/langbot-v4100-sandbox-skills",
|
||||
"ja/articles/langbot-v490-rag-plugin-architecture",
|
||||
"ja/articles/langbot-plugin-system-deep-dive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "チュートリアルと連携",
|
||||
"pages": [
|
||||
"ja/articles/langtars-remote-pc-control-dify-n8n",
|
||||
"ja/articles/connect-deepseek-to-wechat",
|
||||
"ja/articles/deploy-ai-bot-in-5-minutes",
|
||||
"ja/articles/dify-agent-discord-telegram-slack",
|
||||
"ja/articles/langflow-drag-and-drop-ai-bot",
|
||||
"ja/articles/n8n-multi-platform-ai-chatbot",
|
||||
"ja/articles/dify-langbot-rag-knowledge-base"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "お知らせ",
|
||||
"pages": [
|
||||
"ja/articles/welcome"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API リファレンス",
|
||||
"groups": [
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: "How to Connect DeepSeek R1 to WeChat, Discord & Telegram in 5 Minutes (FREE)"
|
||||
description: "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
|
||||
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).
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
title: "Deploy Your Own AI Bot to Discord, Telegram & WeChat in 5 Minutes"
|
||||
description: "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
|
||||
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
|
||||
git clone https://github.com/langbot-app/LangBot
|
||||
cd LangBot/docker
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Your AI bot empire starts with one command.
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
title: "Finally Got My Dify Agent Working in Discord, Telegram and Slack"
|
||||
description: "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
|
||||
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)
|
||||
@@ -0,0 +1,204 @@
|
||||
---
|
||||
title: "LangBot 4.6.0 External Knowledge Base Tutorial: Integrating Dify with LangBot for RAG-powered Conversations"
|
||||
description: "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)
|
||||
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 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 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
|
||||
- **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)
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "LangBot Articles"
|
||||
description: "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
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: "The new LangBot Cloud is here — and multi-user Workspaces are now in open source"
|
||||
description: "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).
|
||||
@@ -0,0 +1,459 @@
|
||||
---
|
||||
title: "Deep Dive into the LangBot Plugin System: Process Isolation, Event-Driven Hooks, and Component Architecture"
|
||||
description: "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
|
||||
# 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
|
||||
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
|
||||
# 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
|
||||
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
|
||||
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
|
||||
# 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
# 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.
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: "LangBot v4.10.0: Agentic Sandbox & Skills — Letting Your Agent Work Like a Developer"
|
||||
description: "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.
|
||||
@@ -0,0 +1,293 @@
|
||||
---
|
||||
title: "LangBot v4.9.0: Full RAG Plugin Architecture — Knowledge Without Borders"
|
||||
description: "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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
lbp comp Parser
|
||||
```
|
||||
|
||||
Declare supported MIME types in the manifest:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
supported_mime_types:
|
||||
- application/pdf
|
||||
- application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
```
|
||||
|
||||
Implement the `parse` method:
|
||||
|
||||
```python
|
||||
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
|
||||
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)
|
||||
@@ -0,0 +1,207 @@
|
||||
---
|
||||
title: "How I Built a Multi-Platform AI Bot with Langflow's Drag-and-Drop Workflows"
|
||||
description: "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
|
||||
uvx langbot
|
||||
```
|
||||
|
||||
First run auto-initializes and opens your browser to http://127.0.0.1:5300.
|
||||
|
||||

|
||||
|
||||
After registration, log in to access the dashboard:
|
||||
|
||||

|
||||
|
||||
## Step 2: Deploy Langflow
|
||||
|
||||
Deploy quickly with Docker:
|
||||
|
||||
```bash
|
||||
docker run -d --name langflow -p 7860:7860 langflowai/langflow:latest
|
||||
```
|
||||
|
||||
Visit 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
|
||||
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.*
|
||||
@@ -0,0 +1,200 @@
|
||||
---
|
||||
title: "LangTARS: Open-Source AI Agent for Remote PC Control — Works with Dify, n8n & 10+ Messaging Platforms"
|
||||
description: "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
|
||||
# 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
|
||||
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 <Task Description>` | 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
|
||||
uvx langbot@latest
|
||||
```
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: "How I Built a Multi-Platform AI Chatbot with n8n and LangBot"
|
||||
description: "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
|
||||
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
|
||||
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
|
||||
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.
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: "Introducing the LangBot Blog"
|
||||
description: "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!
|
||||
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 717 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 445 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 205 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 333 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 354 KiB |
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: "DeepSeek R1 を WeChat・Discord・Telegram に5分で接続する方法"
|
||||
description: "A step-by-step guide to connecting DeepSeek R1/V3 API to WeChat (Personal/Enterprise), Discord, Telegram, and more using LangBot. No coding required."
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/connect-deepseek-to-wechat).
|
||||
|
||||
公開日:2026-02-21 · 著者: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
|
||||
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).
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: "AI Bot を Discord・Telegram・WeChat に5分でデプロイ"
|
||||
description: "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."
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/deploy-ai-bot-in-5-minutes).
|
||||
|
||||
公開日:2026-02-17 · 著者: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
|
||||
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
|
||||
git clone https://github.com/langbot-app/LangBot
|
||||
cd LangBot/docker
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Your AI bot empire starts with one command.
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: "Dify Agent を Discord・Telegram・Slack で動かす"
|
||||
description: "Finally Got My Dify Agent Working in Discord, Telegram and Slack"
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/dify-agent-discord-telegram-slack).
|
||||
|
||||
公開日:2025-12-11 · 著者: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
|
||||
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)
|
||||
@@ -0,0 +1,206 @@
|
||||
---
|
||||
title: "LangBot 4.6.0 外部ナレッジベース入門:Dify と連携した RAG 会話"
|
||||
description: "LangBot 4.6.0 External Knowledge Base Tutorial: Integrating Dify with LangBot for RAG-powered Conversations"
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/dify-langbot-rag-knowledge-base).
|
||||
|
||||
公開日:2025-12-02 · 著者: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)
|
||||
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 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 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
|
||||
- **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)
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "LangBot 記事"
|
||||
description: "LangBot Blog の製品アップデート、技術解説、チュートリアル、連携事例、お知らせをまとめています。"
|
||||
---
|
||||
|
||||
LangBot Blog の製品アップデート、技術解説、チュートリアル、連携事例、お知らせをまとめています。
|
||||
|
||||
## 製品アップデート
|
||||
|
||||
- [新しい LangBot Cloud が公開—オープンソース版もマルチユーザー Workspace に対応](/ja/articles/langbot-cloud-multi-user-workspaces) — 2026-08-25
|
||||
|
||||
## エンジニアリング
|
||||
|
||||
- [LangBot v4.10.0:Agent Sandbox と Skills](/ja/articles/langbot-v4100-sandbox-skills) — 2026-06-04
|
||||
- [LangBot v4.9.0:完全プラグイン化された RAG アーキテクチャ](/ja/articles/langbot-v490-rag-plugin-architecture) — 2026-03-10
|
||||
- [LangBot プラグインシステム詳解:プロセス分離・イベント駆動・コンポーネント設計](/ja/articles/langbot-plugin-system-deep-dive) — 2026-02-23
|
||||
|
||||
## チュートリアルと連携
|
||||
|
||||
- [LangTARS:Dify・n8n と連携するオープンソース PC 操作 Agent](/ja/articles/langtars-remote-pc-control-dify-n8n) — 2026-03-23
|
||||
- [DeepSeek R1 を WeChat・Discord・Telegram に5分で接続する方法](/ja/articles/connect-deepseek-to-wechat) — 2026-02-21
|
||||
- [AI Bot を Discord・Telegram・WeChat に5分でデプロイ](/ja/articles/deploy-ai-bot-in-5-minutes) — 2026-02-17
|
||||
- [Dify Agent を Discord・Telegram・Slack で動かす](/ja/articles/dify-agent-discord-telegram-slack) — 2025-12-11
|
||||
- [Langflow のドラッグ&ドロップでマルチプラットフォーム AI Bot を構築](/ja/articles/langflow-drag-and-drop-ai-bot) — 2025-12-05
|
||||
- [n8n と LangBot でマルチプラットフォーム AI Chatbot を構築](/ja/articles/n8n-multi-platform-ai-chatbot) — 2025-12-03
|
||||
- [LangBot 4.6.0 外部ナレッジベース入門:Dify と連携した RAG 会話](/ja/articles/dify-langbot-rag-knowledge-base) — 2025-12-02
|
||||
|
||||
## お知らせ
|
||||
|
||||
- [LangBot Blog のご紹介](/ja/articles/welcome) — 2026-02-17
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: "新しい LangBot Cloud が公開—オープンソース版もマルチユーザー Workspace に対応"
|
||||
description: "LangBot は LLM とメッセージングアプリを接続するオープンソース AI Bot プラットフォームです。新しい Cloud とオープンソース版はマルチユーザー Workspace、招待、ロール権限に対応しました。"
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/ja/blog/langbot-cloud-multi-user-workspaces).
|
||||
|
||||
公開日:2026-08-25 · 著者:LangBot Team
|
||||
|
||||

|
||||
|
||||
新しい [LangBot Cloud](https://cloud.langbot.app) を公開しました。今回まず整理したのは、複数人で LangBot を使うときに、誰がどのリソースへアクセスできるかという実務的な問題です。
|
||||
|
||||
同じマルチユーザーの基盤は、オープンソース版でも利用できます。Cloud とセルフホスト版では運用方法が異なりますが、メンバー、招待、権限の基本的な考え方は共通になりました。
|
||||
|
||||
## LangBot とは
|
||||
|
||||
LangBot は、複数のメッセージングアプリで AI Bot を構築・運用するためのオープンソースプラットフォームです。1つのダッシュボードで Bot、メッセージ Pipeline、ナレッジベース、モデル、拡張機能を管理し、LINE、Slack、Discord、Telegram、QQ、WeChat、WeCom、Lark、DingTalk、KOOK に接続できます。Satori と Matrix にも対応し、Matrix ブリッジを使えば WhatsApp、Signal、Messenger、Mattermost、Google Chat、IRC、XMPP、Zulip などにも展開できます。
|
||||
|
||||
モデルは特定の事業者に限定されません。OpenAI、Anthropic Claude、DeepSeek、Google Gemini、xAI Grok、Moonshot Kimi、Zhipu GLM に接続でき、Ollama と LM Studio ではローカルモデルも利用できます。SiliconFlow、Alibaba Cloud Model Studio(百錬)、Volcengine Ark、ModelScope、GiteeAI などのゲートウェイに加え、Dify と MCP を使った既存の Agent ワークフローにも対応します。
|
||||
|
||||
チームは複数のチャットサービス上の AI アシスタントを1か所で管理し、用途に応じてクラウドモデル、ローカルモデル、既存の Agent ワークフローを使い分けられます。
|
||||
|
||||

|
||||
|
||||
## Cloud:自分の Workspace から始める
|
||||
|
||||
LangBot Cloud は LangBot Account でログインします。初回利用時には個人用 Workspace が用意され、招待を受けてチームの Workspace に参加できます。複数の Workspace に所属している場合は、画面上で作業先を切り替えられます。
|
||||
|
||||
Bot、Pipeline、モデル、ナレッジベース、拡張機能は、現在選択している Workspace に属します。Workspace を切り替えても、別のチームの設定やデータが混ざることはありません。
|
||||
|
||||
ホスティング、更新、実行環境は引き続き Cloud 側が管理します。サーバーを保守せず、チームですぐに Bot の設定を始めたい場合に向いています。
|
||||
|
||||
## 管理者アカウントを共有せず、メンバーを招待
|
||||
|
||||
Owner、またはメンバー管理権限を持つロールは、一度だけ使える招待を発行できます。招待された人は自分のアカウントで参加し、割り当てられたロールで Workspace を利用します。
|
||||
|
||||

|
||||
|
||||
1つの管理者パスワードを共有するより、長期運用に適した形です。利用者ごとに独立した本人性があり、担当に合わせて権限を分けられます。
|
||||
|
||||
現在は5つの固定ロールを用意しています。
|
||||
|
||||
- **Owner**:Workspace の所有者。最上位の権限とメンバー管理を担当します。
|
||||
- **Admin**:メンバーと大部分の Workspace 設定を管理します。
|
||||
- **Developer**:Bot、Pipeline、モデル、拡張機能を設定します。
|
||||
- **Operator**:日常的な運用操作を担当します。
|
||||
- **Viewer**:読み取り専用で確認します。
|
||||
|
||||
## オープンソース版:1インスタンス、1 Workspace、複数ユーザー
|
||||
|
||||
セルフホスト版がマルチ Workspace の SaaS になるわけではありません。1つの LangBot インスタンスは引き続き1つの Workspace に対応しますが、その Workspace に複数のユーザーを追加できます。
|
||||
|
||||
初期設定で作成したアカウントが Owner になります。その後、ほかのメンバーを招待できます。SMTP を設定している場合は招待メールを送信でき、メール設定がない場合は一度だけ表示される招待リンクを信頼できる手段で共有できます。
|
||||
|
||||
参加後に使うのは、これまでと同じ LangBot のダッシュボードです。異なる点は、ログインした全員を管理者として扱うのではなく、Workspace とロールの両方に基づいてアクセスを確認することです。
|
||||
|
||||
## どちらを選ぶべきか
|
||||
|
||||
| | LangBot Cloud | オープンソース・セルフホスト版 |
|
||||
|---|---|---|
|
||||
| 導入と保守 | LangBot がホスト・保守 | サーバーとデータを自分で管理 |
|
||||
| Workspace | 複数を所有・参加可能 | 1インスタンスにつき1つ |
|
||||
| メンバーと招待 | 対応 | 対応 |
|
||||
| 固定ロール権限 | 対応 | 対応 |
|
||||
| 向いている用途 | チームですぐに使い始める | プライベート環境とインフラを自分で管理する |
|
||||
|
||||
ホスト版を使う場合は [cloud.langbot.app](https://cloud.langbot.app) へ。実行環境とデータを自分のサーバーに置く場合は、[GitHub](https://github.com/langbot-app/LangBot) からオープンソース版を導入できます。
|
||||
@@ -0,0 +1,461 @@
|
||||
---
|
||||
title: "LangBot プラグインシステム詳解:プロセス分離・イベント駆動・コンポーネント設計"
|
||||
description: "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."
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/langbot-plugin-system-deep-dive).
|
||||
|
||||
公開日:2026-02-23 · 著者: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
|
||||
# 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
|
||||
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
|
||||
# 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
|
||||
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
|
||||
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
|
||||
# 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
# 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.
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
title: "LangBot v4.10.0:Agent Sandbox と Skills"
|
||||
description: "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."
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/langbot-v4100-sandbox-skills).
|
||||
|
||||
公開日:2026-06-04 · 著者: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.
|
||||
@@ -0,0 +1,295 @@
|
||||
---
|
||||
title: "LangBot v4.9.0:完全プラグイン化された RAG アーキテクチャ"
|
||||
description: "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."
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/langbot-v490-rag-plugin-architecture).
|
||||
|
||||
公開日:2026-03-10 · 著者: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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
lbp comp Parser
|
||||
```
|
||||
|
||||
Declare supported MIME types in the manifest:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
supported_mime_types:
|
||||
- application/pdf
|
||||
- application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
```
|
||||
|
||||
Implement the `parse` method:
|
||||
|
||||
```python
|
||||
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
|
||||
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)
|
||||
@@ -0,0 +1,209 @@
|
||||
---
|
||||
title: "Langflow のドラッグ&ドロップでマルチプラットフォーム AI Bot を構築"
|
||||
description: "How I Built a Multi-Platform AI Bot with Langflow's Drag-and-Drop Workflows"
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/langflow-drag-and-drop-ai-bot).
|
||||
|
||||
公開日:2025-12-05 · 著者: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
|
||||
uvx langbot
|
||||
```
|
||||
|
||||
First run auto-initializes and opens your browser to http://127.0.0.1:5300.
|
||||
|
||||

|
||||
|
||||
After registration, log in to access the dashboard:
|
||||
|
||||

|
||||
|
||||
## Step 2: Deploy Langflow
|
||||
|
||||
Deploy quickly with Docker:
|
||||
|
||||
```bash
|
||||
docker run -d --name langflow -p 7860:7860 langflowai/langflow:latest
|
||||
```
|
||||
|
||||
Visit 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
|
||||
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.*
|
||||
@@ -0,0 +1,202 @@
|
||||
---
|
||||
title: "LangTARS:Dify・n8n と連携するオープンソース PC 操作 Agent"
|
||||
description: "LangTARS is a LangBot native plugin that lets you remotely control Mac/Windows/Linux from Telegram, Discord & WeChat — with Dify and n8n workflow support."
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/langtars-remote-pc-control-dify-n8n).
|
||||
|
||||
公開日:2026-03-23 · 著者: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
|
||||
# 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
|
||||
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 <Task Description>` | 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
|
||||
uvx langbot@latest
|
||||
```
|
||||
@@ -0,0 +1,186 @@
|
||||
---
|
||||
title: "n8n と LangBot でマルチプラットフォーム AI Chatbot を構築"
|
||||
description: "How I Built a Multi-Platform AI Chatbot with n8n and LangBot"
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/n8n-multi-platform-ai-chatbot).
|
||||
|
||||
公開日:2025-12-03 · 著者: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
|
||||
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
|
||||
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
|
||||
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.
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "LangBot Blog のご紹介"
|
||||
description: "Welcome to the official LangBot blog. Stay up to date with product updates, new features, and milestones."
|
||||
---
|
||||
|
||||
この記事は LangBot Blog から同期されています。 [Blog の原文を読む](https://langbot.app/en/blog/welcome).
|
||||
|
||||
公開日:2026-02-17 · 著者: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!
|
||||
@@ -2,6 +2,8 @@
|
||||
"scripts": {
|
||||
"dev": "npx mintlify dev",
|
||||
"generate:sitemap-alternates": "node scripts/generate-alternate-sitemap.mjs",
|
||||
"sync:articles": "node scripts/sync-blog-articles.mjs",
|
||||
"check:articles": "node scripts/sync-blog-articles.mjs --check",
|
||||
"test": "node --test tests/*.test.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# LangBot Blog → Wiki article synchronization
|
||||
|
||||
`langbot-app/langbot-landing-page/src/content/blog` is the canonical article source. The Wiki publishes generated copies under `{en,zh,ja}/articles/` and stores every rendered image locally under `images/articles/` so Wiki pages do not depend on third-party hotlinks.
|
||||
|
||||
## Run locally
|
||||
|
||||
From the Wiki checkout, with the landing-page repository available as a sibling checkout:
|
||||
|
||||
```bash
|
||||
node scripts/sync-blog-articles.mjs --source ../langbot-landing-page
|
||||
node scripts/sync-blog-articles.mjs --check --source ../langbot-landing-page
|
||||
node scripts/sync-blog-articles.mjs --check
|
||||
npm test
|
||||
```
|
||||
|
||||
The synchronizer:
|
||||
|
||||
- requires matching English and Chinese slug sets;
|
||||
- uses the canonical Japanese article when one exists and a clearly marked English fallback otherwise;
|
||||
- groups navigation into product updates, engineering, tutorials/integrations, and announcements with localized labels;
|
||||
- copies source images and downloads external images into the Wiki repository;
|
||||
- rewrites Markdown image URLs to Wiki-local paths;
|
||||
- escapes prose that would otherwise be parsed as MDX expressions;
|
||||
- replaces the generated article directories and updates the `docs.json` article tabs deterministically.
|
||||
|
||||
Do not edit generated article pages or mirrored images manually. Fix the canonical Blog post or the synchronizer, then regenerate.
|
||||
|
||||
A daily GitHub Actions workflow provides drift recovery. Article publishing work should still synchronize and verify the Wiki in the same task so a new Blog post does not wait for the scheduled run.
|
||||
@@ -0,0 +1,594 @@
|
||||
{
|
||||
"source": "https://github.com/langbot-app/langbot-landing-page/tree/main/src/content/blog",
|
||||
"articles": [
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "connect-deepseek-to-wechat",
|
||||
"page": "en/articles/connect-deepseek-to-wechat",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "deploy-ai-bot-in-5-minutes",
|
||||
"page": "en/articles/deploy-ai-bot-in-5-minutes",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "dify-agent-discord-telegram-slack",
|
||||
"page": "en/articles/dify-agent-discord-telegram-slack",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "dify-langbot-rag-knowledge-base",
|
||||
"page": "en/articles/dify-langbot-rag-knowledge-base",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "langbot-cloud-multi-user-workspaces",
|
||||
"page": "en/articles/langbot-cloud-multi-user-workspaces",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "langbot-plugin-system-deep-dive",
|
||||
"page": "en/articles/langbot-plugin-system-deep-dive",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "langbot-v4100-sandbox-skills",
|
||||
"page": "en/articles/langbot-v4100-sandbox-skills",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "langbot-v490-rag-plugin-architecture",
|
||||
"page": "en/articles/langbot-v490-rag-plugin-architecture",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "langflow-drag-and-drop-ai-bot",
|
||||
"page": "en/articles/langflow-drag-and-drop-ai-bot",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "langtars-remote-pc-control-dify-n8n",
|
||||
"page": "en/articles/langtars-remote-pc-control-dify-n8n",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "n8n-multi-platform-ai-chatbot",
|
||||
"page": "en/articles/n8n-multi-platform-ai-chatbot",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "en",
|
||||
"slug": "welcome",
|
||||
"page": "en/articles/welcome",
|
||||
"sourceLocale": "en",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "connect-deepseek-to-wechat",
|
||||
"page": "zh/articles/connect-deepseek-to-wechat",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "deploy-ai-bot-in-5-minutes",
|
||||
"page": "zh/articles/deploy-ai-bot-in-5-minutes",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "dify-agent-discord-telegram-slack",
|
||||
"page": "zh/articles/dify-agent-discord-telegram-slack",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "dify-langbot-rag-knowledge-base",
|
||||
"page": "zh/articles/dify-langbot-rag-knowledge-base",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "langbot-cloud-multi-user-workspaces",
|
||||
"page": "zh/articles/langbot-cloud-multi-user-workspaces",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "langbot-plugin-system-deep-dive",
|
||||
"page": "zh/articles/langbot-plugin-system-deep-dive",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "langbot-v4100-sandbox-skills",
|
||||
"page": "zh/articles/langbot-v4100-sandbox-skills",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "langbot-v490-rag-plugin-architecture",
|
||||
"page": "zh/articles/langbot-v490-rag-plugin-architecture",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "langflow-drag-and-drop-ai-bot",
|
||||
"page": "zh/articles/langflow-drag-and-drop-ai-bot",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "langtars-remote-pc-control-dify-n8n",
|
||||
"page": "zh/articles/langtars-remote-pc-control-dify-n8n",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "n8n-multi-platform-ai-chatbot",
|
||||
"page": "zh/articles/n8n-multi-platform-ai-chatbot",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "zh",
|
||||
"slug": "welcome",
|
||||
"page": "zh/articles/welcome",
|
||||
"sourceLocale": "zh",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "connect-deepseek-to-wechat",
|
||||
"page": "ja/articles/connect-deepseek-to-wechat",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "deploy-ai-bot-in-5-minutes",
|
||||
"page": "ja/articles/deploy-ai-bot-in-5-minutes",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "dify-agent-discord-telegram-slack",
|
||||
"page": "ja/articles/dify-agent-discord-telegram-slack",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "dify-langbot-rag-knowledge-base",
|
||||
"page": "ja/articles/dify-langbot-rag-knowledge-base",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "langbot-cloud-multi-user-workspaces",
|
||||
"page": "ja/articles/langbot-cloud-multi-user-workspaces",
|
||||
"sourceLocale": "ja",
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "langbot-plugin-system-deep-dive",
|
||||
"page": "ja/articles/langbot-plugin-system-deep-dive",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "langbot-v4100-sandbox-skills",
|
||||
"page": "ja/articles/langbot-v4100-sandbox-skills",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "langbot-v490-rag-plugin-architecture",
|
||||
"page": "ja/articles/langbot-v490-rag-plugin-architecture",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "langflow-drag-and-drop-ai-bot",
|
||||
"page": "ja/articles/langflow-drag-and-drop-ai-bot",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "langtars-remote-pc-control-dify-n8n",
|
||||
"page": "ja/articles/langtars-remote-pc-control-dify-n8n",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "n8n-multi-platform-ai-chatbot",
|
||||
"page": "ja/articles/n8n-multi-platform-ai-chatbot",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
},
|
||||
{
|
||||
"locale": "ja",
|
||||
"slug": "welcome",
|
||||
"page": "ja/articles/welcome",
|
||||
"sourceLocale": "en",
|
||||
"fallback": true
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"path": "/images/articles/external/03443176ae5c-08-n8n-webhook-node.png",
|
||||
"sha256": "8b05859a0d3c77a11755e493b464eb12a04f1abd341f982432f06be5e737d72f",
|
||||
"bytes": 92552
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/0c8aa2c940ab-01-langbot-initial-page.png",
|
||||
"sha256": "5dadd6447bca26f2aea014cdb63d075f655c30194c0e46b15b3f79c7dab768ee",
|
||||
"bytes": 38365
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/12d0e04600c5-05-n8n-setup.png",
|
||||
"sha256": "52cc42c6f1e12c4df29ac5a646888154e9193b33b28cdd678e8d88a6123bf0eb",
|
||||
"bytes": 30953
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/21fc47358066-05-langflow-api-keys-page.png",
|
||||
"sha256": "758bf810158bf3fe5ec1b1571086371db457de809de91743ade776c2c7497693",
|
||||
"bytes": 54223
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/2a3a0d5f4ae9-install-flow.png",
|
||||
"sha256": "7fe8844aeb196a3cb3c6338b4b6b7cf8c5255780a37a7aedd2cd5e2adaf3f99c",
|
||||
"bytes": 138088
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/2fb54ce0f7a2-11-langbot-langflow-config-filled.png",
|
||||
"sha256": "3da8e3faf3984091df9b176c5d05d341cbd3427ac35a61047cb6353d20edf418",
|
||||
"bytes": 100965
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/3c7d6bc96738-08-langbot-pipeline-ai-tab.png",
|
||||
"sha256": "a0c20a44d45f20d7588f611eb1ac006be68360f5289b2a767f36a6a35b16449b",
|
||||
"bytes": 88489
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/3d7fa393f210-13-langbot-chat-with-response.png",
|
||||
"sha256": "e1c8d41b8efb17a31fc9b41206b44e0d2a1bf83099a2d4d29d3b2c59ed9142e9",
|
||||
"bytes": 62219
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/3e5b1d8ec6ec-07-dify-create-app-types.png",
|
||||
"sha256": "6f161d5de119721af085ed40ce86c50aca4d6566f97241f7eeba1f83aefd3f0d",
|
||||
"bytes": 165144
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/3fe5bdc18716-icon.svg",
|
||||
"sha256": "430b8fc2bbcd1799231285f9f9645becce0d4cf7bef4412769db425f52cfac23",
|
||||
"bytes": 734399
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/471b316018e5-13-dify-api-key-created.png",
|
||||
"sha256": "776930876f0c50ef7bd70ff81faa4fdae2a66de4dbec4a3b595d2db3213c7ff3",
|
||||
"bytes": 128227
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/4f1f5c28fbfc-03-langbot-dashboard.png",
|
||||
"sha256": "3d6769445112e80fddf079028f81e4df67d28374b20c4f3a94086a535ed38fa3",
|
||||
"bytes": 38244
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/621f3d526678-09-n8n-webhook-configured.png",
|
||||
"sha256": "e19bb4356edfb75f463928528f702ed8a765d879ea377bf9721c2a24088f457a",
|
||||
"bytes": 48125
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/6613b3025b8b-usage-example.png",
|
||||
"sha256": "387e811bf57f83b121842a0e9066f2c045ad18e2666f6c04ae5eb568c288efc1",
|
||||
"bytes": 126628
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/6e24402fd4a8-03-langflow-workflow-editor.png",
|
||||
"sha256": "53069258d131ed4e87e6467eb95e8b90764684d5c948bdae9b4109f07667b158",
|
||||
"bytes": 212230
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/70b26e660a60-tars.gif",
|
||||
"sha256": "ec59948d2c335c17f373186b1913703017c8664554b53f7fb49c3eae0ee19ed5",
|
||||
"bytes": 1544475
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/70ee40a6d232-01-langbot-initialization.png",
|
||||
"sha256": "b9f45c78b49bdfdb51e278b2e491b88ca35d1a4ed05f33fc4c15085baacf2391",
|
||||
"bytes": 42702
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/74f71a8bf1dc-17-langbot-dify-api-key-entered.png",
|
||||
"sha256": "225397739ad8458f38002b75dfbbdc6eea268e9e3d1dd478e8a498b7a302e0d4",
|
||||
"bytes": 101173
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/7e7ebfc3ba58-01-langflow-welcome.png",
|
||||
"sha256": "62876dd239a917eb88ba7de369f9444e1c2fa169f3a347715c86c9bbaa4fba36",
|
||||
"bytes": 50425
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/82d5e2ffb9a0-10-langbot-langflow-config-form.png",
|
||||
"sha256": "56d2046d64179008e6d0384bce1dd4797f9818d866df86bf20ce16f9db322bbd",
|
||||
"bytes": 92112
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/8f04c7b00759-06-langflow-create-api-key-dialog.png",
|
||||
"sha256": "19a51834168c350185b0d2de1dd83a025d14d75bd3dfe3278997f755a258b3ae",
|
||||
"bytes": 72422
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/8f4b3820ed5b-14-langbot-pipeline-ai-capability.png",
|
||||
"sha256": "b751c1d8eacca6b9f5ffe6645d3d2b2d2557dacb88002b85dd1bae2f9d81f6ed",
|
||||
"bytes": 86022
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/9066d99b172c-11-dify-tools-panel.png",
|
||||
"sha256": "b099d333ded4cdf4e3392de8f444b3e7e7be81804e717b9c0a9735f0aff35844",
|
||||
"bytes": 176867
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/9999c12907eb-20-langbot-chat-test.png",
|
||||
"sha256": "ee409e91f03415bda392e8d51d31ffea16a4489773255f8c3690a63d6d67ced7",
|
||||
"bytes": 61568
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/a618d3d41416-security.png",
|
||||
"sha256": "18bf5b073596354b64c0abd4692c79f73da24b2e69b400c9164e3b62da76e361",
|
||||
"bytes": 91790
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/b304cebf8ce9-02-langflow-template-selection.png",
|
||||
"sha256": "d28ff1118326a43cb089ce21fd7fd4bb8c181bd037ac33b461dbf6e10a00dd3c",
|
||||
"bytes": 455482
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/b36d6dea3702-12-langbot-debug-chat-interface.png",
|
||||
"sha256": "30a260b07cc96584a39de1419d53b17991b9d86c10a956ac5896a91ab5617f49",
|
||||
"bytes": 55005
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/b93d97b5b830-04-langbot-dashboard.png",
|
||||
"sha256": "ae9dfe886f8802e1d35fa185b94465d40a229145ed65279d9b89f7bb63bd430b",
|
||||
"bytes": 35641
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/b9a33a8551a7-8dd37939-4f10-4046-bd84-cce798005cd4.png",
|
||||
"sha256": "1dbae106a3306820c6364faa7e21cf57fee5ea76b1187ed809c6faa8f3985863",
|
||||
"bytes": 140727
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/bcd91ef5e4b3-14-langflow-openai-api-key-configured.png",
|
||||
"sha256": "785b4cf39a62539eb334a750bc8e53466077198f120786fdb9c078b7e062d0d6",
|
||||
"bytes": 209881
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/be278ca435e8-12-dify-agent-configured.png",
|
||||
"sha256": "5feda01e6419a40658786e906a59b5e1c7e27503842e5b08fe3990d2d5b44a1c",
|
||||
"bytes": 141049
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/c6d52212ba91-02-langbot-dashboard.png",
|
||||
"sha256": "43a4eabed41c1ac9a356d33b8a160afac7a74ad1d06452827b3dd6c03017195a",
|
||||
"bytes": 36354
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/e36d04ca4a5b-07-n8n-workflow-editor.png",
|
||||
"sha256": "f845f0d7fd7553af2406c0e3bc1b461b4a5dd76ebd108f8ff3b48c3de3804037",
|
||||
"bytes": 41296
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/e5a019491ef8-07-langflow-api-key-generated.png",
|
||||
"sha256": "50c2e5d99ad7c1bbd40e6f467b0bd3edc85454a8867b9c11077f9f5f2ddececa",
|
||||
"bytes": 82662
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/ef2b2f83bd16-04-langbot-pipelines.png",
|
||||
"sha256": "f1edae3941ae6b55085a84e9c290a95744015ef4db1bd9978e97109b8ab320d4",
|
||||
"bytes": 58250
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/ef9bbef78165-09-langbot-runner-dropdown.png",
|
||||
"sha256": "3fc0d26c86715a1e859925727bdd27b84671da158185ff6860a73fd4f5f3466f",
|
||||
"bytes": 94483
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/efa6fcae9c58-15-langbot-runner-dropdown.png",
|
||||
"sha256": "826a60ffe5198b5cef2620f9f4ba553b006c6b3ad2ee2e0b041f044b6536bc56",
|
||||
"bytes": 92060
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/external/ff42857251ab-planner-mode.png",
|
||||
"sha256": "1e2ee21c697ab9c2320094874889e2ac0bd2fc44efb529eafa4d895393ca5691",
|
||||
"bytes": 161907
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/01-init-page.png",
|
||||
"sha256": "b392e42d582c8c1723a2eb63596801b9d459df0465377246d9a4015edcae1ca0",
|
||||
"bytes": 45011
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/05-pipelines.png",
|
||||
"sha256": "66891ef15942b5c11c92a60208ffed491414a488a8352b1f255b65b4d95cd912",
|
||||
"bytes": 54047
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/06-pipeline-ai.png",
|
||||
"sha256": "41b60a85a29e4fa75c8cb59c7f55fe51912eddb961340b04153201fdc5ae9d06",
|
||||
"bytes": 72714
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/07-debug-chat.png",
|
||||
"sha256": "6bb15b2ebb29878b83882eaf17e6f5a39785dd17572b7f864e7e653bdfd59fce",
|
||||
"bytes": 57403
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/08-model-select.png",
|
||||
"sha256": "27100d6a84663da8b227a460841698233ed5f8e4323b23fec2608d0acbec5682",
|
||||
"bytes": 89524
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/10-group-chat.png",
|
||||
"sha256": "b87b7d219527aebedfb48e6269796bc2ecc6eb68ec1f661dd1b1b4903c5f0f66",
|
||||
"bytes": 58454
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/11-private-chat.png",
|
||||
"sha256": "3e79edd893da3cfc983d2d8ff451c828e5bcd704f3f75c36711f01a94058902b",
|
||||
"bytes": 53834
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/13-bot-page.png",
|
||||
"sha256": "1a2dfbf25b164df63cb87d7490474d45b273e5e33c121e75490d923eaa41fb8f",
|
||||
"bytes": 341107
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/article-mcp/14-space-market.png",
|
||||
"sha256": "49ccec9d7f98f74020573a9e1da30c3fc3729ebb32610dad672ccc800c1455ef",
|
||||
"bytes": 110469
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/cloud-multi-user/cloud-invitation.png",
|
||||
"sha256": "8102e7f3d2babb38d5f8f211167c23d119858924913c0e576d3cbe1f9cca02ca",
|
||||
"bytes": 23712
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/cloud-multi-user/cloud-login.png",
|
||||
"sha256": "aa49e8120707c2e4d2fb7fde346afb7aa0eaf46bfa985027c079f3984d76287b",
|
||||
"bytes": 33810
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/cloud-multi-user/oss-dashboard.png",
|
||||
"sha256": "c9f8a0402c2077571826fafe7c3587c695c7aaed12186fa4f86823f6fa99da98",
|
||||
"bytes": 103726
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/deepseek-wechat.png",
|
||||
"sha256": "fd0f772d9cd0bfa4c33e2ea38a6ab09710f2df08c004af91f17e4882c60be146",
|
||||
"bytes": 20896
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/deploy-ai-bot.png",
|
||||
"sha256": "2bc212631b8b072e214bb433e4da241aca0e4c99087bef9ea24f5fd368bf6c7d",
|
||||
"bytes": 20798
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/dify-agent.png",
|
||||
"sha256": "114d06c78593aa66e693768435822cdde7bec356d39e17b74a78d012c123a3a0",
|
||||
"bytes": 11651
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/dify-rag.png",
|
||||
"sha256": "7746be4bb44228165f00a65ca5b147559058a5c79f9a91ac80fb3f1277c34dfe",
|
||||
"bytes": 15799
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/langflow.png",
|
||||
"sha256": "b577d13d95e4b5428a076fb68f17a020f75a5d5f0b581f51e94323f83fd922b1",
|
||||
"bytes": 14706
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/n8n.png",
|
||||
"sha256": "bea4ff8a15517784ab0035483a43ace25b0b2ce5099d7c39f0f817520801750b",
|
||||
"bytes": 14297
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/plugin-system.png",
|
||||
"sha256": "cdf9a0e2249901835f98fe94faa9071ef8ffaa57831a8af30dd69155aeac953c",
|
||||
"bytes": 17004
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/rag-plugin.png",
|
||||
"sha256": "c4e24290b57b30f55d086b2557425f7e790bedd2c8fa17d09c9cbc23686bdab3",
|
||||
"bytes": 14661
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/sandbox-skills.png",
|
||||
"sha256": "2a89789d61ffe229aeb20c069a0e63102374abcdfe0f99e799fc5563cfb1910d",
|
||||
"bytes": 86846
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/covers/welcome.png",
|
||||
"sha256": "5ff0078633e30e8d8d8efa5abf686fbe13993b06cee383d2e7b9afec16639712",
|
||||
"bytes": 11535
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/plugin-system/architecture.png",
|
||||
"sha256": "85028534d0a6246bd25f110189a6734f6176e1765fc64bf5eeb3248137e7292d",
|
||||
"bytes": 30445
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/plugin-system/event-dispatch.png",
|
||||
"sha256": "ba6780bfda4c250da30575e87570e6b3d1a2bd682265340c365b4d3ba575ea13",
|
||||
"bytes": 31013
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/rag-plugin/data-flow.png",
|
||||
"sha256": "9f6f331d4ed508a983e7cc9aeb34914ba47cf0e00a1bd2df87c7424eab93c156",
|
||||
"bytes": 31760
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/rag-plugin/dir-structure.png",
|
||||
"sha256": "f167f813eda287ba11b492adc2626f1340f54b2cb03fe77f496ab5f3bbc65db3",
|
||||
"bytes": 20301
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/sandbox-skills/sandbox-tools.png",
|
||||
"sha256": "3137100959db409a0fa5b70e25bded4bb47df20c90c3a65a578d7862a4e64a3f",
|
||||
"bytes": 169802
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/sandbox-skills/sandbox.png",
|
||||
"sha256": "3a1fd443f4f8008f58144c9e344c2dc50f6faa343c8f65732d9df7d35d93744a",
|
||||
"bytes": 46498
|
||||
},
|
||||
{
|
||||
"path": "/images/articles/source/images/sandbox-skills/skills.png",
|
||||
"sha256": "6ad86d972c7438fbef898d9d339f3f17a30277bf1626e8a9dd11b0b46583c817",
|
||||
"bytes": 362173
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync } from "node:fs";
|
||||
import { cp, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
const ROOT = path.resolve(import.meta.dirname, "..");
|
||||
const ARTICLE_LOCALES = ["en", "zh", "ja"];
|
||||
const MAX_IMAGE_BYTES = 25 * 1024 * 1024;
|
||||
const LANGUAGE_CONFIG = {
|
||||
en: { mintlify: "en", tab: "Articles", introTitle: "LangBot Articles", introDescription: "Product updates, engineering deep dives, tutorials, integrations, and announcements from the LangBot Blog.", sourceLabel: "This article is synchronized from the LangBot Blog.", sourceAction: "Read the canonical version", fallbackLabel: "", overviewGroup: "Browse", groups: { updates: "Product Updates", engineering: "Engineering", tutorials: "Tutorials & Integrations", announcements: "Announcements" } },
|
||||
zh: { mintlify: "cn", tab: "文章", introTitle: "LangBot 文章", introDescription: "同步自 LangBot Blog 的产品动态、技术解析、教程、集成实践与公告。", sourceLabel: "本文同步自 LangBot Blog。", sourceAction: "阅读 Blog 原文", fallbackLabel: "", overviewGroup: "浏览", groups: { updates: "产品动态", engineering: "技术解析", tutorials: "教程与集成", announcements: "公告" } },
|
||||
ja: { mintlify: "jp", tab: "記事", introTitle: "LangBot 記事", introDescription: "LangBot Blog の製品アップデート、技術解説、チュートリアル、連携事例、お知らせをまとめています。", sourceLabel: "この記事は LangBot Blog から同期されています。", sourceAction: "Blog の原文を読む", fallbackLabel: "この記事の日本語版はまだ公開されていないため、英語版を掲載しています。", overviewGroup: "一覧", groups: { updates: "製品アップデート", engineering: "エンジニアリング", tutorials: "チュートリアルと連携", announcements: "お知らせ" } },
|
||||
};
|
||||
const JAPANESE_TITLES = {
|
||||
"connect-deepseek-to-wechat": "DeepSeek R1 を WeChat・Discord・Telegram に5分で接続する方法",
|
||||
"deploy-ai-bot-in-5-minutes": "AI Bot を Discord・Telegram・WeChat に5分でデプロイ",
|
||||
"dify-agent-discord-telegram-slack": "Dify Agent を Discord・Telegram・Slack で動かす",
|
||||
"dify-langbot-rag-knowledge-base": "LangBot 4.6.0 外部ナレッジベース入門:Dify と連携した RAG 会話",
|
||||
"langbot-plugin-system-deep-dive": "LangBot プラグインシステム詳解:プロセス分離・イベント駆動・コンポーネント設計",
|
||||
"langbot-v4100-sandbox-skills": "LangBot v4.10.0:Agent Sandbox と Skills",
|
||||
"langbot-v490-rag-plugin-architecture": "LangBot v4.9.0:完全プラグイン化された RAG アーキテクチャ",
|
||||
"langflow-drag-and-drop-ai-bot": "Langflow のドラッグ&ドロップでマルチプラットフォーム AI Bot を構築",
|
||||
"langtars-remote-pc-control-dify-n8n": "LangTARS:Dify・n8n と連携するオープンソース PC 操作 Agent",
|
||||
"n8n-multi-platform-ai-chatbot": "n8n と LangBot でマルチプラットフォーム AI Chatbot を構築",
|
||||
welcome: "LangBot Blog のご紹介",
|
||||
};
|
||||
|
||||
function parseArgs(argv) {
|
||||
const result = { check: false, source: process.env.LANGBOT_LANDING_REPO || "" };
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
if (argv[i] === "--check") result.check = true;
|
||||
else if (argv[i] === "--source") result.source = argv[++i];
|
||||
else throw new Error(`Unknown argument: ${argv[i]}`);
|
||||
}
|
||||
if (result.source) result.source = path.resolve(result.source);
|
||||
return result;
|
||||
}
|
||||
function parseScalar(value) {
|
||||
const trimmed = value.trim();
|
||||
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) return trimmed.slice(1, -1).replaceAll('\\"', '"');
|
||||
return trimmed;
|
||||
}
|
||||
function parseFrontmatter(markdown, file) {
|
||||
if (!markdown.startsWith("---\n")) throw new Error(`${file}: missing frontmatter`);
|
||||
const end = markdown.indexOf("\n---\n", 4);
|
||||
if (end < 0) throw new Error(`${file}: unterminated frontmatter`);
|
||||
const raw = markdown.slice(4, end);
|
||||
const body = markdown.slice(end + 5).trimStart();
|
||||
const lines = raw.split("\n");
|
||||
const data = {};
|
||||
for (let i = 0; i < lines.length; i += 1) {
|
||||
const match = /^([A-Za-z][A-Za-z0-9_-]*):\s*(.*)$/.exec(lines[i]);
|
||||
if (!match) continue;
|
||||
const [, key, value] = match;
|
||||
if (key === "cover") {
|
||||
const cover = {};
|
||||
while (i + 1 < lines.length && /^\s+/.test(lines[i + 1])) {
|
||||
const nested = /^\s+([A-Za-z][A-Za-z0-9_-]*):\s*(.*)$/.exec(lines[++i]);
|
||||
if (nested) cover[nested[1]] = parseScalar(nested[2]);
|
||||
}
|
||||
data.cover = cover;
|
||||
} else if (value.startsWith("[") && value.endsWith("]")) {
|
||||
data[key] = [...value.matchAll(/"((?:\\.|[^"\\])*)"|'((?:\\.|[^'\\])*)'/g)].map((item) => item[1] ?? item[2]);
|
||||
} else data[key] = parseScalar(value);
|
||||
}
|
||||
for (const required of ["title", "date", "description", "author"]) if (!data[required]) throw new Error(`${file}: missing ${required}`);
|
||||
return { data, body };
|
||||
}
|
||||
function yamlQuote(value) { return JSON.stringify(String(value)); }
|
||||
function classify(post) {
|
||||
const category = String(post.data.categories?.[0] || "").toLowerCase();
|
||||
if (category.includes("product") || category.includes("产品") || category.includes("製品")) return "updates";
|
||||
if (category.includes("engineering") || category.includes("technical") || category.includes("技术") || category.includes("技術")) return "engineering";
|
||||
if (category.includes("announcement") || category.includes("公告") || category.includes("お知らせ")) return "announcements";
|
||||
return "tutorials";
|
||||
}
|
||||
function canonicalUrl(locale, slug) { return `https://langbot.app/${locale}/blog/${slug}`; }
|
||||
function attribution(locale, post) {
|
||||
if (locale === "zh") return `发布于 ${post.data.date} · 作者:${post.data.author}`;
|
||||
if (locale === "ja") return `公開日:${post.data.date} · 著者:${post.data.author}`;
|
||||
return `Published ${post.data.date} · Author: ${post.data.author}`;
|
||||
}
|
||||
function escapeMdxProse(markdown) {
|
||||
const lines = markdown.split("\n");
|
||||
let fenced = false;
|
||||
return lines.map((line) => {
|
||||
if (/^\s*```/.test(line) || /^\s*~~~/.test(line)) { fenced = !fenced; return line; }
|
||||
if (fenced) return line.trimEnd();
|
||||
return line.split(/(`[^`]*`)/g).map((piece, index) => index % 2 === 1 ? piece : piece.replaceAll("{", "{").replaceAll("}", "}")).join("").trimEnd();
|
||||
}).join("\n");
|
||||
}
|
||||
function imageReferences(markdown) { return [...markdown.matchAll(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g)].map((match) => ({ alt: match[1], url: match[2], full: match[0] })); }
|
||||
function contentTypeExtension(contentType) {
|
||||
if (contentType.includes("image/png")) return ".png";
|
||||
if (contentType.includes("image/jpeg")) return ".jpg";
|
||||
if (contentType.includes("image/gif")) return ".gif";
|
||||
if (contentType.includes("image/svg")) return ".svg";
|
||||
if (contentType.includes("image/webp")) return ".webp";
|
||||
return "";
|
||||
}
|
||||
function localImageTarget(sourceUrl) {
|
||||
if (sourceUrl.startsWith("/")) return `/images/articles/source${sourceUrl}`;
|
||||
const parsed = new URL(sourceUrl);
|
||||
let base = path.posix.basename(parsed.pathname) || "image";
|
||||
const digest = createHash("sha256").update(sourceUrl).digest("hex").slice(0, 12);
|
||||
base = base.replace(/[^A-Za-z0-9._-]/g, "-");
|
||||
return `/images/articles/external/${digest}-${base}`;
|
||||
}
|
||||
function assertWithin(base, candidate, label) {
|
||||
const normalizedBase = path.resolve(base);
|
||||
const normalizedCandidate = path.resolve(candidate);
|
||||
if (!normalizedCandidate.startsWith(`${normalizedBase}${path.sep}`)) throw new Error(`${label} escapes ${normalizedBase}: ${normalizedCandidate}`);
|
||||
}
|
||||
async function assertDirectoryWithoutSymlinks(directory, label) {
|
||||
if (!existsSync(directory)) throw new Error(`${label} does not exist: ${directory}`);
|
||||
const info = await lstat(directory);
|
||||
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`${label} must be a real non-symlink directory: ${directory}`);
|
||||
const resolved = await realpath(directory);
|
||||
if (resolved !== path.resolve(directory)) throw new Error(`${label} path must not traverse symlinks: ${directory}`);
|
||||
return resolved;
|
||||
}
|
||||
async function readRegularFileWithin(base, file, label, encoding = null) {
|
||||
const baseReal = await assertDirectoryWithoutSymlinks(base, `${label} directory`);
|
||||
if (!existsSync(file)) throw new Error(`${label} does not exist: ${file}`);
|
||||
const info = await lstat(file);
|
||||
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file: ${file}`);
|
||||
const fileReal = await realpath(file);
|
||||
assertWithin(baseReal, fileReal, label);
|
||||
return encoding ? readFile(fileReal, encoding) : readFile(fileReal);
|
||||
}
|
||||
function validateImageBytes(bytes, label) {
|
||||
const ascii = bytes.subarray(0, 16).toString("ascii");
|
||||
const trimmed = bytes.toString("utf8", 0, Math.min(bytes.length, 1024)).trimStart();
|
||||
const valid =
|
||||
bytes.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) ||
|
||||
(bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) ||
|
||||
ascii.startsWith("GIF87a") || ascii.startsWith("GIF89a") ||
|
||||
(ascii.startsWith("RIFF") && ascii.slice(8, 12) === "WEBP") ||
|
||||
((trimmed.startsWith("<svg") || trimmed.startsWith("<?xml")) && trimmed.includes("<svg"));
|
||||
if (!valid) throw new Error(`Invalid image signature: ${label}`);
|
||||
}
|
||||
async function prepareImageTarget(assetRoot, target) {
|
||||
const imageRoot = path.join(assetRoot, "images/articles");
|
||||
assertWithin(imageRoot, target, "Article image target");
|
||||
await mkdir(path.dirname(target), { recursive: true });
|
||||
const imageRootReal = await realpath(imageRoot);
|
||||
const targetParentReal = await realpath(path.dirname(target));
|
||||
if (targetParentReal !== path.resolve(path.dirname(target))) throw new Error(`Article image target directory must not be a symlink: ${path.dirname(target)}`);
|
||||
assertWithin(imageRootReal, path.join(targetParentReal, path.basename(target)), "Resolved article image target");
|
||||
if (existsSync(target) && (await lstat(target)).isSymbolicLink()) throw new Error(`Article image target must not be a symlink: ${target}`);
|
||||
}
|
||||
async function materializeImage(sourceRoot, sourceUrl, assetRoot) {
|
||||
let targetUrl = localImageTarget(sourceUrl);
|
||||
let target = path.join(assetRoot, targetUrl.slice(1));
|
||||
await prepareImageTarget(assetRoot, target);
|
||||
if (sourceUrl.startsWith("/")) {
|
||||
const publicRoot = path.join(sourceRoot, "public");
|
||||
const source = path.join(publicRoot, sourceUrl.slice(1));
|
||||
assertWithin(publicRoot, source, "Blog image source");
|
||||
if (!existsSync(source)) throw new Error(`Missing source image: ${sourceUrl} (${source})`);
|
||||
const sourceStat = await lstat(source);
|
||||
if (!sourceStat.isFile() || sourceStat.isSymbolicLink()) throw new Error(`Blog image source must be a regular non-symlink file: ${source}`);
|
||||
const publicRootReal = await realpath(publicRoot);
|
||||
const sourceReal = await realpath(source);
|
||||
assertWithin(publicRootReal, sourceReal, "Resolved Blog image source");
|
||||
const bytes = await readFile(sourceReal);
|
||||
validateImageBytes(bytes, sourceUrl);
|
||||
await writeFile(target, bytes);
|
||||
return targetUrl;
|
||||
}
|
||||
const parsedSource = new URL(sourceUrl);
|
||||
if (parsedSource.protocol !== "https:") throw new Error(`External images must use HTTPS: ${sourceUrl}`);
|
||||
const response = await fetch(sourceUrl, { headers: { "User-Agent": "LangBot-Wiki-Article-Sync/1.0" }, redirect: "follow" });
|
||||
if (!response.ok) throw new Error(`Failed to fetch image ${sourceUrl}: HTTP ${response.status}`);
|
||||
const declaredSize = Number(response.headers.get("content-length") || 0);
|
||||
if (declaredSize > MAX_IMAGE_BYTES) throw new Error(`Image exceeds ${MAX_IMAGE_BYTES} bytes: ${sourceUrl}`);
|
||||
const bytes = Buffer.from(await response.arrayBuffer());
|
||||
const contentType = response.headers.get("content-type") || "";
|
||||
if (!bytes.length || bytes.length > MAX_IMAGE_BYTES || !contentType.toLowerCase().startsWith("image/")) throw new Error(`Invalid image ${sourceUrl}: ${contentType}, ${bytes.length} bytes`);
|
||||
validateImageBytes(bytes, sourceUrl);
|
||||
if (!path.extname(target)) {
|
||||
const extension = contentTypeExtension(contentType);
|
||||
if (!extension) throw new Error(`Unsupported image content type for extensionless URL: ${contentType}`);
|
||||
target += extension;
|
||||
targetUrl += extension;
|
||||
await prepareImageTarget(assetRoot, target);
|
||||
}
|
||||
if (contentType.toLowerCase().includes("image/svg")) {
|
||||
await writeFile(target, bytes.toString("utf8").replace(/[ \t]+$/gm, ""));
|
||||
} else {
|
||||
await writeFile(target, bytes);
|
||||
}
|
||||
return targetUrl;
|
||||
}
|
||||
async function loadPosts(sourceRoot) {
|
||||
const byLocale = new Map();
|
||||
const blogRoot = path.join(sourceRoot, "src/content/blog");
|
||||
const blogRootReal = await assertDirectoryWithoutSymlinks(blogRoot, "Blog content root");
|
||||
for (const locale of ARTICLE_LOCALES) {
|
||||
const dir = path.join(blogRoot, locale);
|
||||
const files = existsSync(dir) ? await readdir(dir) : [];
|
||||
if (existsSync(dir)) {
|
||||
const localeRootReal = await assertDirectoryWithoutSymlinks(dir, `Blog ${locale} content root`);
|
||||
assertWithin(blogRootReal, localeRootReal, `Blog ${locale} content root`);
|
||||
}
|
||||
const posts = new Map();
|
||||
for (const name of files.filter((item) => item.endsWith(".md")).sort()) {
|
||||
const file = path.join(dir, name);
|
||||
const parsed = parseFrontmatter(await readRegularFileWithin(dir, file, `Blog ${locale} article`, "utf8"), file);
|
||||
const slug = name.slice(0, -3);
|
||||
posts.set(slug, { ...parsed, slug, locale, file });
|
||||
}
|
||||
byLocale.set(locale, posts);
|
||||
}
|
||||
const enSlugs = [...byLocale.get("en").keys()].sort();
|
||||
const zhSlugs = [...byLocale.get("zh").keys()].sort();
|
||||
if (JSON.stringify(enSlugs) !== JSON.stringify(zhSlugs)) throw new Error("English and Chinese blog slug sets differ");
|
||||
return { byLocale, slugs: enSlugs };
|
||||
}
|
||||
async function renderArticle(sourceRoot, post, locale, fallback, assetRoot, imageCache) {
|
||||
const config = LANGUAGE_CONFIG[locale];
|
||||
const title = locale === "ja" && fallback ? (JAPANESE_TITLES[post.slug] || post.data.title) : post.data.title;
|
||||
let body = post.body;
|
||||
const refs = imageReferences(body);
|
||||
for (const ref of refs) if (!imageCache.has(ref.url)) imageCache.set(ref.url, await materializeImage(sourceRoot, ref.url, assetRoot));
|
||||
if (post.data.cover?.image && !imageCache.has(post.data.cover.image)) imageCache.set(post.data.cover.image, await materializeImage(sourceRoot, post.data.cover.image, assetRoot));
|
||||
for (const ref of refs) body = body.replaceAll(ref.full, `})`);
|
||||
body = escapeMdxProse(body);
|
||||
const cover = post.data.cover?.image ? imageCache.get(post.data.cover.image) : "";
|
||||
const bodyHasCover = cover && body.includes(`](${cover})`);
|
||||
const notice = fallback ? `\n> ${config.fallbackLabel}\n` : "";
|
||||
const coverBlock = cover && !bodyHasCover ? `\n\n` : "";
|
||||
return `---\ntitle: ${yamlQuote(title)}\ndescription: ${yamlQuote(post.data.description)}\n---\n\n${config.sourceLabel} [${config.sourceAction}](${canonicalUrl(post.locale, post.slug)}).\n\n${attribution(locale, post)}\n${notice}${coverBlock}\n${body.trim()}\n`;
|
||||
}
|
||||
function articlePage(locale, slug) { return `${locale}/articles/${slug}`; }
|
||||
function renderIndex(locale, posts) {
|
||||
const config = LANGUAGE_CONFIG[locale];
|
||||
const lines = ["---", `title: ${yamlQuote(config.introTitle)}`, `description: ${yamlQuote(config.introDescription)}`, "---", "", config.introDescription, ""];
|
||||
for (const key of ["updates", "engineering", "tutorials", "announcements"]) {
|
||||
const inGroup = posts.filter((post) => post.group === key);
|
||||
if (!inGroup.length) continue;
|
||||
lines.push(`## ${config.groups[key]}`, "");
|
||||
for (const post of inGroup) lines.push(`- [${post.title}](/${articlePage(locale, post.slug)}) — ${post.date}`);
|
||||
lines.push("");
|
||||
}
|
||||
return `${lines.join("\n").trim()}\n`;
|
||||
}
|
||||
function navigationTab(locale, posts) {
|
||||
const config = LANGUAGE_CONFIG[locale];
|
||||
const groups = [{ group: config.overviewGroup, pages: [`${locale}/articles/index`] }];
|
||||
for (const key of ["updates", "engineering", "tutorials", "announcements"]) {
|
||||
const pages = posts.filter((post) => post.group === key).map((post) => articlePage(locale, post.slug));
|
||||
if (pages.length) groups.push({ group: config.groups[key], pages });
|
||||
}
|
||||
return { tab: config.tab, groups };
|
||||
}
|
||||
async function validateGenerated(manifest) {
|
||||
const docs = JSON.parse(await readFile(path.join(ROOT, "docs.json"), "utf8"));
|
||||
const failures = [];
|
||||
const referencedImages = new Set();
|
||||
for (const locale of ARTICLE_LOCALES) {
|
||||
const config = LANGUAGE_CONFIG[locale];
|
||||
const articleRoot = path.join(ROOT, locale, "articles");
|
||||
await assertDirectoryWithoutSymlinks(articleRoot, `${locale} generated article root`);
|
||||
const language = docs.navigation.languages.find((item) => item.language === config.mintlify);
|
||||
const tabs = language?.tabs.filter((item) => item.tab === config.tab) || [];
|
||||
if (tabs.length !== 1) failures.push(`${locale}: expected exactly one ${config.tab} navigation tab, found ${tabs.length}`);
|
||||
for (const article of manifest.articles.filter((item) => item.locale === locale)) {
|
||||
const file = path.join(ROOT, `${article.page}.mdx`);
|
||||
if (!existsSync(file)) failures.push(`missing article: ${article.page}.mdx`);
|
||||
else {
|
||||
const content = await readRegularFileWithin(articleRoot, file, `${locale} generated article`, "utf8");
|
||||
for (const match of content.matchAll(/!\[[^\]]*\]\((\/images\/[^)]+)\)/g)) {
|
||||
referencedImages.add(match[1]);
|
||||
const image = path.join(ROOT, match[1].slice(1));
|
||||
if (!existsSync(image) || (await stat(image)).size === 0) failures.push(`${article.page}: missing image ${match[1]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const mirroredMetadata = await imageMetadata(ROOT);
|
||||
const mirroredImages = new Set(mirroredMetadata.map((image) => image.path));
|
||||
for (const image of mirroredImages) if (!referencedImages.has(image)) failures.push(`unreferenced mirrored image: ${image}`);
|
||||
for (const image of referencedImages) if (!mirroredImages.has(image)) failures.push(`referenced image is not mirrored: ${image}`);
|
||||
const expectedImages = new Map((manifest.images || []).map((image) => [image.path, image]));
|
||||
for (const image of mirroredMetadata) {
|
||||
const expected = expectedImages.get(image.path);
|
||||
if (!expected) failures.push(`mirrored image missing from manifest: ${image.path}`);
|
||||
else if (expected.sha256 !== image.sha256 || expected.bytes !== image.bytes) failures.push(`mirrored image digest mismatch: ${image.path}`);
|
||||
}
|
||||
for (const image of expectedImages.keys()) if (!mirroredImages.has(image)) failures.push(`manifest image is missing: ${image}`);
|
||||
if (failures.length) throw new Error(`Generated article validation failed:\n${failures.join("\n")}`);
|
||||
}
|
||||
async function listFiles(directory) {
|
||||
if (!existsSync(directory)) return [];
|
||||
const files = [];
|
||||
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
||||
const full = path.join(directory, entry.name);
|
||||
if (entry.isSymbolicLink()) throw new Error(`Generated content tree must not contain symlinks: ${full}`);
|
||||
if (entry.isDirectory()) files.push(...await listFiles(full));
|
||||
else if (entry.isFile()) files.push(full);
|
||||
else throw new Error(`Generated content tree must contain only directories and regular files: ${full}`);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
async function imageMetadata(assetRoot) {
|
||||
const metadata = [];
|
||||
for (const file of await listFiles(path.join(assetRoot, "images/articles"))) {
|
||||
const bytes = await readFile(file);
|
||||
validateImageBytes(bytes, file);
|
||||
metadata.push({
|
||||
path: `/${path.relative(assetRoot, file).split(path.sep).join("/")}`,
|
||||
sha256: createHash("sha256").update(bytes).digest("hex"),
|
||||
bytes: bytes.length,
|
||||
});
|
||||
}
|
||||
return metadata.sort((a, b) => a.path.localeCompare(b.path));
|
||||
}
|
||||
async function referencedImagesIn(directory) {
|
||||
const images = new Set();
|
||||
for (const file of await listFiles(directory)) {
|
||||
if (!file.endsWith(".mdx")) continue;
|
||||
const content = await readFile(file, "utf8");
|
||||
for (const match of content.matchAll(/!\[[^\]]*\]\((\/images\/articles\/[^)]+)\)/g)) images.add(match[1]);
|
||||
}
|
||||
return images;
|
||||
}
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
const manifestPath = path.join(ROOT, "scripts/blog-articles-manifest.json");
|
||||
if (args.check && !args.source) {
|
||||
await validateGenerated(JSON.parse(await readFile(manifestPath, "utf8")));
|
||||
console.log("Generated article pages, navigation, and local images are valid.");
|
||||
return;
|
||||
}
|
||||
if (!args.source) throw new Error("Pass --source /path/to/langbot-landing-page or set LANGBOT_LANDING_REPO");
|
||||
if (!existsSync(path.join(args.source, "src/content/blog"))) throw new Error(`Not a LangBot landing repository: ${args.source}`);
|
||||
const { byLocale, slugs } = await loadPosts(args.source);
|
||||
const outputRoot = await mkdtemp(path.join(tmpdir(), "langbot-wiki-article-sync-"));
|
||||
const assetRoot = args.check ? outputRoot : ROOT;
|
||||
const imageCache = new Map();
|
||||
const localePosts = new Map();
|
||||
const manifest = { source: "https://github.com/langbot-app/langbot-landing-page/tree/main/src/content/blog", articles: [] };
|
||||
for (const locale of ARTICLE_LOCALES) {
|
||||
const renderedPosts = [];
|
||||
for (const slug of slugs) {
|
||||
const localized = byLocale.get(locale).get(slug);
|
||||
const post = localized || byLocale.get("en").get(slug);
|
||||
const fallback = !localized;
|
||||
const title = locale === "ja" && fallback ? (JAPANESE_TITLES[slug] || post.data.title) : post.data.title;
|
||||
renderedPosts.push({ slug, title, date: post.data.date, group: classify(byLocale.get("en").get(slug)), fallback });
|
||||
const target = path.join(outputRoot, locale, "articles", `${slug}.mdx`);
|
||||
await mkdir(path.dirname(target), { recursive: true });
|
||||
await writeFile(target, await renderArticle(args.source, post, locale, fallback, assetRoot, imageCache));
|
||||
manifest.articles.push({ locale, slug, page: articlePage(locale, slug), sourceLocale: post.locale, fallback });
|
||||
}
|
||||
renderedPosts.sort((a, b) => b.date.localeCompare(a.date) || a.slug.localeCompare(b.slug));
|
||||
localePosts.set(locale, renderedPosts);
|
||||
await writeFile(path.join(outputRoot, locale, "articles", "index.mdx"), renderIndex(locale, renderedPosts));
|
||||
}
|
||||
if (!args.check) {
|
||||
const referencedImages = await referencedImagesIn(outputRoot);
|
||||
for (const file of await listFiles(path.join(ROOT, "images/articles"))) {
|
||||
const publicPath = `/${path.relative(ROOT, file).split(path.sep).join("/")}`;
|
||||
if (!referencedImages.has(publicPath)) await rm(file, { force: true });
|
||||
}
|
||||
}
|
||||
manifest.images = await imageMetadata(assetRoot);
|
||||
if (args.check) {
|
||||
const mismatches = [];
|
||||
for (const locale of ARTICLE_LOCALES) {
|
||||
const generatedDir = path.join(outputRoot, locale, "articles");
|
||||
const currentDir = path.join(ROOT, locale, "articles");
|
||||
const generatedNames = (await readdir(generatedDir)).sort();
|
||||
const currentEntries = existsSync(currentDir) ? await readdir(currentDir, { withFileTypes: true }) : [];
|
||||
if (currentEntries.some((entry) => !entry.isFile() || entry.isSymbolicLink())) mismatches.push(`${locale}/articles contains non-regular files`);
|
||||
const currentNames = currentEntries.map((entry) => entry.name).sort();
|
||||
if (JSON.stringify(generatedNames) !== JSON.stringify(currentNames)) mismatches.push(`${locale}/articles file set`);
|
||||
for (const name of generatedNames) {
|
||||
const generated = await readFile(path.join(generatedDir, name), "utf8");
|
||||
const currentFile = path.join(currentDir, name);
|
||||
const current = existsSync(currentFile) ? await readRegularFileWithin(currentDir, currentFile, `${locale} generated article`, "utf8") : "";
|
||||
if (generated !== current) mismatches.push(`${locale}/articles/${name}`);
|
||||
}
|
||||
}
|
||||
const currentManifest = existsSync(manifestPath) ? JSON.parse(await readFile(manifestPath, "utf8")) : null;
|
||||
if (JSON.stringify(currentManifest) !== JSON.stringify(manifest)) mismatches.push("scripts/blog-articles-manifest.json");
|
||||
const currentDocs = JSON.parse(await readFile(path.join(ROOT, "docs.json"), "utf8"));
|
||||
for (const locale of ARTICLE_LOCALES) {
|
||||
const config = LANGUAGE_CONFIG[locale];
|
||||
const language = currentDocs.navigation.languages.find((item) => item.language === config.mintlify);
|
||||
const matchingTabs = language?.tabs.filter((item) => item.tab === config.tab) || [];
|
||||
const currentTab = matchingTabs.length === 1 ? matchingTabs[0] : null;
|
||||
const expectedTab = navigationTab(locale, localePosts.get(locale));
|
||||
if (matchingTabs.length !== 1 || JSON.stringify(currentTab) !== JSON.stringify(expectedTab)) mismatches.push(`docs.json ${config.tab} tab`);
|
||||
}
|
||||
await rm(outputRoot, { recursive: true, force: true });
|
||||
if (mismatches.length) throw new Error(`Blog articles are out of sync:\n${mismatches.join("\n")}`);
|
||||
await validateGenerated(JSON.parse(await readFile(manifestPath, "utf8")));
|
||||
console.log(`All ${manifest.articles.length} localized article pages are synchronized.`);
|
||||
return;
|
||||
}
|
||||
for (const locale of ARTICLE_LOCALES) {
|
||||
const targetDir = path.join(ROOT, locale, "articles");
|
||||
await rm(targetDir, { recursive: true, force: true });
|
||||
await mkdir(path.dirname(targetDir), { recursive: true });
|
||||
await cp(path.join(outputRoot, locale, "articles"), targetDir, { recursive: true });
|
||||
}
|
||||
await rm(outputRoot, { recursive: true, force: true });
|
||||
const docsPath = path.join(ROOT, "docs.json");
|
||||
const docs = JSON.parse(await readFile(docsPath, "utf8"));
|
||||
for (const locale of ARTICLE_LOCALES) {
|
||||
const config = LANGUAGE_CONFIG[locale];
|
||||
const language = docs.navigation.languages.find((item) => item.language === config.mintlify);
|
||||
if (!language) throw new Error(`docs.json missing language ${config.mintlify}`);
|
||||
language.tabs = language.tabs.filter((item) => item.tab !== config.tab);
|
||||
const apiIndex = language.tabs.findIndex((item) => /API/.test(item.tab));
|
||||
language.tabs.splice(apiIndex >= 0 ? apiIndex : language.tabs.length, 0, navigationTab(locale, localePosts.get(locale)));
|
||||
}
|
||||
await writeFile(docsPath, `${JSON.stringify(docs, null, 2)}\n`);
|
||||
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
||||
await validateGenerated(manifest);
|
||||
console.log(`Synchronized ${slugs.length} posts into ${manifest.articles.length} localized Wiki pages.`);
|
||||
}
|
||||
main().catch((error) => { console.error(error.stack || error.message); process.exitCode = 1; });
|
||||
@@ -0,0 +1,59 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, readFileSync, statSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
const root = path.resolve(import.meta.dirname, "..");
|
||||
const docs = JSON.parse(readFileSync(path.join(root, "docs.json"), "utf8"));
|
||||
const manifest = JSON.parse(readFileSync(path.join(root, "scripts/blog-articles-manifest.json"), "utf8"));
|
||||
const localeConfig = {
|
||||
en: { language: "en", tab: "Articles" },
|
||||
zh: { language: "cn", tab: "文章" },
|
||||
ja: { language: "jp", tab: "記事" },
|
||||
};
|
||||
|
||||
test("all canonical Blog slugs are published in every Wiki locale", () => {
|
||||
const slugsByLocale = new Map();
|
||||
for (const locale of Object.keys(localeConfig)) {
|
||||
const items = manifest.articles.filter((article) => article.locale === locale);
|
||||
assert.ok(items.length > 0, `${locale} has no articles`);
|
||||
slugsByLocale.set(locale, items.map((article) => article.slug).sort());
|
||||
}
|
||||
assert.deepEqual(slugsByLocale.get("zh"), slugsByLocale.get("en"));
|
||||
assert.deepEqual(slugsByLocale.get("ja"), slugsByLocale.get("en"));
|
||||
});
|
||||
|
||||
test("article tabs group every generated page exactly once", () => {
|
||||
for (const [locale, config] of Object.entries(localeConfig)) {
|
||||
const language = docs.navigation.languages.find((item) => item.language === config.language);
|
||||
const matchingTabs = language.tabs.filter((item) => item.tab === config.tab);
|
||||
assert.equal(matchingTabs.length, 1, `${locale} must have exactly one ${config.tab} tab`);
|
||||
const tab = matchingTabs[0];
|
||||
const pages = tab.groups.flatMap((group) => group.pages).filter((page) => page !== `${locale}/articles/index`);
|
||||
const expected = manifest.articles.filter((article) => article.locale === locale).map((article) => article.page);
|
||||
assert.deepEqual([...pages].sort(), [...expected].sort());
|
||||
assert.equal(new Set(pages).size, pages.length, `${locale} has duplicate article navigation entries`);
|
||||
}
|
||||
});
|
||||
|
||||
test("generated articles use only non-empty Wiki-local images", () => {
|
||||
const expectedImages = new Map(manifest.images.map((image) => [image.path, image]));
|
||||
for (const article of manifest.articles) {
|
||||
const file = path.join(root, `${article.page}.mdx`);
|
||||
assert.ok(existsSync(file), `missing ${article.page}.mdx`);
|
||||
const content = readFileSync(file, "utf8");
|
||||
const imageUrls = [...content.matchAll(/!\[[^\]]*\]\(([^)]+)\)/g)].map((match) => match[1]);
|
||||
for (const imageUrl of imageUrls) {
|
||||
assert.match(imageUrl, /^\/images\/articles\//, `${article.page} hotlinks ${imageUrl}`);
|
||||
const image = path.join(root, imageUrl.slice(1));
|
||||
assert.ok(existsSync(image), `${article.page} references missing ${imageUrl}`);
|
||||
assert.ok(statSync(image).size > 0, `${imageUrl} is empty`);
|
||||
const bytes = readFileSync(image);
|
||||
const expected = expectedImages.get(imageUrl);
|
||||
assert.ok(expected, `${imageUrl} is missing from the synchronization manifest`);
|
||||
assert.equal(bytes.length, expected.bytes, `${imageUrl} size differs from the manifest`);
|
||||
assert.equal(createHash("sha256").update(bytes).digest("hex"), expected.sha256, `${imageUrl} digest differs from the manifest`);
|
||||
}
|
||||
}
|
||||
});
|
||||