Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Instrument LLM apps with Langfuse tracing, then use traces, scores, and datasets to test in production, run evaluations on real traffic, catch regressions, and close the loop from incident to golden dataset.
.claude/skills/pramoddutta-langfuse-llm-observability-testing/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 15% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 33% | 0% |
You are an expert AI quality engineer specializing in Langfuse. When the user asks you to instrument, monitor, or test an LLM application using traces and production data, follow these instructions.
bashpip install langfuse # python npm install langfuse # typescript export LANGFUSE_PUBLIC_KEY=pk-... export LANGFUSE_SECRET_KEY=sk-... export LANGFUSE_HOST=https://cloud.langfuse.com # or self-hosted URL
pythonfrom langfuse import Langfuse, observe langfuse = Langfuse() @observe() # creates a trace per call def answer(user_id: str, session_id: str, query: str): langfuse.update_current_trace(user_id=user_id, session_id=session_id, tags=["support-bot", "prod"]) chunks = retrieve(query) # decorate with @observe() too: becomes a span reply = generate(query, chunks) # generations auto-capture model, tokens, cost return reply
Decorate retrieval, reranking, generation, and tool calls separately; a flat trace cannot localize failures. Wrappers/integrations exist for OpenAI, LangChain, LlamaIndex, and the Vercel AI SDK; prefer them over manual spans.
python# 1. User feedback from the app (thumbs up/down) langfuse.create_score(trace_id=trace_id, name="user-feedback", value=0, comment="wrong policy quoted") # 2. Automated LLM-as-judge on sampled traces (configure evaluators in the UI # or run your own job): from my_judges import faithfulness_judge for trace in fetch_traces(tags=["support-bot"], sample=0.1): score = faithfulness_judge(trace.input, trace.output, trace.metadata["contexts"]) langfuse.create_score(trace_id=trace.id, name="faithfulness", value=score) # 3. Human annotation queues in the UI for calibration batches
Alerting policy: dashboard the 7-day moving average per score name; investigate any sustained drop even inside "acceptable" range, since judge drift and product drift look identical until triaged.
The core testing workflow: bad trace -> dataset item -> offline eval -> CI gate.
python# 1. Curate: add a failing production trace to a dataset langfuse.create_dataset(name="support-golden") langfuse.create_dataset_item( dataset_name="support-golden", input={"query": "Can I get a refund after 45 days?"}, expected_output="No; refund window is 30 days. Offer credit options.", source_trace_id=bad_trace.id, # provenance ) # 2. Experiment: run a candidate change against the dataset dataset = langfuse.get_dataset("support-golden") for item in dataset.items: with item.run(run_name="prompt-v9") as root: output = my_app.answer_candidate(item.input["query"]) root.update(output=output) root.score(name="correctness", value=judge(output, item.expected_output)) # 3. Compare runs in the UI (prompt-v8 vs prompt-v9) or via API in CI
CI gate pattern: nightly job runs the current build against the dataset, pushes scores as a run, and fails if aggregate correctness drops below threshold or below the previous run by more than 2 points.
Store prompts in Langfuse prompt management with labels (production, staging). Testing rules: every prompt version change runs the dataset experiment BEFORE the production label moves; traces record which prompt version served each request, so incidents bisect to prompt versions in seconds.
| View | Bug class it catches | |---|---| | Traces filtered by low user-feedback | Real failures, source for dataset items | | Score trend by prompt/model version | Regressions from "harmless" prompt edits | | Cost per trace over time | Token explosions from context stuffing | | Latency percentiles per span | Slow retrieval hiding behind fast generation | | Sessions with high turn count | Loops, users re-asking because answers fail |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-14 | fail→fail | 17,151 | 19,011 | +11% | 1 | 1 | 0% | 1,565 | 3,195 | +104% | 0 | 0 | — |
case-13 | pass→pass | 19,186 | 6,482 | -66% | 1 | 1 | 0% | 2,015 | 2,261 | +12% | 0 | 0 | — |
case-01 | fail→pass | 21,569 | 23,844 | +11% | 1 | 1 | 0% | 3,957 | 4,711 | +19% | 0 | 0 | — |
case-02 | fail→pass | 32,121 | 21,246 | -34% | 1 | 1 | 0% | 4,504 | 5,170 | +15% | 0 | 0 | — |
case-03 | pass→pass | 23,710 | 15,599 | -34% | 1 | 1 | 0% | 3,320 | 4,129 | +24% | 0 | 0 | — |
case-04 | pass→pass | 19,277 | 20,535 | +7% | 1 | 1 | 0% | 3,587 | 4,162 | +16% | 0 | 0 | — |
case-05 | fail→pass | 40,611 | 15,441 | -62% | 1 | 1 | 0% | 2,595 | 3,242 | +25% | 0 | 0 | — |
case-06 | fail→pass | 51,617 | 18,839 | -64% | 1 | 1 | 0% | 2,289 | 3,739 | +63% | 0 | 0 | — |
case-07 | fail→pass | 22,898 | 17,587 | -23% | 1 | 1 | 0% | 2,894 | 3,845 | +33% | 0 | 0 | — |
case-08 | fail→pass | 19,837 | 15,547 | -22% | 1 | 1 | 0% | 2,778 | 3,357 | +21% | 0 | 0 | — |
case-09 | fail→pass | 21,834 | 16,145 | -26% | 1 | 1 | 0% | 3,087 | 3,123 | +1% | 0 | 0 | — |
case-10 | pass→pass | 10,339 | 13,466 | +30% | 1 | 1 | 0% | 1,505 | 2,578 | +71% | 0 | 0 | — |
case-11 | pass→pass | 12,871 | 10,977 | -15% | 1 | 1 | 0% | 1,155 | 2,327 | +101% | 0 | 0 | — |
case-12 | pass→pass | 21,746 | 19,535 | -10% | 1 | 1 | 0% | 2,476 | 3,582 | +45% | 0 | 0 | — |
case-15 | fail→pass | 16,366 | 14,154 | -14% | 1 | 1 | 0% | 1,556 | 2,548 | +64% | 0 | 0 | — |
case-16 | pass→pass | 21,739 | 17,052 | -22% | 1 | 1 | 0% | 2,323 | 3,026 | +30% | 0 | 0 | — |
case-17 | pass→pass | 24,893 | 22,163 | -11% | 1 | 1 | 0% | 2,832 | 4,003 | +41% | 0 | 0 | — |
case-18 | pass→pass | 22,706 | 22,113 | -3% | 1 | 1 | 0% | 2,535 | 4,082 | +61% | 0 | 0 | — |
case-19 | pass→pass | 14,455 | 9,271 | -36% | 1 | 1 | 0% | 1,390 | 2,756 | +98% | 0 | 0 | — |
case-20 | pass→pass | 14,611 | 12,704 | -13% | 1 | 1 | 0% | 2,134 | 3,220 | +51% | 0 | 0 | — |
case-21 | pass→pass | 13,646 | 18,724 | +37% | 1 | 1 | 0% | 2,190 | 3,472 | +59% | 0 | 0 | — |
case-22 | pass→pass | 17,419 | 23,654 | +36% | 1 | 1 | 0% | 3,017 | 4,587 | +52% | 0 | 0 | — |
case-23 | pass→pass | 24,474 | 24,409 | -0% | 1 | 1 | 0% | 3,252 | 4,357 | +34% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 22 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +35 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.