「ブラックボックス化したCOBOLシステム、どこから手をつければいいのか」
先日、ある製造業の情報システム部門と話す機会がありました。40年前に構築されたメインフレームシステムが今でも基幹業務の中核を担っており、担当していたエンジニアはすでに退職。ソースコードは100万行を超えているが、正確な設計書は存在しない――。これは特殊なケースではなく、日本の大手企業の多くが直面している現実です。
AIエージェントは、この問題に対して根本的に新しいアプローチをもたらしています。コードを「読んで理解する」のではなく、「エージェントが並列で解析し、段階的に置き換える」という発想の転換です。この記事では、AIエージェントを活用したレガシーシステムのモダナイゼーション手法を、実際に使えるコード・アーキテクチャパターンつきで解説します。
AIエージェントがモダナイゼーションにもたらす変化の全体像については、AIエージェント構築完全ガイドでも解説しています。
そもそも何が問題なのか — レガシーシステムの「3つの壁」
モダナイゼーションが難しい理由は、技術的な問題だけではありません。現場で頻繁に遭遇する3つの壁があります。
壁1: 暗黙知の喪失(ドキュメントの空洞化)
設計者が退職し、コードだけが残される。COBOLの場合、業務ロジックがコードに直接埋め込まれていることが多く、ソースを読まないとビジネスルールが分からない。AIエージェントが登場する前は、これを人間が手作業で解読するしかなかった。
壁2: 依存関係の複雑さ(スパゲッティ構造)
COBOLプログラム間のCALL関係、データ定義の共有(COPYBOOK)、JCLジョブの連鎖。これらが絡み合った「依存グラフ」を人間が追うのは限界がある。
壁3: 移行リスクへの恐怖(止められない基幹システム)
24時間365日稼働している銀行の勘定系システムや、物流の在庫管理システムは「一時停止して移行」という手順が取れない。段階的に移行するしか選択肢がないが、それには精密な依存関係の把握が必要になる。
| 課題 | 従来のアプローチ | AIエージェントによるアプローチ |
|---|---|---|
| ドキュメント不在 | 人手でコードを読み、設計書を手書き | エージェントがコードを解析し設計書を自動生成 |
| 依存関係の把握 | 図面を手動作成(数ヶ月かかる) | 静的解析エージェントが依存グラフを数時間で構築 |
| 移行リスク | ビッグバン移行(全部一度に切り替え) | ストラングラーフィグパターンで段階的に置換 |
| テスト工数 | 全機能を人手でリグレッションテスト | テストエージェントが自動生成したテストケースで検証 |
AIエージェントを活用した3つのアーキテクチャパターン
レガシーモダナイゼーションにおけるAIエージェントの活用は、大きく3つのパターンに分類できます。
パターン1: リバースエンジニアリングエージェント(理解フェーズ)
まず既存コードを「理解する」フェーズです。AIエージェントがCOBOLソースコードを解析し、設計書・ER図・API定義を自動生成します。
以下は、LangChainを使ってCOBOLコードを解析し、業務ロジックの概要を日本語で抽出するエージェントの実装例です。
# 動作環境: Python 3.11+, langchain>=0.3.0, openai>=1.30.0
# pip install langchain langchain-openai
import os
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
def analyze_cobol_module(cobol_source: str) -> dict:
"""
COBOLモジュールを解析し、業務ロジックのサマリーを返す
"""
llm = ChatOpenAI(
model="gpt-4o",
temperature=0,
api_key=os.environ["OPENAI_API_KEY"]
)
system_prompt = """あなたはCOBOLのエキスパートです。
提供されたCOBOLソースコードを解析し、以下の情報を日本語で抽出してください:
1. このプログラムの主な業務機能(2-3文)
2. 入力データ形式(WORKING-STORAGEから)
3. 出力データ形式
4. 主要な業務ルール(条件分岐・計算ロジック)
5. 他のプログラム/コピーブックへのCALL関係
JSON形式で返してください。"""
response = llm([
SystemMessage(content=system_prompt),
HumanMessage(content=f"以下のCOBOLコードを解析してください:nn{cobol_source}")
])
# 実際の実装では json.loads() でパース処理を追加
return {"analysis": response.content}
# 使用例
with open("ACCTMAIN.cbl", "r", encoding="shift-jis") as f:
cobol_code = f.read()
result = analyze_cobol_module(cobol_code)
print(result["analysis"])
動作環境: Python 3.11+, langchain>=0.3.0, GPT-4o API
ポイント: `temperature=0` にすることで決定論的な出力を得られます。大規模なCOBOLプログラム(10,000行超)の場合はチャンク分割が必要です。
パターン2: ストラングラーフィグ × AIエージェント(段階的置換フェーズ)
ストラングラーフィグパターンは、Martin Fowlerが提唱した段階的移行手法です。レガシーシステムを一度に置換するのではなく、新システムが少しずつ機能を引き継ぎながら、最終的にレガシーを廃止します。AIエージェントを組み合わせると、このプロセスを大幅に加速できます。
以下は、AIエージェントが「どのモジュールを先に移行すべきか」を依存関係グラフから判定するコード例です。
# 依存関係グラフを構築し、移行優先度を計算するエージェント
# 動作環境: Python 3.11+, networkx>=3.0
import networkx as nx
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage
def build_dependency_graph(call_map: dict) -> nx.DiGraph:
"""
CALLマップからCOBOLモジュール依存グラフを構築
call_map: {"MODULE_A": ["MODULE_B", "MODULE_C"], ...}
"""
G = nx.DiGraph()
for caller, callees in call_map.items():
for callee in callees:
G.add_edge(caller, callee)
return G
def get_migration_priority(G: nx.DiGraph) -> list[str]:
"""
依存されていない(他から呼ばれていない)末端モジュールを優先候補として返す
これがストラングラーフィグの「最初に置き換えるべき部分」
"""
leaf_nodes = [n for n in G.nodes() if G.in_degree(n) == 0]
# 依存先の数(out_degree)が少ない順に並べる(移行コストが低い順)
return sorted(leaf_nodes, key=lambda n: G.out_degree(n))
# 使用例
call_map = {
"ACCTMAIN": ["ACCTCALC", "ACCTIO"],
"ACCTCALC": ["TAXCALC"],
"ACCTIO": ["DBWRITE"],
"TAXCALC": [],
"DBWRITE": []
}
G = build_dependency_graph(call_map)
priority = get_migration_priority(G)
print("移行優先順位:", priority)
# 出力例: ['TAXCALC', 'DBWRITE'](依存先がゼロの末端モジュールが先)
動作環境: Python 3.11+, networkx>=3.0
ポイント: 依存されていない末端から移行を始めることで、他のモジュールへの影響を最小化できます。これがストラングラーフィグの本質です。
実際の移行フローと各フェーズでのエージェントの役割
理論だけでなく、実際のプロジェクトではどう進めるかを整理します。
フェーズ1: 現状把握(2-4週間)
- 解析エージェント: ソースコードを全スキャンし、モジュール一覧・依存関係・データ定義を抽出
- ドキュメント生成エージェント: 設計書・ER図・API定義の初版を自動生成
- 成果物: モジュール依存グラフ(networkxで可視化)、業務ロジックサマリー
フェーズ2: 移行計画(1-2週間)
- 優先度計算エージェント: 依存グラフから移行順序を算出(上記パターン2のコード)
- テスト設計エージェント: 各モジュールのテストケースを自動生成(正常系・異常系)
- 成果物: 移行ロードマップ、テストスイート初版
フェーズ3: 段階的移行(モジュールごとにスプリント)
- コード変換エージェント: COBOLからPython/Java/Goへの変換候補を生成(人間が最終確認)
- APIブリッジエージェント: 新旧システム間のデータ変換レイヤーを構築
- 検証エージェント: 新旧システムの出力を比較し、差異を検出
以下は、新旧システムの出力を比較する検証エージェントの実装例です。
# 新旧システムの出力比較エージェント
# 動作環境: Python 3.11+, requests>=2.31.0
import requests
import json
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
def compare_system_outputs(
legacy_endpoint: str,
new_endpoint: str,
test_cases: list[dict],
tolerance: float = 0.001 # 数値の許容誤差
) -> dict:
"""
レガシーシステムと新システムの出力を比較検証する
"""
results = []
for case in test_cases:
legacy_resp = requests.post(legacy_endpoint, json=case["input"]).json()
new_resp = requests.post(new_endpoint, json=case["input"]).json()
# 数値フィールドは許容誤差内かチェック
mismatches = []
for key in legacy_resp:
if key not in new_resp:
mismatches.append(f"missing_key: {key}")
elif isinstance(legacy_resp[key], (int, float)):
if abs(legacy_resp[key] - new_resp.get(key, 0)) > tolerance:
mismatches.append(
f"{key}: legacy={legacy_resp[key]}, new={new_resp[key]}"
)
elif legacy_resp[key] != new_resp.get(key):
mismatches.append(
f"{key}: legacy={legacy_resp[key]}, new={new_resp[key]}"
)
results.append({
"test_case": case["id"],
"passed": len(mismatches) == 0,
"mismatches": mismatches
})
passed = sum(1 for r in results if r["passed"])
return {
"total": len(results),
"passed": passed,
"failed": len(results) - passed,
"details": results
}
# 使用例
report = compare_system_outputs(
legacy_endpoint="http://legacy-system/api/calc",
new_endpoint="http://new-system/api/calc",
test_cases=[
{"id": "TC-001", "input": {"amount": 100000, "rate": 0.05}},
{"id": "TC-002", "input": {"amount": 500000, "rate": 0.03}},
]
)
print(json.dumps(report, ensure_ascii=False, indent=2))
動作環境: Python 3.11+, requests>=2.31.0
ポイント: 数値フィールドには許容誤差(tolerance)を設定します。COBOL特有の固定小数点演算とPythonの浮動小数点演算では微小な誤差が生じることがあるためです。
【要注意】よくある失敗パターンと回避策
失敗1: AIに全てのコード変換を任せる
❌ 「AIが自動変換したコードをそのままデプロイする」
⭕ 「AIが変換した候補コードを人間がレビューし、テストでも検証してからデプロイする」
なぜ重要か: COBOLの業務ロジックにはコメントに残っていない暗黙のルールが埋め込まれていることがあります。「月末処理では端数切り捨てではなく四捨五入」といったルールは、AIが変換コードに正しく反映できないケースがあります。現時点では、AIは「下書きを作る」役割に限定し、必ず人間がレビューするフローが必要です。
失敗2: ビッグバン移行を選択する
❌ 「全機能を一気に新システムに切り替える」
⭕ 「ストラングラーフィグパターンで、末端モジュールから順番に移行する」
なぜ重要か: 基幹システムを一度に切り替えると、移行直後に問題が発生した場合のロールバックが非常に困難になります。段階的移行なら、問題が発生したモジュールだけを切り戻せます。
失敗3: テストケースを人手で全量作成しようとする
❌ 「既存のCOBOLプログラムのリグレッションテストを手動で全量作成する」
⭕ 「既存の入出力ログからAIエージェントにテストケースを自動生成させ、重要箇所のみ人間が追加・確認する」
なぜ重要か: 100万行規模のCOBOLシステムのテストを人手で全量作成するのは現実的に不可能です。AIエージェントにテスト生成を任せることで、カバレッジを維持しながら工数を大幅に削減できます。
失敗4: LLMのハルシネーションを見落とす
❌ 「AIが生成した設計書をそのまま正として扱う」
⭕ 「AIが生成した設計書を元のソースコードと照合して、不一致を人間がチェックする」
なぜ重要か: LLMはCOBOLコードの解析で稀に「存在しない変数名」や「誤った計算ロジック」を生成することがあります。生成ドキュメントを「下書き」として扱い、必ずソースとの照合ステップを設けてください。
セキュリティとデータプライバシーの考慮点
レガシーシステムには顧客の個人情報・口座情報・医療情報が含まれることが多いため、AIエージェントによる解析時にはデータプライバシーへの配慮が必須です。
- マスキング処理: 実データをAIに渡す前に、PII(氏名・口座番号・社会保障番号等)をマスキングする
- ローカルLLM活用: 機密性の高いコードはクラウドAPIではなく、オンプレミスで動かせるLLM(例: Llama系)を使う
- アクセス制御: 解析エージェントがアクセスできるコードリポジトリの範囲を最小限に限定する
- ログ管理: エージェントの解析ログには機密データが含まれる可能性があるため、保存期間と暗号化を厳格に設定する
参考・出典
- The Agentic Strangler: AI & MCP for Monolith Migration — webmethodman(参照日: 2026-04-09)
- 開発AIエージェント「Jitera」、レガシーシステムの再構築をAIで支援するモダナイゼーションサービスを提供開始 — PRTimes(参照日: 2026-04-09)
- 生成AIでモダナイゼーションを自動化 — COBOL口座管理プログラムへの適用事例 — AlgoMagazine(参照日: 2026-04-09)
- AI-Powered Legacy System Modernization: The Enterprise Playbook for 2026 — Metosys(参照日: 2026-04-09)
- Fujitsu Application Transform powered by Fujitsu Kozuchiを提供開始 — 富士通(参照日: 2026-04-09)
まとめ:今日から始める3つのアクション
- 今日やること: 自社のレガシーシステムの「ブラックボックス度」を測定する。設計書の有無、担当者の在籍状況、依存するモジュール数を棚卸しするだけで、移行コストの大まかな見積もりが立てられます
- 今週中: 上記のリバースエンジニアリングエージェント(パターン1のコード)を試しに1モジュールに適用してみる。GPT-4o APIキーがあれば今日から動かせます
- 今月中: ストラングラーフィグの「移行候補モジュール」を特定する。まず依存関係グラフを作り、末端モジュールをリストアップするところから始めましょう
あわせて読みたい:
- AIエージェント構築完全ガイド — 設計パターンと実装の全体像
- AIエージェント構築ツール徹底比較 — Dify・LangChain・n8nの選び方
この記事はAIgent Lab編集部がお届けしました。