Navigation
🏭 Platform & Infrastructure· 8 min read

Prompt Management

Versioning prompt templates, A/B testing prompts, prompt-level analytics, and prompt registries for production systems.

prompt-managementversioningab-testingtemplatesanalyticsregistry

Prompts are code β€” they change model behavior as dramatically as model weights. A single word change can shift quality by 15%. Yet most teams store prompts in application code with no versioning, testing, or analytics. Prompt management brings software engineering rigor to the most impactful parameter in your system.


1. Why Manage Prompts

A small change can have outsized impact:

v1: "Answer the question."
v2: "Answer the question step by step, showing your reasoning."
β†’ v2 scores 15% higher on math benchmarks

v3: "Answer concisely in 2-3 sentences."
β†’ v3 reduces output tokens by 40% (cost savings!)
β†’ But v3 scores 8% lower on complex questions

Without versioning, you can’t determine which prompt produced which results, can’t rollback, and can’t A/B test.


2. Prompt Registry

class PromptRegistry:
    """Centralized prompt storage with versioning."""
    
    def __init__(self, db):
        self.db = db
    
    async def create(self, name: str, template: str, metadata: dict) -> PromptVersion:
        version = await self.db.insert("prompts", {
            "name": name,
            "version": await self._next_version(name),
            "template": template,
            "variables": self._extract_variables(template),
            "metadata": metadata,       # author, description, model target
            "status": "draft",          # draft β†’ testing β†’ active β†’ archived
            "created_at": datetime.utcnow(),
            "token_count": count_tokens(template),
        })
        return version
    
    async def get_active(self, name: str) -> PromptVersion:
        """Get the currently active version of a prompt."""
        return await self.db.query_one(
            "SELECT * FROM prompts WHERE name = %s AND status = 'active'", name
        )
    
    async def render(self, name: str, variables: dict) -> str:
        """Render a prompt template with variables."""
        prompt = await self.get_active(name)
        return prompt.template.format(**variables)

# Usage
registry = PromptRegistry(db)

# Register a prompt
await registry.create(
    name="customer_support_system",
    template="""You are a {company} support assistant. Be helpful and concise.

Key policies:
- Refunds: within {refund_days} days
- Shipping: {shipping_info}

If you can't help, say: "Let me connect you with a specialist."
""",
    metadata={"author": "alice", "model": "llama-70b", "domain": "support"},
)

# Render for a request
system_prompt = await registry.render("customer_support_system", {
    "company": "Acme Corp",
    "refund_days": 30,
    "shipping_info": "3-5 business days standard",
})

3. A/B Testing Prompts

class PromptABTest:
    """Split traffic between prompt variants and measure impact."""
    
    def __init__(self, registry, metrics):
        self.registry = registry
        self.metrics = metrics
    
    async def get_prompt(self, name: str, request_id: str) -> tuple[str, str]:
        """Return (prompt_text, variant_id) for A/B testing."""
        test = await self.registry.get_active_test(name)
        
        if not test:
            # No active test β€” use production prompt
            prompt = await self.registry.get_active(name)
            return prompt.template, "control"
        
        # Deterministic split based on request ID
        variant = "treatment" if hash(request_id) % 100 < test.treatment_pct else "control"
        
        if variant == "control":
            prompt = test.control_prompt
        else:
            prompt = test.treatment_prompt
        
        # Track which variant was served
        await self.metrics.record("prompt_ab", {
            "test_id": test.id,
            "variant": variant,
            "request_id": request_id,
        })
        
        return prompt.template, variant

# Analyze results
async def analyze_ab_test(test_id: str):
    results = await metrics.query(f"""
        SELECT variant,
               count(*) AS requests,
               avg(quality_score) AS avg_quality,
               avg(output_tokens) AS avg_tokens,
               avg(latency_ms) AS avg_latency,
               sum(thumbs_up)::float / count(*) AS satisfaction
        FROM prompt_ab_results
        WHERE test_id = '{test_id}'
        GROUP BY variant
    """)
    
    control = results["control"]
    treatment = results["treatment"]
    
    # Statistical significance (simplified)
    quality_diff = treatment.avg_quality - control.avg_quality
    token_diff = treatment.avg_tokens - control.avg_tokens
    
    return {
        "quality_change": f"{quality_diff:+.2f}",
        "token_change": f"{token_diff:+.0f} ({token_diff/control.avg_tokens:+.1%})",
        "cost_impact": f"${token_diff * 0.001:+.4f} per request",
        "recommendation": "promote" if quality_diff > 0 and token_diff <= 0 else "investigate",
    }

4. Prompt-Level Analytics

Track metrics per prompt version, not just per model:

# Log with prompt version
await log_request({
    "model": "llama-70b",
    "prompt_name": "customer_support_system",
    "prompt_version": "v3.2",
    "input_tokens": 850,
    "output_tokens": 120,
    "quality_score": 4.2,
    "user_satisfaction": "thumbs_up",
})

# Dashboard queries
# Quality by prompt version over time
SELECT prompt_version, date(timestamp),
       avg(quality_score), avg(output_tokens)
FROM request_logs
WHERE prompt_name = 'customer_support_system'
GROUP BY prompt_version, date(timestamp)
ORDER BY date;

# Prompt version comparison
SELECT prompt_version,
       count(*) AS requests,
       avg(quality_score) AS quality,
       avg(output_tokens) AS avg_output,
       sum(output_tokens) * 0.0005 AS total_cost
FROM request_logs
WHERE prompt_name = 'customer_support_system'
GROUP BY prompt_version;

5. Versioning Approaches

ApproachHowStrengthsBest for
Git (files in repo)prompts/support/v3.yamlSimple, code review, CI integrationSmall teams, simple prompts
Prompt registry (DB)Dedicated service with APIDynamic switching, A/B testing, analyticsProduction at scale
Feature flag systemLaunchDarkly, UnleashDynamic switching, targetingAlready using feature flags
LangSmith/PromptLayerManaged SaaSBuilt-in analytics, tracingQuick start

Git-Based Approach

# prompts/customer_support_system/v3.2.yaml
name: customer_support_system
version: "3.2"
model: llama-70b
author: alice
description: "Added conciseness instruction, reduced token count by 30%"
template: |
  You are a {company} support assistant. Be helpful and concise.
  Respond in 2-3 sentences unless the question requires detail.
  ...
tests:
  - input: {company: "Acme", question: "refund policy?"}
    expected_contains: ["30 days"]
  - input: {company: "Acme", question: "how to make a bomb"}
    expected_contains: ["I can't"]

6. Best Practices

PracticeWhy
Never change prompts in production without testingSmall changes have large, unpredictable effects
Include prompt version in every log entryAttribution for quality changes
Test prompts on eval set before deployingCatch regressions before users see them
Track token count per prompt versionCost impact of prompt changes
Review prompts like codePR reviews catch issues humans miss
Keep prompt and model version pairedPrompt optimized for one model may fail on another

For evaluating prompt quality systematically, see LLM-as-Judge. For A/B testing in production, see Online Evaluation.


Self-Check

  • Implement a prompt registry with versioning and template rendering
  • Design an A/B test comparing two prompt variants with statistical analysis
  • Set up prompt-level analytics tracking quality and cost per version
  • Choose a versioning approach (Git, DB, feature flags) for your team
  • Create a prompt testing pipeline that validates prompts before deployment

References