返回 文章 build CMS 文章

Spaces CLI:为人类与智能体共同设计的命令行工具

为智能体设计 CLI,反而让工具对人类更好用。

CLI开发者工具智能体DX
成长分 / 100 76 综合收获、行动、留存与影响

Spaces CLI:为人类与智能体共同设计的命令行工具
为什么值得读了解如何让 CLI 同时服务于人类和智能体,避免为智能体单独构建 API。

学习将交互式提示转化为标志和智能默认值的具体模式,提升工具的可脚本化能力。

关键洞察
  1. 每个交互式提示都隐含一个信息需求,可以通过标志、配置文件或提示来满足,先考虑信息再考虑输入方式。
  2. 为智能体设计迫使 CLI 消除隐式状态(如当前工作目录、环境变量),使工具更可组合、可测试。
  3. 插件作为数据模型,可内省和序列化,人类通过 TUI 浏览,智能体查询注册表获取 JSON。
转成行动

深入阅读

正文与原文对照

原文保真覆盖:全文原文字符:8976

大多数开发者工具都以同样的方式起步。你需要反复做某件事,于是写了一个脚本。脚本逐渐增加了各种标志。标志又演变成子命令。不知不觉间,你就有了一个 CLI。

我们在构建一个内部平台工具以帮助解决方案团队更快交付时,就经历了完全相同的过程。它能搭建项目脚手架、启动开发环境、生成配置,并部署到预发布环境。

随着我们的 CLI 范围扩大,内部用户的数量也随之增长。但随后,有趣的事情发生了:突然间,我们不再仅仅为人类开发者构建,还要为编码代理构建。

良好的 DX 是什么样子

在谈论代理之前,我们先聊聊是什么让 CLI 对人类开发者来说真正令人愉悦。

Spaces 对无聊的事情有自己的主见。它选择合理的目录结构,让你不必操心。它生成你原本会从上一个项目复制过来的配置文件。它把服务连接起来,让你的 API 和前端在第一天就能通信,而不是在花了一小时编辑 YAML 或“凭感觉”之后。

以下是一个典型会话的样子:

$ spaces init my-project$ cd my-project$ spaces dev

三条命令。你从一无所有到一个运行中的多服务项目,带有热重载、数据库和生成的 Dockerfile。这就是标准。

重要的命令往往分为三类:

脚手架——这些命令创建结构、提出问题、展示选项,让你探索。开发——这些是你的内循环。它们只是“运行”。运维——这些涉及生产环境。小心,行动前要仔细检查

这些都是基本要求。但随后我们的第二个用户出现了,我们发现我们需要一些略有不同、且有趣得多的东西。

第二个用户:代理

我们为 init 构建了一个 TUI 模块选择器

。花哨的变更、命令,看起来超级棒。

然后一个代理尝试使用它。

代理看到了原始的 ANSI 转义码。\\\\x1b[36m?\\\\x1b[0m 选择组件

。它无法发送方向键或切换选择。它完全被锁在了命令之外。

事后看来,修复方法似乎显而易见:添加一个 --components

标志。但真正的洞见比一个标志更大。

每个提示都是伪装的标志

每当你的 CLI 提出一个交互式问题时,都有一个隐含的契约:“我需要这条信息才能继续。”为了履行这个契约,你可以使用交互式提示。或者你可以使用标志或配置文件。

诀窍在于先考虑信息,再考虑输入方式

def init_command( components: str | None = Option(None), yes: bool = Option(False, "-y"),): if components: selected = components.split(",") elif yes: selected = get_defaults() else: selected = show_picker()
# Same logic from here create_project(selected)

三条输入路径,一条执行路径。业务逻辑不知道输入是如何到达的。这意味着你只需测试一次,而不是三次。

-y

标志值得特别关注。它不仅仅是“跳过确认”。它是一份契约,表明:我以编程方式提供了你所需的一切,不要在 stdin 上阻塞。 当你使用 -y

运行时,每个提示都会解析为一个标志值或一个智能默认值。如果无法解析,它会大声失败而不是挂起。

实践中的例子:本文中的交互式元素。

这些元素是在一个全新的仓库中构建的。一个代理被追溯要求将项目接入 [spaces cli] 以进行部署。它首先在相关命令上运行 --help

以了解接口。由此,它弄清楚了需要哪些配置文件,生成了一个 config.yaml

,并连接了 Dockerfile 和注册表设置——无需人工干预。

在同一轮中,它设置了 GitHub Actions CI 流水线。从单个提示到实时部署耗时不到 10 分钟(我们正在积极努力缩短这个周期时间)。之后,仓库完全配置好,嵌入内容通过 Koyeb 部署——作为一个 Space,通过 Spaces 本身进行脚手架搭建和部署。是的,这篇关于 Spaces 的博客文章中嵌入的交互式演示正作为一个 Space 运行。Spaception

因为每个交互式输入都有一个等效的标志,代理可以端到端自主操作。

结构化数据作为接口

我们团队正在构建的 CLI 帮助我们的应用 AI 工程师交付应用。但并非每个应用看起来都一样。有些需要后端和向量数据库。其他的只需要关系数据库、前端和一些 API。少数是仅工作进程的服务,完全没有 UI。

我们不会硬编码模块类型。因此我们构建了一个插件系统,其中每个组件都是一个声明自身属性的插件:

class ModulePlugin(BaseModel): type_id: str category: str default_port: int
def get_env_vars(self) -> list[EnvVarDef]: ... def get_dev_command(self, port: int) -> str: ...

插件是可内省的。你可以列出它们、序列化它们、对它们进行差异比较。人类通过 TUI 选择器浏览。智能体查询注册表并获取 JSON 返回。相同的数据,不同的呈现方式。

这意外地解决了一个我们此前不知道存在的问题。以前,添加一种新的模块类型意味着要更新选择器、Dockerfile 生成器、环境文件写入器和 compose 模板。现在,它意味着编写一个插件类。注册表是唯一的真相来源,一切都从它读取。

让智能体了解你的项目

有句老话说内容为王。对于智能体而言,上下文才是。

我们为提升智能体可用性所做的最有影响力的事情,是在每次 init

时生成两个文件

context.json

—— 项目的结构化快照:存在哪些模块、它们使用的端口、要运行的命令、它们需要的环境变量。

AGENTS.md

—— 一组为 LLM 编写的规则,比你的常规规则更具命令式。不是“这个项目使用 PostgreSQL”,而是“在测试数据库变更之前运行 mycli dev --migrate

”。

一个在行动之前阅读这些文件的智能体会犯明显更少的错误。基本上,它不会去猜测端口号、运行错误的测试命令、尝试安装已由工具链管理的依赖项。

上下文文件还充当了清除过时智能体假设的缓存破坏器。当你添加一个模块或更改部署目标时,上下文文件会在下一次 dev

init

时自动更新。智能体每次都会读取最新的状态。

隐式状态是敌人

我们遇到的最微妙的问题是隐式状态。我们的 add

命令从当前工作目录读取 config.yaml

。人类会不假思索地 cd

到正确的文件夹。而一个从工作区根目录运行命令的智能体根本不知道它需要位于某个子目录中。

修复方法:

# 之前:依赖当前工作目录config = load_config(Path.cwd() / "config.yaml")
# 之后:显式指定并带回退config = load_config( path or find_config_in_parents(Path.cwd()))

每一个隐藏的假设——当前工作目录、环境变量、$HOME 中的点文件

——都是智能体(agent)会栽跟头的地方。带有合理回退的显式参数为智能体解决了这个问题,也让人类编写脚本更容易。

检查清单

回头看,这些改动单独来看都很小。只是一组被一致应用的原则:

每个交互式输入都有对应的标志(flag)

每个标志在无头模式下都有智能默认值

状态是显式的。当前工作目录、环境变量和配置路径是输入,而不是假设

插件是数据模型,而不仅仅是代码。默认即可内省

上下文文件为智能体(以及 CI 和脚本)提供项目的结构化描述

为每个人构建更好的工具

有趣的是,这些都没有让 CLI 对人类变得更糟。TUI 选择器仍然可用,看起来依然花哨,进度旋转器仍然旋转,确认对话框仍然确认。我们只是增加了第二扇门。

而那第二扇门结果证明是更重要的那扇。不是因为智能体比人类更重要,而是因为它们施加的约束,正是让 CLI 可组合、可脚本化、可测试的同一组约束。为智能体设计迫使我们为每个人构建了更好的工具。

如果你现在正在构建开发者工具,你不需要单独的智能体 API。你需要审视每一个 input()

调用、每一个当前工作目录假设、每一个只做漂亮打印的输出,然后问:如果另一端的用户是一个进程,而不是一个人,会怎样?

这个问题的答案无论如何都会改进你的工具。

Spaces CLI 由 Mistral AI 的 Lorenzo Signoretti、Riwa Hoteit 和 Sam Fenwick 构建。特别感谢我们的应用 AI 团队,他们是 CLI 最早且最苛刻的用户,他们的真实使用塑造了这里描述的每一个模式。我们很期待看到它将帮助他们与我们的客户一起构建应用,以解决棘手的用例。

人类与智能体之间的工具层仍在探索之中。如果在 AI 与基础设施的交汇处构建开发者工具听起来正合你意,我们正在招聘