4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Excel手順書の自動生成をClaude Codeに丸投げしたら、プロンプトの書き方が9割だった

4
Posted at

はじめに

GMOコネクトの平島です。

複数シナリオ×2パターンで複数シートのExcel手順書、Claude Codeに丸投げしてみたら最初は全然うまくいきませんでした😇 プロンプトの書き方を変えたら動いた、という話です。

具体的には、Markdownで書かれた調査手順を「開発者向け(プレースホルダあり)」と「検証者向け(具体値あり)」の2パターンのExcel手順書に自動変換するスクリプトをClaude Codeに生成させました。テンプレートには結合セル・列幅・罫線・連番数式がびっしり設定されており、書式を維持したまま生成するには、それなりの指示の工夫が必要でした。

コードはClaude Codeが書いてくれます。人間が考えるべきは指示の設計だけです。

先にまとめ

  • 事前調査をプロンプトに埋め込む:まずClaude Codeにテンプレートを調査させ、判明した構造を指示プロンプトに書き込む
  • フォルダを references/(読み取り専用)と output/(書き込み先)に分ける:「元ファイルを上書きしない」は明文化しないとやってしまう
  • 使うAPIを名指しするcopy_worksheet() と明示しないと create_sheet() が選ばれ書式が崩れる
  • Acceptance criteriaを機械的に検証できる条件で書く:「シート数N枚」「連番数式が存在する」のように書くと、Claude Codeが自発的に検証スクリプトまで生成する
  • 指示プロンプト自体をファイルとして output/ に保存する:再実行・チーム共有・Git管理が可能になる

失敗パターン:「なんとなく指示」するとどうなるか

最初に試みたのは、こういう指示でした。

このMarkdownファイルを読んで、テンプレートのExcelに内容を転記するPythonスクリプトを書いて

結果として生成されたコードには、以下の問題が含まれていました。

ハマり1: テンプレートを直接上書きした
load_workbook(template_path)save(template_path) という破壊的な実装になっていました。

ハマり2: create_sheet() で書式が飛んだ
テンプレートの書式が引き継がれず、セルスタイルがすべてデフォルトに。

ハマり3: 出力先が未指定
カレントディレクトリに謎のファイルが生成されました。

ハマり4: 2パターンの作り分けが考慮されない
プレースホルダ版と具体値版の区別がまったくありませんでした。

これらは「普通のスクリプト」としては正しい実装ですが、「書式凝りまくりのExcelテンプレートを維持しつつ複数派生を生成する」という要件が指示に含まれていなかったため、LLMには伝わりませんでした。

足りなかったのはコードの知識ではなく、指示の精度です。

事前調査フェーズ:Claude Codeにテンプレートの構造を調査させる

本番の指示プロンプトを書く前に、まずClaude Codeにテンプレートの構造を調査させます。「このテンプレートのシート構成・列幅・結合セル・数式を調べてほしい」と依頼すると、Claude Codeが以下のようなスクリプトを生成・実行してくれます。

# Claude Codeが生成・実行した調査スクリプト
import openpyxl
wb = openpyxl.load_workbook("references/templates/template_procedure.xlsx")
ws = wb.active

print("シート名:", ws.title)
print("結合セル:", sorted(str(m) for m in ws.merged_cells.ranges))
print("列幅:", {k: round(v.width, 1) for k, v in ws.column_dimensions.items() if v.width})

for coord in ['B6', 'B11', 'C11', 'D11', 'G11', 'B13', 'G13']:
    c = ws[coord]
    print(f"{coord}: value={c.value}, font={c.font.name}/{c.font.size}, bold={c.font.bold}")

この調査で判明した事実をプロンプトに直接埋め込みます

### テンプレートの構造(調査済み)
- シートは1枚のみ。シート名: `対応手順_<サービス名>_手順`
- `B6:D9`(結合セル)に `■前提条件` ブロック
- 11行目がヘッダ:`B=No / C=確認 / D=作業概要 / E=担当 / F=目的 /
  G=詳細手順(実行コマンド等) / H=参照画面 / I=備考`
- 13行目以降がデータ行。`B列`は `=ROW()-12` の連番数式
- 列幅:A列(狭め)/ B列(狭め)/ D列(中)/ G列(広め)/ I列(中)

こうすることで、LLMが「自己流に構造を推測する」のを防ぎます。LLMは指示に書かれていない部分を補完しようとするため、重要な制約ほど明文化が必要です。

また、2種類のテンプレートの差分も調査して明文化しました。

### 2テンプレートの差分(調査済み)
| 観点      | 開発者向け          | 検証者向け                |
|----------|--------------------|-----------------------|
| G列詳細手順 | [取得するリージョン] 等のプレースホルダを残す | プレースホルダを具体値に全置換 |
| I列備考   | 「※環境名は手順書作成時に記載」の注記あり | 同注記なし |

フォルダ設計:「読み取り専用」と「書き込み先」を分離する

プロジェクトのフォルダ構成は、指示プロンプトを書く前に決定しておきます。

project/
├── references/                        # 読み取り専用(元ネタ・テンプレート)
│   ├── playbook.md                    ← 手順の元ネタ(Markdown)
│   └── templates/
│       ├── template_procedure.xlsx    ← 開発者向けテンプレート
│       └── template_procedure_ver.xlsx ← 検証者向けテンプレート
└── output/                            # 書き込み先(生成物)
    ├── procedure.xlsx                 ← 生成された開発者向け手順書
    └── procedure_ver.xlsx             ← 生成された検証者向け手順書

このフォルダ設計の重要なポイントは references/output/ の明確な分離です。

LLMは「元ファイルを修正するか、コピーして修正するか」を指示で明示しないと、どちらかわかりません。指示プロンプトのConstraintsに次のように書きました。

Constraints:
1. 元ファイルを直接修正しない。3つの入力ファイルはいずれも読み取り専用として扱い、
   上書き保存しない。各テンプレートはまず output/ 配下にコピーを作成し、
   コピーに対してのみ編集する。

この一文があることで、生成されたコードは必ず shutil.copyfile(template, outpath) でコピーしてから編集するという実装になりました。

プロンプト設計:Goal/Constraints/Acceptance criteriaの3層構造

実際に使用したエージェント指示プロンプトの構造を解説します。

Goalセクション

Goal:
`references/playbook.md` の全シナリオの調査手順を、既存テンプレートの体裁に
沿ってExcelの手順書に落とし込む。成果物は次の2ファイルを output/ フォルダに作成する。
  1. 開発者向け手順書 … 開発者向けテンプレートをコピーしたxlsx
  2. 検証者向け手順書 … 検証者向けテンプレートをコピーしたxlsx
各ファイルは、テンプレートの既存シートを残したまま、1シナリオ=1シートで
シートを追加した状態にする。

Goalには「何を作るか」と「成果物の形」を書きます。特に重要なのが「テンプレートの既存シートを残したまま追加する」という一文です。これがないとLLMは既存シートを上書きします。

Constraintsセクション

ConstraintsはLLMが「よかれと思ってやってしまう」行動を事前に封じるために書きます。

Constraints:
2. 書式の維持:新規シートはテンプレートの既存シートを複製(openpyxlの
   copy_worksheet等)して作成し、ヘッダ・結合セル・列幅・セル書式・罫線を引き継ぐ。
   テンプレートと同じ列レイアウト(B〜I列、11行目ヘッダ、13行目以降データ)を崩さない。

3. B列の連番数式 `=ROW()-12` はテンプレートの形式を踏襲し、追加行にも設定する。

4. 開発者向けと検証者向けの作り分け:
   - 開発者向け:プレースホルダをそのまま残す。I列備考に
     「※環境名・IPは手順書作成時に置換」の注記を入れる。
   - 検証者向け:プレースホルダを仮値(別途定義)に全置換し、
     コピー&ペーストで即実行できる状態にする。
     I列備考の手順書作成時注記は入れない。

5. プレイブックに記載のない値を創作しない。判断に迷う箇所は
   I備考にその旨を明記する。

注目してほしいのは 「openpyxlのcopy_worksheetを使う」と明示した点です。

openpyxlには新規シートを追加するcreate_sheet()もありますが、これだとテンプレートの書式が引き継がれません。copy_worksheet()を明示しなければ、LLMはcreate_sheet()を選ぶことがあります。使ってほしいAPIを名指しで指定することで、実装方針のブレがなくなります。

Acceptance criteriaセクション

Acceptance criteriaは機械的に確認できる形で書くのがポイントです。「正しい出力になっている」ではなく「シート数が10枚である」のように、検証スクリプトでアサートできる条件を書きます。

Acceptance criteria:
1. `output/` に開発者向け・検証者向けの2つのxlsxが生成されている。
   元の3入力ファイルのタイムスタンプ・内容が変更されていない。

2. 各xlsxは、テンプレート由来の既存シート1枚+新規追加シートN枚の計(N+1)シートを持つ。
   新規シート名は `対応手順_<サービス名>_<シナリオ名>` 形式。

3. 各シナリオシートに、全ステップ行が漏れなく存在する。

4. ヘッダ行(11行目)・列幅・結合セル・B列連番数式がテンプレートと同一の体裁で
   維持されている。

5. 開発者向けはプレースホルダが残存し、検証者向けは具体値に置換されている、
   という差分が全シナリオで一貫している。

6. 生成したxlsxがopenpyxlで正常に開けること(破損していないこと)を確認済みである。

Acceptance criteriaにこれだけ書いておくと、Claude Codeは自発的に検証スクリプトを生成します。実際に生成されたのが以下のような検証コードです。

# Claude Codeが自動生成した検証コード(_verify.py)
for path, ver in [(OUT_DEV, False), (OUT_VER, True)]:
    wb = openpyxl.load_workbook(path)
    assert len(wb.sheetnames) == N + 1, wb.sheetnames        # シート数チェック
    assert wb.sheetnames[0] == BASE_SHEET                    # 元シートが先頭
    for sc in scenarios:
        ws = wb[sc['sheet']]
        assert ws['B11'].value == 'No' and ws['I11'].value == '備考'  # ヘッダ確認
        for i in range(len(sc['rows'])):
            assert ws['B' + str(13 + i)].value == '=ROW()-12'        # 連番数式確認
            for col in 'DFGI':
                assert ws[col + str(13 + i)].value                    # セル非空確認

「何をもって完了とするか」が明確であれば、LLMは完了条件を満たすための実装を自分で設計します。

指示プロンプトをファイルとして残す

もう一つ重要な実践として、エージェント指示プロンプト自体を成果物として保存しました。

output/
├── 【手順書Excel化】_エージェント指示プロンプト.md   ← これも成果物
├── procedure.xlsx
└── procedure_ver.xlsx

プロンプトをファイルとして残すことには、次のメリットがあります。

  1. 再実行できる:Markdownのプレイブックが更新されたとき、同じ指示プロンプトを別エージェントに渡せば即座に再生成できる
  2. チーム共有できる:「どう指示したら動いたか」がGitで管理される
  3. 改善できる:出力が期待と違ったとき、どのConstraintが不足していたかをプロンプトに追記していける

プロンプトファイルをGitに置いておくと、次回は渡すだけで終わります。

結果:Claude Codeが生成したコードの構成

Acceptance criteriaを含む指示プロンプトを渡した結果、Claude Codeは以下の5ファイル構成を自然に生成しました。

_engine.py       # Excel生成の中核ロジック(copy_worksheetによるシート複製、行高計算等)
_run.py          # エントリポイント(TEMPLATE_DEV/VERとOUT_DEV/VERを定義して呼び出す)
_scen_part1.py   # シナリオ前半のデータ定義
_scen_part2.py   # シナリオ中盤のデータ定義
_scen_part3.py   # シナリオ後半のデータ定義
_verify.py       # 検証スクリプト(Acceptance criteriaをコードに落とした版)

コードを分割した理由は、シナリオデータが大きくなるためです。Claude Codeはファイルを分割するかどうかを自分で判断し、_scen_part1/2/3.pyという構成を選びました。これは明示的に指示していません。Acceptance criteriaに「全シナリオを網羅する」と書いたことで、スコープが大きいと判断して自発的に分割したと考えられます。

実装の核心部分がどういうものかだけ示すと、以下のようなイメージです。

# _engine.py の核心(Claude Codeが生成)
def build_workbook(template, outpath, scenarios, ver):
    shutil.copyfile(template, outpath)   # 元ファイルを保護してコピー
    wb = openpyxl.load_workbook(outpath)
    base = wb[BASE_SHEET]

    for sc in scenarios:
        ws = wb.copy_worksheet(base)     # 書式をまるごと引き継ぐ
        ws.title = sc['sheet']
        
        # 行13のセルスタイルを全データ行に複製する
        src_style = {col: copy.copy(ws[col + '13']._style) for col in 'BCDEFGHI'}
        
        for idx, row in enumerate(sc['rows']):
            r = 13 + idx
            for col in 'BCDEFGHI':
                ws[col + str(r)]._style = copy.copy(src_style[col])
            ws['B' + str(r)] = '=ROW()-12'   # 連番数式
            ws['G' + str(r)] = row['g_ver'] if ver else row['g_dev']  # 2パターン切り替え
    
    wb.save(outpath)

ver=True/False の一つのフラグで2種類のファイルを生成できているのは、Constraintsで「2つの差分は〇〇だけ」と明確に定義したからです。

まとめ

  1. 事前調査をプロンプトに埋め込む — LLMは指示にない部分を補完しようとします。まずClaude Codeにテンプレートを調査させ、判明した構造を指示プロンプトに書き込む
  2. references/output/ を分け、制約として明文化する — 「元ファイルを上書きしない」は当然に思えても、書かないとやってしまいます
  3. 使うAPIを名指しするcopy_worksheet() と書けば書式ごと複製、書かなければ create_sheet() で素のシートが生まれます
  4. Acceptance criteriaを機械的に検証できる条件で書く — 「正しい」ではなく「シート数N枚」「=ROW()-12 が存在する」のように書くと、Claude Codeが検証スクリプトまで生成してくれます
  5. 指示プロンプト自体を output/ に保存する — 再実行・チーム共有・Git管理が可能になります

最後の目視確認だけ人間がやれば十分です。


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

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

4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?