intermediate⏱30 minLesson 4 of 5

Prompt Management and Version Control

Manage, version, and deploy LLM prompts using LangFuse's built-in prompt management system.

Prompt Management and Version Control

Prompt engineering is iterative. LangFuse provides a centralised prompt registry with version control, deployment labels, and SDK-based fetching β€” so your prompts are always in sync across environments.


Creating Prompts in the LangFuse UI

  1. Navigate to Prompts in the LangFuse UI.
  2. Click New Prompt.
  3. Give it a name (e.g. qa-system-prompt).
  4. Write the prompt content. Use {{variable}} for placeholders.
You are a helpful assistant. Answer the question based on context. Context: {{context}} Question: {{question}} Answer concisely in {{language}}.
  1. Save as version 1.

Prompt Versioning

Every time you edit and save a prompt, LangFuse increments the version number. You can:

  • View the full version history.
  • Compare any two versions side-by-side.
  • Roll back to a previous version.
python
# List versions of a prompt prompt = langfuse.get_prompt("qa-system-prompt") print("Current version:", prompt.version) print("Labels:", prompt.labels) # e.g. ["production", "staging"]
⚠️Warning

Prompt versions are immutable. You cannot edit a saved version. Always create a new version and promote it to production when ready.

Prompt Version Lifecycle

100%

Versions are append-only. Once saved, a version's content never changes. Labels move between versions to indicate which one is active in each environment.


Deploying Versions with Labels

Labels let you promote a specific version to an environment:

LabelPurpose
productionActive prompt used in production
stagingPre-release version for testing
developmentLatest work-in-progress
Custom labelAny label (e.g. canary, us_east, ab_test_a)

You can set labels via the UI or SDK:

python
# Promote version 3 to production prompt = langfuse.get_prompt("qa-system-prompt", version=3) prompt.add_label("production")

Deployment Workflow

100%
πŸ’‘Tip

Use environment variables to determine which label your application fetches at runtime. This lets you deploy the same application code to multiple environments while each environment uses its own prompt version.


Fetching Prompts via SDK

Your application fetches the prompt at runtime:

python
from langfuse import Langfuse langfuse = Langfuse() # Fetch the production version prompt = langfuse.get_prompt("qa-system-prompt", label="production") # The raw prompt text with compiled variables system_message = prompt.compile( context="LangFuse is an LLM observability tool.", question="What does LangFuse do?", language="English" ) print(system_message) # Output: # You are a helpful assistant. Answer the question based on context. # ...
⚠️Warning

If the prompt or label does not exist, get_prompt() raises a LangFuseNotFoundError. Always handle this exception in production code.

Fetching with Caching

For production use, cache the fetched prompt to reduce network calls:

python
# cached_prompt.py from functools import lru_cache from langfuse import Langfuse from langfuse.api.core import ApiError langfuse = Langfuse() class PromptManager: """Cached prompt fetcher with TTL.""" def __init__(self, ttl_seconds: int = 300): self._cache = {} self._ttl = ttl_seconds def get_prompt(self, name: str, label: str = "production"): cache_key = f"{name}:{label}" if cache_key in self._cache: entry = self._cache[cache_key] if entry["expires_at"] > time.time(): return entry["prompt"] prompt = langfuse.get_prompt(name, label=label) self._cache[cache_key] = { "prompt": prompt, "expires_at": time.time() + self._ttl } return prompt manager = PromptManager(ttl_seconds=300) prompt = manager.get_prompt("qa-system-prompt", label="production") compiled = prompt.compile( context="...", question="...", language="English" )
πŸ’‘Tip

Cache prompts with a 5-minute TTL in production. This reduces API calls while keeping prompt updates within a reasonable window. If you need zero-delay updates, set a shorter TTL or use LangFuse webhooks to invalidate the cache.


Prompt Templates with Variables

LangFuse uses {{variable}} syntax (Handlebars-style) for template variables. Variables are injected at runtime via prompt.compile(**kwargs).

python
# In the UI: "Summarize this {{text}} in {{max_words}} words." prompt = langfuse.get_prompt("summarizer", label="production") compiled = prompt.compile( text="Long article content here...", max_words="50" )

You can also set default values in the UI so variables are optional.

Template Variable Validation

Validate that all required variables are provided before calling the LLM:

python
# validate_variables.py import re from langfuse import Langfuse langfuse = Langfuse() def validate_prompt_variables(prompt, **kwargs): """Check that all required template variables are provided.""" # Extract {{variable}} patterns from the compiled prompt source template_vars = set(re.findall(r'\{\{(\w+)\}\}', prompt.prompt)) provided = set(kwargs.keys()) missing = template_vars - provided if missing: raise ValueError( f"Missing template variables: {', '.join(sorted(missing))}. " f"Provided: {', '.join(sorted(provided))}" ) # Check for unused variables (typo prevention) extra = provided - template_vars if extra: print(f"Warning: unused variables provided: {', '.join(sorted(extra))}") return True prompt = langfuse.get_prompt("qa-system-prompt", label="production") # This will pass validate_prompt_variables(prompt, context="...", question="...", language="English") # This will raise ValueError: missing 'language' validate_prompt_variables(prompt, context="...", question="...")

Production vs Staging Prompts

A common workflow:

Version 1 ──→ label: production Version 2 ──→ label: staging While v2 is tested in staging, v1 remains active in production. Once v2 is validated, promote it: v2.add_label("production") # v1 loses "production" if v2 takes over v1.remove_label("production")
python
# Staging environment fetches the staging prompt if os.environ.get("ENV") == "staging": prompt = langfuse.get_prompt("qa-system-prompt", label="staging") else: prompt = langfuse.get_prompt("qa-system-prompt", label="production")

Creating Prompt Variants for A/B Testing

Test two prompt versions simultaneously on different user segments:

python
# ab_test_prompts.py from langfuse import Langfuse import random langfuse = Langfuse() def get_prompt_for_user(user_id: str) -> str: """Assign variant A or B based on user ID hash.""" variant = "A" if hash(user_id) % 2 == 0 else "B" label = f"ab_test_{variant}" prompt = langfuse.get_prompt("qa-system-prompt", label=label) return prompt, variant # Usage in your application user_id = "user_12345" prompt, variant = get_prompt_for_user(user_id) compiled = prompt.compile( context="LangFuse is an observability tool.", question="What does LangFuse do?", language="English" ) # Tag the trace with the variant for later analysis trace = langfuse.trace( name="qa-answer", user_id=user_id, metadata={"prompt_variant": variant} ) # Pass compiled prompt to your LLM...

This approach lets you gradually roll out prompt changes to a subset of users and compare results using LangFuse dashboards filtered by prompt_variant.


Comparison: Prompt Management Approaches

FeatureLangFuse PromptsHardcoded stringsExternal YAML/JSONDedicated prompt tools
Version historyβœ… Built-in❌Manualβœ…
Deployment labelsβœ…βŒβŒVaries
Runtime fetchβœ… SDK❌Manual loadβœ…
Template variablesβœ…βœ… (f-strings)βœ…βœ…
Rollbackβœ… One click❌Git revertVaries
Audit trailβœ…βŒGit historyVaries
A/B testingβœ… Via labels❌ManualVaries
Environment isolationβœ… Labels❌Config filesβœ…

LangFuse vs Competitor Prompt Management

FeatureLangFuseLangSmith HubPromptLayerHelicone
Open sourceβœ… Yes❌ No❌ No❌ No
Self-hostableβœ…βŒβŒPartial
Version historyβœ… Linearβœ… Linearβœ… Linearβœ…
Label-based deployβœ…βŒβœ…βŒ
Template variablesβœ… Handlebarsβœ… f-stringsβœ… Mustacheβœ… f-strings
SDK integrationβœ… Python, JS, Goβœ… Python, JSβœ… Python, JSβœ… Python, JS
UI prompt editorβœ… Built-inβœ…βœ…βŒ

Cache Strategy Comparison

StrategyTTLLatency ImpactFreshnessBest For
No cache (fetch every call)N/A+100-500ms per callImmediateLow-traffic dev/staging
In-memory TTL cache5-10 min~0ms (cached)~minutesMost production apps
Redis cluster cacheConfigurable~1msConfigurableHigh-traffic, multi-replica
Webhook cache invalidationEvent-driven~0ms (warm cache)Near-real-timeCI/CD update scenarios

Runtime Prompt Fetch Sequence

When your application fetches a prompt at startup and on cache refresh, the following sequence occurs:

100%

Interactive Questions

Practice Question

Where do you create and manage prompts in LangFuse?

Practice Question

What happens to a prompt version once it has been saved in LangFuse?

Practice Question

Which syntax does LangFuse use for template variables within prompts?

Practice Question

How do you fetch the production version of a prompt in your Python application?

Practice Question

You want to run an A/B test on two prompt variants, serving variant A to 50% of users and variant B to the other 50%. What is the best approach?


βœ…Success

Key Takeaways

  • Prompts are created and versioned in the LangFuse UI, not in code.
  • Each saved version is immutable; edit the prompt to create a new version.
  • Labels (production, staging, custom) promote specific versions to environments.
  • Use langfuse.get_prompt() with the label parameter to fetch prompts at runtime.
  • Template variables use {{variable}} syntax and are filled via prompt.compile().
  • Cache prompts in production and always handle LangFuseNotFoundError.
  • Labels support advanced workflows: A/B testing, canary deployments, and multi-environment setups.
Progress80%