テキストを生成しないJevは、どうやってブラウザを操作しUIを組み立てるのか?Browser Useとjson-renderを分解する
Browser Useとjson-renderという2つのオープンソース事例を通じて、Jevが限定されたアクション空間やコンポーネント空間の中で構造化された判断を行う仕組みと、DOMの読み取り、テキスト生成、JSONの組み立て、検証、レンダリング、最終実行を周辺コードが引き続き担う理由を説明します。
目次

AIにブラウザを操作させるとき、最も直感的な方法は、画面キャプチャやDOMを汎用の大規模モデルに渡し、ページを分析して次の手順を考えさせ、クリック位置、セレクタ、あるいはツール呼び出しを生成させることです。
UIの構築も似ています。ユーザーが要望を伝えると、モデルがJSON、JSX、フロントエンドコードを直接生成し、システムがそれを解析、検証、レンダリングしようとします。
しかし、Browser UseのJev Ultrafastとjson-renderのJev実験は、別の道を選びました。
まずコードが、モデルに許される行動を有限集合へ絞り込み、Jevはその中から選ぶだけにする。
Browser Useでは、その集合は現在のWebページで実行可能なアクションと操作可能な要素です。json-renderでは、アプリケーションが事前に用意したコンポーネント、プロパティ設定、データバインディング、レイアウト位置です。
Jevは、完全な操作計画を書く必要も、UI全体のJSONツリーを生成する必要もありません。答えるのは、次のような問いだけです。
- 次の操作はクリック、テキスト入力、スクロール、待機のどれか。
- 現在のページ上のどの要素を操作すべきか。
- UIにどのコンポーネントを表示すべきか。
- コンポーネントをどの親コンテナ、どのスロット、どの順序に置くべきか。
この2つの事例が示しているのは、「文章を生成できないモデルでも何でもできる」ということではありません。示しているのは、別のソフトウェアアーキテクチャです。つまり、自由度の高い生成タスクを、制約され検証可能な判断の連続へ変換するという考え方です。
ここでJevは実際に何をしているのか
JevはTypeSafe AIが公開したSystem Oneモデルです。stateと、開発者が定義したtyped questionsを受け取り、人が読むための長い文章ではなく、Choice、Score、Noulなどの構造化された結果を返します。
確率出力を持つ判断関数として捉えると分かりやすくなります。
現在の状態
↓
開発者が有限の候補集合を構成する
↓
Jevが選択、採点、判定を行う
↓
通常のコードが結果を検証する
↓
アクションを実行する、またはUIをレンダリングする
各プリミティブの役割は次の通りです。
Choice:与えられた選択肢から1つを選ぶ。Score:開発者が与えた順序付き段階のどこに当たるかを評価する。Noul:ある判断が成立する確率を返す。
重要なのは返却形式そのものではなく、責任分担です。Jevはページの文言、ブラウザセレクタ、JavaScript、完全なJSONを自由生成しません。状態、制御フロー、権限、実行は、引き続きコードが握ります。TypeSafeはこのパターンを「非構造化状態を入力し、型付きの確率的判断を出力する」と説明しています。(typesafe.ai)
以下では、Browser Useとjson-renderがこのパターンを実際のシステムへどう落とし込んだのかを見ていきます。
Browser Use:まずWebページを有限のアクション空間へ変える
Browser Useのjev-ultrafastプロジェクトは、ブラウザエージェントの実例です。ユーザーが自然言語で目標を与え、プログラムが現在のページを読み、Jevが次の操作を選び、ブラウザコードが実行します。
公開デモのタスクは、Google FlightsでZürichからLondonへの片道便を検索することです。プロジェクトが報告した録画上の所要時間は約7.1秒で、モデル呼び出し、テキスト生成、ブラウザ実行、ページ読み込み、古くなった判断の再試行を含みます。ただし、これは1つのブラウザ構成における1つのタスクにすぎず、任意のWebサイトでの汎用的な信頼性ベンチマークではありません。(github.com)
ステップ1:ページを読むのはコードであり、Jevが単に「画像を見る」わけではない
各判断サイクルで、ブラウザコードはまず、現在ページ上に見えているコントロールとテキストを読み取り、番号付きの要素表を作ります。
簡略化すると、次のようになります。
[1] button 航空券タイプを変更 · 往復
[2] combobox 出発地は? · San Francisco
[3] combobox 目的地は? · 空
[4] textbox 出発日 · 空
[5] button 検索
ここには、要素タイプ、名前、現在値、番号が含まれます。プログラムは、それぞれの番号に対応する実際のDOMノードも保持し、実行直前に対象を再解決できるようにします。
この事例で、Jevは画面キャプチャを直接見て、ボタンが座標(482, 316)にあると推測しているわけではありません。画面キャプチャは主にデモと人間の確認に使われます。判断を実際に動かしているのは、DOMから抽出された構造化状態です。プロジェクトも、画面上の番号はキャプチャのレンダリング時に追加されるもので、ブラウザ操作には使われないと明記しています。(github.com)
この違いは重要です。
モデルに座標やCSS Selectorを自由生成させると、次のようなものを返す可能性があります。
- ページ上に存在しないセレクタ。
- すでに古くなった要素位置。
- 他の要素に隠れている、またはクリックできないコントロール。
- 任意の挙動を実行できるJavaScript。
Jev Ultrafastでは、モデルはプログラムが直前に観測した要素番号からしか選べません。
ステップ2:Jevがアクションと対象要素を選ぶ
プロジェクトが用意しているアクション集合は次の通りです。
CLICK
TYPE_TEXT
SELECT
SCROLL_UP
SCROLL_DOWN
WAIT
DONE
BLOCKED
プログラムは現在のページ状態に応じて、その時点で本当に使えるアクションと、互換性のある対象だけをモデルへ提示します。
例えば、次のようになります。
- 現在のページにドロップダウンがなければ、
SELECTの対象を提示しない。 - テキスト欄が3つあれば、テキスト入力の対象をその3要素に限定する。
- クリック可能な要素が10個あれば、クリック対象をその10要素に限定する。
1回の判断を簡略化すると、次の形になります。
質問1:次のアクションは何か?
候補:CLICK / TYPE_TEXT / SELECT / WAIT / DONE
質問2:CLICKの場合、どの要素をクリックするか?
候補:[1] / [5] / [8] / [11]
質問3:TYPE_TEXTの場合、どの要素にテキストを入力するか?
候補:[2] / [3] / [4]
これらの質問は1回のリクエスト内で並列に判定できます。実際に使われるのは、選ばれたアクションと互換性のある対象だけです。例えばJevがCLICKを選んだ場合、プログラムはclick_targetだけを読み、TYPE_TEXT向けに事前計算された対象を実行しません。
プロジェクトはこれを、動的でインデックス付きのアクション空間と呼んでいます。この設計により、各ステップで直列に必要となるモデル呼び出しが減り、モデルに操作パラメータを自由生成させずに済みます。(github.com)
ステップ3:文章を書く必要があるときだけ生成モデルを呼ぶ
Jevは「今、出発地入力欄へ文字を入れるべきだ」と判断できますが、入力すべき文字列そのものは生成しません。
アクションがTYPE_TEXTの場合に限り、システムは小型のテキスト生成モデルを呼び出し、現在のタスクと対象フィールドに基づいて入力値を作ります。例えば次の通りです。
{
"text": "Zürich"
}
その結果も、ブラウザが入力する前に、小さなJSONオブジェクトとして解析されなければなりません。
したがって、このブラウザエージェントは2種類の能力を組み合わせています。
| タスク | 担当 |
|---|---|
| 次の操作がクリック、入力、選択、待機のどれかを判断する | Jev |
| ページ上のどの要素を操作するか選ぶ | Jev |
| 入力すべき自然言語テキストを生成する | 小型生成モデル |
| DOMとページ状態を読む | ブラウザコード |
| クリック、入力、選択を実行する | ブラウザコード |
| 目標が本当に達成されたか確認する | 独立した検証コード |
つまり、「Jevがブラウザを操作する」という表現は、Jevがブラウザタスク全体を単独で完了することを意味しません。
より正確には、Jevはブラウザループ内のアクション選択器です。
ステップ4:実行前にコードがページを再確認する
モデルが選択を返しても、プログラムはすぐに盲目的なクリックを行いません。
Jev Ultrafastは実行前に、さらに次の点を確認します。
- 現在のページが、モデルの判断時に観測したページと同じか。
- 対応するDOMノードがまだ存在するか。
- 要素が別の内容に覆われていないか。
- 現在の位置と形状がまだ有効か。
- フォーム値と周辺文脈がスナップショットと一致しているか。
- テキスト生成リクエストの入力が変化していないか。
モデルの応答が返る前にページが変化すると、元の判断はすでに無効かもしれません。プログラムはそれを古い判断として扱い、古い要素への操作を続けません。
また、プロジェクトはモデル出力から直接作れるものを明確に制限しています。モデル出力がそのままCSS Selector、画面座標、Shellコマンド、実行可能なJavaScriptになることはありません。実行対象はすべて、以前に観測された実際のDOMノードへ再解決されなければなりません。(github.com)
この部分のコード自体は「知的」ではありません。しかし、システムの信頼性を決めるのはここです。
7秒という速度を、Jevだけの成果にはできない
プロジェクトは、交互に行った6回の実行で、2つの実装がそれぞれ3回ずつタスクを完了したと報告しています。中央値のタスク時間は約9.450秒から7.092秒へ下がり、約25%短縮しました。ブラウザプロトコル呼び出し回数は1,092回から101回へ減りました。同時に作者は、これは同じタスク、同じブラウザ構成における各実装3回だけの結果であり、一般的な信頼性テストではないと強調しています。(github.com)
性能向上はモデル速度だけでなく、ブラウザ実装全体から生まれています。
- 見えているコントロールを一度に読み取る。
- ブラウザプロトコルの往復を減らす。
- アクションと対象の質問を同じ判断リクエストへ入れる。
- テキスト入力が必要なときだけ生成モデルを呼ぶ。
- 実行後は必要なページ変化だけを待つ。
- 無関係なページ本文をモデル文脈へ入れない。
したがって、「モデルをJevへ置き換えれば、すべてのブラウザエージェントが7秒でタスクを終えられる」と結論づけることはできません。
現在のMVPは、shadow DOM、iframe、canvas、ファイルアップロード、ポップアップタブ、ネストしたスクロール、任意のキーボードウィジェットを完全にはサポートしていません。モデルがDONEを選んだ場合でも、システムはタスクが本当に完了したかを独立して確認します。(github.com)
json-render:ページ全体のJSONを生成せず、Jevにコンポーネントを選ばせる
json-renderが扱うのは別の問題です。自然言語の要求から、すぐにレンダリングできるUIをどう組み立てるかという問題です。
従来の生成UIでは、モデルに次のようなものを直接出力させることがよくあります。
- ReactやVueのコード。
- 完全なJSON UIツリー。
- CSSとレイアウトプロパティ。
- イベント処理ロジック。
- データバインディング設定。
この方法は柔軟ですが、モデルの出力空間も非常に大きくなります。モデルはコンポーネント名を間違えたり、存在しないプロパティを生成したり、未登録のアクションを参照したり、解析できないJSONを返したりする可能性があります。
json-renderのJev実験は、問題を次のように変えました。
アプリケーションがまず有効なコンポーネントインスタンスを用意し、Jevはどれを使い、どう組み合わせるかだけを決める。
この機能は現在も実験的と明記されています。experimental_composeSpecとexperimental_createEvaluatorは安定APIとして公開されておらず、バージョン更新で名前や挙動が変わる可能性があります。公式ドキュメントは、厳密なバージョン固定と変更履歴の確認を勧めています。(json-render.dev)
アプリケーションが先にコンポーネントカタログと候補を渡す
ユーザーが次のように依頼したとします。
売上ダッシュボードを作成してください。上部に注文表、その下に売上、注文数、新規顧客の指標を横一列で配置し、最後に週次売上グラフを置いてください。
アプリケーションは、この一文をそのままJevへ渡し、UI JSONを自由に書かせるわけではありません。
代わりに、まず候補を用意します。
Dashboard
OrdersTable
MetricRow
RevenueMetric
OrdersMetric
NewCustomersMetric
RevenueBarGraph
各候補は単なる名前ではありません。アプリケーション側で設定されたコンポーネントインスタンスで、次の内容を持てます。
- コンポーネントタイプ。
- 固定プロパティ。
- 利用可能なレイアウト設定。
- 状態バインディング。
- データバインディング。
- 呼び出し可能なアクション。
- モデル向けの候補説明。
例えば、ボタン候補は事前に次のように定義できます。
コンポーネント: Button
テキスト: 保存
アクション: savePreferences
引数: 現在の /name 状態を読み取る
Jevはこのボタンを使うかどうか選べますが、未登録のdeleteAllUsersアクションをその場で作ることはできません。
json-renderの公式説明は、利用可能な能力とデザインシステムをプラットフォーム側が管理すると強調しています。Jevが選べるのは、アプリケーションから渡されたコンポーネント、設定、アクションバインディングだけです。足りない文言、データ、コンポーネントをJevが自動で作るわけではありません。(json-render.dev)
第1段階:UIに必要なコンポーネントを選ぶ
新しいUIを作るとき、最初の判断群は次を扱います。
- どの候補をルートノードにするか。
- どのコンポーネントを選ぶか。
- 再利用可能なコンポーネントを何個使うか。
- 同じリソースに複数バリエーションがある場合、どれを選ぶか。
例えば、次のようになります。
ルートコンポーネント: Dashboard
含める:
- OrdersTable
- MetricRow
- RevenueMetric
- OrdersMetric
- NewCustomersMetric
- RevenueBarGraph
選択が終わると、通常のコードがすぐに初期Specを組み立て、コンポーネントプロパティとアクション引数をcatalog schemaに照らして検証し、すでにレンダリング可能なプレビューをストリーミングします。
この時点では、レイアウトがコンポーネントカタログの順番のままかもしれません。それでもユーザーは、構造的に有効な中間結果をすでに確認できます。
これは、モデルに完全なJSONをトークン単位で出力させる方法とは異なります。Jevはシリアライズ済みJSONを書きません。制約付きの選択結果からコードがJSONを組み立てます。 (json-render.dev)
第2段階:親子関係と並び順を決める
コンポーネントを選んだ後、2つ目の判断群がレイアウトを扱います。
- 各コンポーネントがどの親ノードに属するか。
- 親ノードのどの名前付きスロットに置くか。
- 兄弟コンポーネントの順番をどうするか。
最終構造は、例えば次のようになります。
Dashboard
├── OrdersTable
├── MetricRow
│ ├── RevenueMetric
│ ├── OrdersMetric
│ └── NewCustomersMetric
└── RevenueBarGraph
その後、コードが次を確認します。
- 有効なルートが1つだけ存在するか。
- 親子関係に循環が生じていないか。
- 深さが上限を超えていないか。
- コンポーネントが有効なスロットへ置かれているか。
- 各候補の使用回数が許可範囲内か。
- すべてのプロパティ、バインディング、アクション引数がschema検証を通るか。
組み立てたレイアウトに矛盾があれば、システムは壊れたUIツリーを出力せず、直前に検証済みのプレビューを保持します。
ルートが1つだけ、または1つのスロットに子が1つしかない簡単な構造なら、2回目のレイアウト判定自体が不要なこともあります。(json-render.dev)
UIの編集も「選択」であり、全体の書き直しではない
json-renderは既存のSpecも編集できます。例えば次のような変更です。
- 保存ボタンを削除する。
- 注文表をグラフより上へ移動する。
- あるグラフ種類を、別の提供済み候補へ置き換える。
- フィールド順を変える。
- コンポーネント設定を差し替える。
この種の編集は通常、順番に進みます。
- 変更する要素を選ぶ。
- 新しいコンポーネントレシピまたは移動先を選ぶ。
- コードが変更を適用する。
- ツリー全体を再検証する。
変更されないコンポーネントID、状態バインディング、データ、互換性のある子ノードは、可能な限り保持されます。入力されたSpecそのものを直接変更するわけではありません。(json-render.dev)
ボタンを選んでも、ボタンの処理が自動実行されるわけではない
json-renderは「UIの組み立て」と「業務アクションの実行」を明確に分けています。
JevはsavePreferencesに紐づくボタンを選べますが、composer自体はそのアクションを呼び出しません。実際の実行はユーザーがクリックした後に起こり、ホストアプリケーションのaction handlerが担います。
アプリケーション側では、引き続き次が必要です。
- ユーザー権限の確認。
- 引数の検証。
- サーバー側の認可。
- データの妥当性確認。
- 冪等性と監査ログ。
- 危険な操作に対する再確認。
公式ドキュメントは、アクションをカタログに登録しても、任意の引数を安全に受け取れることにはならないと明記しています。composerは将来の実行時状態を検証できず、アプリケーションに代わって認可を行うこともありません。(json-render.dev)
構造が有効でも、UIが正しいとは限らない
json-renderは、出力がサポート対象の構造とschemaに合うことは保証できます。しかし、Jevが選んだUIが完全で、妥当で、見た目も良いことまでは保証できません。
公式ドキュメントの例は次の通りです。
上部に表があるダッシュボードを生成してください
この要求は指標やグラフを明示していないため、表だけが選ばれる可能性があります。
より具体的な要求は次のようになります。
売上ダッシュボードを作成してください:
上部に注文表を置く;
その下に売上、注文、新規顧客の指標を横一列で置く;
最後に週次売上グラフを置く.
こちらの方が、必要な候補をすべて選び、期待する順序に並べる可能性が高くなります。
ここには重要な境界があります。Jevは候補空間の中でしか判断できません。候補空間を十分に用意し、要求を明確に表現する責任は、引き続き開発者にあります。
公開ドキュメントの再利用可能APIでは、標準で最大32回の評価、1バッチ最大32要素の作成、最大深さ8という制限があります。公開Playgroundではさらに厳しく、1バッチ最大14要素、14回の評価、深さ4です。呼び出し数、要素数、深さの上限へ達すると、システムは部分的なSpecを返すことがあります。しかし「処理が完了した」ことは、意味的に正しいことを意味しません。(json-render.dev)
2つの事例は、実は同じアーキテクチャを使っている
Browser Useとjson-renderを並べると、扱う問題は違っても、構造がほぼ同じだと分かります。
| 段階 | Browser Use | json-render |
|---|---|---|
| ユーザーの目標 | フライトを検索する、フォームを埋める、ページを開く | UIを作成または変更する |
| コードが読む状態 | 可視DOM、コントロール、テキスト、値 | 現在のSpec、コンポーネント候補、カタログ、ツリー構造 |
| 有限の候補空間 | クリック、入力、選択、スクロール、操作可能要素 | コンポーネントインスタンス、親、スロット、順序 |
| Jevの担当 | アクションと対象を選ぶ | コンポーネント、親子関係、順序を選ぶ |
| 生成モデルの担当 | 入力が必要なときだけテキストを生成する | Jev経路ではUIを自由生成しない。新しい文言やデータは事前提供または別途生成が必要 |
| 通常コードの担当 | DOMスナップショット、鮮度確認、実行、待機、結果検証 | Spec組み立て、schema検証、ツリー検証、レンダリング、アクション認可 |
| 主な失敗 | ページ状態の陳腐化、対象の消失、未対応コントロール | 候補不足、要求の曖昧さ、不完全なレイアウト、不適切な選択 |
| 最終検証 | タスク目標が本当に達成されたか確認する | Specが完全で、利用可能で、製品要件に合うか確認する |
両者は同じ式で表せます。
環境を構造化状態へ変換する
↓
できることを有限の候補集合へ変換する
↓
Jevに選ばせる
↓
コードで検証し、実行する
↓
結果を再び観測する
モデルに次の一手を自由生成させる方法と比べると、柔軟性の一部を手放す代わりに、制御境界が明確になります。
テキストを生成しなくても、なぜ「知的」に見えるのか
知性は、記事、コード、対話という形で出力されなければならないわけではありません。
多くのソフトウェア処理で、システムが本当に必要とするのは判断だけです。
- 今、どのボタンを押すべきか。
- この要素をどの領域に置くべきか。
- 実行を続けるべきか。
- どのコンポーネント設定がユーザーの要求に最も合うか。
- 現在の結果で目標を達成できたか。
汎用LLMは、まず説明文を生成し、その後で答えをJSONへ包めます。しかし、最終的にコードが1つの選択肢しか必要としないなら、その途中の長いテキスト生成に価値がないこともあります。
Browser Useとjson-renderの実験は、「考える過程」の多くをシステム設計へ移しています。
- 開発者が状態を定義する。
- 開発者が候補空間を定義する。
- 開発者が実行ルールを定義する。
- 従来のルールでは扱いにくい意味判断だけを、モデルが補う。
このアーキテクチャはエラーをなくすのではなく、エラーの形を変えます。
Jevは候補集合にないコンポーネントやアクションを返しません。しかし、次のような間違いは起こせます。
- 間違ったボタンを選ぶ。
- 間違ったコンポーネントを選ぶ。
- タスク完了を早すぎる段階で宣言する。
- 複数の妥当なレイアウトから、より弱いものを選ぶ。
- 要求が曖昧なため、必要な内容を落とす。
型安全が保証するのはインターフェースであり、真実ではありません。提供された調査レポートも、構造に制約があることは、業務判断の正しさを保証しないと強調しています。質問設計、状態モデリング、閾値、独立検証は、本番システムで引き続き中心的な要素です。
どのようなタスクがこのパターンに向くのか
Browser Useとjson-renderから、実用的な基準を引き出せます。
タスクを「有限の候補から選ぶ」に分解できるなら、その判断ノードをJevへ任せる価値があります。
比較的向いているタスクは次の通りです。
- ブラウザやデスクトップアプリのアクション選択。
- エージェントのツールやスキルのルーティング。
- 管理されたコンポーネントカタログからUIを構成すること。
- 候補レイアウトから選ぶこと。
- メール、サポートチケット、文書の分類。
- 候補証拠から関連内容を選ぶこと。
- 結果を再試行するか、人へエスカレーションするかの判断。
一方、次のタスクをJevへ直接任せるべきではありません。
- 長い記事やカスタマーサポート返信を書くこと。
- 候補集合にない新しい文言を生成すること。
- 完全に新しいビジュアルシステムを自由設計すること。
- 複雑なプログラムを書くこと。
- 多段階の算術や日付計算を行うこと。
- 候補アクションが存在しないときに解決策を発明すること。
- 長い推論連鎖とオープンな計画を必要とするタスク。
実際の製品では、複数モデルを組み合わせることが多くなります。
汎用モデル: 目標、テキスト、コード、候補プランを生成する
Jev: 候補を判断、選別、ルーティングする
通常コード: 検証、実行、フォールバック、記録を行う
Browser Useの小型テキストモデルは、この分担を直接示しています。Jevが「入力すべき」と判断し、生成モデルが「何を入力するか」を決めます。
本当に学ぶべきなのは、2つのデモではなく責任分離
Browser Useとjson-renderから最も再利用できる教訓は、「JevがWebを閲覧できる」「JevがUIを生成できる」ということではありません。
より正確な結論は次の通りです。
- Browser Useは、ブラウザ操作を自由生成から、実際のDOM要素に対するアクション選択へ変えた。
- json-renderは、UI JSONの自由記述を、アプリケーション自身のコンポーネントカタログからの選択と並び替えへ変えた。
- Jevは意味判断を提供する。
- コードは権限を制限し、状態を維持し、構造を検証し、結果を実行する。
- 自由な文章が必要なときは、引き続き生成モデルが担当する。
このアーキテクチャでは、AIはシステム唯一の運転手ではなく、コードが制御するフローの中にある1つの判断ノードになります。
実際にAIを本番ソフトウェアへ組み込みたいチームにとっては、モデルが一度で完全な答えを生成できるかより、この点の方が重要かもしれません。システムの信頼性は、モデルが何を選んだかだけでなく、次の条件にも左右されます。
- モデルにどの状態を見せたか。
- 開発者がどの候補を渡したか。
- システムがどのアクションを許可したか。
- 誤った結果を止められるか。
- ページやUIの変化後に再判断できるか。
- 完了状態を独立して検証したか。
テキストを生成しないからといって、Jevに何もできないわけではありません。
それは、モデルの知性が主に文字列ではなく、ソフトウェアが直接利用できる一連の選択として表れ、同時に慎重な検証を必要とするという意味です。