If you’re immersed in the world of artificial intelligence, you’re likely familiar with transformative tools like LangChain, which are pivotal for creating sophisticated AI workflows. These tools unlock a myriad of possibilities for automation and efficiency. However, a new contender has recently entered the arena—DSPY, which introduces a novel approach to AI workflow development.
DSPY is recognized for its innovative use of language models. According to the LangChain Expression Language Documentation, “DSPY is a fantastic framework for LLMs that introduces an automatic compiler that teaches LMs how to conduct the declarative steps in your program. Specifically, the DSPy compiler will internally trace your program and then craft high-quality prompts for large LMs (or train automatic finetunes for small LMs) to teach them the steps of your task.”
The appeal of DSPY lies in its focus on programming simplicity and its ability to alleviate the intricate task of prompt engineering. This enables developers to focus more on the core aspects of building and optimizing AI workflows, rather than getting bogged down in the details.
This blog will begin with an overview of the LangChain Expression Language to establish a foundational comparison before exploring DSPY, starting with its fundamental building blocks. We will review coding examples to demonstrate DSPY’s capabilities and illustrate its practical implications in AI development. The aim is to familiarize you with DSPY and provide a detailed comparison with LangChain through practical, real-world applications.
LangChain introduced the LangChain Expression Language (LCEL) on August 1st, 2023. It is a new syntax aimed at simplifying the creation and composition of AI-driven workflows. LCEL enables developers to transition from prototypes to production without any code modifications, making it an ideal choice for scalable AI solutions.
For our demonstration, we’ll examine a simple Sales Outreach pipeline. This use case involves chaining operations to gather insights, tailor communication, and generate an outreach email, illustrating the practical application of LCEL
Creating this pipeline involves several prompts, each designed to perform specific tasks in the process. Crafting these prompts initially required significant effort and thought, highlighting areas where the efficiency of prompt engineering could be enhanced.
Here’s how the prompts and chains come together:
import langchain as lc
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain_core.prompt_values import ChatPromptValue
from langchain.schema.output_parser import StrOutputParser
#### Str Out Parser
str_parser = StrOutputParser()
#### Models
gpt4 = ChatOpenAI(model="gpt-4",openai_api_key=api_key)
# Prompt for summarizing the target company
summary_of_target_company = """
Given the URL and extracted information from the target company's website:
- Provide a brief overview of the company, summarizing its main business activities and operations.
- Highlight the products or services the company offers.
- Identify the company's primary audience or customer base.
URL: {target_company_url}
Extracted Information: {extracted_information}
"""
# Prompt for personalizing problem and solution
personalized_problem_and_solution = """
Based on the summary of the target company:
- Identify a specific problem the company might be facing.
- Propose a tailored solution leveraging our company's strengths.
Company Summary: {target_company_summary}
Problem We are Targetting: {target_problem}
Our Proposed Solution: {our_solution}
"""
# Prompt for crafting the outreach email
outreach_email = """
Using the personalized problem and solution:
- Craft an outreach email that introduces our company and the proposed solution.
- Include a call to action for a detailed discussion.
Our Company Information: {our_company}
Target Company Summary: {target_company_summary}
Personalized Problem and Solution: {personalized_problem_solution}
Sales Rep Name: {sales_rep_name}
"""
### Creating Prompt Templates
prompt_summary = ChatPromptTemplate.from_template(summary_of_target_company)
prompt_problem_and_solution = ChatPromptTemplate.from_template(personalized_problem_and_solution)
prompt_email = ChatPromptTemplate.from_template(outreach_email)
# Chaining the prompts together to form the Sales Outreach Pipeline
chain_summary = (
{
"target_company_url": itemgetter("target_company_url"),
"extracted_information": itemgetter("extracted_information"),
}
| prompt_summary
| gpt4
| str_parser
)
chain_prob_and_sol = (
{
"target_company_summary": chain_summary,
"target_problem": itemgetter("target_problem"),
"our_solution": itemgetter("our_solution"),
}
| prompt_problem_and_solution
| gpt4
| str_parser
)
chain_email = (
{
"target_company_summary": chain_summary,
"personalized_problem_solution": chain_prob_and_sol,
"our_company": itemgetter("our_company"),
"sales_rep_name": itemgetter("sales_rep_name"),
}
| prompt_problem_and_solution
| gpt4
| str_parser
)
email = chain_email.invoke(
{
"target_company_url": target_company_url,
"extracted_information": extracted_information,
"target_problem": target_problem,
"our_solution": our_solution,
"our_company": our_company,
"sales_rep_name": "John Doe",
}
)
This example illustrates the efficiency of chaining multiple operations using LangChain Expression Language. The new syntax not only simplifies component reuse but also streamlines AI workflow management with fewer lines of code. However, even with a straightforward scenario involving just three prompts, prompt engineering remains a critical next step. Each adjustment carries the potential to significantly impact the entire process, sometimes leading to suboptimal outcomes. This necessitates a cautious approach to modifications, underscoring the delicate balance required in optimizing AI-driven workflows.
Transitioning from LangChain, DSPY offers a fresh perspective on managing AI workflows. While LangChain provides robust capabilities, DSPY simplifies the prompt engineering process, significantly reducing both time and complexity involved in workflow development.
The DSPY framework is engineered around a series of transformation steps, starting with an input and concluding with an output. This structured approach breaks down into key building blocks that enhance the creation and optimization of workflows:
The Signature component in DSPY facilitates auto-prompting and manages data by establishing a structured format for input-output pairs. For example, consider the creation of a sales outreach email:
import dspy
import os
openai_api_key = os.environ.get("OPENAI_API_KEY")
gpt35 = dspy.OpenAI(model='gpt-3.5-turbo')
gpt4 = dspy.OpenAI(model='gpt-4-turbo',api_key=openai_api_key,max_tokens=2000)
dspy.settings.configure(lm=gpt4)
class SalesOutreachEmail(dspy.Signature):
"""Writes a sales outreach email to the target company on behalf of our company focusing on the target problem and solution identified."""
target_company_information = dspy.InputField(desc="Comprehensive view of a company's main offerings, processes, and pain points")
our_company_information = dspy.InputField(desc="Information about our company, its services, ratings, and successful projects")
target_problem = dspy.InputField(desc="The problem we are targeting to pitch for")
our_solution = dspy.InputField(desc="The solution we are offering")
sales_rep_name = dspy.InputField(desc="Name of the sales representative from our company")
email = dspy.OutputField(desc="Final email crafted to pitch the solution and initiate further discussion")
In this setup, field names are not just placeholders but are semantically meaningful, aiding DSPY in understanding and processing the context more effectively.
Modules dictate the dataflow within your program, ranging from straightforward direct prompts to more complex processes like Chain of Thought (CoT). For instance:
write_email_with_COT = dspy.ChainOfThought(SalesOutreachEmail)
generation = write_email_with_COT(
target_company_information=dev_example[0].target_company_information,
our_company_information=our_company_information,
target_problem=target_problem_sample_12,
our_solution=solution_sample_12,
sales_rep_name="Fizah"
)
This module not only generates the final output but also provides a rationale, enhancing transparency and understanding of how conclusions were reached.
This is the rationale that it printed:
We need to clearly articulate how our AI solution directly addresses Shopify’s potential pain points, emphasizing the benefits and the value it adds to their platform. The email should be concise, engaging, and tailored to Shopify’s context, highlighting how our AI chatbot can enhance their customer service and user experience. We should also establish credibility by mentioning our successful track record and expertise in AI solutions. Finally, the email should include a call to action, inviting them to a discussion where we can explore the potential integration in more detail.
And this is the email it printed without any optimizations:
Subject: Enhance Shopify’s Customer Experience with AI-Driven Personalization
Dear Shopify Team,
I hope this message finds you well. My name is Fizah, and I am reaching out from Quid Sol, where we specialize in elevating e-commerce platforms through tailored AI solutions. Having reviewed Shopify’s impressive array of services and global reach, we believe we can add significant value to your platform, particularly in enhancing user interactions and personalization.
…..
Thank you for considering this opportunity to enhance Shopify’s platform. I look forward to the possibility of working together to create even more personalized and engaging shopping experiences for your users.
Warm regards,
Fizah
Sales Representative
Quid Sol
The optimizer refines the modules within the pipeline to enhance performance based on selected metrics. An effective implementation for larger datasets would involve using methods like BootstrapFewShot, BootstrapFewShotWithRandomSearch, or MIPRO. However, utilizing Bootstrap optimizers generally requires at least 50 examples, which is why we could not implement this this use-case.
from dspy.teleprompt import BootstrapFewShot
# Validation logic: check that the predicted answer is correct.
# Also check that the retrieved context does actually contain that answer.
def validate_answer(example, pred, trace=None):
answer_EM = dspy.evaluate.answer_exact_match(example, pred)
#answer_PM = dspy.evaluate.answer_passage_match(example, pred)
return answer_EM
# Set up a basic teleprompter, which will compile our RAG program.
teleprompter = BootstrapFewShot(metric=validate_answer)
# Compile!
compiled_fs = teleprompter.compile(fs(), trainset=trainset)
As we have explored throughout this blog, DSPY presents a compelling architecture that dramatically simplifies the setup of complex workflows. Even without delving into advanced optimization techniques, DSPY stands out by minimizing the manual effort required to write and optimize prompts. This allows developers to achieve effective results more quickly and with less overhead.
By automating much of the prompt engineering process, DSPY enables users to focus on refining the logic and functionality of their AI-driven applications, paving the way for more innovative and efficient solutions in the realm of artificial intelligence. This streamlined approach not only saves valuable time but also enhances the overall quality of the workflows, making DSPY a robust tool in the AI developer’s toolkit.
In an upcoming case study, we plan to record the elapsed time, quality, and efficiency using DSPY in a prototype environment to demonstrate the practical benefits and improvements brought by these optimization and evaluation tools.