promptel vs DSPy: Declarative Prompts vs Compilation
Comparing promptel and DSPy: when to use a declarative prompt specification, when to use a programmatic compiler, and how the two compose.
Why we get asked this question
Of all the comparisons we field, promptel vs DSPy is the one we hear most often. They both touch the same problem — making prompts first-class engineering artefacts rather than ad-hoc strings — but they attack it from different angles and arrive at very different artefacts. If you have an hour to evaluate, this post is designed to save you most of it.
The 30-second version: promptel is a specification format with a JavaScript runtime; DSPy is a Python framework with an optimiser. They are complementary, not competing, and they solve different halves of the problem.
What promptel is
promptel is a declarative prompt framework for Node.js. You write
a prompt in its .prompt DSL or in an equivalent YAML format,
declaring typed parameters, the prompt body, optional prompting
techniques, and constraints such as maxTokens and
temperature:
# summarise.yml
name: Summariser
meta:
version: "1.0"
description: Produce a three-bullet summary of input text
params:
text:
type: string
required: true
body:
text: |
You are a precise summariser.
Produce exactly 3 bullets, each under 120 characters.
No preamble, no explanation.
${params.text}
constraints:
maxTokens: 256
temperature: 0.3
That file is the artefact. It can be diffed, reviewed in a PR, and versioned independently of the application code. The provider is chosen at execution time rather than written into the file:
const { executePrompt } = require('promptel');
const fs = require('fs');
const prompt = fs.readFileSync('summarise.yml', 'utf8');
const result = await executePrompt(prompt, { text: article }, {
provider: 'claude', // or 'openai', 'groq'
apiKey: process.env.ANTHROPIC_API_KEY,
});
promptel also ships built-in techniques — chainOfThought,
fewShot, zeroShot, treeOfThoughts, reAct and
selfConsistency — and a CLI that executes prompts and
converts between the DSL and YAML.
The companion tool, blogus, works on a different stage:
it scans an existing codebase for OpenAI and Anthropic calls,
moves the prompts into its own .prompt files (YAML
frontmatter plus a template), and locks them with content
hashes. Its files are not promptel specifications; the two
tools are independent.
What DSPy is
DSPy is a Python framework for programming — not specifying — LLM behaviour. You write a Python module that describes the computation as a sequence of LLM calls, and DSPy’s optimisers (originally called “teleprompters”) tune the prompts and the few-shot examples empirically against a metric you provide:
import dspy
class Summarise(dspy.Signature):
"""Produce a 3-bullet summary of input text."""
text: str = dspy.InputField()
bullets: list[str] = dspy.OutputField()
summariser = dspy.ChainOfThought(Summarise)
teleprompter = dspy.MIPROv2(metric=summary_quality)
optimised = teleprompter.compile(
summariser,
trainset=eval_set,
valset=val_set,
)
The output is also an artefact — a program whose optimised prompts and demos can be saved and reloaded — but it is not a declarative file. It is what you ship, but it is not what you diff, review, or hand to a non-engineer.
The dimensions that matter
| Dimension | promptel | DSPy |
|---|---|---|
| What you write | A .prompt DSL or YAML file | Python code |
| What the artefact is | A spec file checked into git | A Python program, plus saved optimised prompts and demos |
| Who can review it | Engineers, and anyone comfortable reading YAML | Mostly engineers |
| Where the optimisation happens | Outside the spec; promptel does not optimise prompts | Inside the framework; optimisers such as MIPROv2 run against your metric |
| Runtime | Node.js 18+ | Python |
| Version control story | Git diff of a YAML or DSL file is the prompt diff | Prompts are generated from signatures and optimiser state, so the diff is less direct |
| Types | Typed input parameters with required flags and defaults; an output format, not an output schema | Typed input and output fields on a dspy.Signature |
| Multi-provider | OpenAI, Anthropic and Groq, selected per call | Many providers through its LM configuration |
| Few-shot examples | Written by you, via the fewShot technique | Native: optimisers generate and select demos |
| Cost | Not addressed; a gateway such as route-switch can sit in front | Not a built-in objective, but you can fold cost into your metric |
When to use which
Use promptel when:
- The prompt is a contract that crosses a team boundary and needs to be diffable, reviewable and readable without running Python.
- Your application is in JavaScript or TypeScript.
- You want to switch between OpenAI, Anthropic and Groq without rewriting the prompt or the call site.
- You want named prompting techniques (chain-of-thought, self-consistency and so on) expressed declaratively rather than hand-assembled.
Use DSPy when:
- You are optimising prompts empirically against a metric and want the optimiser to generate few-shot demos.
- You are doing research on prompt optimisation itself and want to compare optimisers (MIPROv2, COPRO and others).
- Your prompt logic is programmatic — branches, retries, tool calls — and a static spec is too rigid.
- You are prototyping in Python and want to iterate fast.
Use both when you want the reviewable artefact and the empirical optimisation: optimise in DSPy, then carry the winning instruction text into a promptel spec as the thing that ships and gets reviewed. That hand-off is manual — neither tool imports the other’s format — so treat it as a deliberate step with its own review, not an automated pipeline.
A workflow that uses both
- Spec the prompt in promptel. Declare the parameters, the constraints and a first draft of the body. This is what a reviewer sees.
- Optimise the wording with DSPy. Build an equivalent
dspy.Signature, run an optimiser against a labelled eval set, and take the best instruction text and demos. - Commit both. The promptel spec, the eval set, the optimiser configuration and the metric all go into the repository, so the optimisation can be rerun.
- Run it against more than one provider. The same spec runs
on OpenAI, Anthropic or Groq by changing the
provideroption, and you should test it on each: portability of the file is not portability of behaviour. - Lock what ships. If your prompts are embedded in application code, blogus can hash and lock them so that CI fails when a prompt changes without review.
If you also want to optimise against production traffic rather than a fixed eval set, route-switch reruns MIPROv2 against traces it captures at the gateway.
Things people get wrong
- They treat them as alternatives. They mostly aren’t. promptel is about the artefact; DSPy is about finding good content for it.
- They expect DSPy to be an audit trail. DSPy is built for optimisation. A record of what was deployed needs version control and, ideally, a lock file.
- They use promptel for throwaway code. The spec overhead is real; for an afternoon’s prototype, raw strings are fine. Reserve specs for prompts that will outlive the afternoon.
- They assume a typed spec validates the answer. promptel types the inputs. Checking that the model’s output has the shape you asked for is still your test suite’s job.
What to read next
- Formalising Prompts as First-Class Research Objects — the argument for prompts as typed artefacts
- Prompt Lifecycle Management: From Extraction to Deployment — the blogus side of the workflow
- Intelligent LLM Routing: Spending Compute Where It Matters — routing across providers and MIPROv2 on captured traces
- DSPy documentation — for the optimiser side
- The
promptelrepository —github.com/Skelf-Research/promptel