オレンジ色のケーブルが接続されたパッチパネル
ニュース深掘り

sqlite-utils 4.0 のスキーママイグレーションと LLM ツール設計

目次を見る

LLM(大規模言語モデル)を使うツールがローカルデータを永続化する際、最も地味で最も厄介な問題が「スキーママイグレーション(データベースの構造変更を段階的に管理する仕組み)」です。Simon Willison が2026年7月にリリースした sqlite-utils 4.0 は、この問題に正面から取り組んだ 124 回目のリリースであり、2020年11月の 3.0 以来初のメジャーバージョンアップです。

なぜ LLM ツールでスキーママイグレーションが必要なのか

RAG(Retrieval-Augmented Generation: 外部知識を検索しながら LLM が回答を生成する手法)やエンベディング(テキストを数値ベクトルに変換したもの)を活用するローカルツールは、SQLite にベクトルデータやメタデータを保存することが多くなっています。ツールがバージョンアップするたびにテーブル構造が変わるため、既存のデータを壊さずにスキーマを更新する手段が不可欠です。Willison 自身が開発する LLM ツール(コマンドラインから各種 LLM を呼び出す OSS)も、`llm/embeddings_migrations.py` という形でこのパターンを数年前から実運用しています。

4.0 で導入された三つの機能

今回の 4.0 では、スキーママイグレーション・ネストされたトランザクション(`db.atomic()` メソッド)・複合外部キーの三つが追加されました。なかでも中心となるのがマイグレーション機能です。

マイグレーションの仕組みは次のように動作します。まず `Migrations` オブジェクトをインスタンス化し、デコレータ `@migrations()` で Python 関数を順番に登録します。各関数が「1 ステップの変更」を表し、ライブラリがその適用済み状態を `_sqlite_migrations` テーブルに記録します。次回実行時は「未適用のものだけ」を差分適用するため、冪等性(何度実行しても同じ結果になる性質)が保たれます。

from sqlite_utils import Migrations

migrations = Migrations("creatures")

@migrations()
def create_table(db):
    db["creatures"].create({"id": int, "name": str, "species": str}, pk="id")

@migrations()
def add_weight(db):
    db["creatures"].add_column("weight", float)

@migrations()
def change_column_types(db):
    db["creatures"].transform(types={"species": int, "weight": str})

CLI からは `uvx sqlite-utils migrate data.db migrations.py` の一行で実行でき、`--list` オプションで適用済み・未適用の一覧も確認できます。

table.transform() が SQLite の制約を回避する仕組み

SQLite は標準 SQL の `ALTER TABLE` が非常に制限されており、カラムの型変更や削除は直接できません。sqlite-utils の `table.transform()` は SQLite 公式ドキュメントが推奨する「新しいテーブルを作ってデータをコピーし、旧テーブルを削除してリネームする」パターンをライブラリ内部で自動実行します。これにより、PostgreSQL や MySQL では当たり前にできる型変更を SQLite でも安全に行えます。

たとえば `change_column_types` ステップで `species` を `TEXT` から `INTEGER` に変えても、ライブラリが裏側で一時テーブルを生成・データ移行・旧テーブル削除を一連のトランザクションとして処理するため、途中で失敗してもデータが中途半端な状態になりません。

Django Migrations との設計上の差異

sqlite-utils のアプローチを理解するうえで、Django の Migrations と比較すると設計思想の違いが明確になります。Django Migrations は Andrew Godwin が開発した仕組みで、モデル定義(ORM クラス)から差分を自動生成でき、過去のバージョンへのロールバックも備えています。

sqlite-utils は意図的にこの二つを省いています。理由はシンプルで、sqlite-utils はモデル定義 ORM を持たないため自動生成の前提がなく、ロールバックはデータベースファイルをコピーしてから適用すれば十分という判断です。SQLite がシングルファイルである特性を活かした割り切りといえます。

Django の前身として Willison も 2008 年の第一回 DjangoCon で `dmigrations` というライブラリを発表していた経緯があり、スキーママイグレーションへの取り組みは 18 年越しの継続テーマです。

LLM 基盤ツールへの適用可能性

ローカル LLM ツールや RAG パイプラインを Python で実装する場合、以下のような用途に直接応用できます。

  • エンベディングのメタデータテーブルにカラムを追加するバージョンアップ
  • チャット履歴スキーマにモデル名カラムを後から追加する変更
  • ベクトルインデックス(sqlite-vec など)の設定テーブルの型変更

Python コードから `migrations.apply(db)` を呼ぶ API も用意されているため、CLI を使わずにアプリ起動時に自動マイグレーションを走らせることもできます。

なお、今回の 4.0 は sqlite-migrate という別パッケージとして 3 年前にベータ公開していた設計をそのまま取り込んだものです。実運用での実績を積んだうえでメインライブラリに統合するという判断は、ML 基盤ツールの安定性を重視する観点からは合理的な進め方といえます。

sqlite-utils 4.0 が示すのは、LLM ツールの「データ層」を育てていく際に必要な最小限の仕組みが Python エコシステムの中で整いつつあるという事実です。派手さはないものの、長く使えるツールチェーンの土台として注目する価値があります。

参考

sqlite-utils 4.0, now with database schema migrations

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。