鉄鋼業界のAI検収システムのデータ変換モジュールについて
注記:本記事に記載されているトラック番号、検収員コード、等級コードなどの具体的な値は、すべて説明用のサンプルデータです。実際の顧客データは一切使用していません。また、記事内のkey名は当社システムで管理している実際のキー(公開可能な範囲)、またはサンプルキーを使用しています。
TL;DR
鉄鋼業界では、各工場が独自のスクラップ管理システムを運用しており、鉄ナビ検収AIと連携する際にデータフォーマットの違いが大きな障壁となっていました。従来は工場ごとにカスタムコードを書いていましたが(リードタイム:1〜2週間)、設定ベースの3ステップ変換システムを開発することで、コード変更なしに新しい工場を追加できるようになりました(リードタイム:1〜2日程度)。
本記事では、SchemaMapper(データ配置)、FieldTransformer(値変換)、ConditionalStructure(全体条件による値の変換ルール)の3ステップで構成される変換システムの設計と実装について紹介します。
はじめに
背景
鉄鋼業界では、鉄スクラップをリサイクルして鋼材を製造する電炉法が、カーボンニュートラル実現の重要な手段として注目されています。電炉メーカーでは、原料となる鉄スクラップを等級別に分類し、危険物を除去する検収業務が極めて重要ですが、検収員の高齢化や技能継承の困難さという課題を抱えています。
この課題を解決するため、EVERSTEELではAIを活用した鉄スクラップ自動解析システム(鉄ナビ検収AI)を開発・提供しています。カメラで撮影された鉄スクラップの画像をAIが自動解析し、等級判定や異物検出を行うことで、検収業務の効率化と標準化を実現します。
筆者の役割
私はEVERSTEELでソフトウェア開発を担当しており、特に鉄ナビ検収AIと各工場の既存システムをつなぐ変換モジュールの設計・実装を担当しています。
直面した課題
鉄ナビ検収AIの導入が進む中で、新たな課題が浮上しました。それは、各工場が独自のスクラップ管理システムを運用しているという事です。
各工場が異なるシステムを持つ背景:
- 歴史的経緯:各工場が長年にわたり独自に開発・運用してきた既存システム
- 独自要件:工場ごとに異なる業務フロー、設備、商習慣に最適化
- ベンダーの多様性:複数のERPベンダーがそれぞれ異なるデータ構造を採用
さらに、各工場のスクラップ管理システムは独自のデータ言語・構造で設計されているため、直接的にはデータを受け取ることができません。
データフォーマットの相違例(※サンプルデータ):
| 項目 | A工場(既存システム) | B工場(ERPシステム) | C工場(独自開発) |
|---|---|---|---|
| 検収員コード | grader.code |
inspector_id |
user.id |
| スクラップ等級 | acceptableCargo.code: "110" |
scrap_grade: "HS1" |
grade.type: "heavy" |
| 重量単位 | kg | ton | kg |
| 日時フォーマット |
YYYY-MM-DDTHH:mm:ss (JST) |
Unix timestamp |
YYYY/MM/DD HH:mm (UTC) |
| データ構造 | ネストされたJSON | フラットなテーブル | XML |
同じ情報でも、フィールド名、データ構造、値の形式、単位が工場ごとに全く異なります。
なぜEVERSTEELが変換を担当するのか
理想的なアプローチ:
本来であれば、各工場のシステムがEVERSTEELの鉄ナビ検収AIの仕様に合わせてデータを送信してもらうことが、最もシンプルで正しいアプローチです。
しかし、現実には以下の課題があります:
-
先方の基幹システム改修コストが高い
- 既存の基幹システムやERPの改修は数百万〜数千万円のコストが発生
- 開発ベンダーとの契約や調整に時間がかかる
- 他の業務システムへの影響リスクがある
-
導入までのスピード感が損なわれる
- 先方のシステム改修を待つと、導入まで数ヶ月〜半年以上かかる
- 検収業務の課題は日々発生しており、早期の解決が求められる
- 競合他社との差別化において、導入スピードは重要な要素
-
工場ごとの交渉コストが膨大
- 国内だけでも数十工場、将来的には多くの工場への展開を想定
- 各工場と個別にシステム改修の交渉を行うのは非現実的
- 工場側の意思決定プロセスも様々で、調整が困難
EVERSTEELが変換を担当する価値:
データ変換をEVERSTEELが担当し、柔軟かつスピード感を持って実施できるようにすることは、以下の点で大きな価値があります:
- 顧客のシステム改修が不要 → 導入ハードルを大幅に下げる
- 迅速な導入が可能 → 数ヶ月 → 数週間に短縮
- EVERSTEELがコントロール可能 → 顧客の都合に左右されない
- 営業競争力の向上 → 「すぐに使えます」が強力な武器になる
- スケーラビリティの確保 → 多くの工場への展開が現実的に
このため、EVERSTEELが柔軟な変換システムを持つことは、ビジネス戦略上の重要な資産となります。
従来のアプローチとその問題点
従来の方法:工場ごとにカスタムコードを実装
各工場に対応するため、従来はプログラマが個別に変換コードを書く方法が取られていました:
// A工場用の変換コード(※サンプル)
function transformForFactoryA(data) {
return {
inspectorId: data.grader.code,
scrapGrade: mapGradeCode(data.acceptableCargo.code),
weightKg: data.loadedWeight,
// ... 数百行のマッピングコード
};
}
// B工場用の変換コード(別ファイル)(※サンプル)
function transformForFactoryB(data) {
return {
inspectorId: data.inspector_id,
scrapGrade: data.scrap_grade,
weightKg: convertTonToKg(data.weight_ton),
// ... 数百行のマッピングコード
};
}
-
コード変更とデプロイが必要
- 新しい工場を追加するたびに、プログラマがコードを書き、テストし、デプロイする必要がある
- リードタイム:2〜4週間(要件定義、実装、テスト、デプロイ)
- デプロイリスク:本番環境への影響、ダウンタイム
-
保守コストの増大
- 工場数が増えるごとに、保守すべきコードが増加
- 10工場 → 10個の変換関数 → 数千行のコード
- バグ修正や仕様変更が全ての工場コードに波及
-
テストの複雑性
- 各工場ごとにテストケースを作成・実行
- 回帰テストのコスト増大
- 工場間での不整合のリスク
-
ドメイン知識の喪失
- 変換ロジックがコードに埋もれ、ビジネスロジックが見えにくい
- 非エンジニア(営業、カスタマーサクセス)が変換ルールを理解できない
-
スケーラビリティの限界
- 多くの工場への対応が必要
- 将来的な規模拡大に対応できる柔軟性が求められる
解決策:設定ベースの3ステップ変換ロジック
本記事では、これらの課題を解決するために開発した設定ベースの3ステップ変換ロジックについて紹介します。
システムのコンセプト
「コードではなく、設定でデータ変換を表現する」
変換ルールをプログラムコードではなく、データベースに保存された設定データ(JSON)として管理することで、コード変更やデプロイなしに新しい工場を追加できるシステムです。
このシステムがもたらす効果
1. リードタイムの短縮
- 従来:2〜4週間(実装 → テスト → デプロイ)
- 新方式:1〜2日(設定作成 → データベース登録)
2. 運用コストの大幅削減
- 人件費削減:プログラマの工数を削減(実装不要)
- 保守コスト削減:バグ修正が一箇所で完結(変換エンジンのみ)
3. ビジネスアジリティの向上
- 迅速な対応:顧客の要望に即座に対応可能
- 営業強化:「すぐに対応できます」と自信を持って提案
- スケーラビリティ:多くの工場への展開が容易
4. 品質とガバナンスの向上
- 標準化:全工場で同じ変換エンジンを使用 → 品質の均一化
- 可視性:変換ルールがデータとして管理される → 監査・レビューが容易
- 変更管理:設定の変更履歴を追跡可能
5. 非エンジニアによる運用
- カスタマーサクセスチームによる設定作成:プログラミング不要
- ドメイン知識の活用:鉄鋼業界に詳しい担当者が直接設定を作成
- 専門性の分離:エンジニアは変換エンジンの開発に集中
6. 技術的メリット
- テストの簡素化:変換エンジン自体をテストすればOK(各工場コードのテスト不要)
- 柔軟性:新しい変換パターンの追加が容易
- 保守性:ロジックがコードではなく設定として可視化
システムアーキテクチャ概要
全体の流れ
3ステップ変換プロセス
各工場のデータは、3つの独立したステップで順番に変換されます:
なぜ3ステップに分けるのか?
単一責任の原則
各ステップは1つのことだけをやります:
| ステップ | 責任 | やること | やらないこと |
|---|---|---|---|
| 1. SchemaMapper | データの位置 | フィールドの移動・配置 | 値の内容には触れない |
| 2. FieldTransformer | 値の変換 | 単位、コード、日時の変換 | データ構造は変えない |
| 3. ConditionalStructure | ビジネスロジック | 条件による動的な処理 | 基本的な変換はしない |
この分離のメリット:
- 理解しやすい:各ステップが何をしているか明確
- テストしやすい:各ステップを独立してテスト可能
- 拡張しやすい:新しい変換タイプを追加しても他に影響しない
- 再利用できる:同じステップを他のプロジェクトでも利用可能
設定ベースの運用
変換ルールは全て設定データとして管理されるため、新しい工場を追加する際の流れは以下のようになります:
1. 設定作成(8時間)
↓
2. テスト作成(2時間)※設定だけでは安全ではないため必須
↓
3. デプロイ
重要なのは、テストを書く以上の実装は発生しないという点です。全ての変換処理は共通化された実装で行われます。
データ変換の3ステップを理解する
ステップ1: SchemaMapper - 「どこからどこへ」を整理
データの位置だけを変更します。引っ越しで荷物を新しい部屋に配置するイメージです。
よく使う機能:
① SIMPLE:単純なコピー
A工場: grader.code → 鉄ナビ検収AI: inspector.smsId
② DYNAMIC_ARRAY:配列全体の移動
A工場: gradingCargo[].code → 鉄ナビ検収AI: scrapGradePercentages[].smsId
スクラップの等級データが配列で来ても、全要素を一度に移動できます。
③ CONCAT:複数を1つに結合
firstName + lastName → fullName
ステップ2: FieldTransformer - 「値の中身」を変換
位置はそのままで、値の内容だけを変換します。
よく使う機能:
① MAPPING:コードを意味のある値に変換(※サンプル)
"110" → "heavy"(甲山スクラップ)
"120" → "shredded"(シュレッダースクラップ)
② DATE:日時とタイムゾーンの変換
"2023-06-14T13:20:31" (JST) → "2023-06-14T04:20:31.000+00:00" (UTC)
③ UNIT:単位変換
34500 kg → 34.5 ton
④ ARRAY_SUM:配列の条件付き合計
ダスト項目(2.1% + 1.3%)→ 合計ダスト率 3.4%
ステップ3: ConditionalStructure - 「ビジネスルール」を適用
条件に応じてデータを動的に変更します。「もし〜なら〜する」のロジックです。
よく使う機能:
① NULL値のデフォルト設定
もし weight が NULL なら → 0 に設定
② 空文字のデフォルト設定(※サンプル)
もし carNumber が空 なら → "UNKNOWN" に設定
③ 条件による分類
もし weight > 10000 なら → category を "large" に設定
④ データ補完
もし secondWeight が NULL なら → firstWeight をコピー
実践例:検収データの変換を追ってみる
シナリオ(※サンプルデータ)
A工場の検収開始イベント
- 検収員が配送された鉄スクラップを検収
- スクラップは「甲山」(コード110 = 重量スクラップ)
- トラック番号:4447
A工場システムのデータ(入力)(※サンプルデータ)
{
"primeContractor": { "code": "020000", "name": "商社A/本社" },
"truckNumber": "4447",
"acceptableCargo": { "code": "110", "name": "甲山" },
"gradingStartAt": "2023-06-14T13:20:31",
"grader": { "code": "135000", "name": "検収員A" }
}
変換プロセス
ステップ1: SchemaMapper - データの再配置
grader.code → inspector.smsId
grader.name → inspector.name
acceptableCargo.code → scrapCategoryKey
gradingStartAt → startedAt
...(他のフィールドも同様)
結果(※サンプルデータ):
{
"inspector": { "smsId": "135000", "name": "検収員A" },
"carNumber": "4447",
"scrapCategoryKey": "110",
"startedAt": "2023-06-14T13:20:31"
}
ステップ2: FieldTransformer - 値の変換
scrapCategoryKey: "110" → "heavy"(コードを意味のある値に)
startedAt: JST → UTC(タイムゾーン変換)
結果(※サンプルデータ):
{
"inspector": { "smsId": "135000", "name": "検収員A" },
"carNumber": "4447",
"scrapCategoryKey": "heavy", // 変換された
"startedAt": "2023-06-14T04:20:31.000+00:00" // UTC変換
}
ステップ3: ConditionalStructure - ビジネスルール適用
もし carNumber が空なら → "UNKNOWN" に設定
(この例では "4447" があるので変更なし)
鉄ナビ検収AIへの出力(※サンプルデータ)
{
"inspector": { "smsId": "135000", "name": "検収員A" },
"carNumber": "4447",
"scrapCategoryKey": "heavy",
"startedAt": "2023-06-14T04:20:31.000+00:00"
}
鉄ナビ検収AIが理解できる標準フォーマットに変換完了!
応用例:複数のデータソースを統合
実際の検収では、一次計量と二次計量のデータを統合する必要があります。
統合の流れ
入力データ(※サンプルデータ)
一次計量(START_INSPECTION):
{
"gradingStartAt": "2023-06-14T13:20:31",
"loadedWeight": 34500 // kg
}
二次計量(SECOND_WEIGHING):
{
"outgoingAt": "2023-06-14T13:55:23",
"truckWeight": 12000, // kg
"gradingCargo": [
{ "code": "1010", "ratio": 80 }, // HS1: 80%
{ "code": "1020", "ratio": 20 } // H1: 20%
],
"dust": [
{ "code": "dust", "ratio": 3.4 } // ダスト: 3.4%
]
}
変換処理
ステップ1(SchemaMapper):
- 一次計量データ →
weight.firstWeightKg,weight.firstWeighingAt - 二次計量データ →
weight.secondWeightKg,weight.secondWeighingAt - 配列データ(gradingCargo) →
scrapGradePercentages[]にマッピング
ステップ2(FieldTransformer):
- 日時を全てUTCに変換
- 等級コード(1010, 1020)→ 等級名(HS1, H1)にマッピング
- ダスト配列を合計して
dustPercentage: 3.4を生成
最終出力(※サンプルデータ)
{
"weight": {
"firstWeighingAt": "2023-06-14T04:20:31.000+00:00",
"firstWeightKg": 34500,
"secondWeighingAt": "2023-06-14T04:55:23.000+00:00",
"secondWeightKg": 12000
},
"scrapGradePercentages": [
{ "smsId": "HS1", "percentage": 80 },
{ "smsId": "H1", "percentage": 20 }
],
"dustPercentage": 3.4
}
実質の検収結果:
- 正味重量: 34.5t - 12t = 22.5トンの鉄スクラップ
- 等級構成: HS1(高級鉄スクラップ)が80%、H1が20%
- ダスト率: 3.4%(業界標準は5%以下が良質)
まとめ
設定ベース変換システムの3つのステップ
このシステムは、**「コードではなく、設定でデータ変換を表現する」**というコンセプトで設計されています。
| ステップ | 役割 | 主要機能 |
|---|---|---|
| 1. SchemaMapper | データの位置を整理 | SIMPLE, DYNAMIC_ARRAY, CONCAT |
| 2. FieldTransformer | 値の内容を変換 | MAPPING, DATE, UNIT, ARRAY_SUM |
| 3. ConditionalStructure | ビジネスルールを適用 | IS_NULL, IS_EMPTY, EQUALS, COPY_VALUE |
具体的な効果
リードタイムの劇的な短縮
- 従来:2〜4週間(実装 → テスト → デプロイ)
- 新方式:1〜2日(設定作成 → テスト → デプロイ)
運用コストの削減
- 新工場追加の工数:200時間 → 10時間(90%削減)
- 設定作成:8時間
- テスト作成:2時間
- 個別実装:0時間
安全性の確保
設定だけでは安全ではないため、必ずテストを書いて検証します。しかし重要なのは:
- テストを書く以上の実装は発生しない
- 全て共通化された実装で処理される
- エンジニアは変換エンジンの品質保証に集中できる
ビジネスへの貢献
- 顧客要望に即座に対応可能
- 多くの工場への展開が可能
- 非エンジニア(カスタマーサクセス)が設定を作成可能
技術的な利点
- 保守性:変換ロジックが設定として可視化
- 品質:全工場で同じ変換エンジンを使用 → 品質の均一化
- テスト:変換エンジン自体をテストすればOK
- 拡張性:新しい変換パターンの追加が容易
業界への貢献
鉄鋼業界のカーボンニュートラル実現に向けて:
- 鉄ナビ検収AIの迅速な導入 → 電炉法の拡大を促進
- 検収業務の効率化・標準化 → 人手不足・技能継承の課題を解決
- データの統一管理 → 鉄スクラップの高品位化
おわりに
本記事では、鉄鋼業界の鉄ナビ検収AIを支える、設定ベースのデータ変換システムについて紹介しました。
「コードではなく、設定で変換を表現する」
このアプローチは、単なる技術的な工夫ではありません。それは、ビジネスアジリティを高め、運用コストを削減し、業界全体のDXを加速させる戦略的な選択です。
設定ベースアプローチの運用
設定ベースのシステムは、現場で一定程度の追加・変更ができるように設計されています。カスタマーサクセスチームやドメインエキスパートが、プログラマに依頼することなく、新しい工場の設定を作成できます。
ただし、設定だけでは安全ではありません。
設定を作成した後は、必ずテストを書いて正しく動作することを検証します。しかし、重要なのは、テストを書く以上の実装は基本的に発生しないという点です。
従来のアプローチ:
新工場追加 → 実装(200時間)→ テスト(50時間)→ デプロイ
設定ベースアプローチ:
新工場追加 → 設定作成(8時間)→ テスト(2時間)→ デプロイ
全ての変換処理は共通化された実装で行われるため、個別のコードを書く必要がありません。エンジニアの役割は、変換エンジン自体の品質を保証することに集中し、各工場への対応は設定とテストで完結します。
とはいえ現状では走り出したばかりなので、足りてないところに気付き修正をすることも多くあり理想通りにはいきません。ただ、修正が未来の顧客でも流用できることが多く、修正幅が少しずつ縮まっていっている実感もあります。
今後の展望:MCP化による更なる効率化
現在のシステムでは、設定作成とテスト作成という工数が発生していますが、更なる効率化を目指しています。
具体的には、Model Context Protocol(MCP)を活用した初期設定・テストの自動生成機能を鋭意開発中です。このシステムが実現すれば:
期待される効果:
- 工場のデータサンプルを提供するだけで、AIが初期設定を自動生成
- 生成された設定に対応するテストコードも自動生成
- 設定作成時間を8時間 → 1時間程度に短縮
- 人間の役割は最終確認とチューニングのみに
この取り組みにより、新工場追加のリードタイムを現在の1〜2日から数時間レベルにまで短縮できる見込みです。
MCP化は、人間とAIの協働による次世代の設定管理を実現する試みです。設定の複雑さをAIが吸収し、人間はビジネスロジックの検証に集中できる——そんな未来を目指して、開発を進めています。
同様の課題を抱えるプロジェクトへ
このアプローチは、以下のような課題に直面しているプロジェクトの参考になるはずです:
- 異なるシステム間のデータ統合
- コード変更のコスト削減
- 運用の迅速化
- 非エンジニアによる運用の実現
鉄ナビ検収AIの普及により、鉄鋼業界の脱炭素化が進むことを期待しています。このデータ変換システムが、そのインフラとして貢献できれば幸いです。
余談
データの構造の登録はJSON Schemaを利用しています。
現在、サーバーはtypescriptで動いているのでzodなども検討しましたが、将来において言語を変えるなどの可能性があるため、言語に依存せず、広くユースケースのあるものを採用したかったためです。
Discussion
個別設定すぎると工数が無限に膨らむし、共通化しすぎると応用が効かなくなるのトレードオフな中で、適切な粒度の共通化を実現するシステム構築の所はとても読んでいてワクワクしました。
コメントいただきありがとうございます。
まだ運用を続けて見極めるところはあるものの、一定程度効果が発揮できそうなので安心しています。