DSPy: Automating Prompts for Language Models

Le brief IA que les pros lisent chaque soir
Les 7 actus IA du jour, décryptées en 5 min. Gratuit.
Inclus dès l'inscription : notre sélection des meilleurs guides & comparatifs IA.
Choisis ton rythme
Gratuit · Pas de spam · Désabonnement en 1 clic
Automate the Writing of Your LLM Prompts with DSPy
Large Language Models
Automating the writing of prompts for large language models (LLMs) is a crucial issue for optimizing interactions with these systems. DSPy emerges as a revolutionary tool that not only allows for the automatic generation of prompts but also evaluates and optimizes them to ensure their effectiveness in production.
We have all likely experienced receiving responses from LLMs that did not exactly meet our expectations. Often, this forces us to rephrase our prompts multiple times until we get a satisfactory answer. To achieve this, we sometimes need to be more explicit, provide examples, or explain why we need the answer. It may also be necessary to define a persona or provide enough context for the LLM to respond appropriately.
This approach can work when we interact directly with the LLM. However, the situation changes dramatically when we develop an application based on an LLM, a software that will operate autonomously and interact with one or more LLMs. In this case, the software uses predefined prompts that it sends to the LLMs. If the results are not satisfactory, we are not present to rephrase the prompts and try again. This means that the prompts must be designed from the outset to be robust and reliable, ensuring consistent operation in production.
Creating a prompt of this nature can be complex. In this article, we will explore why this is the case and how a Python tool named DSPy can help create reliable prompts. DSPy not only automatically generates prompts but also deeply evaluates them, ensuring their effectiveness in production. I will also share an excerpt from my latest book published by Manning Publishing, Building LLM Applications with DSPy, co-authored with Serj Smorodinsky. This book provides a comprehensive description of DSPy and its use for creating applications based on LLMs.
The Difficulty of Creating a Reliable Prompt in Production
One of the main challenges in creating reliable prompts lies in the unpredictability of the inputs we will have to process. For example, if we are developing software that must process documents, these documents may be found online or submitted by users of the software. In the context of document processing, the application may ask an LLM to summarize them, translate them, extract key information, or perform other similar tasks. Suppose the software asks the LLM to critique the plausibility of the content of the documents. For this, we might write a prompt such as:
prompt_text = f"Evaluate how plausible the following text is: {document_text}"
This prompt uses a Python f-string to form the prompt, with a placeholder for the document text. Other prompts may have multiple placeholders for inputs, but for simplicity, we will assume here that each prompt has only one input — the content you want the LLM to process (which is the unpredictable part).
This prompt may work well enough, but it may also not. There are many ways in which the LLM can respond in a way that we do not like, at least occasionally. We may find that the LLM focuses on irrelevant details in the documents. Or that it has a different understanding of what is "plausible" than what we had in mind. Or it may indicate that almost all documents are entirely plausible (or conversely, that almost none are). Or the responses may not be formatted as we wish.
We may need to adjust the prompt to consistently get the responses we expect. To start, we can try this prompt and a few other simple ones, but the final prompt could end up being significantly longer and more detailed than this.
In general, as we test with more inputs (in this case, more documents), we will find more cases where the current prompt does not handle the input well, so we will adjust the prompt to better handle those cases. Sometimes we rephrase the prompt to make it clearer, and other times we add phrases to the prompt to address those specific cases. For example: "If the document makes metaphorical claims, evaluate the general intent and not the literal meaning." We may end up with a number of additional instructions like this in the prompt, which can help the prompt work well for those cases, but, of course, this can also make the prompt less effective for other inputs.
And, as prompts become longer and more complicated, they can become more difficult to adjust. It may become less and less clear what the effect will be of adding, removing, reorganizing, or rephrasing sentences in the prompt.
Other LLM-based applications may work with other types of textual data: text messages, emails, essays, newspaper articles, patent requests, etc. Or they may process images, audio, video, or other modalities. But regardless of the type of input, for a non-trivial application, the specific input that the application encounters (and transmits to the LLM) will be at least partly unpredictable. This means we will need a robust and well-specified prompt to handle a wide range of realistic inputs.
To take the example of emails, if an LLM-based application processes a collection of emails (that it will encounter in production, and that we cannot fully predict), there may be emails that are exceptionally: long, complex, nuanced, confusing, erratic, or otherwise different from what we had anticipated when training the prompt. The only way to test that your application will function reliably in production is to test it with a large, diverse, and realistic set of inputs (in this case, a large collection of realistic emails).
And for each test case, we need to carefully examine the LLM's response and check that it is appropriate. In some cases, this is straightforward. For example, we can pass a text to an LLM and ask it to classify it in a certain way. The LLM can classify the text by identifying the language (English, French, etc.), sentiment, toxicity, etc. In these cases, there is a real class for each input, and there is the class that the LLM returns. We simply need to check that they are the same: if the text is in Spanish and the LLM predicts Spanish, that is correct; otherwise, it is not. Many other LLM tasks also produce results that are easy to evaluate.
However, in some cases, evaluating the responses is not so simple. One example is when we ask the LLM to generate a longer response, such as a summary, translation, critique, suggestions for follow-up steps, or any other lengthy output based on the input. If you have ever looked at two or more different responses from an LLM (where each consists of one or more complete sentences, and possibly much longer) and tried to evaluate which is better, you know that this takes time. And it is prone to error. Some may be more succinct, others more nuanced, others clearer. Nevertheless — as difficult as it is to evaluate — we must assess them to determine the effectiveness of each prompt we try. One of the advantages of DSPy is that it allows you to automate this evaluation.
Prompt Engineering
To see the value of tools like DSPy, it is helpful to look at the alternative and the problem that DSPy solves. Normally, the way we work with LLMs is by using a technique known as prompt engineering. In doing so, we write a prompt, test it (usually with just a few inputs and simply looking at the outputs), write another prompt, test it similarly, and continue.
In simpler cases, this can work, but it has a number of limitations. One of them is: it is very time-consuming to test each candidate prompt with more than a small number of inputs. So, in practice, we normally test each prompt much less than we should. This can pose problems — testing each prompt with very few inputs can give us a misleading idea of which prompts work best.
To complicate matters — for each input, we should really test the prompt multiple times (and not just once), since LLMs are stochastic. If an LLM receives the same prompt (including the same values in the placeholders) multiple times, it may return different responses each time. And some may be better than others. If we have, say, 20 documents to test (in an example where the LLM will be used to estimate the plausibility of each document), ideally, we should test each one multiple times. If we test each 3 times, that means 60 tests in total. Which, in reality, is probably not going to happen. Probably not even close.
And, as mentioned, this is even more difficult when LLMs return longer outputs, as it is time-consuming to read them, and almost impossible to be consistent in how we evaluate them.
Thus, testing each candidate prompt takes time. Testing many candidate prompts takes much more time. And it is not clear that we can really compare them fairly.
All of this means that, in general, prompt engineering has the interesting quality of being both time-consuming and unreliable. It is a very slow, tedious, and error-prone process. Experienced developers can often spend hours or even days on a single prompt. And in the end, they cannot be certain that the one they chose is truly the strongest.
Is There a Better Method?
If we take a moment to reflect, we can see how we handle a similar situation when working with machine learning. If we build a neural network, a Random Forest model, XGBoost (or something similar), every time we train it, we do not manually test each item in the test set one by one. In fact, the idea of doing so seems a bit ridiculous. The process is automated; the testing is quite straightforward. We simply pass each item in the test set through the model, get a prediction for each, and run a function to generate an overall score.
For example, we can use Mean Squared Error or R Squared for a regression problem, and possibly F1 Score, MCC, or AUROC for a classification problem. By using a tool like scikit-learn, we can take the model's predictions for the test set and the corresponding ground truth values, and simply pass them to a function to calculate the overall score. We then have a single number indicating how well this model performed.
We can then, if we wish, retry with different features, different hyperparameters, different training data (or any other change from the previous model), retrain and re-execute.
Brief IA — L'actualité IA en français
L'essentiel de l'actualité de l'intelligence artificielle, décrypté et expliqué chaque jour.