JPMorgan Chase 怎麼用 AI agent 做 continuous component testing

一個把測試金字塔中段成本壓下來的做法,以及它的邊界


2025 年底,JPMorgan Chase 的工程師 An Nadein 公開了一篇文章,講他們怎麼做出一個自動生成 component test 的 AI agent。幾個月後,Chase 核心工程解決方案主管 Michele Willis 在受訪時補充了這個 agent 在組織內的推展狀況。

這個案例在 AI 測試的討論裡被引用得很頻繁,但多數轉述只留下一句「JP Morgan 用 AI 提升了測試覆蓋率」。

那句話沒有錯,可是它把最有意思的部分全部丟掉了。這篇把技術細節攤開來講——他們解決了什麼問題、怎麼解的、他們自己承認了哪些限制,以及這套東西的能力邊界落在哪裡


一、他們診斷的問題:中段的經濟學是壞的

Chase 的說法很直接:他們的單元測試和 contract test,覆蓋率是好的。

壞掉的是中間那一層,component test。

一個 component test 要驗證單一服務的完整行為。而服務很少是獨立存在的——它依賴基礎設施,依賴一整片其他服務。所以要寫這種測試,你得先把那些依賴全部虛擬化:資料庫、訊息佇列、雲端服務、下游供應商。

Chase 描述的現實是:光是搭 scaffolding 去 mock 掉那一整片依賴,就會吃掉好幾週。 而在那之前,你還得先想清楚正確的輸入和輸出資料集長什麼樣子——這件事本身也會讓好幾天憑空消失。

於是工程師的時間花在跟 scaffolding 和猜測搏鬥,而不是在建東西。

團隊的自然反應是放棄這一層,退回到整合環境去做同等級的驗證。結果是回饋迴圈被拉長,問題被推遲到更後面才浮現。

這個診斷是這篇文章最有價值的地方。 測試金字塔中段長年被繞過,不是因為大家不懂它的價值,是因為它的成本結構讓人不划算去做。任何能改變這個成本結構的東西,都是真的貢獻。


二、他們刻意不做的事

Chase 特別把自己跟另一類工具區隔開來:那些從 OpenAPI 或 Swagger spec 出發,產出表面測試的工具。

他們的理由很具體。那種做法對以下這些事情一無所知:

  • 請求實際上如何流經服務
  • 中途碰到哪些資料庫、哪些佇列
  • 觸發了哪些副作用

換句話說,spec 告訴你「介面長什麼樣」,但沒有告訴你「這個服務實際上做了什麼」。從 spec 生出來的測試,只能檢查殼。

所以他們決定從服務本身出發。

這個決定,是整套系統所有優點和所有限制的來源。後面會回頭談。


三、Agent 實際上怎麼運作

在拆解之前先講一件很多轉述都搞錯的事:這不是一個「LLM 讀了程式碼然後理解了它」的系統。

它是一套 hybrid。看原文的動詞就知道:

步驟原文怎麼說誰做的
發現 endpoint“feeding the controllers into an LLM”LLM
建構 call graph“using software engineering techniques”不是 LLM
生成輸入資料“learns patterns from existing unit tests”語義任務,應為 LLM

中間那一步是整套系統的核心,而它明確地不是 LLM 做的。software engineering techniques 講得含糊,但合理的解讀是靜態分析、AST 遍歷這一類傳統的程式分析技術。

記住這個分工。它決定了後面所有的事。

第一步:發現真實的 endpoint

把服務的 controller 餵進 LLM,讓 agent 自動找出真實存在的 endpoint 集合。

不是讀文件,是讀程式碼。文件會過期,controller 不會。

第二步:建構 call graph

對每一個 endpoint,agent 建構出一張完整的 call graph。這張圖描述:

  • 請求怎麼在服務內部移動
  • 碰到哪些資料庫
  • 發出哪些訊息
  • 呼叫哪些下游服務
  • 資料沿途怎麼被轉換

Chase 自己給這張圖的名字是 implementation-level map(實作層級的地圖)。

這個用詞很誠實。記住它。

而且要注意:這張圖是程式分析工具從原始碼機械地萃取出來的。它的精確度很高,因為它就是實作的一份鏡像。這裡面沒有任何「這樣做對不對」的判斷空間,也不需要有——call graph 分析器的工作就是忠實反映程式碼。

第三步:生成 scaffolding 和虛擬化檔案

拿著那張地圖,agent 生成測試的 scaffolding——也就是那個原本要吃掉工程師好幾週的東西。

具體來說,它會建立虛擬化檔案來模擬外部依賴。上一節說的「幾週搭 scaffolding 去 mock 掉那一整片依賴」,被自動化掉的就是這個。

測試資料從哪裡來

這一步容易被忽略,但它的意涵很重。

Agent 不是丟隨機的 payload 進去。它從既有的單元測試學習模式,必要時再從一個簡單的輸入檔案取得人給的提示,這樣資料才會符合服務真正期待的格式和商業規則。

所以資料的分佈,也是從既有的 artifact 推導出來的。

執行環境:完整虛擬化

生成的測試跑在一個完整虛擬化的環境裡,這個環境鏡射整個 runtime——資料庫、訊息佇列、雲端服務、下游供應商。

因為所有東西都被虛擬化了,測試在開發者的機器上和在 CI 裡的行為是一致的。這一點很重要,它消滅了「在我機器上會過」這個經典的時間黑洞。

它驗證什麼

不只是 API 回應。

它驗證整條行為鏈,包含副作用:訊息有沒有被發布出去、紀錄有沒有被寫進資料庫。

這是這套系統技術上最紮實的部分。副作用的驗證一向是 component test 最麻煩的地方,也是它跟「用 spec 產表面測試」最大的差距。


四、成果

Chase 報出來的數字:

項目數字
Pilot 服務數20 個
一個月後約 80 個
建立單一測試的時間從「幾週」變成「幾分鐘」
品質開發者回饋等同於手寫水準

(時間那一欄,原文自己有兩個版本:文章開頭寫「幾天變幾分鐘」,成果段寫「幾分鐘而非幾週」。上表取後者。)

關於那個 20 到 80,Chase 的論證是:這裡面有一部分團隊是他們直接支援的,另一部分是自己主動接手的。當開發者在沒被要求的情況下擴大使用,代表你解決了一個真的問題。

這個論證我接受。開發者用腳投票是很強的訊號,而且它很難造假。

Willis 在受訪時補充了一個角度:人在寫測試案例寫到疲乏或無聊的時候,注意力和嚴謹度都會下降。讓開發者去審查一份測試套件、而不是自己動手寫完每一個,反而能讓他們對程式碼的理解更清楚地反映到品質結果上。


五、他們自己承認的限制

這一段 Chase 寫得很誠實,值得引用。

並非每個生成的測試第一次跑就會通過。

原文的處置是:We disable failing tests ——把失敗的測試停用。但緊接著補充,開發者通常在幾分鐘內能把它們修好,而且理解這些失敗模式正在推動他們的改進 roadmap。

注意那個「把它們修好」的它們指的是失敗的測試,不是程式碼。也就是說:修測試,讓它符合程式碼。

我不認為這是疏失。在那個工程脈絡下這是務實的做法,而且他們把失敗模式當成研究對象,這個態度是對的。

但這裡有一個結構性的問題,我們待會回來。


六、下一步:閉環

這個 agent 不是終點。Chase 說它是他們稱之為 agentic SDLC workflows 的第一塊拼圖。

他們列出的願景有四條,多個 AI agent 持續在開發者身邊運作:

  1. 即時監控程式碼,自動更新測試
  2. 資安漏洞的偵測與修正
  3. 建置與部署設定的管理
  4. 多 agent 的協同編排

請把第一條讀兩遍。

Real-time code monitoring with automatic test updates.

在 characterization test 的框架下,這一條的意涵是嚴重的。

Regression 的定義是:程式碼變了,但行為不該變。 你需要一個在程式碼改動之前就固定下來的 oracle,才能偵測到「行為變了」。

而如果測試會即時追著程式碼自動更新,那麼任何行為改變都會在被察覺之前,就被重新定義為新的正確行為。

回歸偵測在概念上崩解了。

Willis 在訪談裡描述的另一半,把迴圈補完了:她說 agent 下一階段能直接進去修改程式碼,而且某種程度上已經在發生——agentic testing framework 的產出會回饋給 coding assistant,由後者替開發者把變更套用上去。

把兩邊接起來:

agent 從程式碼推導出測試 → 測試失敗 → 結果回饋給 coding assistant → coding assistant 改程式碼 → agent 自動更新測試去符合新的程式碼

這是一個完整的閉環。而它的每一個環節,都不包含人類對「這個服務應該做什麼」的判斷。


七、那這個東西到底是什麼

現在把技術細節收攏起來。四個事實,每一個都是 Chase 自己講的:

  1. Call graph 是靜態分析從原始碼機械萃取的——它是實作的一份鏡像,不是對實作的理解
  2. 測試的期望值建立在那張 implementation-level map 之上
  3. 測試資料的分佈,從既有的單元測試推導
  4. 未來要即時監控程式碼,自動更新測試

放在一起,這個系統的性質就清楚了。

先講一個詞:oracle

測試裡的 oracle(判準),指的是「你憑什麼說這個結果是對的」。

  • 需求文件說應該收 90 元 → 需求文件是 oracle
  • 使用者驗收條件說錯誤訊息要出現在欄位下方 → 驗收條件是 oracle
  • 舊系統算出來是這個數字 → 舊系統是 oracle

沒有 oracle,你就只是在觀察程式碼跑出什麼,而不是在測試它。

Chase 這套系統的 oracle,是實作本身

它不知道這個服務「應該」做什麼。它只知道這個服務「現在」做什麼,然後把那個行為寫成斷言。

這種測試有名字:Characterization Test

Michael Feathers 在《Working Effectively with Legacy Code》裡定義了這個東西,中文一般譯作特徵化測試

它的原始情境是這樣的:

你接手了一坨十年前的程式碼。沒有測試,沒有文件,寫的人早就離職了。你必須改它,但你不敢動,因為你不知道改壞了會怎樣。

怎麼辦?Feathers 的答案是:先不要管它對不對,先把它現在的行為記錄下來。

他給的操作步驟大致是:

  1. 把那段程式碼放進測試框架裡
  2. 隨便寫一個你明知會失敗的斷言,例如 assert(結果 == 0)
  3. 讓測試失敗。失敗訊息會告訴你真正的值是多少——喔,原來是 105
  4. 把測試改成 assert(結果 == 105)
  5. 綠燈。重複。

看第三步和第四步。

你不是從需求推導出 105。你是讓程式碼跑一遍,看它吐出什麼,然後把那個數字抄進測試裡。

這就是我一直在說的「從實作抄期望值」。

而且要注意:就算 105 是錯的,你還是照抄。 Feathers 講得很清楚,這個階段的目的不是驗證正確性,是偵測改變。你要的是一張網,不是一個裁判。

「特徵化」這個詞就是這個意思——你在描繪這段程式碼現在的輪廓,不是在評判它的對錯

所以 Chase 造出來的是什麼

把 Feathers 的五個步驟,跟 Chase 的做法對照一下:

Feathers 的手工步驟Chase 的 agent
手動把程式碼放進測試框架自動生成 scaffolding 和虛擬化檔案
隨便寫個會失敗的斷言從 call graph 直接推導出期望值
從失敗訊息讀出真正的值不需要,call graph 已經告訴它了
把測試改成符合實際行為「We disable failing tests,開發者幾分鐘內修好」

這是一台工業級的 characterization test 生產線。

Chase 沒有發明一個新東西。他們把 Feathers 二十年前的手工技術,自動化到每個月能鋪 80 個服務的規模。

這個成就是真的。而它產出的東西的性質,一點都沒有改變。

用另一組詞:checking 而不是 testing

Michael Bolton 和 James Bach 的區分:

  • Checking——對既有的斷言,做演算法式的評估。機器可以做,因為判準已經給定了
  • Testing——透過探索、實驗、質疑來評估產品。需要一個獨立於實作之外的判準,還需要人的判斷

Chase 造出來的,是一台極其高效的 checking 生成機

這個判斷的四個具體後果

後果一:程式碼裡的 bug 會升格成規格。

如果 controller 裡有一個錯誤的行為,agent 會把它寫成一條測試。從此之後,任何試圖修正它的人,都會看到 CI 變紅。

後果二:測試的想像力被複製,不被擴充。

如果你的單元測試沒有涵蓋某個邊界條件,agent 生成的 component test 大概也不會涵蓋它。因為它的資料模式是從那些單元測試學來的。

後果三:測試失敗時,你沒有第三方裁判。

先講一個具體情境。

假設有一條計算訂單金額的邏輯,正確的答案應該是 90(100 元打九折)。但程式碼寫錯了,漏算折扣,算出 105。

Agent 讀了這段程式碼,寫下一條測試:期望值 = 105

測試通過。永遠通過。因為它問的問題是「程式碼現在做的事,跟程式碼現在做的事,一樣嗎」。

那如果測試紅了呢?

你看到紅燈,理論上有兩種可能:

  • (a) 測試錯了——agent 誤解了某個分支
  • (b) 程式碼錯了——這裡真的有 bug

要分辨這兩者,你需要一個第三方裁判:某個獨立於程式碼之外、能告訴你「正確答案是什麼」的東西。需求文件、驗收條件、跟使用者確認過的具體例子,都是裁判。

但這套系統裡沒有裁判。你唯一能查的就是程式碼。

於是你去看程式碼,程式碼說 105。你把測試改成 105。綠了。

問題在這裡——這個動作,在 (a) 和 (b) 兩種情況下一模一樣。

如果是 (a),你修好了一個錯的測試。 如果是 (b),你剛剛把一個 bug 封存成了規格。

而你沒有任何辦法知道自己做的是哪一件

打個比方。你拿一把尺量桌子,量出 100 公分。你懷疑尺不準,想校正它。但手邊唯一的參照物就是那張桌子。於是你拿桌子去校正尺。

尺永遠準。桌子永遠 100 公分。而你永遠不知道桌子真正多長。

這不是紀律問題。是這個迴圈裡不存在外部訊息的入口。

後果四(最嚴重的一個):一旦測試會自動更新,回歸偵測就不存在了。

前面三個後果,你都還可以靠紀律去緩解——人來審查測試、人來判斷失敗、人來補邊界條件。

但 Chase 願景裡的第一條是「即時監控程式碼,自動更新測試」。

Regression 的定義是:程式碼變了,但行為不該變。 要偵測它,你需要一個在程式碼改動之前就固定下來的 oracle。

如果測試會即時追著程式碼跑,那你的「之前」永遠等於「現在」。行為改變會在被察覺之前,就被重新定義為新的正確行為。

一張跟著房子一起變形的鷹架,量不出房子有沒有歪。


八、Characterization test 有它正當且重要的用途

我不希望這篇被讀成「所以不要用 AI 生測試」。恰恰相反。

回想 Feathers 當初為什麼發明它:你要動一段你不懂的程式碼,而且你不敢。

在那個情境下,「從實作抄期望值」不是缺陷,是唯一合理的做法。因為你根本沒有別的判準可用——需求早就不存在了。

Characterization test 正好適合兩件事,而且無可取代。

Refactoring

Refactoring 的定義是:把程式碼內部整理乾淨,但外面看起來要一模一樣。

例如把一個 500 行的函式拆成 8 個小函式。使用者不該察覺任何差異——同樣的輸入、同樣的輸出、同樣的副作用。

問題是:你怎麼確定自己沒有改壞?500 行拆成 8 個,很容易某個邊界條件就漏掉了。

答案是先架一張網。動手之前把測試跑一遍全綠,動完手再跑一遍還是全綠,就代表行為沒變。它像一張安全網撐在下面,你在上面翻筋斗,掉下來它會接住你。

重點是:refactor 的時候,你根本不在乎程式碼對不對。你只在乎「跟我動手之前一不一樣」。

所以 characterization test 從實作抄期望值這件事,在這個場景下正好就是你要的——因為它抄的是你動手之前的那份實作。它把 refactor 之前的行為凍結成快照,然後檢查你有沒有動到它。

Migration

同一個道理,放大一百倍。

你要把 COBOL 搬到 Java,或把服務從舊平台搬到新平台。你需要證明的是新舊行為等價。這裡的「正確」定義就是「跟舊系統一樣」——舊系統就是你的 oracle。

想像一下大型金融機構的 legacy migration,手上有一張自動生成、涵蓋所有副作用的行為快照網。這是很多團隊夢寐以求的東西,而 Chase 的 agent 在這個場景下價值高到不可思議。

這兩件事的共同點

「現行行為」就是你要保護的東西。 把它當成真理,完全正確。

而且——這一點請記住,它是後面所有討論的關鍵——

快照的價值,全部來自它被凍結在過去的某個時間點。

你需要一個「之前」,才能比對出「之後」。

同一個性質,在驗收的場景下就是缺陷。

因為驗收要問的問題是「這個行為對嗎」,而一個以實作為 oracle 的系統,永遠會回答「對」。


九、覆蓋率上升代表什麼

轉述這個案例的人常說:Chase 透過提升測試覆蓋率,讓大量變更能更安全地進 production。

覆蓋率上升,代表更多的程式碼路徑,現在被某個斷言碰到了

它不代表那些斷言有能力發現錯誤。

而且值得注意:Chase 自己報的所有數字,沒有一個是關於缺陷偵測的。

  • 20 到 80 個服務 → 採用率
  • 幾週變幾分鐘 → 成本
  • 「品質等同手寫」→ 開發者的主觀回饋

沒有 defect detection rate,沒有 escaped defect 的變化,什麼都沒有。

這不是在指控 Chase 隱瞞什麼。是說:這套系統的設計意圖裡,本來就沒有「發現缺陷」這件事。 它的意圖是用最低成本把行為固定下來。它做到了。

要量測「這些測試到底有沒有能力抓到 bug」,你需要的工具是 mutation testing:故意在程式碼裡植入變異,看測試套件能不能殺掉它們。殺不掉的變異,就是你的測試看不見的地方。

我很好奇這 80 個服務的 mutation score 是多少。而且我的預期是不會太好看——理由不神秘:那些 check 是從被 mutate 的那份程式碼推導出來的。當你改變程式碼的行為,你也就同時改變了「正確行為」的定義。

覆蓋率這個指標,剛好完全看不見這個迴圈。


十、Chase 自己其實講對了

有點諷刺的是,原文的結論段把這件事說得比大部分轉述都清楚。

他們說:AI 之所以在這裡成功,是因為它接手了那些例行的、耗時的工作,讓開發者能專注在人類獨有的長處上——創造性的問題解決、架構性的思考、理解客戶需求。他們寫道,人的判斷永遠是需要的。

「routine, time-consuming work」對上「human judgment」。

這就是 checking 對上 testing。他們只是沒有用這組詞。

Chase 的工程師知道自己造了什麼。 失真發生在轉述的過程裡——「AI 接手了例行的 checking」被簡化成「AI 很擅長寫測試」。

而這兩句話之間的距離,就是你的產品在 production 出事和不出事的距離。


如果你要導入這類 agent

三件事。

第一,正名。

把 AI 生成的那一層叫做 characterization check,或者 regression net。不要叫它 test。

名字會影響你怎麼對待它。當有人報告「我們測試覆蓋率 85%」的時候,你要能立刻問出那個關鍵問題:

這 85% 裡面,有多少是從實作推導出來的?

第二,量測偵測力,不要量測覆蓋率。

導入之後第一件事就是跑 mutation testing,建立基線,然後持續追蹤。

如果 mutation score 沒有跟著覆蓋率一起上升,你增加的是安全感,不是安全。

第三,保留一層 oracle 在實作之外的驗證。

這一層必須從需求推導,不能從程式碼推導:

  • 跟使用者一起確認過的具體例子(Given-When-Then)
  • 驗收條件
  • 探索性測試的 charter

這些東西的價值,在 AI 能無限量生成 check 之後,不是下降了,是上升了。

因為當生成 check 的成本趨近於零,唯一稀缺的東西,就是知道要 check 什麼。

而那個知識,從來就不在程式碼裡面。


資料來源

一手來源

An Nadein(Chase 資深首席軟體工程師),〈How We Built an AI Agent That Actually Solves the Real Problem of Continuous Component Testing〉,Next at Chase,2025 年 11 月 12 日 https://medium.com/next-at-chase/how-we-built-an-ai-agent-that-actually-solves-the-real-problem-of-continuous-component-testing-6b536e7a28c8

本文所有關於 agent 運作機制、成果數字、以及 Chase 自承限制的描述,皆出自這篇。

訪談

IT Brew 專訪 Michele Willis(Chase 核心工程解決方案主管),2026 年 2 月 https://www.itbrew.com/stories/2026/02/05/chase-is-using-ai-agents

第四節「開發者審查測試套件」與第六節「agent 直接修改程式碼」出自這篇。

一個值得注意的落差:Willis 在訪談中把 component test 描述為「分析已寫程式碼中依賴關係的一組測試」。An Nadein 的原文定義精確得多——驗證你的服務在所有依賴都被虛擬化的狀態下如何表現。引用時建議採用後者。

概念出處

  • Characterization test:Michael Feathers, Working Effectively with Legacy Code
  • Testing vs. Checking:Michael Bolton & James Bach

發表迴響

探索更多來自 轉念學 - 敏捷三叔公的學習之旅 的內容

立即訂閱即可持續閱讀,還能取得所有封存文章。

繼續閱讀