跳至主内容
写给人,也写给 AI 的命令行

写给人,也写给 AI 的命令行

我们为什么给一个故障响应平台做命令行工具,又为什么把它做成 AI 编程 Agent 操作 Flashduty 的入口——一篇关于 CLI 设计取舍的工程笔记。

快猫技术

本页目录

做 SaaS 的人很容易默认:有了 Web 控制台和 Open API,就不需要命令行工具了。Web 给人点,API 给程序调,中间那一层 CLI 像是多余的。

但当我们真正坐到一线 SRE 旁边看他们怎么处理故障时,发现事情没那么干净。值班工程师此刻正盯着终端里的日志、kubectl 和一串 grep。一条告警进来,他要做的第一件事不是切到浏览器登录控制台、找到对应协作空间、翻到那条故障,而是希望在不离开手头上下文的前提下,认领、看时间线、拉相似历史故障。每一次切窗口都是一次注意力的断点,而故障处理最怕的就是注意力被打散。

这就是 flashduty-cli 存在的第一个理由:让故障响应留在工程师本来就在的地方,也就是终端里。

为什么是 CLI,而不是「只给 API」

有人会说:那我自己用 API 封一个脚本不就行了。可以,但 Open API 是原料,不是工具。

Flashduty 的 API 返回的是结构化数据,字段齐全但对人不友好。要在终端里舒服地用,你得自己处理分页、解析时间、把一堆 JSON 渲染成对齐的表格、把长字段截断、再给一个能 jq 的出口。这些「最后一公里」的体力活,每个团队各做一遍是巨大的浪费。CLI 就是把这条最后一公里一次性铺好:

flashduty incident list                  # 默认看最近 24 小时
flashduty incident get <id>              # 单条 ID 自动切成纵向详情视图
flashduty incident timeline <id>         # 看处理时间线
flashduty incident similar <id>          # 拉相似历史故障

输出默认是给人看的对齐表格,长字段截断;想喂给脚本就加 --json 走完整数据、直接 jq。时间参数也不强迫你算 Unix 时间戳,24h168h2026-04-012026-04-01 10:00:00 都认。这些都是让人能直接上手的设计,而不是把 API 的复杂度原样转嫁给用户。

还有一条我们写进 Agent 守则里的工程边界:CLI 不自己实现 API 端点客户端。 所有对 Flashduty Open API 的访问都走 flashduty-sdk,CLI 只负责命令解析、输出格式化和编排。SDK 与 API 严格 1:1,业务侧的便利(比如按名字解析协作空间 ID)放在 CLI 这一层。这样分层的好处是,API 演进时只需要改 SDK,CLI 跟着升级,不会在两个地方各维护一套半成品的 HTTP 适配。

真正想清楚的那一步:CLI 是 AI Agent 的工具面

如果故事到这里,flashduty-cli 只是一个体面的运维工具。让它变得不一样的,是另一个判断:

今天工程师的终端里,越来越多地坐着一个 AI Agent。

Claude Code、Cursor、GitHub Copilot、Codex、Gemini CLI、Windsurf 这些 AI 编程助手已经能读你的代码库、跑命令、改文件。当一条线上告警发生时,工程师越来越自然地会问身边的 Agent:「这个故障什么情况,谁在值班,跟上次那次像不像?」

问题是,Agent 默认并不知道怎么操作 Flashduty。它可以乱猜 API、可以瞎拼 curl,但更可能的是它根本不知道这个平台的存在,或者用错方式把事情搞砸。比如在没确认的情况下就把两条故障合并了,而合并是不可逆的。

所以我们做了一件事:把「怎么用 Flashduty」这件事,本身做成 Agent 能装载的能力。 flashduty-cli 内置了 10 个 agent skills,一行命令就能装到你手上的 Agent 里:

npx skills add flashcatcloud/flashduty-cli -y -g

安装器会自动探测你装了哪些 Agent,然后把技能分发给它们,目前兼容 41+ 个编程 Agent。装完之后,那个本来对 Flashduty 一无所知的 Agent,就知道了该用哪个命令、参数怎么填、什么操作必须先问人。

一个 skill 里到底装了什么

这 10 个技能不是命令清单的复读。我们真正灌进去的是心智模型和安全边界,也就是一个有经验的值班工程师脑子里那套东西。

地基技能 flashduty-shared 里,第一件事是讲清楚 Flashduty 的三层降噪模型:原始告警事件(Alert Event)按 alert_key 去重成告警(Alert),告警再按聚合规则汇成可处置的故障(Incident)。一条故障可以包含 1 到 5000 条告警。Agent 一旦理解了这个数据模型,它在下钻和上卷时就不会问错层。

剩下九个技能按职责切分:故障生命周期、告警调查、变更追踪、值班查询、协作空间与升级规则、状态页管理、MTTA/MTTR 分析、团队成员审计、通知模板验证。每个技能只在它的范围内给 Agent 最相关的命令和惯用法,而不是一股脑塞 200 多个命令的全集,让 Agent 在对的时刻只看到对的工具。

但最关键的,是安全规则。我们在地基技能里写死了一组给 AI 的硬约束,比如:

  • 未经用户明确确认,绝不创建或关闭故障;
  • 绝不合并故障或告警,合并不可逆;
  • 执行任何破坏性或变更性操作前,先列出会受影响的对象
  • 拿不准时,先 list 再 act,优先只读操作。

这些不是写给人看的提示,是写给模型的「别帮倒忙」清单。一个 Agent 在自动驾驶时最危险的就是过度自信地执行不可逆操作,我们宁可让它多问一句。

给 Agent 用的细节:token 也是成本

做「给 Agent 用的工具」和「给人用的工具」,有一处分野很具体:对 Agent 来说,输出的体积直接等于钱和上下文窗口。

所以除了表格和 JSON,CLI 还提供了第三种输出格式 TOON(Token-Oriented Object Notation)。它和 JSON 一样是完整、不截断的机器可读数据,但对列表这种每行字段都一样的均匀数组,它只在头部声明一次字段名,而不是每一行都重复一遍 key。同样一页故障列表,TOON 比 JSON 省下相当可观的 token。

flashduty incident list --output-format toon

这是个很小的功能,但它体现了一个判断:当工具的主要消费者从人变成模型,你优化的目标函数也得跟着变。人在意可读性,模型在意每个 token 的性价比。

不起眼但要命的可靠性

最后说一件听上去无聊、却最能决定工具好不好用的事:补全和 --help 的可靠性。

CLI 是用 Cobra 写的,自带 shell 补全(bash/zsh/fish/powershell)。但补全这件事,「生成」不等于「可用」。默认命令需要被正确安装,枚举型参数的取值要能补出来,否则用户敲到一半卡住,体验就崩了。对人是别扭,对 Agent 更致命:Agent 探索一个陌生 CLI 的唯一可靠方式就是 --help,如果某个子命令的帮助文本缺了一个参数、或者描述和真实行为对不上,模型就会照着错的信息去拼命令,然后失败。

所以我们把每一个出现在帮助文本里的子命令、参数、枚举值,都当作 Agent 会逐字信任的契约来对待:帮助文本里写的,必须就是它真实的样子。这类打磨不上头条,但它是「Agent 能不能一次把命令敲对」和「反复试错三轮」之间的分水岭。

一个小结

flashduty-cli 走到今天(v1.3.4),我们对它的定位很清楚:它先是一个让 SRE 不必离开终端就能响应故障的趁手工具,同时也是 AI 编程 Agent 操作 Flashduty 的标准入口。覆盖 Flashduty 全部 Open API、一行 curl 安装、10 个能直接装进 41+ 个 Agent 的技能,把这些拼起来,背后是同一个判断:故障响应应该发生在工程师(和他的 AI 搭子)本来就在的地方,而不是再多开一个浏览器标签页。

想试的话,一行命令就能装上:

curl -sSL https://raw.githubusercontent.com/flashcatcloud/flashduty-cli/main/install.sh | sh

仓库与文档都在 github.com/flashcatcloud/flashduty-cli

相关阅读