純文字、標準輸入輸出與高可組合性,讓 CLI 不再只是工程師操作系統的工具,也是 AI Agent 理解環境、執行命令與進行自我修正的介面。過去 CLI 的設計核心是以人為本,追求豐富的彩色表格、進度條與互動選單;然而當呼叫端換成 Agent 或自動化腳本時,這些介面會造成意外中斷。
Agent 呼叫 CLI 時透過標準輸入(stdin)、標準輸出(stdout)、標準錯誤(stderr)與 Exit Code 進行溝通。當 CLI 在執行途中彈出方向鍵選單,Agent 會因為無法提供互動輸入而卡死直到逾時;當 ANSI 控制碼混入輸出,解析欄位就會產生雜訊;當錯誤訊息只寫 Invalid argument,Agent 便無法判斷該如何修正。
這套針對 Agent 的 CLI 設計思維,主要參考了 Eric Zakariasson 與 Google Workspace CLI(gws)作者 Justin Poehnelt 的經驗分享。
Agent 執行命令前需要了解命令名稱、位置參數與 Flag 用法。--help 因此不只是給人類看的說明書,更是 CLI 對外公開的 API 介面規格。
每個命令的說明至少需要包含:
Connect to a database server.
Usage:
dbtool connect --server <name> --user <name> [flags]
Flags:
--server string Server name. Valid values: db-1, db-2, db-3.
--user string Database user name.
--output string Output format: table or json. (default "table")
Examples:
dbtool connect --server db-1 --user admin
dbtool connect --server db-1 --user admin --output json
單純標註「指定伺服器」缺乏具體的合法值資訊。把 db-1, db-2, db-3 與範例寫進 --help,Agent 才能在出錯時參考說明自行修復。
非互動路徑指的是命令執行時能一次性接收所有參數,不需要等待使用者在終端機輸入回答。
當人類在終端機操作時,CLI 可能跳出選單或問答提示:
$ dbtool init
? Select database engine: (use arrow keys)
> postgres
mysql
? Set max connection pool size: 50
? Enable read replica pool? (Y/n) y
這種互動問答適合人類用鍵盤選擇,但當呼叫端是 AI Agent 或自動化腳本時,程式會停在 stdin 等待使用者輸入,最後因為久未收到回答而逾時中斷。
為了支援自動化呼叫,CLI 應提供完整的非互動執行路徑,讓呼叫端在下達命令時一次傳入所有參數:
$ dbtool init --engine postgres --pool-size 50 --read-replica --yes
$ dbtool init --json '{"engine":"postgres","pool":{"max":50,"idle":10},"replicas":["ro-1","ro-2"]}'
在 Go 與 Cobra 中,可以用 term.IsTerminal 檢查 stdin 是否連接到真實終端機。如果缺少必要參數且處於非 TTY 環境(例如被 Agent 呼叫或在管道中),程式應立即報錯終止,而不是顯示互動表單:
package main
import (
"fmt"
"os"
"golang.org/x/term"
)
func canPrompt(f *os.File) bool {
return term.IsTerminal(int(f.Fd()))
}
func runConnect(server, user string) error {
if server == "" {
if !canPrompt(os.Stdin) {
return fmt.Errorf("missing required flag --server in non-interactive mode")
}
// 人類模式:啟動互動選單詢問 server
server = promptUserForServer()
}
return connectServer(server, user)
}
為了清楚區分自動化的控制行為,CLI 應定義明確的控制 Flag:
--non-interactive:強制停用所有互動選單;缺乏必要輸入時直接回傳失敗。--yes(或 -y):對所有確認詢問預設回答同意,適用於批次執行。--force:強制執行危險或跳過特定保護檢查的操作(例如覆寫已有檔案)。文字表格適合人類視覺閱讀,但字串邊界與空白對齊對程式解析不友善。例如以下輸出:
NAME STATUS UPTIME
production database running 1h
呼叫端無法單憑空白切割精準判斷 production database 是一個完整的名稱還是兩個獨立欄位。
當指定 --output json 時,CLI 應輸出格式合法的 JSON 結構,寫入 stdout:
$ dbtool status --output json
{
"name": "production database",
"status": "running",
"uptime_seconds": 3600
}
在 Go 中實作結構化輸出時,把資料寫入 cmd.OutOrStdout() 能確保輸出可被測試與重導向:
type StatusResponse struct {
Name string `json:"name"`
Status string `json:"status"`
UptimeSeconds int64 `json:"uptime_seconds"`
}
func renderStatus(cmd *cobra.Command, resp StatusResponse, format string) error {
if format == "json" {
encoder := json.NewEncoder(cmd.OutOrStdout())
encoder.SetIndent("", " ")
return encoder.Encode(resp)
}
// 預設人類模式:格式化表格
return printTable(cmd.OutOrStdout(), resp)
}
當 CLI 的輸出直接進入 LLM 的 Context Window 時,未經篩選的巨量資料會快速消耗模型容量,並因雜訊過多而降低推理精度。例如查詢資料庫清單若一次回傳上千個欄位的完整 Schema,模型很容易在雜訊中漏看關鍵屬性。
為此,CLI 應具備控制輸出規模的機制:
--fields id,name,status Flag,只回傳下游決策所需的鍵值,主動剔除龐大的除錯日誌與中繼資料。--limit 20),避免單次傾倒大量 JSON 陣列。--full 或 --verbose Flag 時才輸出完整內容。結構化輸出配合視窗保護,讓命令能無缝融入 Shell 管道。結合 jq 等工具,Agent 能精準揀選欄位傳給下一個命令:
$ dbtool list --fields id --output json | jq -r '.[].id' | dbtool delete --stdin --dry-run
當命令執行失敗時,籠統的錯誤訊息(如 Error: invalid parameter)無法幫助呼叫端修復問題。可行動的錯誤訊息應同時說明原因、合法選項與修復建議。在非互動模式下,錯誤訊息應輸出至 stderr:
$ dbtool connect --user admin --output json
輸出至 stderr 的內容:
{
"error": {
"code": "MISSING_REQUIRED_FLAG",
"message": "required flag \"--server\" is missing",
"hint": "specify a valid server with --server: db-1, db-2, db-3",
"help_command": "dbtool connect --help"
}
}
這種結構化錯誤設計包含以下層次:
code:機器可讀的錯誤代碼,供程式進行分支判定。message:清楚說明問題所在。hint:提供修復問題所需的下一步操作與合法參數清單。同時,CLI 必須使用正確的 Exit Code:
0:成功執行。1:一般業務邏輯失敗(例如資源找不到、連線逾時)。2:命令語法或參數錯誤(Misuse of Shell builtin / flags)。3:認證或權限不足。明確的 Exit Code 讓 Agent 無需解析複雜文字,即可快速決定要重試、修正參數還是尋求人類協助。
--dry-run 預覽破壞性操作刪除資料、修改設定或停用服務等具副作用的操作,應提供 --dry-run 預覽 Flag。
執行 --dry-run 時,CLI 會完成完整的參數驗證、權限檢查與變更比對,並回傳預計執行的變更細節,但不會對系統造成實際異動:
$ dbtool delete --user-id u-12345 --dry-run --output json
{
"action": "delete",
"target_resource": "user",
"resource_id": "u-12345",
"affected_records": 1,
"executed": false
}
Agent 可以先執行 --dry-run 取得 JSON 報告,供人類確認無誤後,再移除 --dry-run 進行正式變更。這是在自動化環境中防止破壞性操作的重要防護機制。
終端機的 --help 只能定義單一命令的語法與參數型別,無法表達跨命令的業務規則與環境防護要求。
為了讓 AI Agent 理解整體工具的操作約束(Operational Constraints),可以在專案儲存庫中提供 AGENTS.md 或 SKILL.md。例如 Google Workspace CLI(gws)透過隨附的 Skills 檔案宣告自動化呼叫的標準規範。針對像 dbtool 這類資料庫維護工具,可在檔案中明確約定:
delete 或 truncate 等破壞性操作前,必須先執行 --dry-run 比對影響筆數。--limit 與 --fields,禁止無條件全表掃描。現代 CLI 的設計並非要在人類操作與機器自動化之間選邊站,兩者同樣重要:
--help 說明,能大幅降低人腦的認知負擔,避免在手動操作時出錯。透過標準串流分流、TTY 自動偵測與 --output json 等控制 Flag,同一套命令列工具既能在終端機裡為工程師提供舒適的操作體驗,也能在背景無縫對接 AI Agent,打造出兼具直覺回饋與自動化效率的現代 CLI。