Claude Code × Medical Application

【Claude Code】Medicare Part D × Text-to-SQL Part2:プロジェクト文書と skill

1. はじめに

Part1 では、エージェントが動く環境(権限と MCP)を作りました。Part2 は文書の回です。

この回は、作った順番どおりに進めます。

  1. 着手前に、人が文書を書く
  2. 文書を読ませて、SQL 生成の部分を作らせる
  3. 実行して分かったことを、文書に書き戻す
  4. 何度も繰り返す工程を、skill に切り出す
Part2 の流れ。文書を書く → 文書を読ませて作る → 見つかったことを書き戻す → 繰り返す工程を skill に

2. 着手前に書いた文書

2-1. 3つの文書の分担

コードを1行も書かせる前に、人が3つの文書を用意しました。4つ目の DECISIONS.md は空のまま始め、作りながら書き足していきます。

文書 書くこと 読まれ方
CLAUDE.md 作業の順番、守るルール、やらないこと 毎回、最初に読まれる
docs/design.md 何を作るか(構成、DDL、ツール定義、実行ループ、ガード、画面) 作業の前に読むよう CLAUDE.md で指示
docs/data_dictionary.md CMS 公式データ辞書の日本語訳。列名・型・抑制ルール 列名と型を決めるときの基準
docs/DECISIONS.md 決めたことと、その理由 同じ失敗を繰り返さないために読む

大事なのは、2つの文書が食い違ったときにどちらを優先するかを先に書いておくことです。CLAUDE.md には「設計書の DDL は抜粋なので、データ辞書を優先」と書きました。これが無いと、エージェントは食い違いに気づいても自分で判断するしかありません。

上:設計書(design.md)の「温度 0」。下:DECISIONS.md の「Sonnet 5 以降は temperature を送ると 400」

2-2. CLAUDE.md の構成

節 役割
必読ドキュメント 作業前に読むファイルと、その順番
技術スタック(固定) エージェントに選ばせないもの。モデル名は環境変数で渡し、コードに直接書かない
リポジトリ構成 「この通りに作る」というファイル配置
作業フェーズ データ → プロンプト → エージェント → UI → 評価 → 公開。各フェーズの最後に動作確認をしてから次へ進む
実装ルール 数値の扱い、ガード、Claude の呼び出し方、記録の仕方
やらないこと 認証、医師個人の評価につながる機能など
動作確認コマンド 1問を CLI で通す、API と UI を起動する、評価を流す

作業フェーズは止まる場所を、やらないことは作業の範囲を、動作確認コマンドは完了の条件を決めています。Part0 で「人が渡すのは環境・判断基準・停止条件」と書いたうち、判断基準と停止条件にあたるのがこの文書です。

CLAUDE.md の作業フェーズ。見出しに「各フェーズ末に動作確認してから次へ」と止まる場所を書く

3. 文書を読ませた SQL 生成

3-1. 境界条件

SQL 生成は、作業フェーズの2(プロンプト)と3(エージェント)にあたります。境界条件は、指示のたびに言わなくても済むよう、文書側に書いてありました。

  • ツールは run_sql と plot_spec の2つだけ。増やすときは先に設計書を直す
  • フェーズ3の完了条件は「CLI で1問通す」。通ったら止まる
  • ガードは「省略禁止」。生成した SQL は、ガードを通してから BigQuery に投げる

3-2. ツールを2つに分ける

run_sql と plot_spec を分けた実行ループ。拒否・エラー・0行なら書き直す(最大3回)

SQL の実行と、グラフの仕様を決める作業を、別々のツールにしました。理由は2つです。

  • SQL をアプリ側で検査してから実行できる。 Claude が書いた SQL は、BigQuery に届く前に必ずガードを通る
  • 出力の形は SQL と関係なく決まる。 「折れ線にして」のように表示の形だけを変える指示では、SQL を再実行せず plot_spec だけを返させます

3-3. ガード

app/guards.py は次のような SQL を拒否します。

  • SELECT か WITH で始まらないもの、; で複数の文をつないだもの
  • partd の5つの表以外を参照するもの
  • 書き込み系の命令(DELETE・UPDATE など)を含むもの
  • SELECT *

拒否したときのエラー文は、そのまま Claude への指示文になります。「必ず partd.<テーブル名> の形で書いてください」のように、次にどう書き直せばよいかが分かる文章にしてあります。Claude はそれを読んで SQL を書き直し、3回まで自分で直します。

app/guards.py。拒否するときのエラー文が、次にどう書き直せばよいかを Claude に伝える

ガードが正しく働くかを自動で確かめる仕組み(テストと hook)は、Part3 で扱います。

3-4. 用語辞書

ユーザーの言葉と列の対応は app/prompts/glossary.yaml に置き、システムプロンプトに埋め込みます。冒頭には「すべて BigQuery の実データで確認済み。推測で書かないこと」と書きました。

節 例
言い方 → 列 「処方数」→ tot_clms、「処方量」→ tot_30day_fills
どの表を使うか 州別の集計は軽い geo_drug、医師×薬剤の明細は重い provider_drug
薬効クラス 「GLP-1」のような分類は drug_class 表と結合して絞る

4. 実行で見つかったことと、文書への書き戻し

4-1. 実データを流して分かったこと

設計書どおりに作っても、実データを流すと想定と違うことが起きます。見つかった内容は、その場の会話で終わらせず、必ずどれかの文書に書き戻しました。

見つかったこと 書き戻した先
課金の上限は、実行時の実測ではなく dry-run の見積もりに対して効く。見積もりは実測の数十倍に出るため、上限を厳しくすると provider_drug へのクエリがほぼすべて拒否された 設計書のガード節、CLAUDE.md、DECISIONS
設計書では「温度0」としていたが、Sonnet 5 以降は temperature を送るとエラーになる CLAUDE.md(「温度は指定しない」に変更)、DECISIONS
一般名が列幅で切り詰められている(Empaglifloz/Linaglip/Metformin)。正式名を IN で並べると0行になる 用語辞書、DECISIONS
配合剤は複数の薬効クラスに属する。クラスをまたいで合計すると二重に数える 用語辞書
「処方医数」を薬剤名の行ごとに合計すると重複する。セマグルチドは合計 453,256 に対し、実際は 174,885 人 用語辞書
並べ替えに使っていない指標で順位を述べると、順位を取り違える system.md

設計書の「温度0」は、書いた時点では正しい判断でした。設計書は計画、CLAUDE.md は実際に踏んだあとのルールです。両者のずれは消さず、理由を DECISIONS に残しました。

4-2. 書き戻し先の決め方

書き戻し先 書くもの
DECISIONS.md 何を決めたか、なぜか。1〜3行と日付
CLAUDE.md 次に作業するエージェントが必ず守るルール。踏んだ実例を添える
用語辞書・system.md 質問に答えるときにアプリの Claude が参照するもの

CLAUDE.md の実装ルールに「設計を変えたら DECISIONS に1〜3行で追記する」と書いてあります。そのため、書き戻しは人が思い出さなくても、エージェントの作業の一部として行われます。

4-3. 人が決めたことと、実行が見つけたこと

人が決めたこと(着手前) 実行が見つけたこと(実データを流して)
ツールは2つ。ガードは省略しない 課金上限が見積もりに対して効くこと
抑制された空欄を0にしない 一般名の切り詰め、配合剤の二重計上
迷ったらデータ辞書を優先する 処方医数を合計すると重複すること
設計を変えたら DECISIONS に書く モデル世代による API の仕様変更

左の列は、どう壊れるかに関わる判断です。右の列は、実データを流して初めて分かったことです。右の列は設計書に先回りして書くことはできませんが、見つかったら必ず書き戻すというルールなら、先に書いておけます。

5. 繰り返す工程の skill 化

5-1. 文書だけでは人の手に残った工程

アプリが一通り動いたところで振り返ると、文書だけでは回しきれていない工程がありました。Part1 で手作業で追った「CMS の CSV を BigQuery に載せて確かめる」工程です。

  • CLAUDE.md の作業フェーズ1は1行しかない
  • 落とし穴(URL を直接書かない、スキーマを自動検出させない、NPI の先頭0)は DECISIONS のあちこちに散らばっている
  • 投入結果の確認は、人がコンソールで見ていた

次の題材(Open Payments)も、CMS の CSV を BigQuery に載せるところから始まります。同じ工程をもう一度やることが分かっているので、ここで手順として切り出しました。

5-2. CLAUDE.md と skill の使い分け

CLAUDE.md skill(.claude/skills/<name>/SKILL.md)
読まれるとき 毎回、最初に 呼ばれたとき、または内容が合うと Claude が判断したとき
書くもの いつでも守るルール 順番と止まる場所がある手順
例 識別子は数値として扱わない CSV → BigQuery の投入と確認

skill にするかどうかの目安は2つです。2回目に「またこれか」と思ったか。そして、手順に順番と止まる場所があるか。

5-3. cms-csv-to-bigquery

cms-csv-to-bigquery の工程。–plan のあと人の承認で必ず止まる

SKILL.md には3つのことを書きました。

  • 手順:データ辞書と実際の CSV の列名を照合するところから、投入後の確認まで。URL を直接書かない、スキーマを自動検出させない、空欄を0にしない、といった落とし穴は、該当する工程の中に書いた
  • 守ること:工程をまたいで常に守ること
  • このリポジトリでの値:Part D に固有の値(データセット、年、識別子列など)

冒頭で、年がどの工程に効くか、どこで止まるかを先に宣言しています。

SKILL.md の冒頭。年が効く工程と、止まる場所(5 の –plan)を先に書く

「守ること」の最初の1行が、止まる場所です。

SKILL.md の「守ること」。5 で止まり、承認なしに –run を打たない

Part D に固有の値は、末尾の表にまとめて分けました。手順の本文を書き換えずに、第2作で表だけを差し替えて使えます。

SKILL.md の末尾。Part D 固有の値の表と、別データセットで変えるもの

5-4. 止まる場所

投入の実行(--run)は、skill が使ってよいコマンドの一覧(allowed-tools)にあえて入れていません。Part1 で --run を ask(人の確認が必要)に入れてあるので、skill から呼んでも実行前に必ず止まり、人の承認を待ちます。skill で許可しても settings の ask を飛ばすことはできないので、止まる場所は1か所で管理できます。

/cms-csv-to-bigquery geo_drug 2024 の実行。工程5(–plan)まで進め、承認を求めて止まった

投入後の確認は /verify-data という別の skill に分けました。Part1 で見つかった「表の有効期限」の確認も、ここに手順として加えています。

5-5. 書いた手順を、1回動かして直す

SKILL.md はエージェントがコードを読んで書いたものですが、書いたとおりに動かすと失敗しました。手順には --plan geo_drug 2024 と年を渡す書き方がありましたが、load.sh は年を受け取らず「不明な引数」で止まったのです。年を使うのは取得と前処理だけで、投入は表の単位で行います。SKILL.md をそのように直しました。

skill は、1回動かしてから信用します。 コードを読んだだけでは、引数の仕様まで正確に合わせることはできません。

5-6. 誰が呼べるか

skill 呼べる 理由
cms-csv-to-bigquery 人と Claude 課金が出る --run は settings の ask で止まる
verify-data 人と Claude 読み取り専用の MCP だけで動く
eval・deploy 人だけ API の課金や外部への公開を伴う(Part4)

Claude が自分から呼べないようにするには、SKILL.md の設定に disable-model-invocation: true を書きます。

6. 今回のハーネス

道具 このリポジトリでの実体 文書だけのとき人がやっていたこと
プロジェクト文書 CLAUDE.md、docs/(設計書・データ辞書・DECISIONS)、用語辞書 —
skill cms-csv-to-bigquery、verify-data 手順と落とし穴を思い出し、投入結果をコンソールで確かめる
権限と停止条件 Part1 の settings(--run は ask) 課金が出るコマンドを目で確かめる
MCP Part1 の BigQuery MCP(verify-data が使う) 行数とスキーマを見て貼る

7. まとめ

  • 文書は役割で分ける。 毎回守るルールは CLAUDE.md、作る対象は設計書、名前と型は辞書。食い違ったときにどれを優先するかも書いておく
  • 実行で見つかったことは、必ず文書に書き戻す。 設計書(計画)と CLAUDE.md(踏んだあとのルール)のずれは消さず、理由を DECISIONS に残す
  • 繰り返す工程は skill にする。 止まる場所は skill の中ではなく settings で決め、書いた手順は1回動かしてから信用する

任せられる範囲は、事前にどれだけ検証可能にしておいたかで決まります。 文書は、その「事前」を形にしたものです。

次のステップ

Part3 では、プロンプト設計と hook を扱います。Claude Code への指示に入れる境界条件と止まる場所、そしてガードのテストや型チェックを、人が言わなくても編集のたびに走らせる hook を見ていきます。題材は UI の実装と表示崩れです。

出典

  • Anthropic, Claude Code Docs(memory / skills / settings)
  • CMS, Medicare Part D Prescribers Data Dictionary (data.cms.gov)

コードは GitHub で公開しています: github.com/HerzLeben/medicare-partd-text-to-sql