2026/11/04
AI活用
CodexでCSV取込バッチを冪等化する――二重登録を防ぐ設計・実装・再実行テスト

対象読者:上級者向け
この記事の要点
Codexを使い、Python製CSV取込バッチへ一意制約とトランザクション境界を組み込み、再実行時の二重登録を防ぐ進め方を紹介します。読み取り調査、失敗テスト、最小実装、DB整合性確認を分離し、AIに補助させる工程と人が承認すべき業務判断を対応付けています。
目次

月末のCSV再取込で起きる二重登録
月末の受注処理を担当する業務システムチームを想定します。営業部門から受け取ったCSVをPython製のバッチでデータベースへ取り込み、その結果を基幹システムへ渡す構成です。通常は問題なく処理できますが、通信断や入力不備によって途中で異常終了すると、担当者は原因を修正して同じファイルを再実行します。このとき、最初の実行で一部の行だけが保存されていると、再実行によって同じ受注が二重登録されかねません。
AIを利用しない従来の保守でも、担当者はログ、取込コード、テーブル定義、過去の障害記録を調べ、既存のテストを実行して安全に改修します。これは必要な工程です。ただし、関連ファイルの探索、現在のトランザクション境界の確認、失敗条件の洗い出し、テストデータの作成には、似た内容の読み直しや転記が繰り返し発生します。仕様がコードと運用手順書に分散していれば、調査結果を設計メモへまとめ直す作業も必要でしょう。
今回の中心課題は、単に重複を見つけて削除することではありません。同じ入力を何度実行しても、確定後のデータが一度だけ取り込まれた状態になる「冪等性」を、データベース制約とトランザクションの両方で支えることです。架空の例では、CSVに外部システムが発行した変更されない受注識別子 `source_record_id` が含まれ、PythonとSQLiteを使用しているものとします。これは実装案を説明するための例であり、実在する環境で検証した結果ではありません。
Codexには、リポジトリの探索、変更計画の整理、既存形式に沿ったテスト案の作成、限定されたコード変更の補助を任せます。ただし、一意キーとして何を採用するか、ファイル全体を一つのトランザクションに含めてよいか、重複行を無視するかエラーにするかは業務判断です。AIがコードを書けることと、業務上正しい更新単位を決められることは別問題です。この記事では、その境界を保ったまま調査から検証までを組み立てます。

コード修正より先に分解する五つの手間
二重登録の改修が長引く原因は、重複チェックの一行が難しいからとは限りません。調査、設計、実装、テスト、承認にまたがる判断が、頭の中や一時的なメモに散らばりやすいためです。担当者は、CSVを読む場所、INSERTを実行する場所、コミットする場所、例外を捕捉する場所を探し、それぞれの関係を追跡します。行ごとにコミットしている場合、途中失敗までの行だけが残ります。ファイル単位でロールバックしていても、コミット成功後に完了通知だけが失敗すれば、再実行への備えとして一意制約が必要です。
作業を工程に分けると、Codexへ依頼する範囲と人が判断する範囲を切り分けやすくなります。
| 工程 | 従来の主な作業 | 見落とした場合の問題 |
| --- | --- | --- |
| 資料探し | 取込処理、DB定義、テスト、運用手順を横断して読む | 実際の更新経路と異なる場所を直す |
| 識別子の決定 | CSV列と業務上の受注単位を照合する | 別の受注を同一扱いにする |
| 更新境界の設計 | コミットと例外処理の位置を確認する | 途中失敗で一部の行だけが残る |
| 改修とテスト | 制約、INSERT処理、失敗テストを追加する | 通常系だけ通り再実行で壊れる |
| 整合性確認 | 件数、重複、既存データへの影響を調べる | テスト成功だけで業務上の誤りを見逃す |
一意制約とトランザクションは、役割が異なります。一意制約は、同じ `source_record_id` を持つ行が複数保存されることをデータベース側で防ぎます。トランザクションは、対象ファイルの変更をまとめて確定または取り消すための境界です。どちらか一方だけでは、すべての失敗条件を扱えません。
この例では「入力全体を事前検証し、一ファイルを一トランザクションで登録し、既登録キーは追加しない」という受け入れ条件を置きます。ただし、大容量ファイルではロック時間や再処理量との兼ね合いが生じます。チャンク単位の確定が必要なら、そのチャンク識別子、再開位置、部分成功を利用者へどう示すかまで別途設計しなければなりません。Codexに修正を依頼する前に、この境界を人が明文化することが手戻りを抑える要点です。

Codexがまとめる工程と人に残す判断
Codexを使う狙いは、設計判断を丸ごと委ねることではなく、分散した根拠を一つの変更案へ集約することです。OpenAIが2026年8月3日に公開した公式Cookbookでは、Codexが認識する任意のリポジトリ指針として `AGENTS.md` が紹介され、プロジェクトの目的、コーディング規約、テスト基準、境界などを記録する例が示されています。また、小さな実装フェーズに分け、人が確認できる承認点と検証記録を残す進め方も提案されています。これらはCodexに必須のファイル構成ではなく、公式記事でも任意のワークフロー慣例として説明されています(出典: OpenAI Developers)。
この考え方をCSV取込バッチへ当てはめると、同じ入力、成果物、品質条件を維持した比較ができます。入力は対象リポジトリ、匿名化したCSV、現行テーブル定義、障害ログです。成果物は改修コード、回帰テスト、検証記録であり、品質条件は二重登録がないこと、途中失敗時に定めた境界でロールバックされること、既存の正常取込を壊さないことです。
| 従来の工程 | Codexに補助させる内容 | 人が引き続き担う内容 |
| --- | --- | --- |
| 関連コードの探索 | 読み取り専用で候補ファイルと呼出経路を整理する | 調査範囲と説明の妥当性を確認する |
| 失敗条件の列挙 | 分岐と例外経路からテスト候補を作る | 業務上必要な失敗条件を選ぶ |
| 変更設計 | 一意制約と更新境界を含む差分案を提示する | キーの意味、ロック、互換性を承認する |
| 実装とテスト | 既存規約に沿う最小差分とテストを作る | 差分をレビューして実行結果を確認する |
| 証跡の整理 | 実行した検証と未確認事項を要約する | 本番適用、復旧方法、残存リスクを判断する |
特に有効なのは、読み取り専用の調査と書き込みを伴う実装を分離することです。最初の依頼では変更させず、関係するファイル、現在のトランザクション境界、既存テスト、未確認事項だけを報告させます。その報告を人が確認してから、対象ファイルと受け入れ条件を限定して実装へ進みます。調査結果が誤っていても、コード変更前なら修正しやすいためです。
公式Cookbookは、計画、承認、観測したテスト結果、停止条件を明確にする例も掲載しています(出典: OpenAI Developers)。本記事では、その考え方を一件のバッチ改修へ小さく適用します。Codexの回答は証拠そのものではありません。実際の差分、テスト出力、データベースの照会結果を、人が別々に確かめる必要があります。

実装前に固定する入力と受け入れ条件
Codexへリポジトリを渡す前に、再現可能な材料を用意します。必要なのは、対象ブランチまたは作業用の隔離環境、テスト用データベース、個人情報や社外秘を除いたCSV、現行のテーブル定義、実際に使用しているテストコマンドです。コマンド名はプロジェクトごとに異なるため、AIに推測させず、README、CI設定、既存の開発手順から確認します。本番の認証情報や実データを入力する必要はありません。
架空のCSVには `source_record_id`、`customer_code`、`amount_yen` の三列があるものとします。`source_record_id` は発行後に変化せず、一つの受注を一意に表すという業務上の合意が必要です。表示用の受注番号が後から再利用される、訂正時に別レコードとして扱う、複数行で一受注を構成するといった事情があるなら、その列を安易に一意キーにはできません。複数列の組み合わせや別の冪等性キーが必要かどうかを、データ所有者と確認します。
受け入れ条件は、コードの形ではなく観測できる結果として記述します。提案する最小条件は次のとおりです。
- 未登録のファイルを実行すると、有効な各 `source_record_id` が一件ずつ保存されます。
- 同じファイルを再実行しても、保存済み行は増えません。
- 同一ファイル内に同じキーが複数あっても、確定後の行は一件だけです。
- 入力検証でエラーになった場合、当該ファイルの行は一件も追加されません。
- DB更新中に例外が発生した場合、今回のトランザクションは確定されません。
- コミット後に完了通知が失敗して再実行されても、二重登録されません。
- 既存の正常系テストと、関係する回帰テストが引き続き成功します。
`AGENTS.md` を利用する場合は、既存内容を尊重し、Pythonの規約、テスト方法、変更してよい範囲、生成物を置かない場所など、継続して必要な指針だけを記載します。公式資料上、`AGENTS.md` は任意です。今回だけの障害条件や仮の設計判断は、恒久的な指針へ混ぜず、作業用の依頼文や計画に置くほうが管理しやすいでしょう(出典: OpenAI Developers)。
ここまでの準備が終わったら、変更前のテスト結果とテストDBの初期件数を保存します。比較対象がなければ、改修後に「以前から失敗していたテスト」と「今回壊したテスト」を区別できません。準備、確認、修正も効率比較の作業時間に含めるため、開始時点から工程別に記録します。

読み取り調査から最小実装へ進む五段階
実作業は、調査、計画、失敗テスト、最小実装、検証の五段階に分けます。一度の依頼で全ファイルを変更させるのではなく、段階ごとに出力と停止条件を定めます。公式Cookbookでも、小さなフェーズは可読性を高め、人がコード品質などを確認する承認点を設けやすいと説明されています(出典: OpenAI Developers)。
最初は変更を許可せず、現在の動作を調べてもらいます。次の文章は、この架空例に合わせた提案です。実際には角括弧内をリポジトリ固有の内容へ置き換えます。
コピーして使うCodexへの依頼文です。
このリポジトリのCSV受注取込バッチについて、読み取り専用の調査をしてください。
まだファイルを変更しないでください。
対象は、CSV解析、入力検証、DBへのINSERT、トランザクション開始・確定・ロールバック、
既存テスト、関連するテーブル定義です。
次を報告してください。
1. 関係するファイルと呼出経路
2. 現在の一意性保証とトランザクション境界
3. 途中失敗後の再実行で二重登録が起こり得る経路
4. 既存のテスト方法と不足している失敗条件
5. 判断に必要だがリポジトリから確認できない事項
推測は事実と分けてください。変更案は概要だけにとどめ、承認を待ってください。
調査結果では、実際のファイルパス、関数名、テーブル名を手元で照合します。Codexが「一意制約がない」と述べたら、マイグレーションだけでなく、本番相当のスキーマ定義や初期化処理も確認します。調査が正しければ、受け入れ条件と変更範囲を付けて計画を依頼します。この時点でも、キーの意味や一ファイル一トランザクションという設計は人が承認します。
実装段階で使う依頼文の例です。
承認済みの範囲で、CSV受注取込を再実行可能にしてください。
受け入れ条件:
- source_record_id は業務上変更されない一意キーです。
- DB制約でも source_record_id の重複を防いでください。
- 全行の入力検証後、一ファイルを一つのトランザクションで更新してください。
- 保存済みキーとファイル内重複は追加せず、結果から確認できるようにしてください。
- 更新中の例外では今回分をロールバックしてください。
進め方:
1. 既存形式に沿って、再実行、ファイル内重複、途中例外の失敗テストを追加してください。
2. テストが意図した理由で失敗することを確認し、結果を報告してください。
3. 最小限の実装を行ってください。
4. 対象テストと既存の関連テストを実行してください。
5. 変更ファイル、観測した結果、未確認事項を報告して停止してください。
依頼範囲外のリファクタリング、依存関係の更新、コミット、プッシュ、デプロイは行わないでください。
実装の核は、データベース制約とトランザクション境界です。次のSQLite例は提案用の最小イメージであり、そのまま本番へ適用するコードではありません。既存データに重複があれば制約追加に失敗するため、移行前の調査と解消方針が別途必要です。
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
source_record_id TEXT NOT NULL UNIQUE,
customer_code TEXT NOT NULL,
amount_yen INTEGER NOT NULL
);
Python側では、全行を検証してからトランザクションへ入り、競合時に追加しない形が候補になります。
def import_rows(conn, rows):
validated_rows = [validate_row(row) for row in rows]
with conn:
conn.executemany(
'''
INSERT INTO orders (
source_record_id,
customer_code,
amount_yen
) VALUES (
:source_record_id,
:customer_code,
:amount_yen
)
ON CONFLICT(source_record_id) DO NOTHING
''',
validated_rows,
)
`with conn:` の確定・ロールバック動作や `ON CONFLICT` の構文は、使用するドライバーとDB製品に合わせて確認します。PostgreSQLやMySQLなどへ機械的に置き換えてはいけません。また、重複を黙って無視してよいか、取込結果へ件数やキーをどう記録するかも運用要件に含まれます。Codexには既存の接続管理、ログ形式、マイグレーション方式を読ませ、それに沿う差分だけを作らせます。

再実行テストと人による整合性確認
実装後の確認では、テストが成功したという要約だけを受け取らず、どの入力を使い、何件が保存され、どの例外点でロールバックされたかを見ます。テストDBを毎回同じ初期状態へ戻し、正常入力、同一ファイルの再実行、ファイル内重複、更新途中の強制例外を個別に実行します。途中失敗は、DB呼出しを差し替えて指定回数目に例外を発生させるなど、既存テスト基盤に合った方法で再現します。
確認順序も固定しておくと、AIの生成したテストが本当に要件を測っているか判断しやすくなります。正常取込後に期待件数を確認し、同じファイルをもう一度実行して件数が変わらないことを調べます。続いて、同じ `source_record_id` を二行含むファイルを入力し、一件だけ保存されることと、重複が結果へ適切に表れることを確認します。強制例外のケースでは、例外発生前に実行されたINSERTも確定されていないことを照会します。
DB側の重複確認に使うSQLの例です。テーブル名と列名は実環境へ合わせます。
SELECT source_record_id, COUNT(*) AS row_count
FROM orders
GROUP BY source_record_id
HAVING COUNT(*) > 1;
結果がゼロ行でも、それだけで承認はできません。CSVの全受注が期待どおり保存されたか、訂正取引や取消取引まで誤って重複扱いしていないかを業務データの所有者が確認します。開発者は、トランザクションの開始位置、例外の再送出、接続の再利用、ログへの機密情報混入、同時実行時の競合をレビューします。一ファイル一トランザクションによるロック時間が許容できるかも、想定ファイル件数を踏まえて判断する必要があります。
Codexに検証結果を整理させる場合は、実行していないテストを成功扱いしないよう明示します。公式Cookbookで示されるワークフロー例も、観測したテストや検証証拠を求め、未確認事項を残す形を採っています(出典: OpenAI Developers)。報告には、実行コマンド、終了状態、対象テスト名、DB照会結果、未実施項目を分けて記載させます。人は端末出力やCI結果と照合し、要約だけを承認根拠にはしません。
本番適用前には、既存重複の扱い、マイグレーション失敗時の復旧、ロールバック手順も確認します。一意制約の追加は、すでにある不整合を自動で正してくれる処理ではありません。既存レコードの統合や削除が必要なら、今回のバッチ改修とは別の承認対象として扱うほうが安全です。

同じ条件で効率化を確かめる小さな導入
Codexを使った効果は、コード生成に要した時間だけでは判断できません。読み取り調査の準備、依頼文の作成、計画のレビュー、誤った提案の修正、テスト実行、整合性確認まで含めて比較します。比較対象には、同じリポジトリ、同じ匿名化CSV、同じ受け入れ条件、同じ成果物を用います。片方だけテスト範囲を狭くすれば、作業量の差ではなく品質条件の差になってしまいます。
記録する項目は、関連ファイルの特定に要した作業、設計上の未決事項を洗い出す作業、失敗テストの作成、実装、レビューで見つかった修正、検証、文書化です。実測前に削減率や所要時間を置く必要はありません。Codexが探索結果を的確にまとめ、既存規約に沿うテスト案を出せた環境では、資料探しや下書きの繰り返しが減る可能性があります。反対に、業務キーが未定義で仕様書とコードが食い違う環境では、確認と修正が増えるでしょう。
| 評価項目 | 確認する記録 | 判断の観点 |
| --- | --- | --- |
| 調査 | 読んだファイルと追加調査回数 | 探索の重複が減ったか |
| 設計 | 人が修正した前提と未決事項 | 誤った仮定を早期に発見できたか |
| 実装 | 差分量と範囲外変更 | 最小の変更に収まったか |
| テスト | 作成した失敗条件と実行結果 | 再実行と途中失敗を再現できたか |
| 確認 | レビュー指摘とDB照会 | 品質を落とさず手戻りを減らせたか |
| 運用 | 復旧手順と未確認事項 | 本番判断に必要な証拠がそろったか |
最初の一歩は、実装を依頼することではありません。匿名化した小さなCSVとテストDBを用意し、Codexへ読み取り専用の調査と失敗テスト案だけを依頼します。その報告を担当者が現行コードと照合し、一意キー、トランザクション境界、重複時の扱いを確定してから、最小差分の実装へ進みます。この一件で準備から承認までの記録を残せば、次のバッチ改修でもCodexを使う価値があるか、自社の条件に基づいて判断できます。
