DSPy's Compiled Programs Re-send Few-Shot Demos

DSPy promises to abstract away prompt engineering by compiling prompts and few-shot examples. The core idea is that developers write a program using DSPy modules, define a metric for success, and provide a training dataset. A teleprompter then optimizes this program, finding effective few-shot demonstrations for each module. This process genuinely works, significantly improving LLM performance without manual prompt tuning. However, a critical detail often glossed over in tutorials is the operational cost of this compilation, specifically how many examples are sent with each query in production.

When you compile a DSPy program using the default optimizer, it bootstraps few-shot demonstrations. These selected examples are then pinned to each predictor within the compiled program. This means that every single API call made by your application to the compiled DSPy program will include these attached demonstrations. The number of demos attached is configurable, with a default maximum of up to 20 few-shot examples being sent per call.

DSPy code snippet illustrating default optimizer attaching few-shot demos to predictors

The Cost of Convenience: Latency and Token Usage

The convenience of DSPy's compilation comes at a tangible cost. Each API call to a compiled DSPy program now includes not just the user's input and the prompt instructions, but also a significant number of few-shot examples. This has direct implications for both latency and token consumption.

Latency: Sending more data with each request naturally increases the time it takes for the LLM to process the query and return a response. If your application requires near real-time interactions, the added overhead of transmitting and processing up to 20 demonstrations per call can be a bottleneck. This is especially true if the demonstrations themselves are lengthy or complex.

Token Usage: LLM APIs are typically priced per token. By including up to 20 few-shot examples with every call, your token expenditure can skyrocket. If your application makes thousands or millions of calls daily, this can quickly translate into substantial operational costs. For instance, if each demo uses 500 tokens, and you send 10 demos per call, that's 5,000 tokens *in addition to your actual input and the generated output* per query. Over a million calls, this could amount to billions of extra tokens consumed.

Understanding the Compilation Process

DSPy's compilation process is designed to automate the discovery of effective few-shot examples. The default optimizer, often `BootstrapFewShot`, works by:

  • Taking your program definition and a dataset.
  • For each module (predictor) in your program, it iteratively selects examples from the dataset that best help the LLM perform the task associated with that module.
  • These selected examples are then 'compiled' into the program, meaning they are stored and retrieved alongside the program's logic.
  • The `max_bootstrapped_demos` parameter in the optimizer's configuration controls the upper limit of these stored examples.

The surprising detail here is not that DSPy finds examples, but that these examples are *baked into* and re-sent with every production inference, rather than being a one-time cost during compilation or a separate retrieval mechanism. Think of it less like a database query that fetches context on demand, and more like a recipe that includes a full page of preparation steps for each ingredient, every time you cook the dish.

Mitigating the Impact

For developers and founders deploying DSPy in production, understanding and mitigating this behavior is crucial. Several strategies can be employed:

1. Custom Optimizers and Prompt Engineering

DSPy is an open framework. Developers can write their own optimizers or modify existing ones. This might involve:

  • Developing an optimizer that selects fewer, more impactful demonstrations.
  • Implementing a mechanism to dynamically fetch demonstrations based on input similarity, rather than statically attaching them.
  • Reverting to more traditional prompt engineering for critical, high-volume paths where token efficiency is paramount.

2. Careful Configuration of `max_bootstrapped_demos`

The most straightforward approach is to reduce the `max_bootstrapped_demos` parameter during compilation. Setting this to a lower number, such as 1, 2, or 5, will directly reduce the number of tokens sent per call. This requires careful experimentation to find a balance between performance uplift from few-shot examples and the operational cost.

3. Utilizing DSPy's `compiled` Functionality Wisely

The `dspy.settings.configure` context manager and the `dspy.program.compiled` decorator allow for fine-grained control over DSPy's behavior. Understanding how these settings affect the compiled output is key. For instance, one could potentially compile a program once with a large number of demos for offline evaluation or fine-tuning, but then re-compile it with a much smaller number of demos for production deployment.

4. Considering Alternatives for High-Throughput Scenarios

In scenarios demanding extremely low latency and minimal token usage, it might be necessary to explore alternative approaches. This could involve using DSPy for prompt generation and optimization, but then manually extracting the optimized prompts and a minimal set of examples for deployment in a more traditional API client. Alternatively, using smaller, fine-tuned models that require less contextual data might be more cost-effective.

What This Means for the DSPy Ecosystem

DSPy's approach to prompt compilation offers a powerful way to achieve high performance from LLMs. However, the revelation that compiled programs re-send few-shot demos with every call highlights a critical trade-off between ease of use and operational efficiency. Developers must be aware of this behavior to avoid unexpected costs and latency issues in production environments. The DSPy team and community will likely need to address this by providing more explicit guidance, optimized compilation strategies, or alternative deployment patterns for high-throughput applications. For now, if you run a team that relies on DSPy for production inference, you should audit your compiled programs' token usage and latency immediately.