Loading

7 阶段流水线详解:从源码到可安装 CLI 做了什么

一条命令背后发生了什么

当你执行 /cli-anything ./gimp 的时候,Claude Code 里的 AI Agent 并不是在做一个简单的任务。它会按顺序跑完 7 个阶段,每个阶段都有明确的目标和交付物。

这 7 个阶段是 CLI-Anything 的核心方法论,定义在一个叫 HARNESS.md 的文件里。这个文件是整个项目的 SOP(标准操作流程),所有平台插件(Claude Code 插件、OpenCode 命令、Codex skill)都引用同一个 HARNESS.md,保证不同平台上生成的 CLI 质量一致。

下面逐个拆解每个阶段在做什么、关键决策是什么、以及 Blender 的源码是怎么被处理的。


阶段 1:分析(Analyze)

目标:理解目标软件的架构,找到所有可程序化的功能入口。

这个阶段要做的事情比听起来多得多。不是简单地把源码扫一遍就完事了,而是要回答三个关键问题:

  1. 软件用什么方式暴露功能? 直接命令行参数?IPC 通信?文件格式?Python 绑定?
  2. GUI 操作对应的底层调用是什么? 比如 GIMP 里点击"添加图层",背后调的是什么 API?
  3. 哪些功能可以脱离 GUI 独立运行? 哪些必须有人工交互?

以 GIMP 为例,分析阶段会找到三种可行的后端路径:

  • GEGL 操作:GIMP 2.10+ 的现代图像处理图引擎
  • Script-Fu:GIMP 内置的脚本语言
  • Pillow:作为 fallback,用于生成中间图像文件

这个阶段的交付物是一份功能映射表,列出了所有可暴露的命令及其对应的底层实现方式。


阶段 2:设计(Design)

目标:把分析结果组织成合理的命令结构和数据模型。

这是我认为最有技术含量的阶段。命令结构的设计直接影响 Agent 的使用体验——太粗了不够用,太细了记不住。

设计阶段要做几件事:

命令分组。以 Blender 为例,生成的 CLI 大致按这样的逻辑分组:

blender scene    # 场景管理:创建、加载、保存
blender object   # 物体操作:添加、变换、材质
blender render   # 渲染控制:参数设置、渲染触发
blender material # 材质系统
blender mesh     # 网格数据操作

状态模型。Blender 是一个有内部状态的应用——当前场景、选中的物体、渲染设置。要不要维护这些状态?CLI-Anything 的答案是:可选。你可以在 REPL 模式下维护状态(类似操作 Blender 文件的过程),也可以每次都通过 --project 参数传递完整状态。

输出格式设计。每个命令至少有两种输出格式:人类可读的自然语言描述,以及 --json 模式下的结构化数据。JSON 模式用于 Agent 消费,自然语言模式用于调试。


阶段 3:实现(Implement)

目标:写出可运行的 Click CLI 代码。

这个阶段生成的核心文件结构大概长这样(以 GIMP 为例):

cli_anything/gimp/
├── __init__.py
├── cli.py                    # Click 命令入口
├── core/
│   ├── __init__.py
│   ├── project.py            # 项目文件读写
│   ├── layer.py              # 图层操作
│   ├── filter.py             # 滤镜处理
│   └── export.py             # 导出功能
├── backends/
│   ├── __init__.py
│   ├── gegl.py               # GEGL 后端
│   ├── scriptfu.py           # Script-Fu 后端
│   └── pillow.py             # Pillow fallback
└── utils/
    ├── __init__.py
    ├── repl_skin.py           # REPL 界面统一皮肤
    └── validators.py         # 参数校验

repl_skin.py 是统一 REPL 界面的关键。每个 CLI 都使用同一个 REPL 皮肤,有统一的 banner 样式、命令提示符、进度显示、彩色输出。这意味着 Agent 学会了用 GIMP CLI,切换到 Blender CLI 不需要重新学习界面操作。

JSON 输出的实现。在 Click 命令上装饰 @click.option('--json'),然后在 handler 里根据 flag 切换输出格式:

@click.command()
@click.option('--json', 'output_format', flag_value='json', default=True)
@click.option('--human', 'output_format', flag_value='human', default=False)
def layer_add(output_format, ...):
    result = do_layer_add(...)
    if output_format == 'json':
        click.echo(json.dumps(result, indent=2))
    else:
        click.echo(f"✓ Added layer '{name}' to project")

阶段 4:规划测试(Plan Tests)

目标:制定测试策略,不要等到写完了才想起要测试。

在真正写代码之前,Agent 会先写一份 TEST.md,里面包括:

  • 测试分类:哪些是单元测试(隔离验证单个函数),哪些是端到端测试(验证完整流程)
  • 边界条件:非法参数、超大文件、缺失后端时会发生什么
  • 预期行为:通过的标准是什么

这份文档既是测试计划,也是验收标准。后面的测试结果会更新到这个文件里,形成可追溯的测试历史。


阶段 5:编写测试(Write Tests)

目标:实际写出测试代码。

测试分四层,这个设计挺有意思的:

第一层:单元测试。用合成数据测试核心函数,不依赖任何外部软件。比如测试 layer_add() 函数的参数校验、返回值格式。Blender CLI 的单元测试有 150 个。

第二层:端到端测试(原生)。验证生成的中间文件格式正确。比如 LibreOffice 生成的是 ODF ZIP 包,测试会解压它,检查内部 XML 结构是否合法;Blender 导出的是 .blend 文件,测试会检查文件魔数。

第三层:端到端测试(真实后端)。这是最严格的一层——必须真正调用软件并验证输出。Blender 的测试会真的运行 blender --background 来渲染一个场景,然后检查输出的 PNG 文件里是否包含 %PDF 之外的正确图像数据。如果 Blender 没有安装,这层测试会直接失败,而不是跳过。

第四层:CLI 子进程测试。通过 subprocess.run() 调用安装后的命令,验证 CLI 安装包本身没有错误。

为什么坚持第三层? 项目作者的原话是:"后端缺失时测试直接失败(而非跳过),确保功能的真实性。"这是和其他玩具级 CLI 生成工具最大的区别——不给你虚假的通过率。


阶段 6:记录结果(Document)

目标:把测试结果写回 TEST.md,形成可追溯的文档。

这一阶段容易被忽视,但对维护很有用。每次运行 cli-anything:test 或者 cli-anything:validate,结果都会追加到 TEST.md 里。一段时间之后,打开这个文件就能看到:这个 CLI 经历了多少次迭代、哪些边界情况曾经失败过、修复了哪些 bug。


阶段 7:发布(Publish)

目标:把生成的 CLI 变成可安装的 Python 包。

核心是生成 setup.py

from setuptools import setup, find_packages

setup(
    name=f"cli-anything-{software_name}",
    version="1.0.0",
    packages=find_packages(),
    install_requires=[
        'click>=8.0',
        # 其他依赖
    ],
    entry_points={
        'console_scripts': [
            f'cli-anything-{software_name}=cli_anything.{software_name}.cli:main',
        ],
    },
    package_data={
        f'cli_anything.{software_name}': ['skills/SKILL.md'],
    },
)

entry_points 是关键——它告诉 setuptools 创建一个全局可执行的命令脚本。安装之后,cli-anything-gimp 就在 PATH 里了,任何进程都能找到它。

package_data 把 SKILL.md 打包进 Python 包,Agent 通过 pip 安装之后,REPL banner 会显示 SKILL.md 的绝对路径,AI 工具可以直接读取它来做下一步决策。


refine:增量的秘密

7 阶段构建完成之后,你可能会发现某些功能没有覆盖到。refine 命令就是来解决这个问题的。

refine 的工作方式很有意思:它不会重建,而是对比当前 CLI 的功能清单软件实际拥有的能力,找出缺口,然后只对缺口进行实现。

/cli-anything:refine ./gimp "更多滤镜和批处理相关的命令"

每次 refine 都是增量的——已有的命令不会被改动,新命令会被添加到对应的模块里,测试也会相应扩展。这就像给一个房子做装修,不需要推到重来,可以一个房间一个房间来。


总结:流水线为什么这样设计

回头看这 7 个阶段,有一条主线贯穿始终:

设计先行,实现其次,测试贯穿,文档归档。

不是让 Agent 随便写代码然后跑测试碰运气,而是先把要做什么设计清楚,再用测试来验证这个设计是否被正确实现。这个思路其实和人工写代码的最佳实践是一致的——只不过 CLI-Anything 把这个流程自动化了。

posted @ 2026-03-22 12:07  饭勺oO  阅读(2057)  评论(0)    收藏  举报