Andrew Ng开源OpenWorker:本地优先的桌面AI Agent,从"对话"到"交付成品"的范式转变
一、引言:AI助手的"最后一公里"困境
过去两年,大语言模型的能力突飞猛进。GPT-5系列、Claude Opus 4.8、Gemini 3.6——每个模型都能写出令人惊叹的回答,都能在基准测试中刷新纪录。但有一个问题始终没解决:AI停留在"说"的阶段,无法真正"做"。
你让AI"准备一份客户续约简报",它给你一段文字。你让AI"整理一下这周的日历冲突",它给你一个待办清单。你让AI"排查一下线上故障",它给你一份分析报告。然后呢?你还是要自己动手——打开文档编辑器、切换日历应用、登录Slack发消息。AI回复了你,然后把烂摊子留给你收拾。
2026年7月23日,Andrew Ng 联合 Alexa 前负责人 Rohit Prasad 开源了 OpenWorker,一个MIT协议的桌面AI Agent。GitHub上14.5k星,定位是"桌面AI同事"(desktop AI coworker)。它的核心承诺只有一句话:AI应该交付成品,而不是对话。
┌──────────────────────────────────────────────────────────────┐
│ 范式转变:从"对话"到"交付成品" │
│ │
│ 传统AI助手(Chatbot范式) │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ 用户提问 │ -> │ AI回复 │ -> │ 用户自己动手干活 │ │
│ └──────────┘ └──────────┘ └──────────────────┘ │
│ ↑ │
│ "帮我写个报告" → 一段文字 → 你打开Word自己排版 │
│ │
│ OpenWorker(成品交付范式) │
│ ┌──────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ 用户描述 │ -> │ AI拆解执行+审批 │ -> │ 交付成品文件 │ │
│ │ 目标成果 │ │ 跨工具全流程 │ │ 可直接使用 │ │
│ └──────────┘ └──────────────────┘ └──────────────┘ │
│ ↑ │
│ "准备客户续约简报" → 读CRM+查Slack+分析数据 → HTML报告 │
│ → 请求审批 → 发送给客户CTO │
└──────────────────────────────────────────────────────────────┘
这不是一个简单的聊天机器人升级。这是AI产品设计哲学的深刻转变——从"优化回复质量"到"优化交付完整性"。本文将从架构设计、审批门控、连接器生态、模型路由等维度,深度剖析OpenWorker的技术实现。
二、项目背景:为什么是Andrew Ng
Andrew Ng 不需要过多介绍。斯坦福教授、Coursera联合创始人、Google Brain早期成员、百度前首席科学家、DeepLearning.AI创始人。和他一起做这个项目的 Rohit Prasad,是亚马逊Alexa的前负责人,拥有超过八年的大规模语音AI产品经验。
两人联手做OpenWorker,信号非常明确:AI Agent要从"玩具"变成"工具",产品化是最关键的一步。
OpenWorker于2026年7月20日创建,7月23日正式开源,8月广泛传播。截至本文撰写时,GitHub上已获得14.5k星,仓库包含:
coworker/:Python后端——约119个文件、32,400行代码(Agent引擎、模型提供者、连接器、MCP客户端、记忆系统、自动化调度)surfaces/gui/:React UI + Tauri桌面壳——149个TypeScript/TSX文件stt/:Rust语音识别sidecar(基于whisper.cpp)packaging/:macOS DMG和Windows安装包构建、自动更新清单tests/:78个后端测试模块
这不是一个Demo项目。这是一个可以直接下载、安装、使用的桌面应用。
三、核心设计哲学:不追求"多聪明",追求"把活干完"
OpenWorker 的 README 第一句话写得非常克制:“AI that gets your everyday tasks done.” 翻译过来就是"帮你把日常任务干完的AI。"
这句话的潜台词是:OpenWorker不和你比谁更聪明,它和你比谁更靠谱。
3.1 从"Prompt"到"Outcome"的转变
传统AI的使用方式是写Prompt。你写"给我写一份总结",AI给你一段总结。写得不好,你改Prompt重试。OpenWorker改变了这个交互模型——你给的是一个结果目标(outcome),不是一段提示词(prompt)。
你说"准备下周一客户续约会议的简报",它不会回你一段文字让你自己去整理。而是:
- 检查HubSpot CRM中的客户数据
- 读取相关的Slack对话记录
- 分析使用量趋势
- 生成一份HTML/PDF简报文件
- 请求你的审批后,将简报通过邮件发送给客户CTO
最终交付的是一个成品——一份可以直接打开的文档、一条已经发送的消息、一项已经更新的日历。
3.2 四种Agent角色,而不是一种万能Prompt
OpenWorker 在后端区分了四种Agent角色,每种角色有独立的工具集、系统提示词和行为模式:
# coworker/agent.py - 四种Agent角色定义(示意代码)
AGENT_ROLES = {
"chat": {
"description": "纯对话模式,没有文件和shell权限",
"tools": ["web_search", "read_url"],
"system_prompt": "你是一个友好的对话助手,只能提供信息和建议。"
},
"code": {
"description": "编码专用Agent,带单目录工作区",
"tools": [
"read_file", "write_file", "shell",
"git_clone", "git_commit", "git_push",
"grep_search", "list_files"
],
"system_prompt": (
"你是一位资深工程师。先读懂代码再修改,"
"改完必须验证。多个互不依赖的read/grep请求"
"要在一个批次里并发发出,而不是一次一个。"
),
"workspace": "single_dir"
},
"coworker": {
"description": "知识工作Agent,交付具体成品",
"tools": [
"read_file", "write_file", "web_search",
"connector:slack", "connector:gmail",
"connector:calendar", "connector:github",
"connector:jira", "connector:notion"
],
"system_prompt": (
"你的任务是交付可用的成品文件,而不是回复文字。"
"在发送消息、修改日历、运行命令之前,"
"必须请求用户审批。"
),
"workspace": "multi_dir"
},
"myhelper": {
"description": "长期在线个人助理,跨时间连续线程",
"tools": ["read_file", "write_file", "web_search",
"connector:slack", "connector:gmail",
"connector:calendar", "scheduler"],
"system_prompt": "你是一个长期助理,记住用户的重要事项并主动提醒。",
"persistent": True
}
}
四种角色,四套工具集,四套人格。不同工种配不同人格,比一个万能Prompt硬撑要清晰得多。这种设计也体现了软件工程中的**关注点分离(Separation of Concerns)**原则。
四、四层架构详解
OpenWorker 的架构是清晰的分层设计,全部运行在用户本机。
┌──────────────────────────────────────────────────────────────────┐
│ OpenWorker 四层架构总览 │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 1: 桌面壳 (Tauri 2 + React 18) │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ React UI (Composer, Transcript, Onboarding) │ │ │
│ │ ├────────────────────────────────────────────────┤ │ │
│ │ │ Tauri Rust Shell (进程管理, 系统托盘, 语音) │ │ │
│ │ │ - 启动/监控Python sidecar │ │ │
│ │ │ - 本地语音识别 (ocw-stt, whisper.cpp) │ │ │
│ │ │ - 系统托盘 & 保持唤醒 (caffeinate) │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ Layer 2: 本地Agent服务 (Python FastAPI + uvicorn) │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ TurnEngine (核心执行循环) │ │ │
│ │ │ ├─ SessionManager (会话管理) │ │ │
│ │ │ ├─ PermissionEngine (审批门控引擎) │ │ │
│ │ │ ├─ ToolRegistry (工具注册与调度) │ │ │
│ │ │ └─ ProviderRouter (模型路由) │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ │ 绑定: 127.0.0.1:8765 · 默认12轮模型-工具迭代 │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ Layer 3: 能力与连接器层 │ │
│ │ ┌────────────────────┬─────────────────────────────┐ │ │
│ │ │ 本地工具 │ 外部连接器 │ │ │
│ │ │ ├─ 文件系统 (read/ │ ├─ Slack, GitHub, Jira │ │ │
│ │ │ │ write/list) │ ├─ Gmail, Outlook, Cal │ │ │
│ │ │ ├─ Shell执行 │ ├─ Notion, Linear, │ │ │
│ │ │ ├─ Git操作 │ │ HubSpot, Attio │ │ │
│ │ │ ├─ ripgrep搜索 │ ├─ Google Drive, Dropbox│ │ │
│ │ │ ├─ 待办列表 │ ├─ Asana, Monday.com │ │ │
│ │ │ └─ MCP Client │ └─ MCP Server (任意) │ │ │
│ │ └────────────────────┴─────────────────────────────┘ │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ Layer 4: 模型路由 (aisuite) │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ ProviderRouter → ProviderDescriptor → Client │ │ │
│ │ │ ├─ OpenAI / Anthropic / Google (原生) │ │ │
│ │ │ ├─ DeepSeek / GLM / Kimi / Qwen / MiniMax │ │ │
│ │ │ │ Mistral / Grok (兼容) │ │ │
│ │ │ ├─ Together / Fireworks (开源权重) │ │ │
│ │ │ └─ Ollama (完全本地) │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ 全部运行在本机 · 唯一的云组件:OAuth握手中转服务 │
└──────────────────────────────────────────────────────────────────┘
4.1 Layer 1: 桌面壳(Tauri 2 + React 18)
桌面壳是用户与OpenWorker交互的入口。Tauri 2提供了原生窗口、系统托盘、进程管理等功能。React 18提供了UI组件。
关键设计:Tauri壳负责管理Python sidecar的完整生命周期——启动、监控、终止。如果GUI崩溃,sidecar会自动检测到父进程消失并退出,防止产生僵尸进程。
# coworker/server/run.py - Sidecar进程管理(示意代码)
import os
import signal
import threading
def _exit_when_orphaned():
"""检测父进程是否存活,若已死则自行退出"""
parent_pid = os.getppid()
while True:
try:
# POSIX: os.kill(pid, 0) 检测进程存活
os.kill(parent_pid, 0)
threading.Event().wait(2.0)
except ProcessLookupError:
# 父进程已终止,自己退出
os._exit(0)
except Exception:
threading.Event().wait(2.0)
def _watch_parent_windows(parent_handle):
"""Windows平台:使用WaitForSingleObject监听父进程"""
import ctypes
kernel32 = ctypes.windll.kernel32
while True:
ret = kernel32.WaitForSingleObject(parent_handle, 2000)
if ret == 0: # 父进程已结束
os._exit(0)
4.2 Layer 2: 本地Agent服务(Python FastAPI)
这是整个系统的"大脑"。FastAPI服务绑定在127.0.0.1:8765,提供约120个REST端点和一个WebSocket接口用于流式传输Agent事件。
核心组件是TurnEngine——实现了"模型在环"(model-in-the-loop)执行模式。一次用户请求可能涉及多轮模型推理和工具调用:
# coworker/engine.py - TurnEngine核心循环(示意代码)
class TurnEngine:
"""一次用户"turn"的执行引擎"""
def __init__(self, provider_client, tool_registry, permission_engine):
self.provider = provider_client
self.tools = tool_registry
self.permissions = permission_engine
self.max_iterations = 12 # 默认最多12轮模型-工具迭代
async def run(self, user_input: str, history: list[dict]):
messages = history + [{"role": "user", "content": user_input}]
for iteration in range(self.max_iterations):
# 1. 模型推理
response = await self.provider.complete(messages, tools=self.tools.schemas)
if response.tool_calls:
# 2. 并行执行低风险工具,串行执行高风险工具
results = await self._execute_tools(response.tool_calls)
messages.extend(results)
else:
# 3. 模型给出最终回复,结束循环
return response.content
raise MaxIterationsError(f"达到最大迭代次数 {self.max_iterations}")
async def _execute_tools(self, tool_calls: list):
"""根据风险等级并行或串行执行工具"""
read_calls = []
write_calls = []
for tc in tool_calls:
risk = self.permissions.classify(tc)
if risk == RiskLevel.READ:
read_calls.append(tc)
else:
write_calls.append(tc)
# 低风险:并行执行
read_results = await asyncio.gather(
*[self.tools.execute(tc) for tc in read_calls]
)
# 高风险:串行执行,每个都走审批门控
write_results = []
for tc in write_calls:
outcome = await self.permissions.request_approval(tc)
if outcome == ApprovalOutcome.APPROVED:
write_results.append(await self.tools.execute(tc))
else:
write_results.append({"error": "用户拒绝执行"})
return read_results + write_results
4.3 Layer 3: 能力与连接器层
这一层分为本地工具和外部连接器。本地工具包括文件系统操作(read_file/write_file/list_files)、Shell执行、Git操作、ripgrep搜索等。外部连接器是OpenWorker的核心能力——25+预置连接器 + MCP协议扩展。
4.4 Layer 4: 模型路由(aisuite)
最底层是基于aisuite的模型路由。aisuite是Andrew Ng团队之前开发的轻量级Python库,统一了多家大模型的调用接口。OpenWorker在此基础上增加了Agent能力——工具调用、状态管理、任务编排。
┌─────────────────────────────────────────────────────┐
│ ProviderRouter 模型路由流程 │
│ │
│ 用户选择模型: "Claude Fable 5" │
│ │ │
│ ▼ │
│ model_labels() 映射查找 │
│ │ │
│ ▼ │
│ 模型ID: "anthropic:claude-fable-5" │
│ │ │
│ ▼ │
│ ProviderRouter.get_client("anthropic") │
│ │ │
│ ▼ │
│ registry.py: build_provider_client() │
│ │ │
│ ▼ │
│ ProviderDescriptor.build() → AnthropicProvider │
│ │ │
│ ▼ │
│ TurnEngine.process_turn() 开始执行 │
└─────────────────────────────────────────────────────┘
五、审批门控机制:核心创新
这是OpenWorker最值得深入分析的部分。一个真的去动你文件、发你消息、跑你终端命令的Agent,最怕的就是它自作主张。大多数桌面Agent项目把审批当成UI层面的事后补丁,OpenWorker把它做成了一个类型系统。
5.1 四类风险等级
每个工具调用在注册时就被标注了风险等级:
# coworker/permissions.py - 风险等级与审批门控(示意代码)
from enum import IntEnum
class RiskLevel(IntEnum):
"""工具调用风险等级"""
READ = 1 # 只读:无副作用,永远放行
WRITE_LOCAL = 2 # 写本地:改动工作区文件,按路径范围卡
EXEC = 3 # 执行命令:运行Shell命令
EXTERNAL = 4 # 外部操作:副作用发生在机器之外
# 工具风险注册表
TOOL_RISK_MAP = {
# 只读操作 - 自动放行
"read_file": RiskLevel.READ,
"list_files": RiskLevel.READ,
"grep_search": RiskLevel.READ,
"web_search": RiskLevel.READ,
"connector:slack_read": RiskLevel.READ,
# 写本地操作 - 需审批(路径范围受限)
"write_file": RiskLevel.WRITE_LOCAL,
"create_file": RiskLevel.WRITE_LOCAL,
"git_commit": RiskLevel.WRITE_LOCAL,
# Shell执行 - 高风险,永远需审批
"shell": RiskLevel.EXEC,
"run_script": RiskLevel.EXEC,
# 外部操作 - 需审批,但可设置"常驻规则"
"connector:slack_send": RiskLevel.EXTERNAL,
"connector:gmail_send": RiskLevel.EXTERNAL,
"connector:calendar_update": RiskLevel.EXTERNAL,
}
5.2 五种运行模式
OpenWorker定义了五种运行模式,决定同一风险等级在不同场景下的处理方式:
# coworker/permissions.py - 运行模式(示意代码)
class Mode(IntEnum):
DISCUSS = 0 # 纯对话,只读
PLAN = 1 # 只读+规划,不做任何修改
INTERACTIVE = 2 # 默认模式:读自动放行,写和命令要批准
AUTO = 3 # 全放行,但写操作限制在路径范围内
CUSTOM = 4 # 根据用户白名单自动放行
class PermissionEngine:
"""审批门控引擎"""
def __init__(self, mode: Mode = Mode.INTERACTIVE):
self.mode = mode
self.standing_rules: dict[str, str] = {} # 常驻规则
def check_tool(self, tool_call: ToolCall) -> PermissionResult:
"""检查工具调用是否需要审批"""
risk = TOOL_RISK_MAP.get(tool_call.name, RiskLevel.WRITE_LOCAL)
# 只读操作:所有模式自动放行
if risk == RiskLevel.READ:
return PermissionResult.ALLOW
# 检查常驻规则
rule_key = f"{tool_call.name}:{tool_call.args.get('target', '')}"
if rule_key in self.standing_rules:
return PermissionResult.ALLOW
# 根据模式判断
if self.mode == Mode.DISCUSS or self.mode == Mode.PLAN:
if risk > RiskLevel.READ:
return PermissionResult.DENY
if self.mode == Mode.INTERACTIVE:
if risk >= RiskLevel.WRITE_LOCAL:
return PermissionResult.NEEDS_USER
if self.mode == Mode.AUTO:
if risk == RiskLevel.EXEC:
return PermissionResult.NEEDS_USER # Shell永远需审批
return PermissionResult.ALLOW
return PermissionResult.NEEDS_USER
5.3 审批门控流程
┌─────────────────────────────────────────────────────────────────────┐
│ 审批门控执行流程 │
│ │
│ TurnEngine PermissionEngine │
│ ┌─────────────┐ ┌───────────────┐ │
│ │ 模型请求 │ │ │ │
│ │ 调用工具 │ │ │ │
│ └──────┬──────┘ │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌──────────────┐ │ │ │
│ │ check_tool() │─────────────────►│ risk=EXEC │ │
│ │ 检查风险等级 │ │ needs_user? │ │
│ └──────┬───────┘ │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌──────────────────┐ │ │ │
│ │ Result: NEEDS_USER│ │ │ │
│ └────────┬─────────┘ │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌──────────────────────┐ │ │ │
│ │ 发射 PERMISSION_ │ │ │ │
│ │ REQUIRED 事件到UI │ │ │ │
│ └──────────┬───────────┘ │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌──────────────────────┐ │ │ │
│ │ 用户选择: │ │ │ │
│ │ ├─ ONCE: 仅此一次 │ │ │ │
│ │ ├─ ALWAYS_TOOL: │ │ │ │
│ │ │ 记住此工具+目标 │ │ │ │
│ │ └─ DENY: 拒绝 │ │ │ │
│ └──────────┬───────────┘ │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌──────────────────────┐ │ │ │
│ │ ApprovalOutcome.ONCE │ │ │ │
│ │ → 执行工具调用 │ │ │ │
│ └──────────────────────┘ │ │ │
│ │
│ 关键设计决策: │
│ • 常驻规则(standing rule)仅对EXTERNAL风险开放 │
│ • Shell命令永远需要审批,一次都不放过 │
│ • 无人值守模式不提升自主权,审批请求转到收件箱 │
└─────────────────────────────────────────────────────────────────────┘
两个关键设计决策值得注意:
无人值守不提升自主权:当Agent在无人看管时遇到需要批准的动作,不会自作主张,而是把请求放进收件箱(Inbox),会话挂起直到你回来处理。Agent不会因为你走开了就悄悄获得更多权限。
常驻规则只对EXTERNAL开放:当一个外部动作(如"向Slack #checkout-alerts 频道发送消息")被你批准后,引擎可以记住"这个工具到这个目标",下次不再问你。但这条捷径只对EXTERNAL风险开放。Shell命令永远需要审批,一次都不放过。 安全的边界卡在最危险的动作上,而不是图省事全放开。
六、35个应用连接器架构
OpenWorker的另一个核心能力是其连接器生态。开箱即用支持25+预置连接器,加上MCP协议扩展,实际可达35个以上。
┌─────────────────────────────────────────────────────────────────────┐
│ OpenWorker 连接器架构 │
│ │
│ 连接器管理器 (ConnectorManager) │
│ ┌──────────────────────┐ │
│ │ OAuth Token Manager │ │
│ │ Credential Vault │ │
│ │ Connector Registry │ │
│ └──────────┬───────────┘ │
│ │ │
│ ┌──────────┬──────────┬────────┼──────────┬──────────┬────────┐ │
│ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ │ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │Slack │ │GitHub│ │ Jira │ │Gmail │ │Calendar│ │Notion│ │
│ ├──────┤ ├──────┤ ├──────┤ ├──────┤ ├───────┤ ├──────┤ │
│ │消息 │ │PR │ │Issue │ │读取 │ │读取 │ │读取 │ │
│ │发送 │ │创建 │ │创建 │ │发送 │ │创建 │ │写入 │ │
│ │读取 │ │读取 │ │搜索 │ │搜索 │ │更新 │ │搜索 │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ └───────┘ └──────┘ │
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │Linear│ │HubSpot│ │Outlook│ │Drive │ │Dropbox│ │Asana │ │
│ ├──────┤ ├───────┤ ├───────┤ ├──────┤ ├───────┤ ├──────┤ │
│ │Issue │ │CRM │ │邮件 │ │文件 │ │文件 │ │任务 │ │
│ │管理 │ │数据 │ │日历 │ │操作 │ │同步 │ │管理 │ │
│ └──────┘ └───────┘ └───────┘ └──────┘ └───────┘ └──────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ MCP Client (Model Context Protocol) │ │
│ │ 任何MCP Server都可以接入,带per-tool控制 │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ 数据库 │ │ 浏览器 │ │ 自定义API │ │ 文件系统 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ 凭证存储:全部在本地SecretStore,唯一云组件是OAuth握手中转服务 │
│ 使用Auth0授权码+PKCE流程,Token直接交给本机,绝不在云端存储 │
└─────────────────────────────────────────────────────────────────────┘
连接器的设计遵循统一的接口协议。每个连接器暴露一组工具(如Slack连接器暴露slack_send_message、slack_read_channel、slack_search),这些工具被注册到ToolRegistry中,供Agent引擎调用。
关键设计决策:连接器凭证全部存在本地SecretStore。唯一的云组件是一个可选的OAuth中转服务,使用Auth0授权码+PKCE流程处理一键连接握手。Token直接交给本机,绝不在云端存储。用户也可以完全不登录,手动粘贴API密钥来使用连接器。
七、模型无关设计
OpenWorker是真正的模型无关(model-agnostic)。它不依赖任何特定模型提供商,也不运行自己的推理服务。你自带API Key,或者用Ollama完全本地跑。
7.1 30个精选模型 + 自定义扩展
# coworker/providers/matrix.py - 模型矩阵(示意代码)
from dataclasses import dataclass
@dataclass
class ModelCapabilities:
tools: bool = True
vision: bool = False
parallel_tool_calls: bool = True
streaming: bool = True
pdf: bool = False
# 精选模型矩阵
MATRIX = {
# OpenAI 原生
"gpt-5.6-sol": ModelEntry("GPT-5.6 Sol · OpenAI", _AGENTIC_VISION),
"gpt-5.6-terra": ModelEntry("GPT-5.6 Terra · OpenAI", _AGENTIC_VISION),
"gpt-5.5": ModelEntry("GPT-5.5 · OpenAI", _AGENTIC_VISION),
# Anthropic 原生
"anthropic:claude-fable-5": ModelEntry("Claude Fable 5 · Anthropic", _AGENTIC_VISION),
"anthropic:claude-opus-4.8": ModelEntry("Opus 4.8 · Anthropic", _AGENTIC_VISION),
"anthropic:claude-sonnet-4.6":ModelEntry("Sonnet 4.6 · Anthropic", _AGENTIC_VISION),
# Google 原生
"gemini:gemini-3.6-flash": ModelEntry("Gemini 3.6 Flash · Google", _AGENTIC_VISION),
"gemini:gemini-3.1-pro": ModelEntry("Gemini 3.1 Pro · Google", _AGENTIC_VISION),
# OpenAI 兼容供应商
"zai:glm-5.2": ModelEntry("GLM-5.2 · Z AI", _AGENTIC),
"deepseek:deepseek-v4": ModelEntry("DeepSeek V4", _AGENTIC),
"moonshot:kimi-k2.6": ModelEntry("Kimi K2.6 · Moonshot", _AGENTIC),
"minimax:m2.5": ModelEntry("MiniMax M2.5", _AGENTIC),
"qwen:qwen3-max": ModelEntry("Qwen3 Max · Alibaba", _AGENTIC_VISION),
"xai:grok-4.3": ModelEntry("Grok 4.3 · xAI", _AGENTIC),
"mistral:mistral-large": ModelEntry("Mistral Large", _AGENTIC),
# 开源权重 (通过 Together / Fireworks)
"together:thinkingmachines/Inkling": ModelEntry("Inkling · via Together", _AGENTIC),
# 完全本地 (Ollama)
"ollama:llama-4": ModelEntry("Llama 4 · Ollama", _AGENTIC),
}
7.2 ProviderRegistry 工厂模式
模型路由使用工厂模式,通过ProviderDescriptor将UI配置映射到具体的ProviderClient:
# coworker/providers/registry.py - Provider注册与工厂(示意代码)
from dataclasses import dataclass, field
@dataclass
class ProviderField:
"""定义单个配置输入字段"""
key: str
label: str
type: str = "string" # string, password, select
secret: bool = False
required: bool = True
placeholder: str = ""
@dataclass
class ProviderDescriptor:
"""Provider描述符:包含配置字段和构建函数"""
name: str
label: str
fields: list[ProviderField] = field(default_factory=list)
build: callable = None # (profile, secrets) -> ProviderClient
# 注册所有Provider
DESCRIPTORS = {
"openai": ProviderDescriptor(
name="openai",
label="OpenAI",
fields=[
ProviderField(key="api_key", label="API Key", secret=True),
ProviderField(key="base_url", label="Base URL",
required=False,
placeholder="https://api.openai.com/v1"),
],
build=lambda p, s: OpenAIProvider(api_key=s["api_key"],
base_url=p.get("base_url")),
),
"anthropic": ProviderDescriptor(
name="anthropic",
label="Anthropic",
fields=[
ProviderField(key="api_key", label="API Key", secret=True),
],
build=lambda p, s: AnthropicProvider(api_key=s["api_key"]),
),
"ollama": ProviderDescriptor(
name="ollama",
label="Ollama (Local)",
fields=[
ProviderField(key="base_url", label="Base URL",
required=False,
placeholder="http://localhost:11434/v1"),
],
build=lambda p, s: OllamaProvider(base_url=p.get("base_url",
"http://localhost:11434/v1")),
),
}
def build_provider_client(provider_name: str, profile: dict, secrets: dict):
"""工厂函数:根据provider名称构建客户端"""
desc = DESCRIPTORS.get(provider_name)
if not desc:
raise ValueError(f"Unknown provider: {provider_name}")
return desc.build(profile, secrets)
7.3 本地优先的配置存储
# config.toml - OpenWorker 配置文件示例
# 全局配置: ~/.config/coworker/config.toml
# 工作区配置: <project>/.coworker/config.toml (覆盖全局)
model = "anthropic:claude-sonnet-4.6" # 默认模型
mode = "interactive" # 默认审批模式
max_iterations = 12 # 每轮最大迭代次数
# 无需审批的Shell命令前缀
allowed_commands = [
"ls",
"cat",
"pwd",
"git status",
"git diff",
]
# 自定义模式下自动放行的工具
auto_allow = [
"write_file:reports/",
"connector:slack_read",
]
[server]
host = "127.0.0.1"
port = 8765
八、Slack集成:从消息到成品的完整链路
Slack是OpenWorker最重要的入口之一。在Slack频道里@OpenWorker,一个会话在你的桌面端打开,工作在你的本地工具上运行,结果以线程回复形式送回。
┌─────────────────────────────────────────────────────────────────────┐
│ Slack → Agent → 连接器 → 交付 完整链路 │
│ │
│ 你: @OpenWorker checkout API is 500ing │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Slack (线程回复) │ │
│ │ "收到,正在排查..." │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ OpenWorker 桌面端(本地Agent引擎) │ │
│ │ │ │
│ │ 步骤1: 检查最近部署记录 (GitHub Connector) │ │
│ │ └─→ 读取: github.com/org/repo/deployments │ │
│ │ ← 发现: 2:02am 部署 payment-migration v3.8 │ │
│ │ │ │
│ │ 步骤2: 检查错误日志 (本地Shell) │ │
│ │ └─→ 执行: grep "500" /var/log/app/error.log │ │
│ │ ← 发现: 错误从2:04am开始,峰值18.4% │ │
│ │ │ │
│ │ 步骤3: 对照Runbook (本地文件) │ │
│ │ └─→ 读取: docs/runbook.md │ │
│ │ ← 确认: 回滚条件匹配 │ │
│ │ │ │
│ │ 步骤4: 生成事故时间线 (成品文件) │ │
│ │ └─→ 写入: reports/checkout-incident-timeline.md │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 审批门控: "发送此报告到 #checkout-alerts 频道?" │ │
│ │ [Approve] [Not now] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Slack (线程回复) │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ 事故时间线报告 (已发送) │ │ │
│ │ │ 服务: checkout │ │ │
│ │ │ 峰值错误率: 18.4% │ │ │
│ │ │ 影响时长: 10分钟 │ │ │
│ │ │ 根因: payment-migration v3.8 连接池回归 │ │ │
│ │ │ 建议操作: 回滚v3.8,监控10分钟 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 关键特性:Slack作为入口,但Agent运行在本地,凭证和模型调用都在本机 │
└─────────────────────────────────────────────────────────────────────┘
这个链路展示了OpenWorker区别于传统AI助手的核心:它不是"回复"你,而是"完成"任务。你只说了"checkout API is 500ing",它自己完成了部署检查、日志分析、Runbook对照、文档生成、审批请求、消息发送——整个闭环。
九、定时任务与无人值守
OpenWorker内置了一个常驻调度器,支持定时自动化任务:早间简报、每周报告、频道盯守。
┌─────────────────────────────────────────────────────────────────────┐
│ 定时任务调度架构 │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Scheduler (常驻调度器) │ │
│ │ ┌────────────────────┐ ┌──────────────────────────────┐ │ │
│ │ │ Cron表达式解析器 │ │ 任务队列 (SQLite持久化) │ │ │
│ │ └────────────────────┘ └──────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ (调度触发) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 任务执行器 │ │
│ │ ├─ 创建新会话 (SessionManager.create_session()) │ │
│ │ ├─ 注入用户预设的Prompt │ │
│ │ ├─ 运行TurnEngine │ │
│ │ ├─ 遇到审批请求 → 放入收件箱(Inbox) │ │
│ │ │ └─ 用户不在线时,会话挂起等待 │ │
│ │ └─ 执行完成后 → 保存完整Transcript到本地 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 收件箱 (Inbox) │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ ⏰ 08:00 早间简报 → 待审批: 发送Slack摘要 │ │ │
│ │ │ ⏰ 09:30 每日站会报告 → 待审批: 更新Jira任务 │ │ │
│ │ │ 👀 频道盯守: #alerts → 待审批: 发送告警通知 │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 宕机期间错过的任务:下次启动时自动补跑 │
│ keep-awake: 通过caffeinate(macOS)/SetThreadExecutionState(Win) │
│ 确保系统在任务执行期间不休眠 │
└─────────────────────────────────────────────────────────────────────┘
定时任务的强大之处在于它和审批门控机制的结合。即使任务在无人值守时运行,需要审批的操作不会自动执行,而是放入收件箱等待用户处理。宕机期间错过的任务,下次启动时自动补跑。系统还通过caffeinate(macOS)或SetThreadExecutionState(Windows)确保任务执行期间系统不休眠。
十、本地优先设计
OpenWorker的本地优先(local-first)设计是它的核心安全承诺。
10.1 什么存在本地
┌─────────────────────────────────────────────────────────────────────┐
│ 本地优先:数据驻留边界 │
│ │
│ ✅ 存在本机 (绝不离开) ❌ 可能离开本机 │
│ ┌────────────────────────────┐ ┌────────────────────────┐ │
│ │ Agent循环 (TurnEngine) │ │ 模型API调用 │ │
│ │ 对话记录 (ConversationStore)│ │ (到你选择的LLM提供商) │ │
│ │ 连接器凭证 (SecretStore) │ │ 连接器数据读写 │ │
│ │ 模型API Key (SecretStore) │ │ (到你集成的SaaS工具) │ │
│ │ 生成的文件 (本地文件系统) │ │ OAuth握手 (仅中转) │ │
│ │ 配置文件 (config.toml) │ └────────────────────────┘ │
│ │ SQLite会话数据库 │ │
│ │ 定时任务队列 │ │
│ └────────────────────────────┘ │
│ │
│ SecretStore安全设计: │
│ • 密钥永远不会进入模型上下文、prompt或trace │
│ • 所有敏感信息存于本地加密存储 │
│ • 可完全离线使用(手动粘贴API Key) │
│ │
│ OAuth握手流程: │
│ 用户 → OAuth Provider → 中转服务 → 直接交给本机 (不在云端停留) │
│ 使用 Auth0 授权码 + PKCE 流程 │
└─────────────────────────────────────────────────────────────────────┘
10.2 为什么本地优先重要
在企服领域,数据主权是硬门槛。很多企业不敢用AI Agent,因为数据要上传到云端。OpenWorker的本地优先设计解决了这个问题:
- 模型调用直接走你的机器到LLM提供商,没有中间代理服务器
- 连接器Token直接交给本机,绝不在OpenWorker的服务器上停留
- 完全离线可用:手动粘贴API Key和凭证,不需要任何云服务
- OAuth中转服务使用Auth0授权码+PKCE,Token握手后直接交给本机
唯一的云组件是一个可选的OAuth中转服务,用于一键连接时的握手流程。Token直接交给本机,不在云端存储。
十一、与同类工具对比
OpenWorker 诞生的时间点(2026年7月),桌面AI Agent已经有不少选择。但它们的定位各不相同。
┌─────────────────────────────────────────────────────────────────────┐
│ 同类工具对比:定位决定设计 │
│ │
│ 工具 │ 目标用户 │ 核心场景 │ 运行方式 │ 模型 │
│ ───────────────────────────────────────────────────────────────── │
│ OpenWorker │ 知识工作者 │ 跨工具任务 │ 本地桌面 │ 任意 │
│ │ (全员) │ 成品交付 │ (Tauri) │ │
│ ───────────────────────────────────────────────────────────────── │
│ Claude Code │ 开发者 │ 代码编辑 │ 本地终端 │ Claude │
│ │ │ 仓库级操作 │ + VS Code │ 仅 │
│ ───────────────────────────────────────────────────────────────── │
│ Codex CLI │ 开发者 │ 代码编辑 │ 云端沙箱 │ OpenAI │
│ │ │ 沙箱执行 │ + 本地 │ 仅 │
│ ───────────────────────────────────────────────────────────────── │
│ DeepSeek Harness │ 开发者 │ 插件化 │ 本地CLI │ 任意 │
│ │ │ 一切皆插件 │ │ │
│ ───────────────────────────────────────────────────────────────── │
│ Hermes Agent │ 全栈自动 │ 内容流水线 │ CLI+消息 │ 任意 │
│ │ 化 │ 服务器运维 │ 平台驱动 │ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ OpenWorker 与 Claude Code / Codex 的核心差异: │
│ • Claude Code 擅长"改代码"——在仓库里读懂、修改、测试 │
│ • Codex 擅长"沙箱执行"——隔离环境中运行代码 │
│ • OpenWorker 擅长"填缝隙"——跨Jira+GitHub+Slack+日历 │
│ 拉通数据,合成文档,审批后执行 │
│ │
│ 它们不是竞争关系,而是面向不同场景的互补工具 │
└─────────────────────────────────────────────────────────────────────┘
详细对比表
| 维度 | OpenWorker | Claude Code | Codex CLI | DeepSeek Harness |
|---|---|---|---|---|
| 桌面GUI | ✅ Tauri原生 | ❌ 仅终端 | ❌ 终端+VS Code | ❌ 仅终端 |
| 模型无关 | ✅ 30+模型 | ❌ Anthropic仅 | ❌ OpenAI仅 | ✅ 任意 |
| 工具集成 | 25+连接器+MCP | MCP+文件系统+浏览器 | 沙箱+文件系统 | 一切皆插件 |
| 本地优先 | ✅ 全部本地 | ❌ | ❌ | ✅ |
| 审批门控 | ✅ 类型化风险系统 | ✅ 审批 | ✅ 审批 | ⚠️ 基本 |
| 定时任务 | ✅ 内置调度器 | ❌ | ❌ | ❌ |
| 语音输入 | ✅ Rust STT | ❌ | ❌ | ❌ |
| 开源协议 | MIT | 闭源 | Apache 2.0 | MIT |
| 主要定位 | 知识工作者跨工具任务 | 代码编辑 | 代码编辑 | 插件化Agent框架 |
十二、局限性
客观评价一个技术项目,必须看它的局限性。OpenWorker目前处于open beta阶段,以下问题值得注意:
12.1 生产环境需谨慎
OpenWorker虽然是MIT协议开源,但项目创建不到1个月(截至本文撰写时),社区生态仍处于早期阶段。虽然仓库已经有179个Issues和250个Pull Requests,但代码成熟度、文档完整性、社区支持都还在快速演进中。
12.2 Windows版无代码签名
Windows 10/11 x64版本可以下载使用,但由于尚未完成代码签名,Windows SmartScreen会弹出安全警告。对于企业用户来说,这是一个需要认真评估的风险点。macOS版本已签名并公证,自动更新功能正常。
12.3 无Linux桌面构建
目前只有macOS(Apple Silicon)和Windows(x64)的构建版本。Linux用户需要从源码构建,或者通过浏览器模式(npm run dev)运行UI。
12.4 集成权限需自行配置
虽然OpenWorker提供了25+连接器,但每个连接器的OAuth认证和权限范围需要用户自行配置。对于非技术用户来说,理解不同工具的权限模型(如Slack的Bot Token作用域、Google API的OAuth范围)可能有一定门槛。
12.5 本地小模型的任务拆解质量
OpenWorker的模型无关设计让它可以使用Ollama本地模型。但实际测试表明,本地小模型(如7B-13B参数级别)在任务拆解、工具调用规划等复杂场景下的质量明显下降。这是当前小模型能力的天花板,不是OpenWorker本身的问题,但用户需要了解这个限制。
十三、行业意义与展望
13.1 审批门控作为默认设计
OpenWorker最持久的贡献,可能是把审批门控做成了AI Agent的默认设计。
在OpenWorker之前,大多数Agent框架把安全当成事后补丁——先让Agent能干活,再考虑怎么防止它干坏事。OpenWorker反过来:安全是架构的第一公民。每个工具调用在注册时标注风险等级,五种运行模式定义不同场景下的行为,shell命令永远需要审批,无人值守不提升自主权。
这个设计哲学应该成为所有"能干活"的AI Agent的参考标准。没有闸门的Agent,能力越强越危险。
13.2 从"对话"到"交付"的范式转变
OpenWorker真正的创新,不是技术架构(虽然架构确实做得好),而是产品哲学的转变。
它重新定义了"AI完成一个任务"的标准——不是AI回复了一段话,而是AI交付了一个可用的成品。这个标准听起来简单,但它对产品设计、架构设计、安全设计都产生了深远的影响:
- 对产品设计:交互从"写Prompt"变成"描述目标成果"
- 对架构设计:需要连接器、审批门控、本地存储、文件系统集成
- 对安全设计:需要类型化风险系统、运行模式、驻留规则
13.3 本地优先 = 信任优先
在AI Agent的信任危机日益严重的今天(数据泄露、Prompt注入、权限滥用),OpenWorker的本地优先设计给出了一个清晰的答案:信任不是靠承诺,而是靠架构。当Agent循环、对话记录、连接器凭证、模型密钥全部存在本机,当模型调用直接走你的机器到LLM提供商,当唯一的云组件只是一个OAuth握手中转——信任就不再是一个需要"相信我们"的承诺,而是一个可以验证的事实。
十四、总结
OpenWorker 不是一个划时代的模型,也不是一个颠覆性的技术突破。它是一个把正确的事情做对了的产品——安全当架构设计、成品当交付标准、本地当默认部署、模型当可替换组件。
在AI Agent的"战国时代",OpenWorker可能不是最终的赢家。但它定义了一个新的基线:AI Agent应该交付成品,而不是对话;应该先问再干,而不是先干再问;应该跑在本机,而不是被锁在云端。
对于开发者来说,OpenWorker的代码是一个值得深入研究的参考实现——尤其是它的PermissionEngine、TurnEngine、ProviderRegistry和连接器框架。对于知识工作者来说,它是一个值得尝试的桌面AI同事——虽然还在beta阶段,但方向已经足够清晰。
Andrew Ng 和 Rohit Prasad 做了一个微妙但重要的选择:把AI从"聊天工具"升级为"执行工具"。 这个选择,可能比任何模型能力的提升都更能改变我们与AI的协作方式。
本文基于OpenWorker GitHub仓库(https://github.com/andrewyng/openworker)及公开技术文档撰写,代码片段为示意性实现,完整源码请参考仓库。