Claude Code で作った小さな自動運用(予約投稿・商品の集計・夜間のログ記録)を、Windows のタスクスケジューラで動かしています。
無人で動かしていた夜中のジョブが、UnicodeEncodeError で止まったことがあります。手で実行したときは毎回正常に動いていたスクリプトです。
原因は、コンソールの文字コード(cp932)が、ログに含まれる文字を出力できなかったことでした。手で動かしたときと、スケジューラが動かしたときで、実行環境が同じではなかったわけです。
この記事は、その後「無人で動かす前に必ず見る」ようにした項目のまとめです。AIエージェントに書かせたスクリプトは、作者(=AI)も私も手元の端末でしか動かしていないことが多いので、特に効きます。
この記事の続き(動くコード付き)は、本『AIエージェントに無人で書き込ませる前の実装ガイド ― 二重送信・暴走・夜間停止を防ぐPython』の第8章「夜間実行の落とし穴:手元では動くのに、スケジューラだと落ちる」に書きました。第1〜2章は無料で読めます。
無料・登録不要:この記事の内容を含む「AIエージェント 本番前チェックリスト」(25項目・1ページPDF・日本語)を、そのままダウンロードできます。
https://github.com/neuralalpha-ai/downloads/blob/main/agent-ops-preflight-checklist-ja-v1.0.pdf
1. 標準出力の文字コードを、コードの側で決める
Windows で Python を動かすと、標準出力の文字コードは実行のされ方によって変わることがあります。手元のターミナルでは表示できた文字が、スケジューラ経由やリダイレクト時には cp932 で書こうとして落ちる、ということが起きます(絵文字や、cp932 にない記号が典型です)。
対策は2つのどちらか(両方でも可)です。
import sys
# スクリプトの先頭で、出力を UTF-8 に固定する(Python 3.7+)
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
もしくは、タスクの実行環境に PYTHONUTF8=1 を設定して、Python を UTF-8 モードで動かします。
errors="replace" にしておくと、万一表示できない文字があってもジョブ自体は止まりません。ログの1文字のせいで本処理が止まるのが一番もったいないので、ここは寛容にしています。
2. ファイルの読み書きは encoding を必ず書く
open(path) のように encoding を省略すると、Windows では環境のデフォルト(多くは cp932)が使われます。手元とスケジューラで同じでも、別のPCに移した瞬間に壊れます。
open(path, encoding="utf-8") # 自分が書くファイル
open(".env", encoding="utf-8-sig") # メモ帳で保存された BOM 付きにも耐える
.env のような「人がメモ帳で編集するファイル」は、BOM 付き UTF-8 で保存されることがあるので utf-8-sig で読んでいます。先頭のキー名に見えない BOM が付いて「キーが見つからない」になるのを防げます。
3. 「本番と同じ条件」で一度動かす
手で python job.py を実行して動いた、はテストになりません。違いがあるのは主にここです。
- 実行ユーザー(ログオンしていなくても実行、にすると別の環境になる)
- 作業フォルダ(相対パスで読んでいるファイルが見つからない)
-
環境変数(
PYTHONUTF8や API キーの読み込み方) - 使われる Python(PATH 上の別バージョン)
タスクスケジューラの「操作」で、Python の実行ファイルとスクリプトをフルパスで指定し、「開始(作業フォルダー)」も明示します。そのうえで、タスクを右クリック →「実行する」で一度動かし、ログを確認します。夜まで待たなくても、本番と同じ経路で動かせます。
4. 失敗は「ログファイル」ではなく「人」に届ける
ログファイルに書くだけでは、誰も読まなければ失敗していないのと同じです。対策として次の2つが有効です(私は2つ目を、毎朝の要約として運用しています)。
- ジョブが成功したら「最後に成功した時刻」を記録し、一定時間(例:26時間)更新がなければ別の仕組みが知らせる(死活監視)
- 毎朝、前日の結果の要約を人が読む場所に出す
「落ちたら通知」だと、通知処理ごと落ちたときに何も来ません。「成功が途絶えたら通知」のほうが、無人運用では安全です。
5. 書き込みをするジョブは、落ちた後の再実行に注意
夜中に落ちたジョブを朝に手で再実行するとき、落ちる前にどこまで書き込みが終わっていたかを確認しないと、投稿や送信が二重になります。
私は書き込みの前に「試行(attempt)」を記録し、結果が分からないもの(タイムアウトなど)があるうちは次を送らない、という記録帳を使っています。仕組みは前回の記事に書きました。
まとめ
- 出力の文字コードはコードで固定(
reconfigureかPYTHONUTF8=1) - ファイルは
encodingを必ず指定(人が編集するファイルはutf-8-sig) - 本番と同じユーザー・作業フォルダ・Python で一度動かす
- 失敗ではなく「成功が途絶えたこと」を人に届ける
- 再実行の前に、どこまで書き込んだかを確かめる
続きを読む
この記事の内容は、本『AIエージェントに無人で書き込ませる前の実装ガイド ― 二重送信・暴走・夜間停止を防ぐPython』の第8章「夜間実行の落とし穴:手元では動くのに、スケジューラだと落ちる」で、動くコード付きで解説しています。
本の第1章・第2章は無料で読めます。まず無料の章から合うか確かめてください。
Kindle 版(Amazon)もあります。端末で読みたい方はこちらからどうぞ。
AIエージェントに勝手に書き込みをさせる前のチェック項目を、25項目の1ページ(日本語・PDF)にまとめて無料で配布しています。今回の内容は「7. 実行環境」の項目です。
https://neuralalpha.gumroad.com/l/agent-ops-preflight-ja?utm_source=qiita&utm_medium=article
二重送信を防ぐ記録帳(Python標準ライブラリのみ・1ファイル)はこちらで公開しています。
https://github.com/neuralalpha-ai/agent-write-ledger
手元の仕組みをまず15分で点検したい方には、無料の本も用意しました(実際に起きた5つの失敗と、それぞれ3分で確かめる手順)。
https://zenn.dev/neuralalpha/books/agent-ops-free-checks