Andre

Originally published at olund.dev.

私のツールの一部は、設定済みの声で長文コンテンツを生成します。声とは、読者層・文体・語彙・書き手の印象を記述した編集アイデンティティのことです。このアイデンティティは設定ファイル内の文章として存在します。そして、文章によるトーンの設定には厄介な性質があります。つまり、書き込み専用なのです。欲しい声を記述(「ゆったりとしていて、好奇心旺盛で、第一原理から説明し、誇張は一切しない」)し、生成器に消費させると、その記述が実際に機能したかどうかは、全文の生成を実行して料金を支払い、結果を読んで初めてわかります。文体がずれていれば、形容詞を修正して再度料金を支払うことになります。

問題はフィードバックループです。全文生成を繰り返してペルソナを調整するのは、結婚式のケータリングを繰り返してレシピを調整するようなものです。私が欲しかったのは「味見」でした。つまり、このアイデンティティで、この声による2行のサンプルを、今すぐ、ほぼ無償で得られることです。

最小限のエージェント

解決策はプレビューエージェントです。その設計目標は、LLM呼び出しが到達し得る限り無料に近づけることでした。実行をためらうプレビューは、結局実行されないプレビューだからです。そのすべては「引き算」です。

  • ツールなし。 エージェントはファイルの読み込み、閲覧、検索ができません。必要なもの—アイデンティティの文章と任意のトピック—はすべてディスパッチ時にインラインで渡されます。これはコスト制御だけでなく、ツールを持たずコンテキストが完全にインラインであるエージェントは再現性が高いためです。同じ入力、同じクラスの出力、周囲の要因によるドリフトは一切ありません。
  • 高速なローカルモデル。 声の試聴は狭い作業です。スタイル記述への忠実度が必要であり、深い推論は不要です。呼び出しはワーカーランタイムの最安ティア—自分のマシン上で動作する小型モデル—にルーティングされます。測定された試聴はエンドツーエンドで約500トークンで、ローカルハードウェアではほぼゼロ円です。
  • 厳格な出力スキーマ。 エージェントは2〜6行のサンプル行を返さなければならず、各行は声が実際に話すであろう1〜2文で、それ以外は一切含みません。見出しもト書きも声に関する解説もありません。スキーマは呼び出し層で強制されるため、不正な応答はUIに到達する前に再試行されます。プレビューはチャットではなく契約です。
  • 構造的にエフェメラル。 これを提供するルートは何も書き込みません。データベース行も成果物も履歴もありません。永続化されたプレビュー出力は状態になり、一覧表示・移行・クリーンアップの対象になります。プレビューの価値は、それが蒸発することにあります。
  • タイムアウトと1回の再試行。 90秒、1回の回復試行、その後明示的に失敗。ハングするプレビューはエラーするプレビューより悪いです。

プロンプト側は1つのルールを3通りの表現で繰り返します。アイデンティティのトーン・レジスター・語彙を正確に一致させる。各行は単独で話せる散文でなければならない。メタコメントを行わない。声を説明するサンプル行ではなく、声を体現するサンプル行を生成すること—これが失敗モードであり、指示はそれを直接攻撃します。

生まれるループ

設定UIでは、アイデンティティエディタの横にサンプルボタンがあります。任意のトピックを入力してクリックすると、瞬時に設定された声による数行の文章が表示されます。アイデンティティを編集して、再度サンプリング。調整ループは「全文を生成して読み、顔をしかめる」から、数秒ごとの反復へと短縮されます。

見た目以上に重要な2つのUI判断があります。

プレビューは何も無効化しない。 これは単なるfire-and-return呼び出しで、キャッシュ更新はありません。状態を変更しないからです。プレビューをミューテーションのようにアプリのデータレイヤーに接続するのはカテゴリエラーであり、すべてのプレビューに再取得コストを発生させます。

空の状態が教える。 アイデンティティが設定されていない場合、ボタンは黙って無効化されず、エンドポイントは「identity is unset」で拒否し、UIもそれを表示します。前提条件が見えないプレビュー機能は、壊れているように見えます。

下層にはシームの判断もあります。ルートはワーカーを同期的に待機し、上限を2分とします。ジョブIDを返してクライアントがポーリングする方式ではありません。プレビューはインタラクティブです。人間がその場で待っています。プレビューにプログレスバーが必要になった瞬間、それはプレビューとして失敗しています。そのため、API形状はレイテンシ予算をエンコードします。ユーザーが見ている間に回答できないなら、エラーすべきであり、ステータス更新をストリーミングすべきではありません。

なぜこれはパターンであって機能ではないのか

一般的な形はこうです。人間が書いた記述をシステムが消費して高価なものを生成する場合、その間に可能な限り安価なサンプラーを挿入する。 記述から出力までのギャップは、静かに自信が死ぬ場所です。設定を書いたあなたは、それが自分の意図を表していると思う。しかし、唯一の検証手段が全文生成のコストを要求するのです。

サンプラーが価値を発揮するのは、以下の条件を満たすときです。

  1. 反射的に使えるほど即時である。 秒単位であって、分単位ではない。サンプリングする価値があるかどうかを判断する必要が生じた瞬間、反復は止まります。
  2. 罪悪感なく使えるほど安価である。 ローカルモデルか最安のAPIティア。タスクは設計上狭い。最も狭いワーカーを使用する。
  3. 正直であるほど制約されている。 ターゲット形式でスキーマ強制された出力。サンプラーが「何をするつもりか」についてのエッセイを返すなら、それは演劇です。
  4. 無視できるほどステートレスである。 永続化なし、履歴なし、クリーンアップなし。40回実行しても、何も蓄積されません。

私は今、散文の設定が生成を駆動するたびにこの形を採用しています。作品の前にペルソナをサンプリングし、バッチの前に要約スタイルをサンプリングし、レビューの前にレビュアーの厳しさをサンプリングする。各サンプラーは半日程度の作業です。引き算は構築が速いためです。エージェント定義全体が1画面に収まり、それが乗るワーカーランタイムはすでに存在していたからです。

下層にある静かな教訓は、モデルルーティングについてです。本能的には、すべてのタスクを最強の利用可能モデルに送りたくなります。しかし、プレビューの仕事は代表的かつ即時的であることであり、最大限であることではありません。厳格なスキーマとインラインコンテキストを持つ小型ローカルモデルは、自由度を増したフロンティアモデルが即興で生成するよりも、「設定された声がどのように聞こえるか」をよりよく代表します。タスクの狭さにワーカーを合わせれば、一部のタスクはほぼ無料になることがわかります。