数据截至 (上游 commit 38006dda2d96)
接入层:多渠道 drivers、统一队列、MCP
30 秒导读: QwenPaw 的 agent 内核只认一种输入(
AgentRequest)和一种输出(Event流)。本章讲清两件事:左边,十几种 IM(飞书、钉钉、Discord、Telegram……)怎么把各自的原生消息标准化成这一种输入,再经一套统一优先级队列喂给内核;右边,外部工具怎么通过 driver / MCP 接进来,受统一策略引擎 + 加密凭据 + OAuth/白名单管控后,变成 agent 能调的 tool。
1. 这是什么(零基础也能懂)
一句话定义: 接入层是 agent 内核的两副插座——一副朝用户(渠道 channels),一副朝工具(drivers)。
先记住一个前提:agent 内核(见 第 01 章)不关心消息从飞书来还是从 Telegram 来, 也不关心工具是本地函数还是远程 MCP 服务。它只吃 AgentRequest,只吐 Event 流。接入层的全部职责,就是把五花八门的外部世界,翻译成内核认得的这一种语言。
这一层解决的两个真实痛点:
| 痛点 | 场景化白话 | 接入层怎么答 |
|---|---|---|
| 一个 agent 想同时上十几个 IM | 你写好一个客服 bot,老板要它同时在飞书、钉钉、企业微信、Discord 上线 | 渠道抽象:每个 IM 只实现"怎么收、怎么发",标准化逻辑复用父类 |
| 想让 agent 用别人家的工具 | 你想让 agent 能查 GitHub、操作数据库,但这些能力在别的进程/别的服务里 | driver + MCP:把外部工具注册成受管控的 capability,再暴露成 tool |
它能做什么(功能清单):
- 把 18 种内置渠道的原生消息,统一标准化成
AgentRequest(飞书群消息、钉钉卡片、语音……)。 - 按"渠道 × 会话 × 优先级"三元组分队列,不同会话并发、同会话严格串行,
/stop这类控制命令走高优先级插队。 - 渠道级访问控制:黑白名单 + 待审批(pending),陌生人第一条消息自动挂起等放行。
- 把外部 MCP 服务器注册成 driver,其工具经策略引擎(allow/ask/deny)、加密凭据、OAuth、工具白名单层层过滤后,变成 agent 可调的 tool。
一句话直觉/类比: 把接入层想成跨国机场的两道翻译闸机。入境闸(渠道)不管你说哪国话,统一翻成"标准语"送进城;出境闸(driver)不管城里要用哪国的服务,先查签证(策略)、验证件(凭据),放行了才让接触。城内(agent 内核)永远只讲标准语。
2. 顶层全景(它大概怎么转)
本节讲"大盘":一条消息进来、一次工具调用出去,分别流经哪些部件。
2.1 两条主线一张图
先看入站主线(用户消息 → 内核)和工具主线(内核要调工具 → 外部服务)。图从左到右是数据流向,▢ 是部件,→ 是流向。
入站主线(渠道 channels)
┌────────┐ 原生消息 ┌──────────────┐ AgentRequest ┌──────────────┐
│ 飞书/钉钉│ ───────────▶ │ 具体 Channel │ ─────────────▶ │ UnifiedQueue │
│ /TG/... │ webhook/ws │ 标准化+ACL闸 │ 入队(带优先级)│ 三元组 分队列 │
└────────┘ └──────────────┘ └──────┬───────┘
串行消费 │
▼
┌────────────┐
│ agent 内核 │
│ (第01章) │
└─────┬──────┘
工具主线(drivers) │ Event 流
┌────────┐ call_tool ┌──────────────┐ invoke ▼(回发经 Channel.send)
│ 外部MCP │ ◀─────────── │ MCPDriverHandler│ ◀────────── ┌────────────┐
│ 服务器 │ ───────────▶ │ 策略+凭据闸 │ ───────────▶ │ DriverManager│
└────────┘ 结果 └──────────────┘ capability │ 注册/分发 │
└────────────┘
怎么读:上半是"用户 → agent",下半是"agent → 工具"。两侧各有一道"闸"——入站的 ACL 闸、工具侧的 策略+凭据闸。城中央(agent 内核)只跟标准契约打交道。
2.2 部件一句话职责
渠道侧(app/channels/):
| 部件 | 干什么 | 文件 |
|---|---|---|
BaseChannel | 所有渠道的父类:定义"入站标准化 + 出站渲染 + ACL + 流式"的骨架 | app/channels/base.py:82 |
| 渠道注册表 | 懒加载 18 个内置渠道 + 汇入插件系统注册的渠道 | app/channels/registry.py:122-134 |
ChannelManager | 拥有队列与消费循环;对外提供线程安全的 enqueue | app/channels/manager.py:69 |
UnifiedQueueManager | 按三元组 QueueKey 动态建队列、动态起消费者、闲置回收 | app/channels/unified_queue_manager.py:60 |
CommandRegistry | 把 /stop、/status 等命令映射到优先级 0/10/20/30 | app/channels/command_registry.py:23 |
MessageRenderer | 把内核 Message 渲染成可发送的 content parts(markdown/emoji 可配) | app/channels/renderer.py:78 |
AccessControlStore | 每渠道的黑白名单 + 待审批,持久化到 JSON | app/channels/access_control.py:158 |
工具侧(drivers/、app/mcp/):
| 部件 | 干什么 | 文件 |
|---|---|---|
DriverManager | 拥有外部能力的存储、生命周期、分发;协议中立 | drivers/manager.py:47 |
DriverHandler | 模板方法基类:策略评估 + 凭据解析 + 审批,子类填协议细节 | drivers/handler.py:42 |
MCPDriverHandler | 具体协议实现:连 MCP 服务器,把其 tools 暴露成 capability | drivers/handlers/mcp.py:51 |
evaluate_policy | 策略引擎:对一次工具调用返回 allow / ask / deny | drivers/policy.py:77 |
AsyncCredentialStore | 每工作区的 YAML 凭据库,secret 全程加密落盘 | drivers/credentials/store.py:40 |
DriverCapabilityTool | 适配器:把一个 capability 包成 AgentScope 的 ToolBase | drivers/adapters/agentscope_tool.py:135 |
MCPConfigService | Console 管理 MCP 客户端:增删改、工具白名单、访问策略 | app/mcp/config_service.py:74 |
2.3 主线走一遍(高层,不进代码)
入站: 飞书推来一条群消息 → FeishuChannel 解析出 content_parts 和会话标识 → ChannelManager.enqueue 按命令算优先级、按会话算 session_id,丢进 UnifiedQueueManager → 对应队列的消费者取出、标准化成 AgentRequest、过 ACL 闸 → 交给内核 _process → 内核吐 Event 流 → MessageRenderer 渲染 → Channel.send 回发。
工具: 内核准备工具时,build_driver_agent_tools 向 DriverManager 要所有 active driver 的 capability,每个包成 ToolBase → 模型决定调用某工具 → DriverCapabilityTool.__call__ → DriverManager.invoke_capability 按 capability_id 路由到 MCPDriverHandler → 策略引擎判 allow/ask/deny(ask 走审批闸,见 第 05 章)→ 解析加密凭据 → 调远程 MCP tool → 结果转回内核。