Skip to content

Commit 94a7759

Browse files
committed
final dspy
1 parent d5d2a3c commit 94a7759

1 file changed

Lines changed: 31 additions & 67 deletions

File tree

Lines changed: 31 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,22 @@
11
---
2-
title: "DSPy solves prompting problems and creates production ones"
3-
description: "DSPy is an invaluable tool for AI workflow prototypes which creates new headaches in production."
4-
pubDate: 2026-04-02
2+
title: "DSPy's abstractions worked for us, but its runtime didn't"
3+
description: "DSPy is a useful AI workflow development tool, but its runtime configuration introduced issues for our production workflows."
4+
pubDate: 2026-04-06
55
---
66

7-
I felt a pang of jealousy after enjoying [Skylar Payne's write-up on DSPy](https://skylarbpayne.com/posts/dspy-engineering-patterns/). Fantastic post! It deserves a read for its all-too-accurate description of the path taken by [DSPy](https://dspy.ai) (or any half-baked implementation of its concepts) into production, as well as the centrality of
8-
- typed signatures,
9-
- composable modules, and
10-
- prompt optimizers
11-
in successful AI workflows.
7+
I greatly enjoyed Skylar Payne's write-up on [DSPy](https://skylarbpayne.com/posts/dspy-engineering-patterns/). I felt it would be helpful to share my experiences building production workflows with DSPy for two separate projects in 2025 at [Not Diamond](https://www.notdiamond.ai/).[^1]
128

13-
My jealousy stems from procrastinating on a similar post describing my experiences building production workflows with DSPy for two separate projects in 2025 at [Not Diamond](https://www.notdiamond.ai/).[^1] This is that post. Thanks, Skylar, for the well-written kick-to-the-butt.
9+
This is that post. Thanks, Skylar, for the well-written kick-to-the-butt.
1410

15-
## The concepts don't block adoption
11+
## Learning the library v. using it
1612

17-
Anyway, we diverge on his conclusion. Skylar writes:
13+
I agree with the criticality of signatures, modules and optimizers in modern AI workflows. DSPy uses these concepts to great effect; between that, its comprehensive library of prompt-optimizing algorithms, and its excellent documentation, we quickly learned the library.
1814

19-
> DSPy has adoption problems because it asks you to think differently before you’ve actually felt the pain of thinking the same way everyone else does.
20-
21-
> The patterns DSPy embodies aren’t optional. If your AI system gets complex enough, you will reinvent them. The only question is whether you do it deliberately or accidentally.
22-
23-
We agree that AI engineers absolutely must understand signatures and interfaces, both exemplified by the ubiquity of structured outputs. Optimizers - as a component of workflow evals - are similarly critical today.
24-
25-
But these concepts are not too esoteric to hinder DSPy’s adoption. Engineers can use DSPy's heaps of excellent documentation to quickly learn its concepts, which align with common practices for evals and workflow optimization. Let's give folks the benefit of the doubt, and assume a new user can quickly reach competency in DSPy.
26-
27-
My view is that Skylar has mixed up the causality here. Engineers don't struggle with DSPy because of its novel concepts. Instead, **DSPy's limitations hinder the creation of modular, optimizable AI workflows**.
28-
29-
During our testing I observed three concrete limitations in DSPy:
30-
- Its optimized programs have limited portability,
31-
- It takes a strongly-opinionated approach to concurrency, and
32-
- It has an LLM client dependency which can be challenging to work with and tedious to replace.
15+
That said, I tried to apply DSPy to a specific flavor of distributed system: async-optimized throughput, cross-provider targets, supporting multi-workflow stakeholders in enterprise settings. Its runtime didn't work with our production system, however, so we pulled it.
3316

3417
## Conflicting concurrency
3518

36-
Both of my team's attempts at integrating DSPy into live workflows involved distributed, asynchronous training and dynamic model-switching via Celery with `gevent`. Users switch LMs via `dspy.configure`, but we encountered [this](https://github.com/stanfordnlp/dspy/blob/main/dspy/dsp/utils/settings.py#L159-L163) configuration issue:
19+
Both of my team's attempts at integrating DSPy into live workflows involved distributed, asynchronous training and dynamic model-switching via Celery with `gevent`. We abandoned DSPy shortly after encountering [this](https://github.com/stanfordnlp/dspy/blob/main/dspy/dsp/utils/settings.py#L159-L163) configuration issue:
3720

3821
```python
3922
if not in_ipython and config_owner_async_task != asyncio.current_task():
@@ -43,7 +26,7 @@ if not in_ipython and config_owner_async_task != asyncio.current_task():
4326
)
4427
```
4528

46-
As suggested, we tried `dspy.context` to avoid async task conflicts. But every model switch required context managers, [as demonstrated by this example snippet](https://github.com/stanfordnlp/dspy/blob/main/docs/docs/tutorials/cache/index.md?plain=1#L178-L181):
29+
So we used `dspy.context`, as suggested to avoid async task conflicts. As mentioned before, though, we wanted to switch models, which obligated [this example snippet](https://github.com/stanfordnlp/dspy/blob/main/docs/docs/tutorials/cache/index.md?plain=1#L178-L181):
4730

4831
```python
4932
with dspy.context(lm=dspy.LM("openai/gpt-5-mini")):
@@ -53,18 +36,21 @@ with dspy.context(lm=dspy.LM("openai/gpt-5-nano")):
5336
result2 = predict(question="Who do *you* think is the GOAT of soccer?")
5437
```
5538

56-
In a Markdown doc or Jupyter notebook this reads fine. In a production codebase this practically _invites_ bugs. We abandoned both efforts shortly thereafter.
57-
5839
While DSPy 3.0 shipped async improvements, [limitations in configurability](https://github.com/stanfordnlp/dspy/issues/8197) and async support still persist:
59-
- program training still [requires a synchronous context](https://github.com/stanfordnlp/dspy/issues/9075)
40+
- program training seems to [require a synchronous context](https://github.com/stanfordnlp/dspy/issues/9075)
6041
- `dspy.settings` has [mixed support](https://github.com/stanfordnlp/dspy/issues/8197) across coroutines
6142
- multi-model workflows have limited support across [different concurrency models](https://github.com/stanfordnlp/dspy/issues/8797)
6243

63-
More broadly, **this anti-modular design emerges from DSPy's tightly-coupled inference-time story**. By optimizing your program with DSPy, you've also bought into the library's architectural decisions for concurrency. This is no more evident than [in the FastAPI tutorial](https://dspy.ai/tutorials/deployment/#deploying-with-fastapi), where the user defers concurrency management to DSPy via `dspy.asyncify`. But production deployments require performance optimizations tailored to the needs of *your* workflows - not those of a library dependency.
44+
More broadly, **this design tightly couples DSPy's runtime to your production setting**. By optimizing your program with DSPy, you've also bought into the library's architectural decisions for concurrency. This is no more evident than [in the FastAPI tutorial](https://dspy.ai/tutorials/deployment/#deploying-with-fastapi), where the user's concurrency management must be deferred to DSPy via `dspy.asyncify`.
45+
46+
Our production deployment requires performance optimizations tailored to the needs of *our* workflows, instead of those of a library dependency. To avoid conflicts between Celery and DSPy's concurrency management, we essentially distributed prompt optimization by model and ran each optimizer serially within a task.
6447

48+
This architecture wrecked the system's overall throughput. DSPy's optimizers were now the slowest part of our optimization stack.
6549
## Pulling prompts
6650

67-
What did I mean earlier by a "tightly-coupled inference-time story?" Let's say you want to use your optimized prompts outside of DSPy. You'd have to extract them [like so](https://github.com/stanfordnlp/dspy/issues/7830):
51+
Once optimization completed, we encountered additional challenges when porting optimized prompts out of the distributed tasks. These prompts needed to pass human review for use in workflows which did not depend on DSPy, or existed outside of automated pipelines altogether (like AI chat interfaces).
52+
53+
Let's say you want to use your optimized prompts outside of DSPy. You'd have to extract prompts [like this](https://github.com/stanfordnlp/dspy/issues/7830):
6854

6955
```python
7056
{
@@ -77,55 +63,33 @@ What did I mean earlier by a "tightly-coupled inference-time story?" Let's say y
7763
}
7864
```
7965

80-
Omar Khattab[^2] has [explained the intention](https://github.com/stanfordnlp/dspy/issues/8042#issuecomment-2773833904) behind this choice:
81-
82-
> DSPy does a lot of heavy-lifting indeed, and it's very common that people try to extract the optimized prompt but end up hurting quality in the process. We do not usually advise that you extract anything for this reason, since the optimized prompt assumes a lot of DSPy behavior like the way the inference calls are made.
83-
84-
> That said, if you really want to get a prompt you can apply this process. Note that it gives you a list of messages.
85-
86-
He's right, of course. Many of DSPy's optimizers[^3] treat signatures as optimizable parameters. If the user decouples those components from the underlying prompts, they can inadvertently unwind performance gains.
87-
88-
But...that's _my_ prompt. Now, though, it has limited applicability. Concretely: **this inference-time requirement creates a golden-path dependency on DSPy.**
89-
90-
This bleeds into other concerns. For eg. observability, we faced a choice between forking the library to add spans or adopting [MLflow](https://dspy.ai/tutorials/observability/#tracing). If your production service doesn't use Python, you're instead relying on unofficial ports[^4]. If your prompt needs to land in a non-software workflow handled by Claude Cowork or Codex? You're back to Omar's earlier snippet.
91-
66+
Note that many of DSPy's optimizers[^2] treat signatures as optimizable parameters. If the user decouples those components from the underlying prompts, they can inadvertently unwind performance gains. Users should preserve DSPy's programs as-is.
9267

68+
**But these programs do not willingly exit the DSPy runtime**. We attempted to extract programs using the above snippet, as well as applying various scripts to DSPy's JSON I/O options and its `Signature` and `ChatAdapter` classes. None of these approaches ultimately met the requirements of our end users for workflow compatibility and human understanding.
9369
## Dealing with dependencies
9470

95-
DSPy also relies on leaky abstractions thanks to its underlying LLM client library, LiteLLM. Several years ago, LiteLLM's cross-provider compatibility and ease of implementation made it the de-facto LLM client for workflow prototyping. Since then the AI ecosystem aligned onto OpenAI-style messages, and Skylar’s post demonstrates common patterns in client usage and request retries.
71+
Our production runtime deploys into environments with tighly-constrained requirements, so we require control over our supply chain. DSPy, however, executes all of its LLM client requests via LiteLLM. Introducing DSPy meant accepting LiteLLM into our carefully-curated environments and observability stack.
9672

97-
LiteLLM poses its own issues in production contexts, however, such as poor logging configuration. It produces verbose, poorly-formatted logs at `INFO` level, which polluted Datadog, Sentry and our local testing tools:[^5]
73+
I've found that LiteLLM presents significant issues in non-trivial production contexts. The library introduces verbose, poorly-formatted logs at `INFO` level, which polluted Datadog and our local testing tools:[^3]
9874

99-
![Commonly seen in logs across Jupyter notebooks and dev environments](/images/dspy-litellm-logs.png)
75+
![A common sight if you forgot to set the LiteLLM root logger to `WARN`.](public/images/dspy-litellm-logs.png)
10076

101-
LiteLLM also recently experienced [a supply chain vulnerability](https://docs.litellm.ai/blog/security-update-march-2026) after a core maintainer's PyPI credentials were compromised. This risk exists for any OSS project, but if LiteLLM is not proxying your requests then your alternatives can be as simple as `openai.OpenAI` and `tenacity`. [^6]
77+
Our supply chain requirements extend to security concerns, so it's worth noting LiteLLM recently experienced [a supply chain vulnerability](https://docs.litellm.ai/blog/security-update-march-2026), where a core maintainer's PyPI credentials were seemingly compromised.
10278

103-
In DSPy, users can swap out the underlying client library with some effort. [There's no documentation on this capability](https://dspy.ai/learn/programming/language_models/#advanced-building-custom-lms-and-writing-your-own-adapters). A DSPy contributor is working on extracting LiteLLM into an extra dependency [while redesigning `dspy.LM` ](https://github.com/stanfordnlp/dspy/issues/9514), which significantly helps production users once the change lands.
79+
We overcame these challenges by building our own client logic with `openai.OpenAI` and `tenacity` which extends across structured outputs and function calling.[^4] As of today there's broad support for `chat.completions` across AI providers. Our in-house client library works comprehensively and reliably, thanks to strong compatibility with our observability stack.
10480

10581
## What it is and what it is not
10682

107-
Admittedly, our experience with DSPy could confirm "Khattab's Law" as proposed by Skylar:
108-
109-
> Any sufficiently complicated AI system contains an ad hoc, informally-specified, bug-ridden implementation of half of DSPy.
83+
If you need to manage your own concurrency, port your prompts to different settings, or keep an orderly deployment environment, you might find that DSPy's dependency stack and runtime don't fit your needs.
11084

111-
Our prompting layer leverages typed signatures, a separate eval harness and first-party provider SDKs to optimize prompts over eval datasets.
112-
113-
But none of the facts I've laid out above dilute the usefulness of DSPy. It's still a fantastic exploratory tool for prompt optimization. Our broader AI ecosystem benefits from research produced by labs like Stanford NLP and MIT CSAIL, whose researchers primarily maintain DSPy.
114-
115-
That said, it's worth acknowledging difficulties integrating it into production systems with optimized concurrency, comprehensive monitoring, and tight requirements for environment builds.
116-
117-
As Skylar noted, DSPy’s concepts form the basis of any well-designed AI workflow. I believe that applying those concepts inward - to the library’s architecture and interfaces - would broaden its impact far beyond prototyping and research.
85+
For other scenarios, though, DSPy can help launch and optimize your workflow prototypes very quickly, and its concepts will endure well into the production phase of your project.
11886

11987
*Thanks to Devansh Jain and James Kunstle for input on this write-up*.
12088

121-
[^1]: Astute readers will note that Not Diamond offers [Prompt Optimization](https://www.notdiamond.ai/#prompt-optimization). If you haven't closed this tab yet, I *solemnly vow* to avoid any sales in this post.
122-
123-
[^2]: Lead author on [the original DSPy paper](https://arxiv.org/abs/2310.03714) and core contributor to the open-source library.
124-
125-
[^3]: Notably, this is not true of GEPA, where users can [seamlessly extract prompts](https://x.com/LakshyAAAgrawal/status/1978198977985069348) from the optimized adapter. While GEPA is available in DSPy, this capability is primarily available in the underlying `gepa-ai` [library](https://gepa-ai.github.io/gepa/).
89+
[^1]: Astute readers will note that one of our products at Not Diamond is [Prompt Optimization](https://www.notdiamond.ai/#prompt-optimization). If you haven't closed this tab yet, I *solemnly vow* to avoid any sales in this post.
12690

127-
[^4]: There are community-built libraries for languages like [TypeScript](https://github.com/ruvnet/dspy.ts) and [Rust](https://github.com/krypticmouse/DSRs). They lack feature parity or ongoing support.
91+
[^2]: Notably, this is not true of GEPA, where users can [seamlessly extract prompts](https://x.com/LakshyAAAgrawal/status/1978198977985069348) from the optimized adapter. While GEPA is available in DSPy, this capability is primarily available in the underlying `gepa-ai` [library](https://gepa-ai.github.io/gepa/).
12892

129-
[^5]: Honestly, "Local Engineer Complains About Dependency Logging Configs" is the least surprising headline of the day! But LiteLLM's usage of `INFO` for low-signal logs feels disconnected from common logging practices in production systems. It's symptomatic of LiteLLM's other issues, a discussion of which falls well outside of this post's scope.
93+
[^3]: Honestly, "Local Engineer Complains About Dependency Logging Configs" is the least surprising headline of the day! But LiteLLM's usage of `INFO` for low-signal logs feels disconnected from common logging practices in production systems.
13094

131-
[^6]: I expect most AI engineers writing Python have battle scars with `openai.OpenAI` and `tenacity.retry` at this point.
95+
[^4]: I expect most AI engineers writing Python have battle scars with `openai.OpenAI` and `tenacity.retry` at this point. `RETRYABLE_HTTP_ERROR_CODES` should be a permanent fixture of Pythonic AI project templates.

0 commit comments

Comments
 (0)