AI CLI工具项目复盘:从Python脚本到跨平台分发包的产品化之路
一、从"给我写个脚本"到"可以公开发布"
一切从一句"帮我写个脚本"开始:每天需要把本地Markdown文件中的Mermaid图表自动渲染为PNG并嵌入文档。Python脚本15分钟写完,能用。但很快需求扩展——需要支持批量处理、自定义主题、导出PDF。脚本从50行膨胀到400行,开始在同事之间传阅。某天有人问:"这玩意能不能brew install?"
这句话开启了从脚本到产品的产品化过程。技术本质没变——调用mermaid-cli渲染图表。但用户体验的期望从"我能跑"变成了"我能install"、"我能配置"、"我能CI集成"。
二、从Python脚本到独立二进制
第一步:PyPI发布 —— 让Python用户能装
将脚本改造成标准的Python包结构:
mmd-render/ setup.py mmd_render/ __init__.py cli.py renderer.py themes/ README.md# setup.py from setuptools import setup, find_packages setup( name="mmd-render", version="0.1.0", packages=find_packages(), install_requires=["click", "playwright"], entry_points={ "console_scripts": [ "mmd-render=mmd_render.cli:main", ], }, python_requires=">=3.9", )发布到PyPI后,用户只需pip install mmd-render即可使用。但问题随之而来:有些用户没有Python环境,或Python版本不对(系统自带Python 3.7,需要3.9+)。"这工具很好,但我装不上"——这是用户的原话。
第二步:PyInstaller打包 —— 消除Python依赖
PyInstaller能将Python脚本打包为独立可执行文件(包含Python解释器和所有依赖):
pip install pyinstaller pyinstaller --onefile --name mmd-render cli.py生成的单个二进制文件约25MB(主要是Playwright的浏览器内核),但不需要任何Python环境。在GitHub Release中为三个平台提供下载:
mmd-render-darwin-amd64mmd-render-darwin-arm64mmd-render-linux-amd64
第三步:包管理器分发 —— 消除"下载zip"的摩擦
"curl下载→chmod→移动到PATH"的三步安装在开发者社区是不容忽视的摩擦。接入包管理器:
# Homebrew Formula class MmdRender < Formula desc "Render Mermaid diagrams from Markdown files" homepage "https://github.com/user/mmd-render" url "https://github.com/user/mmd-render/releases/download/v0.2.0/mmd-render-darwin-arm64" sha256 "abc123..." version "0.2.0" def install bin.install "mmd-render-darwin-arm64" => "mmd-render" end test do system "#{bin}/mmd-render", "--version" end endbrew install mmd-render——一行命令搞定安装。安装量从PyPI的约200次/月增长到brew+PyPI合计约1200次/月。
三、CLI的用户体验设计
命令设计的渐进式暴露:
# 简单路径:零配置即可用 mmd-render docs/ # 中级路径:常用参数 mmd-render docs/ --theme dark --output-dir rendered/ --watch # 高级路径:配置文件 mmd-render docs/ --config mmd-render.yml配置文件支持(.mmd-render.yml或mmd-render.yml):
theme: dark output_format: png scale: 2 watch: true # CI模式——非交互,失败即退出 ci: false # 排除模式 exclude: - "**/node_modules/**" - "**/.git/**"错误信息的友好化:
# 用户犯错时,不报Python Traceback,而是给出清晰的诊断 def validate_input(path: str): if not os.path.exists(path): click.echo(f"错误: 路径 '{path}' 不存在", err=True) click.echo("提示: 检查路径是否正确,或使用 --help 查看用法") sys.exit(1) mermaid_files = glob.glob(os.path.join(path, "**/*.md"), recursive=True) if not mermaid_files: click.echo(f"警告: 在 '{path}' 中未找到Markdown文件", err=True) sys.exit(0)四、产品化过程中的坑
坑1:Playwright依赖的大小问题。mermaid-cli依赖Playwright的Chromium内核(约150MB)。PyInstaller打包后二进制从3MB膨胀到25MB,GitHub Release的单文件限制(2GB)虽然没触及,但用户下载体验明显变差。方案:将Chromium作为外部依赖——如果系统已安装Chromium则复用,否则提示安装。
坑2:Homebrew Formula的审核流程。提交Homebrew Formula需要满足严格的审核标准:必须有test do块、依赖声明完整、无网络请求、无sudo。第一次提交因为缺少test do被拒。
坑3:版本管理的语义化。早期版本号随意(0.1.2→0.1.3a→0.1.3b),导致用户和CI脚本难以追踪。引入SemVer + 自动发布:Git Tag → GitHub Actions → 构建→ PyPI + GitHub Release。
五、总结
从脚本到产品的产品化过程,核心经验:
- 包管理器分发(brew/pip/npm)是CLI工具分发的最佳方式——"一行命令安装"是准入门槛
- 独立二进制消除了Python版本依赖——打包后的25MB对用户的便利性是值得的
- CLI设计遵循"渐进式暴露"——零配置可用,高级功能可配,CI模式可脚本化
- 错误信息不要暴露Traceback——用户要的是诊断,不是调试信息
- SemVer + 自动发布是可持续维护的基础
最大的教训:从脚本到产品最大的工作量不在代码(400行Python扩展到了约800行),而在分发。PyPI搭建、Homebrew Formula编写、GitHub Actions CI配置、多平台测试——这些"非代码工作"约占总工期的60%。如果知道最终要公开发布,应该从一开始就按标准Python包组织代码,避免后续重构。