张路.
POSTS · · 7 分钟阅读

本地化不是翻译:给一个 Agent 运行时做中文版

把界面文案换成中文只是第一天的工作。真正的坑是:用户装好中文版,用了两周,某次自动更新之后它变回了英文。

先说清楚这是谁的项目

OpenWorker 是 Andrew Ng 团队开源的桌面 AI coworker,MIT 协议。它不是我的。

我做的是它的中文侧 —— 中文站 oaosf.cn、中文版 macOS 构建,以及一份源码深度分析。这篇讲的是做这件事过程中两个值得说的发现。

发现一:自动更新会把本地化冲掉

本地化一个桌面应用,字符串是最容易的部分。真正的坑在分发链路上。

用户装好中文版,用得好好的,某次自动更新之后 —— 它变回英文了。因为更新源指向的是上游仓库,而上游的构建里没有你的本地化。

这个 bug 有几个讨厌的性质:

  • 它不会立刻出现,要等到上游发一次新版
  • 用户不会觉得是更新的锅,只会觉得「这软件怎么突然变英文了」
  • 重装能修,所以它很容易被当成偶发问题放过

0.1.7 把自动更新源切到了中文仓库,这条才算填上。回头看,「本地化一个应用」的完整定义至少包括三件事:翻译、独立的构建产物(独立 Bundle ID,让它能和英文版并存),以及独立的更新通道。少了第三件,前两件的效果有保质期。

顺带:当前构建未经公证,首次打开如果 macOS 拦了,右键 App 选一次「打开」。

发现二:它不是套壳聊天应用

做本地化要通读源码,读完之后我对这个项目的判断变了。

一开始我以为这是个「加了工具调用的桌面聊天窗口」。它不是 —— 它是一套本地 Agent 运行平台,壁垒来自工具循环、权限系统、连接器和持久化,而不是它接了哪个模型。

架构上分三层:React + Tauri 2 的桌面壳负责窗口、托盘和进程生命周期;FastAPI + WebSocket 的 Python sidecar 暴露本地 API 和事件流;SessionManager 协调会话、Inbox、自动化、审计与持久化。

有两个设计细节我觉得值得抄。

并发读取,串行写入

TurnEngine 先逐个授权工具调用,然后并发执行明确标记为低风险的读取;写入、Shell 和未标注的工具保持严格顺序

这是个很朴素但很对的取舍。Agent 的工具调用里,读取占大多数且互不干扰,串行跑纯粹浪费时间;而写入和 Shell 一旦并发,竞态和意外副作用的排查成本极高 —— 更要命的是这类 bug 往往不可复现。用「是否显式标注为低风险读取」当闸门,而不是靠模型自己判断,把安全性从概率变成了规则。

注意默认值的方向:未标注的工具走串行。默认保守,要并发得显式声明 —— 这个方向选反了就是灾难。

权限分四级,不是一个开关

READ          读取与搜索          直接执行
WRITE_LOCAL   本地文件写入        按模式批准,受可写目录约束
EXEC          运行命令            重点确认,复杂 Shell 不能自动命中白名单
EXTERNAL      发送消息 / 改外部系统  目标级授权

「复杂 Shell 不能自动命中白名单」这条很关键。白名单机制最常见的破法就是把危险命令藏在管道、子 shell 或者变量展开里;承认「解析不了的就别自动放行」,比假装白名单是完备的要诚实得多。

顺便也读出了问题

源码分析不只是夸。两处值得记:

  • server/manager.py 已经超过 4000 行,连接器执行层接近 5000 行。功能闭环很完整,但中心模块正在变重,后续维护需要继续拆分边界
  • 官方仍标注 Open Beta。建议先在低风险工作流里用,逐项检查连接器权限、审批模式和模型数据政策

统计取自 main@01b6f83,而且是代码资产数量,不代表我跑过原项目的测试

「本地优先」是一条边界,不是一句承诺

这条我在项目页和这里都要重复一遍,因为它最容易被误读。

会话、记忆、密钥和主要运行状态确实留在设备上。但你一旦选择云端模型或启用外部连接器,完成任务所需的数据仍然会发送到对应服务。想要完全本地,用 Ollama 并且不启用外部连接器。

「本地优先」描述的是默认值和控制权归属,不是「数据永远不出网」。把它当后者理解,迟早出事。

想自己看

装:站上下载 DMG,拖进「应用程序」,与英文版并存,自带模型密钥。

读源码:

git clone https://github.com/zhanglunet/openworker-zh-localized
cd openworker-zh-localized
bash packaging/setup_dev_env.sh
.venv/bin/openworker-server --cwd ~/project --port 8765

后端 Python 3.10+,前端 Node 20+,完整桌面壳还要 Rust 工具链。想研究 Agent 架构的话,它是一份能跑起来的参考实现 —— 这可能比它作为产品的价值更高。