💬

Langfuse × RAGAS:RAGOpsの実践ガイド

に公開

RAGの運用フェーズにおいて“評価と改善のループ”をどう構築するか?
本記事ではLangfuseとRAGASを組み合わせ、実際にRAGOpsを実践した方法をご紹介します。

はじめに

Hacobuでは、MOVOサービス全般に関する困り事を解決するためのQAエージェント(Agentic RAG)を社内向けに提供しています[1]。前回のブログにも記載した通り、幾つかの課題がありました。

課題感

  • テクニカルサポート側
    • FAQに情報があるにも関わらず、適切な回答が得られない場合の原因調査に手間がかかる
  • IT部門側
    • レスポンス時間・コストの可視化が必要
    • 現状より回答精度が悪化しないかが心配で、プロンプトを気軽に変えられない

解決策

前述の課題を解決するために、LangfuseとRAGASを導入することで、RAGOpsを実践することにしました。

RAGOpsとは

RAGOpsとは、LLMOpsを拡張した概念で、RAGの可観測性を重視することが提案されており、機能としては、以下があります[2]。

  • RAGの動作監視と異常検出
  • 質問、回答、コンテキスト(検索結果)などのデータをログに記録
  • プロンプトなどの成果物のステータスと更新履歴を追跡

これにより、インシデントが発生した場合には、責任の所在を明らかにできます。

RAGOpsを実践する方法として、幾つかのツールがありますが、今回はセルフホストが可能なLangfuse v2を選択しました。Langfuseの最新版はv3なのですが、RedisやClickhouseなどのデータストアが必要であり、スケーラブルではあるものの、運用が煩雑になるため、まずはデータストアがPostgreSQLのみであるv2を導入しました。

Langfuse v2を導入することで、以下のことが実現できます。

  • 可観測性:エージェントの内部状態や意思決定を可視化でき、問題がより発見しやすい
  • トレーサビリティ:エージェントの入出力を追跡でき、問題の原因を迅速に特定でき、再現性のあるテストが可能です
  • 開発と運用の効率性:プロンプトを管理できるだけでなく、RAGAS(RAG Assessment)を導入することで、RAGの回答精度も監視できます

RAGASとは

RAGASとは、LLMを用いてRAGシステムを評価できるライブラリです[3]。回答結果だけでなく、検索結果も評価できます。現時点では、8つの評価指標が規定されています。

  1. Faithfulness:生成された回答が与えられたコンテキストに基づいているかを図る指標
  2. Answer relevancy:生成された回答が元の質問にどれだけ適切であるかを評価する指標
  3. Context recall:取得されたコンテキストが正解とどの程度一致しているかを測定する指標
  4. Context precision:コンテキスト内に存在するすべての正解に関連する関連項目が上位にランク付けされているかどうかを評価する指標
  5. Context utilization:コンテキスト内に存在する回答に関連するすべてのチャンクが上位にランク付けされているかどうかを評価する指標
  6. Context entity recall:正解にコンテキストのエンティティがどの程度含まれているかを測る指標
  7. Noise Sensitivity:コンテキストに含まれる誤った情報を取り込んでしまっているかを測る指標
  8. Summarization Score:コンテキストから重要な情報をどれだけうまく取り入れられているかを評価する指標

これらの指標は、「質問」「回答」「コンテキスト(ベクトルDB検索結果)」「正解」の4つの組み合わせを用いて算出する指標です。ただし、実際の運用においては、ユーザからの質問に対する正解を付与するのは非常に手間であるため、重要な情報や誤った情報といった、生身の人間しか判断できないような情報を用いずに算出できる次の3つの指標のみを実装しました。

1.Faithfulness
2.Answer Relevancy
5.Context Utilization(Langfuseではllm_context_presicion_without_referenceと表示)

Langfuseとは

Langfuse[4]は、LLMを活用したアプリケーション開発における、開発効率向上とアプリケーションの品質向上の両方を実現するオープンソースプラットフォームです。セルフホストだけでなく、有償のSaaSとしても提供されています。Langfuseの主な機能は以下の通りです。

  • モニタリング
    • トレース
    • リアルタイム・メトリクス
    • フィードバック
  • アナリティクス
    • 評価
    • テスト
    • ユーザ導線
  • デバッグ
    • ログ収集
    • エラートラッキング

以降では、実際の画面を示しながら、RAGOpsをどのように実践できるかを示します。

トレース画面

まずはトレース画面です。質問に対する回答に加えて、レスポンス時間とコストが表示されています。

トレースの検索結果画面

次はトレース画面の検索結果画面です。検索結果に加えて、応答時間も表示されています。もし検索結果一覧に、回答となるべくQAドキュメントが含まれない場合、QAドキュメントの内容を見直すべきであることを検知できます。

RAGASの画面

そして、RAGASの画面です。実装した3つの評価指標がスコアとして表示されています。評価ポリシーに応じて、それぞれ閾値を設定して、監視することができます。

from langchain_openai.chat_models import ChatOpenAI
from langchain_openai.embeddings import OpenAIEmbeddings
from langfuse import Langfuse
from ragas.dataset_schema import SingleTurnSample
from ragas.run_config import RunConfig
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper
from ragas.metrics.base import MetricWithLLM, MetricWithEmbeddings
from ragas.metrics import (
    Faithfulness,
    LLMContextPrecisionWithoutReference,
    ResponseRelevancy,
)
from ragas.metrics.critique import SUPPORTED_ASPECTS, harmfulness

metrics = [
    Faithfulness(),
    ResponseRelevancy(),
    LLMContextPrecisionWithoutReference(),
]
llm = ChatOpenAI()
emb = OpenAIEmbeddings()

init_ragas_metrics(
    metrics,
    llm=LangchainLLMWrapper(llm),
    embedding=LangchainEmbeddingsWrapper(emb),
)

def init_ragas_metrics(metrics, llm, embedding):
    for metric in metrics:
        if isinstance(metric, MetricWithLLM):
            metric.llm = llm
        if isinstance(metric, MetricWithEmbeddings):
            metric.embeddings = embedding
        run_config = RunConfig()
        metric.init(run_config)

def evaluate(question, answer, contexts)
    langfuse = Langfuse()
    langfuse.auth_check()
    trace = langfuse.trace(
        name="RAGAS",
        session_id=UNIQUE_ID,
    )

    trace.span(
        name="retrieval",
        input={"question": question},
        output={"contexts": contexts},
    )

    trace.span(
        name="generation",
        input={"question": question, "contexts": contexts},
        output={"answer": answer},
    )

    scores = {}
    for m in metrics:
        sample = SingleTurnSample(
            user_input=question,
            retrieved_contexts=contexts,
            response=answer,
        )
        scores[m.name] = await m.single_turn_ascore(sample)

        for m in metrics:
            trace.score(name=m.name, value=scores[m.name])

RAGASを通知するサンプルコードです。3つの評価指標をメトリクスとして定義し、それぞれトレースのスコアとして通知しています。

プロンプトの設定画面

最後は、プロンプトの設定画面です。プロンプト内に変数を定義できますが、Hacobuでは変数を利用せず、LCEL(LangChain Expression Language)のchainで渡す変数を含めて定義しています。

from langfuse import Langfuse
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

langfuse = Langfuse()
prompt = ChatPromptTemplate.from_template(
    langfuse.get_prompt("rag-prompt", label="latest").compile()
)
chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

プロンプトの呼び出すサンプルコードです。引数のlabelを変えることで、異なるプロンプトを引き出すことができます。

まとめ

LangfuseとRAGASを導入することで、RAGOpsを実践できるようになり、これまで課題であった以下の問題を解決することができました。

  • 適切な回答が得られない場合の原因究明が非常に容易になった
  • レスポンス時間とコストを可視化できた
  • 回答精度を定量的に評価できるようになり、様々なプロンプトを試すことができるようになった

参考文献

[1] Agentic RAGに進化した社内向けQAエージェント
[2] LLM テクニックの習得: LLMOps
[3] RAGAS
[4] Langfuse

Hacobuテックブログ

Discussion