| name | langfuse-core-workflow-b |
| description | Execute Langfuse secondary workflow: Evaluation and scoring.
Use when implementing LLM evaluation, adding user feedback,
or setting up automated quality scoring for AI outputs.
Trigger with phrases like "langfuse evaluation", "langfuse scoring",
"rate llm outputs", "langfuse feedback", "evaluate ai responses".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Langfuse Core Workflow B: Evaluation & Scoring
Overview
Secondary workflow for Langfuse: evaluating and scoring LLM outputs for quality monitoring.
Prerequisites
- Completed
langfuse-core-workflow-a (tracing setup)
- Understanding of evaluation metrics
- (Optional) Evaluation framework like RAGAS or custom evaluators
Instructions
Step 1: Add Manual Scores to Traces
import { Langfuse } from "langfuse";
const langfuse = new Langfuse();
async function scoreTrace(
traceId: string,
scores: { name: string; value: number; comment?: string }[]
) {
for (const score of scores) {
await langfuse.score({
traceId,
name: score.name,
value: score.value,
comment: score.comment,
});
}
}
const trace = langfuse.trace({ name: "chat" });
await scoreTrace(trace.id, [
{ name: "relevance", value: 0.9, comment: "Directly answers question" },
{ name: "accuracy", value: 0.85, comment: "Minor factual imprecision" },
{ name: "helpfulness", value: 1.0, comment: "Very helpful response" },
]);
Step 2: Capture User Feedback
async function handleFeedback(req: {
traceId: string;
rating: "good" | "bad";
comment?: string;
}) {
await langfuse.score({
traceId: req.traceId,
name: "user-feedback",
value: req.rating === "good" ? 1 : 0,
comment: req.comment,
dataType: "BOOLEAN",
});
return { success: true };
}
function FeedbackButtons({ traceId }: { traceId: string }) {
const submitFeedback = async (rating: "good" | "bad") => {
await fetch("/api/feedback", {
method: "POST",
body: JSON.stringify({ traceId, rating }),
});
};
return (
<div>
submitFeedback("good")}>👍
submitFeedback("bad")}>👎
);
}
Step 3: Automated Evaluation with LLM-as-Judge
async function evaluateWithLLM(
traceId: string,
question: string,
answer: string,
context: string[]
) {
const evaluationPrompt = `
You are evaluating an AI assistant's response.
Question: ${question}
Context provided: ${context.join("\n")}
AI Response: ${answer}
Rate the response on these criteria (0.0 to 1.0):
1. Relevance: Does it answer the question?
2. Accuracy: Is it factually correct based on context?
3. Completeness: Does it fully address the question?
Respond in JSON: {"relevance": 0.X, "accuracy": 0.X, "completeness": 0.X}
`;
const evalTrace = langfuse.trace({
name: "llm-evaluation",
metadata: { evaluatedTraceId: traceId },
tags: ["evaluation", "automated"],
});
const generation = evalTrace.generation({
name: "judge-response",
model: "gpt-4",
input: evaluationPrompt,
});
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: [{ role: "user", content: evaluationPrompt }],
: { : },
});
scores = .(response.[]..!);
generation.({
: scores,
: {
: response.?. || ,
: response.?. || ,
},
});
( [name, value] .(scores)) {
langfuse.({
traceId,
: ,
: value ,
: ,
});
}
scores;
}
Step 4: Score Specific Generations
async function scoreGeneration(
traceId: string,
observationId: string,
scores: Record<string, number>
) {
for (const [name, value] of Object.entries(scores)) {
await langfuse.score({
traceId,
observationId,
name,
value,
});
}
}
const trace = langfuse.trace({ name: "multi-step" });
const gen1 = trace.generation({ name: "step-1", model: "gpt-4" });
gen1.end({ output: result1 });
const gen2 = trace.generation({ name: "step-2", model: "gpt-4" });
gen2.end({ output: result2 });
await scoreGeneration(trace.id, gen1.id, { quality: });
(trace., gen2., { : });
Step 5: Dataset-Based Evaluation
async function runDatasetEvaluation(datasetName: string) {
const dataset = await langfuse.getDataset(datasetName);
const results = [];
for (const item of dataset.items) {
const trace = langfuse.trace({
name: "dataset-eval",
metadata: { datasetItemId: item.id },
});
const output = await runPipeline(item.input);
trace.update({ output });
const score = compareOutputs(output, item.expectedOutput);
await langfuse.score({
traceId: trace.id,
name: "dataset-match",
value: score,
});
await langfuse.datasetItem.link({
datasetItemId: item.id,
traceId: trace.id,
});
results.({ : item., output, score });
}
results;
}
Output
- Manual scoring for human evaluation
- User feedback capture (thumbs up/down)
- Automated LLM-as-judge evaluation
- Generation-level scoring
- Dataset-based evaluation runs
Error Handling
| Issue | Cause | Solution |
|---|
| Score not appearing | Wrong traceId | Verify trace ID exists |
| Invalid score value | Out of range | Use 0.0-1.0 or define range |
| Feedback delayed | Not flushed | Call flushAsync() if needed |
| Eval trace missing | Not linked | Use metadata to link traces |
Examples
RAGAS Integration (Python)
from langfuse import Langfuse
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy
langfuse = Langfuse()
async def ragas_evaluation(trace_id: str, question: str, answer: str, contexts: list):
result = evaluate(
questions=[question],
answers=[answer],
contexts=[contexts],
metrics=[faithfulness, answer_relevancy],
)
for metric_name, score in result.items():
langfuse.score(
trace_id=trace_id,
name=f"ragas/{metric_name}",
value=score,
comment="RAGAS automated evaluation",
)
return result
Evaluation Dashboard Query
const traces = await langfuse.fetchTraces({
name: "chat",
fromTimestamp: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
toTimestamp: new Date(),
});
const scoresByName: Record<string, number[]> = {};
for (const trace of traces.data) {
for (const score of trace.scores || []) {
if (!scoresByName[score.name]) scoresByName[score.name] = [];
scoresByName[score.name].push(score.value);
}
}
const averages = Object.fromEntries(
Object.entries(scoresByName).map(([name, values]) => [
name,
values.reduce((a, b) => a + b, 0) / values.,
])
);
.(, averages);
Resources
Next Steps
For debugging issues, see langfuse-common-errors.