AI News HubLIVE
站内改写5 分で読了

AI観測可能性の4つのシグナル

実例を通じて、AIアプリケーションの観測可能性に必要な4つのシグナル(バージョン管理されたプロンプト、トレース、ユーザースコア、モデルスコア)を紹介します。

ソースHacker News AI著者: iroha1203

数ヶ月前、私たちはチャット体験を本番環境にリリースしました。ユーザーが質問すると、アプリはそれをLLMモデルにルーティングし、モデルは内部ツールをいくつか呼び出して回答を返します。一応機能しましたが、モデルがうまく答えた理由も、悪く答えた理由も全くわかりませんでした。モデルはアプリに接続されたブラックボックスで、最善のデバッグツールはログを読んで推測することでした。私たちのアプリはごく普通の運用上の質問に答えられないことに気づきました。「ユーザーが回答が悪いと言ったすべてのチャットを表示し、システムプロンプトのバージョンごとにグループ化し、モデルが呼び出したツールを含む会話全体を読ませてほしい」。これは「デプロイX以降のこのエンドポイントの500エラーをすべて表示」のAI版ですが、私たちのアプリは答えられませんでした。

それがきっかけで、より賢いモデルを探すのをやめ、観測可能性レイヤーを追加し始めました。最終的にLangfuseを使いましたが、特定のベンダーよりも機能が重要です。Helicone、Arize Phoenix、LangSmith、Braintrustはすべて同じ問題のバージョンを解決します。数ヶ月の反復の後、必要な機能は4つの種類に分かれることに気づきました。私はこれらを、すべてのAI機能が自身について発信すべき4つのシグナルと呼んでいます。すべてのプロンプトにバージョンがある(今日モデルはどの正確な単語を見たか?)。実際の作業を反映したトレース(どのツールをどの順序でどの引数で呼び出したか?)。ユーザーからのスコア(人間は結果を気に入ったか?)。別のモデルからのスコア(人間が静かなとき、誰が評価するか?)。もちろん、4つすべてなしでもAI機能は構築できますが、意図的に改善することはできません。

最初に行ったことは、すべてのプロンプトをコードから移動し、アプリが実行時に取得するバージョン管理されたストアに入れることでした。コードはバージョンを参照せず、ラベルを要求します。例えば、PromptRepo.compile(name: "classify_question", label: "production")。Langfuse UIで人間が「production」ラベルをバージョン間で移動させます。プロモーションはクリック一つ、ロールバックもクリック一つ、デプロイは不要です。PRを元に戻してCIを待つ代わりにボタンをクリックして悪いプロンプトをロールバックした最初の時、これが正しい形だとわかりました。プロンプトがコンテンツになると、問題に最も近い人々がプロンプトを書くようになり、フィードバックループが大幅に短縮され、品質が向上しました。

チャットは単一の呼び出しではなく、小さなプログラムです。質問を分類し、適切なプロンプトをロードし、一つか二つのツールを呼び出し、回答を構成します。トレースが1行だけだと、何かが起こったことしかわかりません。トレースツリーは実際に何が起こったかを教えてくれます。トレースが呼び出しのツリーであれば、モデルが下した決定のデータベースになります。例えば、以前は[INFO] chat_completed user_id=123 duration_ms=4200 tokens=1840という1行のログだけでしたが、改善後は以下のようなトレースツリーになります:trace: "chat", span: load-prompt (version=production:v12), generation: classify-question (model=haiku, category="billing"), generation: compose-answer, span: tool-call.lookup_invoice (200ms), span: tool-call.lookup_customer (180ms), generation: final-response (model=sonnet, 1.2k tokens)。各ノードはプロンプト名とバージョン、モデルID、トークン使用量、そして管理するメタデータフィールド(実行した顧客、質問の分類カテゴリ、実行したツール、会話が新規かどうか)を保持します。このメタデータが最も重要であることが後でわかりました。特定のツールが実行され、ユーザーが回答が悪いと言った、スコープX内のすべてのチャットをトレースからフィルタリングした最初の時、トレースリストはもはやログではなく、モデルが下した決定のクエリ可能なデータベースであることに気づきました。経験則:後でフィルタリングしたい次元でトレースにタグを付けましょう。事前には安価ですが、後で追加することは不可能です。

UIのすべてのアシスタントメッセージにはいいねとわるいねボタンがあります。ユーザーがクリックすると、行を保存し、トレースに対するスコアとして観測可能性ツールにポストバックします。わるいね単独ではアクションに結びつきませんが、トレースに付属したわるいねは、モデルが見たもの、呼び出したもの、どのプロンプトバージョンが生成したか、質問のカテゴリを教えてくれます。これにより、わるいねが特定のカテゴリに集中しているか、特定のプロンプトバージョンか、特定のツール呼び出しの後かを尋ねることができます。わるいねされたトレースはレビューすべきですが、時間がかかり、ノイズであることもあります(ユーザーがサポートしていないものを求めていたり、誤ってクリックした場合)。しかし、10件に1件は本当のシグナルであり、それがプロンプトの変更、新しいツール、バグ修正につながります。これらの配管の目的は一つの新しいクエリです:ユーザーが悪いとラベル付けしたすべてのトレースを表示。そのクエリを実行し、生成された完全な会話(プロンプトバージョン、ツール呼び出し、モデル、レイテンシなど)を読めるようになると、推測ゲームは終わります。

人間のフィードバックは有用ですがまれで、ほとんどのユーザーは何もクリックしません。そこで、最初のモデルを評価するために2番目のモデルを追加しました。バックグラウンドジョブが完了したチャットを取得し、別の「ジャッジ」プロンプト(本番プロンプトと同じバージョン管理ストアにありラベル付けされている)を通して実行し、結果を同じトレースにスコアとして書き戻します。これでトレースは2つの評価の流れを持ちます。ユーザーとジャッジが一致する場合、ジャッジは実際のユーザーと同期しています。一致しない場合、それがシステム内で最も興味深いトレースです。いずれにせよ、ジャッジはすべてのチャットで実行されるため、リグレッションはそれを引き起こしたプロンプトをリリースした当日に現れ、誰かが苦情を言う1週間後ではありません。ジャッジは事実性、指示順守、完全性、幻覚、アシスタントが正しい内部コンテキストを使用したかなどを評価します。私たちはこれを過小評価していました。リリース前にリグレッションをキャッチするジャッジは、より速く賢いモデルよりも価値があります。誰もいいねをクリックしないときにスケールする唯一のシグナルです。学んだ教訓:ジャッジは単なるプロンプトであり、間違う可能性があります。バージョン管理、プレイグラウンド、ロールバックボタンが必要で、ユーザー向けプロンプトとまったく同じです。各シグナルは同じトレースに書き戻されます。それがすべてのトリックです。

4つのシグナルは重複しており、それは意図的です。プロンプトバージョンはトレースに現れ、ユーザースコアはトレースに付属し、ジャッジスコアもトレースに付属します。それらは実際には4つの別々のものではなく、同じアイデアを4つの異なる角度から見たものです。AI機能を観測可能にし、それから意図的に変更できるようにする。しばらくの間、AI機能を別の種類のソフトウェアとして扱っていました:デバッグが難しく、テストが難しく、制御が難しい。しかし、AI機能はソフトウェアです。入力があり、決定を下し、出力を生成し、他のものと同じように観測できます。4つのシグナルは意図的に重複しています。それらは一つのアイデア、つまりシステムを観測可能にすること、を4つの角度から見たものです。それらを持ったときに変わるのは、モデルが賢くなることではなく、推測をやめることです。プロンプト変更をリリースするとき、ジャッジがリグレッションを教えてくれることを知っています。わるいねを読むとき、それを生成した正確な会話を再生できることを知っています。新しいプロンプトを本番に昇格させるとき、壊れたらワンクリックでロールバックできることを知っています。モデルはエンジン、観測可能性レイヤーはダッシュボードです。ダッシュボードなしでも運転できますが、意図的に運転することはできません。