7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIを検索ツールで終わらせない:Javaコード解析スラッシュコマンド化で学んだ5つのTIPS

7
Posted at

はじめに

Claude Code を使い始めてしばらく経ちますが、最初のうちは「コードの仕様を調べてもらう」「エラーの原因を聞く」といった検索・質問用途がほとんどでした。😇

転機になったのが、多層アーキテクチャのJavaシステムを対象に「商品ごとに外部連携IFの定義書を作る」作業を繰り返すことになったときです。毎回、Resource → Service → Mapper と処理フローを手で追って設定値を拾い集めるのが苦痛で、「これをAIに任せられないか」と考えたのがきっかけです。

試行錯誤の末、Claude Code のカスタムスラッシュコマンドとして3ステップのAgentを作り、商品名とエントリポイントを渡すだけで定義書xlsxを自動生成できるようになりました。この記事はその過程で気づいたTIPSの記録です。


先にまとめ

  • TIP1: まず実行して確認してから、mdに書き起こす
  • TIP2: 複雑な処理はSTEPに分割し、完了条件と引き継ぎ情報を明示する
  • TIP3: OUTPUTフォーマットを具体的な書式例つきで指定する
  • TIP4: 解析ルールは「例外ケース」まで書く。暗黙知を出し切るのが難しい
  • TIP5: バグを見つけたら即座にmdへ書き戻す。修正はコードだけでなく指示書にも

今回作ったもの

対象は、顧客向けWebシステムの「商品ごとの外部連携IF定義書」作成作業です。

システムはResource → Service → Mapper → フレームワーク共通クラスという多層構造で、商品ごとに処理が分岐します。IFに何がセットされるかを把握するには、処理フローを丁寧に追う必要があります。手で解析すると、1商品あたり半日以上かかることもありました。

カスタムスラッシュコマンド(/create-if)として、以下の3ステップで実装しました。

STEP1: 処理フローの追跡
  エントリポイントからフレームワーク共通メソッドに到達するまでの
  呼び出し連鎖をコードから追跡し、解析基点情報を出力する

STEP2: IF項目・設定値の解析
  STEP1の解析基点情報をもとに、商品固有フィールドに
  セットされる値をコードから読み取る(内部保持)

STEP3: xlsx生成
  STEP2の解析結果をIFテンプレートCSVの設定値列に埋め込んだ
  Pythonスクリプトを生成・実行し、xlsx変換まで行う

コマンド化後は、同じ作業が数分で完了するようになりました。


TIP1: まず実行して確認してからmdに書き起こす

最初にやってしまった失敗が「最初からmdにまとめようとした」ことです。

「作業指示をドキュメント化したい」という気持ちが先に立ち、AIに実際に処理をやってもらう前にmdの構成を考え始めてしまいました。当然、実際に動かすとあちこちが噛み合わず、mdの書き直しが何度も発生しました。

うまくいったのは、まず指示を出して実行させ、結果を確認してOKを出してからmdに書き起こす、というサイクルです。

指示 → 実行 → 確認(OK) → mdへ記録 → 次のSTEP指示へ

「OK言ったらmdに書き起こして」というひと言を繰り返すだけで、検証済みの内容だけが指示書に積み上がっていきます。

もう一点、途中でAIが「特定のINPUTに対する実行結果」をmdに書いてしまうという事象もありました。スラッシュコマンドのmdに記録すべきなのは「処理手順のロジック」であって「特定のINPUTに対する結果」ではありません。

実際にこう指摘しました。

「INPUTを元に外部連携処理のフローを把握してもらいたい。これは次以降のSTEPの対象範囲を明確にするため。INPUTはAgent呼び出し毎に変わるので、その結果をmdに出力する必要はない」

指示書に書くべきことと、そうでないことを明確にするのは、人間向けのドキュメントを書くときと同じ感覚が必要です。


TIP2: 複雑な処理はSTEPに分割する

最初は「エントリポイントを渡したらxlsxまで一気にやってほしい」と思っていました。ただ、多層構造のコード解析 + 設定値の整理 + ファイル生成、という処理を1プロンプトに詰め込むと、AIが途中で迷子になりやすいです。

対策として、処理を3つのSTEPに分割し、それぞれに「完了条件」と「引き継ぎ情報の出力フォーマット」を明示しました。

① 完了条件を明記する

各STEPの末尾に「次のSTEPに進む」まで含めた完了条件を置きます。

**完了条件**
- フレームワーク共通メソッドへの到達を確認できた
- 引き継ぎ情報(処理フロー+解析基点情報)を出力した
- 次のSTEPに進む

「次のSTEPに進む」を完了条件に含めておくと、ユーザーが何も言わなくても次のSTEPに自動的に進みます。

② 引き継ぎ情報の書式を指定する

STEP1の出力がSTEP2の入力になる構造なので、引き継ぎ情報の書式を指定しておきます。

## STEP2 引き継ぎ情報

### 解析基点情報
- 基本Mapperクラス     : {クラス名}
- 業務別Mapperクラス   : {クラス名}
- objを設定している箇所: {クラス名}.{メソッド名}

書式を決めておくと、STEP2がSTEP1の出力を正確に参照できます。「とりあえず引き継いで」だと、何が引き継がれているかAI側も曖昧になります。


TIP3: OUTPUTフォーマットを明示する

「仕様をまとめて」だけでは、回答の粒度がバラバラになります。特にコード値("2"(メール)など)の表記は、最初にフォーマットを決めておかないと統一が崩れます。

今回は以下のルールをmdに明記しました。

ケース 記載方法
固定値 "値"(意味)
パラメータから取得 parameters["キー"] または具体値が分かる場合はその値
条件付き値 【条件】条件内容 → 改行 → 値
設定なし(条件不成立・null) -(ハイフン)

フォーマットを決める前の出力では、コード値の意味がなかったり("2" だけで何の2か不明)、条件の書き方がケースごとに違ったりしていました。最初の実行で「コード値は "コード値"(意味) の形式で」と指摘し、ルールを確定させてそのままmdに記録しています。

フォーマット指定は、指摘が入ったタイミングで即座にmdへ追記するのがコツです。「次から気をつけて」では次のコマンド実行で忘れられます。


TIP4: 解析ルールは「例外ケース」まで書く

ルールを言語化するいちばん難しい部分は、暗黙知の例外ケースです。

たとえば「パラメータに値がない場合はIFに設定しない」というルールがあるとします。ほとんどのケースはそれで正しいのですが、「nullでも、内部のユーティリティメソッドが空文字に変換してIFに設定される」という例外が存在しました。

最初のSTEP2ではこの例外を見落としていて、IFに設定される値の一部が欠落していました。

指摘を受けてルールを書き直したのが以下です。

**解析時の注意点**

- objを参照しているキーが「objを設定している箇所」で設定されているか確認すること
  - 設定されておらずnullでも、ユーティリティメソッド等を経由して
    空文字等に変換されてIFに設定される場合は、その値を設定値として記録する
  - nullのまま条件不成立となりIFに設定されない場合は、除外する

「nullなら設定しない」という単純なルールより、「nullでも空文字変換されることがある」という例外ケースを明文化する方が、実際の出力精度が上がります。

暗黙知の例外は、実行してみないと発見できないことが多いです。だからこそTIP1の「まず実行」が重要で、実行 → 指摘 → ルール追記というサイクルを回すことで、指示書が育っていきます。


TIP5: バグを見つけたら即座にmdへ書き戻す

コマンドが一通り動くようになってから、出力に問題が見つかりました。

バグの内容

IFテンプレートの「設定なし」項目のセルが、ハイフン(-)ではなく空文字になっていました。

生成されたPythonスクリプトの make_row 関数が、値が未指定の場合のデフォルトを空文字のままにしていて、ハイフンを自動適用するロジックが存在しませんでした。

# Before: 設定なし項目が空文字のまま
def make_row(row, val=''):
    row = list(row)
    while len(row) <= 8:
        row.append('')
    row[8] = val  # val='' のまま書かれてしまう
    return row

修正内容

「コンテナ行(グループの区切り行)は空のまま、フィールド行にはデフォルトで - を適用する」というロジックを追加しました。

# After: フィールド行に自動でハイフンを適用
def is_container(row):
    return any('(情報域)' in (c or '') for c in row[:6])

def make_row(row, val=''):
    row = list(row)
    while len(row) <= 8:
        row.append('')
    if val == '' and not is_container(row):
        val = '-'
    row[8] = val
    return row

修正後、305行にハイフンが設定され、空のまま残る非コンテナ行は0件になりました。

重要なのはここから

コードを修正しただけでは、次回コマンドを実行したときにAIが同じスクリプト(バグあり)を生成してしまいます。

そこで、バグ修正の内容をコマンドのmd(スラッシュコマンドの指示書)にも書き戻しました。STEP3の「処理の流れ」に make_row の実装仕様と実装例を追記しています。

**処理の流れ**

1. STEP2の解析結果をもとに、商品固有の設定値を埋め込んだ
   Pythonスクリプト(generate_if.py)を生成する
   - `make_row(row, val='')` は以下のロジックで設定値を決定する:
     - `val` が指定されている場合はその値を使用する
     - `val` が未指定かつ行がコンテナ行の場合は空文字のままにする
     - `val` が未指定かつ行がフィールド行の場合は `-` を設定する
   - 実装例:
     ```python
     def is_container(row):
         return any('(情報域)' in (c or '') for c in row[:6])

     def make_row(row, val=''):
         row = list(row)
         while len(row) <= 8:
             row.append('')
         if val == '' and not is_container(row):
             val = '-'
         row[8] = val
         return row
     ```

これで次回以降の実行では、AIが正しい make_row を含むスクリプトを生成します。「バグを直した」だけでなく「指示書を更新した」ことで、デグレードが防がれます。

コードの修正は1回で終わりますが、指示書の更新は次回以降の全実行に効きます。手間は同じ、効果は繰り返されます。


まとめ

  • 試してからmdへ書き起こすサイクルを守ると、検証済みの指示書が積み上がる
  • 処理をSTEPに分割して引き継ぎ情報を構造化すると、AIが複雑な処理でも迷子にならない
  • OUTPUTフォーマットと例外ケースのルールを最初に決めておくと、修正往復が減る
  • バグはmdにも反映する。コードだけ直しても、次の実行で同じスクリプトが生成される

スラッシュコマンド化してから、1商品あたりの定義書作成にかかっていた時間が大幅に短縮されました。「検索用途でしか使えていないな」と感じていたAIの活用が、こういう形で実務のサイクルに組み込めると、また違う手ごたえがあります。


最後に、GMOコネクトではサービス開発支援や技術支援をはじめ、幅広い支援を行っておりますので、何かありましたらお気軽にお問合せください。

お問合せ:https://gmo-connect.jp/contactus/

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?