Langfuse
Langfuse
Ragas와 Langfuse는 RAG(Retrieval-Augmented Generation) 파이프라인을 평가하고 모니터링하는 데 도움을 주는 강력한 조합이에요.
출처: 문서
본문
Langfuse란 무엇인가?
Langfuse ( GitHub )는 LLM 추적(tracing), 프롬프트 관리, 평가를 위한 오픈소스 플랫폼이에요. trace와 스팬에 점수를 매겨 RAG 파이프라인 성능에 대한 인사이트를 얻을 수 있어요. Langfuse는 OpenAI, LangChain 등을 포함한 다양한 통합을 지원해요.
Langfuse를 Ragas와 함께 사용했을 때의 주요 이점
- Trace 점수 매기기 : trace와 스팬에 점수를 매겨 RAG 파이프라인 성능에 대한 인사이트 제공
- 상세 분석 : trace를 세그먼트로 나누고 분석해 낮은 품질 점수를 식별하고 시스템 성능 개선
- 점수 보고 : 특정 사용 사례와 사용자 세그먼트에 대한 상세 보고서로 드릴다운
Ragas ( GitHub )는 trace/스팬, 특히 RAG 파이프라인에 대한 Model-Based Evaluation을 실행하는 데 도움을 주는 오픈소스 도구예요. Ragas는 RAG 파이프라인의 다양한 측면에 대해 reference-free 평가를 수행할 수 있어요. reference-free이기 때문에 평가를 실행할 때 ground-truth가 필요 없고, Langfuse로 수집한 프로덕션 trace에 대해 실행할 수 있어요.
시작하기
이 가이드는 Ragas와 Langfuse를 사용한 RAG 평가의 end-to-end 예시를 안내해요.
환경
Langfuse에 가입해 API 키를 받으세요.
import os
# get keys for your project from https://cloud.langfuse.com
os.environ["LANGFUSE_SECRET_KEY"] = "sk-..."
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-..."
# your openai key
# os.environ["OPENAI_API_KEY"] = "sk-..."
%pip install datasets ragas llama_index python-dotenv --upgrade
데이터
이 예시에서는 RAG 시스템을 쿼리하고 그 출력을 수집해 이미 준비된 데이터셋을 사용할 거예요. Langfuse에서 프로덕션 데이터를 가져오는 방법은 아래를 참고하세요.
데이터셋은 다음 컬럼을 담아요.
question: list[str] - RAG 파이프라인이 평가될 질문들answer: list[str] - RAG 파이프라인에서 생성되어 사용자에게 주어진 답변contexts: list[list[str]] - 질문에 답하기 위해 LLM에 전달된 컨텍스트들ground_truth: list[list[str]] - 질문에 대한 실제 정답. 단, 온라인 평가에서는 ground-truth 데이터에 접근할 수 없으므로 무시할 수 있어요.
from datasets import load_dataset
amnesty_qa = load_dataset("vibrantlabsai/amnesty_qa", "english_v2")["eval"]
amnesty_qa
Found cached dataset amnesty_qa (/home/jjmachan/.cache/huggingface/datasets/vibrantlabs___amnesty_qa/english_v2/2.0.0/d0ed9800191a31943ee52a5c22ee4305e28a33f5edcd9a323802112cff07cc24)
0%| | 0/1 [00:00<?, ?it/s]
Dataset({
features: ['question', 'ground_truth', 'answer', 'contexts'],
num_rows: 20
})
메트릭
이 예시에서는 Ragas 라이브러리의 다음 메트릭을 사용할 거예요.
faithfulness: 제공된 컨텍스트에 대한 생성된 답변의 사실적 일관성 측정answer_relevancy: 생성된 답변이 주어진 프롬프트에 얼마나 핵심적이고 관련 있는지 평가context precision: 컨텍스트에 존재하는 모든 ground-truth 관련 항목이 더 높은 순위에 있는지 평가하는 메트릭. 이상적으로는 모든 관련 청크가 최상위 순위에 나타나야 해요. 이 메트릭은 질문과 컨텍스트로 계산되며 값의 범위는 0~1로, 점수가 높을수록 정밀도가 좋아요.aspect_critique: 해로움(harmlessness), 정확성 같은 미리 정의된 측면에 따라 제출물을 평가하도록 설계됐어요. 또한 사용자는 자신의 특정 기준에 따라 제출물을 평가하는 측면을 자유롭게 정의할 수 있어요.
이 메트릭들과 동작 방식에 대해 더 알아보려면 문서를 확인하세요.
# import metrics
from ragas.metrics import faithfulness, answer_relevancy, context_precision
from ragas.metrics.critique import SUPPORTED_ASPECTS, harmfulness
# metrics you chose
metrics = [faithfulness, answer_relevancy, context_precision, harmfulness]
다음으로, 선택한 LLM과 임베딩으로 메트릭을 초기화해요. 이 예시에서는 OpenAI를 사용해요.
from ragas.run_config import RunConfig
from ragas.metrics.base import MetricWithLLM, MetricWithEmbeddings
# util function to init Ragas Metrics
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)
from langchain_openai.chat_models import ChatOpenAI
from langchain_openai.embeddings import OpenAIEmbeddings
# wrappers
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper
llm = ChatOpenAI()
emb = OpenAIEmbeddings()
init_ragas_metrics(
metrics,
llm=LangchainLLMWrapper(llm),
embedding=LangchainEmbeddingsWrapper(emb),
)
설정
Ragas로 model-based evaluation을 사용하는 방법은 2가지가 있어요.
- 각 Trace 점수 매기기 : 각 trace 항목에 대해 평가를 실행해요. RAG 파이프라인에 대한 각 호출이 어떻게 수행되는지 훨씬 잘 알 수 있지만 비용이 들 수 있어요
- 일괄 점수 매기기 : 이 방법에서는 주기적으로 trace의 임의 샘플을 가져와 점수를 매겨요. 비용을 줄이고 앱 성능의 대략적인 추정치를 주지만 중요한 샘플을 놓칠 수 있어요.
이 쿡북에서는 두 가지를 모두 설정하는 방법을 보여줄게요.
Trace 점수 매기기
단일 trace의 작은 예시를 가져와 Ragas로 점수를 매기는 방법을 볼게요. 먼저 데이터를 로드하세요.
row = amnesty_qa[0]
print("question: ", row["question"])
print("answer: ", row["answer"])
question: What are the global implications of the USA Supreme Court ruling on abortion?
answer: The global implications of the USA Supreme Court ruling on abortion can be significant, as it sets a precedent for other countries and influences the global discourse on reproductive rights. Here are some potential implications:
...
이제 앱을 계측하기 위해 Langfuse 클라이언트 SDK를 초기화해요.
from langfuse import Langfuse
langfuse = Langfuse()
선택한 메트릭으로 trace에 점수를 매기는 유틸리티 함수를 정의하고 있어요.
async def score_with_ragas(query, chunks, answer):
scores = {}
for m in metrics:
print(f"calculating {m.name}")
scores[m.name] = await m.ascore(
row={"question": query, "contexts": chunks, "answer": answer}
)
return scores
question, contexts, answer = row["question"], row["contexts"], row["answer"]
await score_with_ragas(question, contexts, answer)
calculating faithfulness
calculating answer_relevancy
Using 'context_precision' without ground truth will be soon depreciated. Use 'context_utilization' instead
calculating context_precision
calculating harmfulness
{'faithfulness': 0.0,
'answer_relevancy': 0.9999999999999996,
'context_precision': 0.9999999999,
'harmfulness': 0}
각 요청과 함께 점수를 계산해요. 아래에서는 다음 단계를 수행하는 더미 애플리케이션을 설명했어요.
- 사용자로부터 질문 받기
- 질문에 답하는 데 쓸 수 있는 데이터베이스나 벡터 스토어에서 컨텍스트 가져오기
- LLM에 질문과 컨텍스트를 전달해 답변 생성하기
이 모든 단계는 Langfuse의 단일 trace 안에서 스팬으로 로깅돼요. trace와 스팬에 대해 더 자세히 알고 싶다면 Langfuse 문서를 읽어 보세요.
# the logic of the dummy application is
# given a question fetch the correspoinding contexts and answers from a dict
import hashlib
def hash_string(input_string):
return hashlib.sha256(input_string.encode()).hexdigest()
q_to_c = {} # map between question and context
q_to_a = {} # map between question and answer
for row in amnesty_qa:
q_hash = hash_string(row["question"])
q_to_c[q_hash] = row["contexts"]
q_to_a[q_hash] = row["answer"]
# if your running this in a notebook - please run this cell
# to manage asyncio event loops
import nest_asyncio
nest_asyncio.apply()
from langfuse.decorators import observe, langfuse_context
from asyncio import run
@observe()
def retriver(question: str):
return q_to_c[question]
@observe()
def generator(question):
return q_to_a[question]
@observe()
def rag_pipeline(question):
q_hash = hash_string(question)
contexts = retriver(q_hash)
generated_answer = generator(q_hash)
# score the runs
score = run(score_with_ragas(question, contexts, answer=generated_answer))
for s in score:
langfuse_context.score_current_trace(name=s, value=score[s])
return generated_answer
question, contexts, answer = row["question"], row["contexts"], row["answer"]
generated_answer = rag_pipeline(amnesty_qa[0]["question"])
calculating faithfulness
calculating answer_relevancy
Using 'context_precision' without ground truth will be soon depreciated. Use 'context_utilization' instead
calculating context_precision
calculating harmfulness
Langfuse에서 점수 분석
Langfuse UI에서 점수를 분석하고 각 질문·사용자별 점수로 드릴다운할 수 있어요.
→ 아직 Langfuse를 사용하지 않나요? 인터랙티브 데모에서 대시보드를 살펴보세요.
점수 매기기는 blocking 이라서 점수가 계산되기를 기다리기 전에 생성된 답변을 먼저 보냈는지 확인하세요. 또는 score_with_ragas() 를 별도 스레드에서 실행하고 trace_id를 전달해 점수를 로깅할 수도 있어요.
리소스
- Ragas로 model-based evaluation을 실행하는 방법을 더 배우려면 Model-Based Evaluation 가이드를 확인하세요.
- 여기에서 LLM 애플리케이션을 분석하고 개선하는 방법을 배워 보세요.
피드백
피드백이나 요청이 있다면 GitHub Issue를 만들거나 Discord 커뮤니티에서 작업을 공유해 주세요.