QwenPaw 2.0 Driver:统一的外部能力接入层

Cosolar 2 阅读 大模型技术架构

声明:本文转载自 QwenPaw,资料仅供学习参考使用。

为什么要做 Driver

Agent 系统正在快速演进。今天的 Agent 不再局限于调用几个内置工具——它需要对接 MCP Server、远程 Agent (A2A)、ACP 服务,以及未来更多的外部协议。

这些能力在通信方式(stdio / HTTP / WebSocket)、认证机制(静态 Token / OAuth2 / AK/SK)和协议格式(JSON-RPC / REST / gRPC)上各不相同。如果 Agent 直接耦合这些差异,每新增一种外部能力就要修改 Agent 核心代码。这违反了 Open-Closed 原则,也让系统的安全治理变得碎片化。

我们需要回答一个根本问题:Agent 系统应该如何感知和管理异构的外部能力?

Driver 是什么

Driver 将对外部能力的访问抽象为统一的、协议无关的资源模型,使 Agent 系统可以统一地声明(Declarative)、调用(Invocable)和治理(Governable)。

三个关键词定义了 Driver 的价值:

  • 可声明 — 系统知晓哪些外部能力存在、如何到达
  • 可调用 — 通过标准接口发起调用,屏蔽底层协议差异
  • 可治理 — 控制谁在什么条件下可以调用,精确到单个工具

Driver 不拥有外部服务。外部服务独立存在、独立部署、拥有自己的生命周期。Driver 抽象的是连接与调用,而非服务本身。

MCP 是我们交付的第一个具体协议实现。A2A、ACP 等后续协议通过 register_handler_type() 即可扩展接入。

架构概览

Driver Layer 由五个核心组件构成:
image.png

┌────────────────────────────────────────────────────────────────┐
│                        Driver Layer                             │
│                                                                │
│  DriverManager ──builds──▶ DriverHandler                       │
│       │                         │                              │
│       │ reads/persists          │ holds ref                    │
│       ▼                         ▼                              │
│  ┌─────────────────────────────────────────────────────┐       │
│  │ DriverCard (YAML)                                    │       │
│  │ name · protocol · endpoint · credentials · policy    │       │
│  └─────────────────────────────────────────────────────┘       │
│                                                                │
│  Capability: capability_id + kind + action + schema            │
│  Access Policy: ABAC (Subject × Principal × Target → Effect)   │
└────────────────────────────────────────────────────────────────┘
                          ▲ invoke_capability()
                          │
┌─────────────────────────┴──────────────────────────────────────┐
│ Agent Runtime                                                   │
│   build_agent() → Toolkit: [DriverCapabilityTool(cap, invoker)] │
│   LLM → tool_call → invoker → Handler → 协议执行 → 结果返回    │
└─────────────────────────────────────────────────────────────────┘

核心设计决策

1. Card 与 Handler 分离

DriverCard 是纯静态声明(YAML 文件),Handler 是运行时实例。这个分离带来三个好处:

  • Card 可版本控制、人类可审计
  • Handler 可独立重建而不丢失配置
  • 同一 Card 格式可对接不同的 Handler 实现

2. 治理管线不可绕过

所有调用必经 _authorize_invocation() 治理管线——先裁决访问策略,再执行协议操作。这不是一个可选的中间件,而是内嵌于 Handler 调用路径的强制约束

invoke_capability()
  └─ _authorize_invocation()     ← 必经
       ├─ evaluate_policy(ctx)   → allow / deny / ask
       └─ _request_approval(ctx) → 委托给 ApprovalGate(ask 时)
  └─ protocol_execute(...)       ← policy 通过后才执行

3. Capability URI 统一标识

每个 capability 通过 URI 唯一标识,跨会话可持久引用:

driver://{protocol}/{driver_name}/{kind_plural}/{name}#{action}

例如:

  • driver://mcp/github-mcp/tools/create_issue#invoke
  • driver://a2a/translate-agent/agents/translate#invoke

Agent 不需要知道 capability 来自 MCP 还是 A2A——它只看到统一的 DriverCapability 描述和 invoke_capability() 入口。

4. 凭证四层解耦

凭证管理采用四层架构,实现声明与秘密的完全分离:

Layer 1: CredentialRef (Card 中的引用声明)
Layer 2: AsyncCredentialStore (加密存储)
Layer 3: CredentialProvider (解析 + 刷新)
Layer 4: ResolvedCredential (注入 Handler 的运行时凭证)

Card 中永远不会出现明文秘密——只有引用。Provider 层支持动态注册,DingTalk/飞书等非标准 OAuth 通过注入自定义 TokenExchanger 适配。

5. Open-Closed 扩展

新增协议只需三步:

  1. 实现 XxxDriverHandler — 覆写 _setup/_teardown + list_capabilities/invoke_capability
  2. 注册:manager.register_handler_type("xxx", XxxDriverHandler)
  3. 声明:新建 YAML(protocol: xxx

无需修改现有代码。protocol 为字符串(非枚举),保证了真正的 Open-Closed。

Access Policy:精细到每个工具的访问控制

Access Policy 采用 ABAC(基于属性的访问控制)模型,四维裁决:

三种裁决效果:

  • allow — 立即执行
  • deny — 阻止并返回错误
  • ask — 挂起等待人工审批(通过注入的 ApprovalGate 协议实现)

优先级裁决:多条规则匹配时,按 Target 精确度 > Principal 精确度 > Subject 精确度 > 严格程度排序,最具体的规则胜出。

这意味着你可以:

  • 默认 ask 所有调用,但 allow 来自 Console 的 admin 用户
  • 对特定危险工具 deny 所有人,即使默认策略是 allow
  • 工作日白天 allow,其余时间 ask

Quick Start: MCP Driver

以下是一个完整的 MCP Driver 配置示例,展示如何声明一个带凭证绑定和工具级访问策略的 MCP 服务:

# drivers/mcp/hello-mcp.yaml
name: hello-mcp
protocol: mcp
endpoint:
  transport: stdio
  command: python
  args: ["./mcp_servers/hello_server.py"]
  env:
    ECHO_SECRET:
      source: credential
      credential: static
      field: ECHO_SECRET
config:
  display_name: Hello MCP
  description: 本地 stdio MCP 演示服务,提供 print_content 和 get_secret_status 两个工具
enabled: true
policy:
  default_effect: ask
  rules:
    - subject: "*"
      effect: deny
      target: { kind: tool, name: get_secret_status }

这份配置做了三件事:

  1. 声明连接方式:通过 stdio 启动本地 Python MCP Server
  2. 绑定凭证:环境变量 ECHO_SECRET 从凭证存储中解析注入,Card 中无明文
  3. 定义访问策略:默认所有调用需要审批(ask),但 get_secret_status 工具被完全禁止(deny

Console 管理

在 Console 的 智能体 → MCP 页面中,点击任意 MCP 客户端卡片上的 工具&权限 即可可视化管理访问策略:

image.png

  • 设置客户端级默认效果(Ask / Allow / Deny)
  • 为特定来源渠道和用户添加覆盖规则
  • 为单个工具设置独立的访问策略
  • 保存后立即生效,无需重启

端到端调用流程

一次完整的 Driver 调用经历以下阶段:

用户请求 → list_capabilities() → build_agent(toolkit)
→ LLM 决策调用工具
→ DriverCapabilityTool.__call__()
→ DriverManager.invoke_capability()
  → parse capability_id → 路由到正确 Handler
  → Handler._authorize_invocation()  [策略裁决]
  → Handler.protocol_execute()       [协议执行]
→ DriverInvocationResult → Agent 继续推理

Driver 连接在 Workspace 级别保持持久化(进程内维持 MCP client session),而 Agent 和 Toolkit 则随每次请求重建。这确保了资源复用隔离安全之间的平衡。

展望

Driver 模块为 QwenPaw Agent 系统建立了统一的外部能力接入基座。当前已交付 MCP 协议支持,A2A 和 ACP 的 Handler 骨架已就绪。未来的演进方向包括:

  • 更多协议:完整的 A2A(Agent-to-Agent)和 ACP 协议实现
  • 可观测性:调用链路追踪、策略裁决审计日志

Driver 的设计哲学是:让 Agent 专注于推理,让 Driver 处理连接的复杂性。 无论外部世界的协议如何变化,Agent 看到的始终是统一的 Capability 模型。