個人アプリでAIをUIに織り込む
この記事では、Millwrightという3層のデータ契約アーキテクチャを紹介します。このアーキテクチャにより、モデルがマークアップやスタイル、DOMに触れることなく、モデル生成の分析結果をレンダリングできます。型付き結果ウィジェット、バージョン管理されたボード仕様、追加のみのナビゲーションにより、安全で監査可能、元に戻せるAI統合を実現します。
本記事では、個人アプリにおいてAIが生成した動的コンテンツをUIに安全に組み込む方法について、マークアップやスタイル、DOMにAIモデルが直接触れることなく実現する方法を解説します。著者は自身のアプリmillfolioのMillwright機能を紹介します。これは、3層のデータ契約アーキテクチャを採用し、モデルの出力を安全にレンダリング可能なUIコンポーネントに変換します。
第1層はウィジェット層です。ウィジェットのコンテンツは小型のサンドボックスプログラムによって生成され、これらのプログラムはAskで一度きりの質問に答えるものと同じです。プログラムは決してHTMLやMarkdownを返しません。代わりに、バージョン管理されたJSONエンベロープ内の型付きデータブロック(テキスト、テーブル、KPI、時系列/カテゴリ系列、シェア円グラフ、オフライン比例シンボルマップなど)を返します。各値は明確に型付けされており、例えば通貨値は{raw, text}の形式で渡され、クライアントは数値と書式設定された文字列をそれぞれ使用します。この「信頼されたクローム」インバリアントにより、プログラムはデータのみを生成し、クロームがすべてのインタラクションを管理します。テーブル列がマーチャントやタグとしてタグ付けされると、クロームが自動的にセルをVaultレコードへのディープリンクに変換し、プログラムがスクリプトやリンクを注入することはできません。なぜなら、契約にはそれらを配置する場所がないからです。
第2層はボード層です。ボード自体は、バージョン管理されたセマンティックな仕様ドキュメントであり、各ウィジェットのID、タイトル、サイズヒント、および計算するプログラムへのポインタを含む順序付きリストです。編集操作(タイルのリサイズ、プログラムの編集、ウィジェットの削除)ごとに、新しいコンテンツアドレスバージョン(バイトの16桁16進数FNV-1aハッシュ)が生成され、バージョンログに追加されます。「現在の」ボードはそのログへのポインタに過ぎず、アンドゥはポインタの移動で済み、編集に失敗しても以前のバージョンが失われることはありません。候補となる仕様が受け入れられる前に(インライン✎エディタからでもモデルからでも)、バリデーターがチェックします。ウィジェットIDはパスセーフで一意でなければならず、参照されるプログラムが存在し、リモートURLは拒否され、構造的な制限が適用されます。モデルが提案し、バリデーターが却下します。
第3層はページ層です。ウィジェットのグループはページに昇格でき、AskやVaultと並んで独自のトップレベルナビゲーションボタンが付与されます。ページを解散するとウィジェットはボードに戻ります。ページは同じ仕様ドキュメント(pages[]セクション)、同じバージョン管理、同じバリデーターを使用しますが、追加のルールがあります。ナビゲーションの変更は追加のみ可能です。生成された編集はページを追加できますが、組み込みのタブをリネームしたり削除したりすることはできません。ユーザーがモデルの動作を検査するために依存するUIの部分(AskやVaultなど)は、モデルによって編集できません。
なぜ層を分けるのか、モデルにUIを直接書かせる代わりに?明らかな代替案は、モデルにHTML/JSXを生成させてサンドボックス化することですが、その欠点は、差分を取れず、構造的に検証できず、ポインタを移動して元に戻せず、毎回のレンダリングがセキュリティ上の判断となることです。3つのデータ層は逆のトレードオフを提供します。モデルの貢献はすべて検査、バージョン管理、検証、拒否が可能なドキュメントであり、ピクセルは常にアプリに同梱されたコードによって描画されます。この階層化により、公開デモも安全に公開できます。デモボードは合成Vault上で同一のメカニズムを使用し、編集はブラウザのlocalStorageに保持され、同じハッシュ、バリデーター、クロームを使用します。
まとめ:Millwrightは厳格なデータ契約と階層構造により、安全で監査可能かつユーザー制御可能なAI統合を実現し、AIがUIを直接操作することによるセキュリティリスクと予測不可能性を回避します。ユーザーはdemo.millfolio.appで実際にウィジェットを編集し、元に戻す体験ができます。