asopi tech
asopi techOSS Developer
それでも、私はAlopex DBを作る

【2026年7月版】

それでも、私はAlopex DBを作る

公開日: 2026/07/15
読了時間: 約 28分

今さら、新しいデータベースを作っているなんて、俺は何をしてるんだろう。

Alopex DBの作業中、そう思うことが何度でもある。世の中には、すでに優れたデータベースがいくつもあります。SQLiteは、自己完結したサーバーレスの組み込みSQLデータベースとして広く使われています。DuckDBは、アプリケーション内で動く分析向けデータベースとして、ParquetやS3 APIのような周辺技術も扱えるように設計されています。FoundationDBは、分散環境でACIDトランザクションを提供するKey-Value Storeです。CockroachDBのように、水平スケールや耐障害性を前提にした分散SQLデータベースもあります。既存のデータベースが未熟だから、自分で作りたいわけではありません。むしろ、それぞれの製品は、それぞれの場所で非常によくできています。

ローカルで軽く動かしたいならSQLiteは強い。手元で分析したいならDuckDBは便利です。クラウド上で堅牢に動かすならPostgreSQLや分散SQLを選べばいい。ベクトル検索が必要なら専用のベクトルDBがあります。グラフを扱うならグラフDBやグラフライブラリがあります。一つひとつの選択は、どれも自然です。

問題は、プロジェクトを立ち上げるたび、方式を変えるたび、スケールアウトするたびに、その選択とデータコンバートを何度もやり直すことです。

単なるライブラリの差し替えではない

私はこれまで、GraphRAG、AIエージェント、時系列解析、実験管理、教師データの作成、分散実行基盤のようなものを作ってきました。最初は小さな実験なので、ローカルのファイルやSQLiteで十分です。少し分析したくなるとDuckDBを使いたくなります。複数のプロセスやコンテナから触りたくなるとPostgreSQLが候補になります。クラウドへ持っていくと、マネージドDB、オブジェクトストレージ、キュー、ジョブ基盤が入ってきます。スケールアウトを考えると、また別の分散DBが候補になります。RAGを入れればベクトルDBが必要になり、GraphRAGを作ればグラフ構造の保存先も考えなければなりません。

一つのプロジェクトを最後まで進めるだけで、私は同じデータを何度も書き直しています。立ち上げのときはSQLiteにスキーマを起こす。分析要件が出てくればDuckDBへ吐き出す。共同作業になればPostgreSQLへ移す。クラウドへ上げれば、マネージドDBの制約に合わせてまたスキーマを削る。そのたびに、AUTOINCREMENTだったIDがUUIDになり、JSONカラムへ逃していた属性を正規化されたテーブルへ戻し、逆にテーブルに収まらなかった項目を別のJSONへ追い出す。同じ「顧客」「文書」「実行ログ」というデータが、環境を移るたびに少しずつ違う形になっていきます。

ここで起きるのは、単なるライブラリ差し替えではありません。

ローカルで数千件のデータを扱っていたときは、スクリプトを一回流せば済みます。しかし、それが数百万件になり、数千万件になり、ベクトルやグラフや評価結果を含むようになると、同じ「移行」という言葉では扱えなくなります。データを読み出し、変換し、再投入し、インデックスを張り直し、Embeddingを作り直し、グラフのノードとエッジを再生成し、評価結果との対応関係を保ち、途中で失敗したら再開できるようにする必要があります。移行スクリプトは、もはや数行のコンバータではなく、それ自体が一つの小さなバッチ処理系です。

設計図の上では、これはきれいに見えます。SQLiteからPostgreSQLへ矢印を引き、PostgreSQLからベクトルDBへ矢印を引き、そこからGraphRAGへ矢印を引けば、システムの流れは説明できます。インタフェースを抽象化し、Repositoryを置き、アダプタを書けば、コードとしても整った形に見えます。矢印もクラス図も、書いている間は確かに前に進んでいる感覚があります。

けれど、その矢印の上を実際に流れるものには量があります。

矢印の上を流れるデータには量がある

数MBのJSONを変換するのと、数十GBの履歴データを変換するのは違います。数千件のEmbeddingを作り直すのと、数千万件のEmbeddingを作り直すのは違います。前者は一瞬で終わり、後者はGPUを何時間も占有し、電気代とクラウド課金という形で財布にも響きます。DuckDBは大きなデータを扱うためにディスクへのspillも備えていますが、それでも、どの処理がメモリに載り、どの処理がディスクへ逃げ、どこで再実行可能にするかは、設計上の現実的な問題です。同じSQLでも、一晩で終わるか、朝になっても終わらずメモリを食い潰して死んでいるかは、データの量とマシンの限界のあいだで決まります。

ここで、さらに厄介なのは、移行や変換のスクリプトも今ではコーディングエージェントに書かせることが多いという点です。エージェントは、変換ロジックや型定義やAPI境界を書くこと自体は得意です。しかし、実際にそのデータが何十GBあり、オンメモリで読み込めばWSL環境ごと落ちるとか、途中で失敗したらまた最初からやり直しになるとか、生成したEmbeddingやノードIDが過去の評価結果と対応しなくなるとか、そういう実行時の重さには関心が向きにくい。こちらが指摘しなければ、read_csvで全部読み、巨大な配列を作り、メモリを食い尽くし、落ちてから少し直し、また落ちる。そういう迷惑なトライ&エラーが何度も起きます。

具体的に何が起きるか。数十GBのテーブルを変換してほしいと頼むと、エージェントはまず全件をメモリへ読み込むコードを平然と書きます。ローカルの小さなサンプルでは動くので、本人は正しいコードを書いたつもりでいます。それを本番サイズで流すと、WSLのメモリを使い切り、Linux側のOOM killerがプロセスを殺すか、Windowsごと引きずられて重くなる。私はログを見て「これはチャンクで回して」と指示する。エージェントはチャンク化する。今度はチャンクの境界でIDの採番がずれる。また指示する。次は途中失敗からの再開ができず、こけたら最初からやり直しになる。バッチ処理として当たり前に必要な、逐次読み込み・冪等な再実行・チェックポイントといった配慮が、最初の一発では出てこないのです。

これは、エージェントが怠けているという話ではありません。今のAI開発支援が、フロー、モデル、API、インタフェース、関数の形を中心に世界を見ているからです。処理の境界はよく見える。しかし、その境界を通過するデータの量、変換時間、再生成コスト、失敗時の復旧点は、背景として扱われにくい。人間なら、段ボール十個を運ぶのと倉庫一棟を空にするのが別の作業だと体でわかります。その「重さの感覚」が、コードの抽象化だけを見ていると抜け落ちます。

私はここに、今のAI時代の開発の危うさを感じています。

それは移行ではなく、知識の作り直しだ

例えば、GraphRAGで文書からノードを作り、Embeddingを生成し、エッジを張り、検索結果を評価したとします。後からDBを変えることになったとき、問題は「保存先が変わる」ことではありません。ノードIDが変わるかもしれない。Embeddingの生成単位が変わるかもしれない。分割したチャンクの境界が変わるかもしれない。評価データが参照していた過去の検索結果と、新しい検索結果が対応しなくなるかもしれない。

そうなると、以前「この質問にはこの根拠ノードが効いた」と確かめた評価結果が、宙に浮きます。参照先のノードIDがもう存在しないからです。チャンクの切り方が一段変わっただけで、同じ文書から作ったはずのグラフが別物になり、精度を比べる土台そのものが消えます。保存先を移しただけのつもりが、積み上げた検証をまるごと捨てている。

それは移行ではなく、知識の作り直しです。

教師データでも同じです。あるデータセットがどの元データから作られ、どのスクリプトで変換され、どのモデルがラベルを付け、人間がどこを確認したのか。その履歴が残っていなければ、後から同じものを作ったつもりでも、本当に同じとは言えません。ラベル付けに使ったモデルのバージョンが上がっただけで、境界事例の判定は静かに変わります。それを知らずに再学習に混ぜれば、原因のわからないスコアの揺れとして跳ね返ってきます。Data-centric AIの議論では、AIの性能をモデルだけではなく、データの品質、量、維持管理から考える必要があるとされています。Data-centric AIのサーベイでも、学習データ、推論データ、データ保守を含むライフサイクル全体を扱う必要が整理されています。

この考え方には強く同意します。ただ、私が現場で感じている問題は、もう少し低いところにあります。

データはいつのまにか別物になる

データを中心に考えると言いながら、実装になると、データはすぐに処理の都合へ従属します。ローカルではこの形式、分析ではこの形式、クラウドではこのDB、検索ではこのインデックス、RAGではこのベクトル、評価ではこのCSV。目的ごとに最適な道具を選んでいるうちに、データは少しずつ別物になっていきます。どの一手も合理的で、その場では正しい判断です。だからこそ止めどころがなく、気づけばあちこちに少しずつ違う版のデータが散らばっています。

例えば、あるテーブルの主キーです。最初は連番のidだけで足りていました。複数プロセスから書き込むようになると、衝突を避けるために(tenant_id, id)の複合キーへ変えます。監査のためにcreated_atやupdated_atを足し、物理削除をやめてdeleted_atを足す。容量やスキーマの都合で、あとから使われていないカラムを消すこともあります。一つ一つは、その時点でのストレージや整合性の都合として筋が通っています。しかし、それを書くコーディングエージェントは、そのカラムが本来何を表現するために置かれたのかを知りません。deleted_atという名前を見れば論理削除用だと推測し、tenant_idという名前を見ればテナント分離用だと推測します。名前から推測できる範囲では正しい。けれど実際には、deleted_atが「削除された日時」ではなく「表示から外した日時」として運用されていたり、tenant_idがテナントではなく契約単位で振られていたりする。そうした、カラム名には現れない元々の意図は、引き継ぐ人がいない限り、そのつど都合のよい解釈に置き換わっていきます。これはバッチが更新を取りこぼしたからではありません。カラムを足したり整理したりするたびに、そのカラムが何を表しているかという解釈そのものを、暗黙に決め直しているからです。しかも、その決め直しは一度では終わりません。パイプラインや移行コードをエージェントに書かせるたびに、名前から推測された解釈がまた一段積み重なり、元の意図との距離は少しずつ広がっていきます。

そして、別物になったことに気付くのは、たいてい後になってからです。

検索結果が以前と微妙に違う。評価スコアが再現しない。再学習に使ったデータがどの版だったのか分からない。ローカルで試した実験結果をクラウドに持っていくと、IDの対応が崩れる。コンテナでは動くが、分散構成にした瞬間に処理順序や整合性の前提が変わる。どれも、単体では小さな食い違いです。しかし積み重なると、「なぜ先週の数字が出ないのか」を丸一日かけて追いかけることになり、たいていの場合、犯人はモデルではなく、いつのまにか入れ替わっていたデータのほうです。そうした問題は、設計段階のきれいな図には現れません。

Alopex DBを作りたいと思ったのは、この経験が積み重なったからです。

知識を、詰め替えるのではなく育てたい

私は、ローカルではSQLite、分析ではDuckDB、クラウドではPostgreSQL、分散では別のDB、ベクトルはさらに別のDB、グラフは別のライブラリ、という選び方を否定したいわけではありません。それは現実的で、強力で、今でも正しい選択です。

ただし、そのたびにデータそのものを作り直すなら、話は変わります。

開発段階が変わるたびにDBを変える。DBを変えるたびにスキーマを変える。スキーマを変えるたびに変換を書く。変換のたびにEmbeddingを作り直し、グラフを再生成し、教師データを再整理し、評価結果をつなぎ直す。これは、アプリケーションを成長させているというより、同じ知識を何度も別の容器へ詰め直している状態です。そして詰め直すたびに、こぼれるものがあります。来歴が切れ、IDの対応が薄れ、過去の評価が使えなくなる。

具体的に何が変わるか。Parquetにネストして持てたオブジェクトも、SQLiteやDuckDBへ移せば、フラットな列に均すかJSON文字列へ押し込むしかありません。ベクトルDBへ移せば、テキストを1文字直すたびにEmbeddingを再計算することになり、その古いベクトルを消すか、過去の評価が参照するまま残すかで悩まされます。PostgreSQLでスパース検索を足せば、そのためのカラムやインデックスをまた作る。移すたびに、データの形そのものが変わっていきます。

また、ある特徴量を試したくなれば、エージェントはその場でカラムを一本足し、名前を適当に選び、すぐに結果を見せてくれます。トライ&エラーがその日のうちに何周も回るのは、素直にありがたい。けれど、うまくいかなければまた次のカラムを足す。

最初はそれでも構いません。数千件ならやり直せます。数万件なら一晩待てばいいかもしれません。厄介なのは、増え方そのものです。そうやって、ほんの少し違うだけのデータが複製されては積み重なり、ストレージの量は倍、また倍と膨れ上がっていきます。

こうして列が積み上がり、DBを変えるたびに作り直す。これでは、知識が育っているとは言えません。データの量は増えても、「情報」の総量は増えていないからです。形式を変えて、毎回スクラップ&ビルドを繰り返しているだけです。

Alopex DBでやりたいこと

Alopex DBでやりたいことは、ここにあります。

ローカルで生まれたデータが、コンテナへ移り、クラウドへ広がり、分散構成へスケールしても、同じデータとして扱われ続ける。実行形態が変わっても、データモデルを毎回作り直さない。保存場所が変わっても、ID、来歴、生成条件、評価結果との関係が切れない。Embeddingやグラフや教師データを、環境変更のたびにゼロから作り直すのではなく、これまで積み上げたものとして扱う。スケールアウトは、知識を捨てて建て直す機会ではなく、同じ知識をそのまま大きくする操作であってほしい。

これは、単に「同じAPIで使えるDB」が欲しいという話ではありません。

APIだけ同じでも、裏側でデータの意味が変わってしまえば意味がありません。接続文字列を差し替えれば動くように見えても、IDの採番や型の丸め方が変わっていれば、それは同じデータではない。欲しいのは、ローカルから分散まで同じように見える薄いインタフェースではなく、データが増え、重くなり、移動に時間がかかり、再生成にコストがかかることを前提にしたデータベースです。

Alopex DBは、構造化データ、時系列、ベクトル、グラフ、メタデータ、来歴、監査情報を、最初から同じ知識体系の中で扱うことを目指しています。すべてを万能にこなす巨大なDBを作りたいわけではありません。むしろ出発点は小さくてよい。ローカルで動き、アプリケーションに組み込めることが重要です。そのうえで、データが増え、次のフェーズを迎えたときに、最初に作った知識を捨てなくて済むようにしたい。

ここが、Alopex DBを作る理由です。

既存DBに機能が足りないからではありません。既存DBを組み合わせるだけでは、プロジェクトが成長するたびにデータの同一性が壊れやすく、それを担保するために膨大なコストと時間が掛かるからです。AIエージェントやGraphRAGのようなシステムでは、その壊れた部分の補修も、結局はエージェントにやらせることになります。変換スクリプトを書かせ、対応表を作らせ、欠けたメタデータを探させ、評価をやり直させる。けれど、エージェントは何が本来の意図だったかを知りません。だから直したつもりの一箇所がまた別の食い違いを生み、その食い違いを直すスクリプトをまた書かせることになる。終わりの見えないその作業に、時間が無限に吸い込まれていきます。本来ならモデルや体験の改善に使えたはずの時間が、データの同一性をつなぎ直す作業に消えていく。私はその時間を取り戻したいのです。

データそのものから、もう一度考える

私は、データベースをもう一度、データそのものから考えたいのです。

プロジェクトは成長し、AIも進歩していきます。その速さに合わせて、DBもストレージも次々に乗り換えていくのが当たり前になりました。処理の形も、使うモデルも、上のレイヤーは絶えず移り変わります。問題は、乗り換えのたびにデータモデルまで変わってしまうことです。成長すればデータは増え、その増えたぶんを丸ごと次の環境へ移し替えなければなりません。移し替えには、変換や再生成の膨大なコストがかかります。しかもそのコストは、データが積み上がるほど大きくなる。成長するほど、次の一歩が重くなっていくのです。

だからこそ、データモデルとストレージは、段階が進むたびに使い捨てる部品ではなく、プロジェクトとAIの成長を通してずっと持ち越していく背骨であってほしい。上のレイヤーで何が移り変わっても、その下でデータは同じものであり続ける。そういう位置に、データを据え直したいのです。

欲しいのは、データを詰め替える場所ではなく、育てていける場所です。試行錯誤も、失敗も、評価も、捨てずに積み上がっていく。Alopex DBで作りたいのは、その場所です。