step type カタログの埋め方 ― 行番号と表記ゆれをどう扱うか
この記事は生成AIとの対話をもとにまとめたものです。
第1回: 設計書を「データ」にして画面を自動生成する ― Spring Boot + Thymeleaf で人が HTML を書かない開発
第2回: 画面の次は Service ― 詳細設計 Excel から業務ロジックを組み立てる全体設計
第3回: では、人はいつ、何をするのか ― 生成が決定論的になったあとに残る仕事
第2回で、詳細設計の1行1行を step type カタログ に対応付ける、と書きました。
初期案は 10種前後。自社の Excel サンプルで「この行はどの type か」を洗い出し、
docs/step-type-catalog.mdに固定します。
今回はその「洗い出して固定する」の実務です。詳細設計は自然言語で書かれるので、必ずこの疑問が出ます。
同じ意味の行が、人によって・シートによって違う書き方をされている。
このぶれをどうやって type に対応付けるのか?
問題の整理:ぶれは2種類ある
実際の詳細設計シートを開くと、こういう状態です。
シートA: 「バリデーションチェックを行う」
シートB: 「入力チェック実施」
シートC: 「必須項目・桁数チェック」
3行とも意味は同じ(→ validateForm)ですが、文字列としては別物です。これが 表記のぶれ。
もう1つあります。
シートA: 「Serviceを呼び出し、更新結果を取得する」 ← 1行に2つの意味
シートB: 「Serviceを呼び出す」「戻り値を判定する」 ← 2行に分かれている
行の切り方が人によって違う。これが 粒度のぶれ です。第2回で「1行が1ステップではなく、意味のまとまりが1ステップ」と書いたのは、このためです。
先に結論:ぶれとの戦いは移行時に閉じ込める
大事なことを最初に書きます。自然言語の解釈が必要なのは、既存 Excel を IR に変換する移行時の一度だけです。
移行後は IR が正本になり、設計書は DocEmitter が step type から 標準文言 で再生成します。「バリデーションチェックを行う」も「入力チェック実施」も、移行時に一度 validateForm に正規化されたあとは、再生成される設計書ではすべて同じ文言で出力されます。
移行前: 人の数だけ表記がある(ぶれは無限に再生産される)
移行後: type → 標準文言(カタログが統制語彙になり、ぶれは構造的に消える)
新規開発ではさらに単純で、設計者は最初から step type の語彙で書くため、解釈そのものが発生しません。つまりこれから説明する対応付けの仕組みは、恒常的なパイプラインではなく、移行のための使い捨ての道具です。パーサを移行後に退役させるのと同じ思想です。
対応付けは3層で行う
Excel の1行(正確には「意味のまとまり」)を type に対応付ける流れは、3層に分けます。
Excel の行
│
├─ 第1層: 辞書マッチ(決定論)
│ 蓄積済みの「表記パターン → type」対応表で機械判定。
│ ここで当たれば人も AI も関与しない
│
├─ 第2層: AI の分類提案
│ 辞書にない行は LLM が type を提案する。
│ ただし出力はあくまで「提案」。人が承認して初めて確定
│
└─ 第3層: unknown で止める
確信の持てない行は無理に分類せず unknown。
人が判断し、判断の結果を辞書に追記する
第2回の「unknown の3手」(検出 → 人が判断 → 委譲として生成)の手前に、辞書と AI 提案を置いた形です。原則は同じで、AI は提案まで、確定の判子は人が持つ。そして人が裁いた結果は、必ず辞書に書き戻します。
辞書のフォーマット
辞書は機械が読む正本(YAML)と、人が読むカタログ(Markdown、生成物)の2つに分けます。
# specs/schema/step-type-dictionary.yaml(移行用・正本)
- type: validateForm
layer: controller
patterns:
- "バリデーション"
- "入力チェック"
- "必須*チェック" # 「必須項目チェック」「必須・桁数チェック」等
examples:
- text: "バリデーションチェックを行う"
source: 顧客編集_confirm!B12
- type: buildServiceInput
layer: controller
patterns:
- "*引数*作成"
- "*パラメータ*設定"
examples:
- text: "Service引数を作成する"
source: 顧客編集_confirm!B14
-
patternsはキーワードの部分一致・前方一致程度にとどめます。凝った正規表現は移行メンバー全員が読めなくなるので避けます -
examplesには 実際に出会った表記と出典セル を残します。あとで「なぜこのパターンを入れたのか」を追えるのはここだけです - 人向けの
docs/step-type-catalog.mdは、この YAML から DocEmitter で生成します。手で二重管理しません
初期辞書の作り方
最初から辞書は書けません。順序はこうです。
- 代表的なシートを 2〜3画面ぶん、人力で 全行マッピングする(AI に下書きさせてよいが、全行を人が確認)
- その過程で出会った表記を
patternsとexamplesに起こす。これが初期辞書 - 4画面目からパーサを回し、辞書ヒット率を見る
同じ会社の設計書は同じテンプレ・同じ流儀で書かれていることが多いので、表記ゆれは実際には 有限のパターンに収束します。体感として、最初の数画面で辞書の骨格はできあがり、以降は追記が減っていきます。減らない場合は後述の撤退シグナルです。
type を分ける基準は「文言」ではなく「生成物」
辞書を埋めていると、必ず「これは新しい type にすべきか、既存 type の表記ゆれか」で迷います。判断基準は1つです。
生成されるコードが変わるなら別 type。変わらないなら同じ type の表記ゆれ。
「入力チェック」と「バリデーション実施」は生成物が同じなので同一 type。「入力チェック」と「相関チェック(A 入力時は B 必須)」は生成物が違うので別 type です。文言の近さで判断すると辞書が発散します。
行番号(sourceRef)は step に持たせる
移行時に起こしたフロー IR の各 step には、元 Excel の出典を記録します。
handler: customerEdit.confirm
steps:
- layer: controller
type: validateForm
sourceRef: 顧客編集_confirm!B12 # 元シートのセル・行
- layer: controller
type: buildServiceInput
mapping:
customerId: form.id
sourceRef: 顧客編集_confirm!B14-B15 # 2行を1stepに束ねた場合は範囲
ルールが2つあります。
-
sourceRefはメタデータであり、生成には一切影響させない。docOnlyと同格です。同じ IR からは同じコードが出る、という決定論の対象外に置きます - 粒度のぶれの記録を兼ねる。 2行を1 step に束ねたら範囲で書く。1行を2 step に割ったら同じセルが2つの step に現れる。この対応が後述の承認作業の材料になります
sourceRef の用途は3つです。
- 移行レビューの対照表(次節)
- 指摘の突合 — 「設計書の◯行目がおかしい」という指摘を、どの step の話かに機械的に変換できる
-
辞書の根拠 —
examples.sourceとあわせて、分類判断のトレーサビリティになる
フローの逆翻訳チェックは「差分ゼロ」を狙わない
ここが画面 IR との大きな違いです。
画面 IR の逆翻訳(IR → 再生成 Excel → 元と比較)は、項目名や桁数の突き合わせなので 差分ゼロ を狙えます。しかしフローの逆翻訳は、「元の自然言語」対「type から生成した標準文言」の比較になるため、文言レベルでは必ず差分が出ます。差分ゼロを承認条件にすると、全 handler が人の目視行きになり、移行が止まります。
そこでフローの承認は、差分比較ではなく 対照表方式 にします。sourceRef を使って、元の行と対応付けた step を並べた表を機械生成し、人はそれを見て承認します。
顧客編集_confirm シート → customerEdit.confirm の対照表
行 元の表記 step type
────────────────────────────────────────────────────────────────────
B12 バリデーションチェックを行う steps[0] validateForm
B14 Service引数を作成する steps[1] buildServiceInput
B15 (↑に含む: 設定項目の一覧) steps[1] 〃
B16 Serviceを呼び出し更新結果を取得 steps[2], steps[3] call, mapResult
B18 ステータスコードを名称に変換 steps[5] mapCodeToName
B19 ※旧システムでは夜間バッチで実施 (docOnly へ) -
人が確認するのは次の3点だけです。
-
対応漏れ: 元の行で、どの step にも
docOnlyにも行っていないものがないか - 束ね方: 複数行を1 step にした箇所、1行を複数 step に割った箇所が妥当か
- docOnly 行き: 「コード化しない」と仕分けられた行が、本当に説明・背景であって業務ルールではないか
3点目が実は最重要です。業務ルールが docOnly に落ちて消えるのが、移行で最も怖い事故です。迷ったら docOnly ではなく manual step + @Disabled テスト骨子に倒します。消えずに「未実装マーカー」として残るからです。
進捗と撤退のシグナルを数字で持つ
移行の進捗は「何画面終わったか」ではなく、辞書の被覆率 で測ります。
| 指標 | 意味 | 見方 |
|---|---|---|
| 辞書ヒット率 | 第1層(決定論)で分類できた行の割合 | 画面を追うごとに上がっていくのが健全 |
| unknown 率 | 第3層まで落ちた行の割合 | 下がらない・画面ごとにバラつくなら黄信号 |
| 辞書の追記数 | 1画面あたり何パターン増えたか | 収束していくのが健全 |
第2回の撤退基準「詳細設計の行の5割超が step type 化できない」は、この指標でそのまま監視できます。10画面やって辞書ヒット率が伸びない・unknown 率が5割に張り付く、という状態は「この設計書は定型で書かれていない」ことの証拠なので、方式の適用範囲を見直します(例: データ定義と画面だけ IR 化し、フローは対象外にする)。
やってはいけないこと(辞書編)
| やりがちなこと | 何が起きるか |
|---|---|
| 辞書を持たず、毎回 LLM に全行分類させる | 同じ行が日によって違う type になる。移行のやり直しがきかない |
| AI の分類を人の承認なしで辞書に自動追記 | 誤分類が辞書に固定され、以降の画面に自動で伝播する |
sourceRef を生成ロジックに使う |
行番号が意味を持ち始め、元 Excel の編集で IR が壊れる。メタデータに徹する |
| 表記を揃えるために元 Excel を書き換える | 凍結前の元本改変。差分承認の根拠が消える。揃えるのは IR 側 |
| 文言の近さで type を増やす | 辞書が発散する。分ける基準は生成物が変わるかどうか |
まとめ
- 自然言語のぶれには 表記のぶれ と 粒度のぶれ の2種類がある。対応付けの単位は行ではなく「意味のまとまり」
- 対応付けは 辞書(決定論)→ AI 提案 → unknown で人へ の3層。人の判断は必ず辞書に書き戻し、辞書を育てる
- type を分ける基準は文言ではなく 生成されるコードが変わるかどうか
-
sourceRef(行番号)は step に持たせる。ただし 生成に影響しないメタデータ に徹する - フローの逆翻訳は差分ゼロを狙わず、対照表を人が承認する 方式にする。最重要チェックは「業務ルールが docOnly に消えていないか」
- 進捗は辞書ヒット率・unknown 率で測り、撤退基準に直結させる
- そしてこの仕組み全体が 移行のための使い捨て。移行後はカタログが統制語彙になり、表記ゆれは構造的に消える
次に書く予定の話題(シリーズ案)
- 自社 Excel サンプル1画面からフロー IR を起こす実例(対照表の実物つき)
- rule IR で判断表を実行する最小 DSL
- IR プレビュー環境の最小構成 ― 指摘を「IR の行」で受けるために
お使いの詳細設計の「書き方の流儀」が記事の例と違う場合は、コメントで教えてください。辞書パターンの実例集として反映します。