TL;DR
医療従事者向け Web アプリ 休薬レンズ (KyuyakuLens) では、学会の診療ガイドライン (以下 GL) の PDF を LLM で読み取って「手術前に止めるべき薬」のデータベースを作っています。この読み取りを高精度な Anthropic の Claude Opus に任せると、大きな GL 1 本で $4 ほどかかっていました。
そこに無料枠 3,000 リクエスト/月という太っ腹な さくらのAI Engine (gpt-oss-120b) を組み合わせ、 精度を一切犠牲にせずに AI コストを 1 桁下げることに成功しました。やったことは 3 つだけです。
- 役割分担: 値の抽出と裁定は Opus (精度の砦)、独立チェックは無料の gpt-oss
- ページ選別: 無料の gpt-oss に「読むべきページ」を選ばせ、 Opus には数ページだけ渡す
- 迷ったら止まる: 選別が空振りしたら全ページ送信に逃げず、中止して人間に投げる
「高価なモデルと無料モデルをどう役割分担させるか」という、コスト最適化の一般解として読んでもらえると嬉しいです。
何を作っているか (休薬レンズの紹介)
休薬レンズ は、お薬手帳の写真から、手術前・造影 CT 前に「一時的に止めるべき薬」を瞬時に見つけるアプリです。
手術や、造影剤を使う CT 検査の前には、事前に止めておかないと危険な薬があります。たとえば血液をサラサラにする薬は止めずに手術すると出血が止まりにくくなり、一部の糖尿病薬は検査と重なるとまれに命に関わる異常を起こします。だから医師は「この薬は何日前から止めてください」と指示を出す必要があります。この「一時的に薬を止めること」を医療では 休薬と呼びます。
やっかいなのは物量です。チェック対象の成分は 155 種類、対応する商品名はさらに膨大です (同じ成分でもメーカーにより名前違う、複数の薬を混ぜた合剤も)。短い診察時間でお薬手帳を見て抜けなく判断するのは現実には困難です。止め忘れたため手術できなかったり、気づかず手術してしまう「見落とし」は全国で恒常的に起きています。
そこでお薬手帳を OCR で読み取り、薬のデータベースと照合して「この薬を止めるべき」を色分けで提示する支援ツールを作りました。この判定の根拠になるのが学会ガイドライン (GL) 30 本超で、本記事の主役はその PDF から「何日前に止めるか」を抜き出す抽出パイプラインの話です。
課題: 高精度な Opus はページ数が多いと課金が問題となる
GL の PDF 読み取りには Claude Opus 4.8 のネイティブ PDF 入力を使っています。 PDF をそのまま (図や表のレイアウトごと) モデルに渡せるので、医療 GL の表 (「薬剤名 | 手術前に止める日数」のような表) を高精度で読めます。医療では数値の取り違えが事故に直結するので、この精度は譲れません。
問題はコストです。ネイティブ PDF 入力は 入力トークン ≒ PDF の全ページ数で決まります。実測するとこうなりました:
| GL | ページ数 | 概算コスト (1 回の読み取り) |
|---|---|---|
| 小さい GL | 4p | 約 $0.13 |
| 大きい GL | 304p | 約 $4 |
GL は 30 本超あり改訂のたびに読み直します。さらに後述のとおり 1 本を複数回読むので、実効コストはこの数倍。「全部 Opus」は精度は最高でも継続運用のコストとしては重い。
補足: 「プロンプトで『42 ページ目だけ見て』と指示すれば安くなるのでは?」と思うかもしれませんが、 減りません。指示テキストとは無関係に、 PDF 本体は全ページが課金されます。本当に減らすには渡す PDF そのものを該当ページだけに切り出す必要があります (後述)。
さくらのAI Engine という強力な選択肢
ここで さくらのAI Engine の出番です。これがとにかく使い勝手が良い。
- OpenAI / Anthropic 互換 API なので、既存のクライアントがほぼそのまま動く。導入コストが限りなくゼロ
- 無料枠 3,000 リクエスト/月 という太っ腹さ。しかもリクエスト数課金なので、 1 リクエストにどれだけ入力を詰め込んでも消費は「1」。大きな文書を丸ごと渡す用途にめっぽう強い
- モデルは
gpt-oss-120b。大きなコンテキストを高精度で扱えるので、 GL 全文を 1 リクエストで渡せる - 国内リージョンで動く安心感 (医療関連の開発ではここも効く)
GL 抽出は 1 GL あたり数リクエストなので、通常運用は完全に無料枠に収まります。「入力をどれだけ詰めても 1 回」という課金は今回の用途と相性が抜群でした。
それでも「全部 さくらのAI」にはできなかった
最初は「全部さくらのAIの gpt-oss で無料にしよう」と考えました。でもこれは性能ではなく入力形式の制約で断念しました。
さくらのAI Engine は PDF をそのまま渡せないので、一度こちらでテキスト化してから渡します。ところが医療 GL の表を汎用のテキスト抽出ライブラリ (pdfminer 等) にかけると、 行と列の対応が崩れます。下は実際の GL の表 (メトホルミンの造影剤検査時の休薬を各国 GL で比較した表) です。
出典: 日本医学放射線学会・日本放射線科専門医会造影剤安全性委員会「ヨード造影剤使用時のビグアナイド系糖尿病薬の取り扱いに関する提言」 (2012) 表 1
この表を pdfminer にかけると、こうなります (実際の出力の抜粋):
表1:ビグアナイド系糖尿病薬服用患者のヨード造影剤使用に関するガイドラインの比較
JDS
ACR
CAR
ESUR
RCR
RANZCR
言及せず 血清クレアチニン値
eGFR
eGFR(または SCr 値)
… (各列の 1 行目の値が ここに続く)
── ずっと後方に、行見出しだけが 固まって出現 ──
腎機能 評価基準
腎機能 異常値
腎機能 正常患者におけるビグアナイド系糖尿病薬投与中止時期
ビグアナイド系糖尿病薬投与再開時期
── JDS 列の値は さらに離れた位置に 単独で ──
検査後 2 日間は投与禁忌
列見出し・行見出し・セルの値がバラバラの順序で出力され、「検査後 2 日間は投与禁忌」がどの GL のどの項目の値か復元できません。数値の取り違えはそのまま事故のもとです。
一方、 Opus にネイティブ PDF のまま渡すと、レイアウトを保ったままこう構造化できます:
name: メトホルミン
drug_class: ビグアナイド系糖尿病薬
procedure: 造影 CT (ヨード造影剤)
source: JDS (日本糖尿病学会)
withhold_after_contrast:
duration: 検査後 2 日間 (48 時間)
action: 投与禁忌 (再開しない)
quote: "検査後 2 日間は投与禁忌"
行と列の対応を保てるので、どの値がどの項目のものか取り違えません。これが事故に直結するため、 値の抽出と裁定は Opus (ネイティブ PDF)、独立した検証は無料の gpt-oss という役割分担にしました。ここからさくらのAI Engine が「無料の検証役」として働きます。
全体の流れ
実際の処理は、大きく 「① ページ選別 → ② 独立 2 本の読み取り → ③ 突き合わせ」 の順に流れます。このうち Opus (高価) が担うのは ② の 1 本目 (抽出) と ③ の裁定。 gpt-oss (無料) が担うのは ① ページ選別と ② の 2 本目 (独立検出) です。
GL PDF (例: 304 ページ)
│
│ ① ページ選別 (大きい PDF のときだけ) ── 無料 (gpt-oss)
│ 1. PDF を 全ページ 機械でテキスト化
│ 2. さくらのAI が「休薬に関係するページ」を選ぶ
│ 3. 該当ページだけの PDF を 切り出す (例: 6 ページ)
▼
切り出した PDF (6 ページ) ← 以降の 3 工程すべてが これを使う
│
├─ ② 1 本目の読み取り … Claude Opus (ネイティブ PDF) → primary.yaml [値の砦]
├─ ② 2 本目の読み取り … さくらのAI (無料) → independent.yaml [独立検出]
│ ↑ Opus の 誤り・見落としを 無料で 炙り出す
▼
└─ ③ 突き合わせ … Claude Opus (ネイティブ PDF)
2 本の 食い違いを PDF 原文で 裁定して 確定
│
▼
人間 (医師) が 最終確認
① ページ選別で、Opus に渡すページを絞る
Opus の入力コストは PDF のページ数に比例します。 304p を丸ごと渡せば $4 のまま。でも休薬の記述は大きな GL でもたいてい数〜数十ページに集中しています。そこで高価な Opus に渡す前に、無料の gpt-oss でページを絞ります:
- 無料のテキスト抽出で PDF を全ページテキスト化 (課金ゼロ)
- それを無料の gpt-oss に渡して「休薬に関係するページ」を選ばせる (無料枠)
- 選ばれたページだけを切り出した PDF を作る
「読むべき場所を無料の AI に探させ、高価な AI には必要な所だけ精読させる」。これで Opus の入力トークンが全ページ → 該当ページに激減し、コストが桁で下がります。ページ数がしきい値を超えた PDF でだけ自動発動し、小さな GL は従来どおり全ページ渡します。選んだページは補助ファイルに保存し、後続の 3 工程で同じページ集合を使います。
実測 (心臓以外の手術の GL, 101p): gpt-oss は 65/101 ページを選択。肝心の休薬表 (p42) を正しく含み、薬剤名だらけの参考文献リスト (p91-93) は正しく除外。選ばれたページだけで対象 7 剤 (ワルファリン 5 日ほか) を正確に抽出できました。無料の gpt-oss の「目利き」がしっかり機能しています。
② なぜ 2 回読むのか
切り出した PDF を、 2 つの AI が別々に独立して読みます。単一の LLM はもっともらしい誤りを静かに混ぜるので、独立 2 本の差分であぶり出すのが狙いです (この「独立 2 本立て」はさくらAI 採用前からあるコアな設計)。
- 1 本目 = Claude Opus (ネイティブ PDF)。値の砦。抽出値は基本こちらを採る
- 2 本目 = さくらのAI Engine (無料)。独立した検出役。 Opus と食い違った項目に「ここを疑え」の印をつける
ここで大事なのは、 2 本目の gpt-oss は値を決めるために居るのではない点です。採用値はほぼ 1 本目の Opus。では何のためかというと、 Opus が静かに間違えた・見落とした項目を無料で炙り出す ためです。 Opus 一本だと全項目が「検証されていない」状態ですが、 2 本立てなら「2 系統が一致 = 信頼できる」「食い違い = 要確認」に仕分けできます。しかも別モデルにしたことで、同じ癖の同じ間違いが起きにくい。この「無料の検証網」が手に入るのがさくらのAI Engine の効きどころです。
③ Opus が 2 本を原文で突き合わせる
最後に、 Claude Opus (ネイティブ PDF) が 1 本目と 2 本目の結果を突き合わせ、食い違う項目を PDF 原文と照らして確定版を作ります。その後人間 (医師) が最終確認します。
裁定をなぜ gpt-oss でなく Opus にやらせるのか。裁定の最優先ルールは「PDF 原文にどちらが忠実か」で決めることだからです。ここを gpt-oss (崩れうるテキストしか読めない) に任せると、 Opus がネイティブ PDF で正しく読んだ値を、崩れテキストを根拠に誤って覆すおそれがある。だから裁定も原文を正確に読める Opus に任せます。 gpt-oss はあくまで ② の「独立検出役」に専念です。
まとめると、課金されるのは ① で絞り込んだ数ページを、 ② の 1 本目 (抽出) と ③ (裁定) で Opus が読む分だけ。 ① ページ選別と ② の 2 本目 (独立検出) はさくらのAI Engine の無料枠に載ります。
迷ったら止まる
ページ選別は便利な反面、 gpt-oss が肝心のページを取りこぼすと見落としが起きます。そこで 2 段の保険をかけました。
- 取りこぼしにくくする: プロンプトで「迷ったら含めよ」と指示し、選ばれたページの前後 1 ページも自動で足す
- 空振りしたら全ページ送信に逃げない: ここが医療特有の判断
選別が該当 0 件になったとき、素朴には「安全側に倒して全ページ Opus に送ろう」としたくなります。でもこれはあえて却下しました。選別が壊れている状態なのに高額な全送信を黙って実行してしまうし、「なぜ空振りしたか」を人が確認する機会も奪うからです。採った方針は:
選別が空振りしたら作業を中止し、メールで開発チームに通知する。次にどうするかは人間が判断する。
このとき Opus は 1 回も呼びません。「自動化が迷ったら、黙って突き進まず人間に投げる」。医療のように誤りのコストが高い分野では、 フォールバックの安易さこそがリスクだ、という教訓です。
コスト、 before / after
大きい GL (304p) を例に、全部 Opus で全ページ読んでいた頃と比べます。
| 工程 | Before (全部 Opus・全ページ) | After (役割分担 + ページ選別) |
|---|---|---|
| ① ページ選別 | ─ | 無料 (gpt-oss) |
| ② 1 本目の読み取り (Opus) | 約 $4 (全ページ) | 約 $0.13 (該当ページのみ) |
| ② 2 本目の読み取り (独立検出) | 同上 | 無料 (gpt-oss) |
| ③ 突き合わせ (Opus・裁定) | 同上 | 約 $0.15 (該当ページ + yaml 2 本) |
| 合計 | 約 $12 | 約 $0.3 |
Opus は抽出と裁定で 2 回呼びますが、どちらもページ選別で絞った数ページだけを読むので、全ページ Opus 1 回 ($4) より 1 桁以上安い。そして ① ページ選別と ② の 2 本目 (独立検出) は丸ごとさくらのAI Engine の無料枠に載ります。精度の砦と裁定は Opus に残したまま、実効コストを 1 桁下げられました。無料枠 3,000 リクエストの懐の深さに何度も助けられました。
さくらのAI Engine 実装 tips
OpenAI 互換なので実装はとても素直です。ハマりどころは gpt-oss 特有のこの辺:
$bodyArr = [
'model' => 'gpt-oss-120b',
'max_tokens' => $maxTokens, // 大きめに (下記)
'messages' => $normalized,
];
// ... POST /chat/completions with Authorization: Bearer ...
if (($data['choices'][0]['finish_reason'] ?? null) === 'length') {
// 思考トークンで出力が途中で切れた → max_tokens 不足
throw new Exception('truncated (finish_reason=length). Increase max_tokens.');
}
-
gpt-oss-120bは思考 (reasoning) トークンを出力に含む。max_tokensを小さくすると答えが完成する前にfinish_reason=lengthで切れる。長い出力を返すなら 16,384 以上を推奨。無料枠内はトークン非課金なので大きめに取って損はない - 空の応答に注意: 思考だけ返して本文が空のことがあるので空チェックを入れる
- リクエスト数課金を意識する。入力をむやみに分割すると枠を余計に食う。全文を 1 リクエストで渡すほうが有利
既存経路はいじらず sakura_ai を追加する形にしたので、設定を戻すだけで即ロールバックできます。
まとめ
- 高精度な Claude Opus のネイティブ PDF は医療 GL に最適だが、大きな GL で $12 かかっていた
- さくらのAI Engine (無料枠 3,000 req/月) を組み合わせ、値の抽出と裁定は Opus に残し、ページ選別と 2 本目の独立検出を無料枠に載せた
- 医療ならではの肝は「選別が空振りしたら中止して人間に投げる」。迷ったら止まる設計
- 結果: 精度を犠牲にせず、 AI コストを 1 桁削減
「無料モデルで全部やる」ではなく「高価なモデルと無料モデルの役割分担」。さくらのAI Engine の「互換 API・リクエスト数課金・無料枠 3,000・大コンテキスト」という特性は、前さばき / 相互チェック / 補助タスクを無料でガンガン回す使い方によく効きます。コストと精度のトレードオフに悩んでいる方は、まず無料枠を一度使い倒してみることを強くおすすめします。
そして休薬レンズ本体も、このパイプラインで作ったデータベースを土台に、定期的に GL を自動チェックし、新しい GL が見つかったら AI で読み取り → 構造化 → 医師が確認、というループで最新の GL に追随しています。医療現場の「休薬の見落とし」を 1 件でも減らすために作っています。医療従事者の方はぜひ 休薬レンズ を試してみてください。
参考リンク

