A Versioned Prompt Registry Behind One Key: Which Template Version Each Backend Actually Received
A versioned prompt registry lets you treat prompt templates like code: every change gets an immutable version, and every API call records exactly which version was sent. This guide explains why that matters when routing one API key across multiple backends (Claude, GPT, DeepSeek, Qwen, GLM, Kimi), how to design a lightweight registry, and how to log and audit the version actually received by each model.
Why a versioned prompt registry matters
When you call multiple model backends through one API key, the same logical prompt often gets rendered differently for each provider. A small template change for one model can silently affect another. A versioned prompt registry solves this by giving every template revision an immutable identifier and recording that identifier on every request.
Without a registry, you get:
- Templates edited in place, so you cannot reproduce an old result.
- No record of which prompt text a backend actually received.
- Hard-to-debug quality differences that look like model behavior but are really prompt drift.
With a registry, each call is traceable: template name, version, rendered prompt, and backend model are all logged together.
Core concepts
- Template: a named prompt with placeholders (e.g.,
summarize_v2). - Version: an immutable snapshot of that template. Once published, a version never changes.
- Render: substituting variables into a specific version to produce the final prompt string.
- Receipt: the log entry that records which version was rendered and sent to which backend.
Designing the registry (conceptual)
Keep the registry simple and storage-backed. A minimal schema:
template_name(string)version(integer or semver)body(the template text with placeholders)created_at(timestamp)metadata(optional tags: owner, intended backends, notes)
Rules:
- Immutable versions. Publishing a change creates a new version; it never overwrites an old one.
- Explicit rendering. Rendering takes
template_name,version, and a variable map. It returns the final string plus a receipt object. - Receipt on every call. The receipt travels with the request to the model backend and is stored in your logs.
Routing across multiple backends
A single API key can call many models, but each backend may need a different rendering of the same logical prompt. The registry should support per-backend template variants without duplicating logic.
A practical pattern:
- Define a base template per task (e.g.,
classify_intent). - Define optional backend-specific overrides (e.g.,
classifyintent@claude,classifyintent@gpt). - At call time, resolve the version for the target backend: use the override if present, otherwise the base version.
- Record both the logical template name and the resolved version in the receipt.
This keeps one source of truth while allowing small formatting differences where a backend benefits from them.
Logging what each backend actually received
The receipt is the key artifact. For each API call, log at least:
request_id(your own identifier)template_nameandversionbackend_model(e.g., the exact model string sent)renderedprompthash(a hash of the final prompt string)rendered_prompt(optional, subject to your privacy policy)variables(optional, redacted as needed)
With this, you can answer questions like: “Which version of summarize did DeepSeek receive on request 12345?” and “Did all backends get the same prompt version for this experiment?”
Versioning workflow
- Draft a change in a branch or draft record.
- Test by rendering the new version and running it against a small evaluation set for each target backend.
- Publish a new immutable version.
- Roll out by updating the routing configuration to point selected backends or traffic percentages to the new version.
- Monitor receipts and quality signals; roll back by pointing configuration to the previous version.
Because versions are immutable, rollback is instant and safe.
Common pitfalls
- Editing published versions. This breaks reproducibility. Always create a new version.
- Logging only the template name. Without the version, you cannot reconstruct the exact prompt.
- Assuming all backends get identical text. If you use overrides, log the resolved version per backend.
- Storing secrets in templates. Keep credentials and sensitive variables out of the registry body; inject them at render time.
How this fits with a multi-model API aggregator
When you use one API key to reach Claude, GPT, DeepSeek, Qwen, GLM, and Kimi, the aggregator handles billing and routing. Your prompt registry sits on your side: it decides which template version to render for each call, and it records the receipt. The aggregator’s logs show the request; your registry logs show the prompt version behind it.
Billing is transparent: you pay the official price × 1.3 for usage. If you contribute prompts or templates to a shared registry, credit is returned at the official price × 1.1 (or × 1.2 for premium contributions) in USDC. This makes a versioned registry not just an engineering practice but also a way to account for reusable prompt assets.
Getting started checklist
- [ ] Choose a storage backend for templates and versions.
- [ ] Define a receipt schema and log it with every model call.
- [ ] Implement render(template, version, variables) returning text + receipt.
- [ ] Add backend-specific overrides only where needed.
- [ ] Set up a publish/rollback workflow with immutable versions.
- [ ] Review receipts regularly to confirm which versions each backend receives.