M1 项目本体 · L02

第一条消息的旅程:easel skill 到底做了什么

45 min · 精读 easel/cli.py(207行) + commands/skill.py(171行) + openclaw_cmd.py(63行) + timeouts.py(13行)

🎒 预备知识:完成上一课「L01」(零基础入口:第 0 课)

本课唯一结论:CLI 是薄壳——easel skill 全部工作只是 拼一条消息发给 agent。真正读 SKILL.md、调脚本、产出文件的是 OpenClaw 里的 agent。 理解这一点,三入口(chat/web/skill)的代码就都通了。

一、基础:为什么 CLI 可以这么薄(薄壳的架构理由)

直觉上 CLI 应该"执行"用户要的事——但 Easel 的 CLI 刻意不做任何执行。 原因在 L01 的发现:Easel 没有 loop、没有模型调用代码,执行主体是 gateway 里的 agent。 CLI 的全部价值是三件事:选画像 → 拼消息 → 交给 agent。薄壳不是偷懒, 是 A17 原则的体现:入口层(业务层的一部分)保持 harness 无关,重活全在 harness 层。

薄壳带来一个漂亮的推论:三个入口(chat/web/skill)行为必然一致—— 因为它们拼的是同一种消息、走同一个 agent。如果 CLI 自己实现一套执行逻辑, 三入口就会各自漂移(超时不一样、画像注入不一样)——Easel 历史上真出过这问题 (timeouts.py 的 docstring 自述:此前三处各写各的,skill=900s、web=300s、cli=7200s, 同一任务经不同入口被不同超时掐断)。收敛到单一真相源后才一致:三个入口按档位取值——制作层 PRODUCE=7200s、直接执行 DIRECT=300s、chat 沿用 PRODUCE。

二、调用链全景(逐行走读)

easel skill social-content -i "写条微博" -p 我的账号
 └ easel/commands/skill.py
    ① 找 skills/openclaw/social-content/SKILL.md(自动补 skill- 前缀)
    ② _resolve_input:文本直接传;图片/音视频只传文件路径
    ③ 拼消息 = persona_prefix(画像) + "请执行 /social-content" + 输入
    ④ openclaw --profile easel agent
         --session-key agent:main:skill-<毫秒时间戳>
         --timeout 7200                # 秒;timeouts.py TIMEOUT_PRODUCE
    ⑤ stdout 过滤 ANSI 色码和日志行,打印 agent 输出

逐站细看:① 找不到时自动补 skill- 前缀——用户体验细节 (用户记不住哪些技能带前缀),一行代码消除一类"技能不存在"错误。 ② 二进制文件(图/音视频)不塞进消息——只传路径, agent 自己按路径读文件(消息是文本协议,塞 base64 是窗口杀手,L2)。③ 画像注入用 L05 的 persona_prefix——三入口共用同一函数(单一真相源again)。④ session-key 带毫秒时间戳:每次 easel skill 是独立会话,不与 chat 混。⑤ 过滤 ANSI 色码——gateway 的输出带终端色码,直接透传会污染非终端场景。

三、三个"为什么"(精读时找答案)

① 为什么 openclaw_cmd.py 优先 [node, /path/to/openclaw.mjs] 而不是 PATH 里的可执行文件?
Windows 的 openclaw 命令是 .cmd shim(批处理转接器),CreateProcess(Windows 创建进程的系统调用)跑不了它, 且多行消息参数会被截断。直接找 .mjs 入口用 node 跑 = 跨平台确定行为。63 行的模块解决一个 平台差异问题——每个坑一个专属小模块,是 Easel 工程风格的缩影。

② 为什么超时常量单独放 timeouts.py(13 行)?
13 行的文件看起来"小题大做",但 docstring 里写着事故史:三入口各写各的超时导致行为不一致。 收敛后一处改全生效。常量的价值不在少,在"只有一份"。

③ 为什么 skill.py 不自己执行脚本?
执行主体是 agent:它读 SKILL.md 决定流程(哪个脚本、什么参数、失败怎么办)。 CLI 若抢跑,agent 的编排智能就被短路了——SKILL.md 里那些"先 check 再 plan 再 publish" 的流程纪律全部失效。让专家(agent)干编排,让脚本干执行,CLI 只当传令兵。

四、常见踩坑

坑 1:在 CLI 里复刻 agent 逻辑。"快速路径"绕过 agent 直接调脚本—— 短期快,长期三入口行为分裂。坑 2:消息里塞二进制。文件只传路径。坑 3: 超时魔法数字散落。每个 spawn 点自己写 7200——改一次漏三处。

五、检索练习

六、出口检验

对着源码说出:一条 easel skill 消息从 argv 解析到 agent 收到,中间经过哪 5 步、 每步在哪个文件。然后回答:如果 SKILL.md 写的脚本路径错了,报错会出现在哪一层? (提示:CLI 不执行任何脚本——那错误只能在谁那里出现?)

一手源:easel/commands/skill.py 全文精读(171 行,今天读得完)。