
シリーズ: Claudeで変わる仕事術 上級・第5弾 / 前回:上級・第4弾 毎回ゼロからやらない。Loop Engineeringで"回り続ける仕事"を作る
前回、CBPのPDF確認はLoopになった。
前回のPDFを覚える。 今回のPDFと比べる。 同じならスキップする。 新しければ、指定商品を探す。
最後に、Claudeが結果をまとめてログに残す。
~/cbp-check/run_check.sh SUGAR | claude -p "上記のJSONをもとに..." \
--output-format text >> ~/cbp-check/commodity-loop.log
ここまで第4弾で作った。
でも、仕事で使おうとすると、次の疑問が出てくる。
「このログ、後から本当に確認できるか?」
今回のテーマはそこだ。
Claudeが残した記録を、業務で使える証跡にする。

1. ログはあるのに証跡がない
第4弾で作ったログを開く。
tail -n 30 ~/cbp-check/commodity-loop.log
中身はこんな形だ。
確認日: 2026-06-30T22:41:41
status: unchanged_pdf
PDFタイトル: Quota Status Report: June 29 2026
PDF URL: https://www.cbp.gov/sites/default/files/2026-06/commodity_status_report_weekly_june_29.pdf
商品名: SUGAR
該当件数: 67
代表的な該当箇所: 前回と同じPDFなので抽出をスキップした
注意点: なし
読める。URLも残っている。
でも、ここから先に戻れない。
「67件」はどのページにあったのか。 Claudeが読み取れなかった部分はなかったのか。 この数字、本当に信頼していいのか。
ログには書いていない。
なぜか。
第4弾のプロンプトが、それを頼んでいないからだ。
証跡に必要なのは、Claudeに「どこまで返すか」を明確に指定することだ。
これが、プロンプトによる証跡設計だ。
2. 証跡はプロンプトで決まる
Claudeは、聞き方次第で根拠ごと返せる。
第4弾のプロンプトと、改善したプロンプトを並べる。
第4弾のプロンプト(現在)
上記のJSONをもとに、次の項目を必ず含めて日本語で短くまとめて。
確認日、status、PDFタイトル、PDF URL、商品名、該当件数、
代表的な該当箇所、注意点。
不明な点は推測せず「不明」と書いて。
URLは残っている。でも2つが抜けている。
「代表的な該当箇所」は曖昧だ。何ページにあったかが分からない。 「不明な点は不明と書いて」と言っているが、何が不明になりえるかを指定していない。
Claudeは、聞かれたことだけ返す。
指定していない項目は、省略されるか、まとめて処理される。
改善したプロンプト
上記のJSONをもとに、次の固定フォーマットで日本語で返して。
確認日: (checked_atの値)
status: (updated_pdf / unchanged_pdf / needs_human のいずれか)
PDFタイトル: (pdf_titleの値)
PDF URL: (pdf_urlの値。必ず実際のURLを返す。省略しない)
商品名: (productの値)
該当件数: (match_countの値)
該当ページ: (matchesの中にあるpageの一覧。なければ「なし」)
代表的な行: (matches先頭3件のlineを番号つきで。unchanged_pdfの場合は「前回と同じPDFのため抽出なし」)
数字についての注意: PDFから取った数字は抽出上の誤差がありえる。最終確認は原本PDFを直接開くこと
不明点: (JSONに含まれる情報だけで判断できなかった項目。なければ「なし」)
変わったのは3点だ。
| 項目 | 第4弾 | 改善後 |
|---|---|---|
| ページ番号 | なし | 「該当ページ」として明示 |
| 実際の行 | 「代表的な該当箇所」(曖昧) | matches の行をそのまま返す |
| 不明点の定義 | 「不明な点は不明と書いて」(曖昧) | 「JSONに含まれる情報だけで判断できなかった項目」と限定 |
Claudeに「うまくまとめて」と頼まない。残す項目を固定で指定する。
これが証跡設計の基本だ。

3. ハンズオン:プロンプトを改善して証跡を作る
ステップ1:今のコマンドを確認する
まず、第4弾で作ったコマンドを確認する。
claude -p "$(~/cbp-check/run_check.sh SUGAR)
上記のJSONをもとに、次の項目を必ず含めて日本語で短くまとめて。確認日、status、PDFタイトル、PDF URL、商品名、該当件数、代表的な該当箇所、注意点。不明な点は推測せず「不明」と書いて。statusが unchanged_pdf の場合は、前回と同じPDFなので抽出をスキップした、と書いて。ただしPDF URLは必ず実際のURLを残して。" \
--output-format text
これが第4弾の到達点だ。
ステップ2:改善したプロンプトで実行する
プロンプトを変えて実行する。
claude -p "$(~/cbp-check/run_check.sh SUGAR)
上記のJSONをもとに、次の固定フォーマットで日本語で返して。
確認日: (checked_atの値)
status: (updated_pdf / unchanged_pdf / needs_human のいずれか)
PDFタイトル: (pdf_titleの値)
PDF URL: (pdf_urlの値。必ず実際のURLを返す。省略しない)
商品名: (productの値)
該当件数: (match_countの値)
該当ページ: (matchesの中にあるpageの一覧。なければ「なし」)
代表的な行: (matches先頭3件のlineを番号つきで。unchanged_pdfの場合は「前回と同じPDFのため抽出なし」)
数字についての注意: PDFから取った数字は抽出上の誤差がありえる。最終確認は原本PDFを直接開くこと
不明点: (JSONに含まれる情報だけで判断できなかった項目。なければ「なし」)" \
--output-format text
status が unchanged_pdf の場合、matches が空なので「該当ページ: なし」「代表的な行: 前回と同じPDFのため抽出なし」になる。これは正しい動作だ。

改善プロンプトの効果が見えるのは status: updated_pdf の時だ。新しいPDFが出た時に、ページ番号と実際の行が返ってくる。
補足:強制的に
updated_pdfで試したい場合last_checked.jsonを削除すると「初回実行」扱いになり、PDFを最初から読み直します。rm ~/cbp-check/last_checked.jsonを実行してからコマンドを再実行すると、matchesに中身が入った状態で確認できます。
プロンプトを変えただけで、返ってくる情報の質が変わる。
ステップ3:--output-format json に変える
次は、出力形式を変える。
第4弾では --output-format text を使っていた。
テキストは人間が読みやすい。
でも、後から「この1週間のエラーだけ見たい」「今月のコストを合計したい」となった時に扱いにくい。
--output-format json に変えると、Claudeの返答全体がJSON形式で出てくる。
claude -p "$(~/cbp-check/run_check.sh SUGAR)
上記のJSONをもとに..." --output-format json
出力は1行の長いJSONで返ってくる。
ファイルに保存したJSONLを読み返すときは、result フィールドだけ取り出すとそのまま読める。
tail -n 1 ~/cbp-check/commodity-loop.jsonl | python3 -c "
import json, sys
d = json.loads(sys.stdin.read())
print(d.get('result', ''))
"
JSON全体を整形して見たい場合は ensure_ascii=False を使う。日本語がそのまま表示される。
tail -n 1 ~/cbp-check/commodity-loop.jsonl | python3 -c "
import json, sys
d = json.loads(sys.stdin.read())
print(json.dumps(d, ensure_ascii=False, indent=2))
"
{
"type": "result",
"subtype": "success",
"is_error": false,
"result": "確認日: 2026-07-05\nstatus: updated_pdf\n...",
"total_cost_usd": 0.126,
"duration_ms": 6715,
"num_turns": 1,
"session_id": "..."
}
補足:raw JSONのまま開くと日本語が
確認日のように見える。 これはJSONの仕様で、json.loads()で読めば正しい日本語に変換されます。ensure_ascii=FalseはPythonが出力時に再エスケープするのを防ぐオプションです。
主要なフィールドはこの4つだ。
| フィールド | 内容 |
|---|---|
result | Claudeの返答テキスト(\n 区切りで各項目が入る) |
total_cost_usd | このAPI呼び出しのコスト |
duration_ms | 処理時間(ミリ秒) |
is_error | エラーかどうか(false が正常) |
毎日動かすLoopでは、コストが積み上がる。total_cost_usd が記録に残ると、後から振り返りやすい。

ステップ4:ラッパースクリプトを作ってファイルに追記する
cron で毎日動かすには、コマンドをスクリプトにまとめておく。
nano ~/cbp-check/run_claude_check.sh
開いたら、次の内容を貼り付ける。
#!/bin/bash
exec 2>> ~/cbp-check/commodity-loop-errors.log
cbp_result=$(~/cbp-check/run_check.sh SUGAR)
claude -p "$cbp_result
上記のJSONをもとに、次の固定フォーマットで日本語で返して。
確認日: (checked_atの値)
status: (updated_pdf / unchanged_pdf / needs_human のいずれか)
PDFタイトル: (pdf_titleの値)
PDF URL: (pdf_urlの値。必ず実際のURLを返す。省略しない)
商品名: (productの値)
該当件数: (match_countの値)
該当ページ: (matchesの中にあるpageの一覧。なければ「なし」)
代表的な行: (matches先頭3件のlineを番号つきで。unchanged_pdfの場合は「前回と同じPDFのため抽出なし」)
数字についての注意: PDFから取った数字は抽出上の誤差がありえる。最終確認は原本PDFを直接開くこと
不明点: (JSONに含まれる情報だけで判断できなかった項目。なければ「なし」)" \
--output-format json \
>> ~/cbp-check/commodity-loop.jsonl
exec 2>> ... をスクリプト先頭に書くと、スクリプト全体(run_check.sh の失敗・bashのエラー含む)のエラーがすべてそのファイルに向く。
保存する。nano なら Ctrl + O、Enter、Ctrl + X だ。
実行権限を付ける。
chmod +x ~/cbp-check/run_claude_check.sh
手動で実行して確認する。
~/cbp-check/run_claude_check.sh
ログを確認する。
tail -n 2 ~/cbp-check/commodity-loop.jsonl

JSONLは、1行に1つのJSONが入る形式だ。毎回の実行結果が1行ずつ積み上がる。
ステップ5:cronのコマンドを差し替える
第4弾で設定したcronを更新する。
crontab -e
第4弾のcron行を削除して、ステップ4で作ったスクリプトに置き換える。
0 8 * * 1-5 ~/cbp-check/run_claude_check.sh
出力先もエラー処理もスクリプト内に書いてある。cron行はスクリプトを呼ぶだけでいい。
Pythonのコードは変えていない。 cronの起動時間も変えていない。
変わったのは、プロンプトと出力形式だけだ。
4. 証跡として使う3つの方法
このJSONLログは、後から3つの用途で使える。
1. 読み返す
result フィールドだけ取り出して読む。
tail -n 3 ~/cbp-check/commodity-loop.jsonl | python3 -c "
import json, sys
for line in sys.stdin:
d = json.loads(line)
print(d.get('result', ''))
print('---')
"
2. 原本に戻る
result にPDF URLが含まれている。
そのURLを開けば、Claudeが見たPDFそのものを確認できる。
Claudeが「67件」と言ったなら、そのPDFの「該当ページ」を自分で開けばいい。
「AIがそう言ったから」ではなくなる。「このPDFの、このページを見た」と言えるようになる。
3. コストを確認する
毎日動かすLoopのコストを確認する。
grep -o '"total_cost_usd":[0-9.]*' ~/cbp-check/commodity-loop.jsonl
積み上がった費用を把握できる。
5. 業務で使う時の注意点
数字を断定させない
PDFから取った数字は、レイアウトによっては列がずれることがある。
プロンプトに「数字についての注意」を固定で入れているのはそのためだ。
Claudeに「残量は○○です」と言い切らせない。
「PDFの該当行にはこの数値がある。最終確認は原本PDFを見ること」と返させる。
不明点を消さない
JSONに情報がない時、Claudeが推測して埋めることがある。
それを防ぐために「JSONに含まれる情報だけで判断できなかった項目」と指定している。
情報がなければ「不明」と返す。推測で埋めない。
業務では、不明点が正直に残る方が信頼できる。
ログに機密情報を入れない
今回は公開PDFなので問題ない。
ただし、社内資料に同じことをするなら、ログに入れてはいけない情報がある。
- パスワード・APIキー
- 顧客の個人情報
- 社外秘の数字
ログは残る。残るからこそ、入れる情報を選ぶ。
6. 今回のまとめ
第4弾では、LoopがClaudeの結果をテキストで残した。
第5弾では、そのログを証跡に変えた。
変えたのは2つだけだ。
- プロンプト:「該当ページ」「代表的な行」「不明点の定義」を明示した
- 出力形式:
--output-format text→--output-format json(JSONL)
Pythonのコードは変えていない。
証跡の質はPythonで決まらない。Claudeに何を返させるかで決まる。
ここが、第5弾で渡したかったことだ。
次回は、ここまで作った仕組みを、会社員向けの毎朝確認業務として束ねる。
ニュース・参考URL・CBPのPDF確認・朝レポート。
Loopと証跡を持った仕組みが、毎朝の業務フローになっていく。