work2cli-hitl — 把日常工作沉淀成 CLI + 全局飞书守卫单例(自进化工作台)
本 skill 是方法论 + 设计规范 + 自进化注册表。指导 agent 带用户把重复性日常工作(查数据、导报告、回写状态……)按系统沉淀成本地 CLI 工具集,所有写操作统一走一个全局飞书守卫单例服务做人工审批(极简 HITL),并且每做完一个 CLI 就把使用说明回写进本 skill——skill 随使用越来越厚。
设计蓝本提炼自多个实战项目的共性做法(内部平台的 CLI 化 + 写操作审批),本 skill 只保留可迁移的部分,不依赖任何特定公司、系统或行业。
定位与边界(重要:先跟用户讲清楚,别过度承诺)
这是软控(soft control),不是硬隔离。 必须在使用前就把边界说明白,否则会给人虚假的安全感:
- 它解决的:① agent 偶尔判断错(把没做完的事标记成完成、改错对象、发错通知);② agent 干太快——人还没看清就已经执行完了。目标是在「变更远端状态」这个关键时刻插入一次人工点头,给操作者(和旁观者、上级)一种可解释的安全感。
- 它不解决的:被明确指示绕过限制的执行者。如果 prompt 让 agent 绕过(直接改守卫代码、跳过 guard 调底层 API、改配置/环境变量/审计日志、手工构造请求),软控拦不住。审批强度 = 执行者是否愿意遵守约定,它对「协作的 AI」有效,对「对抗的 AI」无效。
- 想要硬控,得把控制点移出执行者权限范围:写接口的凭据只在审批通过后短期下发(服务端侧审批)、远端 RBAC/工单强制、凭据与执行环境分离、代码仓库分支保护。这些是权限与工程问题,加一层 md 或一个 CLI 模块解决不了。
- 正确的宣传口径:「防手滑、防 AI 幻觉、防干得太快」——不要说「防越权/防绕过/安全边界」。
前置假设(只有这两条,其余一律不假设)
- 用户的 agent 已具备浏览器操作能力——能打开网页、读取页面内容、执行页面脚本、观察页面发出的网络请求。勘察阶段全靠它。
- 环境已装好
lark-cli并完成登录(lark-cli auth login)——全局守卫服务的审批通道靠它。
除这两条外不做任何假设:不假设目标系统类型、不假设有 API 文档、不假设内外网、不假设操作系统与实现语言、不假设用户懂技术细节。缺什么就在勘察阶段自己搞清楚,不要退回给用户一堆前置问题。
什么时候用
- 用户说「我每天都 XXX,能不能自动化」「把我的工作做成工具」
- 用户丢来一个网站/系统地址说「帮我把这个沉淀成 CLI」(最常见入口,走自主勘察,不要反问用户一堆问题)
- 用户已有散落各处的脚本,想收拢成规范 CLI
- 用户担心 agent 自动执行写操作(改状态/回写/发消息/删改)不可控,要加人工审批
- 用户提到 HITL、Human-In-The-Loop、写守卫、飞书卡片审批、全局守卫/统一审批服务
- 用户做完一个 CLI 要求「把使用说明沉淀进来」「更新注册表」「自进化」
总体架构
用户给一个网址(或一段描述/已有脚本)
│ ①agent 用浏览器操作能力自主勘察:抓 API、读写分型、认枚举语义(不问用户)
▼
cli/<system>/ # ②按系统组织:一个系统一个目录
├── <system>_cli # 读:直接执行
├── _guard_client # 写:接全局守卫(所有 CLI 共用同一份客户端)
└── ...
│
│ ③写操作 → 全局飞书守卫单例(一个常驻守护进程服务所有 CLI)
│ 卡片审批(本人点按钮 / 回 ok <hash8>)→ 决定落 spool → CLI 领取
▼
<skill 目录>/<domain>/ # ④agent skill 包装(playbook)
│
│ ⑤自进化:使用说明回写 registry/<system>.md,SKILL.md 只留索引
▼
registry/ # 越用越厚的注册表(渐进描述:细节在条目,入口在本文件)
三个设计支柱:
- CLI 管「怎么做」:按系统分目录,独立认证独立迭代;公共的守卫客户端全机共享一份,不复制。
- 全局守卫单例管「能不能做」:飞书事件总线独占,所以收按钮回调的守护进程全机只有一个,服务所有系统的所有 CLI——不是每个项目起一个。设计详见 references/write-guard-hitl.md。
- 注册表管「沉淀」:每做完一个 CLI,把使用说明回写进 registry,下次直接复用;本文件只维护索引(渐进描述)。协议详见 references/self-evolution.md。
⚠️ 以上都是软控:约束的是「愿意配合的 agent 少犯错、慢一点」,不约束「铁了心绕过的执行者」。定位详见上节。
Phase 1:自主勘察(agent 自己发现,别反问用户)
原则:用户只负责给入口(网址)和最后拍板,中间的信息收集全部自己做。 具体勘察手法详见 references/autonomous-discovery.md。
勘察流程:
- 进系统:用浏览器操作能力打开目标网站(优先复用已有登录态;需要登录时让用户登录一次,token/cookie 自动拿,不问用户要)。用户机器上已有同类浏览器控制方案时优先用自带的,本 skill 的浏览器能力假设是兜底。已有脚本/API 文档/OpenAPI 的直接读,跳过网络勘察的大半工作。
- 摸 API 面:跟随真实操作路径观察网络请求 + 从页面脚本里挖 API 全表、状态枚举、签名算法(动态 + 静态交叉验证)。
- 读写分型(自己判):
- 只读 → 直接做,不拦;
- 写 → 必须接全局守卫,且必须支持
--dry-run; - 不可逆写(涉及资金/删除/生产变更)→ dry-run 强制、审批文案写明不可逆、执行后回查验证。
- 产出《勘察报告》:API 清单 + 读写判定依据 + 建议 CLI 命令面 + 认证方式。这是唯一一次找用户确认的点(写操作漏判 = 绕过 HITL,宁可多报待确认)。用户拍板后直接开写。
只有信息 genuinely 拿不到时才问用户,问的时候带上下文和猜测方案让用户选。
Phase 2:CLI 设计规范(脚本按系统组织)
逐条规范见 references/cli-design-guide.md。组织约定:
<工作区>/cli/
├── _guard/ # 全局守卫客户端(全机共享一份,所有 CLI 引用它,不复制)
│ └── guard_client.<ext>
├── <system-a>/ # 一个系统一个目录
│ ├── <system-a>_cli.<ext>
│ └── tests/
├── <system-b>/...
└── README.md # 各系统入口索引
要点速记:一个系统一个 CLI;命令用动词(list/detail/report/export/apply/pull);输出默认 JSON + --format table + --fields;exit code 语义化(0/1/2/3/4/5/6/7);写命令必须有 --dry-run;认证自备(环境变量 > 本地缓存 > shell 配置文件解析 > 浏览器登录态,全失败 exit 2 打印手动方法);高频多步操作做聚合命令;外部依赖收口到独立模块可整体 mock;大文件/大结果必须渐进读取(元数据先行 → 分块 → CLI 内置 grep,防撑爆 agent 上下文);服务端缺的能力在 CLI 层补偿(正则/路径过滤/行范围/批量);跨系统 ID/URL 映射固化进注册表(一个系统的输出直接喂另一个系统)。详见 references/cli-design-guide.md §8-11。
Phase 3:skill 包装(让 agent 自然语言驱动)
每个系统配一个 skill 目录(用当前 agent 运行时的 skill 目录约定),SKILL.md 写触发词 + 命令索引(带「什么时候用」注释)+ playbook 工作流 + 已知坑;skill 只写流程和判断标准,执行全落 CLI。
Phase 4:写操作飞书 HITL(全局守卫单例)
八条底线(完整设计见 references/write-guard-hitl.md):
- fail-closed:任何环节失败一律视为拒绝,绝不放行。等待上限 10 分钟。
- 审批对象是参数本身:规范化参数 SHA256 前 8 位;批准后重算比对,不一致拒绝执行。
- 双通道审批:主通道飞书交互卡片按钮(消息ID+点击人+哈希三重匹配);兜底文本
ok <hash8>/no(5 秒轮询,stdout 倒计时)。 - 结果回写按「成功 1 次」预算设计(实战坑):回调 token 只能成功刷一次卡片(第二次报 300040)——刷新职责归守护进程,CLI 把执行结果写回 spool 由它统一刷,预算只留「执行结果」态;失败静默回退文本;审计记
result_channel。 - 审计留痕:每次授权事件追加 JSON 行到审计文件(时间/工具/操作/哈希/结果/原因/result_channel/card_reused)。
- 拒绝语义可机读:拒绝/超时/哈希不一致 → exit 3,stdout 末行
{"approved": false, "reason": "..."}。 - 回调消费全局唯一(实战坑):
card.action.trigger事件总线独占 → 收回调的守护进程全机一个,服务所有 CLI;CLI 按需拉起它(pid+锁),自己绝不直接event consume。 - 在途卡片复用 + 决定一次性消费(实战坑):同参数哈希且未决定 → 复用老卡片不重发;决定领取即 consumed,同 hash 第二个等待者按未授权处理(exit 3),防同一操作被执行两次。
Phase 5:验收 + 自进化回写
交付前逐项打勾(命令按实现语言等价替换):只读连通 → 写命令 dry-run → 守卫离线测试(批准/拒绝/超时/哈希不一致/网关故障/守护进程重启回归/卡片复用/300040 回退)→ 真机端到端演练一次。
验收通过不算完——必须执行自进化回写(Phase 6):
Phase 6:自进化回写(本 skill 越用越厚的关键)
每完成一个系统的 CLI(或重大迭代),立即按 references/self-evolution.md 的协议:
- 把使用说明回写为
registry/<system>.md(命令索引、认证方式、状态枚举、已知坑、实战记录); - 在下方「注册表索引」加/更新一行;
- 脚本落位到
cli/<system>/,守卫客户端确认走全局共享_guard/。
注册表索引(已沉淀的 CLI)
| 系统 | 一句话 | 条目 | 状态 |
|---|---|---|---|
| (待沉淀——每接入一个系统在这里加一行) |
参考形态见 demo/:demo 目录展示了「cli 按系统组织 + 守卫客户端共享 + 守护进程单例 + 注册表条目」的完整骨架。
交付清单(做完应有什么)
<工作区>/
├── cli/
│ ├── _guard/ # 全局守卫客户端(共享)
│ └── <system>/ # 每系统一个目录(CLI + 测试)
├── <skill 目录>/<domain>/ # 每系统一个 SKILL.md + references/
├── registry/<system>.md # 自进化注册表条目
└── README.md
反模式(拦住用户别这么做)
- ❌ 把 token/cookie 硬编码进 CLI 或 skill——认证必须自备且不入库
- ❌ 写操作不 dry-run 直接执行,或 dry-run 和正式执行走不同参数路径
- ❌ 守卫 fail-open(发不出审批消息就放行)——这是 HITL 不是装饰品
- ❌ agent 的判断逻辑写死在脚本里——判断归 skill(md),执行归 CLI
- ❌ 一个万能 CLI 塞所有系统——按领域拆,各自独立认证独立迭代
- ❌ 把软控当硬控宣传(「防绕过/安全边界」)——误导用户,出事时更糟
- ❌ 每个项目/系统起一个守卫守护进程——事件总线独占,全局只能有一个,多起必互相踢下线
- ❌ 做完 CLI 不回写注册表——skill 不进化就退化成一次性教程;使用说明散在聊天记录里等于没沉淀
- ❌ 审批卡片给人看 uuid 串/JSON 墙——人看不懂的卡片点了批准等于没审;批量目标给人类可读明细,一次 ≤5 个
- ❌ 兜底指令
ok <hash>埋在散文里——退化到文本回复时哈希要给独立代码块,一键可复制 - ❌ 新 CLI 接守卫只看「卡片能发出来」——接入验收 = 真人点一次按钮、卡片变终态、命令真执行(漏注册时全链路无报错,唯独点击收不到)
