同じダブル振り子の生成コントラクトを、OpenRouter上でClaude 3.5 SonnetとDeepSeek V3に対して実行しました。いずれも同一の初期条件(θ1 = π/2、θ2 = π/2、両方の角速度はゼロ)です。public/workers/simulator-host.jsのホストレンダラーは、モデルのgetInfo()が返す内容からinfo.theta1とinfo.theta2を読み取り、その後、上端中央を固定した支点と、L1+L2から導かれる固定スケールを使って2つのボブを描画します。モデル内部でどんな規約を使ったかは気にしません。受け取った角度をそのままプロットするだけです。
シミュレーション開始から最初の1秒の間、2つのパネルは鏡像のように見えました。Claudeの振り子は下向きにぶら下がり、水平からのリリースで想定される通りに揺れました。DeepSeekの振り子は支点から上向きを向きました。まるで初期条件が「下向きの鉛直からπ/2」ではなく「上向きの鉛直からπ/2」を意味しているかのようです。どちらのパネルも、まったく同じ描画コードを通してレンダリングされています。異なっていたのは、step()とgetInfo()の出力だけでした。
これがこれほどすっきりと表面化するのは、コントラクト設計のためです。モデルはstep(dt)、getInfo()、reset()だけを実装します。draw関数を書くことはありません。ピクセルのすべてはホストが所有しています。したがって、モデルが自前のレンダリングロジックによって規約の選択を隠す方法はありません。モデルAが正のy軸(上)からθを測り、モデルBが負のy軸(下)から測るのであれば、ホストは両方を同じ描画方法で描き、食い違いは空間的な反転として即座に可視化されます。
生成コントラクトはlib/prompt.tsにあります。モデルは、運動方程式と初期条件を指定するシステムメッセージを受け取ります。そして、最初の行がfunction createSimulator(である、ちょうど1つの囲い付きコードブロックを返さなければなりません。インポートなし、エクスポートなし、DOMアクセスなし、そしてdrawなし。プロンプトは角度の規約も指定していますが、2つのモデルは同じ文章を別々に解釈しました。generated-simulators/<slug>.trace.jsonにキャッシュされたトランスクリプトを確認したところ、両方のモデルがチェーン・オブ・ソートの中でその規約を認めた上で、互いに食い違うコードを書いていることが分かりました。
これはVerdentで作られた「Physics Bench」という小さなプロジェクトのものです。現在カバーしているのは1つの問題(ダブル振り子)で、スコアリング用のパイプラインはありません。単にモデル同士を並べて実行し、見られるようにしているだけです。面白いのは、レンダリングをモデルに制御させる能力を取り除くと、どれだけ多くの微妙な意見の相違が明確になってしまうかです。規約の不一致は最も視覚的に劇的ですが、重力トルク項の符号が食い違って、瞬間的な反転ではなく、より遅いドリフトが生じるケースも見ました。
他のモデルを差し替えて試してみたい人向けに言うと、コントラクトは十分に厳格なので、OpenRouter上の多くのモデルは最初の試行で有効なシミュレータを生成できます。そして失敗した場合(NaNの伝播、SIMULATOR_MAX_TOKENS = 16000での途中切断)には、エラーを同じ会話にユーザーメッセージとして送り返す補正ループがあり、モデルが文脈を失わずに自分のコードを修正できるようになっています。
運動方程式から物理を実装するようにモデルへ促す際に、規約の曖昧さに他の人も遭遇したことがあるのか気になります。また、それを確実に曖昧さなくするためのプロンプト文言を見つけられたでしょうか。
[link] [comments]



