跳到正文
FunCoding

搜索

搜索文档、文章、Skill 和 MCP

守护进程架构

概述

一个 qwen serve 进程默认托管一个 Express HTTP 服务器和一个主工作区。启用 multi_workspace_sessions 后,它还可以为实时会话闭环托管额外的工作区运行时;每个注册的工作区拥有自己的 @qwen-code/acp-bridge / qwen --acp 子进程对。多个客户端(CLI TUI、IDE 伴侣、IM 频道机器人、Web BFF、自定义脚本)通过 HTTP + SSE 连接,并且要么共享同一个 ACP 会话(sessionScope: 'single',默认),要么按对话线程拆分会话(sessionScope: 'thread')。

在 ACP 子进程中,MCP 服务器通过 McpTransportPool (F2) 在工作空间范围内共享:一个(服务器名称 + 配置指纹)元组映射到一个 MCP 传输,无论有多少会话发现它。桥接器的 MultiClientPermissionMediator (F3) 在四种策略之一下协调所有已连接客户端的权限投票。

本文档提供了系统级概览,其余文档在此基础上展开。每个关键流程均以 Mermaid 时序图展示;各组件实现细节请参见其他 18 篇文档。

进程拓扑

flowchart LR
    subgraph clients["客户端"]
        WUI["Web Shell<br/>(packages/web-shell/client/daemon)"]
        TUI["CLI TUI<br/>(packages/cli/src/ui/daemon)"]
        IDE["VS Code IDE<br/>(packages/vscode-ide-companion)"]
        CH["频道机器人<br/>(钉钉 / 微信 / Telegram / 飞书)"]
        SDK["任意 SDK 消费者<br/>(packages/sdk-typescript/src/daemon)"]
    end

    subgraph daemon["qwen serve 进程(主工作区加可选的会话运行时)"]
        EXP["Express 应用<br/>(packages/cli/src/serve/server.ts)"]
        BR["AcpBridge<br/>(packages/acp-bridge/src/bridge.ts)"]
        MED["MultiClientPermissionMediator<br/>(F3)"]
        EB["每个会话的 EventBus<br/>(eventBus.ts)"]
        FS["WorkspaceFileSystem<br/>(cli/src/serve/fs/)"]
    end

    subgraph child["ACP 子进程 (qwen --acp)"]
        AGT["QwenAgent 运行时"]
        POOL["McpTransportPool<br/>(F2, core/src/tools)"]
        BDG["WorkspaceMcpBudget"]
    end

    subgraph external["外部"]
        MCP1["MCP 服务器 A<br/>(stdio)"]
        MCP2["MCP 服务器 B<br/>(websocket)"]
    end

    WUI -- "HTTP+SSE" --> EXP
    TUI -- "HTTP+SSE" --> EXP
    IDE -- "HTTP+SSE (loopback)" --> EXP
    CH -- "HTTP+SSE" --> EXP
    SDK -- "HTTP+SSE" --> EXP

    EXP --> BR
    BR --> MED
    BR --> EB
    EXP --> FS

    BR -- "ACP NDJSON over stdio" --> AGT
    AGT --> POOL
    POOL --> BDG
    POOL -- "共享传输" --> MCP1
    POOL -- "共享传输" --> MCP2

守护进程与 ACP 子进程通过 AcpChannel 连接(默认使用真实的子进程 stdio 管道对;测试时使用 inMemoryChannel)。守护进程的所有行为都受此分离影响:HTTP 和 SSE 流量在守护进程中终结,代理决策和工具调用在子进程中发生,而桥接器则连接两者。

包结构图

flowchart TB
    subgraph serve["packages/cli/src/serve"]
        RQS["run-qwen-serve.ts<br/>(引导)"]
        SRV["server.ts (Express)"]
        CAP["capabilities.ts"]
        AUTH["auth.ts"]
        FSM["fs/ (沙箱)"]
        DSP["daemon-status-provider.ts"]
    end

    subgraph br["packages/acp-bridge"]
        BR2["bridge.ts"]
        BC2["bridgeClient.ts"]
        EB2["eventBus.ts"]
        MED2["permissionMediator.ts"]
        ST2["status.ts"]
        CH2["channel.ts / spawnChannel.ts"]
    end

    subgraph core["packages/core/src/tools"]
        POOL2["mcp-transport-pool.ts"]
        ENT["mcp-pool-entry.ts"]
        WBG["mcp-workspace-budget.ts"]
        SMV["session-mcp-view.ts"]
    end

    subgraph sdk["packages/sdk-typescript/src/daemon"]
        DC["DaemonClient.ts"]
        DSC["DaemonSessionClient.ts"]
        EVT["events.ts"]
        SSE["sse.ts"]
        AUTHF["DaemonAuthFlow.ts"]
        UI["ui/* (#4328 + #4353)<br/>normalizer / transcript / store / render"]
    end

    subgraph adapters["适配器"]
        WUIP["web-shell/client/daemon/session/<br/>DaemonSessionProvider.tsx"]
        TUIA["cli/src/ui/daemon/<br/>daemon-tui-adapter.ts"]
        CHB["channels/base/<br/>DaemonChannelBridge.ts"]
        DT["channels/dingtalk"]
        WX["channels/weixin"]
        TG["channels/telegram"]
        FS["channels/feishu"]
        IDEA["vscode-ide-companion/<br/>daemonIdeConnection.ts"]
    end

    RQS --> SRV
    RQS --> CAP
    RQS --> AUTH
    RQS --> FSM
    RQS --> BR2

    BR2 --> BC2
    BR2 --> EB2
    BR2 --> MED2
    BR2 --> CH2

    BR2 -.spawns.-> core
    POOL2 --> ENT
    POOL2 --> WBG
    POOL2 --> SMV

    WUIP --> DSC
    WUIP --> UI
    TUIA --> DSC
    CHB --> DSC
    DT --> CHB
    WX --> CHB
    TG --> CHB
    IDEA --> DSC

    DSC --> DC
    DC --> EVT
    DC --> SSE
    DC --> AUTHF

三个信任边界值得关注:HTTP 边缘(serve/auth.ts 中间件链)、桥接器到 ACP 子进程边界(NDJSON over stdio,无认证;子进程完全信任桥接器)、以及代理到 MCP 服务器边界(代理可以调用触及主机的工具)。

工作流 1:HTTP 请求生命周期

sequenceDiagram
    autonumber
    participant C as 客户端 (SDK)
    participant MW as 中间件<br/>(origin-strip→log→trace-id→host→same-origin→CORS→bearer→rate-limit→JSON→telemetry→mutationGate)
    participant R as 路由处理器
    participant BR as AcpBridge
    participant BC as BridgeClient
    participant CH as ACP 子进程

    C->>MW: POST /session/:id/prompt<br/>Authorization: Bearer …<br/>X-Qwen-Client-Id: …
    MW->>MW: loopback same-origin Origin strip
    MW->>MW: access-log hook(跳过 GET /health 和 POST */heartbeat)
    MW->>MW: inbound trace-id capture
    MW->>MW: hostAllowlist(DNS 重新绑定防护)
    MW->>MW: remote same-origin Origin strip + bearer check(非 loopback 且携带 token)
    MW->>MW: allowOriginCors(可变允许列表;未匹配的 Origin -> 403)
    MW->>MW: bearerAuth(恒定时间比较)
    MW->>MW: rateLimit(启用时)
    MW->>MW: express.json body parser
    MW->>MW: daemonTelemetryMiddleware
    MW->>MW: mutationGate(对变更型路由严格处理)
    MW->>R: 请求已验证
    R->>BR: bridge.sendPrompt(sessionId, body, clientId)
    BR->>BC: client.sendPrompt(sessionId, …)
    BC->>CH: ACP JSON-RPC over stdin
    CH-->>BC: ACP 响应/通知
    BC-->>BR: 结果
    BR-->>R: 结果
    R-->>C: 200 JSON

非流式路由(prompt、cancel、model switch、metadata、workspace CRUD)以单个 JSON 回复终止。流式输出通过 SSE 通道带外传送,并非此连接的分块 HTTP 响应体。参见工作流 2。

工作流 2:SSE 事件传递与重播

sequenceDiagram
    autonumber
    participant C as 客户端
    participant SR as GET /session/:id/events
    participant EB as EventBus<br/>(每个会话)
    participant BC as BridgeClient
    participant CH as ACP 子进程

    C->>SR: GET …/events<br/>Last-Event-ID: 42 (可选)
    SR->>EB: subscribe(lastSeenId=42, maxQueued=N)
    EB-->>SR: replay frames 43..currentTail<br/>(从环形缓冲区)
    SR-->>C: NDJSON: id=43, type=session_update, …
    CH-->>BC: ACP 通知 (例如 agent_message_chunk)
    BC->>EB: publish({type, data})
    EB-->>SR: enqueue id=N
    SR-->>C: id=N, type=…, data=…
    Note over EB,SR: 如果订阅者队列 >= maxQueued,<br/>EventBus 发出 client_evicted 终端帧<br/>并关闭订阅者。

环形缓冲区有界 (eventRingSize, 默认 8000)。重新连接的客户端如果 Last-Event-ID 早于环形缓冲区头部,将收到 state_resync_required,并必须从 loadSession 的有界重放快照窗口重建,或在已有本地历史时使用 resumeSession。慢速客户端在队列填充 75% 时触发 slow_client_warning,在达到上限时触发 client_evicted。

工作流 3:多客户端权限协调

sequenceDiagram
    autonumber
    participant CH as ACP 子进程 (代理)
    participant BC as BridgeClient.requestPermission
    participant MED as 协调器 (策略)
    participant EB as EventBus
    participant C1 as 客户端 A<br/>(发起者)
    participant C2 as 客户端 B

    CH->>BC: ACP requestPermission(requestId, options)
    BC->>MED: request({requestId, sessionId, originatorClientId, allowedOptionIds}, timeoutMs)
    MED->>EB: publish permission_request<br/>(广播给订阅者)
    EB-->>C1: SSE permission_request
    EB-->>C2: SSE permission_request

    alt first-responder
        C2->>MED: POST /permission/:requestId optionId=allow
        MED-->>BC: resolved
        BC-->>CH: ACP 响应
        MED->>EB: permission_resolved
        C1->>MED: POST /permission/:requestId (迟到投票)
        MED-->>C1: 409 permission_already_resolved
    else designated
        C2->>MED: vote (clientId != originatorClientId)
        MED-->>C2: 403 permission_forbidden
        C1->>MED: vote (匹配发起者)
        MED-->>BC: resolved
    else consensus (N-of-M)
        C1->>MED: vote
        MED->>EB: permission_partial_vote (1/N)
        C2->>MED: vote
        MED->>EB: permission_partial_vote (2/N)
        Note over MED: 当某个选项的票数达到法定人数时,resolve
    else local-only
        C2->>MED: vote (远程)
        MED-->>C2: 403 permission_forbidden (remote_not_allowed)
        Note over MED,CH: 阻塞直到本地投票者解决
    end

跨策略逃生舱口:任何客户端可以投票 CANCEL_VOTE_SENTINEL 来短路请求,使其变为 cancelled / agent_cancelled。桥接器阻止网络调用者通过普通 optionId 字段夹带哨兵(InvalidPermissionOptionError)。

工作流 4:MCP 传输池获取 / 释放 / 重启

sequenceDiagram
    autonumber
    participant S as ACP 子进程中的会话
    participant P as McpTransportPool
    participant SIF as spawnInFlight (去重)
    participant E as PoolEntry
    participant BDG as WorkspaceMcpBudget
    participant SRV as MCP 服务器

    S->>P: acquire(name, cfg, sessionId)
    P->>SIF: 检查 (name+fingerprint) 的 inflight
    alt cached inflight
        SIF-->>P: 已有 promise
    else cold start
        P->>BDG: tryReserve(name)
        BDG-->>P: ok / refused
        alt refused
            P-->>S: BudgetExhaustedError
        else ok
            P->>E: new PoolEntry(...)
            E->>SRV: connect transport
            SRV-->>E: ready
            E-->>P: connected
        end
    end
    P->>P: sessionToEntries.add(sessionId, id)
    P-->>S: PooledConnection

    Note over S,P: 会话使用 entry,然后…

    S->>P: release(id, sessionId)
    P->>E: detach session
    E->>E: arm drain timer (默认 30s)
    Note over E: refs==0 → drain timer fires → close transport<br/>(MAX_IDLE_MS 5min hard cap survives attach/detach churn)

    Note over S,P: 操作员重启流程…
    S->>P: restartByName(name, opts?)
    P->>E: drain + close
    P->>E: spawn replacement
    E->>SRV: reconnect
    P->>EB: publish mcp_server_restarted<br/>with stable entryIndex
    P-->>S: single result 或 {entries: RestartResult[]}

releaseSession(sessionId) 使用反向索引 sessionToEntries 以 O(refs) 的时间复杂度释放该会话持有的所有 entry。守护进程关闭时,drainAll() 设置 draining 标志(拒绝新的 acquire),并在可配置超时内等待每个 entry 关闭。

工作流 5:生命周期——启动与优雅关闭

sequenceDiagram
    autonumber
    participant Op as 操作者 (信号)
    participant RQS as runQwenServe
    participant APP as Express 应用
    participant BR as AcpBridge
    participant CH as ACP 子进程

    Op->>RQS: qwen serve --workspace … --token …
    RQS->>RQS: 验证标志 + 规范化工作空间
    RQS->>RQS: 分配 PermissionAuditRing
    RQS->>BR: createHttpAcpBridge(options)
    RQS->>APP: createServeApp(bridge, …)
    RQS->>APP: listen(host, port)
    RQS->>RQS: 安装 SIGINT / SIGTERM 处理器

    Op->>RQS: SIGTERM
    RQS->>BR: dispose device-flow registry
    RQS->>BR: bridge.shutdown()
    BR->>CH: 发送优雅关闭 (10s 截止时间)
    CH-->>BR: exit
    RQS->>APP: server.close() (5s 强制关闭计时器)
    APP->>APP: closeAllConnections() (+2s 二级)
    Note over Op,RQS: 关闭期间第二个 SIGTERM →<br/>bridge.killAllSync() + process.exit(1) (孤儿进程防护)

两阶段关闭之所以重要,是因为进行中的 HTTP 请求、进行中的 SSE 订阅者和 ACP 子进程中进行中的工具调用都需要有界的关闭窗口。如果任何操作在这些截止时间后仍被阻塞,强制关闭路径将接管,以确保挂起的子进程不会阻止守护进程退出。

关键文件

关注点文件
引导packages/cli/src/serve/run-qwen-serve.ts
Express 应用packages/cli/src/serve/server.ts
能力注册表packages/cli/src/serve/capabilities.ts
认证中间件packages/cli/src/serve/auth.ts
桥接器packages/acp-bridge/src/bridge.ts
BridgeClientpackages/acp-bridge/src/bridgeClient.ts
权限协调器packages/acp-bridge/src/permissionMediator.ts
EventBuspackages/acp-bridge/src/eventBus.ts
MCP 传输池packages/core/src/tools/mcp-transport-pool.ts
工作空间 MCP 预算packages/core/src/tools/mcp-workspace-budget.ts
工作空间文件系统packages/cli/src/serve/fs/
SDK DaemonClientpackages/sdk-typescript/src/daemon/DaemonClient.ts
SDK SessionClientpackages/sdk-typescript/src/daemon/DaemonSessionClient.ts
事件模式packages/sdk-typescript/src/daemon/events.ts

参考资料