- date
- entry
- 039
- topic
- tooling
- rev
- —
codex exec 把 -i 放在 prompt 前面:prompt 被當成第二個圖檔路徑
同一行指令把 -i 從 prompt 後面搬到前面,就變成「No prompt provided via stdin」。原因是 -i 一次收好幾個值;stdin 有內容時更糟,它不報錯,直接換掉你的 prompt。
我請 Codex 看圖時,原本的寫法是 codex exec "<prompt>" -i <圖檔>:prompt 在前,圖在後。10 月 13 日凌晨 0 點 32 分,一個幫官網做配圖的工作階段把順序反過來,寫成 -i <參考圖> "<prompt>",stdin 依我的指令範本重導向到 /dev/null。
結果它沒有開始工作。log 只有兩行:Reading prompt from stdin...、No prompt provided via stdin.,prompt 明明寫在命令列上。這篇要談的是那段 prompt 去了哪裡。依序回答四件事:怎麼重現、為什麼會被吃掉、stdin 有內容或一直開著時各會怎樣,以及三種不會出事的寫法。
重現:0.2 秒就結束
那次的修法是把 --image 移到 prompt 後面,同一個工作階段重跑,約一分鐘後工具回報圖已產生。為了把現象單獨拿出來,我用一張 8×8 的紅色 PNG(red.png)在 codex-cli 0.159.2 重跑同樣的順序:
$ codex exec --skip-git-repo-check -s read-only -i red.png "Reply with exactly: OK" < /dev/null
Reading prompt from stdin...
No prompt provided via stdin.
$ echo $?
1
整個指令 0.23 秒結束,在讀取 prompt 的階段就停了,沒有任何模型回覆。它不是卡住,也沒有說 prompt 有問題,只說 stdin 裡沒有 prompt。把 prompt 移到 -i red.png 前面,就正常回答(最後一節有實測)。
為什麼會被吃掉:-i 一次收好幾個值
codex exec --help 裡這兩段放在一起看就清楚了:
Usage: codex exec [OPTIONS] [PROMPT]
[PROMPT]
Initial instructions for the agent. If not provided as an argument (or if `-` is used),
instructions are read from stdin. ...
-i, --image <FILE>...
Optional image(s) to attach to the initial prompt
<FILE>... 的三個點表示這個選項可以一次收多個值。在這個例子裡,-i 後面緊接的非選項參數被一個個當成圖檔路徑收下(下面的 -- 和等號寫法可以切開它們)。所以 -i red.png "Reply with exactly: OK" 在它眼裡是兩個圖檔路徑:red.png,和一個檔名叫 Reply with exactly: OK 的檔案(第二個讀取失敗)。位置參數 PROMPT 因此是空的,照說明的第一句,它改去讀 stdin。
我又試了 -i red.png red.png "Reply with exactly: OK",結果一樣,和「三個值都被收進 -i」的解釋一致。引號讓這句話成為一個 shell 參數;它歸 -i 還是歸 PROMPT,由 codex 的命令列解析決定。
stdin 有內容時:不報錯,prompt 被換掉
上面那次會失敗,是因為 stdin 是空的。stdin 有內容、而且讀到 EOF 時,這次測試是成功完成的,只是回答的不是我問的問題(下面只列 stdout,stderr 的啟動資訊省略):
$ echo "Reply with exactly: STDIN-OK" | \
codex exec --skip-git-repo-check -s read-only -i red.png "Reply with exactly: ARG-OK"
STDIN-OK
$ echo $?
0
這次的 session 紀錄(~/.codex/sessions/ 底下的 rollout JSONL)裡,送給模型的使用者訊息依序是:red.png 這張圖、一行「Codex could not read the local image at `Reply with exactly: ARG-OK`: No such file or directory (os error 2)」,最後是 stdin 那句話。也就是說,我寫的 prompt 變成一條「找不到圖檔」的提示交給模型,終端機上沒有印出任何讀圖失敗的警告,結束碼是 0。
這種情況比 0.2 秒失敗難發現。假設一支腳本把 diff 從 stdin 餵進去,再用 -i 附一張截圖、後面接「請檢查這段改動有沒有破壞版面」:照上面的行為,那句指示會被當成檔名,模型拿到的是 diff、截圖和一條找不到檔案的提示。它可能還是會回一份報告,但正常的 prompt 欄位裡沒有你的指示,不能假定它照你的要求檢查版面(這是照實測行為推的情境,我沒有真的這樣用過)。
stdin 一直開著時:等
第三種情況是 stdin 是一條沒關的 pipe。〈codex exec 卡住不動〉寫過,它會讀 stdin 讀到 EOF。prompt 被 -i 吃掉之後,讀 stdin 就成了它唯一的 prompt 來源。用 sleep 8 | 模擬一條開著 8 秒的 pipe:
$ sleep 8 | codex exec --skip-git-repo-check -s read-only -i red.png "Reply with exactly: OK"
Reading prompt from stdin...
No prompt provided via stdin.
# 量測附註:exit=1,約 8 秒
它等到 pipe 關掉才失敗。025 那篇量到 60 秒仍未結束的是「有 prompt、另讀 stdin」那條路;這裡是另一條路,但同樣要等到 EOF。從程式呼叫、又沒有關 stdin 的寫入端時,照這個機制推,它會停在 Reading prompt from stdin... 一直等,看起來跟當機一樣;在這個例子裡,原因是 prompt 在命令列上被收走了。
三種不會被吃掉的寫法
同一張紅色圖,問「圖片主要是什麼顏色,回一個小寫單字」,三種寫法都回 red、結束碼 0,和「圖有附上、prompt 也讀到了」相符(檔名本身也帶著顏色,要嚴格確認可以看 session 紀錄裡的 input_image):
P="What is the dominant color of the attached image? Reply with one lowercase word."
# 1. prompt 放在 -i 前面
codex exec --skip-git-repo-check -s read-only "$P" -i red.png < /dev/null
# 2. 用 -- 結束選項,後面的一律是位置參數
codex exec --skip-git-repo-check -s read-only -i red.png -- "$P" < /dev/null
# 3. 用等號把值綁在選項上
codex exec --skip-git-repo-check -s read-only --image=red.png "$P" < /dev/null
我現在的指令範本寫的是第一種,理由是它最不依賴對解析器的記憶:一般的非空 prompt 放在第一個位置參數,後面的 -i 就收不到它。第二種適合 prompt 可能以 - 開頭的情況;但 prompt 剛好只有一個 - 時,照說明它仍代表「讀 stdin」。三種都接 < /dev/null,萬一順序又寫錯,它會在取得 prompt 時立刻失敗(這次量到 0.23 秒),不會等呼叫端的 pipe,也不會讓 stdin 的內容頂替 prompt。
有 session 紀錄時,可以直接看裡面的使用者訊息確認 prompt 有沒有送進去。出現「could not read the local image at `…`」、反引號裡正好是你原本的 prompt,而正常的 prompt 不見了,就是這個問題。
如果只記得一件事
在 codex exec,-i 和 prompt 之間要用上面三種寫法之一隔開。說明裡的 <FILE>... 表示它收多個值;在這個版本,-i 會收走緊接在後的 prompt。開頭那次是立刻失敗,算是運氣好;stdin 有內容時,同一個錯會安靜地換掉你的 prompt。
修訂紀錄
- 首次發布