ローカル実行の適用範囲と、残る経路
筆者作成の図です。特定サービスの画面ではありません。
AIを使うとき、重要な情報が外に出るかどうかは、モデルの性能では決まりません。決めるのは経路です。同じモデルでも、クラウドに送れば経路上の各地点に本文の写しが残り、端末の中だけで処理すれば、その経路自体が存在しません。
そこで確かめたいのは「送らずに処理する」という選択肢がどこまで通用するかです。実際にローカルで置換処理を走らせ、外部通信が起きないことを確認しました。確かめた範囲は「送らない」側だけで、各サービスの保存期間や学習利用の条件は対象外です。
環境
| 項目 | 内容 |
|---|---|
| OS | Windows 11(社内端末) |
| 言語 | Python 3 |
| 使用ライブラリ | 標準ライブラリのみ(re、socket) |
| 検査対象 | 社内文書を模した文字列(法人名・電話・メール・口座番号) |
| 通信の扱い | ソケットを塞いだ状態で実行 |
| 確認日 | 2026年10月22日 |
re と socket 以外は使いません。外部ライブラリを追加すると、その導入時点で通信が発生しうるためです。
手順1:送らずに足りる作業を分ける
最初に分けるのはファイルではなく、その中の情報です。
要約や集計を頼むとき、モデルが実際に必要とするのは金額・日付・区分といった業務項目です。氏名や連絡先は、たいてい答えの質に影響しません。だとすれば、不要な項目を外した版を手元で作れば済み、送る必要そのものがなくなります。
判断の目安として、三つに分けておきます。
- 端末の外に出さない:個人情報、取引先の連絡先、金額の原票
- 置換してから送る:金額の集計、区分ごとの件数、傾向の要約
- そのまま送ってよい:公開情報、社外公表済みの資料
「送らない」で足りるのは三番目と、二番目の一部です。残りは「置換してから送る」に寄せます。ここで大事なのは、送らない判断をファイル単位でやらないことです。同じファイルでも、必要な列だけを抜けば送れる場合があります。
手順2:置換をローカルで済ませる
置換の規則はこれだけです。
import re
RULES = {
"電話": re.compile(r"(?<![0-9])0[0-9]{1,3}-[0-9]{2,4}-[0-9]{3,4}(?![0-9])"),
"メール": re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}"),
"口座": re.compile(r"(?<![0-9])[0-9]{16,19}(?![0-9])"),
}
文字クラスは [0-9] と [A-Za-z] のようにASCIIで書きます。理由は排錯の節にまとめます。
置き換えた値は対応表として手元に残します。[電話1] だけを送ってしまうと、返ってきた回答を元の値に戻せません。ただし対応表そのものが新しい機密になります。元の文書と同じ扱いにして、ログや共有フォルダに置かないようにします。
もう一点、採番のカウンタは呼び出しをまたいで共有します。文書ごとに1から始めると、別の文書でも [電話1] が使われ、対応表が上書きされて戻せなくなります。
筆者作成の流れ図です。特定サービスの画面ではありません。
手順3:それでも残る経路を確認する
端末の中で処理しても、経路がゼロになるわけではありません。残るのは次の三つです。
クライアントが読む範囲。 コード補助ツールは作業ディレクトリを読みます。初期設定が広いと、依頼していない文書まで対象になります。最小のフォルダに絞ってから使います。実際の項目名はツールごとに違うため、設定項目の確認先のような手順で照合します。
ログと一時ファイル。 置換前の文字列がログに残ることがあります。置換は読み込み直後に行い、元の文字列を標準出力に出さないようにします。
鍵と宛先。 送らない作業だけをしていても、鍵は設定ファイルに残りがちです。環境変数に移したうえで、元の行を消します。移すだけでは消えません。
宛先は、認証なしで2回叩いて確かめます。
curl -i https://api.crazyrouter.com/v1/models
curl -i https://api.crazyrouter.com/v1/chat/completions
2026年10月22日時点で、1つ目は401 Unauthorized、2つ目は405 Method Not Allowedを返しました。404でないことが大事で、404ならパスの綴りを先に疑います。この2つで分かるのは「宛先が存在し、認証を要求していること」だけです。保存期間と学習利用は別の質問として切り分けます。
送る作業が残る場合、宛先を一箇所にまとめておくと切り替えが楽になります。私はCrazyrouterを使っています。ただし中継は経路上の1台で、本文はそのまま読めます。Crazyrouterに限らず、間に中継を挟む場合は同じことが言えます。変わるのは適用される条件であって、本文が短くなるわけではありません。
検証:ローカル処理が通信しないことを確かめる
socket.socket と socket.create_connection を例外に差し替えてから、置換処理を実行します。完走すれば、この経路はネットワークを開いていません。
入力 : 株式会社北斗物流のご担当者様、電話03-1234-5678、メールsato@hokuto.co.jp、金額128000円、口座6222021234567890、ご確認ください。
置換後: 株式会社北斗物流のご担当者様、電話[電話1]、メール[メール1]、金額128000円、口座[口座1]、ご確認ください。
対応表: {'[口座1]': '6222021234567890', '[電話1]': '03-1234-5678', '[メール1]': 'sato@hokuto.co.jp'}
外部通信: 発生せず(ソケットを塞いだ状態で完走)
読み取れることと、読み取れないこと。
読み取れること。 置換処理そのものは外部通信をしません。金額128000は業務項目として残り、電話・メール・口座だけが置き換わりました。
読み取れること。 法人名は残りました。株式会社北斗物流は規則では消せません。法人名まで消したい場合は、列名の一覧やキーワード辞書で別途対応する必要があります。規則で消せるのは「形が決まっているもの」だけです。
読み取れないこと。 端末上の他のアプリが同時に何を送っているか。塞いだのはこの処理の経路だけで、クライアントアプリ自身の通信は別に確認が必要です。
筆者作成の整理図です。特定サービスの画面ではありません。
排錯:実際に詰まった3点
\b が日本語では効かない。 最初に \b0[0-9]{1,3}-[0-9]{2,4}-[0-9]{3,4}\b と書いたところ、1件も置換されませんでした。電話03-1234-5678 の 話 と 0 の間に単語境界がないためです。(?<![0-9]) と (?![0-9]) に変えて解決しました。エラーは出ず、規則が黙るだけです。
\w が漢字を食べる。 メールの規則を [\w.+-]+@[\w-]+\.[\w.]+ と書くと、メールsato@hokuto.co.jp の「メール」まで一致に含まれ、対応表に余分な文字が入ります。ASCIIの文字クラスに変えて解決しました。
対応表のカウンタを共有していない。 呼び出しごとに1から採番すると、別の文書でも [電話1] が使われ、対応表が上書きされます。状態を呼び出しをまたいで持ち回る必要があります。
まとめ:確認できたことと、できていないこと
| 確認したこと | 確認していないこと |
|---|---|
| 置換処理は外部通信をしない(ソケットを塞いで完走) | 端末上の他アプリの送信内容 |
| 電話・メール・口座はローカルで置換できた | 法人名の自動除去 |
| 宛先が存在し、認証を要求している | 保存期間と学習利用の有無 |
表の右側は、ローカル実行では分かりません。各サービスの規約と管理画面で確認する項目です。
「送らずに処理する」は、経路を一つ減らす選択です。ゼロにはなりません。残った経路を把握しておけば、同じ質問が来たときに同じ答えを返せます。
確認日:2026年10月22日。置換処理とソケット遮断は同日に実行しました。保存期間・学習利用の条件はサービスごとに異なるため、実際に利用するサービスの規約で確認してください。


