教程概述
系列目录:CLI-Anything 教程系列索引
Design 是 CLI-Anything 7 阶段流程的第二阶段。在 Analyze 阶段识别了软件 API 之后,Design 阶段需要将这些 API 组织成结构化的 CLI 命令。
你将学到
- ✅ Design 阶段的输入输出
- ✅ 命令组设计原则
- ✅ 状态模型设计
- ✅ REPL 交互设计
- ✅ 输出格式设计
Design 阶段的核心任务
从 API 到 CLI
flowchart TD
A[API 映射文档] --> B[Design 阶段]
B --> C[命令组结构]
B --> D[状态模型]
B --> E[REPL 设计]
B --> F[输出格式]
C --> G[架构设计文档]
D --> G
E --> G
F --> G
style B fill:#e3f2fd
style G fill:#e8f5e9
设计输出
flowchart LR
subgraph Output["Design 阶段输出"]
A[命令组结构图]
B[CLI 规范文档]
C[状态机定义]
D[REPL 交互流程]
E[JSON Schema]
end
style Output fill:#e8f5e9
命令组设计
设计原则
flowchart TD
A[命令组设计原则] --> B[语义化分组]
A --> C[层次化结构]
A --> D[一致性命名]
A --> E[避免冲突]
命令组结构示例
GIMP CLI 命令组:
flowchart TD
Root[cli-anything-gimp] --> Project[project]
Root --> Image[image]
Root --> Layer[layer]
Root --> Selection[selection]
Root --> Filter[filter]
Root --> Tool[tool]
Root --> Export[export]
Project --> P1[new]
Project --> P2[open]
Project --> P3[save]
Project --> P4[close]
Project --> P5[info]
Image --> I1[new]
Image --> I2[scale]
Image --> I3[crop]
Image --> I4[flatten]
Image --> I5[duplicate]
Layer --> L1[new]
Layer --> L2[delete]
Layer --> L3[move]
Layer --> L4[merge-down]
Layer --> L5[opacity]
Filter --> F1[list]
Filter --> F2[apply]
Filter --> F3[gaussian-blur]
Filter --> F4[sharpen]
Filter --> F5[color-balance]
Export --> E1[render]
Export --> E2[batch]
命名规范
| 类型 | 规范 | 示例 |
|---|---|---|
| 命令组 | 名词,小写 | project, image, layer |
| 子命令 | 动词-名词,kebab-case | new, scale, merge-down |
| 参数 | 名词,下划线 | --output-path, --blur-radius |
| 标志 | 布尔,形容词 | --overwrite, --visible-only |
命令设计检查清单
## 命令设计检查清单
### 每个命令必须定义:
- [ ] 命令名称(符合命名规范)
- [ ] 命令描述(一句话说明)
- [ ] 参数列表(名称、类型、必需/可选、默认值)
- [ ] 返回值(成功时返回的数据结构)
- [ ] 错误情况(可能的错误及返回码)
- [ ] 示例(至少一个使用示例)
### 命令设计原则:
- [ ] 单一职责:一个命令只做一件事
- [ ] 幂等性:相同输入产生相同结果
- [ ] 可组合性:输出可作为其他命令的输入
- [ ] 一致性:相似命令有相似的参数和输出
状态模型设计
为什么需要状态模型?
flowchart LR
A[无状态设计] --> B[每次操作重新加载]
B --> C[性能低下]
B --> D[上下文丢失]
E[有状态设计] --> F[保持会话上下文]
F --> G[高效连续操作]
F --> H[支持撤销/重做]
style A fill:#ffcccc
style B fill:#ffcccc
style C fill:#ffcccc
style D fill:#ffcccc
style E fill:#e8f5e9
style F fill:#e8f5e9
style G fill:#e8f5e9
style H fill:#e8f5e9
状态对象设计
# 状态模型示例(GIMP)
class ProjectState:
"""项目状态对象"""
def __init__(self):
self.project_id: str = None
self.project_path: str = None
self.images: Dict[str, ImageState] = {}
self.active_image_id: str = None
self.modified: bool = False
self.history: List[Command] = []
self.history_position: int = -1
def to_dict(self) -> dict:
return {
"project_id": self.project_id,
"project_path": self.project_path,
"images": {k: v.to_dict() for k, v in self.images.items()},
"active_image_id": self.active_image_id,
"modified": self.modified,
"history_count": len(self.history),
"history_position": self.history_position
}
class ImageState:
"""图像状态对象"""
def __init__(self):
self.image_id: str = None
self.name: str = None
self.width: int = 0
self.height: int = 0
self.layers: List[LayerState] = []
self.active_layer_id: str = None
self.selection: SelectionState = None
class LayerState:
"""图层状态对象"""
def __init__(self):
self.layer_id: str = None
self.name: str = None
self.visible: bool = True
self.opacity: float = 100.0
self.position: Tuple[int, int] = (0, 0)
状态持久化
flowchart TD
A[REPL 会话] --> B[内存状态]
B --> C[定期保存]
C --> D[状态文件]
D --> E[会话恢复]
style B fill:#e3f2fd
style D fill:#f3e5f5
状态文件格式:
{
"version": "1.0.0",
"project_id": "proj_20260403_001",
"created_at": "2026-04-03T10:30:00Z",
"modified_at": "2026-04-03T11:45:00Z",
"active_image_id": "img_001",
"images": {
"img_001": {
"image_id": "img_001",
"name": "Photo.jpg",
"width": 1920,
"height": 1080,
"active_layer_id": "layer_002",
"layers": [
{
"layer_id": "layer_001",
"name": "Background",
"visible": true,
"opacity": 100.0
},
{
"layer_id": "layer_002",
"name": "Text",
"visible": true,
"opacity": 85.0
}
]
}
},
"history": [
{"command": "image new", "timestamp": "..."},
{"command": "layer new --name Text", "timestamp": "..."}
]
}
状态转换图
stateDiagram-v2
[*] --> Empty: 启动
Empty --> Opened: project open
Opened --> Modified: 执行编辑操作
Modified --> Saved: project save
Saved --> Opened: 继续编辑
Opened --> Empty: project close
Modified --> Empty: project close (discard)
REPL 设计
REPL 交互模式
flowchart TD
A[启动 REPL] --> B[显示 Banner]
B --> C[显示提示符]
C --> D{用户输入}
D --> E[解析命令]
E --> F[执行命令]
F --> G[显示结果]
G --> C
D --> H[退出]
style B fill:#e3f2fd
style C fill:#e3f2fd
style F fill:#e8f5e9
REPL 提示符设计
# 初始状态
cli-anything-gimp>
# 打开项目后
cli-anything-gimp[ProjectName]>
# 选择图像后
cli-anything-gimp[ProjectName/ImageName]>
# 未保存状态(添加 * 标记)
cli-anything-gimp[ProjectName]*>
REPL 命令集
# 内置 REPL 命令
REPL_COMMANDS = {
"help": "显示帮助信息",
"exit": "退出 REPL",
"quit": "退出 REPL(同 exit)",
"history": "显示命令历史",
"save": "保存当前状态",
"status": "显示当前状态",
"undo": "撤销上一步操作",
"redo": "重做上一步操作",
}
REPL 交互示例
$ cli-anything-gimp
╔══════════════════════════════════════════╗
║ cli-anything-gimp v1.0.0 ║
║ GIMP CLI for AI Agents ║
║ 16 commands, 4 command groups ║
╚══════════════════════════════════════════╝
Type 'help' for available commands, 'exit' to quit.
gimp> help
Available commands:
project Project management (new, open, save, close)
image Image operations (new, scale, crop, flatten)
layer Layer operations (new, delete, merge, move)
filter Filters and effects (blur, sharpen, color)
export Export and rendering (render, batch)
Built-in commands:
help Show this help message
history Show command history
save Save current state
status Show current status
undo Undo last operation
redo Redo last operation
exit Exit REPL
gimp> project new --name "MyProject" --template photo
✓ Created project: MyProject
Project ID: proj_20260403_abc123
Template: photo
gimp[MyProject]> image new --width 1920 --height 1080 --name "Photo"
✓ Created image: Photo (1920x1080)
Image ID: img_001
gimp[MyProject/Photo]> layer new --name "Background" --fill white
✓ Created layer: Background
gimp[MyProject/Photo]> filter gaussian-blur --layer Background --radius 5
✓ Applied Gaussian Blur (radius=5) to layer: Background
gimp[MyProject/Photo]*> undo
✓ Undone: filter gaussian-blur
gimp[MyProject/Photo]> export render output.png --format png
✓ Exported: output.png (1920x1080, 2.3 MB)
gimp[MyProject/Photo]> save
✓ Saved project state
gimp[MyProject/Photo]> exit
Goodbye! 👋
输出格式设计
人类可读格式
# 成功输出
✓ Created project: MyProject
Project ID: proj_20260403_abc123
Created at: 2026-04-03 10:30:00
# 错误输出
✗ Failed to open image: invalid_format.jpg
Error: Unsupported image format
Supported formats: PNG, JPEG, GIF, TIFF
# 警告输出
⚠ Image dimensions exceed recommended size
Current: 8000x6000 (48 MP)
Recommended: 4000x3000 (12 MP)
Performance may be impacted
JSON 格式(Agent 使用)
# 统一 JSON 输出结构
class CLIResponse:
"""CLI 统一响应格式"""
def __init__(self, success: bool, command: str):
self.success = success
self.command = command
self.timestamp = datetime.utcnow().isoformat()
self.data = {}
self.error = None
self.warnings = []
def to_dict(self) -> dict:
return {
"success": self.success,
"command": self.command,
"timestamp": self.timestamp,
"data": self.data,
"error": self.error,
"warnings": self.warnings
}
成功响应示例:
{
"success": true,
"command": "project new",
"timestamp": "2026-04-03T10:30:00.000Z",
"data": {
"project_id": "proj_20260403_abc123",
"project_name": "MyProject",
"template": "photo",
"created_at": "2026-04-03T10:30:00.000Z",
"state_file": "/home/user/.cli-anything/gimp/proj_20260403_abc123.json"
},
"error": null,
"warnings": []
}
错误响应示例:
{
"success": false,
"command": "image open",
"timestamp": "2026-04-03T10:31:00.000Z",
"data": {
"requested_path": "/path/to/invalid.xyz"
},
"error": {
"type": "UnsupportedFormatError",
"message": "File format '.xyz' is not supported",
"code": "E_FORMAT_UNSUPPORTED",
"suggestions": [
"Convert to supported format: PNG, JPEG, GIF, TIFF",
"Check file extension matches actual content"
]
},
"warnings": []
}
架构设计文档模板
Design 阶段输出的是一份完整的架构设计文档:
# 架构设计文档 - <软件名称> CLI
## 1. CLI 概览
- **CLI 名称**: cli-anything-<software>
- **版本**: 1.0.0
- **目标软件**: <软件名称> <版本>
- **设计目标**: 让 <软件> 可被 AI Agent 自动化调用
## 2. 命令组结构
### 2.1 顶层命令
cli-anything-
### 2.2 全局选项
| 选项 | 类型 | 默认值 | 说明 |
|-----|------|-------|------|
| `--json` | flag | false | 输出 JSON 格式 |
| `--project` | string | - | 指定项目状态文件 |
| `--verbose` | flag | false | 详细输出 |
| `--quiet` | flag | false | 静默模式 |
### 2.3 命令组
#### project - 项目管理
| 子命令 | 描述 | 关键参数 |
|-------|------|---------|
| `new` | 创建新项目 | `--name`, `--template` |
| `open` | 打开现有项目 | `--path` |
| `save` | 保存项目 | `--path` (可选) |
| `close` | 关闭项目 | `--discard` |
| `info` | 显示项目信息 | - |
#### image - 图像操作
| 子命令 | 描述 | 关键参数 |
|-------|------|---------|
| `new` | 创建新图像 | `--width`, `--height`, `--name` |
| `scale` | 缩放图像 | `--width`, `--height`, `--interpolation` |
| `crop` | 裁剪图像 | `--x`, `--y`, `--width`, `--height` |
| `flatten` | 合并所有图层 | `--merge-visible-only` |
... (其他命令组)
## 3. 状态模型
### 3.1 状态对象
```python
class ProjectState:
project_id: str
project_name: str
created_at: datetime
modified_at: datetime
images: Dict[str, ImageState]
active_image_id: Optional[str]
history: List[Command]
history_position: int
class ImageState:
image_id: str
name: str
width: int
height: int
layers: List[LayerState]
active_layer_id: Optional[str]
3.2 状态转换
[状态转换图]
4. REPL 设计
4.1 提示符格式
- 无项目:
<software>> - 有项目:
<software>[ProjectName]> - 有修改:
<software>[ProjectName]*>
4.2 内置命令
| 命令 | 功能 |
|---|---|
help |
显示帮助 |
history |
显示历史 |
undo |
撤销 |
redo |
重做 |
save |
保存状态 |
status |
显示状态 |
exit |
退出 |
5. 输出格式
5.1 人类可读格式
✓ Operation successful
Detail 1: ...
Detail 2: ...
5.2 JSON Schema
{
"success": boolean,
"command": string,
"timestamp": string (ISO8601),
"data": object,
"error": {
"type": string,
"message": string,
"code": string,
"suggestions": [string]
} | null,
"warnings": [string]
}
6. 错误处理
6.1 错误类型
| 错误代码 | 类型 | 说明 |
|---|---|---|
| E_FILE_NOT_FOUND | FileNotFoundError | 文件不存在 |
| E_FORMAT_UNSUPPORTED | UnsupportedFormatError | 格式不支持 |
| E_INVALID_PARAMETER | ValidationError | 参数无效 |
| E_SOFTWARE_NOT_FOUND | SoftwareNotFoundError | 目标软件未安装 |
6.2 错误响应格式
{
"success": false,
"error": {
"type": "ValidationError",
"message": "Width must be positive",
"code": "E_INVALID_PARAMETER",
"suggestions": ["Check your input values"]
}
}
7. 依赖关系
7.1 外部依赖
- <软件名称> >= <版本>
- Python >= 3.10
- Click >= 8.0
7.2 内部依赖
- cli_anything.
.core - cli_anything.
.repl - cli_anything.
.utils
8. 实现注意事项
8.1 关键设计决策
- 状态管理: 使用 JSON 文件持久化状态
- 撤销/重做: 使用命令模式实现
- 批处理: 支持从文件读取命令序列
- 并发: 单进程模式,避免并发冲突
8.2 性能考虑
- 大文件使用流式处理
- 状态文件定期保存,非每次操作
- 图像数据不保存在状态文件中,只保存路径
## 设计最佳实践
### DO(推荐做法)
```python
# ✅ 语义化的命令名称
@click.command()
def merge_down(): # 向下合并图层
pass
# ✅ 完善的参数验证
@click.option('--opacity', type=float, default=100.0,
callback=validate_opacity)
def set_opacity(opacity):
pass
def validate_opacity(ctx, param, value):
if not 0 <= value <= 100:
raise click.BadParameter('Opacity must be between 0 and 100')
return value
# ✅ 清晰的输出信息
@click.echo(f"✓ Created layer: {layer_name}")
DON’T(避免做法)
# ❌ 模糊的命令名称
@click.command()
def do_something():
pass
# ❌ 缺少验证
@click.option('--size', default='100')
def resize(size): # 没有验证 size 格式
pass
# ❌ 静默失败
def create_layer(name):
if not name:
return # 没有错误信息!
小结
Design 阶段将 Analyze 阶段的 API 映射转化为可实现的 CLI 架构:
- 命令组设计 - 合理组织命令,建立层次结构
- 状态模型 - 设计状态对象和持久化方案
- REPL 设计 - 定义交互模式和提示符
- 输出格式 - 统一人类可读和 JSON 输出
关键要点
- ✅ 命令命名语义化、一致性
- ✅ 状态模型完整支持 REPL 会话
- ✅ 输出格式兼顾人类和 Agent
- ✅ 错误处理完善,提供修复建议
- ✅ 文档详尽,便于后续实现
系列导航:
- ← 上一篇:教程 2:第一阶段 - 软件分析
- → 下一篇:教程 4:第三阶段 - CLI 实现
- 返回:教程系列索引