エージェントのデータベース操作をリアルタイム差分表示する「diffium-db」公開
本文の状態
日本語全文を表示中
詳細モードで約14分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
TLDR AI
開発者は AI エージェントによるデータベース変更の可視化が困難な課題に対し、リアルタイムでスキーマと行の変更を監視するターミナル UI ツール「diffium-db」を開発し公開した。
AI深層分析を開く2026年9月2日 02:24
AI深層分析
キーポイント
AI エージェント運用における可視化の欠如
従来の Git diff や既存のスキーマ比較ツールは、エージェントが実行した行レベルの変更やその文脈をリアルタイムで示すことができないという課題がある。
diffium-db の機能と仕組み
PostgreSQL に対してベースラインを設定し、1 秒ごとにデータベースを読み込んで変更を検知するターミナル UI で、スキーマ変更と行の変更を同時に監視できる。
監視対象の多様性
テーブル(列、デフォルト値、制約、インデックスなど)、ビュー、マテリアライズドビュー、列挙型、関数に加え、行自体の変更も追跡する。
CI/CD への統合可能性
diff コマンドは変更があれば終了コードを返す機能を持ち、これにより CI ジョブの失敗判定や自動停止プロセスに組み込むことが可能である。
CI/CD連携のためのexit-code機能
diffコマンドは--exit-codeオプションにより変更を検知すると1を返し、CIジョブの失敗やエージェントループの停止に利用可能である。
重要な引用
The ten minutes after that are the problem.
It watches tables (columns, defaults, identity, constraints, indexes, triggers), views, materialized views, enums and functions. It also watches rows.
That last one takes --exit-code, so it returns 1 when anything changed, which is enough to fail a CI job or stop an agent's loop before it does the next thing.
The branch is copy-on-write off its parent, so it comes up holding the same schema and the same rows as the database you care about.
編集コメントを表示
編集コメント
AI エージェントが自律的にデータベースを操作する時代において、その挙動を人間が追跡・検証できる手段は不可欠である。本ツールはその課題に対し、軽量かつ即座に適用可能な実用的な解決策を示していると言える。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
以下の投稿は、diffium-db の構築に関する Claude Code のセッションを振り返ったものです。内容は AI によって生成されていますが、私は記事全体を読み込み、どこか異なる点がないか確認しました。結論から言うと、違いはありません。すべて私が diffium-db を開発する過程で得た経験に基づいています。
もし本プロジェクトに関連して私と直接議論したい場合は、dennis@a2w.io までメールを送るか、x.com/@kuberdenis でご連絡ください。なお、このプロジェクトは私が差分を監視するために使用している別のプロジェクト(github.com/kubeden/diffium)の直接的な続編となります。
私はエージェントにマイグレーションを実行させています。これが現在の作業フローです。あなたがやりたいことを説明すると、何らかの手段で SQL が生成されます。あなたはそれを読み、承認します。数秒後にはスキーマが変更されています。しかし、その後の 10 分間こそが問題なのです。
git diff を実行すればマイグレーションファイルの変更点は確認できますが、それが実際にデータベースに何をもたらしたかはわかりません。また、エージェントが途中経過として更新した行に関する情報も一切表示されません。なぜなら、その時点ではその更新が妥当だと判断されたからです。
優れたスキーマ差分ツールは存在しますが、ほとんどが同じ仕組みで動作します。作業後に実行し、2 つのデータベースを比較して差分(delta)を提示するだけです。作業が行われている間、その結果を常に表示しておくような機能はありません。
そこで私は、新しいツールを作りました。diffium-db はターミナル UI です。そして、作業中は常に開いておくことを想定しています。
リソース
- kubeden/diffium-db:本プロジェクトのレポジトリ(MIT ライセンス)
- kubeden/diffium:本作がモデルにしたファイル差分 TUI
- OpenTUI: このツールは、同様のターミナルフレームワーク「OpenTUI」を基に構築されています。
何ができるのか
PostgreSQL などのデータベースを指定し、基準となるスナップショット(ベースライン)を取得して監視モードを開始します。すると、その瞬間からデータベース上で起きたすべての変更が左側にリスト表示され、選択した変更の詳細は右側に表示されます。このツールは 1 秒ごとにデータベースを再読み込みするため、リアルタイムで変化を追跡できます。
例えば、画面を見ている間に demo.users テーブルにカラム 2 つとインデックスが追加されたマイグレーションを検知した様子です。

このツールは、テーブル(列、デフォルト値、ID 属性、制約、インデックス、トリガー)、ビュー、マテリアライズドビュー、列挙型、関数といったスキーマ全体を監視します。さらに重要なのは、行レベルでの監視です。実はここが最も興味深い部分であり、後ほど詳しく解説します。
4 つのコマンドがあります。watch はターミナル UI を起動し、snapshot でベースラインを取得します。また、baselines で利用可能なベースラインの一覧を表示でき、diff では変更点を出力して終了します。
最後の例では --exit-code オプションを指定しているため、何らかの変更が発生した際に終了コード 1 を返します。これにより、CI ジョブの失敗判定や、エージェントが次の処理に進む前にループを停止させることが可能になります。
セットアップ
監視対象のデータベースとしてPostgresを用意し、Bun 1.3以降が必要です。それ以外の依存関係は @opentui/core のみで、インストールもすぐに完了します。
git clone https://github.com/kubeden/diffium-db && cd diffium-db
bun install
export DATABASE_URL='postgresql://...'
まずベースラインを取得し、ウォッチャーを起動します。
bun run src/index.ts snapshot
bun run src/index.ts watch
これが全体のセットアップです。リポジトリにはデモが含まれており、エージェントの動作を待たずに実際に動く様子を確認できます。
examples/demo/01-baseline.sql は、ユーザーとプロジェクトを含む小さな demo スキーマを作成します。一方、examples/demo/02-agent-change.sql は、「請求プランを追加して整理整頓する」と指示した際にエージェントが実行する処理です。
ターミナルを 2 つ用意してください。1 つ目では以下を実行します。
psql "$DATABASE_URL" -f examples/demo/01-baseline.sql
bun run src/index.ts snapshot --schema demo
bun run src/index.ts watch --schema demo
2 つ目のターミナルでは、ウォッチャーを開いた状態で...
psql "$DATABASE_URL" -f examples/demo/02-agent-change.sql
画面に表示される変更は 5 つ。demo.users テーブルに新しい列が 2 つ、新しいインデックスが 1 つ追加され、既存のインデックスが 1 つ削除されました。また、新しい列も 1 つ追加されています。さらに、行の挿入と削除がそれぞれ 1 件ずつ発生しています。 (原文の技術表記: demo.projects)
j と k で移動し、s で並列表示とインライン表示を切り替え、e で差分全体をファイルに書き出します。
設定場所
デモはあくまでデモです。実際の業務で使用する際は、本番環境を指さないようにしてください。このツールの目的は、エージェントを実行させ、その実行結果を後から確認できるようにすることであり、そのような動作は破棄可能な環境で行う必要があります。
そのため、私はエージェントにブランチを与えます。Neon では、プロジェクト ID を指定する 2 つのコマンドが必要です(複数のプロジェクトを持っており、CLI が自動で推測しないためです):
neonctl branches create --project-id <project> --name agent-run
neonctl connection-string agent-run --project-id <project>
DATABASE_URL に 2 つ目のコマンドの出力を指定し、そこからベースラインを取得して、エージェントにブランチ上で作業させます。このブランチは親からのコピーオンライト方式で作成されるため、対象となるデータベースと同じスキーマとデータ行を保持した状態で起動します。私がこの記事を書いている間に作成したブランチでは、起動に 1.2 秒かかりました。アイドル状態のブランチも計算リソースを停止するため、削除し忘れたブランチが裏で稼働し続けることはありません。
差分を確認し、移行内容が意図通りであれば本番データベースに適用します。エージェントの行動が予期せぬものであれば、コピー上で検知でき、そのコピーは削除されます。
これが基本ループであり、この記事もこの手法で作成されました。デモおよびスクリーンショットはすべて、デモプロジェクトのブランチ上で実行されています。デモデータベース自体には、私が設定した状態がそのまま保持されています。
Postgres ならどの環境でも動作し、ツールに Neon 固有の依存はありません。ブランチ機能によってコストを抑えられ、毎回実行可能になります。記憶に頼る必要もありません。
行レベルの差分で証明できること
スキーマの変更は比較的容易です。問題は行データにあります。
ある行が変更されたことを示すには、その行の過去の状態を把握しておく必要があります。すべての行を保存するのは現実的ではないため、diffium-db は指紋のような仕組みを採用しています。具体的には、主キーとレコード全体を文字列に変換した md5(t::text) のハッシュ値、そして短いプレビュー情報を保持します。
これはデータベース内で計算コストが低く、保存も軽量です。さらに、挿入・更新・削除の区別を明確に行えます。キーが新規なら挿入、キーが消えれば削除、キーは同じだがハッシュ値が変わっていれば更新と判断できます。
ただし、テーブルに列が追加された瞬間にこの仕組みは機能しなくなります。すべての行のハッシュ値が一斉に変化するため、単純なツールでは「全行が編集された」と誤って報告してしまいます。実際には誰かが編集したわけではなく、スキーマの形状が変わっただけです。
diffium-db はそのような誤検知を起こしません。

左側のペインに ~3? と表示され、差分の上部にはその理由が記載されています。システムは列の変更を検知し、テーブルをマークした上で、回答のうちどの部分を信頼すべきかを提示しました。
カウントが ~3 ではなく ~3? と表記されているのは、差分の最初の行にその理由があるためです。2 つの列が追加されたことでテーブル内のすべての行が編集済みとして扱われ、更新回数はツールが確証を持って言える唯一の部分ではありません。
挿入と削除は依然として正確です。これらは主キーに基づいており、主キー自体はデータの形状を気にしないからです。推測となるのは更新カウントのみであり、証明可能な数値の隣に推測結果をそのまま表示すれば、画面全体の信頼性が損なわれてしまいます。
主キーを持たないテーブルや、--row-limit(デフォルトは 5000)を超えるテーブルについては、行カウントのみが表示され、変更の主張は一切表示されません。これは同じ原則に基づいています。
構造はテキストである
構造的な差分解析は、一つの重要な決定に支えられており、これが多くの挙動を説明しています。
すべてのオブジェクトは、一意で決定的な行順序を持つ単一のテキストブロックとしてレンダリングされます。テーブルの場合、列、制約、インデックス、トリガーの順に並べられます。構造的な差分解析とは、まさにこれらのブロックに対する行ごとの比較に過ぎません。
この一つの選択が多くの利点をもたらします。カラムを追加する際、テーブル全体が曖昧に「変更された」と表示されるのではなく、単に 1 行が増えるだけとなります。これにより、左右に並べて +2 -0 のカウントを確認したり、長いデフォルト設定を横スクロールで確認したりすることが可能になります。

サイドバイサイド表示ではなく、変更箇所をインラインで表示するため、パンの端で切り捨てられることがありません。s キーで切り替え、w キーで折り返しを切り替えることができます。これらの設定は実行間でも記憶されます。
コストがかかることもあります。diffium-db が示すのは、現在のデータベースの状態であり、そこに到達するための DDL 文ではありません。監視ツールとしてはこのトレードオフの側面が正しいと言えます。生成されたマイグレーションファイルが必要であれば、別のプログラムを選ぶ必要があります。
ベースラインの保存場所
ベースラインは生接続ではなく保存された値です。そのため、今すぐ取得して明日と比較したり、複数の名前付きバージョンを保持したり、それを生成したマイグレーションの隣にコミットしたりすることが可能です。
デフォルトでは、スナップショットは .diffium-db/snapshots/ ディレクトリに JSON 形式で保存されます。--store neon --store-url <url> を指定すると、Postgres の diffium_db スキーマ内に格納されるようになります。CI とローカル環境で「before(変更前)」の定義を一致させたい場合にこの設定が役立ちます。
ウォッチャーは自身のスキーマを参照しないため、保存先 URL と監視対象の URL は同じデータベースを指しても問題ありません。
上記のブランチ構成では、ベースラインはそのブランチ自体に存在し、記述対象からスキーマ一つ分離れた場所に置かれます。つまり、ブランチ全体が実験そのものとなり、ブランチを削除すると記録も同時に消去されます。
今後の課題
v1 でまだ実装されていない機能は、優先順に以下の通りです。
ORM の認識機能なし。 v1 版ではカラムが追加されたことだけを通知します。どのマイグレーションファイルやモデル定義によってそのカラムが作成されたのかを特定することはできません。このマッピングは ORM の役割であり、私が次に目指す機能です。実装先は src/orms/ ディレクトリとし、意図的に空の状態として文書化しています。
PostgreSQL 専用。 カタログの読み込み処理は src/pg/ に隔離されており、その境界より上側ではカタログという概念を認識していません。そのため、別のデータベース方言に対応する場合は明確な実装場所が存在します。ただし、まだ実装されていません。
シーケンス機能なし。 すべての bigserial 型カラムは挿入時に自動的に生成されますが、これは単なるノイズに過ぎません。誰かが問い合わせるまで無視されます。
ポーリング方式(レプリケーションではない)。 1 秒ごとに再読み込みを行います。大規模データベースでは論理レプリケーションの方が遥かにコスト効率が良いですが、もしポーリングがボトルネックになった場合に備えて、その時に検討すべき明確な選択肢です。
このリストに含まれていない、そして今後追加されることもない重要な制約があります。diffium-db は監視対象のデータベースに対して書き込みを行いません。書き込みが行われるのは、ユーザーが指定した場合に限り、同システム独自のストレージのみです。
総括
ソースコードは約 2,200 行、テストコードは 900 行です。そのうち 64 のテストはデータベースを一切使用せずに実行されます。ターミナル関連のテストも含まれており、これは OpenTUI がメモリ上に描画を行い、その画面に対してアサーションを行えるためです。レイアウトに変更が加えられた場合、テストが失敗することで問題に気づく仕組みになっています。これは、私が再撮影を忘れたスクリーンショットで静かに不具合を見逃してしまうリスクを防ぐための設計です。
私が何度も立ち返るのは、行ごとの差分表示です。各テーブルに対して単なる数値を出力し、ユーザーがその意味を推測させる方が簡単だったかもしれません。
「このデータについては証明できません」というバージョンは、見た目が少し見苦しくなりますが、それでも採用します。エージェントの隣に配置され、その行動を報告する仕組みは、エージェント自身よりも慎重であるべきだからです。
このプロジェクトは GitHub で MIT ライセンスの下で公開されています。実際に試して誤った結果が表示された場合は、ぜひ Issue を作成してください。
お読みいただきありがとうございました。
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み