跳到正文

Easel

一个给社媒创作者用的开源内容工作台:一个 agent 把整条链路跑完——聚合热榜、策划选题、生成文案与卡片与配音与视频、把成品发到七个平台上已经登录的账号,再把数据读回来,沉淀进那个决定了下一轮产出的账号画像里。

Screenshot of Easel
编辑截图, 1 Oct 2026Easel ↗

这是什么

Easel 是一个把社媒账号整条链路跑完的开源工作台,底座是 OpenClaw agent 运行时,交付形态是一条 Python 命令行加一个跑在 7860 端口上的 React 工作台。它的能力被组织成五个连续的阶段——发现、策划、创作、发布、归因——而它们是目录,不是菜单项:每个技能是一份不到两百行的 SKILL.md,旁边是按需加载的领域知识与可直接执行的脚本,于是卡片、配音、字幕、剪辑、短剧和论文解读都是一路写成 outputs/ 项目目录里的文件。一份六个维度的账号画像——定位、风格、受众、平台、偏好与红线、长期记忆——会被拼在每一轮对话前面,这正是输出会越来越贴近真实账号、而不是一股通用模型腔的原因。发布覆盖七个平台,每个平台在真发之前都有一道闸门;数据则经由同一套技能体系流回来,于是下一轮从上一轮量到的东西开始。

谁做的ZJU-REAL 是一个实验室账号,不是一个人:README 上挂着浙江大学、北京大学、REAL Lab 与 OpenDCAI Lab 的标志,并把这件事说成是把研究搬到真实社媒创作场景里的一次实践。210 次提交分布在十三个作者名上:其中 143 次来自一个 xiaohongshu.com 的邮箱,没有关联任何 GitHub 账号;账号 lidingm 署名 Dingming Li 的有 19 次;mengyuyuan 有 23 次。210 次里 65 次带关联账号,12 条带共同作者尾注,其中七条点名某个 Claude 模型——六条只写「Claude」,一条写 Opus 4.8 与百万 token 上下文。GitHub 列出的贡献者是十个人,仓库停在 2,633 个星、402 个 fork。

它是怎么搭起来的

组成 · 6

一个接管本机、把判断交给 agent 运行时的 Python 程序。正门是一条命令行;主要界面是一个 FastAPI 服务加一套 React 工作台,管理会话、素材、账号、画像、内容库与发布状态;两者身后是 agent 运行时——它读一个技能目录、调用其中写明的脚本,并把结果写进磁盘。这个分层解释了仓库的形状:智能住在散文与参考文档里,干活的是 Python 脚本,而状态住在文件里——每个内容项目一个 outputs/ 目录加一份隐藏 manifest,每个账号一个 profiles/ 目录,登录态、发布日志与分析数据放在下划线前缀的系统目录里。任何要碰外部世界的东西,要么过一道闸门(真发之前的出站内容检查),要么过一把锁(一个浏览器 profile 同一时刻只给一个进程用),因为一个无人看管的社媒 agent 的失败方式不是答错,而是做错。

easel/
Python 命令行:分发、一个 14,031 字节的环境自检工具与一个连通性自检、网关管理,以及三个让 agent 运行时变得可寻址的模块——一个按环境变量、配置文件或 profile 哈希解析网关主机、端口与 URL,一个定位运行时的二进制或其模块入口,一个把专用 profile 隔开、绝不碰机器上原有的安装。它自己有九个模块、44 KB,其下 commands/ 另有五个文件。
web/
工作台背后的 FastAPI 应用有 191,207 字节,接口覆盖设置、会话、素材、发布与统计;前端是二十三个组件加七个库模块——49,200 字节的能力菜单、29,109 字节的 API 客户端、12,490 字节的 store——以及 web/static/ 下预构建的页面,包括一个 76,045 字节的产品主页与一个 40,262 字节的工作台外壳。
skills/openclaw/
七百一十六个文件、12.9 MB,把能力做成目录而不是菜单。每个技能是一份不到两百行的 SKILL.md,只带名字、描述与所属流水线层,旁边是可选的 references/(领域知识,按需加载)与 scripts/(不进 prompt 的可执行工作),分属五个阶段与一个通用层。README 徽章写的是 113 个技能,而两个校验器在 pull request 陆续加东西的过程中报出过 114 与 115。
skills/shared/
五十五个文件的跨技能机件,也是与各平台的分界线所在:85,581 字节的多平台网页发布器、76,086 字节的抖音发布器、59,517 字节的多角色配音、48,656 字节的评论处理、48,135 字节的账号数据工具、47,674 字节的小红书发布器、42,058 字节的视频生成、41,251 字节的读回模块、36,706 字节的图片生成、33,769 字节的公众号读取器,以及 21,848 字节、被每个发布脚本调用的内容闸门。
docs/ 与 profiles/
六个文档:技能接口规范、24,646 字节的能力地图、一份讲提示词如何分层的说明、中英两份已知问题,以及 10,681 字节的致谢。profiles/_template/ 用七个文件定义一个账号——定位、风格、受众、平台、偏好、记忆,加一份 README——而一份画像可以同时用在多个已登录平台上。
scripts/ 与 tests/
十个脚本,含两个安装器(Shell 的 37,037 字节、PowerShell 的 24,259 字节)、两个技能校验器、一个产物迁移工具、一个会话修复脚本,以及两套系统的网关启动器;另有十三个测试文件,覆盖网关端点、工作区解析、web 安全规则、本地写防护、数据追踪器,以及那个需要按供应商分叉的图片客户端。

取舍,以及它替代了什么

  • 国内平台走浏览器自动化,海外平台走一个聚合 API 替代 给每个平台都接官方开发者 API

    维护者写明国内这一层走浏览器自动化或平台 CLI;而提出海外发布的那位贡献者写清了为什么不照做:TikTok、Instagram、YouTube、LinkedIn 等等每一个都要申请开发者应用、走 OAuth、过审核,逐个接入成本很高,于是原型把这些平台统一交给一个上传服务,不用浏览器也不用 cookie,并且是补充而非替换。

  • 结果不明就让它不明 替代 在服务端报错、超时或响应不可用之后重发

    因为重发一条比漏发一条更糟。请求标识同时充当幂等键,脚本拿它去查而不是重发;查不清的结果用自己的退出码结束,在日历里记为待确认,并且不进 publish-log。技能文档写明:不得再次运行发布命令,只能用 status 核对。

  • 修生产路径,而不是修测试夹具 替代 在那一个把配置写坏的测试文件里重定向 home 目录

    同一个 bug 上同时开着两条 pull request,第二条的作者把两条都摆了出来:一条只改测试夹具,另一条把 web 服务里五处直接读写配置的地方收口到本就有隔离变量的工作区模块,于是此后任何测试只要设一个变量就天然隔离。第二条被作为治本的方案提出,生产行为零变化;第一条则被描述为治标。

  • 宁可说某能力不可用,也不让它看起来可用 替代 把机器上探测到的每个命令行 agent 都算成能用

    探测模块把状态分成三种——已安装、有底座支持、免 key 可用——只有第三种才可以对用户宣称。运行时里没有对应后端的助手会被如实标为不支持并给出说明,理由正是作者写下的那句:宁可少说,也不能让人以为装了就能用。

  • 把网关地址解析出来,而不是钉死或打补丁 替代 写死文档里的默认端口,或者去改运行时

    运行时会给非默认 profile 分配一个由哈希得来的端口,这正是写死的地址出错的原因。修法是在一个模块里按运行时自己的优先级解析主机、端口与 URL,并且刻意既不钉死端口、也不给运行时打补丁,因为这个端口可能被占用、也可能由用户自己指定。七个文件里的十三处调用点被迁到了它上面。

  • 让预览框能跑脚本,但仍停在 opaque origin 上 替代 连 same-origin 一起放开,好让某一个按钮能用

    预览框原本禁用了脚本,于是里面那个「复制到平台」的按钮点了没反应。修法是放开脚本执行与剪贴板写入,但不给 same-origin,于是框里的脚本跑得起来,却依然够不到本站。同批的另一个修法是把本地图片内联进预览,因为粘贴出去的页面会让平台去抓一个它够不到的本地地址,正文图就此裂开。

依据docs/SKILL-SPEC.md(5,439 字符)、skills/openclaw/paper-explainer/references/slide-design.md(6,456)、skills/openclaw/xhs-note-creator/references/output-spec.md(1,315)、README.md(17,793 字符——报告打印了前 6,000 个,其余于 2026-10-01 取自默认分支),以及上文日志里引用到的各 pull request 与 issue 正文。

制作过程

6 个阶段
  1. 01

    三十四天、四个 release,以及一个大部分是视频的仓库

    仓库建于 2026-08-28 的 02:22 UTC,第 210 次提交落在 2026-10-01,整份记录只覆盖三十四天:八月剩下的三天里提交了 50 次,九月 159 次,十月一日一次。这段时间里出了四个 release——v0.1.0(2026-08-31)、v0.1.1(2026-09-14)、v0.2.0(2026-09-17,内置视频产线加三级转录)、v0.2.1(2026-09-24,设置面板、对话直连网关提速与安全加固)。周围是 2,633 个星、402 个 fork、14 个 watcher、24 个未关的 issue,许可为 Apache-2.0。文件树有 988 个文件、330 MB,而其中几乎没有代码:assets/readme/ 是 59 个文件、239 MB——九支完整成片(最大 57,580,062 字节),旁边配着六秒预览与缩略图;web/static/ 又添了同样性质的 206 MB,供产品主页使用。技能目录是 716 个文件、12.9 MB,共享脚本 55 个文件、1 MB,React 工作台 46 个文件、519 KB,测试 110 KB,Python 命令行 44 KB,文档 52 KB。210 次提交里 65 次带关联的 GitHub 账号,12 条带共同作者尾注,其中七条点名某个 Claude 模型,六条只写「Claude」。十三个作者名里,除开这两处,其余都在个位数:qywMichelle 六次、xiaojinlucky 四次,88lin、lanyinzly、zjg23edu 各三次,Dear47 两次,naivezip、wulinjuan、arctanxarc233 各一次。

  2. 02

    七个平台、两条接入路径,以及压在所有人之上的一个派发器

    README 说七个平台支持登录、适配与发布:小红书、抖音、快手、知乎、B 站、微信视频号、微信公众号。一位贡献者在提海外发布时说得很直白——国内这一层走的是浏览器自动化或平台 CLI——文件体积也对得上。小红书是一个 47,674 字节的脚本驱动 Playwright;有条 pull request 给浏览器 profile 加了跨进程锁文件,Windows 用 msvcrt,其他系统用 fcntl,因为后台的登录探测与前台正在扫码或发笔记的进程会同时去抢同一个目录,底层直接以 exit 21 崩掉;同一处改动还会优先使用 ~/.cloakbrowser 下的防检测浏览器内核,或用 EASEL_CLOAK_BROWSER 指定的内核,因为无头模式总是撞上平台的 300012 验证码。抖音是 76,086 字节,仓库里最大的单个脚本:有头登录时不许提前关掉那个窗口,而 keep-open 模式被明令禁止重新打开浏览器上下文,因为同一个 profile 被第二次占用会直接崩。B 站调用第三方上传工具 biliup,有一条 pull request 让 pip 带上 --prefer-binary,因为 biliup 1.2.11 在 PyPI 上只有源码包,而它的 Rust 依赖要求的编译器比报告者本机的新。微信公众号是唯一直接对接平台 API 的那个:正文里引用本地内容库的图片会被先走素材接口上传、换成 CDN 链接再建草稿,因为平台不会去抓一个本地路径。知乎、快手、视频号各有自己的技能;而在它们之上,publish_dispatch.py 持有平台注册表,把适配过的成片派发给对应的发布器。

  3. 03

    真发之前的那道闸门,以及结果不明时该怎么办

    任何把文本发到公开平台的脚本,都必须在真发之前调用出站内容闸门。这个闸门是一个 21,848 字节的模块,分两级。BLOCK 级是真·敏感信息——API key、内部 URL 与域名、代理地址、内部路径、带着真值的环境变量名——它会以退出码 7 终止发布,fail-closed。WARN 级是 AI 措辞或模型名,而在论文解读里这些很可能本来就是正常内容,所以只提醒不拦截。dry-run 只告警、从不拦截。发布本身默认就是 dry-run,真发要显式加参数。更有意思的决定是「结果真的不知道」时怎么办,而作者把第一版写错了、又在公开场合改正:只有明确的 400、401、403、422 才算确定被拒;5xx、超时、连接中断,或者 2xx 但响应体不可用,都绝不算失败。这些情况下脚本一律不重发,而是拿那个同时充当幂等键的请求 id 去查:查到就继续跟踪,查不到就把这次尝试记为「unknown」——退出码 4——在内容日历里记为待确认并把 id 写进备注,同时刻意不让它进 publish-log,免得后来有人把它当成失败又发一遍。技能文档把规则直接写死:出现 unknown 之后不得重跑发布命令,只能用 status 去核对。这套约定写在技能接口规范里,是发布与评论类技能的安全契约:任何把文本发到公开平台的脚本都照此接入,硬拦要放行必须显式传 --allow-unsafe。它还有自己的退出码——确定被拒是 3,结果不明是 4;作者在后来的更新里写明第一版把「提交结果不确定」处理错了,并补了十个离线用例,其中五个在改动前失败,两个校验脚本与全量 pytest 也一并跑过。发布层还有一个排期器(队列脚本 11,569 字节)与一个通知脚本(8,342 字节)。

  4. 04

    把数据读回来是另一套脚本,也有它自己的 bug

    归因层是闭环合上的地方,而它比这个词听起来要重:一个 48,135 字节的账号数据工具、一个 41,251 字节、认得各平台数据形状的读回模块、一个 33,769 字节的公众号读取器,外加一个数据追踪技能、一个带粉丝记录 schema 的发布分析技能、一个带 7,640 字节基准文件的表现复盘技能、一个带 4,084 字节打分脚本与 4,888 字节评分标准与 4,014 字节兜底基准表的帖子评分技能、一个跨帖聚合的内容复盘、一个评论洞察脚本,以及一份 publish-log。这些 bug 的样子很说明问题。有一条 pull request 里,作者报告小红书统计把草稿也算进去了:创作者中心默认停在「全部」标签页,脚本直接读那张列表,于是无标题草稿和写了一半的半成品被当成已发布笔记统计。修法是先点进「已发布」标签页,再过滤掉草稿标记、空标题和发布编辑页链接,并把指标文案规范成平台自己的说法。同一条 pull request 还保护了抖音的读回:有头登录在等扫码时,往状态文件里记一句人话提示;并禁止 keep-open 模式为了收数据重开上下文。这个闭环在 README 里的说法是读取播放、互动、评论与内容表现,把有效结构和偏好沉淀回账号画像。机制是一份 manifest 契约:每一层把自己产出的路径与一行摘要记进每个内容项目唯一的那份隐藏元数据文件,下游按路径去读上游的文件而不是重新推导;失败的步骤也会带着失败状态记下来,于是一次运行可以断点续跑,而不是从头再来。这些工具大多自带离线自检,数据追踪那条还有一份专门的 pytest 用例(tests/test_data_tracker.py),发布日志同样由脚本读写而不是手写。

  5. 05

    两个技能校验器,以及四个跟 agent 聪不聪明无关的 bug

    一个脚本逐目录检查每个技能——frontmatter、资源链接、产物与发布的安全契约。第二个脚本解析每份 SKILL.md 里写到的 Python 命令,拿它们和脚本自身的参数定义对账,专门抓文档与代码之间路径和参数的漂移。其上还有一个持续集成的 workflow,另有十三个测试文件,而每个发布脚本都自带离线自检,覆盖标题算法、选择器、锁竞争与锁释放。仓库自己的 bug 列表说明,出问题的都是管道,不是判断力。跑测试会把真实的配置文件覆盖成夹具值,于是 provider 目录和主模型引用失配,此后每次对话都报未知模型;而 web 服务里有五处直接读写那个文件,绕过了本来就有隔离机制的模块。网关地址在七个文件的十三处被写死成 18789,可专用 profile 实际监听的是 37289,于是环境自检在网关明明在跑、也在应答的时候打印失败。在 Windows 上,定位 agent 运行时的代码可能落在 npm 生成的 shim 文件上,而这种文件要经过命令解释器执行,于是一段多行消息在第一个换行处被截断:agent 只收到八十七个字符的画像前缀,其余全丢,对每条消息都回一句简短确认。修法是改去解析 shim 同级的运行时与模块入口,之后同一段 prompt 量到三百五十五个字符。还有安装脚本在严格错误处理下运行,而其中一步把 npm 配置命令的错误输出丢掉了,于是任何非零退出都会让整个安装停住,屏幕上只剩一个步骤计数。此外还有一处与工具链有关:B 站用的第三方上传工具在 PyPI 上只发了源码包,本地编译它需要更新的 Rust 编译器,于是安装脚本改用 --prefer-binary 去挑带 wheel 的旧版本。

  6. 06

    外面的贡献者、一个满了的微信群,以及关于某个平台的一句警告

    这段窗口里的三十个 issue 与 pull request,多数来自实验室之外,而且有几条相当有分量。一位贡献者一个人占了八条:设置面板保存 Claude 通道却从未真正生效、从端点拉取模型列表、探测本机已装的 agent 命令行并免 key 接入、把配置路径收口到统一入口让测试碰不到用户的真实文件、网络不通时自动选 npm 与 pip 镜像、安装器里剩下的静默失败,以及那个端点形状和标准不一样的图片供应商。另一位在同一周里提了四条:小红书发布、两处数据统计、公众号草稿里的图片,以及一次安全加固——收紧本地端口上的跨域访问、加 Host 检查防 DNS 重绑定、并禁止通过接口删除系统目录。还有两位各提两条,其中一条是聊天里的毛病:拼音打到一半敲回车就把消息发出去了;另一条是夜间模式开关。其余六条各来自一个账号。有一条新功能先按贡献指南开了提案 issue 再附代码,并写明方向不合适直接关掉即可。维护者的回复把支持引导到微信群里——列表里就有一条 issue,说群超过两百人进不去了——而在另一个帖子里,主要提交者建议贡献者用 working agent 之类的东西去调自己的问题,因为大家的机器和配置差别太大。公开的路线图有四项,而社区的 pull request 已经在打第二项「安装简易化」。最后是 README 自带的那句警告,它在发布文档里其实很少见:自动发布到小红书可能被平台检测到自动化操作,存在验证、限流或账号风控风险,所以建议先预览、做发布前检查、由用户确认后手动发布;其他平台则被描述为正常。那次加固还改了两处细节:带 Origin 的浏览器跨站写请求只要不在本地白名单里就一律 403 拦掉,不带 Origin 的本机脚本则要求对端必须是回环地址;跨域白名单本身被收紧到 7860、7870 与 5173 这三个本地端口。

相关档案

全部档案 →