ETA 开源的智能体工作台

GROK BOT / MUSE 的开源替代 · 零锁定 · 100% BYOK

你的 AI,住在谁家? 你的机器,你的模型,你的数据。

Grok Bot 说每个智能体都有自己的电脑——在他们的机房里。Muse 接进你的邮箱和相册,Today 读你的日程和会议。它们都在别人家学会了懂你,也在别人家记着你。eta 是同一件事的开源版本:那个懂你的东西,住在你自己家。

$ git clone \
    https://github.com/0xfe-project/eta.git
$ cd eta
$ bun run setup
$ bun run dev

打开 http://127.0.0.1:3000 。数据在 ~/.eta,和装好之后是同一份。

一个数据目录装下全部:对话、记忆、任务、用量。拷走就是全部。

对话 · 维护者 空闲
  1. 你 结账页一直没有 e2e 测试。你来做,做完交回待验收。
  2. list_projects · list_files checkout/
  3. draft_task #57 · mac-mini · 新 worktree
  4. tasks.confirmDraft #57 → queued
  5. 维护者 好,#57 开了。我在 mac-mini 的新 worktree 里写,写完整理成 PR 交回待验收;合并等你点。
任务 看板
待办 1
#54 整理发布说明 维护者
进行中 1
#55 升级 Caddy 到 2.11.7 维护者
待验收 0
已完成 2
#57 结账页 e2e 测试 mac-mini
#56 订单导出 CSV linux-box
待处理
  • #57 结账页 e2e 测试 待验收 · PR #214 ← 点它继续回放 ✓ PR 已合并,任务已完成
  • 版本观察 定时 · 1 份新报告
  • #55 升级 Caddy 到 2.11.7 草稿 · 等你确认
事件 示例日志
  1. 09:00:00.002 INFO routine finished name=release-watch report=1
  2. 09:41:02.114 INFO turn started agent=maintainer
  3. 09:41:03.004 INFO list_files project=checkout paths=42
  4. 09:41:04.870 INFO task drafted id=57 machine=mac-mini workspace=worktree
  5. 09:41:05.032 INFO task confirmed id=57 status=todo
  6. 09:41:05.410 INFO run started task=57 machine=mac-mini branch=eta/task-57
  7. 09:52:40.006 INFO pr recorded task=57 pr=214 tool=open_pull_request
  8. 09:55:12.771 INFO comment added task=57 by=you
  9. 09:55:13.020 INFO run started task=57 run=2 reason=comment
  10. 10:01:03.090 INFO run finished task=57 run=2 pr=214 updated
  11. 10:01:03.512 INFO task status=in_review task=57 pending=+1
  12. 10:03:20.418 INFO pr merged pr=214 method=squash head=unchanged
  13. 10:03:20.950 INFO task done task=57 worktree=kept branch=kept
完成
12/12

会话回放 · 脚本数据 · 时间已压缩 · 不是实时 回放会停在需要你确认的地方,就像 eta 本身。

对照

Grok Bot、Muse、Today 都在做同一件事:住进你的生活,在你开口之前动手。差别只是谁接的应用更多。代价是同一个:那个懂你的东西,住在别人家。

为什么不直接用 Grok Bot

同一件事,两种所有权
Grok Bot / Muse / Today eta
住在哪 他们的机房。订阅停了,团队就散了 你的机器,你自己的机房
谁在答你 动态路由,你不知道这一次是谁在答 你定。100% BYOK:ChatGPT、Grok、Gemini,自己机房那台私有模型也算
记忆 一个黑箱,只能相信它记得对 一个 SQLite 文件,能查、能改、能删、能拷走
做了啥 给你一份总结 每一步都在时间线上:读了哪个文件、跑了哪条命令、改了哪一行
规则谁定 涨价、限流、封号,你只能接受 他们改不到你手上
能改吗 不能 源码全在

五条对立

  • 租来的 7×24,和你自己的 7×24。 它们按订阅卖你一支团队,停订就收走。eta 是你自己养的一支,装一次就一直在。

  • 它记得你。你记得它记得什么吗? 记忆就是你硬盘上的一个 SQLite 文件:能翻、能搜、能一条条删。删掉就是真的删掉。

  • 一个订阅,还是你自己的模型。 100% BYOK:订阅账号、API key、你自己机房那台私有模型,都是第一等公民。

  • 黑箱记忆,和一个文件。 断网、拔线、搬家,拷一个目录就跟着走。

  • 封号那天。 它们封号等于你的智能体消失。eta 没有这个开关——能按下开关的人只有你。

Claude Code 很好。问题是它不属于你。

写代码它最强,但它是一个租来的终端
Claude Code eta
会话 关上终端就散了 智能体常驻,记得上一轮,按点自己开工
模型 他们一家,涨价和限流由他们决定 100% BYOK,包括你机房那台
数据 会话和代码流进他们的服务 一个 SQLite 文件在你硬盘上,拷走就是全部

需要 100% 透明度和 100% 控制的人。

愿意把生活交给 AI,但不愿意把生活交给一家公司。

  • 公司不让代码出内网的人。

  • 被限流过、被悄悄换过模型、被封过号的人。

  • 要能对审计说清「这个智能体到底读过什么」的人。

是什么

eta 是一个常驻智能体的工作台。它跑在你自己的机器上,在浏览器里用,桌面和手机都行。它不是一次会话,是一支一直在的队伍。

  • 常驻智能体 每个有自己的名字、指令、人设卡和模型。重启不丢东西,记得上一轮说到哪。

  • 群聊与主理人 几个智能体在一个对话里,@ 到谁谁答;没有 @ 的时候交给主理人。

  • 任务看板 智能体提出任务,你确认,它在自己的会话里去做,做完交回待验收。

  • 定时任务 按计划跑一次短会话,留一份报告;没有新东西就不打扰你。

  • 记忆 记得以前说过什么,你能翻、能搜、能删。记忆是库里的行,不是它的心情。

  • 产物 智能体发布静态网页:报告、图表、原型。可预览、可分享、可撤回。

  • 自带模型 100% BYOK:订阅账号、API key、你机房那台私有模型,都算。

图 1. 个进程:没有集群,没有消息队列
1
图 2. 个 SQLite 文件装下对话、任务、记忆、用量和设置
1
图 3. 个必需的外部服务,不连外网也能开
0
图 4. 的源码在你手上,读得完
100%

智能体

智能体是你对话、交代任务的对象:名字、指令、人设卡、模型、准它用哪些环境包。它不是一次会话,也不是一台机器。

每个人的脸是它自己 id 长出来的:改名字不换脸,加一个智能体就多一张。上面这群是真的在呼吸。

一个一直存在的身份

全局配置

名字、指令、人设卡、模型和思考档都写在它身上。对话、任务、定时任务都用这份身份,不会每开一次就换一个人。

自己的会话

每个对话一个

每个(智能体 × 对话)一个会话,记得上一轮说到哪,重启后按同一条记录找回。会话在一个工作目录里跑。

群聊与主理人

互相唤醒 ≤ 5 次 / 条

一个群里放几个智能体,只有被 @ 的会回答;没 @ 时交给主理人。被唤醒的只收到它没看过的行,不重读整段历史。

记忆

一个 SQLite 文件

一轮开始前想起相关的事,结束后记下新的。翻、搜、加、删都在界面上;记忆就是数据目录里的一个 SQLite 文件。

产物

沙箱 · 可撤回

智能体发布静态网页:报告、图表、原型。任意版本可公开分享,撤回后永久失效。页面在不透明 origin 里跑,读不到 cookie。

改了不打断

下一轮生效

改名字、指令、模型或环境包,正在写的那一轮先写完;下一轮用新的。

状态

颜色只用来报信
空闲 没有在跑的回复
工作中 正在回复,或在用工具
等你确认 停在一条提问或审批上
模型不可用 需要重新登录或换 key;恢复后自动继续

任务

智能体在对话里提出任务,你确认后它去执行:在项目里开一个 worktree,做完交回待验收。你看到的是每一步,不是一句「已完成」。

一张任务卡上有

执行者
提出它的那个智能体,不转手
工作区
新 worktree(分支 eta/task-<编号>)或项目目录
来源对话
执行结果发回它被提出的地方
状态
积压、待办、进行中、待验收、已完成、受阻、已取消
评论
你在任务上说话,会把同一个会话再叫醒一次

写的人和审的人

派给一个智能体去做,再让它把结果交回来:任务上的每条评论都会把它叫醒一次,在同一个会话里接着干。没有任何东西会自己合并 PR,或自己把任务标成完成。

它手上的工具 任务里的会话
· list_tasks
· get_task
· update_task
· comment_task
# 汇报做完,任务交回待验收
· finish_task
# 合并是一次你来点的操作,不是它
· open_pull_request

状态

黄线处轮到你
  1. 01 积压
  2. 02 待办
  3. 03 进行中
  4. 04 待验收
  5. 05 已完成
  • 受阻
  • 已取消

会话在它所在的那台机器上、以你的用户身份跑,工具该有的权限它都有。只把专门用来干活的机器接进来。

定时

让一个东西按时去做一件事。它背后没有智能体,只有模型、一份简报和一个时钟。每次运行要么汇报,要么什么也不说。

计划

各带时区
每天 09:00 版本观察 · 看上游项目的发布说明
每周一 10:00 周报素材 · 汇总这一周的公开动态
每 6 小时 X 上的提及 · 搜关于你项目的讨论
0 */2 * * 1-5 自定义 cron · 工作日每两小时

要求

  1. v1 每天看上游的新版本,写一段摘要。
  2. v2 + 只报和自部署有关的变化。 ← 你的反馈
  3. v3 + 只看破坏性变更,附原文链接。 ← 你的反馈

交接说明

每次运行给下一次写交接说明,也能查以前的运行和报告。

反馈变成要求

你对报告说「只看破坏性变更」,它就合进新一版简报,下次起生效。旧版本可以恢复。

有边界

一次运行最多 15 分钟;同一时刻最多一次;错过的点不补跑——重启后从现在算起。

掌控

掌控感优先。智能体在做什么,你看得见、停得住、改得了。需要你点头的事,会一直等你。

确认请求 试试看
需要确认 · Bash 命令 维护者
$ git push origin fix/checkout-clock

规则 confirm-push · 来自环境包的 akhook.yml

✓ 已允许 · 仅这一条命令 · 10 分钟内有效 · 只用一次 ✕ 已拒绝 · 它被要求不许换个写法绕过去

命令、改文件、工具调用,都变成同一种卡片,在对话里或「待处理」里答复。等待没有时限。

工具调用规则 akhook
# 内置的那一层,随源码一起在
- id: gh-pr-create
  on: shell_exec
  checks:
    - argv: [gh, pr, create]
  message: 推送分支后用 open_pull_request 工具开 PR。

# 环境包里带的 akhook.yml
- id: confirm-push
  on: shell_exec
  action: ask
  checks:
    - argv: [git, push]
  message: 推送需要你确认。

每条 shell 命令、每次改文件,执行前都过一遍规则:要么拦下并告诉它该用什么,要么先问你。环境包能带自己的 akhook.yml,关不掉内置的那层。这是防手滑的护栏,不是安全边界。

待处理

标签和页面标题上有数字

所有等你的事集中在一处:

  • 提问与审批 来自所有会话
  • 待确认的任务草稿 它想干的事,还没开工
  • 待验收、受阻的任务 等你拍板
  • 失败的运行 最后一次没跑成
  • 定时任务 没看过的新报告和失败

随时插手

插入当前回复
它正在写的时候改方向
停止全部
一键停下群里所有回复
/compact
压缩上下文,接着聊

环境包

环境包是一份目录:技能、MCP 服务器、hook 规则,可选地还有一个服务。智能体勾上哪些,它在那台机器上的会话就带着哪些。不用重新打包,也不跑安装脚本。

内置环境包

按需给智能体勾选
github
以你的 GitHub App 身份使用 git、gh,开 PR、提 issue。默认不加载,--github 才挂上。
内置
grok
Grok Imagine 生成图片和视频,网页和 X 搜索。
内置
oomol
用 oo CLI 使用你在 OOMOL 里连接的应用:邮件、日历、文档。
内置
你自己写的
一个目录:plugin.json、mcp.json、skills/、akhook.yml。改了就生效,不用重启。
一个环境包 一个目录,改了就生效
my-bundle/
├── plugin.json       # name、description、requires、config、service
├── mcp.json          # stdio 或 streamable HTTP
├── skills/*/SKILL.md
└── akhook.yml        # 工具调用规则
技能
包的 skills/ 进会话的技能目录,harness 照常发现
MCP 服务器
mcp.json 是一个配置来源,stdio 进程在会话所在的机器上启动
hook 规则
akhook.yml 只对载入它的智能体的会话生效
配置和密钥
AES-256-GCM 存进 SQLite,只交给这个包自己的进程
来源
内置、git(锁到某个提交)、本地目录,热重载
服务
可选的回环子进程,由宿主代理

模型

模型用你自己的。三选一:用设备码绑 ChatGPT(Codex)或 Grok 的订阅账号、填 Google AI Studio 的 key、或者接任何 OpenAI 兼容的服务——包括你自己机房里那台。

模型账号 示意
  • ChatGPT work 可用
  • ChatGPT personal 暂时失败 · 只冷却这一个模型
  • xAI grok 需要重新登录
用量 按智能体 · 保留 30 天
维护者 5 小时 24 小时 7 天
设备码 不用复制粘贴密钥
WDJB - MJHT

在提供方的页面输入这串码,eta 就拿到了登录。

只冷却出错的那个

某个模型在某个账号上失败,只冷却那一个模型,整个账号照常用。

刷新不花额度

「刷新状态」检查每个账号,不消耗额度。

看得见状态

需要重新登录、额度用完、暂时失败,都写在账号旁边;恢复后自动继续。

私有模型也是一等公民

自己机房那台只要兼容 OpenAI 接口,就是一个正常模型,不是「兼容项」。

架构

全部东西在一个进程里:Web、产品数据、会话的运行、模型凭据。没有控制面,也没有消息队列。

你 浏览器 一条 WebSocket:/ws,或普通的 HTTP
WebSocket · 监听非回环地址时自动开令牌门
一个进程 apps/eta
Web
构建好的静态页,由它自己发出
产品
agents · conversations · tasks · artifacts · routines
SQLite
eta.sqlite:对话、任务、用量、设置
会话
本机会话直接在进程里跑
LLM 网关
一个 Go 库,用 ffi 加载:订阅账号与 key
↓ SQLite · 文件 ↔ /machines/connect
一个数据目录 $HOME/.eta,没有开关
  • eta.sqlite
  • sessions/ · agents/
  • worktrees/ · artifacts/
  • bundles/ · persona/
别的机器 eta-daemon,一个单文件
  • 会话在那台机器上跑
  • 密钥在本地生成,主 node 只存指纹
  • 只出站连接主 node
记忆 一个 SQLite 文件
  • 按对话取记忆
  • 零依赖,离线也能用
  • 在你自己的机器上
机器外
  • OpenAI · xAI(你的账号)
  • Google AI Studio
  • 你自己的模型服务
  • GitHub(可选)

一条消息怎么走

  1. 01 浏览器发一条消息进进程;消息和它要执行的事一起写进 SQLite。
  2. 02 会话在它所在的那台机器上跑,只带上它没看过的那些行。
  3. 03 模型请求经网关走你自己的账号或 key,不经过我们。
  4. 04 事件回到进程、推回浏览器,每一步的工具活动都留在时间线上。

边界

  • — 监听回环以外的地址时自动开令牌门;浏览器用 cookie,脚本用 Bearer。
  • — 预览和分享走带签名或一次性令牌的地址,页面在沙箱里,读不到 cookie。
  • — daemon 只认自己的设备凭据;主 node 从不持有它的私钥。

数据

对话、任务、记忆、产物、智能体的工作区,都在你自己的机器上,在一个目录里。离开这台机器的,只有你让它离开的。

留在机器上

■
  • 一个目录:$HOME/.eta
  • eta.sqlite:对话、任务、用量、设置、环境包的配置(加密)
  • sessions/、agents/、worktrees/、artifacts/:会话、工作区、产物
  • 记忆也是一个 SQLite 文件,在同一个目录里
  • 错误和日志留在你自己的机器上;不接外部服务就什么都不发
  • 删掉智能体,它写过的行留在时间线上

会出去

→
  • 模型请求 → 你绑的账号,或你填的 key
  • GitHub:只有你装了 App 的仓库,而且默认不加载
  • OOMOL:如果你配置了
  • git 来源的环境包:你去拉的那个仓库

先说清楚

  • 会话在机器上以你的用户身份跑,工具该有的权限它都有。
  • 规则和审批是护栏,不是安全边界。
  • 令牌门在监听非回环地址时自动开启;放在代理后面,别让端口裸着。
  • 环境包的配置加密保存,但加载它的进程读得到。

分享出去的页面

公开链接
绑定一个版本,撤回后永久失效
私有预览
签名地址,1 小时过期
沙箱
CSP sandbox、无 same-origin,读不到 cookie 和存储

部署

一台机器就够。一个进程、一个数据目录、一个 SQLite 文件。装之前先想清楚:数据放在哪台机器上。

  1. 01

    直接跑那个进程

    推荐
    git clone \
      https://github.com/0xfe-project/eta.git
    cd eta
    bun run setup
    bun run --cwd apps/web build
    HOME=/var/lib/eta bun apps/eta/src/main.ts \
      --web-dir apps/web/dist \
      --host 0.0.0.0 --port 8787

    数据目录固定是 $HOME/.eta,没有开关:换位置就换 HOME。systemd 里是 Environment=HOME=/var/lib/eta。

  2. 02

    它自己会把门关上

    令牌门

    监听回环以外的地址时,令牌门自动开启:除 /up 外每个请求都要令牌。浏览器打开一次 /?token=<令牌> 就种下 cookie(HttpOnly、SameSite=Strict、30 天),脚本用 Authorization: Bearer。放在反向代理后面时,把 --allow-origin 设成页面的外部地址。

  3. 03

    绑上你自己的模型

    设置 → 模型服务

    订阅账号(用设备码)、Google AI Studio 的 key、任何 OpenAI 兼容的服务,三选一,也可以同时来。私有部署的模型填地址和 key 就行。

  4. 04

    再加一台机器

    可选
    # 设置 → 机器 → 添加,复制它给出的命令
    eta-daemon setup <主 node 地址> <安装码>
    eta-daemon run
    eta-daemon install-service

    daemon 是一个单文件,不含 Web 和数据库:会话在那台机器上跑,产品数据和模型调用回到主 node。安装码一次性、24 小时过期,配对时你核对指纹后批准。install-service 把它注册成 systemd 用户服务或 launchd agent。

需要什么

机器
Linux(amd64 / arm64)或 macOS;Windows 走 WSL
运行方式
一个进程 + 自带的模型网关
数据
一个目录,默认 $HOME/.eta
不需要
Kubernetes、消息队列、对象存储

问答

eta 是什么?

一个常驻智能体的开源工作台,装在你自己的机器上:智能体有记忆、能用工具、按时间自己跑,需要你点头的事会停下来等你。Grok Bot、Muse、Today 做的是同一件事,只是它们住在别人的机房里。

有托管版吗?

没有。eta 跑在你的机器上,数据也在那台机器上。我们不碰你的会话,也不转售模型算力。

能几个人一起用吗?

不能。eta 是单用户服务,没有账号和角色。它给自己上锁的方式是令牌门:监听非回环地址时自动开启。

能用哪些模型?

随你。订阅账号(ChatGPT Codex、Grok)、Google AI Studio 的 key、任何 OpenAI 兼容的服务都行;私有部署的模型填地址和 key 就能用。模型是 provider:模型 id,默认的 provider 就是 Anthropic。

那和 Claude Code 比呢?

它是租来的一个终端:关掉就散了,模型只有他们一家,会话流进他们的服务。eta 是常驻的一支队伍,模型你自己带,数据在你自己硬盘上。写代码它很强,这一点不用争。

智能体会自己合并代码吗?

不会。任务交付的 PR 要你在任务页点合并;合并前核对 head 没变,按仓库允许的方式合并,不用管理员权限越过保护。GitHub 那条链默认不加载。

把别的电脑接进来安全吗?

daemon 只出站连接主 node,密钥在本地生成、主 node 只存指纹,配对要你核对后批准;安装码一次性、24 小时过期。会话在那台机器上以你的用户身份跑,所以只接专门干活的机器。

需要多大的机器?

一个进程,几百 MB 内存起步;每个会话再按模型和工具算。不用 Kubernetes,也不用消息队列。

数据存在哪?

一个目录,默认 $HOME/.eta:一个 SQLite 文件装对话、任务、记忆、用量和设置,会话是文件,产物也是文件。拷走这个目录就是全部。

原则

eta 的界面按这几条做。这一页也是。

  1. 01

    掌控感优先

    用的时候要感到事情在自己手里。冷静,只是为了不把人赶出这个状态。

  2. 02

    留在心流里

    目的是让人留在心流里,并且能停在事情里面。

  3. 03

    能删就删

    逐个检查界面元素并尝试移除。删掉后不影响理解和操作,就不保留。

  4. 04

    像仪表盘

    像汽车仪表盘一样利用空间。紧凑是尊重,不是拥挤。

  5. 05

    直角

    全局使用直角。颜色一律走变量,浅色和深色都支持。

  6. 06

    60ms linear

    交互过渡 60ms linear,不拖尾、不弹跳。尊重减少动态效果的设置。

  7. 07

    说人话

    智能体和技术部分用日常说法,让人知道发生了什么。不堆系统术语。

  8. 08

    做完

    精致指对齐、间距、状态、反馈和动效都做完,而不是换一层更「专业」的皮。

把智能体放到你自己的机器上。

看部署步骤 源码