❤️‍🔥

AIコーチングの開発で、一番ネックだった品質評価を段階的に実装した話

に公開

こんにちは!mentoでエンジニアをしているyoshikenです。

我々は今年1月にAIと「mento マネジメントAI」を発表し、対話・コーチングを行う「AIコーチング」機能をリリースしました!

https://prtimes.jp/main/html/rd/p/000000071.000048788.html
https://prtimes.jp/main/html/rd/p/000000071.000048788.html

この記事は、マネジメントAIの裏側を紹介するブログシリーズの4本目です!以前の記事は下記noteからどうぞ。

https://note.com/hashtag/mentoマネジメントAIの裏側

0. はじめに

「AIコーチング」のようなLLMベースの機能を作る際に大事なのは、「その価値をどう評価するか」です。

特に正解のない「コーチング」をどうやって評価するのかはとても難しいポイントでした。

この記事では「LLM as a Judge(LLMによる自動評価)」だけではなく、人による評価も含めて実際にいつどの順番でどう実装していったのかを紹介します。

1. 前提:AIコーチングの仕組み

今回リリースした機能は「メンバーがAIと対話(コーチング)をした内容がマネージャーにレポートされる」機能です。

ここで課題となるのが、「コーチングに唯一の正解はない」 という点です。

例えばユーザーを正解に誘導するカスタマーサポート用のチャットボットのようなAIであると応答が正解か不正解かは容易に判断できます。

一方でコーチングをAIによって行う場合、「ユーザーの入力に対してAIはこれを返すべき」「AIの返答に対してありうるユーザーの入力はこれが入力される」という正解がとても定義しにくいため、とても評価が難しいという課題がありました。

この課題についてどのように評価を定義・実行していったかの試行錯誤は、mento AIエンジニアのfukaさんのブログを参照してください!

https://note.com/qluto/n/nc0bf41079878

ここでは、具体的にどう評価自体を「実装」しながらLLM機能を開発していったかを紹介します。


2. CLIで高速な試行錯誤を回す (PoC期〜)

開発の最初期、まだフロントエンドもバックエンドも整っていない段階で最初に行ったのは、CLI(コマンドラインインターフェース)での人の評価を早く回す実装でした。

実際にAIコーチング自体に価値があるかを検証する必要があるため、最速でプロンプトやシナリオを「人の目」で確かめて修正していく必要があります。

データ・AIエンジニアが中心になってプロンプトやシナリオを磨き込むために、元になるデータはyamlをベースとして最低限の環境設定で展開できるような形にしていました。


検証を回すためのlコーチングシナリオyam例

これがあることによって、そもそもAIコーチングという機能自体に価値がありそうか、今時点のLLMが要求に耐えうるかを早めに判断できました。

また、PoC以降でも「複数人のAIコーチング結果からのレポート」のような複雑なデータをベースとした対話の部分ではCLIを用いての検証がとても有効で、プロジェクト終盤までアップデートしていきました。


3. LLM as a Judgeの導入(本実装期〜)

本実装が進むにつれ、評価を自動化するために「LLM as a Judge」を導入しました。

前段にあるように、AIコーチングの評価は正解がないため難しく、どのようにJudgeを実装していくかが課題でした。

今回は 「特定の課題を持つペルソナLLM」 を複数用意し、AIコーチと擬似的な対話を行わせ、その内容を別のLLM(Judge)が評価指標に基づいて採点する仕組みを作りました。

他にも、事前にシナリオをある程度定義しておいてそれに沿ったコーチングをできているかの一致度を見る方法なども試しましたが、コーチングという性質上必ずしも同じシナリオを辿ることが正解とは言えないため、ペルソナだけを定義してコーチングさせる方法の方が適していました。

また、複数ペルソナを定義することによって、主要なペルソナだけをテストしたい時と、エッジケースを含めてテストしたい時の両方に対応できるというメリットもあります。


コーチングを受けるペルソナの例

mentoはバックエンドがPython/FastAPIで動いているので、テストは他のUnitテストと同様にpytestで実行しています。
今時点では特別なフレームワークなどは使っていません。

@pytest.mark.parametrize("persona,scenario,criterion", test_params)
async def test_evaluation_matrix(persona, scenario):
    context = AICoachingSessionContext(
        persona=persona, scenario=scenario,
    )
    evaluator = AICoachingSessionEvaluator()

    # AIコーチングを実行
    target_result = await evaluate_target.execute(context)

    # AIコーチングの内容を評価 
    result = await evaluator.evaluate(result=target_result, context=context)

    # スコアが閾値を上回ることを確認
    assert result.score >= result.threshold_score

一方で、実装と評価が密結合すぎたという反省点がありました。

当時のテストが、APIの振る舞いのテストというよりも実装の中身に依存する形のテストになっていて、実装が変更された際に追従する必要がありました。

これによってメンテナンスコストが大きくなっていました。

元々e2eテストの基盤がなかったため手っ取り早く実装できる方法をとっていましたが、理想的にはAPIを複数回呼び出すE2Eテストのような形の方が良かったと思ってます。

また、評価項目がぼんやりしている状態でLLM as a Judgeを作ってしまうとテストが成功したり失敗したりするFlakyな状態になってしまい、かえってノイズになってしまう問題も起こりうるので評価項目を先にしっかりと固めておくのがおすすめです。


4. Observabilityツール × 人の目のハイブリッド評価(ベータ版リリース〜)

ベータ版のリリース後、実際のユーザーのログ(オンラインデータ)が溜まり始めると、評価のフェーズが一段階進みました。

mentoでは DatadogのLMM ObservabilityOpik といったObservability(可視化)ツールを導入しはじめました。

このツールの導入は、導入は簡単ながらトレーサビリティがすごく上がりました。

例えばOpikだと

  from opik.integrations.openai import track_openai

  client = track_openai(AsyncOpenAI(api_key=api_key))

のようにOpenAIへのリクエストをラップしてトレースしたり

  import opik
  from opik import opik_context

  class CreateCoachingSessionService:
      @staticmethod
      @opik.track(
          name="CreateCoachingSessionService",
      )
      async def run(sponsor_employee_id: str):
          # セッションIDでトレースをグループ化
          opik_context.update_current_trace(thread_id=coaching_session.coaching_session_id)![](https://storage.googleapis.com/zenn-user-upload/47e803ce7f89-20260127.png)
          # ...

のようにthread_idを付与することでAIコーチングの複数回のやり取りを一つのスレッドとして共通化することが簡単にできます。

AIコーチングの一連のやり取りを一つもまとまりの単位で可視化、点数付けをすることができ、品質改善のための分析が格段に楽になりました!

mentoではOpikのEvaluation機能(実データでのLLM as a Judge)で全件の対話をLLMが自動で採点し、週次のレビューでその採点を人間がチェックしていきました。

mento社内には複数人のプロコーチが社員として在籍しているので、LLMの点数付けしたものの中で「極端に低いもの」や「逆に高すぎるもの」を抽出し、社内のプロコーチと一緒に目視で確認していました。(AIコーチングの内容は個人や会社の情報を含むため、適切に権限を管理して取り扱っています)

ここで「プロコーチの点数」と「LLM(Judge)の点数」を照らし合わせ、「AIコーチのプロンプトを直すべきか」あるいは「評価基準(Judge)の方がズレているので修正すべきか」 を判断しました。

このループを何度も回せたことがAIコーチングの品質改善に大きく寄与しました。


5. ローカルでの「20ペルソナ・ストレステスト」

リリース後は、CI(継続的インテグレーション)上のLLM as a JudgeAIコーチングの品質をチェックしています。

しかし実行時間の関係でCIでチェックするのは主要なペルソナに絞っています。

一方、プロンプトの大幅な変更の際には20ほどのペルソナで「負荷テスト」のような位置付けでローカルでテストを動かしています。

「あまり喋ってくれない人」「意地悪な質問をする人」など、約20パターンのペルソナを用意することでN1(自分自身の主観)では気づけなかった、特定の属性に対する性能劣化を防ぐことができています。


まとめ

LLMプロダクトの開発において、評価の仕組みは一度作って完成ではありません。

CLI → LLM as a Judge → Observabilityツールでオンラインテスト → CI / ローカルでの負荷テスト

のようにプロダクトの成長に合わせて評価もアップデートさせていきました

これからAIプロダクトを作る皆さんも、まずは小さなCLIや、点数付けの基準作りから始めてみてはいかがでしょうか。

Discussion