Prompt Versioning: Create, Label, and Serve Prompt Versions
Create prompt templates, commit numbered versions, assign labels like production, and serve the right version at runtime via SDK or dashboard.
Create a prompt template, commit it as v1, serve it in your app, then commit a v2 with a different model configuration, evaluate it, promote it to production, and roll back, all through fi.prompt.
| Time | Difficulty | Package |
|---|---|---|
| 15 min | Beginner | futureagi + ai-evaluation |
- Future AGI account → app.futureagi.com
- API keys:
FI_API_KEYandFI_SECRET_KEY(see Get your API keys) - An
OPENAI_API_KEY(litellm routes thegpt-4o-minicalls through it) - Python 3.11+
Install
pip install futureagi ai-evaluation litellm
export FI_API_KEY="your-api-key"
export FI_SECRET_KEY="your-secret-key"
export OPENAI_API_KEY="your-openai-key"
Tutorial
Create a prompt via SDK
import os
from fi.prompt import Prompt
from fi.prompt.types import PromptTemplate, SystemMessage, UserMessage, ModelConfig
prompt_client = Prompt(
template=PromptTemplate(
name="support-response",
messages=[
SystemMessage(
content="You are a helpful customer support agent for TechStore. "
"Answer the customer's question clearly and professionally."
),
UserMessage(
content="Customer question: {{question}}"
),
],
model_configuration=ModelConfig(
model_name="gpt-4o-mini",
temperature=0.7,
max_tokens=1000,
),
),
fi_api_key=os.environ["FI_API_KEY"],
fi_secret_key=os.environ["FI_SECRET_KEY"],
)
# Create the prompt as a draft and commit it as v1
prompt_client.create()
prompt_client.commit_current_version(
message="Initial support prompt",
label="production",
)
print(f"Created: {prompt_client.template.name} ({prompt_client.template.version})")You should see:
Created: support-response (v1)Check the dashboard: Prompts (left sidebar) → open support-response → click History → the History drawer lists v1 with the production label.
Serve the prompt in your application
import os
import litellm
from fi.prompt import Prompt
def answer_question(question: str) -> str:
prompt = Prompt.get_template_by_name(
name="support-response",
label="production",
fi_api_key=os.environ["FI_API_KEY"],
fi_secret_key=os.environ["FI_SECRET_KEY"],
)
# compile() returns a list of message dicts; pass to any LLM
messages = prompt.compile(question=question)
response = litellm.completion(
model="gpt-4o-mini", # swap for any litellm-supported model
messages=messages,
)
return response.choices[0].message.content
print(answer_question("What is your return policy?"))You should see a support-style answer printed to the terminal. compile() returns standard [{"role": "system", "content": "..."}, ...] message dicts, so it works with any litellm-supported model: swap in "groq/llama-3.3-70b-versatile" or "anthropic/claude-sonnet-4-20250514" and keep the rest of the function unchanged.
Create v2 with chain-of-thought reasoning
Each version can have its own model configuration. This v2 uses a lower temperature for more deterministic chain-of-thought responses.
from fi.prompt.types import PromptTemplate, SystemMessage, UserMessage, ModelConfig
# Create a new version with updated messages and model config
prompt_client.create_new_version(
template=PromptTemplate(
name="support-response",
messages=[
SystemMessage(
content="You are a precise customer support agent for TechStore.\n\n"
"Think through the customer's question step by step before answering:\n"
"1. What is the customer asking?\n"
"2. What information do I have that directly addresses this?\n"
"3. What is the clearest, most helpful response?"
),
UserMessage(
content="Customer question: {{question}}\n\nAnswer:"
),
],
model_configuration=ModelConfig(
model_name="gpt-4o-mini",
temperature=0.3,
max_tokens=1000,
),
),
commit_message="Add chain-of-thought reasoning",
)
# Save and commit v2
prompt_client.save_current_draft()
prompt_client.commit_current_version(message="v2: chain-of-thought prompt")
print(f"v2 created: {prompt_client.template.version}")You should see:
v2 created: v2v2 exists as a committed version but carries no label yet, so Step 2’s get_template_by_name(label="production") still serves v1.
Test v2 before promoting it
Use is_concise here: for a support agent, concise answers are a key quality signal. Swap in any of the 72+ built-in eval metrics like groundedness, tone, completeness, or instruction_adherence depending on what you want to measure. Score v1 alongside v2 so you have something to compare before promoting.
import litellm
from fi.evals import evaluate
test_cases = [
"What is your return policy?",
"How long does standard shipping take?",
"Can I exchange a product instead of returning it?",
]
v1_prompt = Prompt.get_template_by_name(
name="support-response",
version="v1",
fi_api_key=os.environ["FI_API_KEY"],
fi_secret_key=os.environ["FI_SECRET_KEY"],
)
v2_prompt = Prompt.get_template_by_name(
name="support-response",
version="v2",
fi_api_key=os.environ["FI_API_KEY"],
fi_secret_key=os.environ["FI_SECRET_KEY"],
)
print(f"{'Question':<45} {'v1':>8} {'v2':>8}")
print("-" * 63)
v1_pass = v2_pass = 0
for question in test_cases:
row = [question[:43]]
for label, prompt in (("v1", v1_prompt), ("v2", v2_prompt)):
messages = prompt.compile(question=question)
response = litellm.completion(model="gpt-4o-mini", messages=messages)
output = response.choices[0].message.content
result = evaluate("is_concise", output=output, model="turing_small")
row.append(str(result.passed))
if label == "v1" and result.passed:
v1_pass += 1
if label == "v2" and result.passed:
v2_pass += 1
print(f"{row[0]:<45} {row[1]:>8} {row[2]:>8}")
print(f"\nv1: {v1_pass}/3 concise v2: {v2_pass}/3 concise")You should see (illustrative, your model’s phrasing will vary):
Question v1 v2
---------------------------------------------------------------
What is your return policy? True True
How long does standard shipping take? True True
Can I exchange a product instead of returni False True
v1: 2/3 concise v2: 3/3 conciseresult.score is a float in [0, 1]. result.passed is the boolean pass/fail derived from it (score >= 0.5). Here v1’s answer to the exchange question ran long and failed is_concise, while v2’s step-by-step instruction kept all three answers concise. That 2/3 → 3/3 delta is the evidence for promoting v2, not just “it should work better.”
Promote v2 to production
Prompt.assign_label_to_template_version(
template_name="support-response",
version="v2",
label="production",
fi_api_key=os.environ["FI_API_KEY"],
fi_secret_key=os.environ["FI_SECRET_KEY"],
)
print("v2 is now live in production.")You should see v2 is now live in production.
Your application now serves v2 on the next request, no redeploy. The get_template_by_name(label="production") call in Step 2 automatically picks up the new version.
Roll back to v1
If v2 causes issues, reassign the production label back to v1. Your app picks up the change on the next request.
Prompt.assign_label_to_template_version(
template_name="support-response",
version="v1",
label="production",
fi_api_key=os.environ["FI_API_KEY"],
fi_secret_key=os.environ["FI_SECRET_KEY"],
)
print("Rolled back to v1.")You should see Rolled back to v1., and the next call to answer_question() serves v1 again.
View version history
versions = prompt_client.list_template_versions()
for v in versions:
draft = "draft" if v.get("isDraft") else "committed"
print(f" {v['templateVersion']} {draft} {v['createdAt']}")You should see both versions listed (illustrative: list_template_versions() returns the backend history verbatim, so order isn’t guaranteed and your timestamps will match when you ran this tutorial):
v2 committed 2026-08-18T09:15:00Z
v1 committed 2026-08-18T09:10:00Z Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
AuthenticationError on prompt_client.create() | FI_API_KEY or FI_SECRET_KEY missing or unexported | Re-run the export block in the current shell, then re-run the script |
litellm.AuthenticationError on litellm.completion(...) | OPENAI_API_KEY not set | Export OPENAI_API_KEY, or pass a different model string litellm can route with a key you have |
ModuleNotFoundError: No module named 'fi.prompt' | futureagi not installed, or an unrelated fi package shadows it | pip install futureagi, and check pip show fi doesn’t point at a different package |
get_template_by_name(label="production") raises a not-found error | No version carries the production label yet | Commit at least one version with label="production" or call assign_label_to_template_version() first |
get_template_by_name(version="v2") raises a not-found error | Typo in the version string, or v2 hasn’t been committed yet | Call list_template_versions() to see the exact committed version strings |
evaluate("is_concise", ...) raises an unknown-model error | model isn’t a valid Turing model name | Use turing_small or another name from the built-in eval metrics list |
v2 changes don’t show up in answer_question() | v2 was committed but not promoted, or the label was assigned to a different version | Confirm the label with list_template_versions(), then re-run assign_label_to_template_version() |
Next: compare prompt variants side by side in Experimentation.
Questions & Discussion