開始寫鐵人賽之前,我原本以為最麻煩的事情,是每天從大量程式碼、測試紀錄和 Git 變更裡整理出「今天到底做了什麼」。
真正開始做之後,我才發現,比整理更危險的是另一件事:
AI 太容易替開發者宣布成功。
只要今天改了幾個檔案、跑過一些測試,語言模型就很容易把這些零散資訊整理成一篇完整、流暢,而且語氣非常肯定的成功故事。
例如:
今天成功完成了整套自動化流程。
問題是,工程現場可能根本不是這樣。
也許文章已經生成。
也許 57 個 focused tests 都通過。
也許安全掃描全部是綠色。
但 Chromium 沒有成功啟動、完整 build 受到環境權限阻擋,甚至真正的 iThome 草稿根本還沒有被保存。
如果 Writer 最後只是為了讓文章「讀起來完整」,把這些狀態全部壓成一句「今天完成了」,那它反而會成為整套系統裡最不可信任的一環。
所以 ARCI 鐵人賽 Day 01,我沒有先做一個更會寫文章的 AI。
我先做了一個:
會阻止自己報喜的工程日誌。
ARCI 一直在處理一個我很在意的問題:
AI 做出的判斷,到底能不能被解釋、驗證,甚至在條件不足時選擇停止?
如果 ARCI 自己的開發日誌卻會把「部分完成」潤飾成「全部完成」,那後面再談可信任決策,其實會有點矛盾。
因此我把每日文章生成流程重新拆開。
最簡單的作法原本是:
Git / Tests
↓
LLM
↓
文章
但我最後改成:
工程現場
↓
Evidence Collector
↓
Fact Admission
↓
Journal Writer
↓
Publication Safety
↓
公開草稿
這幾層看起來只是多繞了一圈,但責任完全不同。
Evidence Collector 負責回答:
今天到底真的發生了什麼?
Fact Admission 負責回答:
哪些事情已經有足夠證據,可以公開寫進文章?
Writer 最後才回答:
我要怎麼把這些事實寫成人看得懂的故事?
這個順序非常重要。
因為 Writer 從此不再擁有「決定事實」的權力。
最早的工程日誌其實已經有很完整的 evidence system。
問題是,它讀起來不像文章。
每一個結論後面可能跟著:
對稽核來說很好。
對讀者來說,非常痛苦。
但如果把這些東西全部拿掉,又走到另一個極端:
文章變好讀了,卻沒有人知道裡面的重大結論到底是不是真的。
所以我最後採用兩條輸出。
負責讓人閱讀。
保留:
負責讓系統與開發者追查。
保留:
兩份文件描述的是同一件事情,只是面對不同讀者。
證據約束文章,但證據本身不需要變成文章。
這是今天整套系統裡,我最喜歡的一條規則。
文章裡的重大主張不能直接從 raw data 進 Writer,而必須先經過:
Raw Evidence
↓
Normalize
↓
Fact Admission
↓
Publishable Fact
假設今天真正的工程結果是:
PARTIAL
BLOCKED_ENVIRONMENT
那 Writer 可以解釋:
今天主要功能已完成,但完整驗收仍受到執行環境限制。
它不能自己把它改成:
今天成功完成整套系統。
同樣地:
NOT_RUN
不能被改寫成:
PASS
這聽起來應該是很基本的事情。
但語言模型最擅長的能力之一,就是把故事補完整。
工程系統有時候真正需要的,反而是:
故事不完整,就讓它保持不完整。
Ironman subsystem 的 focused tests:
9 files
57 tests
PASS
TypeScript typecheck:
PASS
TypeScript build:
PASS
standalone Vite build:
PASS
Publication Safety 也完成了:
12 / 12 gates PASS
findings = 0
也就是至少在目前的文章產生與公開安全範圍內,沒有發現:
如果只把這些數字挑出來看,其實非常容易寫出一句:
所有驗證順利完成。
但完整結果不是這樣。
Ironman 的整體測試是:
57 / 58 PASS
唯一沒有通過的 controlled Chromium 測試,是因為執行環境拒絕 Chromium 的 bootstrap_check_in。
完整 repository test 則記錄到:
545 PASS
18 FAIL
121 SKIP
總計 684 個測試項目。
失敗主要集中在:
另外完整:
npm run build
因為 tsx IPC pipe 遇到:
EPERM
而失敗。
npm audit 也因 registry DNS:
ENOTFOUND
無法完成。
這些結果都不能被 Writer 隱藏。
因為:
測試程式碼能工作
和:
整個外部流程已被驗證
是兩個完全不同的命題。
這次 Day 01 的主要文章、架構圖、evidence map、安全掃描與 fact grounding 都已經完成。
但是目前真正的外部路徑仍然沒有完整證據:
Article
↓
Authenticated Browser
↓
iThome Editor
↓
Image Upload
↓
Preview
↓
Save Draft
↓
Reload
↓
Same Content
因此 manifest 中最重要的狀態仍然是:
securityScan = PASS
factGrounding = PASS
DRAFT = NOT_RUN
PUBLISH = NOT_RUN
其中:
DRAFT = NOT_RUN
不能被改成:
DRAFT = SAVED
因為目前還沒有 authenticated iThome session 與真正的外部草稿保存證據。
這也是今天這套系統真正發揮作用的地方。
它沒有因為文章已經寫好了,就順手假設網站那一段也成功了。
原本第一次 closeout 時,Git commit 也受到環境權限問題阻擋。
.git/index.lock 無法正常寫入,所以當時的狀態只能老實記成:
Commit = NOT_CREATED
而不是硬湊一個 SHA。
後來我重新處理這個收尾步驟,最後成功把 Day 01 journal 與 publication safety 相關變更收進一個 bounded commit。
也就是說,這裡的狀態真的從:
NOT_CREATED
變成了:
COMMITTED
這次我刻意把這件事情保留下來,因為它其實很能說明整套設計的核心。
第一次失敗時,系統沒有搶先說成功。
後來真的完成之後,狀態才被更新。
不是因為文章需要一個漂亮的結尾。
而是因為工程現場真的多了一份新的證據。
以前我沒有特別在意 NOT_RUN 這個詞。
它很像一句:
還沒做而已。
但這次我開始覺得,它其實是自動化系統非常重要的一種語言。
因為:
PASS
代表:
我做了,而且成功。
FAIL
代表:
我做了,而且失敗。
NOT_RUN
代表:
我根本還沒有足夠的觀測結果可以回答。
這三個狀態不能互換。
而 AI 最容易做的事情,就是偷偷把第三個變成第一個。
因此我開始把:
UNKNOWN
NOT_RUN
BLOCKED
PARTIAL
全部當成一級狀態。
不是錯誤訊息。
而是系統正式能表達的答案。
這裡還有一個我覺得很容易混淆的地方。
現在 Day 01 已經有 commit。
這證明的是:
今天的工程成果已經形成一個可以被 Git 追蹤的版本。
它不證明:
iThome 草稿已經保存。
更不證明:
文章已經發布。
所以目前狀態仍然應該拆開:
Code / Journal
↓
COMMITTED
但是:
iThome Draft
↓
NOT_RUN
以及:
Publish
↓
NOT_RUN
我越做越覺得,這種「不要把不同層級的成功合併成一個成功」的觀念,可能不只適用在日誌系統。
之後 ARCI 真正處理決策時也一樣。
模型產生了一個答案,不代表決策完成。
決策被接受,不代表執行完成。
執行完成,也不代表結果真的有效。
每一個箭頭,都應該有自己的證據。
如果要問 Day 01 做出了什麼,我現在反而不會回答:
一套 AI 自動寫文章系統。
比較接近的是:
一套限制 AI 寫作權力的系統。
Writer 可以:
但是它不能:
NOT_RUN 寫成 PASS
PARTIAL 改成 READY
這些限制看起來很保守。
但如果未來 ARCI 真正要做更大的自主決策,我希望它從很小的事情開始,就保留這種習慣:
有能力產生答案,不代表有資格宣布答案。
現在 Git 這條線已經往前走了一步。
下一個真正缺少的,就是 iThome 草稿驗證。
下一輪我要驗證:
登入狀態
↓
找到 Editor
↓
填入公開稿
↓
上傳圖片
↓
Preview
↓
Save Draft
↓
Reload
↓
確認內容一致
同時還有一個硬條件:
Publish Action Count = 0
也就是第一階段只驗證:
DRAFT = SAVED
不驗證:
PUBLISH = DONE
只有當真正的瀏覽器操作留下可接受的外部證據,我才會讓 manifest 改變。
在那之前,它仍然只能是:
NOT_RUN
我本來以為第一天會寫:
如何讓 AI 自動生成漂亮的開發日誌。
結果最後做的事情幾乎完全相反。
我花了一天讓 Writer 知道:
什麼時候不要把文章寫得太漂亮。
今天確實比第一輪又往前走了一步。
文章完成了。
安全檢查通過了。
測試留下了完整結果。
而原本沒能建立的 Git commit,後來也真正完成了。
但 iThome 草稿仍然沒有被假裝成已保存。
發布更沒有發生。
這也是我希望整個系列之後一直保留的標準:
可以失敗。
可以卡住。
可以隔幾個小時才補完。
但每一個「完成」,都必須等證據真的出現之後再說。
對一個想做可信任決策系統的高中生開發者來說,我覺得這個習慣,比第一天就做出一個全自動發文機器更重要。
至少今天,當系統有機會提前報喜時,它沒有這麼做。
等事情真的完成之後,它才改口。
我覺得這是一個不錯的 Day 01。