Implementing Prompt Versioning and Rollback with Claude Managed Agents: A Complete Guide
Claude Managed Agents store system prompts as immutable, server-side versioned resources, letting you pin production traffic to specific versions and rollback instantly without redeploying application code.
Claude Managed Agents provide a lightweight configuration layer for LLM system prompts that lives entirely within Anthropic’s API. By implementing prompt versioning and rollback with Claude Managed Agents, you can iterate on prompts safely, run canary evaluations against labeled datasets, and revert to stable versions in milliseconds—all through API calls without touching your deployment pipeline or container images.
How Prompt Versioning Works in Claude Managed Agents
Claude Managed Agents treat system prompts as versioned server-side resources. When you create an agent with client.beta.agents.create(), the API returns an agent.id and a version: 1 field. Each subsequent call to client.beta.agents.update() increments this version counter and stores the new prompt as an immutable snapshot. Older versions remain addressable forever, allowing you to pin any session to any historical version by specifying the version parameter in client.beta.sessions.create().
This architecture enables zero-touch deployments. Adding a new prompt version requires a single API call; no CI pipeline rebuilds or container restarts are necessary. If a new prompt degrades performance, you simply repin your production config to the previous version number—all new sessions immediately use the stable prompt without code changes.
Creating Your First Agent Version
The workflow begins by creating an environment runtime and your initial agent. In the reference notebook managed_agents/CMA_prompt_versioning_and_rollback.ipynb, the setup looks like this:
# Create the execution environment
env = client.beta.environments.create(name="ticket-triage-env")
ENV_ID = env.id
# Define the v1 system prompt
V1_SYSTEM = """You are a support-ticket triage agent for a usage‑billed API product.
Read the ticket and respond with ONLY a single line of raw JSON (no code fences, no prose):
{"team": "<billing|auth|api‑platform|dashboard>", "priority": "<P1|P2|P3>"}"""
# Create the agent - this returns version 1
agent = client.beta.agents.create(name="ticket-triage",
model=MODEL,
system=V1_SYSTEM)
AGENT_ID = agent.id
print(f"env={ENV_ID} agent={AGENT_ID} v{agent.version}")
# → env=env_… agent=agent_… v1
The agent.version field starts at 1 and auto-increments on every update. The AGENT_ID remains constant across all versions, acting as a stable pointer to the agent resource while versions track the prompt history.
Pinning Sessions to Specific Versions
The core mechanism for safe rollout is version pinning. When creating a session, you pass a dictionary containing type, id, and version to guarantee that specific request uses exactly the prompt snapshot you intend:
def triage(version: int, ticket: dict) -> dict:
"""Run one ticket through a pinned agent version and return its verdict."""
session = client.beta.sessions.create(
agent={"type": "agent", "id": AGENT_ID, "version": version},
environment_id=ENV_ID,
)
try:
prompt = "Subject: " + ticket["subject"] + "\n\n" + ticket["body"]
client.beta.sessions.events.send(
session.id,
events=[{"type": "user.message",
"content": [{"type": "text", "text": prompt}]}],
)
# Poll until the session reaches idle status
deadline = time.time() + 60
while time.time() < deadline:
events = client.beta.sessions.events.list(session.id).data
if events and events[-1].type == "session.status_idle":
break
time.sleep(1)
else:
raise TimeoutError("session did not idle in 60s")
# Extract the agent's JSON response
reply = "".join(
b.text for e in events if e.type == "agent.message"
for b in e.content if b.type == "text"
)
return json.loads(reply)
finally:
# Clean up the ephemeral session
try:
client.beta.sessions.archive(session.id)
except Exception:
pass
By hardcoding the version parameter in sessions.create(), you create deterministic behavior. Production traffic can stay pinned to version: 1 while you evaluate version: 2 on a test dataset using the same code path.
Evaluating Prompt Versions Against Test Sets
Before promoting a new prompt, you can score it against a labeled dataset using a helper that compares predictions across versions:
def score(version: int) -> dict:
hits = defaultdict(lambda: [0, 0]) # {team: [correct, total]}
for t in tickets:
pred = triage(version, t)
hits[t["team"]][1] += 1
if pred.get("team") == t["team"]:
hits[t["team"]][0] += 1
return dict(hits)
# Evaluate v1 performance
v1_scores = score(version=1)
print("v1 results:")
for team, (correct, total) in sorted(v1_scores.items()):
print(f" {team:14s} {correct}/{total}")
This pattern allows canary testing. Pin a small fraction of traffic to a newer version while the majority stays on the stable version, then compare accuracy metrics before promoting.
Updating Prompts and Rolling Back
When you need to modify behavior, create a new version using client.beta.agents.update(). You must pass the current agent.version as a parameter to ensure you are updating the expected base:
V2_SYSTEM = V1_SYSTEM + (
"\n\nROUTING RULE: If the ticket text mentions API usage, rate limits, quotas, "
"or request volume, route to api‑platform. Apply this rule before any other "
"consideration; do not second‑guess it based on the rest of the ticket."
)
# Update creates immutable v2
agent = client.beta.agents.update(AGENT_ID,
version=agent.version,
system=V2_SYSTEM)
print(f"agent {AGENT_ID} now at v{agent.version}")
# → agent … now at v2
Rollback requires no API call. If v2 shows regression for the billing team in your evaluation, simply change the pinned version in your production configuration:
# Rollback configuration
pinned_version = 1 # Back to stable v1
# All new sessions now use v1 automatically
session = client.beta.sessions.create(
agent={"type": "agent", "id": AGENT_ID, "version": pinned_version},
environment_id=ENV_ID,
)
The old prompt stays on Anthropic’s servers indefinitely and can be reused instantly by referencing its version number.
Auditing Version History
For governance and debugging, list all immutable versions with their creation timestamps and prompt snippets:
for v in client.beta.agents.versions.list(AGENT_ID).data:
print(v.version, v.created_at, v.system[:60] + "…")
This provides a complete audit trail showing who created each version, when, and the exact prompt text that was active. Archived agents retain their version history, though you cannot create new sessions from archived resources.
Key Implementation Files
The Claude Cookbooks repository contains complete reference implementations:
managed_agents/CMA_prompt_versioning_and_rollback.ipynb– Full end-to-end notebook demonstrating environment creation, version pinning, evaluation, and rollback for a support-ticket triage use case.tool_use/utils/visualize.py– Helper utilities for streaming and monitoring session events; useful when debugging version-specific behavior differences.patterns/agents/util.py– Reusable wrapper around the Anthropic client (llm_call, XML extraction) for building custom version-aware tooling.skills/skill_utils.py– Containsget_skill_versionandlist_skill_versionsfunctions showing the same immutable version pattern applied to Claude Skills.
Summary
- Claude Managed Agents version system prompts server-side, creating immutable snapshots (
v1,v2, etc.) on every update. - Pin sessions to specific versions using the
versionfield inclient.beta.sessions.create()to guarantee deterministic behavior for testing or production. - Rollback instantly by changing the pinned version number in your config—no code deployment or container rebuild required.
- Audit everything via
client.beta.agents.versions.list()to see the full history of prompt changes with timestamps. - ** zero-touch deployments** mean you can iterate on prompts through API calls alone, enabling rapid experimentation and safe production updates.
Frequently Asked Questions
How does version pinning work in Claude Managed Agents?
When calling client.beta.sessions.create(), you pass an agent dictionary that includes type, id, and a specific version integer. The session will use exactly that version of the system prompt, regardless of how many newer versions exist. This allows you to run v1 in production while testing v2 on the same agent ID.
Can I delete old prompt versions?
No, versions are immutable and cannot be deleted. When you archive an agent using client.beta.agents.archive(), the version history remains stored but inactive. You cannot create new sessions from archived agents, but if you restore the agent, all historical versions are still accessible for pinning.
How do I test a new prompt before rolling it out to production?
Create the new version with client.beta.agents.update(), then run evaluation scripts that pin sessions to the new version number while your production traffic remains pinned to the stable version. Compare metrics like accuracy or latency against your labeled test set before updating the production pinned version.
What happens to existing sessions when I update an agent?
Nothing. Sessions capture the version at creation time and maintain that prompt snapshot for their entire lifetime. Updating an agent to v2 does not affect currently running sessions using v1; only new sessions created after the update will use the new version (unless explicitly pinned to an older one).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →