CH-02 运行环境:Chromium 壳与原生 Helper
图 2-1:豆包工作整体架构——非 Electron 的混合架构
图 2-1:豆包工作整体架构——非 Electron 的混合架构
2.1 一个 1MB 的主二进制
在 macOS 上安装豆包工作后,应用本体位于 /Applications/DoubaoWork.app。按照 Mac 应用的标准结构,它的可执行文件在 Contents/MacOS/DoubaoWork。如果你查看这个文件的大小,会发现一个反直觉的事实:
DoubaoWork.app/Contents/MacOS/DoubaoWork — 1.0 MB
1MB。对于一个号称能操作电脑、调用工具、运行多 Agent 的桌面应用来说,这个数字小得不正常。作为对比,一个典型的 Electron 应用主进程通常在 100MB 以上(因为它打包了 Node.js 运行时和 V8 引擎),一个原生 macOS 应用的主二进制也通常在 10-50MB。
1MB 意味着豆包工作的主二进制不是应用逻辑的载体,而是一个启动器(launcher)。它的职责是:初始化 Chromium 运行时、加载渲染进程、建立主进程与 Helper 进程之间的通信通道,然后把控制权交给 Web 技术栈。「源码」
这个判断有多重证据支撑:
第一,应用包内存在一个独立的浏览器应用 Contents/Helpers/DoubaoWork Browser.app,体积 977MB。这是一个完整的 Chromium 浏览器,包含 Blink 渲染引擎、V8 JavaScript 引擎、网络栈和所有 Chromium 子模块。豆包工作的主二进制通过启动这个浏览器实例来承载 UI 和应用逻辑。「源码」
第二,用户数据目录是标准的 Chromium profile 结构。~/Library/Application Support/DoubaoWork/Default/ 下有 Cookies、History、Favicons、Local Storage、Extension State、Preferences 等 Chromium 标准目录,还有一个 RunningChromeVersion 文件记录着 147.0.7727.149。「源码」
第三,应用包的 Info.plist 中声明了多个 URL scheme(dola、doubao、doubaowork),这些 scheme 由 Chromium 的 URL 请求机制处理,不是原生代码路由。「源码」
所以豆包工作的架构本质上是:一个极薄的原生启动器 + 一个完整的 Chromium 浏览器 + 一组原生 Helper 进程。UI 渲染、应用逻辑、Agent 调度、工具调用的编排都运行在 Chromium 渲染进程中(JavaScript/TypeScript),只有浏览器能力之外的操作——执行本地命令、运行 MCP 服务器、实施沙箱——才通过原生 Helper 完成。
这种架构选择有利有弊。好处是开发效率高、跨平台一致性好、可以利用 Chromium 的扩展系统和 Web 技术栈;代价是内存占用大(977MB 的浏览器内核)、原生能力需要额外的桥接层、应用逻辑暴露在渲染进程中(可以通过 Chrome DevTools Protocol 观测)。
2.2 进程架构
豆包工作运行时的进程模型可以分为三层:
DoubaoWork(主进程,1MB 启动器)
├── DoubaoWork Browser.app(Chromium 浏览器内核,977MB)
│ ├── 渲染进程(UI + Agent 逻辑 + 工具编排)
│ ├── GPU 进程
│ ├── 网络进程
│ └── 扩展进程(Chrome Extension)
├── command_helper(命令执行桥,2.2MB)
├── mcp-helper(MCP 助手,1.2MB)
│ └── libmcp_helper.dylib(MCP 核心库,11MB,Rust)
├── sandbox-launcher(沙箱启动器)
│ ├── libsandbox.dylib(Seatbelt 沙箱桥接)
│ └── libauditSboxSDK.dylib(审计 SDK)
├── relaunch_helper(重启助手,1.0MB)
├── saman_updater(更新框架,1.1MB)
└── finder-ext.appex(Finder 扩展)
2.2.1 主进程与浏览器内核
主进程(DoubaoWork)负责应用生命周期管理和窗口创建。它启动 DoubaoWork Browser.app,后者是一个完整的 Chromium 实例。Chromium 版本为 147.0.7727.149,这是一个相当新的版本(Chromium 147 于 2025 年底发布),说明豆包工作的浏览器内核保持着积极的更新节奏。「源码」
浏览器内核承载了所有 Web 技术栈的运行:UI 界面用 Web 技术渲染,Agent 的规划和调度逻辑用 JavaScript/TypeScript 实现,工具调用通过 Chromium 的 Extension API 和原生 Helper 桥接完成。
值得注意的是,豆包工作没有使用 Electron。Electron 是最常见的 Chromium 桌面应用框架,但它打包了 Node.js 运行时,允许渲染进程直接调用 Node.js API。豆包工作选择了更接近 Chromium 原生的方式——它有自己的 Helper 进程体系,而不是依赖 Node.js。这使得它的架构更接近 CEF(Chromium Embedded Framework)或自定义 Chromium 发行版,但 Helper 进程的分工又比 CEF 更精细。
2.2.2 command_helper:命令执行桥
command_helper 是豆包工作执行本地命令的桥梁。当 Agent 需要在终端中运行命令时(比如执行 ls、git status、python3 script.py),请求从渲染进程发出,通过 Chromium IPC 到达主进程,主进程启动 command_helper 子进程来执行。
command_helper 的二进制中嵌入了 macOS Seatbelt 沙箱配置(scheme 语法),说明它同时承担了沙箱策略执行的职责。在沙箱模式下,它限制命令可以访问的文件系统路径;在完全访问模式下,它对用户授权目录开放读写权限。沙箱机制的细节在 CH-06 展开。「源码」
2.2.3 mcp-helper:原生 MCP 运行时
mcp-helper 和 libmcp_helper.dylib 是豆包工作最有技术含量的原生组件。它们用 Rust 编写,基于 rmcp 3.0.0(Rust 生态的官方 MCP SDK),负责管理所有 MCP(Model Context Protocol)服务器的生命周期。
当 Agent 需要调用外部工具(比如飞书文档读取、数据库查询、自定义 MCP 服务器)时,mcp-helper 负责启动 MCP 服务器进程、建立传输连接、处理 OAuth 认证、转发工具调用、序列化结果返回渲染进程。它支持 STDIO、HTTP+SSE 和 Streamable HTTP 三种传输协议,实现了完整的 OAuth 2.0 流程。MCP 机制的细节在 CH-05 展开。「源码」
2.2.4 sandbox-launcher:沙箱启动器
sandbox-launcher 配合 libsandbox.dylib 和 libauditSboxSDK.dylib 实现了基于 macOS Seatbelt 的应用沙箱。当 Agent 需要启动一个外部应用(比如打开浏览器访问网页、运行一个脚本)时,sandbox-launcher 负责在受限环境中启动它。
它的启动流程包括:验证父进程身份和代码签名、通过环境变量 AHA_SANDBOX_CONFIG 接收沙箱配置、dlopen libsandbox.dylib、应用 Seatbelt 策略、通过 LaunchServices 启动应用。审计 SDK 则监控沙箱内的进程行为、网络访问和文件操作,支持白/黑名单和加密日志。「源码」
2.2.5 relaunch_helper 与 saman_updater
relaunch_helper 负责应用重启(比如更新后重启、崩溃后恢复)。saman_updater 是字节内部的 "saman" 桌面更新框架,负责检查更新、下载增量包、安装更新。更新渠道在 manifest.json 中声明为 release(channel 1001),whatsNewVersion 为 1.57.0。「源码」
"saman" 是字节桌面端基础设施的代号,在豆包工作的用户数据中频繁出现:Preferences 中有大量 saman.* 配置键,Local State 中有 saman 相关的全局设置。它类似于一个桌面端运行时框架,提供更新、同步、窗口管理等基础能力。
2.2.6 finder-ext.appex
这是一个 Finder 扩展(App Extension),允许用户从 Finder 的右键菜单中直接与豆包工作交互(比如"用豆包工作处理这个文件")。它是豆包工作融入 macOS 桌面体验的触点之一。「源码」
图 2-2:App 包结构与进程关系
2.3 manifest.json:信任域与产品线整合
应用包的 Contents/Resources/manifest.json 是一个关键配置文件,它不仅声明了版本信息,还定义了豆包工作的安全边界——哪些域名可以加载主框架、哪些脚本可以执行、哪些扩展拥有特权。
版本信息如下:
{
"productName": "DoubaoWork",
"version": "2.25.18",
"commitId": "345d9dd425dd3a64c0258bb12c7f3049a022af2b",
"updateChannel": "release",
"channel": "1001",
"whatsNewVersion": "1.57.0",
"onboardingVersion": "1.6.0"
}
commitId 是构建时的 Git commit 哈希,可以用于精确追踪版本。channel 1001 是内部渠道编号。「源码」
更值得关注的是 signature.trustedOrigins 配置(Base64 编码,解码后)定义了四类信任域:
主框架域(INIT_MAIN_ON_ROOT_DOMAIN):cici.com、dola.com、ciciai.com、doubao.com,协议为 https 端口 443。这意味着豆包工作的主界面可以从这四个域名的根域加载。「源码」
精确框架域(FRAME_URL_EXACT):www.cici.com、www.ciciai.com、www.dola.com、accounts.dola.com、www.doubao.com、beta.doubao.com、inhouse.doubao.com、accounts.doubao.com。这些是具体的子域,包括测试版(beta)和内部版(inhouse)。「源码」
脚本域(SCRIPT_URL_DOMAIN_IS):除了四个主域,还包括 CDN 域 cicicdn.com、dolacdn.com、ciciaicdn.com、doubaocdn.com、bytedance.net、byteintl.net。这些域名的脚本可以在应用内执行。「源码」
特权扩展(FRAME_URL_IS_PRIVILEGED):6 个 chrome-extension://<hash> 标识符。这些是预装的 Chrome Extension,拥有普通扩展没有的特权 API 访问权限。它们是 Harness 能力的重要注入点——Agent 的部分工具能力可能通过这些特权扩展暴露给渲染进程。「源码」
自定义 Scheme(FRAME_URL_ALLOW_DANAGER_API):dola-background、doubao-background、doubaowork-background 三个自定义协议,拥有 "Danger API" 权限。注意这里的 "DANAGER" 可能是 "DANGER" 的拼写变体,但在配置中确实如此。这些 scheme 是后台 Agent 执行的通道——它们允许后台页面(不显示 UI 的页面)调用敏感 API,是 Agent 在后台持续运行的基础设施。「源码」
三产品线整合
信任域配置揭示了一个重要事实:豆包工作不是一个独立产品,而是字节三条 AI 产品线的统一桌面容器。
- cici(扣子/Coze):字节的 AI Bot 开发平台,cici.com 和 ciciai.com
- dola(哆啦):字节的内部/早期 AI 助手品牌,dola.com
- doubao(豆包):面向消费者的 AI 助手,doubao.com
豆包工作的应用包名为 com.work.pc.doubao,内置浏览器为 com.work.pc.doubao.browser,但信任域覆盖了所有三个品牌。这意味着同一个桌面客户端可以加载不同产品线的 Web 界面,Agent 能力是跨产品线共享的。「源码」
2.4 用户数据目录
豆包工作的用户数据存储在 ~/Library/Application Support/DoubaoWork/,遵循 Chromium 的 profile 结构:
DoubaoWork/
├── Local State # 全局状态(设备信息、用户信息、saman 配置)
├── Last Version # "2.25.18"
├── RunningChromeVersion # 147.0.7727.149:1
├── Default/ # 默认 Chromium profile
│ ├── .doubaowork/ # 豆包工作专属数据
│ │ └── agent_mode/
│ │ └── workspace/
│ │ ├── .skills/ # 103 个预装 Skill
│ │ └── .user_skills/ # 用户自定义 Skill(空)
│ ├── DoubaoStorage/Aida/ # Aida 存储
│ │ ├── doubao-office/
│ │ └── entry-main/
│ ├── Preferences # Chromium + saman 配置(116KB)
│ ├── Secure Preferences # 安全配置(12KB)
│ ├── Cookies / History / Favicons
│ ├── Extension State/ # Chrome Extension LevelDB
│ ├── Local Storage/ # Web localStorage
│ └── ...
├── GrShaderCache / GPUCache
├── Crashpad/ # 崩溃报告
└── Monitor/
2.4.1 agent_mode/workspace
.doubaowork/agent_mode/workspace/ 是 Agent 模式的工作区,也是用户唯一可以直接编辑的 Harness 扩展点。.skills/ 目录存放 103 个预装 Skill(由 App 管理,权限为 drwx------),.user_skills/ 目录用于用户自定义 Skill(默认为空)。「源码」
这个目录结构与 WorkBuddy 形成鲜明对比。WorkBuddy 在 ~/.workbuddy/ 下放了 SOUL.md、IDENTITY.md、USER.md、MEMORY.md、prompt.tpl、plugins/、skills/、mcp.json 等一系列明文配置文件,用户可以完全控制自己的 Harness。豆包工作把所有这些都收走了,只留下 Skill 目录作为扩展接口。这种设计哲学的差异在 CH-10 详细对照。
2.4.2 DoubaoStorage/Aida
DoubaoStorage/Aida/ 下有两个子目录:doubao-office/ 和 entry-main/。"Aida" 是字节内部的另一个代号(可能与 Opera 的 Aida AI 无关,是字节内部项目名),从目录名推测与文档处理(office)和主入口(entry-main)相关。这个目录的具体用途需要进一步分析其内容,但它是豆包工作在 Chromium 标准存储之外的专属数据区。「源码」
2.4.3 Preferences 中的 Agent 配置
Chromium 的 Preferences 文件(JSON,116KB)包含一些 Agent 相关的配置:
| 配置键 | 值 | 含义 |
|---|---|---|
extensions.last_chrome_version |
147.0.7727.149 | Chromium 版本 |
saman.task_mode.runtime.active_environment_id |
UUID | 当前任务环境(本地/云电脑) |
saman.local_storage_for_web.home |
含技能暴露缓存 | 首页技能列表 |
saman.local_storage_for_web.launcher |
含 discovered_skills | 已发现技能图标 |
saman.text_picker.skills_list |
技能数组 | 文本选择器技能 |
sync.data_type_status_for_sync_to_signin.skill |
false | 技能不同步到账号 |
saman.native_sync.enabled |
true | 原生同步开启 |
「源码」
几个值得注意的点:
第一,技能配置存储在 Chromium 的 localStorage 中(saman.local_storage_for_web),不是独立的配置文件。这意味着技能列表是 Web 应用状态的一部分,由渲染进程管理。
第二,技能数据包含 agentId、botId、commandId 三种标识符,说明技能可能来自不同的后端——有些是 Agent 类型、有些是 Bot 类型、有些是命令类型。这与"技能·连接器·工作伙伴"的三层产品概念对应。
第三,技能默认不同步到账号(skill: false),但书签双写同步开启(bookmark_sync.dual_write_enabled: true)。这说明技能目前被视为设备本地状态,而不是用户账号的跨设备资产。
第四,active_environment_id 是一个 UUID,标识当前任务执行环境。当用户切换"本地电脑"和"云电脑"时,这个 ID 会变化,Agent 的工具路由和权限策略也会随之改变。
2.5 内置浏览器与外部浏览器的关系
豆包工作内置了一个完整的 Chromium 浏览器(DoubaoWork Browser.app),但它不是简单地在应用内打开网页。这个内置浏览器有独立的 Bundle ID(com.work.pc.doubao.browser)、独立的偏好设置文件(com.work.pc.doubao.browser.plist)和独立的缓存目录。「源码」
当 Agent 需要执行浏览器自动化任务时(比如操作网页、填写表单、抓取数据),它使用的是这个内置浏览器,而不是用户系统默认的浏览器。这有几个好处:
- 环境隔离:Agent 的浏览器操作不影响用户日常浏览器的标签页、Cookie 和登录状态。
- 权限控制:内置浏览器可以通过沙箱和策略限制 Agent 的行为。
- 自动化接口:内置浏览器可以通过 CDP(Chrome DevTools Protocol)或扩展 API 被程序化控制。
第三方开源项目 doubao-mcp-bridge 正是利用了这一点——它通过 CDP 连接到豆包工作的内置浏览器,监听 Agent 的 MCP 调用(MCP_CALL_JSON 消息),从而把豆包工作的工具能力暴露给外部 MCP 客户端。「社区」这从侧面证实了豆包工作内部使用 MCP 协议进行工具调用,且这些调用在渲染进程中可被 CDP 观测。
2.6 更新机制
豆包工作使用 saman_updater 进行自动更新。manifest.json 中的更新配置:
updateChannel: "release":正式发布渠道channel: "1001":内部渠道编号whatsNewVersion: "1.57.0":"新功能"页面版本onboardingVersion: "1.6.0":引导流程版本
更新流程由 saman_updater 和 relaunch_helper 协作完成:saman_updater 检查并下载更新,relaunch_helper 在更新安装后重启应用。这是一个标准的 Sparkle 式更新模式,但由字节内部框架实现。「源码」
版本号 2.25.18 表明豆包工作已经经历了相当多的迭代。从 2.0 到 2.25,每个小版本可能包含功能更新和 bug 修复。whatsNewVersion 1.57.0 与主版本号不同步,说明"新功能"弹窗的内容更新频率与应用版本发布频率不同。
2.7 Chromium 版本选择与安全更新
豆包工作使用 Chromium 147.0.7727.149,这是一个值得注意的版本选择。
2.7.1 为什么不用系统 WebView
在 macOS 上,开发者可以选择使用系统内置的 WKWebView(基于 Safari/WebKit)而不是打包整个 Chromium。豆包工作选择打包 977MB 的 Chromium 而不是用 WKWebView,有几个原因:
- 一致性:Chromium 版本在所有用户机器上一致,不依赖 macOS 版本。WKWebView 的行为随 macOS 更新而变化,可能导致兼容性问题。
- 扩展系统:Chromium 支持 Chrome Extension API,豆包含特权扩展和后台页面依赖这些 API。WKWebView 不支持 Chrome 扩展。
- CDP 支持:Chromium 内置 Chrome DevTools Protocol,这是浏览器自动化(computer_use_tool)和第三方桥接(doubao-mcp-bridge)的基础。WKWebView 的远程调试能力有限。
- 跨平台:同一套 Chromium 代码可以在 macOS 和 Windows 上运行,降低跨平台维护成本。
代价是 977MB 的磁盘占用和较高的内存使用。但对于一个需要浏览器自动化、扩展支持和跨平台一致性的 Agent 产品来说,这个代价是合理的。
2.7.2 Chromium 安全更新
Chromium 是一个活跃更新的项目,每个大版本修复大量安全漏洞。豆包含 Chromium 版本 147 是相当新的版本(Chromium 147 于 2025 年底发布),说明团队保持着积极的更新节奏。
saman_updater 不仅更新豆包工作自身的代码,也可能更新内置的 Chromium。这一点很重要:如果 Chromium 版本长期不更新,Agent 的浏览器自动化和内置浏览器可能暴露在已知安全漏洞中。从版本号来看,豆包工作在这方面做得不错。
2.8 进程间通信机制
豆包含多进程架构需要高效的 IPC 机制。根据 Chromium 架构和二进制符号分析,可以推断 IPC 通道如下:
2.8.1 Chromium Mojo IPC
Chromium 使用 Mojo IPC 库进行进程间通信。渲染进程(Agent 逻辑)与主进程之间的通信通过 Mojo 完成:
- 渲染进程发送工具调用请求到主进程
- 主进程返回工具执行结果
- 主进程发送权限请求到渲染进程(如请求目录授权)
Mojo 使用 Unix domain socket(在 macOS/Linux 上)或命名管道(在 Windows 上),支持异步消息传递和结构化数据传输。
2.8.2 原生 Helper IPC
主进程与原生 Helper(command_helper、mcp-helper、sandbox-launcher)之间的通信可能通过以下方式:
- FFI 直接调用:对于 libmcp_helper.dylib 和 libsandbox.dylib,主进程通过 dlopen + FFI 直接调用函数,不需要 IPC
- 子进程管道:对于 command_helper 和 mcp-helper 可执行文件,主进程通过 stdin/stdout 管道或 Unix domain socket 通信
- XPC:对于需要系统服务的场景(如 Finder 扩展),可能通过 macOS XPC 通信
2.8.3 渲染进程与 Helper 的隔离
关键的安全设计是:渲染进程不直接与 Helper 通信。所有通信都经过主进程中转。这意味着:
- 渲染进程被攻破后,攻击者不能直接向 mcp-helper 发送恶意请求
- 主进程可以验证所有来自渲染进程的请求
- Helper 只信任来自主进程的请求(通过父进程签名验证)
这是一种典型的"安全内核"设计:主进程是安全边界,渲染进程是不可信的,Helper 是特权的。
2.9 运行环境层的架构判断
综合以上证据,可以对豆包工作的运行环境层做出几个架构判断:
判断一:Chromium 壳是刻意选择,不是偷懒。 豆包工作没有用 Electron,而是自己实现了一套 Helper 进程体系(command_helper、mcp-helper、sandbox-launcher)。这说明团队需要对原生能力有更精细的控制——特别是 MCP 服务器管理和 macOS Seatbelt 沙箱,这些是 Electron 的 Node.js 环境难以安全实现的。用 Rust 写 MCP 助手而不是用 Node.js,也反映了对性能、内存安全和并发的考量。「推断」
判断二:安全边界在 Helper 层,不在渲染层。 渲染进程运行着 Agent 的核心逻辑,但它不直接执行命令或管理 MCP 服务器。所有敏感操作都通过 Helper 进程完成,Helper 进程有独立的身份验证(父进程签名验证)和沙箱策略。这是一个纵深防御设计:即使渲染进程被攻破,攻击者也无法直接执行任意命令。「推断」
判断三:三产品线共享桌面端是平台化战略。 信任域覆盖 cici/dola/doubao 三个品牌,说明豆包工作不是单一产品的客户端,而是字节 AI 能力的统一桌面入口。Agent 能力(Skill、MCP、沙箱、多 Agent 编排)是跨产品线复用的基础设施。「推断」
判断四:本地 Harness 的可定制性被刻意收窄。 与 WorkBuddy 把所有 Harness 文件暴露给用户不同,豆包工作只开放了 .user_skills/ 目录。这降低了用户误配置的风险,也使得 Harness 行为更可预测、更可审计,但代价是高级用户无法深度定制 Agent 的身份、记忆和行为规则。这是"产品"与"工具"的分野——豆包工作面向大众用户,WorkBuddy 面向开发者和 power user。「推断」
下一章将进入引导层,分析豆包工作如何定义 Agent 的身份、组装系统提示词、管理记忆——这些是决定 Agent"是谁"和"记住什么"的核心机制。
CHAPTER 03 · ZJBB-004
授权:CC BY-NC-SA 4.0 (署名 · 非商业性使用 · 相同方式共享)—— 转载请保留原作者署名与相同协议。
整理:疯狂的豇豆 · 查看完整来源清单
❓ 什么是CH-02 运行环境:Chromium 壳与原生 Helper?
图 2-1:豆包工作整体架构——非 Electron 的混合架构
❓ 如何理解一个 1MB 的主二进制?
在 macOS 上安装豆包工作后,应用本体位于 /Applications/DoubaoWork.app。按照 Mac 应用的标准结构,它的可执行文件在 Contents/MacOS/DoubaoWork。如果你查看这个文件的大小,会发现一个反直觉的事实:
❓ 如何理解进程架构?
豆包工作运行时的进程模型可以分为三层:
❓ 如何理解manifest.json:信任域与产品线整合?
应用包的 Contents/Resources/manifest.json 是一个关键配置文件,它不仅声明了版本信息,还定义了豆包工作的安全边界——哪些域名可以加载主框架、哪些脚本可以执行、哪些扩展拥有特权。