標籤: 人機協作

  • AI 不是答案販賣機:企業導入人機協作的實戰心法

    重點摘要(TL;DR)
    • 企業 AI 導入失敗,多半敗在把 AI 當成「問一句答一句」的 GPT 答案販賣機,而不是目標導向的人機協作:AI 供廣度與最新,人做判斷與收斂。
    • 差別在用法的 pattern,不在工具廠牌。同一個模型,用錯 pattern 就廢、用對就強。
    • 讓組織接受 AI,要種子優先:讓最被信服的領域師傅先贏一場,而且把 AI 定位成「放大他的判斷」,不是取代他。
    • 「問 AI 會不會拿我的資料去訓練?」要分兩層:機制上問(inference)≠ 訓練(training);政策上平台會不會留存是另一回事,所以敏感資料控制資料流。
    • 自己微調一個模型?先撞規模牆:小模型光參數就 7B、你連 7B 筆資料都沒有、堪用的要 70B 以上——多數團隊玩不起。

    企業 AI 導入這件事,真正難的從來不是技術 demo,而是怎麼讓一整個組織把 AI 用對。多數人腦袋裡的 AI,是 ChatGPT 那種「問一句、拿一個答案、結束」的答案販賣機;但實際有產值的人機協作,是另一回事。這篇把我在推動企業 AI 落地時整理出來的一套心法寫下來:從「pattern 不是工具」的觀念、怎麼用種子師傅落地,到幫傳統資料/系統背景的人釐清 LLM 到底是什麼。

    AI 不是「答案販賣機」:兩種用法的 pattern

    大家誤解的 AI 用法,是把它當成一個「知道答案的人」:問得模糊、答得模糊,答錯就下結論「AI 不準」。這是單回合的答案販賣機 pattern。真正會用的人,跑的是目標導向協作 pattern:帶一個可驗證的目標進去,AI 生一版,人用判斷砍掉一半、再問、再收斂。AI 答錯不是 AI 沒用,是還沒被你的判斷收斂。

    ❌ 答案販賣機 pattern(錯誤用法) ✅ 目標導向協作 pattern(用好的方式)
    單回合:問一句、拿一個答案、結束 多回合:不斷整理、收斂
    把 AI 當「知道答案的人」,答錯就放棄 AI 是材料供應商,不是答案
    問得模糊 → 答得模糊 → 結論「AI 不準」 人帶可驗證的目標來,才收斂得出東西

    差別在 pattern,不在工具廠牌

    這裡要特別澄清一件常被誤導的事:這不是「GPT 不行、某某工具才行」的工具戰。OpenAI 的模型一樣能跑 harness、跑 agent;Claude、Gemini 也是。重點是你用哪一種 pattern——同一個工具,用答案販賣機 pattern 就廢,用目標導向協作 pattern 就強。把焦點放在「怎麼用好」,而不是「押哪個廠牌」,這是企業導入時最該先校正的心態。想看協作底層怎麼串(Loop、Harness、MCP、A2A),可以參考〈一張圖看懂 AI Agent 系統〉。

    人機協作的分工:AI 供廣度,人做判斷

    協作的核心是分工:AI 負責廣度與最新,人負責判斷與商務收斂。舉兩個真實情境——我本身不懂 ESG 領域,但能產出可用的 ESG 內容,靠的不是「AI 很強」,而是「業務判斷 × AI 廣度」,主角是人。

    領域 AI 帶來 人(領域師傅)帶來
    ESG 最新法規、國際框架、同業做法 哪條跟自家業務有關、哪條是噪音、收斂到什麼決策
    HR 入職流程 同業最佳實務、欄位該收什麼 真實痛點、敏感資料邊界、主管要看什麼

    這張分工表還有一個關鍵作用:它把人的角色從「會被取代」翻成「不可取代的那一端」。答案要靠人的業務判斷才能收斂——這正是降低抗拒的鑰匙。

    怎麼讓組織接受 AI?種子優先,別碰師傅的逆鱗

    一個在「瞎忙、光追上就累」的組織,無法大爆炸式導入。務實的做法是種子優先、由上而下:挑最被信服(不是最資深、也不是去攻克最抗拒)的領域師傅,給他一場掛在他名下的勝利——讓他因為用了 AI 而更神,功勞歸他。一個受敬重的師傅公開贏一場,勝過十場培訓。關於為什麼多數企業 AI 落地會失敗,我在〈數位轉型不是換系統〉講得更細。

    而領域師傅最怕的是「你把我腦袋數位化完,就不需要我了」。破解法是把角色講清楚:AI 當 Generator、師傅當 Verifier——AI 先生一版,師傅一眼看出哪裡外行、打回去重做,他永遠是「說了算」的裁判。AI 結構上做不到的那塊(意外、跨領域、個人經驗、把失敗翻成靈感)永遠是人的。結果是:重複的 AI 做(工作輕鬆化)× 判斷被放大(價值最大化)。AI 是把師傅的判斷裝上擴音器,不是把師傅關進系統。

    導入成功怎麼衡量?
    不是「多少人學會了 SOP / prompt 範本」(那只是換了個更花俏的搜尋引擎),而是「多少師傅開始帶自己的目標來,而且會推翻 AI 的答案」——那才是真的在駕駛,換腦成功。

    「AI 會拿我的問題去訓練嗎?」那是上個時代的恐懼

    資料/資安背景的人最大的恐懼是:「我問 AI 公司機密,它是不是學起來、洩給對手?」這要分兩層講清楚,別混在一起。

    • 機制層:你問 AI,就像問一個有學問的人「從腦子裡想個答案」——他當下不會把你的問題拿去重練腦袋。你問的時候模型參數是凍結的,你的話只進「當下對話記憶(context)」,不進模型本體。問(inference)≠ 訓練(training)
    • 規模層:頂尖模型有海量公開語料 + 大量 AI 自己產的合成資料可用,根本不缺你那一筆。一筆查詢對十億級參數的模型,既不需要、也沒有統計意義。
    • 政策層:平台會不會「留存」你的對話、日後再拿去訓練,是另一回事——這層不能盡信「我們不訓練」的承諾。

    所以真正穩的做法,不是賭平台的承諾,而是控制資料流:哪些資料只在地端餵、哪些可上雲,是可控的切分。把「機制上的安全」跟「政策上的信任」分開,團隊就不會再被這個恐懼卡住。延伸閱讀:〈當 AI 夠重要,價值與風險就是一體兩面〉。

    那「自己微調一個模型」呢?先撞上規模這道牆

    有一種進階方法叫微調(fine-tune):拿你的數據去調模型的細部參數,讓通用模型變專精。聽起來很美,但對多數團隊,先撞上的是「規模」這道牆。

    這道牆 現實
    參數規模 常見的「小」模型,光參數就 7B(70 億)起跳;訓練它用的資料更是兆級 token,遠超參數量。
    你的資料量 你連 7B 筆自有資料都沒有;整個公司的資料庫,跟大廠訓練量比是一滴水。
    品質取捨 跑得動的 7B 通用能力不堪用,堪用的要 70B 以上、你又跑不動——兩頭都不划算。
    機器與數據門檻 GPU 叢集養不養得起、資料夠不夠乾淨、能不能驗證它調對了沒。

    務實結論:先把 prompt + 檢索(RAG)用好;真要微調,只在「單一窄任務 + 算過成本」時才考慮。模型的價值,正來自一個你複製不了的數據規模——理性的路不是「自己訓一個」,是「把這顆已吃過全世界資料的通用模型,指揮好」。雲端 vs 本地、訂閱 vs API 怎麼選,可以看〈Claude 分流實戰〉。

    LLM 是什麼?它是你會的 ML 養大的那一隻

    很多資料/系統背景的人看不懂 LLM,不是學不會,是太熟舊地圖。但要強調:這不是「新打敗舊」。你會的 ML——用大量數據訓練出一個數學模型來預測——正是 LLM 的根。LLM 就是把這件事推到極大:超大數據 + GPU 暴力算力,養出一個「通用」的數學模型。你懂 ML,你就懂 LLM 的根;它不是要你丟掉所學,是你那條線長大的盡頭。

    真正變的,是用法:

    你熟的做法 現在的做法 講白話
    每個任務,拿你的數據訓練一個專用模型 有一個出廠就通用的大模型,用「講人話」當場指揮 從訓練變指揮
    介面:寫 SQL / 程式 介面:寫自然語言(prompt) 從寫程式變講需求
    你的資料 = 拿去訓練 你的資料 = 當場貼給它參考(context)或檢索(RAG) 給它看,不是被它吞

    順帶釐清 tokenmodel 在架構的位置:model 是「會思考的通用算力」(不是資料庫),可分等級——便宜快的小模型 vs 貴而強的大模型,按任務取捨;token 是它的「水電表」,你輸入多長、它輸出多長,就用掉多少,帳就這樣算。

    一句話心法

    企業 AI 導入,不是換一個更聰明的搜尋引擎,是換一種工作分工:人守住判斷與目標,AI 負責生產與廣度,驗證用對抗。把焦點放在 pattern,不押工具;讓師傅當裁判,不是被取代;敏感資料控制資料流,不賭承諾。這三件做對,AI 才真的落得了地。

    延伸閱讀:〈傳產與金融怎麼把 AI 落地?三個真實場景〉、〈跟 AI 寫程式的 15 條軟工紀律〉。

  • AI 跟人類聯手 debug:從 OOM 假象到 Spring DI 真相

    重點摘要

    • 表面看是 OOM crash loop,真因是 Spring DI 失敗 — 16 個 Java 容器每 3 分鐘 die 一次,累積 ~180 次重啟。
    • AI 一連 4 次「以為修好其實沒」,被自己的 grep 騙了:OutOfMemoryError 抓到的是 JVM 啟動印出的 -XX:+ExitOnOutOfMemoryError env var 字串,不是真的 OOM exception。
    • 真正解開的轉折是「人類的業務直覺糾正方向」 — 我問 AI 「retry-job 不就是把訊息丟回去嗎,為什麼要查 DB?」,AI 才回去看代碼發現是別的 service 設計錯了。
    • 人機協作的甜蜜點:AI 挖技術細節,人類提業務 frame,兩個能力反過來會卡住。

    這篇是一次真實的除錯紀錄。起點是「CCBOT 沒回應」這種小事,沿途碰上 simpleec 電商系統的 channel-job crash loop、Spring DI 失敗偽裝成 OOM、retry-job 反覆修不對,中間 AI 跟我來回交鋒 7 次才把問題真正解開。寫下來主要是想討論一件事:AI 除錯到底比人快在哪裡、人類業務直覺又補了什麼,以及這個 GAP 怎麼填。

    一切從 CCBOT 沒回應開始

    那天我在 Telegram 對 ccbot(我自己寫的 Claude Code Telegram bridge)發訊息,沒回應。問 AI:「幫我看看你的 CCBOT 有沒有問題,怎麼現在好像沒有回應了?」

    AI 第一步 systemctl --user status ccbot,結果發現 service 是 inactive (dead) since 2026-05-14 22:34:09。已經死掉兩天。AI 接著推理出原因:那個時間點正好是我在 tmux session 開始搭一個 Kiro 版的 telegram bot,當時為了避免兩個 bot 搶同一個 chat,我手動 stop 了 ccbot — 然後忘了開回來。

    順手讓 AI 重啟 ccbot,順帶查一下系統健康度。然後就看到:

    load average: 85.25, 81.85, 80.28
    

    8 個 CPU core 的機器跑到 load 85+ 是失控。docker ps 一看,我的 simpleec 電商 OMS 系統 50 個容器裡,有十幾個 channel-job container 的狀態是 Up 21 seconds / Up About a minute / Up 3 minutes。container 一直在重啟,沒一個穩定。

    第一次以為修好:Kafka 調參(其實沒)

    AI 翻了 channel-job 的 application.yml,發現一個經典坑:Spring Kafka consumer 完全沒設 max-poll-recordsfetch-max-bytes。預設值是 500 records / 52 MB,意思是一個 poll() 就可能塞 52 MB 訊息進 heap。channel-job 容器配 -Xmx256m,扣掉 Spring Boot + Hibernate + otel-agent 的基底,剩餘 heap 根本撐不住單次 poll 的爆衝。

    這是真實問題,我也讓 AI 一次修了 6 個 *-job 的 yml,把 max-poll-records 壓到 5-10,fetch-max-bytes 砍到 1 MB,順便把 16 個 channel-* 容器的 -Xmx256m 升到 -Xmx384m 給點 buffer。AI 還順手抓到一個 Lombok bug:SeedTestOrdersHandlerfakeChannel.isActived(),但欄位是 Boolean(包裝類),Lombok @Data 對包裝類產生的 getter 是 getActived() 不是 isActived(),從 4/9 起 build 就壞了,但用 quick-redeploy 單服務 build 從沒抓到。

    build 過了,docker compose 重啟,5 分鐘觀察 — channel-* 都 Up 2 minutes,RestartCount=0,heap 用量 122-190 MB / 384 MB,看起來健康。AI 報告「OOM crash loop 修好了 ✅」,我也信了。

    一小時穩定觀察揭穿假象

    我說「先觀察 1 小時穩定再 commit」,AI 開一個 background watch 每 15 分鐘檢查。1 小時後通知回來,結果震驚:

    時間點 不穩定的 channel-* 數
    T+15min8 個「Up About a minute」(剛重啟)
    T+30min1 個「Up 2 seconds」(剛 crash)
    T+45min10 個「Up About a minute」
    T+60min2 個 (crash)

    每 3 分鐘 die 一次,docker 一直在 restart。AI 重新看 log,grep OutOfMemoryError 抓到 16 個容器全部有「OutOfMemoryError」字串,結論「真的是 OOM,我之前 Xmx 升 384 還不夠」。我點頭準備繼續升 heap。

    但 AI 接著做了一件對的事:再 grep 一次,這次用更精確的 pattern java\.lang\.OutOfMemoryError。結果 — 0 個 match。前面抓到的「OutOfMemoryError」全是這一行:

    Picked up JAVA_TOOL_OPTIONS: -Xmx384m -XX:+ExitOnOutOfMemoryError
                                                      ↑ 這 16 個字元被 grep 抓到
    

    JVM 啟動時印出的 JAVA_TOOL_OPTIONS 環境變數,字串裡的 ExitOnOutOfMemoryError 最後 16 字元剛好是 OutOfMemoryError。AI 自己被自己的 grep 騙了兩次。換用 UnsatisfiedDependencyException 重 grep,16 個容器全 match。真兇現身。

    真兇:半成品的 Excel 上傳功能

    真實死因是 Spring DI 失敗:

    UnsatisfiedDependencyException: Error creating bean 'channelJobConsumer'
      → parameter 5: 'processExcelBatchHandler'
      → parameter 0: No qualifying bean of type
        'com.simpleec.core.repository.ExcelUploadBatchRepository' available
    

    追下去:有人(可能是另一個 AI session,或我自己忘了)在 channel-job 模組加了一個 ProcessExcelBatchHandler,但它依賴的 ExcelUploadBatchRepository 放在 simpleec-core 模組。問題是 ChannelJobApplication@EnableJpaRepositories 只 scan com.simpleec.channeljob.repository,看不到 core 模組。Spring 啟動找不到 bean → exit 1 → docker restart → 又找不到 → 永久 crash loop。35 天沒人發現,因為 quick-redeploy.sh 每次只 build 單個 service,從沒做過整體 build,所以這個 DI 問題只在 docker container 啟動時才暴露。

    AI 提議「補 Spring 的 scan path」修。我看了一眼設計直覺說「不對 — 這個 Excel 功能根本不該在 channel-job 做,channel-job 是『跟外部 API 對接』,Excel 上傳是內部資料源,該丟到 backend-job 走 task.backend topic」。AI 同意,把 4 個 Excel 檔案 park 到 /tmp/excel-feature-parked/,留 README 跟 3 個 revert patch 等原開發者後續按正確架構重做。

    但這還沒完。修好 channel-* 後,我順手問「順帶看一下 retry-job」,因為它 RestartCount=1146,意思是 12 小時內重啟超過 1000 次。

    第二場拉鋸:retry-job 的反覆修不對

    AI 看 log,發現 retry-job 的 DltConsumer(處理 dead letter queue)也是 DI 失敗:依賴 core 模組的 FailedTaskLogRepository,但 retry-job 設計上排除了 DataSource。AI 提議「補 scan + 把 retry-job 也升 384m heap」。

    我打斷:「retry-job 不就是把訊息丟回 queue 嗎,為什麼要查 DB?」

    這句業務直覺問題改變了 AI 的方向。AI 回去看 RetryJobConsumer.javaDltConsumer.java 對比,發現:

    • RetryJobConsumer 處理 task.failed — 純 Redis ZSet 存延遲排程,到時間丟回原 topic,完全沒 DB。設計正確。
    • DltConsumer 處理 task.dlt — 把死信寫到 failed_task_logs 表供「人工檢查」(註解明說),只 write 沒 read

    所以「retry-job 不該重」這個設計直覺是對的,但 DltConsumer 確實有合理需求要寫一筆 audit log。修法收斂成:用 JdbcTemplate 寫一行 INSERT,不載 Hibernate(可以省 50-80 MB heap),DltConsumer 改 5 行,RetryJobApplication 移除 DataSourceAutoConfiguration exclude。AI rebuild,等 90 秒,RestartCount=0,Status=running。報告「修好了」。

    17 小時後我回來檢查,retry-job RestartCount=1159。又開始 crash 了。

    原因:AI 第一次修法不完整。Spring Boot autoconfig 是「貪食」的 — 我們移除了 DataSourceAutoConfiguration exclude 讓 DataSource 能建,但 classpath 上 simpleec-core 還有一堆 JpaRepository class(ChannelRepository、OrderRepository…),Spring 看到就觸發 JpaRepositoriesAutoConfiguration,試圖建 entityManagerFactory 來支撐這些 Repository,但 Hibernate 還是 excluded → bean 不存在 → context refresh 失敗 → 啟動 exit。

    關鍵教訓:看 docker 的「Up X seconds」不代表健康。Spring Boot 啟動要 ~6-10 秒,如果在 context refresh 時 crash,container exit 後 docker 立刻 restart,新 instance 的 uptime 立刻變 Up About a minute — 但它正在「啟動 → 失敗 → 再啟動」的循環。AI 之前 90 秒驗證看 Up time 沒看 RestartCount 趨勢,被騙了。

    真正的修法是再加一個 exclude:JpaRepositoriesAutoConfiguration.class。告訴 Spring「即使 classpath 有 JpaRepository class,也不要 scan」。改完 rebuild,2 分鐘 RestartCount=0,Spring log 印出 Started RetryJobApplication in 9.585 secondsRETRY JOB Started (Layer A - Foundation) banner — 這次是真的啟動完成,不是「啟動失敗 + 立刻 restart」的假象。

    AI 的角度 vs 我的角度

    退一步看,這場 7 個小時的除錯,我跟 AI 各自看到的「問題」完全不同:

    面向 AI 看到的 我看到的
    問題本質系統訊號(load 85、container die、error log)業務職責(channel-job 該做什麼、retry-job 該做什麼)
    調查工具grep、docker stats、jstack 思路、Spring autoconfig 知識「這個 service 設計上應該長什麼樣」的直覺
    修法第一反應「補 scan path / 加 exclude / 升 heap」(技術 patch)「這個東西放錯位置了」(架構糾錯)
    驗證標準RestartCount、Status=running、log 是否有 ERROR「跑起來行為符合我預期嗎?」
    盲點被 grep 假陽性騙、被 docker Up time 假象騙、技術細節驅動忘了業務 frame不會自己去看 log/code、需要 AI 幫我把抽象設計直覺落地成具體修法

    關鍵時刻有兩個。第一個是「AI 自己抓到自己的 grep 假象」 — 這需要 AI 對自己之前的結論保持懷疑,願意重新驗證。第二個是「我問了一句業務問題」 — retry-job 不該重,channel-job 該不該碰 DB。這兩種角度互補,缺一個都會卡。

    我們的 GAP 在哪

    AI 跟人類的 GAP 不對等 — 不是「誰比較強」的問題,是「擅長的層級不同」:

    AI 的擅長:在大量資訊裡找 pattern、寫 bash one-liner、記得框架的暗角配置(JpaRepositoriesAutoConfiguration 這種冷門 class 名)。每秒可以掃 100 個檔案、grep 一萬行 log。但 AI 容易過度信任「表面訊號」 — log 裡有 OOM 字樣就以為是 OOM、container Up 1 minute 就以為穩定。AI 也容易跳到「技術修法」,缺少業務 frame 時會給出 over-engineered 答案(channel-job 不能查 DB? 補 scan 就好啦)。

    人類的擅長:對「事情該怎樣」有直覺。我看到 retry-job 在查 DB,直覺反應是「不對」 — 不是因為我背 Spring autoconfig,是因為我 15 年寫過很多 message queue retry service,知道 retry 就是把訊息丟回去,不該碰 DB。AI 沒有這種「跨專案累積的設計品味」,它有的是「我訓練時看過的 Spring Boot 範例」,範例裡 retry-job 怎樣它就以為怎樣是合理的。

    但人類的弱點也明顯:我不會主動去 grep log、不會耐心讀 100 行 Spring 啟動 trace 找 root cause、也記不住 HibernateJpaAutoConfigurationJpaRepositoriesAutoConfiguration 的差別。我需要 AI 把我的直覺翻譯成具體修法。

    真正的 GAP 不在能力,而在溝通的精準度。當我說「retry-job 不該重」,如果 AI 沒抓到我意思是「不該載 Hibernate」,可能就會修錯方向。當 AI 報告「OOM crash 修好了」,如果我沒追問「真的 1 小時都穩定嗎?」,可能就會在 commit 後 production 出包。

    怎麼一起工作得更好

    這次經驗給我幾個具體做法:

    1. 業務 frame 永遠先說 — 啟動 debug 任務前,先告訴 AI「這個 service 設計上是幹嘛的,該做什麼不該做什麼」。AI 才不會跳進「補 scan / 升 heap」的局部修。
    2. 驗證標準要明確 — 不要說「跑 90 秒看穩不穩」(會被 Up time 假象騙),要說「看 RestartCount 趨勢、看 Spring 啟動 log 有沒有 Started XxxApplication in N seconds」。
    3. AI 給結論時人要追問「真的嗎」 — 第一次說「OOM 修好了」,我點頭就會直接 commit。改成「我們先觀察 1 小時」這個本能,救了這次 deploy。
    4. 「以為修好其實沒」要記成 brain — 我有一套 brain file 系統累積各種坑(訓馬筆記裡有詳細介紹)。這次 Spring autoconfig 的雜食陷阱、Lombok Boolean wrapper getter、grep 假陽性、docker Up time 假象,全部寫進 brain,下次任何 Java 專案都會 trigger 警示。
    5. 技術細節 AI 挖、業務 frame 人類訂、修法兩個一起決 — 不要讓 AI 完全自主,也不要把 AI 當 stack overflow 查工具。最大產出來自「兩個視角持續對話」。

    總結

    這次除錯從 ccbot 沒回應開始,連鎖挖到 channel-job + retry-job 兩個 Spring DI 真因,過程中 AI 被自己的 grep 騙、被 docker Up time 假象騙、被「修一個 service 就 OK」的局部視角騙。最後修對的關鍵是兩件事 — AI 對自己之前的結論保持懷疑願意重 grep,跟我用業務直覺把方向拉回來。

    我也越來越相信:AI 除錯不會取代人類,只會讓「業務直覺好的人」更強。如果你只是想找個工具按 enter 就把 production 修好,AI 會給你看似合理但實質繞遠路的修法。如果你能用業務直覺糾正方向,AI 就是你身邊那個記憶力極好、bash 寫得飛快、能同時跑 8 個 background task 的隊友。

    關於 simpleec OMS 系統本身的設計,可以看 多通路電商 OMS 系統實戰系列。前一次我跟 AI 一起做 code review 找到 20 個 bug 的紀錄在這篇。這次的 7 小時除錯,跟前面那次 review 的差別是 — 那次是「靜態看 code」,這次是「跑起來才發現的動態問題」。兩種 AI 協作模式都有用,搭配起來才完整。