资讯动态

Qwen Code 冷首次会话支持:从 daemon 到 ACP 子进程的端到端启动性能剖析设计

发布时间:2026/9/11 16:19:58 来源:尧图企业网站定制
Qwen Code 冷首次会话支持从 daemon 到 ACP 子进程的端到端启动性能剖析设计【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code导读本文基于 Qwen Code 仓库中的设计文档 docs/design/2026-07-13-cold-first-session-support.md完整讲解冷首次会话cold first session场景下如何在不牺牲现有/health快速响应行为的前提下为一次冷请求建立跨 daemon、共享 ACP 通道与 ACP 子进程的端到端可观测链路。读完本文你将掌握deferred-runtime 请求时间回填机制、通道等待路径的三分类语义、通道 UUID 关联独立预热 trace 的方法、W3C trace-context 在 ACPsession/new上的注入以及 ACP 子进程session_start六阶段计时的实现与验证方式。背景与动机粗粒度路由耗时无法解释的 700–800ms在qwen serve的部署形态中daemon 进程监听 HTTP 端口通过 ACP bridge 与按需拉起的qwen --acp子进程通信。首次请求冷首次会话存在一个典型痛点/health已返回 200listener 就绪但紧接着的POST /session依然明显偏慢——浏览器端观测到的时间无法区分这段延迟究竟花在代理、daemon、通道还是子进程上。设计文档记录了下游0.19.3-preview.2采样的关键证据从 health 成功到 Session 成功的中位数P50为2,534ms其中POST /session自身的 P50 为1,713ms。health 到请求之间的延迟与 POST 时长呈负相关这与首次请求在等待自动预热automatic preheat的剩余部分这一假设一致但浏览器端时间戳无法拆分出 proxy、daemon、channel、child 各自的贡献。文档还记录了一次使用全局安装的qwen 0.19.10的本地 dry-run 对照实验场景观测结果进程启动 → listener 就绪203mshealth 后紧跟冷POST /session1,033ms浏览器/ 962msdaemon单独运行中已预热的POST /session222ms浏览器/ 221msdaemon作者明确强调这些是单次示例运行不是验收基准acceptance benchmark。它们只能说明当前粗粒度的路由时长掩盖了大约 700–800ms 的延迟这部分可能是通道等待channel wait、ACP 子进程启动bootstrap或二者皆有——这正是本文设计要回答的问题。当前架构与已具备的可观测性设计文档用一张序列图勾勒出现状GET /health只证明 listener 就绪daemon 在健康检查之后异步触发preheat()拉起子进程随后到达的POST /session经由spawnOrAttach()走三条分支之一——复用已就绪通道、等待进行中的 spawn、或现场拉起新通道最后子进程完成session/new的 settings Config auth chat 流程。在本次设计之前仓库已具备四类可观测能力HTTP 请求 spanPOST /session在运行时应用收到请求之后产生一个 HTTP 请求 span但 bootstrap 层 deferred-runtime 的等待不在此 span 内bridge spanchannel.spawn、channel.initialize、session.new已有独立的 telemetry spanW3C trace-context 注入与提取通过 ACP 保留的_meta键完成目前用于 prompt 分发qwen.channel.prompt等见 packages/acp-bridge/src/bridgeTypes.ts 中的CHANNEL_PROMPT_META_KEYopt-in JSONL 剖析器用于记录GeminiClient.startChat()的详细阶段。缺失的部分是bootstrap 层在请求 span 之前的 deferred-runtime 等待、当前请求的通道等待时长、与独立启动的预热 trace 的关联、session/new上的 trace 传播以及子进程内startChat之前的计时。本次实现切片implementation slice正是补齐这五块。设计总览观察性优先的实现切片本切片对应 issue #4748 的下一步实现作者明确把优先级定为可观测性observability而不是又一个启动缓存或新会话协议。它复用现有的 daemon OpenTelemetry request/bridge spans 与 ACP_meta扩展点新增六件事bootstrap 请求计时让 deferred-runtime 等待被计入随后的 HTTP span而不是被误判为代理/网络耗时per-request 通道等待 span说明本次 Session 是复用就绪通道、加入进行中的 spawn、还是现场 spawn通道不透明 ID用 UUID 关联自动预热 trace 与后续 Session trace而不虚构父子关系session/new上的 trace-context 注入子进程session/new单 span 六阶段计时settings、Config 初始化、认证、文件系统准备、Session 注册、响应构造ACP Session ID 写入 JSONL 记录加入既有的 opt-inQWEN_CODE_PROFILE_SESSION_START记录使startChat详细阶段能与 trace 关联。同时明确本切片不做不新增响应头、不新增公开 JSON 字段、不新增能力标志、不引入第二套 profiler 格式。ACP readiness 是 P0 明细出来之后的独立 P1 client/API 变更。父 daemon 与 bridge 侧设计Deferred-runtime 请求时间回填当非 bootstrap 请求在 deferred runtime 挂载之前到达时委派的 bootstrap 应用记录三件事请求的 wall-clock 到达时间、剩余的 runtime 等待时长、该请求是启动了 runtime 加载还是加入了 health/fallback 调度已开始的工作。运行时 telemetry 中间件在挂载后拿到同一个请求对象将 HTTP span 的起始时间回填backdate到到达时刻路由时长指标也使用同一边界。这样即使走冷 deferred-runtime 路径浏览器时长 − daemon 请求时长也能成为一个有意义的代理/网络残差。在源码中deferred runtime 由deferRuntimeUntilFirstHealth控制packages/cli/src/serve/run-qwen-serve.ts 第 4411–4412 行健康检查后通过FAST_PATH_RUNTIME_START_AFTER_HEALTH_MS调度启动并配有FAST_PATH_RUNTIME_START_FALLBACK_MS兜底定时器确保即使没有 health 请求runtime 也会被拉起同文件第 9066、9082 行附近的日志deferred runtime: fallback timer fired, starting/deferred runtime: health timer fired, starting。bootstrap 层记录 arrival time 与 wait 时长后由 telemetry 中间件回填 span 起始时间二者配合完成冷路径的时间归位。通道等待的三分类语义在doSpawn()等待ensureChannel()之前bridge 先对同步通道状态做分类reused已存在一个非 dying 的通道直接复用joinedinFlightChannelSpawn已存在加入等待spawned_on_request既无存活通道也无进行中的 spawn现场拉起。随后把 await 包进一个channel.waitbridge span。设计上要求生产 telemetry 实现同步调用回调因此读取分类 调用ensureChannel()之间不会让出 JavaScript 事件循环避免分类与真实状态之间出现竞态。这一分类与源码实现完全对应在 packages/acp-bridge/src/bridge.ts 的ensureChannel()第 4580 行起中先检查shuttingDown然后if (channelInfo !channelInfo.isDying) return channelInfo;复用分支再if (inFlightChannelSpawn) return await inFlightChannelSpawn;加入分支否则新建 spawn promise 并把inFlightChannelSpawn置为该 promise、完成后清空第 5235、5239 行。注释明确说明这是为了让并发spawnOrAttach调用 coalesce 到同一个 spawn 上保证永不重复拉起两个子进程。通道 UUID关联独立预热 trace 的桥梁每个新建的ChannelInfo在调用channelFactory()之前获得一个随机 UUID源码中即const acpChannelId randomUUID();第 4594 行。同一 ID 只被附加到三类 span 上channel.spawnchannel.initializesession.new通道已知之后设计强调这个 ID 是诊断性 trace 数据不是 metrics label也不是公开标识符。自动预热与首次 Session 可以属于不同的 trace通道 ID 只负责把二者关联起来而不声称后来的 HTTP 请求导致了更早的预热工作——避免虚构父子因果关系。preheat()获得独立的channel.preheatbridge span。加入它的 Session 拥有一个只度量剩余等待的channel.waitspan。此时channel.initialize与channel.wait在时间上重叠二者不能相加。session/new的 trace-context 注入在既有的session.newspan 内bridge 把当前活跃的 trace context 注入NewSessionRequest._meta。既有的注入 helper 会先剥离客户端提供的保留键再写入 daemon 拥有的值。子进程响应之后span 事件记录 ACP Session ID用于与 JSONL profiler 关联。在 packages/cli/src/acp-integration/acpAgent.ts 中可以看到_meta协议扩展点的一贯用法REQUESTED_SESSION_ID_META_KEY、SESSION_INITIALIZATION_DEADLINE_META_KEY、CHANNEL_STARTUP_PROFILE_META_KEY等都通过params._meta?.[KEY]读取第 5181、5200、4999 行。trace-context 注入正是复用这一机制——_meta是可选的普通 ACP 客户端非 daemon可以完全忽略它。ACP 子进程侧设计session_start六阶段计时QwenAgent.newSession()从请求中提取 daemon context源码中为extractDaemonTraceContext(params)第 5218 行在父 bridge 的session.newspan 之下启动一个子 spanqwen-code.daemon.session_startwithDaemonSpan(...)第 5219–5220 行。如果 context 缺失或无效则走常规 OTel 根 span 行为Session 创建照常继续。子进程使用performance.now()记录固定、互不重叠的六阶段时长阶段边界源码对应settings_loadloadSettingsCached(cwd)acpAgent.ts 第 5230 行profiler.timeSync(settings_load, ...)config_setupnewSessionConfig()包含loadCliConfig()、config.initialize()以及正常的首次startChat()第 5236 行profiler.time(config_setup, ...)authensureAuthenticated()第 5258 行file_system_setupsetupFileSystem()第 5262 行session_registercreateAndStoreSession()正常构造并注册 ACPSession防御性的 Gemini 初始化只有在 Config 未初始化时才在此计时第 5266 行response_buildmodels、modes、config options 与响应对象构造第 5287 行profiler.timeSync(response_build, ...)阶段失败时记录failed_stage并保留原始错误源码中有qwen-code.daemon.session_start.failed_stage属性见 packages/cli/src/acp-integration/acpAgent.test.ts 第 4355–4356 行对config_setup失败阶段的断言。所有阶段计时基于performance.now()保证同一 span 内各阶段之和与 wall-clock 可比。实现 E2E 观测到的典型数据config_setup约 200ms其中约 140ms 被既有嵌套startChatprofiler 记录。这证实了正常的startChat()发生在config.initialize()期间而不是之后的 Session 注册阶段JSONL 中的 Session ID 使这一嵌套成本无需靠文件时间戳猜测即可关联。文档同时指出如果下游代表性 trace 显示剩余未归因的 Config 成本显著未来可考虑把 Config 构造与config.initialize()拆分——但本切片不做因为那需要把一个 profiler 穿透 new/load/resume/transcript 共用的方法。JSONL profiler 的 Session ID 关联既有剖析器由环境变量QWEN_CODE_PROFILE_SESSION_START开启实现在 packages/core/src/core/session-start-profiler.ts第 15 行导出SESSION_START_PROFILE_ENV。每条记录是追加写入的 JSONL落在Storage.getRuntimeBaseDir()/session-start-perf/session-start-date.jsonl第 108、118 行。SessionStartProfileRecord接口定义了timestamp、source、ok、totalMs、stages、failedStage等字段以及可选的sessionId?: string第 23 行。接口注释特别说明stages之和可以与totalMs不同因为部分阶段重叠、阶段之间有未计量的代码。sessionId是增量且可选的字段子进程通过profiler.setSessionId(session.getId())acpAgent.ts 第 5286 行写入真实 Session ID使 trace 中的 span 能与 JSONL 中startChat的详细阶段精确 join对既有 JSONL 消费者而言字段缺失时记录结构与文件布局完全不变session-start-profiler.test.ts 第 425 行断言了无sessionId场景的向后兼容。同时该实现带有符号链接防护非 Windows 平台使用O_NOFOLLOW打开文件写入前用lstat拒绝目录替换与符号链接植入session-start-profiler.ts 第 65–105 行。属性契约Attribute contract为避免指标标签爆炸本切片只发射固定属性名 有界取值qwen-code.daemon.channel.pathreused | joined | spawned_on_requestqwen-code.daemon.runtime.pathstarted_on_request | joined请求穿越 deferred-runtime 门时qwen-code.daemon.runtime.wait_ms 有限非负的剩余 runtime 等待时长HTTP 请求时长直方图runtime_pathstarted_on_request | joined穿越 deferred-runtime 门的请求否则noneqwen-code.daemon.acp_channel.id daemon 生成的 UUIDqwen-code.daemon.session_start.stage_ms 有限非负的阶段时长qwen-code.daemon.session_start.failed_stage 单一固定阶段名session.id ACP 生成的 Session ID隐私边界不加入任何 workspace 路径、prompt、设置值、凭据、模型响应或文件内容。这与 docs/design/2026-08-13-privacy-safe-tool-result-boundary-diagnostics.md 等文档一贯的诊断数据最小化思路一致。失败、并发与兼容性矩阵设计文档对边界条件逐一给出了明确结论OTel 关闭现有行为不变bridge 走 no-op telemetry seam子进程 profiler 在环境变量未开启时不产生文件输出Deferred runtime 失败bootstrap 应用仍返回既有的启动错误计时元数据是进程内的绝不暴露在响应中trace 元数据缺失/无效子进程创建无父 span 或干脆不创建 spanSession 创建照常完成telemetry 属性写入失败阶段属性 best-effort 记录不能改变 Session 结果预热失败channel.wait如实反映请求的重试路径既有子进程清理与懒重试语义不变并发首次 Session每个请求拥有各自的channel.wait与子进程 Session span同时可引用同一通道 ID并发请求经inFlightChannelSpawn合并到同一 spawn旧版或非 daemon ACP 客户端_meta可选子进程继续接受普通NewSessionRequest既有 JSONL 消费者sessionId增量可选既有字段与文件布局不变通道拆除诊断 UUID 只活在ChannelInfo上随通道消失不影响复用、空闲超时或 kill 逻辑。被否决的替代方案设计文档记录了四个明确 rejected 的方案理解它们有助于把握设计边界自定义 profile ID ACP 响应信封在NewSessionResponse._meta里返回第二套计时 schema 会与 OTel 重复需要额外的校验/版本化形成两个事实来源。W3C context 已携带因果性通道 UUID 处理了唯一一处故意分离的预热 trace。Server-Timing/X-Qwen-Profile-Id响应头只对浏览器侧诊断有用但需要代理头透传与 CORS 暴露决策超出了本仓库范围daemon 请求 span 与既有路由时长已提供服务端时间。若下游 tracing 始终不可用可作为后续跟进。让/health等待 ACP这把延迟移入 readiness会引发 health-probe 回归。/health保持 listener/liveness 语义ACP readiness 是未来独立的、能力门控capability-gated的契约。共享 Config 或预创建 Session两者都在剖析出主导阶段之前改变了隔离与生命周期语义明确排除在范围外。验证与测试设计聚焦单元测试必须证明session/new收到 daemon 拥有的 trace 元数据穿越 deferred-runtime 门的 Session 请求其 HTTP span 从 bootstrap 到达时刻起算并记录是启动还是加入 runtime 加载channel.wait正确报告 spawned / joined / reused 三条路径单个通道 UUID 串联 spawn、initialize 与 Session spans子进程提取父 context 并记录全部固定阶段失败阶段被记录且原始错误被保留session-start JSONL 在提供 Session ID 时包含它、缺失时保持向后兼容telemetry 关闭或元数据畸形时 Session 行为不变。E2E dry-run 在同一 workspace 与同一认证下对比两个用例① health 后立即POST /session② health 后显式 preheat再POST /session。两者都验证 Session 成功并检查 trace 树——冷用例必须包含请求的channel.wait路径与子进程阶段属性预热用例必须报告reused。性能结论要求在代表性下游环境中至少 30 次串行冷启动不能从本地单次运行推断。源码侧的既有测试与断言已能印证设计acpAgent.test.ts第 4063–4080 行断言qwen-code.daemon.session_startspan 携带settings_load_ms、config_setup_ms、auth_ms、file_system_setup_ms、session_register_ms、response_build_ms全部六类属性第 4355–4356、4402–4403 行断言failed_stage在config_setup/file_system_setup失败时被正确记录。实现边界与评审门禁本切片的生产代码改动被严格限定在四处deferred-runtime 请求交接与 telemetry 中间件run-qwen-serve即 packages/cli/src/serve/run-qwen-serve.tspackages/acp-bridge 中既有的 telemetry seamchannel.wait、channel.preheat、通道 UUID、session.new注入ACP 子进程的newSessionpackages/cli/src/acp-integration/acpAgent.ts 第 5178 行起既有 core session-start profilerpackages/core/src/core/session-start-profiler.ts的sessionId附加字段。不涉及任何 Session/config/auth 行为变更。跨包下游消费者审查清单包括run-qwen-serve.ts中的 daemon bridge 构造与 test/embed 桥接 telemetry 实现、deferred runtime 路由准入与请求 telemetry/metrics 消费者、所有AcpSessionBridge.spawnOrAttach()调用方它们收到完全相同的BridgeSession形态、可省略_meta的非 daemon ACP 客户端、以及视sessionId为可选的 session-start profiler 测试与 JSONL 读取方。由于该改动横跨 core/bridge/CLI 边界即使生产逻辑变更刻意保持很小也需要 maintainer review。小结冷首次会话支持的本质是把一段谁也无法解释的延迟拆解成 daemon 门控等待、通道复用/等待/拉起、子进程六阶段初始化三组可观测信号并通过通道 UUID 与 Session ID 两条关联键把自动预热 trace、请求 trace 与 JSONL 细粒度剖析无缝拼接。它严格坚守先测量、后优化的工程纪律在剖析数据指认主导阶段之前不做 Config 共享、Session 预创建等影响语义的激进优化也不让/health的 liveness 语义为 ACP 就绪让路——这份克制的边界划分正是它在生产可观测性工程中值得参考的地方。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价