Claude Code × Medical Application

【Claude Code】目視の確認を自動チェックに Part4:公開と運用 — 帳票を受け取らない設計

1. はじめに — 帳票は外に出せない

申請前の帳票は、会社の中でもっとも外に出しにくい資料の1つです。試験の結果そのものが載っていて、社外のサービスに上げてよいかは、社内の手順で厳しく決められているのがふつうです。

便利な確認ツールがあっても、「PDF をこのサイトに送ってください」という形では、製薬の現場では使ってもらえません。そこで TLF Visual QC は、最初から次の形に決めていました。

  • 帳票を受け取らない:計算はすべて、使う人のブラウザの中で行う
  • サーバーは画面を配るだけ:受け取る仕組みそのものを持たない
  • 外への通信を止める:画面の部品も含めて、サイトの外に何も取りに行かない

この回では、この形をどう作り、どう確かめたかを見ていきます。

  1. 構成 — サーバーを持たない
  2. 外への通信を止める仕組みと、確かめ方
  3. 本番の環境でだけ起きた不具合
  4. 通信を切っても動くようにする
  5. 配信元が付け足すもの — 通信エラーの報告と、解析のスクリプト
  6. 検証済みでないツールとしての位置づけ
  7. 例の帳票を同梱しない理由
  • 0件PDF を読み込んで計算する間の、サイトの外への通信
  • 約200件画面を開いたときに読み込むファイル(すべて同じサイトの中)
  • 0サーバー側で動くプログラム(画面のファイルを配るだけ)
  • 3つ本番の環境でだけ起きたこと(書体のずれ、通信エラーの報告、解析のスクリプト)

2. 構成 — サーバーを持たない

ブラウザの箱の中で PDF.js・改ページの計算・色の計算が動き、Cloudflare Pages の箱は画面のファイルを配るだけの構成図。PDF はブラウザの箱から出ない
構成。PDF はブラウザの中だけで扱い、サーバーは画面のファイルを配るだけ

TLF Visual QC は、HTML・JavaScript・PDF.js のデータファイルだけでできた 静的なサイト です。Cloudflare Pages に置いていて、サーバー側で動くプログラムはありません。

DPC のダッシュボード TLF Visual QC
置き場所 Cloudflare Pages Cloudflare Pages
サーバー側の処理 あり(AI への問い合わせを中継し、返答を検査する) なし
外部のサービス 生成 AI(Claude) なし
利用者のデータ 質問の文がサーバーと AI を通る PDF はブラウザから出ない
費用 AI の利用料(1問あたり約0.1円) 配信だけ

連載「社内の BI を AI ダッシュボードに」のダッシュボードは、質問を AI に送るためにサーバー側の処理を持っていました。このツールには AI もサーバー側の処理もないので、受け取ったデータを守る仕組みを考える必要が、そもそもありません。受け取らない作りがいちばん確実な守りです。

PDF を読み込むと、ブラウザの中で次の順に処理されます。

  1. PDF.js がページを画像に描く(Part1 の手順)
  2. 別の作業用のスレッド(Worker)で、灰色化・縮小・白の割合の計算をする
  3. 結果の表とページの画像を画面に出す

ページの画像も結果も、ブラウザのメモリにあるだけです。画面を閉じれば消えます。

3. 外への通信を止める仕組みと、確かめ方

3-1. CSP — ブラウザに「外に出るな」と伝える

「外に送る処理を書いていない」だけでは、まだ足りません。将来だれかが部品を足したときに、その部品が外に通信するかもしれないからです。

そこで、サイトの応答に CSP(コンテンツ セキュリティ ポリシー) を付けています。ブラウザに「このページは、同じサイトのファイルしか読み込まない、同じサイトにしか通信しない」と伝える設定で、ブラウザがこれを守ります。外のサーバーへの通信は、ページのプログラムが試みてもブラウザが止めます。

公開版の応答ヘッダー。content-security-policy で、読み込みと通信の行き先を同じサイト(self)に限っている。cache-control の no-transform は6章
公開版(visual-qc.herzleben.co.jp)の応答ヘッダー

解析のタグや広告、外部のフォント、CDN からの部品も使っていません。必要な部品は、すべてサイトに同梱しています。

3-2. 確かめ方 — 通信の記録を見る

実際に外への通信がないかは、ブラウザの開発者ツールで通信の記録を見れば確かめられます。

画面を開く・ダミーの帳票を読み込んで30ページを計算する・CHECK のページの画像を開く、の3段で記録した通信の件数。外への通信はどの段も0件
通信の記録(ダミーの帳票)。外への通信は0件

ブラウザの通信の記録を、3つの段に分けて取りました。

段 通信 行き先
画面を開く 約200件 すべて同じサイトの中(画面のファイル、PDF.js の書体や文字の対応表、ダミーの帳票)
ダミーの帳票を読み込み、30ページを計算する 外へは0件 ブラウザの中だけ(30ページを約0.7秒、CHECK 8ページ)
CHECK のページの画像を開く 外へは0件 ブラウザの中だけ

画面を開いたときの約200件は、書体などの PDF.js のデータを先に読んでおくためです(5章)。PDF を読み込んで計算する間に、外への通信は増えません。

4. 本番の環境でだけ起きた不具合

手元の開発用の環境では、テストがすべて通っていました。ところが、公開用にビルドしたもので同じダミーの帳票を読み込むと、改ページの値がずれました。

原因は書体でした。PDF.js は、PDF に埋め込まれていない書体を描くために、書体のデータファイルを読みに行きます。ビルドしたものでは、そのファイルの置き場所を PDF.js が取り違え、代わりの書体で描いていた のです。代わりの書体は文字の幅や形が違うので、白い画素の数が変わり、白の割合がずれます。画面には何のエラーも出ず、結果の表は普通に出ていました。

同じページを正しい書体と代わりの書体で描いた画像を並べ、白の割合の違いを示した図
書体が変わると、白の割合が変わる(ダミー)

直したあと、本番と同じ形でビルドしたものを、実際のブラウザで動かして確かめる テスト(check:build)を足しました。開発用の環境だけで確かめていると、ビルドのときにだけ起きる違いを見逃すからです。いまは、公開する前に必ずこのテストを通しています。

5. 通信を切っても動くようにする

5-1. 最初の版では動かなかった

PDF をブラウザの中だけで扱うなら、画面を開いたあとに通信を切っても動くはずです。これは「外に送っていない」ことのいちばん分かりやすい証拠にもなります。

ところが、最初に公開した版では、通信を切ると「この PDF は開けませんでした」になりました。PDF.js の Worker や書体のデータを、PDF を開くたびにサイトから読み直していて、それらが「使うたびにサーバーに確かめる」設定で配られていたためです。

5-2. 直し方

  • 画面を開いた時点で、必要なものを先に読んでおく:PDF.js の Worker と計算用の Worker を立ち上げ、書体などのデータ(約2.4MB)とダミーの帳票をメモリに読み込んでおく。PDF.js には、サイトからではなくメモリから渡す
  • 名前が中身で決まるファイルは、長く手元に置いてよいと伝える:ビルドで名前に印が付くファイルと、版番号の付いたフォルダに置いた PDF.js のデータは、中身が変わらないので、ブラウザに長く置いてよい(immutable)と設定した

画面を開いて読み込みが終わってから通信を切れば、ダミーの帳票も自分の PDF も、通信があるときと同じ結果で計算できます。

通信を切ったあとで、ダミーの帳票の30ページを計算した画面。CHECK 8ページと、5ページの画像が出ている
通信を切ったあとでも、同じ結果が出る(ダミー)

この確かめも、本番と同じ形のテスト(check:build)に入れています。

6. 配信元が付け足すもの — 通信エラーの報告と、解析のスクリプト

本番のドメイン(visual-qc.herzleben.co.jp)に切り替えたあと、公開版の応答とブラウザの動きを確かめていて、配信元の Cloudflare が自動で付け足しているもの が2つ見つかりました。どちらもツールが外に送るものではありませんが、「外への通信はない」と言い切るために止めています。

6-1. 通信エラーの報告(Network Error Logging)

Cloudflare は、応答に 通信エラーの報告(Network Error Logging)を頼むヘッダー(nel と report-to)を付けていました。

  • ブラウザがこのサイトとの通信に失敗したとき(つながらない、エラーが返る、など)に、失敗した URL や時刻などを Cloudflare に知らせる仕組み
  • Chrome や Edge が対応している
  • PDF の中身やファイル名は含まれない(PDF は通信に乗らないため)
  • CSP の対象外で、ページの側からは止められない

管理画面でこの設定を切りましたが、10分たってもヘッダーは付いたまま でした。そこで、応答からこの2つのヘッダーを消すルールを herzleben.co.jp のドメインに足し、ヘッダーが消えたことを確かめています。以前にサイトを開いたブラウザには、最長7日間、前の指示が残ることがあります。

6-2. 解析のスクリプトの差し込み(Web Analytics)

もう1つは、本番の形のテスト(check:build)を公開版に向けて回したときに見つかりました。外への通信と、CSP の違反の警告が出ていたのです。

原因は、herzleben.co.jp のドメインで有効にしていた Cloudflare のアクセス解析(Web Analytics)でした。会社のサイトの訪問者を数えるための設定ですが、同じドメインの下にあるこのツールのページにも、解析用のスクリプトを自動で差し込んでいました。CSP が止めるので実際には送られませんが、ブラウザは読み込もうとして、警告を出していました。

  • curl のような道具で応答を取っても差し込みは見えず、ブラウザで開いたときだけ 現れる
  • 直し方:ツールの HTML の応答に no-transform(配信元に中身を書き換えないよう伝える指定)を付けた。会社のサイトの解析はそのまま
本番でだけ起きたことの一覧。書体のずれ(4章)、通信エラーの報告のヘッダー、解析のスクリプトの差し込みと、それぞれの見つけ方と直し方
本番でだけ起きたこと3つと、見つけ方・直し方

7. 検証済みでないツールとしての位置づけ

7-1. 断り書き

ツールの入口と About には、次の断り書きを置いています。

本ツールは検証済みのツールではありません。結果は目視確認の補助として使い、最終的な判断は各社の手順に従って行ってください。PDF はお使いのブラウザの中だけで処理され、外部には送られません。

初めて開いたときは、入口のページで目的・使い方・データの扱いとあわせてこの断り書きを読み、同意してから使い始める形にしています(同意は30日有効)。

入口のページの「ご利用にあたって」と同意の欄
入口のページの「ご利用にあたって」

7-2. なぜ「検証済み」と言わないか

製薬の業務で使うシステムは、多くの会社で、使う目的に合っていることを手順に沿って確かめ、記録を残してから使います(コンピュータ化システムバリデーション、最近はリスクに応じて確かめ方を絞る CSA の考え方も広がっています。解説記事)。

このツールは、Part2 で紹介したとおり、部品・全体・現実の3段で確かめています。ただ、それは 作る側の確かめ で、各社の業務の手順に沿った確かめではありません。使う会社ごとに、どの帳票に、どの段階で、どう使うかが違うからです。

そのため、このツールは目視の確認を置き換えるものではなく、目で見るページと色を絞り込むための補助 としています。社内の手順に組み込むなら、各社の手順に沿って確かめる必要があります。

7-3. 手法の限界も書いておく

About には、手法の限界も書いています。

  • もともと短い表のページにも CHECK が付く
  • 脚注やフッターが測る範囲の下半分にあると、白の割合が下がり、見逃すことがある
  • 色覚のシミュレーションは二色覚を想定した目安で、実際の見え方を保証しない

限界を書いておくのは、印が付かなかったページを「問題なし」と読まれないようにするためです。

8. 例の帳票を同梱しない理由

ツールには、最初からダミーの帳票と例の配色が入っていて、すぐに試せます。一方、Part2 で使った R Consortium の申請パイロットの帳票は、同梱せず、リンクで案内 しています。

  • 申請パイロットの資料は、今後の申請の手本として誰でも見られるように公開されている
  • ただし、帳票の PDF にはライセンスの表示がなく、HerzLeben が複製して配ってよいとは書かれていない
  • 利用者が自分でダウンロードしてツールに読み込むなら、PDF はその人のブラウザの中で処理されるだけで、こちらが配ることにはならない

公開されているものでも、配ってよいとは限りません。ダミーは自前で作り、公開の帳票はリンクで案内する、と分けています。

9. まとめ

  • 申請前の帳票は外に出せない。だから最初から、帳票を受け取らず、計算をすべてブラウザの中で行う形にした
  • サーバーは画面を配るだけで、CSP で外への通信を止めている。通信の記録で、誰でも確かめられる
  • 公開用にビルドしたものでだけ、書体を取り違えて白の割合がずれた。本番と同じ形で確かめるテストを足した
  • 必要なものを画面を開いた時点で先に読むようにし、通信を切っても同じ結果で計算できるようにした
  • 配信元が付け足す通信エラーの報告のヘッダーと、解析のスクリプトの差し込みも止めた。どちらも本番のドメインを実際のブラウザで確かめて見つかった
  • 検証済みでないツールとして、目視の補助に位置づけ、手法の限界とあわせて明記している

データを守るいちばん確実な方法は、データを受け取らないことです。そのうえで、受け取っていないことを、誰でも確かめられる形にしておくことです。

次のステップ

連載はここまでです。全体像と使いどころは Part0 にまとめています。社内の帳票の決まりに合わせたチェックや、社外に出せないデータを扱うツールのご相談は、お問い合わせからお寄せください。

出典

  • Yi Guo. Detecting Abnormal Page Breaks Using Grayscale Pixel-Density Analysis in R Shiny. PharmaSUG 2026, Paper AP-233. https://pharmasug.org/proceedings/2026/AP/PharmaSUG-2026-AP-233.pdf
  • R Consortium R Submission Working Group. submissions-pilot1-to-fda. https://github.com/RConsortium/submissions-pilot1-to-fda
  • Cloudflare. Understanding Network Error Logging. https://developers.cloudflare.com/support/network/understanding-network-error-logging/
  • Cloudflare. Web Analytics FAQ. https://developers.cloudflare.com/web-analytics/faq/
  • 本ページの画面写真は、架空のデータで作成したダミーの帳票を読み込んだものです