OpenWorker 深度拆解:当吴恩达决定「给每个程序员造一个本地AI同事」——从 Local-First 架构到分档权限引擎,一个 12K Star 的桌面 Agent 如何重新定义人机协作的终极形态
引言:AI Agent 的战场正在从「对话框」转向「工作台」
2026年的AI工具赛道,正在发生一次深刻的范式转移。
如果2025年我们还在比谁的聊天机器人更会接话、谁的对话框更丝滑,那么2026年的竞争维度已经完全换轨——不再是「谁更会说话」,而是「谁真的能把活干完」。
这个趋势最直观的体现是:桌面端AI Agent赛道在7月底迎来了一次集体爆发。不同AI推出的OpenWork以1.7万Star领跑,而吴恩达(Andrew Ng)亲自下场的OpenWorker在7月底公开测试后,Star数从5K一路飙升至1.2万,一周之内冲上桌面Agent顶流。
OpenWorker最核心的差异点在于:它不是一个聊天机器人,而是一个交付成果的本地AI同事。
你告诉它「准备明天和客户的会议简报」,它自己拆步骤、调工具,最后交给你一份能直接用的文件——不是一段需要你再加工的对话。更关键的是,凡是会留后果的动作(发消息、改日程、执行命令),它都先举手等你点头。
这不是Demo级别的玩具,而是吴恩达团队用了Python 3.10+、FastAPI、Tauri 2、React 18等生产级技术栈,写了约3.24万行Python代码和149个TypeScript/TSX文件,外加78个后端测试模块搭建的完整工程产品。
下面我们从架构到代码,把这间「本地工作室」拆个底朝天。
一、背景:为什么是吴恩达?为什么是Local-First?
1.1 创始团队:画蓝图的人 + 落地的人
一款开源项目刚发布就万人空巷,背后两个人的履历给了它天然的信任底盘。
吴恩达(Andrew Ng) 是AI圈公认的教父级人物:DeepLearning.AI创始人、AI Fund管理合伙人、斯坦福教授、Amazon董事会成员。他在AI教育和投资领域深耕十余年,从Coursera到Google Brain再到Landing AI,几乎每一个AI浪潮都有他的身影。
Rohit Prasad 的名气没那么响,但工程份量不轻:前Amazon Alexa首席科学家,后来升任Amazon AGI(通用人工智能)团队负责人,直接向CEO Andy Jassy汇报。他是真正在十亿级设备上做过Agent交互的工程老将。
一个画蓝图,一个把蓝图落成能跑的系统——这个组合本身就说明OpenWorker不是周末玩具。
1.2 Local-First:为什么要把AI关在本地?
2026年AI工具面临的最大信任危机,不是模型不够聪明,而是数据去哪了。
当你把公司财务报表丢给一个云端AI助手整理,当你让它帮你发一封邮件,当你让它调用CRM系统读取客户数据——这些数据经过了哪些中间节点?有没有被缓存?有没有被用于训练?出了事谁负责?
这些问题在2026年变得异常尖锐。欧盟AI法案已经生效,企业对数据主权的要求越来越严格,而云端AI Agent天然面临着数据过境的风险。
OpenWorker的解法很简单:把整间工作室建在你本机,推理可以走本地Ollama,数据永远不离你的硬盘。 唯一上云的是可选的OAuth握手中转,而且手贴凭证就能绕开。
这不是「噱头式隐私」,而是架构级的承诺。
二、四层架构拆解:一间本地工作室的完整蓝图
OpenWorker的技术架构可以压成四层,全部默认跑在你本机。我们借用一间工作室的比喻来拆解——L1到L4各司其职:
2.1 L1:门面与前台(Tauri 2 + React 18)
Tauri 是原生容器,React 负责画面。它做的是「前台」的活:
- 承载会话界面,让你和AI同事对话
- 把需要你签字的待批单据亮出来
- 收着无人值守时的信箱(你不在时,AI把需要审批的任务存起来)
- 监控里面的主理人进程有没有失控(否则就出现「你关了窗,里面的循环还在偷偷跑」的尴尬)
技术选型上,Tauri 2相比Electron的优势非常明显:原生Rust后端,打包体积只有Electron的1/10,内存占用低3-5倍。对于一个需要长期驻留桌面的Agent应用来说,这个选择非常务实。
# Tauri 2 + React 18 的项目结构
openworker/
├── surfaces/gui/ # L1 前端
│ ├── src/
│ │ ├── components/ # React组件
│ │ ├── hooks/ # React Hooks
│ │ └── App.tsx
│ ├── package.json # 149个TypeScript/TSX文件
│ └── tauri.conf.json # Tauri配置
├── coworker/ # L2 后端
│ ├── risk.py # 危险等级分类器
│ ├── permissions.py # 权限裁决引擎
│ ├── agent.py # 主理人核心逻辑
│ ├── tools/ # 工具层
│ └── __init__.py # 119个Python文件,约3.24万行
└── tests/ # 78个后端测试模块
2.2 L2:主理人(FastAPI + aisuite)
这是工作室真正动脑子的一层,用Python写,FastAPI + uvicorn,默认绑定在 127.0.0.1:8765。
它建立在吴恩达的另一个开源项目 aisuite 之上——aisuite提供跨厂商的统一chat-completions接口,外加Agent层、工具层和MCP支持。OpenWorker的README说得很直白:想自己搭Agent框架,先看aisuite。
L2负责的事情包括:
- 任务拆解:把一句模糊的需求拆成可执行的步骤序列
- 多轮循环:模型↔工具的多轮交互,直到任务完成
- 工具调用:协调L3去碰真实世界
- 权限审核:每次操作都过一遍权限关卡
- 工作记录:保存完整的transcript,方便复盘
# L2 核心循环的简化示意
import asyncio
from fastapi import FastAPI
from aisuite import Client
app = FastAPI()
client = Client() # aisuite统一接口
async def agent_loop(task: str, context: dict):
"""主理人的核心工作循环"""
messages = [{"role": "user", "content": task}]
while True:
# 1. 调用模型思考下一步
response = client.chat.completions.create(
model="anthropic/claude-sonnet-4-20250514",
messages=messages,
tools=get_available_tools(), # L3提供的工具列表
tool_choice="auto"
)
# 2. 检查是否有工具调用
if not response.choices[0].message.tool_calls:
return response.choices[0].message.content
# 3. 执行工具调用(每一步都过权限关)
for tool_call in response.choices[0].message.tool_calls:
# 先过权限审核
risk_level = assess_risk(tool_call) # risk.py
decision = check_permission(risk_level, tool_call) # permissions.py
if decision == "deny":
messages.append(tool_call_result(
tool_call.id, "权限不足,操作被拒绝"
))
continue
elif decision == "approve_required":
# 推送到L1等待用户审批
await request_user_approval(tool_call)
continue
# 执行工具
result = await execute_tool(tool_call)
messages.append(tool_call_result(tool_call.id, result))
2.3 L3:跑腿与外接线路
主理人自己不伸手,得靠这一层去碰真实世界。分成两大类:
本机手脚:
- 文件系统读写
- Git操作
- ripgrep搜索
- 终端命令执行
外接线路(25+个):
GitHub、Slack、Jira、Notion、Linear、HubSpot、Outlook、monday.com、Gmail、Google Calendar……不够再接任意MCP工具。
官方的扩展哲学很清楚:优先拉外接线路、接MCP,而不是去改主理人的工作方式本身,这样工作室才好维护。
# L3 工具层示例
from typing import Callable, Dict
class ToolRegistry:
"""工具注册中心"""
def __init__(self):
self._tools: Dict[str, Callable] = {}
self._risk_levels: Dict[str, str] = {}
def register(self, name: str, func: Callable, risk_level: str = "read"):
"""
注册工具
risk_level:
- read: 只读,永远放行
- write_local: 写本机工作区,需路径校验
- exec: 执行命令,默认需签字
- external: 出本机,需审批
"""
self._tools[name] = func
self._risk_levels[name] = risk_level
def get_tool_schemas(self):
"""返回符合OpenAI格式的工具定义"""
return [
{
"type": "function",
"function": {
"name": name,
"description": func.__doc__,
"parameters": get_schema(func)
}
}
for name, func in self._tools.items()
]
# 注册工具
registry = ToolRegistry()
registry.register("read_file", read_file, risk_level="read")
registry.register("write_file", write_file, risk_level="write_local")
registry.register("run_command", run_command, risk_level="exec")
registry.register("send_slack", send_slack_message, risk_level="external")
registry.register("update_calendar", update_calendar_event, risk_level="external")
registry.register("search_codebase", ripgrep_search, risk_level="read")
2.4 L4:背后的智库
经aisuite把L2的请求转给配置好的模型或本机Ollama。预置30个验证过适合工具调用的模型,覆盖:
| 厂商 | 模型 |
|---|---|
| OpenAI | GPT-4o, GPT-4.1, o3 |
| Anthropic | Claude Sonnet 4, Claude Opus 4 |
| Gemini 2.5 Pro, Gemini 2.5 Flash | |
| 国产 | DeepSeek V4, Kimi K3, Qwen3.8, GLM-5 |
| 本地 | Ollama全系列 |
v0.1.7又加进了AWS Bedrock、Google Vertex AI、OpenRouter、Meta Muse Spark。
OpenWorker不抽成、不托管推理——换的是智库,门面和工作方式不变。你可以随时把Claude换成DeepSeek,整个工作室的操作方式不变。
三、权限引擎深度剖析:给Agent配一把「分档钥匙」
这是OpenWorker最值得单独拿出来说的设计,也是它区别于其他桌面Agent的核心竞争力。
3.1 问题:多数Agent的权限管理为什么是灾难?
大多数桌面Agent处理权限的方式非常粗暴:要么全放(用户提心吊胆),要么每步都弹窗(用户烦到关闭)。
更深层的问题是:很多框架把「在不在场」和「能干什么」混为一谈。你让Agent在后台跑一个定时任务,人一走,它就默认权限变大了——这就像请假条因为你出差了就自动生效一样荒谬。
3.2 OpenWorker的解法:四档危险等级 + 五种授权模式
OpenWorker在代码层面实现了一套分档权限机制,核心在两个文件:
coworker/risk.py:给每次动作贴危险标签coworker/permissions.py:按模式给出「放行/需人审/拒绝」的裁决
四档「危险等级牌」:
| 等级 | 含义 | 示例 |
|---|---|---|
read | 看了看,没留痕迹,永远放行 | 不写不发的只读工具 |
write_local | 改本机工作区,且路径得在允许圈内 | 写文件、打补丁 |
exec | 跑命令 | 执行shell(默认长期要签字) |
external | 东西出了本机,可挂「常驻规则」 | 连外部应用/需审批的动作 |
五种「授权尺度」(Mode):
| Mode | 行为 |
|---|---|
discuss | 纯旁观,只分析不动手 |
plan | 规划模式,带规划契约 |
interactive(默认) | 读自动过,写/命令/外发都要你确认 |
auto | 全放行,但仍画着路径圈,出圈不行 |
custom | 你点名哪些工具自动过 |
3.3 源码解析:risk.py 的危险分类逻辑
# coworker/risk.py - 危险等级分类器
from enum import Enum
from typing import Any
class RiskLevel(Enum):
"""四档危险等级"""
READ = "read" # 只读,永远放行
WRITE_LOCAL = "write_local" # 写本地,需路径校验
EXEC = "exec" # 执行命令,默认需签字
EXTERNAL = "external" # 外发,需审批
def assess_risk(tool_name: str, arguments: dict) -> RiskLevel:
"""
根据工具名称和参数,判断本次操作的危险等级
核心逻辑:不是按工具名称硬编码,而是按操作语义分类
"""
# 只读工具:永远安全
READ_TOOLS = {
"read_file", "search_codebase", "list_directory",
"git_log", "git_diff", "ripgrep_search"
}
if tool_name in READ_TOOLS:
return RiskLevel.READ
# 写操作:检查是否在允许路径内
WRITE_TOOLS = {"write_file", "edit_file", "create_directory"}
if tool_name in WRITE_TOOLS:
path = arguments.get("path", "")
if is_within_allowed_paths(path): # 校验路径
return RiskLevel.WRITE_LOCAL
else:
# 路径越界,升级为external(出圈了)
return RiskLevel.EXTERNAL
# 命令执行:始终需签字
EXEC_TOOLS = {"run_command", "execute_shell", "git_push"}
if tool_name in EXEC_TOOLS:
# 检查是否包含危险操作符
command = arguments.get("command", "")
if contains_dangerous_operators(command): # ; & | > 等
return RiskLevel.EXTERNAL # 高危命令,必须审批
return RiskLevel.EXCEL
# 外部操作:发消息、改日历等
EXTERNAL_TOOLS = {
"send_slack", "send_email", "update_calendar",
"create_jira_ticket", "post_to_notion"
}
if tool_name in EXTERNAL_TOOLS:
return RiskLevel.EXTERNAL
# 未知工具:默认external,保守处理
return RiskLevel.EXTERNAL
def contains_dangerous_operators(command: str) -> bool:
"""检测命令中是否包含危险操作符"""
dangerous = [';', '&', '|', '>', '>>', '&&', '||',
'rm -rf', 'sudo', 'chmod 777']
return any(op in command for op in dangerous)
3.4 源码解析:permissions.py 的裁决逻辑
# coworker/permissions.py - 权限裁决引擎
from enum import Enum
from typing import Optional
class PermissionMode(Enum):
DISCUSS = "discuss" # 纯旁观
PLAN = "plan" # 规划模式
INTERACTIVE = "interactive" # 默认:敏感操作需确认
AUTO = "auto" # 全放行(仍在路径圈内)
CUSTOM = "custom" # 自定义白名单
class PermissionDecision(Enum):
ALLOW = "allow" # 直接放行
APPROVE_REQUIRED = "approve_required" # 需用户审批
DENY = "deny" # 直接拒绝
def check_permission(
risk_level: RiskLevel,
tool_call: dict,
mode: PermissionMode = PermissionMode.INTERACTIVE,
custom_whitelist: list = None
) -> PermissionDecision:
"""
根据危险等级和当前模式,裁决是否放行
关键设计:
1. 你不在场 ≠ 权限更大(人走了只是找你的方式变了)
2. 「常驻规则」只管外发,电闸永远要你亲手合
3. shell的放行名单会拒绝带 ; & | > 的命令
"""
# 纯旁观模式:什么都不让干
if mode == PermissionMode.DISCUSS:
if risk_level == RiskLevel.READ:
return PermissionDecision.ALLOW
return PermissionDecision.DENY
# 规划模式:只读+规划
if mode == PermissionMode.PLAN:
if risk_level in (RiskLevel.READ, RiskLevel.WRITE_LOCAL):
return PermissionDecision.ALLOW
return PermissionDecision.APPROVE_REQUIRED
# 自定义模式:检查白名单
if mode == PermissionMode.CUSTOM:
if tool_call["function"]["name"] in (custom_whitelist or []):
return PermissionDecision.ALLOW
# 白名单外的,按interactive处理
# auto模式:全放行,但仍检查路径
if mode == PermissionMode.AUTO:
if risk_level == RiskLevel.EXTERNAL:
# 外发操作即使auto也要检查常驻规则
if has_standing_rule(tool_call):
return PermissionDecision.ALLOW
return PermissionDecision.APPROVE_REQUIRED
return PermissionDecision.ALLOW
# interactive模式(默认):按危险等级分档
# mode == PermissionMode.INTERACTIVE
if risk_level == RiskLevel.READ:
return PermissionDecision.ALLOW # 读:永远放行
if risk_level == RiskLevel.WRITE_LOCAL:
return PermissionDecision.ALLOW # 写本地:路径校验通过即放行
if risk_level == RiskLevel.EXEC:
return PermissionDecision.APPROVE_REQUIRED # 命令执行:必须签字
if risk_level == RiskLevel.EXTERNAL:
return PermissionDecision.APPROVE_REQUIRED # 外发:必须签字
# 兜底:未知等级,保守拒绝
return PermissionDecision.DENY
3.5 三个反直觉但关键的设计决策
决策一:你不在场,不等于它权限更大。
你让工作室后台跑个定时任务(比如每天自动整理简报),遇到要签字的动作,它不会自作主张,也不会跳步,而是把单据塞进信箱、把这次会话挂起,等你回来批。
大多数框架把「在不在人」和「能干什么」混为一谈——你一走,它就默认能多干。OpenWorker把两件事拆开:你不在只改变它「找你的方式」,不改变它能动的边界。
决策二:「常驻规则」只管外发,电闸永远要你亲手合。
「一次批准」不会蔓延成「永久放行全世界」。而且shell的放行名单会拒绝带 ; & | > 的命令——本质就是所有会改、会发、会写的敏感动作,执行前强制等你签字。
决策三:未知工具默认external,不是默认allow。
当一个新工具被注册但没有被分类到任何一个已知类别时,OpenWorker不会放行,而是把它标记为EXTERNAL,走审批流程。这是典型的「默认拒绝」安全策略。
四、实战:从安装到跑通一个完整任务
4.1 三种上手路径
路径一:下载桌面客户端(最省事)
去 openworker.com 下载,支持Apple Silicon Mac和Windows 10/11。Mac已签名公证、支持自动更新;Windows包有但未完成代码签名,安装会弹SmartScreen警告。暂无Linux版,暂无中文界面。
路径二:从源码跑(想改造框架的开发者)
# 环境要求:Python 3.10+, Node 20+, Rust工具链
git clone https://github.com/andrewyng/openworker
cd openworker
# 初始化环境
bash packaging/setup_dev_env.sh
# 启动本地主理人(L2)
.venv/bin/openworker-server --cwd ~/your/project --port 8765
# 启动门面(L1)
cd surfaces/gui && npm install && npm run dev
# 要完整桌面版(Tauri),替换最后一步
npm run tauri dev
路径三:通过MCP接入现有工具流
# 在Codex/Claude Code中接入OpenWork的MCP服务
codex mcp add openwork --url https://api.openworklabs.com/mcp/agent
codex mcp login openwork
4.2 端到端任务示例:准备会议简报
假设你是一个销售,明天要和客户开会,让OpenWorker帮你准备简报:
# 任务描述(在L1界面输入)
task = """
准备明天和ABC公司的会议简报:
1. 从本地CRM文件读取上次沟通记录
2. 从HubSpot拉取最新的客户交互数据
3. 从日历获取明天的会议时间、参会人
4. 整合成一份Markdown格式的会议简报
5. 通过Slack发送给我
"""
# OpenWorker的执行流程:
# Step 1: L2拆解任务
# → 读取本地文件 [风险: read → 自动放行]
# → 调用HubSpot API [风险: external → 需审批]
# → 调用Calendar API [风险: external → 需审批]
# → 写入本地文件 [风险: write_local → 放行]
# → 发送Slack消息 [风险: external → 需审批]
# Step 2: L2执行只读操作
# ✓ 读取本地CRM文件 → 成功
# → HubSpot API调用 → 弹窗等待审批
# → 用户点击「批准」
# ✓ 拉取HubSpot数据 → 成功
# Step 3: 生成简报
# ✓ 写入会议简报.md → 成功
# → 发送Slack消息 → 弹窗等待审批
# → 用户点击「批准」
# ✓ Slack消息已发送
# 最终交付:一份Markdown简报 + Slack通知
4.3 定时任务场景
OpenWorker还支持无人值守的定时任务,但权限机制依然严格:
# 定时任务配置
scheduled_task = {
"name": "每日简报",
"schedule": "0 8 * * 1-5", # 工作日早8点
"task": "整理昨日GitHub Trending前10项目,生成简报发到Slack",
"permission_mode": "interactive"
}
# 执行流程:
# 1. 定时触发 → L2启动
# 2. 读取GitHub Trending [read → 自动放行]
# 3. 生成简报 [write_local → 放行]
# 4. 发送到Slack [external → 用户不在?]
# → 不是「因为人不在所以放行」
# → 而是「单据塞进信箱,等用户回来批」
# 5. 用户上线后看到待审批通知,点击批准
# 6. Slack消息发送
五、性能优化与生产级考量
5.1 本地推理的性能权衡
OpenWorker支持Ollama本地推理,这意味着你可以完全离线使用。但本地推理的性能取决于你的硬件:
| 硬件配置 | 推荐模型 | 响应延迟 |
|---|---|---|
| MacBook M2 16GB | Qwen2.5-7B-Q4 | 2-5秒 |
| RTX 4090 24GB | DeepSeek V4-7B-Q8 | 1-3秒 |
| RTX 3060 12GB | Qwen2.5-3B-Q4 | 1-2秒 |
| 无GPU | Phi-3-mini-Q4 | 5-15秒 |
对于需要复杂推理的任务(多步规划、代码生成),建议使用云端API;对于简单任务(文件读取、格式转换),本地模型足够。
5.2 多Session并行
与Claude Code等终端工具不同,OpenWorker支持多Session并行。你可以在一个窗口处理客户简报,在另一个窗口让它整理代码review,互不干扰。
底层实现上,每个Session有独立的agent_loop实例,共享同一个ToolRegistry,但各自维护独立的messages上下文和权限状态。
5.3 安全审计日志
所有操作都有完整的transcript记录,包括:
- 每次模型调用的输入输出
- 每次工具调用的参数和结果
- 每次权限裁决的决策过程
- 用户的每次审批操作
这对于企业合规审计非常有价值——你可以清楚地看到Agent在什么时候做了什么,为什么这么做,是否经过了授权。
六、竞品对比:桌面Agent赛道的四条路线
桌面办公Agent已经分出四条路线,把OpenWorker和正面竞品摆一起:
| 维度 | OpenWorker | GPT Work (OpenAI) | OpenWork (Different AI) | 腾讯Marvis |
|---|---|---|---|---|
| 开源协议 | MIT,可商用 | 闭源 | 开源 | 闭源 |
| 部署方式 | 本地优先 | 云端优先 | 本地+MCP | 本地读文件 |
| 模型自由度 | BYOM/Ollama | 强绑OpenAI | 50+模型 | 绑腾讯生态 |
| 可私有化 | ✅ | ❌ | ✅ | ❌ |
| 连接器 | 25+ + MCP | 本地文件 | MCP工具 | 腾讯生态内 |
| 权限粒度 | 四档分层 | 粗粒度 | 中等 | 中等 |
路线对立很清楚:
- GPT Work 是「豪华但只能进特定商场的会员制服务」,闭源云端
- OpenWork 基于OpenCode,走的是「共享能力」的路线,多个Agent共用Skills和MCP工具
- OpenWorker 走的是「本地交付」的路线,一个人一个工作室,权限精细到代码级
七、局限性与适用场景
7.1 它现在适合谁?
- ✅ 重视办公数据隐私的个人和小团队
- ✅ 常用海外SaaS(Slack/Jira/HubSpot等)
- ✅ 想自己改造AI工作流的开发者
- ✅ 需要可审计权限机制的企业场景
7.2 它现在不适合谁?
- ❌ 需要无人值守全自动化的场景(权限机制决定了必须有人审)
- ❌ 对中文支持要求高的用户(暂无中文界面)
- ❌ Linux用户(暂无Linux版本)
- ❌ 需要企业级SLA保障的生产环境(仍是Open Beta)
7.3 冷静一下:几个需要注意的点
- 仍是Open Beta:边角在快速打磨,PR可能和内部路线图冲突
- 模型成本自理:模型密钥运维在你自己肩上,模型与SaaS的协议独立于MIT
- 无官方task benchmark:质量绑定你选的模型
- OAuth云握手不在「本仓100%开源」叙事里:敏感环境优先手贴凭证
八、总结展望:本地AI Agent的未来
OpenWorker的价值,是把「本地优先、开源可私有化、模型自由」这套能力,封装成普通人能开的「工作室」——而且用一套真正能审计、能fork的权限引擎,把「Agent安全」从口号做成了代码里的分档钥匙。
从更大的视角看,OpenWorker代表了AI Agent发展的一个重要方向:从云端回归本地,从对话走向交付,从黑盒走向可审计。
当越来越多的企业开始关注数据主权,当AI Agent从Demo走向生产,OpenWorker这样的Local-First方案会变得越来越重要。它不是要替代Claude Code或Cursor——那些是写代码的专家;它是要替代你的「行政助手」和「流程自动化脚本」——那些帮你整理资料、发消息、跑流程的日常琐事。
对于想脱离大厂封闭生态、又要真把活干完的人,它确实给了条新路。
项目信息:
- GitHub: https://github.com/andrewyng/openworker
- 官网: https://openworker.com
- 协议: MIT
- 技术栈: Python + FastAPI + Tauri 2 + React 18
- 模型支持: 30+预置模型 + Ollama本地 + 任意OpenAI兼容API
- 连接器: 25+预置 + MCP扩展