LangBot

Activity, over time

Daily observations from the public event archive.

Attention

Daily stars & forks

Loading attention chart

Star accumulation

Cumulative stars

Loading star accumulation chart

Pull requests

Daily opens & merges

Loading pull requests chart

Conversations

Daily issues & comments

Loading conversations chart

Community

Daily accounts & reviews

Loading community chart

Releases

Daily releases

Loading releases chart

Mentions

15posts · 30d
Nov 40 posts
  1. 这个评论让我第一次感觉看 AI 提的 issue 很舒服,大家说说这个是啥模型?LangBot#2817 t.co

    1 like · 27 views
  2. Zenn · · @rockchinq

    Three Telegram Bot Routes: Welcome, Q&A and Moderation Agents Scenario A Telegram community group has three chores — greet joiners with the two rules, answer "how do I configure this", deal with ad spam on the spot — and today all three ride one bot: one pipeline, one prompt. The welcome matters only at the instant someone joins; rules Q&A faces every ordinary message; kicking and banning never belonged to a Q&A assistant. Merged into one prompt, the model has to guess which situation it is in, and a wrong guess punishes the wrong person. How it works in 4.11 The processor layer gained an Agent option. On 4.10 a bot carried one pipeline, so "speak when someone joins" had to be decided inside it. 4.11 replaces that with event routing: rows of event pattern → processor, where a processor is a pipeline or an Agent. An Agent brings a runner (supplied by a plugin; here langbot-team/LocalAgent) plus a system prompt. Adapters declare which events they emit. Support depends on the adapter and platform — check the support matrix first. One event hits exactly one route. Rows are tried in order: enabled → pattern matches → filters pass → does the target declare support. The first row clearing all four wins and the rest are skipped (official docs docs/event-based-agents/04-event-routing.md §3). Measured 2026-10-16, check-route returned 目标处理器 | Agent · tg-welcome-agent | 命中规则 | 第 2 条路由 | 诊断步骤 | 第 1 条路由已跳过。事件与此路由不匹配。已选择第 2 条路由。第 3 条路由已跳过。事件与此路由不匹配。 — route 2 matched. The event scope decides three things: which events reach the Agent, what the debug dropdown offers, and whether the Agent can be picked as a route processor. Measured 2026-10-16: creating the group.member_joined route offered only tg-welcome-agent and dify-wecomcs-agent; tg-rules-agent and tg-moderation-agent were absent. What the adapter declares is what the menu holds. telegram-omni declares 11 event types, word for word with source LangBot/src/langbot/pkg/platform/adapters/telegram/adapter.py:168-180: message.received, message.edited, message.reaction, group.member_joined, group.member_left, group.member_banned, bot.invited_to_group, bot.removed_from_group, bot.muted, bot.unmuted, platform.specific. So the "add behavior" menu offers four presets (reply to messages, welcome new members, handle departures, handle group moderation) plus "configure another event"; the fifth preset, review friend requests (friend.request_received), is absent because this adapter declares no such event (source LangBot/web/src/app/home/bots/components/bot-form/EventBindingsEditor.tsx:1554-1559, presets at :194-230). The conclusion stops there: the menu follows the declared events, not our judgment about a platform. Hands-on steps Start a local demo instance: docker network create a4 docker run -d --name a4-core --network a4 --restart on-failure -e TZ=Asia/Shanghai \ -e BOXENABLED=false -e SPACEDISABLE_TELEMETRY=true -p 15800:5300 \ -v "$PWD/deploy/a4/data:/app/data" rockchin/langbot:v4.11.0 docker run -d --name a4-runtime --network a4 --network-alias langbot_plugin_runtime --restart on-failure \ -e TZ=Asia/Shanghai -e SPACEDISABLE_TELEMETRY=true -v "$PWD/deploy/a4/data/plugins:/app/data/plugins" \ rockchin/langbot:v4.11.0 uv run --no-sync -m langbot_plugin.cli.init__ rt Admin demo@langbot.local / LangBot411!demo (local demo value — replace with your own). Create the Telegram bot. Bots → New → platform Telegram, paste the BotFather token. This demo uses a placeholder (local demo value — replace with your own), so Telegram never connects; what is verified is where an event is delivered. Detail page /home/bots?id=<uuid> (source LangBot/web/src/app/home/bots/page.tsx:7-8). Read the adapter's event catalog. The panel reports 11 event types; those eleven names bound every menu below. Create three Agents, one event each. Runner LocalAgent (plugin:langbot-team/LocalAgent/default), model deepseek-chat, event scope a single event: tg-welcome-agent → group.member_joined, tg-rules-agent → message.received, tg-moderation-agent → group.member_banned. Prompt and tools for the welcome Agent. A greeting under 60 characters naming the two rules, with "reply to the current conversation" (event_reply) allowed. Run one debug round trip first. On tg-welcome-agent's debug page — its dropdown offers only the event it declares — run group.member_joined with {"member_name": "小林", "member_id": "10086", "group_id": "-1001234567890"}. Measured 2026-10-16: one tool call recorded, tool "reply to the current conversation event_reply", text verbatim: 欢迎语已通过「发送消息」工具发出(模拟投递,未真实调用平台 API): > 欢迎小林加入 LangBot 交流群!🎉 提问请带上 LangBot 版本号,禁止发广告。配置说明可在 GitHub 仓库 docs 目录查看。 (53 字,符合 60 字以内要求:包含欢迎、两条群规与 docs 目录提示。) Only the tools moderation needs. The events-and-tools page reports one event (member banned group.member_banned) and four tools enabled; of 14 platform tools only "kick group member" is checked — event_kick_member → kick_member, risk dangerous (source LangBot/src/langbot/pkg/agent/runner/platform_tools.py:99-203), labelled sensitive in the UI (read/write/dangerous = read-only/action/sensitive; source LangBot/web/src/i18n/locales/zh-Hans.ts:1368-1370). Create the three routes: message.received → tg-rules-agent, group.member_joined → tg-welcome-agent, group.member_banned → tg-moderation-agent. Verify The routes persist. Read back from bots.event_bindings (updated_at 2026-10-16 17:23:44); raw JSON in series/langbot-4.11-release/evidence/demo-test-records.md §8.3: # Event pattern Processor priority enabled route 1 message.received Agent · tg-rules-agent 0 true route 2 group.member_joined Agent · tg-welcome-agent 0 true route 3 group.member_banned Agent · tg-moderation-agent 0 true Dry-run one event. Check-route with "member joined group": target processor | Agent · tg-welcome-agent, matched rule | route 2, diagnostics route 1 skipped, route 2 selected, route 3 skipped. Each route accepts only its own event. Limits and caveats No real Telegram traffic. A local demo token (local demo value — replace with your own) means the verified result is "the configuration took effect and the event routed to the right Agent, which completed one model call", not delivery into a group. Platform tools run under Mock in the debug page, verbatim: "平台工具使用 Mock:Agent 真实调用工具,回复、发送等平台动作模拟执行,不发送真实消息。其他工具仍按实际配置执行。" and "未记录到工具调用;生成文本不代表消息已发送。" Confine dangerous tools. event_kick_member is enabled only for the moderation Agent; on a real platform a kick is immediate. Processor choice depends on the event. A pipeline target binds only message.received (official docs docs/event-based-agents/04-event-routing.md §3); join and ban events need an Agent or a plugin processor. Support depends on the adapter and platform — check the support matrix first. To confirm: one debug round trip was observed, not consecutive turns inside a group; tg-rules-agent's answer quality was not evaluated. [Inference] "two routes may both deliver one event" is outside the measurement — arbitration selects one row (route 2 matched; 1 and 3 skipped). For one event to reach two targets, use the separate plugin-processor path. Summary and next steps Three chores, three Agents, and the glue is rows of event pattern → processor: group.member_joined wakes the welcome Agent, message.received the rules Agent, group.member_banned the moderation Agent. Each scope is one event wide, so nothing else can wake it. Coming from 4.10, start at the v4.11.0 release page; matching details are in docs/event-based-agents/04-event-routing.md; runner plugins live in the langbot-plugins repository. Managed hosting at space.langbot.app is one other option, subject to your deployment constraints.

  3. Zenn · · @rockchinq

    Dify in WeCom Customer Service: A Runner Plugin and Event Routing Scenario The knowledge base, prompts and tools live in a Dify application; the WeCom customer-service side only answers customers. Today every question is retyped into Dify's chat box and the answer copied back — the two are not connected. On 4.10 the only route was a pipeline: Dify as a provider (Dify service API), wired into one pipeline. That duplicated the orchestration — Dify canvas plus LangBot's own model, prompt and conversation settings — and one pipeline served one bot. 4.11 makes a processor an Agent, whose Runner comes from a plugin. Connecting Dify to WeCom customer service becomes three configuration actions, walked below on a local instance. How it works in 4.11 Runners are marketplace plugins. Dify is the official langbot-team/DifyAgent plugin, runner id plugin:langbot-team/DifyAgent/default (official docs docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md:29 maps the old dify-service-api runner to it). It sits in Batch 1, "traditional service API runner" (:45), and the old built-in runner code provides no runtime fallback (:22). Connection details live in the processor's runner config. Choose Agent mode, pick Dify Agent, then fill Dify-side values; DOM readings here (measured 2026-10-16): 基础 URL (dify), API 密钥, 超时 (30), 过期 (3600), 身份 (发送者). Event routing maps events to processors; the first match wins. Matching picks by priority from high to low, then list order, and runs exactly one response target (official docs docs/event-based-agents/04-event-routing.md:118-119) — so a second route on message.received would not make Dify run twice. Adapters declare a platform's events. This one declares message.received, message.deleted, wecomcs.enter_session and more, plus send_message, reply_message, get_message, get_user_info (source LangBot/src/langbot/pkg/platform/adapters/wecomcs/adapter.py:79-99). Route-editor presets follow those declarations (source LangBot/web/src/app/home/bots/components/bot-form/EventBindingsEditor.tsx:1554-1559). Support depends on the adapter and platform — check the support matrix first. Hands-on steps Fire up a local instance to try this on (the exact commands used in the recorded run): docker network create a4 docker run -d --name a4-core --network a4 --restart on-failure -e TZ=Asia/Shanghai \ -e BOXENABLED=false -e SPACEDISABLE_TELEMETRY=true -p 15800:5300 \ -v "$PWD/deploy/a4/data:/app/data" rockchin/langbot:v4.11.0 docker run -d --name a4-runtime --network a4 --network-alias langbot_plugin_runtime --restart on-failure \ -e TZ=Asia/Shanghai -e SPACEDISABLE_TELEMETRY=true -v "$PWD/deploy/a4/data/plugins:/app/data/plugins" \ rockchin/langbot:v4.11.0 uv run --no-sync -m langbot_plugin.cli.init__ rt Admin account demo@langbot.local / LangBot411!demo (local demo value — replace with your own). Create the WeCom customer-service bot: Bots → New → platform 企业微信客服 (WeCom Customer Service); 企业ID, 密钥, 令牌 and 消息加解密密钥 are required, Webhook 回调地址 is optional (real traffic needs a public URL). Fields per LangBot/src/langbot/pkg/platform/sources/wecomcs.yaml (spec.legacy: true). This instance keeps demo values. Install the Dify Agent runner plugin: Marketplace → search Dify → 「Dify Agent」 → install. Measured progress text (2026-10-16): 「已完成」「100%」「包大小: 169.8 KB」「安装成功」; the installed card carries aria-label="已安装 Dify Agent". Create the Agent: Processors → New → Agent mode → runner Dify Agent (the processor here is dify-wecomcs-agent). Fill in the Dify connection: 基础 URL must be reachable from the runtime container (alias dify here, hence dify, local demo value — replace with your own); API 密钥 is app-demo-dify-key here (local demo value — replace with your own); 超时 30, 过期 3600, 身份 发送者. Bind the event route: bot detail page (/home/bots?id=<uuid> — a query parameter, not a path segment; source LangBot/web/src/app/home/bots/page.tsx:7-8) → event routing → route 1 is 收到消息 message.received; switch its processor from ⚙️ 企业微信客服 流水线 to 🤖 dify-wecomcs-agent, then reload. Check the route: the route-check reply, measured verbatim on 2026-10-16, is 已命中路由 | 目标处理器 | Agent · dify-wecomcs-agent | 命中规则 | 第 1 条路由 | 诊断步骤 | 已选择第 1 条路由。 Run one event-debug pass: Agent detail → event debug → run test with 「请问你们支持私有化部署吗?」. Verify The first check is the route-check reply quoted in step 6. For the second, read the Dify endpoint's log. This walkthrough used a local Dify-compatible endpoint (container lang7-dify, deploy/a4/dify-stub.py, alias dify, host 127.0.0.1:18710 → 5001, implementing only POST /v1/chat-messages): docker logs lang7-dify One measured request line, verbatim: [2026-10-16T16:28:29] POST /v1/chat-messages auth=Bearer app-dem... body={"inputs": {}, "query": "请问你们支持私有化部署吗?", "user": "lb_3ae22b5eaf4cf199693bee82712ae06f5ea6c21128cd9feab3dc347985a5e56", "response_mode": "streaming", "conversation_id": "", "files": []} On your own deployment, look for a POST /v1/chat-messages whose query is the sentence you sent. Do not read multi-turn continuity into this. The session fired three times (16:28:29, 16:28:50, 16:38:28), each with an empty conversation_id; delivery and a returned answer are all this proves. [Inference] The runner plugin reads the id from ctx.state.conversation["external.conversation_id"] and passes an empty string when none is there, so the first call cannot carry one; later calls would only if the plugin writes Dify's returned conversation_id back into state. Official docs ask external runners to keep that id in plugin storage or context runtime state rather than LangBot's internal conversation uuid structure (official docs docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md §6). To be confirmed. Limits and caveats Not a real Dify deployment. Replace 基础 URL and API 密钥 with your own Dify site's values (local demo value — replace with your own). No real WeCom customer message entered the system. Real traffic needs 企业ID, 密钥, 令牌, 消息加解密密钥 and a public callback URL; this instance keeps demo values. Verified here: the configuration taking effect, plus the Agent ↔ Dify round trip — not a customer on a phone. Support depends on the adapter and platform — check the support matrix first. Debug platform actions are simulated. The page says: 「平台工具使用 Mock:Agent 真实调用工具,回复、发送等平台动作模拟执行,不发送真实消息。其他工具仍按实际配置执行。」 (measured 2026-10-16) — a passing debug run shows the configuration is live, not that WeCom-side delivery matches. Multi-turn continuity is unverified. All three triggers carried an empty conversation_id. One response target per event. A second route on the same event does not make Dify run twice, and the old pipeline should not stay on that event. The runner is a plugin. Old built-in runners get no runtime fallback (official docs docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md:22); another environment needs the plugin installed too. Summary and next steps Connecting Dify to WeCom customer service on 4.11 is three configuration actions: install langbot-team/DifyAgent, create an Agent-mode processor holding the Dify 基础 URL and API 密钥, and point the bot's message.received route at it. This walkthrough exercised the message reaching Dify and an answer coming back. The migration reference is frozen at pipeline-migration-config-map.zh-CN.md, mapping a Dify provider from an old pipeline to its replacement plugin and config. See the v4.11.0 release for the change list; the series' second and third articles cover runner-plugin provenance and the 4.10 → 4.11 upgrade. A hosted option is worth evaluating separately if you would rather not maintain an instance (space.langbot.app) — this article covers self-hosted deployments only.

  4. DEV · · @rockchinq

    QQ friend requests and group invites: two approvals, one Agent Scenario A QQ bot mostly does not chat; it handles two requests: a stranger asks to add it as a friend, or it gets pulled into a group. A pipeline built for "message in → reply out" cannot catch them — they are not messages. On 4.10, OneBot v11 was a protocol adapter reaching protocol ends like NapCat or Lagrange, but every event landed in one pipeline — splitting by event meant writing that logic yourself. 4.11 promotes it to an event-driven adapter, where request events land on an Agent. How it works in 4.11 A new package. src/langbot/pkg/platform/adapters/aiocqhttp/ is a standalone adapter package added in v4.11.0. git diff --stat v4.10.11 v4.11.0 lists, verbatim (source): __init__.py (+5), adapter.py (+243), api_impl.py (+238), event_converter.py (+275), manifest.yaml (+149), message_converter.py (+285), onebot.png, platform_api.py (+84), transport.py (+125), types.py (+9) — eleven files with the old entry point, +1416 / -1. The old config is downgraded. The previous src/langbot/pkg/platform/sources/aiocqhttp.yaml changed by four lines: spec.legacy: true added, spec.categories going from [protocol] to [popular, protocol] (source). So the wizard now generates a reverse-WS URL instead. One event, one row. A bot stores rows of event pattern → processor (a pipeline, an Agent or a plugin processor). Rows are tried in order: enabled → pattern matches → filters pass → target declares the event; the first row clearing all four wins, the rest skipped (official docs docs/event-based-agents/04-event-routing.md §3). The event scope decides the tools. Only friend.request_received brings "处理好友请求", only bot.invited_to_group brings "处理入群邀请". Both carry an event trigger and risk dangerous (source LangBot/src/langbot/pkg/agent/runner/platform_tools.py:176-203). Both act immediately, so never hand them to an Agent that does not need them. Support depends on the adapter and platform — check the support matrix first. Hands-on steps Start a local demo instance: docker network create a4 docker run -d --name a4-core --network a4 --restart on-failure -e TZ=Asia/Shanghai \ -e BOX__ENABLED=false -e SPACE__DISABLE_TELEMETRY=true -p 15800:5300 \ -v "$PWD/deploy/a4/data:/app/data" rockchin/langbot:v4.11.0 docker run -d --name a4-runtime --network a4 --network-alias langbot_plugin_runtime --restart on-failure \ -e TZ=Asia/Shanghai -e SPACE__DISABLE_TELEMETRY=true -v "$PWD/deploy/a4/data/plugins:/app/data/plugins" \ rockchin/langbot:v4.11.0 uv run --no-sync -m langbot_plugin.cli.__init__ rt Admin demo@langbot.local / LangBot411!demo (local demo value — replace with your own). Demo bot QQ 演示机器人, adapter onebot-v11. 1. Step 2 of the wizard: pick OneBot v11. Its card reads, verbatim, "OneBot v11 适配器,用于接入 QQ 兼容协议端,支持事件驱动编排". !Wizard, platform step: OneBot v11 selected 2. Configure the adapter. Mode 反向 WS — 协议端连接 LangBot (protocol end dials LangBot); the protocol-end URL is generated on save; token demo-onebot-token-replace-me (local demo value — replace with your own); expected account 10001; 保存并启用 gives 保存成功 and badge 已启用. Put that URL and token into your own NapCat / Lagrange. !Adapter config: reverse WS, URL, token, expected account 3. Read the event catalog. The routing card reports "共 12 类" — "机器人状态 · 4", "好友 · 2", "群组 · 3", "消息 · 2" and one more; the full list is supported_events in the adapter's manifest.yaml. Support depends on the adapter and platform — check the support matrix first. !Event catalog: 12 event types 4. Route 1 uses the preset 审核好友请求. The "添加行为" menu offers six presets, 回复收到的消息 / 欢迎新成员 / 处理成员离群 / 审核好友请求 / 处理群管理事件 / 配置其他事件, intersected with the adapter's declared events (source LangBot/web/src/app/home/bots/components/bot-form/EventBindingsEditor.tsx:1554-1559), so the preset Telegram filters out survives here; the trigger is 收到好友请求 / friend.request_received. !Add-behavior menu: six presets, one being review friend request 5. Create the Agent inline. 创建新处理器, runner 本地 Agent (plugin:langbot-team/LocalAgent/default), name qq-approvals-agent; on submit, 创建成功, and route 1's processor becomes ⚙️ qq-approvals-agent. !Create-processor dialog: runner Local Agent, name qq-approvals-agent !Route 1: friend request received points at qq-approvals-agent 6. Route 2. 添加行为 → 配置其他事件 → 机器人被邀入群 (bot.invited_to_group), same processor ⚙️ qq-approvals-agent — only Agents declaring the event are offered. !Both routes saved side by side, both pointing at the Agent 7. Pin the Agent's event scope. It starts as one wildcard chip 全部事件 *; the two events replace the wildcard, the card reports "共 2 项", each with three tools: friend.request_received → 回复当前会话(操作)/ 查询事件发起者(只读)/ 处理好友请求(敏感) bot.invited_to_group → 查询事件发起者(只读)/ 查询当前群组(只读)/ 处理入群邀请(敏感) Other-tool counters on that page, verbatim: 平台工具14 沙盒0 MCP0 插件0 已选 0. !Agent event scope: two events, three available tools each Select a model first — this Agent's 模型 reads 请选择模型, so 保存 first pops 运行器配置待完善, then 保存成功. Verify Both rows persist. Read back from bots.event_bindings: | # | Event pattern | Processor | order | enabled | | --- | --- | --- | --- | --- | | route 1 | friend.request_received | Agent · qq-approvals-agent | 0 | true | | route 2 | bot.invited_to_group | Agent · qq-approvals-agent | 1 | true | Both target_uuid values are 69159ec4-3941-4a1a-994f-45c0b532d01d; the bot row has enable=1. Check-route hits twice. Measured 2026-10-16, 检查事件路由 with "收到好友请求": 已命中路由 | 目标处理器 | Agent · qq-approvals-agent | 命中规则 | 第 1 条路由 | 诊断步骤:已选择第 1 条路由。第 2 条路由已跳过。事件与此路由不匹配。 With "机器人被邀入群": 第 1 条路由已跳过。事件与此路由不匹配。已选择第 2 条路由。 Its listbox holds twelve entries, matching the adapter declaration. !Check route: friend request received hits route 1 !Check route: bot invited to group hits route 2 Scope and tools agree. The three tools in step 7 are the source's event-bound group: event_respond_friend_request bound only to friend.request_received, event_respond_group_invite only to bot.invited_to_group. Limits and caveats - No real QQ traffic, and 运行测试 was never run. The instance has no QQ protocol end (no NapCat, no Lagrange), the token is a local demo value (local demo value — replace with your own), and the Agent has no model, so a test run would prove nothing. - Platform tools run under Mock. The note hides in the tooltip of the 事件调试 card's info icon, verbatim: "平台工具使用 Mock:Agent 真实调用工具,回复、发送等平台动作模拟执行,不发送真实消息。其他工具仍按实际配置执行。" !Event-debug card: the platform-tools-use-Mock note as a tooltip - Select a model before saving, otherwise the save reports 运行器配置待完善. - Old config, new home. Reverse WS has the protocol end dial LangBot's main port, with no extra listener; HTTP is unsupported — old host/port config is the legacy adapter now. - A menu entry is not a capability. The menu renders from the adapter declaration — twelve types here, which is why the friend-request preset survives. Support depends on the adapter and platform — check the support matrix first (official docs docs/event-based-agents/01-event-system.md:453-476). - 待确认: verified at the config layer only; no real protocol end was attached, and the approve/reject policy is prompt and model behaviour that was not evaluated. - [Inference] "one event to two processors" was not measured; arbitration selects one row and skips the rest. Summary and next steps Wiring these two approvals is not about a longer prompt. It is two rows of event pattern → processor — friend.request_received and bot.invited_to_group wake the same qq-approvals-agent — and with the scope narrowed to those two, both sensitive tools live on that Agent alone. Coming from 4.10, start at the v4.11.0 release page; the migration map is docs/pipeline-migration-config-map.zh-CN.md; runner plugins live in the langbot-plugins repository. Managed hosting at space.langbot.app is another option.

  5. Zenn · · @rockchinq

    Wiring n8n into Slack: @mention the bot to run an automation workflow Scenario The team works in Slack; the automations live in n8n. Sending a release notice means someone leaving the channel, opening n8n, clicking Execute and pasting the result back. Making the conversation the entry point takes two decisions on the bot side: who runs it — the message goes to n8n, not a model — and what runs — one Slack turn maps onto one n8n webhook. On 4.10 both meant a glue service you maintained; 4.11 makes "who runs it" a plugin: a processor picks a plugin-provided executor under Runner, and n8n is one. An @mention here reaches one n8n workflow, and its answer comes back. How it works in 4.11 Runners come from plugins. 4.11 moved the built-in runners out of the host. n8n is the official langbot-team/N8nAgent plugin, runner id plugin:langbot-team/N8nAgent/default (source LangBot/docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md:30); the same document puts it in Batch 2, the external-workflow batch with langflow-agent (webhook/workflow in and out, timeout, external conversation id, :46), and says the old built-in runner code offers no runtime fallback (:22). Runner settings live on the processor. With method Agent and n8n under Runner, the fields appear on the AI capability tab — not the plugin page, which reports none. Fields measured 2026-10-16 on v4.11.0: Webhook URL, auth type (none here), response handling (forward as chat reply), provider user identity (sender), an "enable LangBot assets" toggle. One n8n call's ingress and egress sit on this page. Event routing only answers "event → processor". The bot detail page binds an event pattern to a processor. An EventBinding carries id, event_pattern, target_type(agent|pipeline|discard), target_uuid, filters, priority, enabled, description; matching drops enabled=false, checks event_pattern and the filters, requires the target to declare support, takes the highest priority (ties by list order), and runs exactly one response target (official docs docs/event-based-agents/04-event-routing.md §3). External systems — Webhook, Dify, n8n, Coze — are served by the matching Runner plugin, and a plugin EventListener is not a third response-target type (§1). What decides whether it fires is the processor's trigger conditions: key at (UI label @, description "Whether to trigger when the message mentions the bot"), from LangBot/src/langbot/templates/metadata/pipeline/trigger.yaml:19-23; the same page holds group response rules, access control, message-ignore rules and aggregation. at defaults to false in the source template; this instance shows it checked — that processor's value, not a factory default. The same restriction can live on the binding: filters offer chat_type, chat_id, message_text, message_element_types, and message_element_types supports contains, not_contains over At, AtAll (source LangBot/web/src/app/home/bots/components/bot-form/EventBindingsEditor.tsx:146-183). [Inference] The trigger condition belongs to the pipeline chain's response rules (src/langbot/pkg/pipeline/pipelinemgr.py:43; the @ rule at src/langbot/pkg/pipeline/resprule/rules/atbot.py:34), while agent routing goes through Runner Host orchestration (official docs docs/event-based-agents/04-event-routing.md §4.2) — not the same switch. The Slack side. Fields, from LangBot/src/langbot/pkg/platform/sources/slack.yaml: bot_token (Bot Token, required), socket_mode (Socket Mode, default false), app_token (App-Level Token in socket mode, xapp- with connections:write), webhook_url (Webhook Callback URL, non-socket mode), signing_secret (non-socket mode). Socket Mode on means no public callback URL. Support depends on the adapter and platform — check the support matrix first. Hands-on steps Start a local demo instance (commands from this series' measured environment): docker network create a4 docker run -d --name a4-core --network a4 --restart on-failure -e TZ=Asia/Shanghai \ -e BOXENABLED=false -e SPACEDISABLE_TELEMETRY=true -p 15800:5300 \ -v "$PWD/deploy/a4/data:/app/data" rockchin/langbot:v4.11.0 docker run -d --name a4-runtime --network a4 --network-alias langbot_plugin_runtime --restart on-failure \ -e TZ=Asia/Shanghai -e SPACEDISABLE_TELEMETRY=true -v "$PWD/deploy/a4/data/plugins:/app/data/plugins" \ rockchin/langbot:v4.11.0 uv run --no-sync -m langbot_plugin.cli.init__ rt Admin demo@langbot.local / LangBot411!demo (local demo value — replace with your own). 1. Create the Slack bot Bots → New → platform Slack; fill in the Slack app's Bot Token, scopes at least chat:write and app_mentions:read. With this demo's placeholder token, slack.com/api/auth.test errors follow — expected: the article verifies the message → n8n path. 2. Install the n8n Runner plugin Marketplace → search n8n → install N8nAgent (v0.2.0, langbot-team); it shows up under /home/extensions beside the built-in LocalAgent. 3. Create the processor and pick the Runner Processors → New → method Agent → Runner n8n. Set Webhook URL on the AI capability tab to an address the runtime container can reach: both containers share Docker network a4 with the n8n alias n8n, so the value is n8n (local demo value — replace with your own); use the 127.0.0.1 form only if the whole chain shares a single network namespace. 4. Build the n8n workflow A Webhook node (HTTP Method POST, Path langbot-slack, Response Mode Respond to Webhook) into a Respond to Webhook node returning JSON, e.g. {"text": "n8n 收到:{{ $json.body.text }}(工作流已执行)"}; save and activate it. 5. Bind the event route Bot detail page → Event Routing → one route: event pattern message.received, target the new processor; the filter panel can add message_element_types contains At to restrict it to @-carrying messages. 6. Confirm the trigger condition On the trigger conditions tab, confirm @ under group response rules is checked — it is here; in the source template at defaults to false. Verify One thing to verify: whether the runtime container can turn a message into one HTTP call that lands on the n8n webhook. This bypasses Slack — POST the address from another container: docker exec a4-core python3 -c 'import json,urllib.request as u r=u.urlopen(u.Request("n8n", data=json.dumps({"text":"@langbot 发一版通知"}).encode(), headers={"Content-Type":"application/json"}),timeout=20) print(r.status, r.read().decode())' A working probe returns the JSON the Respond to Webhook node builds; with the workflow inactive the path is unregistered and the request fails. The measured result and control run (2026-10-16) are in series/langbot-4.11-release/evidence/demo-test-records.md. Limits and caveats A Runner is a plugin, not a host feature. The old built-in runner code provides no runtime fallback (docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md:22), so n8n presupposes the plugin on the runtime. at is instance state, not a factory default (LangBot/src/langbot/templates/metadata/pipeline/trigger.yaml:19-23). One event, one response target (docs/event-based-agents/04-event-routing.md §3): a second route on message.received does not make n8n run twice — check the route first. The Webhook URL must be reachable from the runtime container, not your browser: one that answers curl on the host can fail in the container. Slack was not verified end to end: the demo bot's placeholder token means this article covers the message → webhook path only. Summary and next steps The conversation is the entry point, event routing decides who runs it, and the Runner plugin is the bridge: install langbot-team/N8nAgent, point a processor's Webhook URL at one n8n webhook, bind message.received to that processor — an @mention becomes one workflow run whose reply returns to the channel. Primary reference: the pinned migration doc docs/pipeline-migration-config-map.zh-CN.md and the v4.11.0 release page. Article 2 covers Runner plugin sources; article 3 covers upgrading from 4.10. Managed hosting is only a conditional secondary option at space.langbot.app; this article makes no promise about it.

  6. DEV · · @rockchinq

    One Telegram group, three event routes: welcome, rules Q&A and moderation each get their own Agent Scenario A Telegram community group has three chores — greet joiners with the two rules, answer "how do I configure this", deal with ad spam on the spot — and today all three ride one bot: one pipeline, one prompt. The welcome matters only at the instant someone joins; rules Q&A faces every ordinary message; kicking and banning never belonged to a Q&A assistant. Merged into one prompt, the model has to guess which situation it is in, and a wrong guess punishes the wrong person. How it works in 4.11 The processor layer gained an Agent option. On 4.10 a bot carried one pipeline, so "speak when someone joins" had to be decided inside it. 4.11 replaces that with event routing: rows of event pattern → processor, where a processor is a pipeline or an Agent. An Agent brings a runner (supplied by a plugin; here langbot-team/LocalAgent) plus a system prompt. Adapters declare which events they emit. Support depends on the adapter and platform — check the support matrix first. One event hits exactly one route. Rows are tried in order: enabled → pattern matches → filters pass → does the target declare support. The first row clearing all four wins and the rest are skipped (official docs docs/event-based-agents/04-event-routing.md §3). Measured 2026-10-16, check-route returned 目标处理器 | Agent · tg-welcome-agent | 命中规则 | 第 2 条路由 | 诊断步骤 | 第 1 条路由已跳过。事件与此路由不匹配。已选择第 2 条路由。第 3 条路由已跳过。事件与此路由不匹配。 — route 2 matched. The event scope decides three things: which events reach the Agent, what the debug dropdown offers, and whether the Agent can be picked as a route processor. Measured 2026-10-16: creating the group.member_joined route offered only tg-welcome-agent and dify-wecomcs-agent; tg-rules-agent and tg-moderation-agent were absent. What the adapter declares is what the menu holds. telegram-omni declares 11 event types, word for word with source LangBot/src/langbot/pkg/platform/adapters/telegram/adapter.py:168-180: message.received, message.edited, message.reaction, group.member_joined, group.member_left, group.member_banned, bot.invited_to_group, bot.removed_from_group, bot.muted, bot.unmuted, platform.specific. So the "add behavior" menu offers four presets (reply to messages, welcome new members, handle departures, handle group moderation) plus "configure another event"; the fifth preset, review friend requests (friend.request_received), is absent because this adapter declares no such event (source LangBot/web/src/app/home/bots/components/bot-form/EventBindingsEditor.tsx:1554-1559, presets at :194-230). The conclusion stops there: the menu follows the declared events, not our judgment about a platform. Hands-on steps Start a local demo instance: docker network create a4 docker run -d --name a4-core --network a4 --restart on-failure -e TZ=Asia/Shanghai \ -e BOX__ENABLED=false -e SPACE__DISABLE_TELEMETRY=true -p 15800:5300 \ -v "$PWD/deploy/a4/data:/app/data" rockchin/langbot:v4.11.0 docker run -d --name a4-runtime --network a4 --network-alias langbot_plugin_runtime --restart on-failure \ -e TZ=Asia/Shanghai -e SPACE__DISABLE_TELEMETRY=true -v "$PWD/deploy/a4/data/plugins:/app/data/plugins" \ rockchin/langbot:v4.11.0 uv run --no-sync -m langbot_plugin.cli.__init__ rt Admin demo@langbot.local / LangBot411!demo (local demo value — replace with your own). 1. Create the Telegram bot. Bots → New → platform Telegram, paste the BotFather token. This demo uses a placeholder (local demo value — replace with your own), so Telegram never connects; what is verified is where an event is delivered. Detail page /home/bots?id=<uuid> (source LangBot/web/src/app/home/bots/page.tsx:7-8). !Wizard, platform step: Telegram selected 2. Read the adapter's event catalog. The panel reports 11 event types; those eleven names bound every menu below. !Telegram adapter event catalog: 11 event types 3. Create three Agents, one event each. Runner LocalAgent (plugin:langbot-team/LocalAgent/default), model deepseek-chat, event scope a single event: tg-welcome-agent → group.member_joined, tg-rules-agent → message.received, tg-moderation-agent → group.member_banned. !Agent event scope picker: exactly one event checked 4. Prompt and tools for the welcome Agent. A greeting under 60 characters naming the two rules, with "reply to the current conversation" (event_reply) allowed. !Welcome Agent config: runner, main model and system prompt 5. Run one debug round trip first. On tg-welcome-agent's debug page — its dropdown offers only the event it declares — run group.member_joined with {"member_name": "小林", "member_id": "10086", "group_id": "-1001234567890"}. Measured 2026-10-16: one tool call recorded, tool "reply to the current conversation event_reply", text verbatim: 欢迎语已通过「发送消息」工具发出(模拟投递,未真实调用平台 API): > 欢迎小林加入 LangBot 交流群!🎉 提问请带上 LangBot 版本号,禁止发广告。配置说明可在 GitHub 仓库 docs 目录查看。 (53 字,符合 60 字以内要求:包含欢迎、两条群规与 docs 目录提示。) !Welcome Agent debug output: one tool call and the verbatim reply 6. Only the tools moderation needs. The events-and-tools page reports one event (member banned group.member_banned) and four tools enabled; of 14 platform tools only "kick group member" is checked — event_kick_member → kick_member, risk dangerous (source LangBot/src/langbot/pkg/agent/runner/platform_tools.py:99-203), labelled sensitive in the UI (read/write/dangerous = read-only/action/sensitive; source LangBot/web/src/i18n/locales/zh-Hans.ts:1368-1370). !Moderation Agent events and tools page: only "kick group member" checked 7. Create the three routes: message.received → tg-rules-agent, group.member_joined → tg-welcome-agent, group.member_banned → tg-moderation-agent. !"Add behavior" menu on Telegram: four presets plus "configure another event" Verify The routes persist. Read back from bots.event_bindings (updated_at 2026-10-16 17:23:44); raw JSON in series/langbot-4.11-release/evidence/demo-test-records.md §8.3: | # | Event pattern | Processor | priority | enabled | | --- | --- | --- | --- | --- | | route 1 | message.received | Agent · tg-rules-agent | 0 | true | | route 2 | group.member_joined | Agent · tg-welcome-agent | 0 | true | | route 3 | group.member_banned | Agent · tg-moderation-agent | 0 | true | Dry-run one event. Check-route with "member joined group": target processor | Agent · tg-welcome-agent, matched rule | route 2, diagnostics route 1 skipped, route 2 selected, route 3 skipped. Each route accepts only its own event. !Check route: group.member_joined matches route 2 and points at tg-welcome-agent Limits and caveats - No real Telegram traffic. A local demo token (local demo value — replace with your own) means the verified result is "the configuration took effect and the event routed to the right Agent, which completed one model call", not delivery into a group. - Platform tools run under Mock in the debug page, verbatim: "平台工具使用 Mock:Agent 真实调用工具,回复、发送等平台动作模拟执行,不发送真实消息。其他工具仍按实际配置执行。" and "未记录到工具调用;生成文本不代表消息已发送。" - Confine dangerous tools. event_kick_member is enabled only for the moderation Agent; on a real platform a kick is immediate. - Processor choice depends on the event. A pipeline target binds only message.received (official docs docs/event-based-agents/04-event-routing.md §3); join and ban events need an Agent or a plugin processor. Support depends on the adapter and platform — check the support matrix first. - To confirm: one debug round trip was observed, not consecutive turns inside a group; tg-rules-agent's answer quality was not evaluated. - [Inference] "two routes may both deliver one event" is outside the measurement — arbitration selects one row (route 2 matched; 1 and 3 skipped). For one event to reach two targets, use the separate plugin-processor path. Summary and next steps Three chores, three Agents, and the glue is rows of event pattern → processor: group.member_joined wakes the welcome Agent, message.received the rules Agent, group.member_banned the moderation Agent. Each scope is one event wide, so nothing else can wake it. Coming from 4.10, start at the v4.11.0 release page; matching details are in docs/event-based-agents/04-event-routing.md; runner plugins live in the langbot-plugins repository. Managed hosting at space.langbot.app is one other option, subject to your deployment constraints.

  7. DEV · · @rockchinq

    Wiring Dify into WeCom Customer Service: install a Runner plugin, let an Agent own message.received Scenario The knowledge base, prompts and tools live in a Dify application; the WeCom customer-service side only answers customers. Today every question is retyped into Dify's chat box and the answer copied back — the two are not connected. On 4.10 the only route was a pipeline: Dify as a provider (Dify service API), wired into one pipeline. That duplicated the orchestration — Dify canvas plus LangBot's own model, prompt and conversation settings — and one pipeline served one bot. 4.11 makes a processor an Agent, whose Runner comes from a plugin. Connecting Dify to WeCom customer service becomes three configuration actions, walked below on a local instance. How it works in 4.11 Runners are marketplace plugins. Dify is the official langbot-team/DifyAgent plugin, runner id plugin:langbot-team/DifyAgent/default (official docs docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md:29 maps the old dify-service-api runner to it). It sits in Batch 1, "traditional service API runner" (:45), and the old built-in runner code provides no runtime fallback (:22). Connection details live in the processor's runner config. Choose Agent mode, pick Dify Agent, then fill Dify-side values; DOM readings here (measured 2026-10-16): 基础 URL (http://dify:5001/v1), API 密钥, 超时 (30), 过期 (3600), 身份 (发送者). Event routing maps events to processors; the first match wins. Matching picks by priority from high to low, then list order, and runs exactly one response target (official docs docs/event-based-agents/04-event-routing.md:118-119) — so a second route on message.received would not make Dify run twice. Adapters declare a platform's events. This one declares message.received, message.deleted, wecomcs.enter_session and more, plus send_message, reply_message, get_message, get_user_info (source LangBot/src/langbot/pkg/platform/adapters/wecomcs/adapter.py:79-99). Route-editor presets follow those declarations (source LangBot/web/src/app/home/bots/components/bot-form/EventBindingsEditor.tsx:1554-1559). Support depends on the adapter and platform — check the support matrix first. Hands-on steps Fire up a local instance to try this on (the exact commands used in the recorded run): docker network create a4 docker run -d --name a4-core --network a4 --restart on-failure -e TZ=Asia/Shanghai \ -e BOX__ENABLED=false -e SPACE__DISABLE_TELEMETRY=true -p 15800:5300 \ -v "$PWD/deploy/a4/data:/app/data" rockchin/langbot:v4.11.0 docker run -d --name a4-runtime --network a4 --network-alias langbot_plugin_runtime --restart on-failure \ -e TZ=Asia/Shanghai -e SPACE__DISABLE_TELEMETRY=true -v "$PWD/deploy/a4/data/plugins:/app/data/plugins" \ rockchin/langbot:v4.11.0 uv run --no-sync -m langbot_plugin.cli.__init__ rt Admin account demo@langbot.local / LangBot411!demo (local demo value — replace with your own). 1. Create the WeCom customer-service bot: Bots → New → platform 企业微信客服 (WeCom Customer Service); 企业ID, 密钥, 令牌 and 消息加解密密钥 are required, Webhook 回调地址 is optional (real traffic needs a public URL). Fields per LangBot/src/langbot/pkg/platform/sources/wecomcs.yaml (spec.legacy: true). This instance keeps demo values. !Bot wizard: 企业微信客服 (WeCom Customer Service) selected 2. Install the Dify Agent runner plugin: Marketplace → search Dify → 「Dify Agent」 → install. Measured progress text (2026-10-16): 「已完成」「100%」「包大小: 169.8 KB」「安装成功」; the installed card carries aria-label="已安装 Dify Agent". !Marketplace search for Dify: the Dify Agent card !Installed plugin card: Dify Agent by langbot-team 3. Create the Agent: Processors → New → Agent mode → runner Dify Agent (the processor here is dify-wecomcs-agent). !Runner picker listing installed plugins 4. Fill in the Dify connection: 基础 URL must be reachable from the runtime container (alias dify here, hence http://dify:5001/v1, local demo value — replace with your own); API 密钥 is app-demo-dify-key here (local demo value — replace with your own); 超时 30, 过期 3600, 身份 发送者. !Runner config: base URL, API key, timeout, expiry, identity 5. Bind the event route: bot detail page (/home/bots?id=<uuid> — a query parameter, not a path segment; source LangBot/web/src/app/home/bots/page.tsx:7-8) → event routing → route 1 is 收到消息 message.received; switch its processor from ⚙️ 企业微信客服 流水线 to 🤖 dify-wecomcs-agent, then reload. 6. Check the route: the route-check reply, measured verbatim on 2026-10-16, is 已命中路由 | 目标处理器 | Agent · dify-wecomcs-agent | 命中规则 | 第 1 条路由 | 诊断步骤 | 已选择第 1 条路由。 !Route check: target Agent · dify-wecomcs-agent 7. Run one event-debug pass: Agent detail → event debug → run test with 「请问你们支持私有化部署吗?」. !Event debug: Dify received the question, answered by the local endpoint Verify The first check is the route-check reply quoted in step 6. For the second, read the Dify endpoint's log. This walkthrough used a local Dify-compatible endpoint (container lang7-dify, deploy/a4/dify-stub.py, alias dify, host 127.0.0.1:18710 → 5001, implementing only POST /v1/chat-messages): docker logs lang7-dify One measured request line, verbatim: [2026-10-16T16:28:29] POST /v1/chat-messages auth=Bearer app-dem... body={"inputs": {}, "query": "请问你们支持私有化部署吗?", "user": "lb_3ae22b5eaf4cf199693bee82712ae06f5ea6c21128cd9feab3dc347985a5e56", "response_mode": "streaming", "conversation_id": "", "files": []} On your own deployment, look for a POST /v1/chat-messages whose query is the sentence you sent. Do not read multi-turn continuity into this. The session fired three times (16:28:29, 16:28:50, 16:38:28), each with an empty conversation_id; delivery and a returned answer are all this proves. [Inference] The runner plugin reads the id from ctx.state.conversation["external.conversation_id"] and passes an empty string when none is there, so the first call cannot carry one; later calls would only if the plugin writes Dify's returned conversation_id back into state. Official docs ask external runners to keep that id in plugin storage or context runtime state rather than LangBot's internal conversation uuid structure (official docs docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md §6). To be confirmed. Limits and caveats - Not a real Dify deployment. Replace 基础 URL and API 密钥 with your own Dify site's values (local demo value — replace with your own). - No real WeCom customer message entered the system. Real traffic needs 企业ID, 密钥, 令牌, 消息加解密密钥 and a public callback URL; this instance keeps demo values. Verified here: the configuration taking effect, plus the Agent ↔ Dify round trip — not a customer on a phone. Support depends on the adapter and platform — check the support matrix first. - Debug platform actions are simulated. The page says: 「平台工具使用 Mock:Agent 真实调用工具,回复、发送等平台动作模拟执行,不发送真实消息。其他工具仍按实际配置执行。」 (measured 2026-10-16) — a passing debug run shows the configuration is live, not that WeCom-side delivery matches. - Multi-turn continuity is unverified. All three triggers carried an empty conversation_id. - One response target per event. A second route on the same event does not make Dify run twice, and the old pipeline should not stay on that event. - The runner is a plugin. Old built-in runners get no runtime fallback (official docs docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md:22); another environment needs the plugin installed too. Summary and next steps Connecting Dify to WeCom customer service on 4.11 is three configuration actions: install langbot-team/DifyAgent, create an Agent-mode processor holding the Dify 基础 URL and API 密钥, and point the bot's message.received route at it. This walkthrough exercised the message reaching Dify and an answer coming back. The migration reference is frozen at pipeline-migration-config-map.zh-CN.md, mapping a Dify provider from an old pipeline to its replacement plugin and config. See the v4.11.0 release for the change list; the series' second and third articles cover runner-plugin provenance and the 4.10 → 4.11 upgrade. A hosted option is worth evaluating separately if you would rather not maintain an instance (space.langbot.app) — this article covers self-hosted deployments only.

  8. 现在竟有这种奇观,issue出来之后两个账户过来抢着提pr LangBot#2707 t.co

    3 likes · 111 views
  9. DEV · · @rockchinq

    Put Claude Code and Codex Inside WeChat, Lark and DingTalk with LangBot Runner Plugins Scenario A team already lives in Claude Code and Codex — repos, shell, MCP servers on their own machines. Colleagues should reach that same power from WeChat Work, Lark and DingTalk group chats, without a terminal. On 4.10 that was awkward — not inbound: 4.10.11 already ships openclaw_weixin, lark, dingtalk under src/langbot/pkg/platform/sources/. The processing side was the wall: nine built-in runners under src/langbot/pkg/provider/runners/ (localagent.py, difysvapi.py, … weknoraapi.py), so adding Claude Code meant patching LangBot or wrapping the CLI in an HTTP service outside the bot. 4.11 flips that: a Runner is a plugin, and the platform is only the event source. Support depends on the adapter and platform — check the support matrix first. !Runner plugins in the plugin marketplace How it works in 4.11 New in 4.11: the built-in runners are gone. src/langbot/pkg/provider/runners/ holds ten files at v4.10.11 and does not exist at v4.11.0; src/langbot/pkg/provider/ keeps only modelmgr/, session/, tools/ (git ls-tree, both revisions). A Runner is a plugin component. The runtime reports installed plugins' runners over LIST_RUNNERS; the host discovers and caches them in RunnerRegistry (src/langbot/pkg/agent/runner/registry.py:20,24,152), generating ids as plugin:author/plugin_name/runner_name (src/langbot/pkg/agent/runner/id.py:24-27) and rejecting anything without that prefix (id.py:52-57; PROTOCOL_V1.md §13). The manifest is the contract: id, name, label, description, capabilities, permissions, config_schema, metadata (§4.2); capabilities is a typed boolean model for UI and resource projection, not a permission grant, and permissions is the access ceiling, intersected with the binding policy. Official ids: plugin:langbot-team/ClaudeCodeAgent/default, plugin:langbot-team/CodexAgent/default (docs/agent-runner-pluginization/OFFICIAL_RUNNER_PLUGINS.md:33-34). New in 4.11: the adapter layer. src/langbot/pkg/platform/adapters/ is absent at v4.10.11; 4.11 splits it into twelve packages with manifest.yaml, adapter.py, event_converter.py, and the Lark manifest declares kind: MessagePlatformAdapter (src/langbot/pkg/platform/adapters/lark/manifest.yaml:1-4,13). The inbound file inventory is unchanged: between v4.10.11 and v4.11.0, all 22 files under src/langbot/pkg/platform/sources/ are modifications — no additions, no deletions — so no new platform arrived here. Where config lands: ai.runner.id stores the runner id and ai.runner_config[<runner_id>] its settings (src/langbot/templates/metadata/pipeline/ai.yaml:6-18), so switching runners keeps earlier settings. Hands-on steps Runner plugins execute in a separate plugin-runtime container, so this is a two-container setup (local demo values — replace them): docker network create lang7-411 docker run -d --name lang7-411-core --network lang7-411 -e BOX__ENABLED=false \ -p 15400:5300 -v "$PWD/deploy/411/data:/app/data" rockchin/langbot:v4.11.0 docker run -d --name lang7-411-runtime --network lang7-411 --network-alias langbot_plugin_runtime \ -v "$PWD/deploy/411/data/plugins:/app/data/plugins" \ rockchin/langbot:v4.11.0 uv run --no-sync -m langbot_plugin.cli.__init__ rt The admin account starts as admin@langbot.local; the password is a local demo value — replace it. Step 1 — install from the marketplace. UI: Extensions → Extension Marketplace (web/src/i18n/locales/zh-Hans.ts:337-341); CLI: POST /api/v1/plugins/install/marketplace with {plugin_author, plugin_name}, returning {task_id} for task plugin-install-marketplace; the record carries install_source=marketplace (src/langbot/pkg/api/http/controller/groups/plugins.py:923-960). Here we installed langbot-team/LocalAgent; the Claude Code and Codex plugins use the same door (repo langbot-plugins, Runner/claude-code-agent, Runner/codex-agent). Step 2 — confirm where it landed. The artifact is not in the mounted plugins/ directory but inside the runtime container at /app/data/plugin-runtime/artifacts/sha256/<digest>/code/ (manifest.yaml, main.py, pkg/, components/, CERTIFICATION.md), while the host-mounted plugins/ stays empty (observed 2026-10-16); <digest> is the recorded artifact_digest. Three checks: the API (GET /api/v1/plugins returns a {plugins: [...]} array containing it, plugins.py:387-397); the database (a plugin_settings row holding artifact_digest, runtime_revision, install_source, enabled, src/langbot/pkg/entity/persistence/plugin.py:12-35,46-51); and the logs — Mounting plugin langbot-team/LocalAgent... → Initializing Runner component: default → Component default initialized, instance type: DefaultRunner (observed 2026-10-16). The point: the runtime instantiates a component instance and the Runner capability hangs off it. Step 3 — pick the Runner in the pipeline. In the pipeline editor open AI capabilities (web/src/i18n/locales/zh-Hans.ts:1941) and find the stage card labelled "运行方式" (src/langbot/templates/metadata/pipeline/ai.yaml:7-9) with its required "运行器" dropdown (:14-18). The value is the full runner id, e.g. plugin:langbot-team/LocalAgent/default; for Claude Code, plugin:langbot-team/ClaudeCodeAgent/default. Here the LocalAgent block at ai.runner_config[<runner_id>] read timeout: 300, max-tool-iterations: 100 (observed 2026-10-16). Step 4 — connect the chat platform as an event source. Connect a WeChat Work, Lark or DingTalk adapter; events arrive, event bindings route them into a pipeline, and the selected Runner does the work — one bot, several pipelines, each free to use a different Runner. Claude Code and Codex settings come from their READMEs: command defaults to claude / codex, timeout to 300 / 1800 seconds, plus workspace, ssh-target, mcp-servers-json (Runner/claude-code-agent, Runner/codex-agent). !The plugin list after install: LocalAgent 0.2.1 !Runner selection in the pipeline editor Verify API: GET /api/v1/plugins contains langbot-team/LocalAgent with enabled true. Database: digest and revision persisted — values observed here: SELECT plugin_author, plugin_name, artifact_digest, runtime_revision, install_source, enabled FROM plugin_settings; -- langbot-team|LocalAgent|2d2ce17055d70dd7736e6902595be3262992fd1598a00bd9d79d19fef1c16329|1|marketplace|1 Runtime container: the same ls shows the files listed above while the host-mounted plugins/ stays empty; an empty result means the wrong layer. Pipeline: ai.runner.id must start with plugin: (web/src/app/home/pipelines/pipeline-config-safety.ts:7-25) and ai.runner_config must hold an object keyed by that id. Then send a real message and confirm the reply comes from the installed Runner. Limits and caveats - The platform is only an event source. The inbound src/langbot/pkg/platform/sources/ file set matches 4.10.11 — all 22 files are modifications, none added or removed — and what 4.11 adds is the platform/adapters/ event-adapter layer; the adapter plus the event binding decides whether a platform works, not the Runner. The support matrix in 01-event-system.md is a preliminary assessment, per the doc. - Marketplace installs need egress. The archive is fetched from {space_url}/api/v1/marketplace/plugins/download/{author}/{name}/{version} (src/langbot/pkg/plugin/connector.py:1795-1797); installs from GitHub or a local archive record github / local. - Artifacts are content-addressed. The marketplace-side digest need not equal your local one; compare with plugin_settings.artifact_digest — here the two LocalAgent 0.2.1 digests differ (observed 2026-10-16). - The external CLI runners are permissive by default. ClaudeCodeAgent defaults dangerously-skip-permissions to true (false restores Claude Code's own checks; LangBot has no interactive approval flow today) and CodexAgent to approval-policy=never / sandbox-mode=danger-full-access. Trusted workspaces only. - Interaction is not a hanging wait. Both CLI runners turn requests such as ask_user_question into interaction.requested for the adapter to render as a card; the CLI stops while the user answers, and the answer starts a new run resuming the same session. - The harness path is an MVP. The protocol doc states an external harness runner is currently an MVP / dev path: proof the protocol can be wired up, not a release-grade security boundary or finished Docker deployability (PROTOCOL_V1.md §13). - Certification fields can disagree. Here install_info._certification read verification: "invalid" with certificate_id: null, while the marketplace reported certificate_status: "issued" — do not assume local verification passed because a plugin is listed (see article 4). Summary and next steps In 4.11 a Runner is not shipped with LangBot but installed from the marketplace, configured in the app, selected per pipeline. The platform delivers the event; the Runner decides how it is processed — a local CLI agent (Claude Code, Codex, ACP) or a service runner such as Dify, Coze, n8n, Langflow or DeerFlow. Primary reference: the pinned migration doc pipeline-migration-config-map.zh-CN.md, which also lists the minimum Runner plugin versions the migrator requires; the v4.11.0 release page has the notes. Plugins are also listed in Runner/ in langbot-plugins. Next: if your instance was upgraded from 4.10, that old config landing in the new shape is article 3 — the migration touches ai.runner.runner, ai.runner.id and ai.runner_config together. For a hosted environment, Cloud is worth a separate look (https://space.langbot.app).

    5 reactions
  10. DEV · · @rockchinq

    Stop Letting Your Group Bot Only Reply: Route Every Event to Its Own Agent Scenario A new member joins and the bot says nothing. A friend request arrives and it sits there. The bot is pulled into another group and does nothing there either — and that is usually not the adapter's fault: only messages can travel down the current path. In 4.10 a bot had a single entry point: use_pipeline_name, use_pipeline_uuid and pipeline_routing_rules (measured 2026-10-16 on a live v4.10.11 instance), and its rules could only filter launcher_type, launcher_id, message_content and message_has_element (source src/langbot/pkg/persistence/alembic/versions/0009_migrate_event_bindings.py:35-52). A QQ group bot should answer questions by day, welcome new members and hand friend requests to a human; on 4.10 you could only split messages across pipelines. How it works in 4.11 4.11 replaces "bot → one pipeline" with "bot → event bindings". Each binding answers three questions: which event, under which conditions, to whom. Data model (new in 4.11). Bindings live in new JSON columns on the bots table: event_bindings and plugin_processors (source src/langbot/pkg/entity/persistence/bot.py:55-56). There is no separate routing table; the three legacy columns are dropped by 0011_drop_legacy_bot_routing.py. One binding carries (official docs docs/event-based-agents/04-event-routing.md:69-73, plus order at runtime): - event_pattern — the event type: exact match, namespace.*, or the global * (source src/langbot/pkg/platform/botmgr.py:152-159). - filters — conditions on chat_type, chat_id, message_text, message_element_types; operators include eq, neq, contains, not_contains, starts_with, regex; all of them must hold. - target_type + target_uuid — pipeline, agent or discard (in the UI: Pipeline / Agent / Discard). - priority — higher wins; enabled — false eliminates the row. - order — list position, written from the array index on save (source src/langbot/pkg/api/http/service/bot.py:257); dragging a row edits it. Arbitration. An event ends up with exactly one response target. Disabled bindings are dropped, the event type is matched, the target is checked, filters are evaluated, and survivors are sorted by (priority, -order) descending — first wins (source botmgr.py:274-345, collect :330, sort :335; official docs 04-event-routing.md:115-119). Losers are recorded as lower_priority, so dragging a row up and raising its priority mean the same thing. Event naming (new in 4.11). Event types are {namespace}.{action} strings (official docs docs/event-based-agents/01-event-system.md:5), e.g. message.received, group.member_joined, friend.request_received, feedback.received. Platform events use platform.{adapter}.{action} (:447-451). Support depends on the adapter and platform — check the support matrix first. Separate 4.10 from what is new. Adapters are unchanged: http_bot.py is byte-identical between v4.10.11 and v4.11.0 (27,029 bytes), and 4.10 already shipped aiocqhttp, kook and wecombot. What is new is the event-binding data model and routing mechanism — adapters emit events, bindings pick the handler. A legacy adapter declaring no supported_events only emits message.received (source EventBindingsEditor.tsx). Support depends on the adapter and platform — check the support matrix first. Upgrading is not a blank slate. 0009_migrate_event_bindings.py turns use_pipeline_uuid into one binding and each pipeline_routing_rules entry into another (launcher_type→chat_type, launcher_id→chat_id, message_content→message_text, message_has_element→message_element_types, eq→contains, neq→not_contains; source :38-52). Bindings keep the same pipeline UUID; no agent or Runner configuration is created. That migration has an empty downgrade (source :133, not reversible), and 0035_pipeline_received_only.py later narrows message.* to message.received. Hands-on steps Start a fresh 4.11 instance (container name and data directory are yours to change): docker network create lang7-411 docker run -d --name lang7-411-core --network lang7-411 \ -e BOX__ENABLED=false -e SPACE__DISABLE_TELEMETRY=true -p 15400:5300 \ -v "$PWD/deploy/411/data:/app/data" rockchin/langbot:v4.11.0 The first boot runs the migrations first. Open http://127.0.0.1:15400 and sign in as admin@langbot.local (the password is a local demo value — replace it). Then: 1. Create or select a bot (the demo uses the generic HTTP adapter). 2. On the bot detail page, find the Event Routing card — it decides which processor gets an incoming event. 3. A new bot already has a message.received route pointing at a pipeline. The supported-events header groups events by what the adapter declares (here: Messages · 1). !Event routing card on the bot detail page: one message.received route pointing at a pipeline, plugin processors empty 4. Click Add behavior and pick a preset (reply to messages, welcome new members, ...), or use Add route to fill in event and target yourself. 5. On the new route row, pick the event and processor: the picker offers existing pipelines and agents, or Create new processor. The screenshot below shows the row added but not yet saved. !Adding a route to the event routing card and picking a processor 6. Expand Trigger conditions for filters: chat type, chat ID, message text, message elements — all must hold. 7. Drag rows to reorder (order), change weights with Priority, switch a route off with Enabled. 8. After saving, run Check routing (a dry run) before live traffic. An upgraded instance needs no manual re-wiring: copy the 4.10.11 data directory, point the same docker run at it, and legacy rules land as bindings through the mapping above. The field map is in pipeline-migration-config-map.zh-CN.md. Verify Three ways, fastest first: 1. Check routing in the card: pick an event type, run one synthetic dispatch, and it lists every binding's outcome — matched / delivered / discarded — or why it lost: event_pattern_mismatch, filters_mismatch, lower_priority, processor_incompatible (a pipeline pointed at a non-message event). Dry run and real dispatch share one matcher (source botmgr.py:361, diagnose_eba_event_binding). 2. Match records: recent matches held in the bot's memory. Cleared on restart — use the operation log for long-lived checks. 3. Look at the database: sqlite3 deploy/411/data/langbot.db "select name, event_bindings from bots". A migrated binding (measured 2026-10-16): {"event_pattern":"message.received","target_type":"pipeline","target_uuid":"2e59bbf9-…","filters":[],"priority":0,"enabled":true,"description":"Migrated from default pipeline binding","order":0}. While the pipeline behind target_uuid exists, the route works. Limits and caveats - One response target per event. Bindings arbitrate rather than run in parallel: if two match, only the first runs. - A pipeline target only accepts message.received. Any other event fails as processor_incompatible — "Pipeline targets only support message events" (source botmgr.py:162, :315-317). To greet someone on join, target an agent or plugin event processor. - Wildcards were narrowed. Legacy message.* bindings were rewritten to message.received by 0035. Hand-written message.* still matches those events, but a pipeline target fails the target check. - Adapters and events are not one-to-one. Support depends on the adapter and platform — check the support matrix first. The official table (docs/event-based-agents/01-event-system.md:453-476) calls itself a preliminary assessment: confirm per platform before relying on an event and treat anything unconfirmed as unverified. - Plugin event processors are a second, parallel channel. The plugin-processors section at the card's bottom takes no part in arbitration: it receives plugin-declared events independently of the routes above (source botmgr.py:285 skips those rows, :1012 reads plugin_processors separately). A plugin subscribing to an event is not your bot handling it. - Migration is a one-way door. The binding conversion has no downgrade and 0011 drops the old columns; Runner configuration migration is a separate, user-confirmed API. Back up the data directory first. Summary and next steps Upgrading from 4.10? Read the field map first: pipeline-migration-config-map.zh-CN.md. See the v4.11.0 release page for this version's scope and the official 01-event-system.md for event types and fields. Next up: Runner plugins — installing one from the marketplace and selecting it in a pipeline.

    5 reactions

Latest stargazers

The last 10 distinct accounts we observed this year.

Most active participants

PR, issue, comment & review events in the past year.

Packages

1
PackageVersionDownloads · 30dTrend · 30d
langbotv4.11.2
v4.11.2
≥1.9K

Live mentions badge

Show how many posts mention your project in the last 30 days.

Live mentions · last 30 days
langbot-app/LangBot mentions in the last 30 days