一張視覺隱喻圖,將扁平的文字文件與從老舊程式碼中浮現、彼此緊密相連的圖結構相對比,聚焦於 COBOL 轉 Java 遷移領域。
Artificial IntelligenceSoftware EngineeringTechnology

AI 完美翻譯了三十年的 COBOL,接著它搞垮了資料庫

Ashutosh SinghalAshutosh Singhal2026年2月5日16 min

那是一個星期二的晚上,我盯著一段毫無道理的堆疊追蹤紀錄。

我們當時正與一支金融服務團隊合作,試圖將一個核心交易模組從 COBOL 遷移到 Java。AI 已經完成了它的工作——或者說我們是這麼以為的。生成的 Java 程式碼乾淨、結構良好,編譯時連一個錯誤都沒有。單元測試也通過了。通話中的每個人都謹慎樂觀。然後他們把它部署到測試環境,第一筆電匯就損毀了資料庫。

那個錯誤不在 Java 裡。這段 Java在語法上完美無瑕。錯誤出在 AI 從未看見的東西上。

一個名為 TRN-LIMIT 的變數——它並非定義在 AI 所翻譯的原始檔案裡,而是在執行鏈中前方數千行處引入的一個 COPYBOOK 裡——含有一個 REDEFINES 子句。這是一種 COBOL 結構,同一個記憶體位址會依據在完全不同模組中設定的旗標,被解讀為兩種不同的資料型別。AI 把 TRN-LIMIT 看作一個簡單的數值欄位。但它不是。它是一個依執行期上下文而偽裝成整數的壓縮十進位數(packed decimal)。AI 幻想出了一個標準定義,於是這個 Java 應用程式把損毀的二進位資料寫進了資料庫的一個欄位。

那天晚上,我和團隊坐在一間會議室裡,剖析究竟哪裡出了錯,我意識到一件會重塑我們在 VeriPrajna 所建構一切的事:AI 失敗不是因為它笨。它失敗是因為它看不見。

那個沒人願意談的 1.52 兆美元問題

以下是 2025 年全球經濟令人不安的現實:43% 的銀行系統仍在 COBOL 上運行,而這些系統處理了全部 ATM 交易的 95%。運行《財星》500 大企業的軟體呢?大約 70% 是在二十多年前寫成的。光是美國的技術債,就已膨脹到估計 1.52 兆美元。

而寫下這些程式碼的人正陸續退休。不是「哪天可能會退休」——他們現在就在離開,帶走了數十年的機構知識。與此同時,80% 的聯邦 IT 預算用於維持既有的老舊系統運作,只剩不到 20% 能投入任何新事物。

我曾隔著談判桌面對一些技術長,他們描述自己的現代化處境時,就像在描述一棟地基崩塌的房子:你知道你得修,你知道拖延只會讓情況更糟,但每個試過的承包商都只讓事情更燒錢,卻沒有真正解決問題。

數字印證了這一點。有 70% 到 80% 的老舊系統現代化專案未能達成其目標。而這還是在之前生成式 AI 尚未進場時的情況。

為什麼大家都以為 GPT 能解決這個問題?

我懂。我真的懂。當 GPT-4 問世時,軟體顧問市場全面加速運轉。突然間每家公司都有了一個「COBOL 遷移加速器」——而如果你掀開引擎蓋看看,那不過是包在基礎模型外面的一層薄殼。貼上你的 COBOL 段落,換回一個 Java 方法。魔法般。

我和我的共同創辦人花了好幾週評估這些工具。我們會餵給它們來自客戶環境的真實老舊程式碼,再檢查輸出。語法幾乎總是正確的。程式碼也編譯通過。然後它會以極難診斷的方式失敗,因為程式碼的外形看起來沒錯,即便其含義是錯的。

最危險的錯誤,不是讓你系統崩潰的那種。而是在有人察覺之前,悄悄損毀你資料長達六個月的那種。

問題出在架構層面,歸結到大型語言模型如何處理資訊。LLM 使用一種注意力機制來衡量輸入中不同部分的重要性。現代模型號稱擁有高達百萬 token 的上下文視窗。但研究已證明一種現象,稱為「迷失於中間」效應:LLM 呈現出一條 U 形的表現曲線,對提示開頭與結尾的資訊記得很好,卻對任何位於中間的內容顯著退化。

在一個現代化專案裡,單一個 COBOL 程式可能長達數千行,並引用同樣長達數千行的 copybook。如果 MAX-TRANSACTION-LIMIT 的定義正好落在那龐大上下文的中間,AI 在統計上很可能會漏掉它。而當它漏掉某樣東西時,它不會停下來詢問。它會產生幻覺。它會捏造出一個貌似合理的定義然後繼續往下走。

當你把程式碼當成文字對待時,會發生什麼事?

一張並列比較圖,展示標準向量 RAG 檢索如何漏掉關鍵的程式碼相依關係,對比基於圖的檢索如何跨檔案追溯邏輯連結。

這是我看到整個「AI 外殼」生態系犯下的核心錯誤,也是我早期一直與一位潛在投資人爭論的論點。他看著我們的做法——為程式碼儲存庫建構知識圖譜——然後說:「為什麼不乾脆用一個更大的上下文視窗?GPT-5 會解決這個問題。」

我在筆電上叫出一個 COBOL 程式。「幫我找出 ACCOUNT-BALANCE 的定義,」我說。

他在檔案裡搜尋。找不到。因為它根本不在那個檔案裡。它在一個 copybook 裡,透過第 47 行的一個陳述式引入,而那個 copybook 本身又引用了一個由完全不同團隊維護的共用資料區段。

「現在想像你是一個 LLM,」我說。「你正在為與『支付處理』相關的程式碼做向量相似度搜尋。你會找到五個提到『支付』一詞的區塊。你會完全漏掉那個叫做 GlobalVarDef.cbl 的檔案——它定義了支付邏輯所使用的稅率——因為那個檔案裡任何地方都沒提到『支付』這個詞。」

標準的檢索增強生成,也就是 RAG——大多數 AI 編碼工具用來為 LLM 增添知識的技術——是基於文字相似度來檢索上下文。它把程式碼轉換成向量,再尋找相似的向量。這對 FAQ 聊天機器人來說運作得極好。但用在程式碼上,卻是災難性地不足。

程式碼不是自然語言。「貓坐在墊子上」無論你五十頁前讀了什麼,意思大致都一樣。但 x = y + 1毫無意義,除非你知道 xy 的定義、型別與當前狀態——而它們可能定義在不同的檔案、不同的模組,或繼承自某個父類別。

我在我們研究的互動版本中深入探討了這個結構性問題。簡而言之:軟體不是文字。軟體是一張圖。

我們停止打造更好外殼的那個夜晚

有那麼一個時刻——我記得很清楚——當時我的團隊正在辯論我們的架構。我們有兩條路。第一條:打造一條更聰明的 RAG 管線。更好的分塊、更好的嵌入、更好的提示。持續迭代這套外殼做法,直到它運作得夠好。第二條:徹底拋棄以文字為本的範式,把程式碼當成它真正的樣子來對待——一個由邏輯構成的關聯式系統。

第一條路更快。第一條路是投資人所理解的。第一條路已經有十幾個競爭者在證明市場需求。

我的首席工程師走到白板前,把一個 COBOL 程式畫成一張圖。變數、函式、copybook、資料庫表格都是節點。而邊則代表 CALLSREADSUPDATES_TABLEIMPORTS_COPYBOOK。接著她追溯了一條相依鏈:模組 A 呼叫模組 B,模組 B 修改變數 X,而變數 X 又被位於完全不同目錄中的模組 C 讀取。

「叫向量搜尋去找出那條鏈看看,」她說。

沒有人辦得到。

那就是我們下定決心要打造出我們如今稱之為儲存庫感知知識圖譜(Repository-Aware Knowledge Graph)的那個夜晚——一個統一的圖資料庫,結合了程式碼的靜態結構(抽象語法樹、呼叫圖)與商業邏輯的語意含義(文件、註解、變數意圖)。我們不打算打造一個更好的翻譯器。我們要打造的是一張地圖。

你要如何把三十年的 COBOL 變成一張地圖?

一張四階段的管線圖,展示知識圖譜的建構過程:結構化解析、實體/關係抽取、跨儲存庫符號解析,以及遞移閉包計算。

這個過程有四個階段,而實作細節我就不多說了——你可以在我們完整的技術深度剖析中找到那些內容。但這些概念很重要,因為它們解釋了為何這套做法能在外殼失敗之處成功。

首先,我們以結構化而非文字化的方式解析程式碼。 標準 RAG 管線使用「天真切分」——它們每 500 個 token 就把檔案切開一次,往往把函式簽章與其主體切斷。我們使用像 Tree-sitter 這樣的解析器來生成抽象語法樹,它們尊重程式碼的邏輯邊界。一個函式會被當作一個完整的邏輯單元來處理,而不是一段隨機的文字。

其次,我們抽取實體與關係。 類別、段落、變數、資料庫表格、API 端點——這些成為節點。而它們之間的邊——CALLSUPDATES_TABLEDEFINES_VARIABLE——則成為連結彼此的組織。現在我們可以查詢這張圖:「顯示每一個更新 CUSTOMER-ID 欄位的段落。」精確的結果,即刻呈現。用 grep 試試看吧。

第三——這正是有趣之處——我們跨整個儲存庫解析符號。 標準的解析器會把檔案 A 裡的 ACCT-NUM 和檔案 B 裡的 ACCT-NUM 看作兩個不同的字串。我們的系統會判定兩者都指向一個共用 copybook 中的同一個條目,並把它們合併成單一節點。我們也把文件與程式碼合併:如果一份 PDF 需求文件描述了「User API」,而程式碼中含有一個名為 UserAPI 的類別,系統就會把意圖與實作連結起來。

第四,我們計算遞移閉包。 還記得那次銀行的失敗嗎?A 相依於 B,B 相依於 C,而 AI 看見了 A 卻漏掉了 C。我們的圖會深度遍歷——從 A 到 B 到 C——以找出每個變數的根定義。當 AI 為模組 A 生成程式碼時,它會從模組 C 匯入正確的定義,即使模組 C 位於完全不同的目錄或儲存庫也一樣。

為什麼標準 RAG 在程式碼遷移上會失敗?

人們總是對此提出反駁。「RAG 用在程式碼上運作得好好的,」他們會說。「用更好的嵌入就行了。」

讓我給你三個向量相似度搜尋徹底失靈的情境:

一位開發者把 Account 重新命名為 Acct。語意相似度下降了,即便邏輯完全相同。一個名為 FNC-001 的函式執行利息計算,卻不含任何註解——搜尋「利息計算」永遠也找不到它。而最常見的失敗是:向量 RAG 檢索到一個提及「支付」的單元測試和一則 UI 註解,卻漏掉了核心的商業邏輯,因為變數名稱與查詢字詞不相符。

基於圖的檢索問的不是「哪些文字看起來相似?」它問的是「什麼在邏輯上相連?」——而這個區別,正是能編譯的程式碼與能運作的程式碼之間的差異。

我們所稱的 GraphRAG 是在結構上運作,而非在相似度上。當有人問「重構支付邏輯」時,系統會用向量搜尋找出進入點——比如說 ProcessPayment 段落。但接著,它不會就此停下,而是遍歷圖的邊。它透過 CALLS 邊拉入子程序,透過 READS 邊拉入變數定義,透過 INCLUDES 邊拉入 copybook。這些相連的片段在文字上或許並不相似,但在邏輯上密不可分。

研究顯示,GraphRAG 在多跳推理(連結相隔數步的事實)方面顯著勝過向量 RAG。在軟體中,幾乎每一個嚴重的錯誤都是多跳推理的失敗。如果我更改了模組 A 中的利率邏輯,模組 Z 裡的哪些報表畫面會壞掉?向量 RAG 無法回答這個問題。圖可以,因為它會遍歷那條連結它們的函式呼叫鏈。

GOTO 問題(或者說:為什麼 COBOL 會讓 AI 幻想出迴圈)

一張圖,展示 COBOL 的 GOTO 跳轉如何被對應成控制流程圖中的邊,接著再透過模式比對轉換成適當的 Java 控制結構(迴圈、條件式、返回)。

我想跟你說一個差點把我們搞垮的特定技術挑戰,因為它說明了這項工作為何比人們想像的要困難得多。

COBOL 有一個 GOTO 陳述式。Java 沒有。GOTO 讓程式執行可以跳到任何地方——向前、向後、跳進另一個區塊的中間。它造就了每位電腦科學教授都會警告你的那種「義大利麵式程式碼」。翻譯 GOTO 不是一個語法問題。它是一個拓撲問題。

我們看著三種不同的商用 AI 工具,嘗試翻譯一個大量使用 GOTO 的 COBOL 模組。一個生成了一次遞迴函式呼叫,若放到生產環境會導致 StackOverflowError。另一個產出了一個沒有離開條件的 while(true) 迴圈。第三個——我個人的最愛——則乾脆捏造了一個原始程式碼中根本不存在的控制流程。它看起來貌似合理。它卻是徹底錯誤。

我們的做法:把 GOTO 的目的地對應成控制流程圖中的邊。然後對這張圖使用模式辨識。一個跳回到較早標籤的 GOTO?那是一個迴圈。一個略過某區塊的 GOTO?那是一個條件式。一個跳到離開段落的 GOTO?那是一個返回陳述式。在圖結構的引導下,AI 會把這些跳轉重構成 while 迴圈、if/else 區塊,或 break/continue 陳述式。

沒有圖,AI 是在猜測。有了圖,它是在做工程。

聊天機器人與代理人之間的差別

我們不打造聊天機器人。我需要把這點講清楚,因為市場上充斥著讓你「與你的程式碼庫對話」的工具,而那和我們做的並不是同一回事。

聊天機器人接收你的問題,把它送給 GPT-4,再把返回的任何東西交給你。如果輸出是錯的,你就得手動除錯。那是市面上每一個 AI 外殼的工作流程。

我們所部署的,是能夠規劃、執行並自我修正的自主代理人。這個代理人會分析目標 COBOL 檔案的 AST,辨識相依關係,查詢知識圖譜,生成 Java 程式碼,然後在沙箱中編譯它。如果編譯器拋出錯誤——「找不到變數」——代理人會讀取該錯誤,向圖查詢缺失的相依項,並重新生成。接著它會執行從原始 COBOL 執行軌跡衍生出的單元測試,以驗證行為上的等價性。

這個「編譯—修正」迴圈,把驗證的重擔從人類轉移到了系統身上。但是——這一點在受監管的產業中極其重要——知識圖譜提供了完整的可解釋性。開發者可以確切看見 AI 做出每一個決策的原因:「AI 匯入了 com.bank.logic,因為它發現了一個對 COPYBOOK-X 的相依關係。」不是「相信我,我是 AI」。而是:這是這段邏輯的引用鏈。

在銀行業,每一行程式碼都必須是可稽核的。你不能部署一個「大概」做對了的黑盒子。你需要一個能展示其推導過程的系統。

那死程式碼呢?

有一件事讓我很意外:老舊系統充滿了沒有人再使用的程式碼。舊的促銷活動、已下架的產品、來自 1997 年的除錯常式。以文字為本的 AI 會把交給它的所有東西都遷移過去——它無法區分作用中的程式碼與死程式碼。

我們的呼叫圖會辨識出無法到達的節點——那些沒有任何進入邊的段落或檔案,意味著沒有東西呼叫它們。我們在遷移開始之前就把這些死程式碼標記出來以供刪除。根據我們的經驗,這通常能讓程式碼庫減少 20-30%。那不是一個小小的優化。那是消除了四分之一的工作量,以及四分之一的攻擊面。

「更大的上下文視窗難道不會解決這個問題嗎?」

我到現在還不斷被問到這個問題。人們的假設是:如果 GPT-5 或 Claude 4 能處理一千萬個 token,「迷失於中間」的問題就會消失。

不會的。原因如下。

即便注意力退化有所改善——而它確實會改善——你仍然是在做文字檢索。你仍然是在搜尋相似的字串,而不是在遍歷邏輯連結。如果你需要的那個變數,定義在一個與你正在翻譯的檔案共用零個關鍵字的檔案裡,那麼一個百萬 token 的上下文視窗也幫不上忙。問題不在於視窗的大小。問題在於這個視窗看的是錯的東西

我聽到的另一個異議:「知識圖譜建構起來很昂貴。」的確如此。解析整個儲存庫、解析符號、計算遞移閉包——這是一筆可觀的前期投資。但請考慮另一個選項。人工遷移一個大型 COBOL 系統要花費數千萬美元,並耗時數年。以外殼為本的 AI 遷移前期成本較低,卻會源源不斷地產生由幻覺引起的錯誤,這些錯誤需要昂貴的人工除錯。基於圖的做法有較高的建置成本,卻有大幅降低的返工成本。麥肯錫的數據指出,生成式 AI 能將編碼任務減少 50%,但前提是要正確地部署。相較於標準的 AI 工具,我們看見了開發者生產力提升 2 到 3 倍,這具體來說是因為開發者不再花數小時去尋找某個變數究竟定義在哪裡。

地圖就是資產

以下是我希望自己在一開始就明白的道理:知識圖譜不只是一個用於遷移的工具。它是一項永久的資產。

一旦你的程式碼庫以圖的形式存在,它就會持續作為你系統的一個活的呈現。隨著新的 Java 程式碼演進,圖也會更新。你會得到始終保持最新的自動化文件。你會得到架構漂移偵測——如果新程式碼違反了你所定義的模組化規則,系統會向你發出警示。你會得到隨需而至的影響分析:「如果我更改這個方法,會有什麼壞掉?」

現代化不是一次性的事件。它是一個生命週期。那些把它當作一個專案來對待的組織——有一個開始日期和一個結束日期——正是那些五年後又回到原點、淹沒在新一代技術債中的組織。

程式碼不是文字

我不斷回想起的那個教訓——就是那個星期二晚上盯著堆疊追蹤紀錄時得到的——看似簡單得騙人:程式碼不是文字,而把它當成文字對待的工具,會產出看起來對、卻表現錯的結果。

整個「AI 外殼」經濟建立在一個範疇錯誤之上。它假設,因為 LLM 在處理語言方面極為出色,它們處理程式碼想必也同樣出色。但程式碼不是語言。程式碼是一張圖——一個由相依關係、資料流與狀態變化構成的、稠密而彼此相連的系統,同時存在於多個維度中。試圖用以文字為本的工具去現代化它,就像是靠一份街道名稱清單、卻沒有地圖來在一座城市裡導航。你會「迷失於中間」。

我們打造了那張地圖。而它奏效了——不是因為我們比那些打造基礎模型的團隊更聰明,而是因為我們問了一個不同的問題。他們問的是:「我們要如何讓 AI 更懂文字?」我們問的是:「假如問題根本就不是文字呢?」

老舊系統現代化的未來,不是一個更大的語言模型。它是一個以軟體真正運作的方式來理解軟體的系統——理解為結構,而非字串。

那就是我們在 VeriPrajna 下的賭注。每一天,都有另一個組織發現他們由 AI 生成的 Java 編譯得漂漂亮亮,運行起來卻災難性地失敗。每一天,語法翻譯與語意理解之間的落差,都變得更加難以承受地被忽視。那些彌合了這道落差的組織,將不只是把他們的程式碼現代化。他們終將真正理解它——其中許多組織是頭一次做到。

Related Research

Also Published On