コードを書く代わりにAIエージェントを指示することで、どうやってOMLAを作ったか
- カテゴリ
- AIとローカルLLM
- 公開日
- 2026年7月11日
- 更新日
- 2026年9月16日
- 著者
- Jacob Lloyd — プロジェクト完了後、AIの支援を受けて執筆
- 読了時間
- 約15分で読めます
かんたんに言うと: プログラミングチームもいない一人の人物が、どうやってAIアシスタントを指導し、非営利団体向けの本格的なオンラインライセンスプラットフォームを構築したかという実話です。明確な文書による計画、何重もの二重チェック、そして人間の承認なしには一切公開できないというルールといった作業方法が説明されています。AIの助けを借りて本格的なソフトウェアを構築するための実践的な手順書と言えるでしょう。
これが、私がAIコーディングエージェントを使って、非営利団体向けのロイヤルティライセンス管理用バックエンドであるOMLAをほぼ一人で構築した際の具体的な手順です。ぜひこのプロセスを参考にしてください。完成したバックエンドは、この方法が実際に機能することを示す証拠に過ぎません。
要約
- 内容: Claude Codeやローカル上のAIエージェント群を活用し、私がアーキテクト兼チェック担当者として関与することで、実際に非営利団体向けバックエンドを構築したワークフローです。
- コスト: 特別な追加費用はほとんどかかりません。コーディングエージェントの利用料や、第二の意見を得るための予備のGPUがあれば十分です。本当のコストは、ご自身でレビューに費やす時間だと言えます。
- 必要なもの: コーディングエージェント、曖昧さのない具体的な仕様書、そして「人間が『GO』という単語を入力するまで何もデプロイしてはならない」という厳格なルールです。
- 最終的に得られるもの: 確実に動作するバックエンドとその証拠資料です。段階的な監査や、実際のローカル環境でのテストも行われ、人間が「GO」と入力しない限りデプロイスクリプトは作業を進めません。
最終的にできあがったもの
手順説明の前に、最終的に何ができたかをご紹介します。それがOMLA、つまりオープンモデル・ライセンシング協会です。これはオープンなAIモデル向けのロイヤリティ管理用バックエンドおよび公開サイトとなります。
| 項目 | 結果 |
|---|---|
| 現状 | ワシントン州の非営利団体として組織中で、501(c)(3)認定も申請済みです。サイトには意図的に「ベータ版 0.9.0」というバッジが付けられています。 |
| ライセンス条件 | 個人利用・研究目的・教育目的であれば無料です。商用利用の場合は、発生した収益とモデル運用にかかるコストのうち多い方の30%を支払う必要があります。 |
| データベース | 12個のコアテーブルを有し、Postgres 17がSupabase上で稼働しています。すべてのテーブルに行レベルのセキュリティが適用されており、金銭計算には浮動小数点数ではなく整数単位のセント値が用いられます。 |
| 認証方式 | 従来型暗号と量子耐性暗号の両方に対応したed25519およびML-DSA-65による署名方式を採用しています。これにより将来量子コンピュータが実用化されてもシステムが無効化されることはありません。検証済みの署名がない場合はロイヤリティ情報も記録されません。 |
| 監査履歴 | 書き込み専用のログ体系で、SHA-256ハッシュにより連鎖管理されており、毎時チェックが行われます。また日次でチェーンの最新状態をデータベース外に保存する「アンカー」も設けています。 |
| フロントエンド | 約30ページのシンプルな静的HTMLページから構成され、フレームワークは使用していません。13言語に対応し、一般的な共有ホスティング上で運用されています。 |
| 開発担当者 | 指揮を執る人間が1名、コード作成にはClaude Codeが活用され、ローカルなオープンモデル・エージェント群がレビューや雑務を担当しています。 |
| 2026年7月時点での状況 | すべての準備・テストは完了済みです。私自身が「GO」と入力するまで、デプロイはドライランのみとなっています。 |
もしこれが週末だけで実現したと思われるかもしれませんので、大まかなタイムラインを記しておきます:
- 2026年5月下旬 — 個人用チャットボットの副業プロジェクトからライセンス非営利団体への転換を決定し、最初の12テーブル構成も設計
- 数日以内 — 量子耐性署名方式の導入、金銭処理関連のエッジ関数の実装、そして「カストディなし」仕様への改修(詳細は後述)
- 6月上旬 — バックエンド部分が本番環境へ投入された(フロントエンド側ではない)
- 6月16日 — 法的文書の改訂が完了し、ライセンス v1.0が施行された
- 7月2日 — サイトおよびバックエンド全体の更新、13言語への再翻訳も行われた。現在は私の「GO」指示を待っている状態です(2026年7月時点)
ちなみに面白い由来として、OMLAの元々の意味は「Open Machine Learning Assistant」であり、個人用チャットボットプロジェクトの略称でした。しかしその基盤となるインフラはライセンス事業へもそのまま転用されました。結果的にこのチャットボットは機能が制限されたドキュメント補助ツールへと変貌し、本番環境では意図的に無効化されています。趣味で作ったチャットボットがライセンス基盤に成長するというのは、ごく普通のスコープクリープ現象ですよね?
他のすべてを可能にしたルール
コードを書く前から、最も重要な役割を果たした設計上の決断があります。それはOMLAは一切、金銭を移動させたり保持したり送金したりしないということです。OMLAは支払うべき金額を計算し、受取人が自ら提供したウォレット情報や支払い詳細を公開します。商用利用者はクリエイターに直接、ピアツーピアで支払いを行います。決済処理もエスクローも「OMLA残高」も存在しません。ウォレット識別子は単なる経路情報であり、口座ではありません。
ライセンスの文言も明確にこう記されています:「OMLAは公開するだけで、支払いは行わない」。
なぜこのような仕組みにしたのでしょうか?金銭送金業者となることは、ライセンス取得に加えて規制面でも非常に複雑な問題を伴います。資産の保管を省略することで、KYCや制裁対応、税務上の負担は実際に取引を行う両当事者へと移りますが、本来そうあるべきだからです。このたった一つの決断があったからこそ、個人の趣味作家でもこのプロジェクトに挑戦できたのです。
実はこれは当初の設計ではありませんでした。開発途中で、スキーマには依然として資産保管の意味合いが含まれていたため、paymentsという名前のテーブルをroyalty_statementsへと改名せざるを得ませんでした。データベースが実際の機能を正しく表すようにするため、カラムやインデックス、トリガー、RLSポリシー、列挙型などを丸ごと移行させたのです。以前の文言が後々私に厄介事をもたらしたこともあります(注意点を参照してください)。
以下が、OMLAが一切金銭を保持せずにロイヤリティが支払われるまでの流れです。
ステップ4が重要な分岐点です。ステップ1で署名が検証されなければ、ロイヤリティ明細は一切作成されません。OMLAは金額とウォレット情報のみを公開し、その後のことは他の二当事者間で決められます。これはまるで私が「ある友人があなたに20ドル支払うべきだ」と伝えて、二人で話し合ってもらうようなものです。
実際のループ:人間、エージェント、検証、デプロイ
このワークフロー自体は複雑ではありません。ただし、誰が何をするかが厳格に定められており、何か変更があるたびにこの流れが繰り返されます。
重要なのは各ボックスそのものではなく、どの段階も飛ばされないことです。すべての変更において、まず私が「何を作るべきか、なぜそうする必要があるか」を明記します。エージェントたちはその仕様に基づいて草案を作成し、私が目にする前に他のエージェントがその草案を監査します。その後本番環境と同様の挙動を示すローカル環境上でテストが実行されます。最後に私が変更内容を確認するのです。
バックエンド全体はまずローカル環境で実行されます。つまり、私のマシン上でrootレスなコンテナ内に本物のSupabaseスタック、Postgres 17およびエッジ関数が構築されています。本番環境はあくまでデプロイ先であり、真の基準となるものではありません。もしローカル環境と本番環境の挙動が異なった場合、私が更新を反映させるまでは本番環境側に問題があると見なされます。
品質基準は私の頭の中だけでなく、プロジェクト設定にも記載されています。すなわち「監査に耐えうる状態がデフォルト:外部からの監査を通過できる成果物のみをリリースする」ということです。すべての処理において、独立したレビュアー(AIでも人間でも構いません)が後から不備を見つけようとすることを前提としています。
モックではなく実際のスタックでテストを行う
もしテストが偽の結果を返しているのであれば、上記のどれも意味がありません。そのため、ここでは一切モックデータベースを使用していません。すべてのテストは実際に使えるローカル環境で実行されます。3つのレイヤーすべてが問題なく動作してから初めてデプロイへ進みます。
deno test # 13 unit tests for the edge functions
./test/run.sh # isolated scratch DB per run, migrations w/ ON_ERROR_STOP,
# smoke tests (PQ signatures, wrong-key rejection, audit
# hash-chain, payout gate), RLS cross-tenant isolation,
# an adversarial suite, then rollback + reapply
./test/integration.sh # the full money path against the live local stack:
# register → tampered payload rejected → wallet verify →
# splits → usage report → statement published → replay
# (asserts zero duplicate writes) → admin gate → compliance
「重複した書き込みが一切発生しないことを確認する」という部分は、一見単純なように見えますが実は非常に重要です。利用状況のレポートは他者のシステムから送られてくるため、偶発的に同じレポートが再送信されることがあります。このテストでは、同じレポートを再処理しても新たな記録が一切生成されないことを確認しているのです。単にエラーが起きないだけでは不十分なのです。後にこのテストスイートは、結果をログ出力するだけでなく実際にその結果が正しいかどうかを検証するように強化されました。一見細かすぎるように思えますが、実際には「合格したはずなのに何も起きていない」という状態をデバッグする際に役立つのです。
監査結果を再監査する仕組み
AIが自分自身の作業内容をチェックするだけでは形式的な確認に過ぎません。そのため、意図的に複数段階の監査を行っています。まずは複数のエージェントが最初の修正作業を行い、その後より強力なモデルが最終的な独立した監査として処理を実施します。
この最終段階の監査は形式的なものではありません。一度のチェックで、各エージェントによる修正内容を検証したところ、最初の段階では見逃されていた検索パスのバイパスや、列情報・署名情報・履歴情報の不備が見つかりました。これらに対応するための改善作業が実施され、12件の指摘事項が特定され、それぞれに回帰テストも併せて導入されたため、問題が再発することはなくなりました。
規模に応じて監査体制も変えています。日常的な作業には3体のエージェントからなるレビューパネルを使用します。一方、フロントエンド全体や多言語対応の大規模な改修の際には24体のエージェントからなる群れが活用されました。この監査により、サイトの内容が現状と合っていないこと(古いチャットボット製品について記述されており、実際にはライセンス管理用のバックエンドとなっていた)が判明し、現実に合わせた再構築が必要であることが明らかになりました。
デプロイのゲート:私が指示を出すまでドライランが続く
デプロイランナーの役割はただ一つです。この行より上にある処理が誤って本番環境に反映されないようにすることです。デフォルトでは常にドライランが行われ、本番環境に変更を加えるには明示的なフラグが必要です。これは私がエージェントにこのウェブサイトの公開をさせる際にも使っている「まずドライランしてから実行」というパターンと同じです。
./deploy.sh # dry run (default) — shows exactly what WOULD happen
./deploy.sh --go # only a human runs this, only when ready
さらに、ガイド付きのラッパー機能も用意されています。本番環境に変更を加える前には、実際に「GO」と入力する必要があります。また、エージェントに関する規則も明文化されており、単なる提案ではなく必須のルールとなっています。つまり、人間からの明示的な指示がない限り--goを付けてはならないのです。
こうしたポリシーだけでなく、ランナー自体に組み込まれている保証もあります:
- フロントエンドの同期処理では
--deleteが一切使用されないため、管理していないホスト上のファイルが削除されることはありません - フロントエンドの上書き前には必ずサーバー側でバックアップが取られます
- リンク先のプロジェクトが予定された本番用プロジェクトと一致しない限り、バックエンドは動作しません。これにより誤ったデータベースに変更が加わることを防ぎます
- リモート環境に対して実行されるのは追加処理のみで、リセット処理は一切行われません
- 原則として、ドキュメンテーション用のチャットボット機能はあらゆるデプロイから除外されます
- シークレット情報はリポジトリ外のモード600のファイルにのみ保存され、テキスト形式でのチェックによりサービス用キーが配布されるコンテンツ内に含まれていないことも確認されます
正直なところ、このプロジェクトのデプロイログはほとんど「私が尻込みしていた記録」です。完全に準備が整ったリデザインが実行を待っている間、何度もドライランを繰り返していたのです。これはバグではありません。「ゲート付き」という仕組みが本来こうあるべき姿なのです。
注意点
特に気をつけるべき点は以下の通りです:
- ゼロ幅バイトの問題。 移行用のSQLは純粋なASCIIでなければなりません。目に見えない非ASCIIバイトが1つだけ存在すると、
$$というドルクォート記法が分断され、2つのSQL文が無意識のうちに結合してしまう可能性があります。そのため、毎回非ASCIIバイトが存在しないかgrepで確認し、$$の数が偶数であることもチェックする必要があります。 - パーセント記号の扱い。
RAISE EXCEPTIONの書式文字列において、%%はそのままパーセント記号として解釈され、各値には必ず1つずつ%が必要です。私自身も一度この罠に引っかかった経験があり、それが今回書き留める理由となりました。 - SECURITY DEFINERではsearch_pathを固定する必要がある。 すべての定義者関数やトリガー関数においてsearch_pathを固定しておくことで、誰かが
pg_temp内にテーブルを作成し、誤ったデータを混入させることを防ぎます。攻撃的なテストではこのように固定されている関数の数をカウントし、その数が減少した場合にはテスト失敗となります。 - スーパーユーザ権限が失われることがある。 不適切な再起動後、ローカルのPostgresユーザーがスーパーユーザ権限を失っている場合があります。対処法はサーバーをきちんと停止してから再起動することだけで、トラブルシューティングの必要はありません。エージェントがこの現象を繰り返し発見しなくなるよう記録しておきます。
- 国際化処理がHTMLを上書きする。 翻訳レイヤーがページのテキスト内容を完全に置き換えるため、HTML側を修正してもロケールファイルが更新されていなければ古い文言がそのまま表示されます。以前のコンテンツが削除されたはずなのに再び現れる現象も、まさにこの仕組みによるものです。
- 監査用チェーンも意図的に偽造可能であることを認識している。 データベースに対して完全な制御権を持つ攻撃者であれば、ハッシュチェーンのトリガーを削除し、整合性の取れた偽の履歴を作り出すことも可能です。ただし内部チェックだけではそれを見抜けません。そこで毎日実行されるジョブによって、チェーンの末尾ハッシュがデータベース外にも記録されており、テストスイートでは内部チェックが騙されつつも外部アンカーが不正な書き換えを検知できることを確認します。
- 本番サイトとステージングサイトは別物である。 執筆時点(2026年7月)では、公開されているサイトにはまだ再設計前のテキストが残っています。これは再設計版がステージング中でまだ本番にデプロイされていないためです。また、公開用の
/aboutURLは単なるディレクトリインデックスに過ぎず、AI関連情報ページはその1階層下に存在します。
関連記事:作業を実施したローカルエージェントスタック、これらのエージェントが私のウェブサイトをどのように管理しているか、同じ処理を自分でも実行したい場合のLLMアシスタントのセットアップ方法、そして任意のオープンソースプロジェクトを自分の環境に合わせて調整させるための方法もご覧ください。