Claude CodeでPLCのログ解析 — 異常の前兆をCSVから探す手順
装置・PLCのログCSVをClaude Codeにスクリプトで解析させ、閾値超過と徐々に進む変化を拾う手順です。生データの扱いと、結果の検証方法までまとめます。
PLCや装置が吐く秒単位のログCSVは、1日分でも数万〜数十万行になります。これをClaude Codeに丸ごと読ませるのは現実的ではありません。向いているのは、解析用のスクリプトを書かせて手元で走らせ、集計結果だけを読ませるやり方です。
この記事では、振動・温度・圧力を記録した1日分のログを例に、閾値超過の検出、前兆になりうる緩やかな変化の検出、生データの扱い、結果の検証までを順に示します。例のCSVは手順の確認用に作った合成データで、実際の設備のものではありません。
大容量ログは「読ませる」より「スクリプトを書かせる」
Claude CodeのReadツールは、ファイル全体がトークン上限を超えると先頭の1ページだけをPARTIAL viewの通知付きで返します。続きはoffsetとlimitで読み進める仕組みなので、8万6千行のログを最後まで目で追わせる使い方には向きません。
代わりに、Claudeに集計スクリプトを書かせます。実行するのは手元のPythonで、Claudeが受け取るのはスクリプトの標準出力だけです。同じスクリプトは翌日のログにもそのまま使えます。
チャットにCSVを添付して集計する方法はClaude CSV分析の使い方にあります。数千行の単発確認ならそちらが手軽で、本記事は繰り返し回す大容量ログが対象です。
チャットの集計とClaude Codeのスクリプト解析
チャットに添付
ファイルをアップロードして、その場で表やグラフを受け取ります。ファイルの中身はClaudeに渡る前提です。
Claude Codeでスクリプト
手元のファイルに対してスクリプトを走らせます。Claudeに返るのは標準出力に出した分だけです。
生データを外に出さない配置を先に決める
「ログが外に出ない」は、スクリプトを手元で走らせるだけでは成り立ちません。Claude Codeはすべてのユーザープロンプトとモデルの出力をネットワーク越しに送ります。Claudeが読んだスクリプトの出力も、会話に載った時点で同じ経路に乗る前提で扱います。
ログがどこまで届くかは、置き場所ごとに次のとおりです。
- CSVのファイルそのもの: 手元にとどまる。ただしClaudeが
headやReadで開けば、その部分は会話に載る - スクリプトの標準出力: Claudeが読む。集計値や時刻の一覧だけを出す設計にする
- セッションの記録:
~/.claude/projects/に平文で保存され、既定の保持は30日
学習に使われるかどうかはプランで決まり、Team・Enterprise・APIでは商用条件のもとで対象外です。詳しくはClaude Codeのデータは学習に使われるかにまとめています。設備データの扱いは社内規程にも左右されるため、持ち出し可否は先に確認しておくと手戻りが減ります。
手元の設定で「Claudeが生データを開かない」状態にする
生ログを置くディレクトリにReadの拒否ルールを置くと、Claudeのファイルツールからは開けなくなります。ルールは.claude/settings.jsonに書きます。
{
"permissions": {
"deny": ["Read(./raw/**)"]
}
}このルールは、Claudeがcatやheadなどで同じファイルを開く場合や、< fileのようなリダイレクトにも効きます。一方で、ファイルを名指しせずに読むコマンドや、Pythonスクリプトが自分でファイルを開く処理は、権限ルールの対象外です。
つまり権限ルールだけで運用するなら、Claudeのファイルツールはraw/を開けず、Claudeが走らせる解析スクリプトはraw/を読める、という配置になります。ただしスクリプトが生の行をそのままprintすれば、それはClaudeに届きます。出力の制限は設定ではなく、CLAUDE.mdに書く運用で担保します。
## ログ解析の決まり
- raw/ の行をそのまま出力しない。出すのは集計値・時刻・件数だけ
- 1回の出力は50行以内。超える場合は件数と先頭5件にする
- 閾値と基準期間は引数にして、スクリプト内に固定しない
- 解析スクリプトは analysis/ に保存し、毎回同じものを使い回すOSの仕組みでアクセス自体を塞ぎたい場合は、サンドボックスがあります。サンドボックスはmacOS・Linux・WSL2で動き、ネイティブWindowsではコマンドが保護なしで動きます。既定ではオフで、/sandboxで有効にします。既定のネットワークは、許可したドメイン以外に直接出ない構成です。工場のPCがWindowsなら、WSL2の中でClaude Codeを動かす構成が前提になります。設定項目の詳細はsandbox.enabledの解説を参照してください。
サンドボックスを有効にすると、Readの拒否ルールがサンドボックスの設定にも取り込まれ、OSの層でも効きます。するとRead(./raw/**)は、ClaudeがBashで走らせるpython3 analysis/scan.py raw/press01.csvにも及び、スクリプトもraw/を読めなくなります。一方でサンドボックスが囲うのはシェルコマンドだけで、ReadやWebFetchなどの組み込みツールは権限ルールに従います。
構成は2通りです。
- 権限ルールだけで運用する:
raw/はClaudeのファイルツールから閉じ、スクリプトはClaudeに実行させる。サンドボックスは有効にしない - サンドボックスも併用する: スクリプトはClaudeを介さず手元で実行し、Claudeには
report.txtのような集計結果だけを読ませる。後段の「毎日の解析」はこの構成です
以降の例では、手元でraw/を読むスクリプトの出力をClaudeに見せる形で進めます。
閾値超過を出すスクリプトを書かせる
準備として、raw/press01.csvを置きます。列は時刻、温度、振動、圧力、アラームの5列で、1秒1行です。
ts,temp_c,vib_mm_s,pressure_kpa,alarm
2026-09-30 00:00:00,59.90,2.077,499.3,0
2026-09-30 00:00:01,59.87,1.860,499.4,0依頼文は、列の意味と出力の制約を添えます。
raw/press01.csv を解析するPythonスクリプトを analysis/scan.py に書いて。
標準ライブラリだけを使うこと。引数は「ファイル 列名 閾値」。
出すのは行数、欠損数、閾値超過の件数と最初の時刻、1時間ごとの平均だけ。標準ライブラリだけにするのは、手元の環境にpandasが入っていないことが多いためです。Python 3.12.2の素の環境でimport pandasはModuleNotFoundErrorになります。生成されたスクリプトを走らせた出力が次のとおりです。
python3 analysis/scan.py raw/press01.csv vib_mm_s 3.0rows=86400 missing=0
over 3.0: 10377 first=2026-09-30 20:27:50
hourly mean:
18h 2.004
19h 2.091
20h 2.633
21h 3.226
22h 3.833
23h 4.4351日分の8万6千行が、Claudeの手元には十数行で届きます。振動の1時間平均は19時台から上がり始めています。アラームが立つのは23時20分で、閾値3.0を最初に超えたのは20時27分です。このデータでは、アラームの約2時間52分前に超過が始まっていたことになります。
閾値超過だけでは足りない — 基準期間との比較で前兆を拾う
固定の閾値は「超えたら」しか言えません。前兆は、閾値の手前で緩やかに進む変化として現れることが多いので、正常な期間を基準にして外れ具合を見ます。前半12時間を基準期間に取り、平均+標準偏差の5倍を60秒連続で超えた最初の時刻を探すスクリプトの出力です。
baseline mean=2.000 sd=0.150 limit(5sd)=2.749
sustained over limit from 2026-09-30 21:09:42固定の3.0より早い時点で、21時09分に「持続的な超過」と判定されました。温度の列に同じスクリプトを掛けた結果は、持続的な超過なしでした。13時53分に30秒だけ温度が跳ねる区間が合成データに入っていますが、60秒連続の条件を満たさないので拾われません。
この「連続して超えた秒数」の条件が、一過性のノイズと前兆の切り分けになります。依頼文では「何秒続いたら候補とするか」を引数で指定させます。
前兆の見つけ方の使い分け
固定閾値
設備の仕様値や警報値に近い判定です。超えた時刻は明確ですが、気づくのは遅くなります。
基準期間との差
正常な時間帯の平均と標準偏差から外れ具合を見ます。基準期間の選び方で結果が変わります。
持続時間の条件
短時間のスパイクを除き、続く変化だけを残します。秒数は設備の応答に合わせて決めます。
結果をそのまま信じない — 検証の手順
スクリプトが出した時刻は、Claudeが書いたコードの結果です。次の順で確かめます。
- 答えが分かっているデータで走らせる: 振動を徐々に上げた区間や、欠損を10行入れた区間を自分で仕込み、拾えるかを見る。今回の例は、この方法で作った合成データです
- 別の方法で数え直す: 同じ件数をシェルで数える。温度が70を超えた行は、
awkで数えても30行(13時53分20秒〜49秒)で、スクリプトの数と一致しました - 該当時刻の前後を人が見る: 検出時刻の前後数十行を取り出し、機器の動作記録と並べて読む
- 前提を点検する: 単位、時刻のタイムゾーン、サンプリング間隔、欠損の扱いを確認する。欠損は
missingとして別に数えさせる
awk -F, 'NR>1 && $2>70 {c++; if(!s)s=$1; e=$1} END{print c, s, e}' \
raw/press01.csv30 2026-09-30 13:53:20 2026-09-30 13:53:49スクリプトの結果は「異常の可能性がある時刻」であって、故障の予測ではありません。保全の判断は、設備を知る担当者が機器の履歴と合わせて行う前提で使います。
毎日の解析をコマンド1本にする
スクリプトが固まったら、Claudeを介さずに定期実行できます。集計だけを回し、気になる出力があるときだけClaudeに渡す構成です。標準入力に流し込む場合、パイプで渡せる入力は10MBまでで、超えるとエラー終了します。大きいファイルはパスを指定して読ませます。
python3 analysis/trend.py raw/press01.csv vib_mm_s > report.txt
claude -p "report.txt を読み、前日との違いを5行で要約して"Claude Code自体も、スケジュール実行の仕組みを持っています。クラウドで動くRoutinesと、手元のマシンで動くデスクトップのスケジュールタスクがあります。生ログを手元から出さない方針なら、手元で動く側を選びます。
つまずきやすい点
- 日付と時刻が別の列に分かれている: 依頼文で結合の指示を出す。タイムゾーンの記載がなければ、どの時刻系かを先に確認する
- 全角の列名や単位付きの列名: 文字コードがShift_JISのことがある。
encodingを指定させる - 稼働停止中の行が含まれている: 停止中は値が0に近く、基準期間の平均を下げる。稼働中の行だけで基準を作る
- 複数の装置を一度に: 装置ごとに基準を分ける。混ぜると、個体差が異常に見える
まとめ
大容量のログは、Claudeに読ませずスクリプトを書かせます。生データを開かせない設定と、出力を集計値に絞る決まりは別物なので、両方を置きます。前兆は固定閾値だけでは遅れるため、基準期間との差と持続時間の条件を組み合わせると早く拾えます。ただし出てきた時刻は検証用データと別経路の集計で確かめてから使います。