How to build multi-hop reasoning with DSPy
Quick answer
Use
dspy to build multi-hop reasoning by defining multiple Signature classes representing each reasoning step and chaining them with dspy.ChainOfThought. This enables sequential AI calls where outputs from one step feed as inputs to the next, creating a multi-hop reasoning pipeline.PREREQUISITES
Python 3.8+OpenAI API key (free tier works)pip install dspy openai>=1.0
Setup
Install dspy and openai packages, and set your OpenAI API key as an environment variable.
- Run
pip install dspy openai - Export your API key:
export OPENAI_API_KEY='your_api_key'on Linux/macOS or set in Windows environment variables
pip install dspy openai Step by step
Define multiple dspy.Signature classes for each reasoning step, then chain them with dspy.ChainOfThought to perform multi-hop reasoning. The example below shows a two-step reasoning: first extracting key facts, then answering a question based on those facts.
import os
import dspy
from openai import OpenAI
# Configure DSPy with OpenAI LLM
lm = dspy.LM("openai/gpt-4o-mini", api_key=os.environ["OPENAI_API_KEY"])
dspy.configure(lm=lm)
# Step 1: Extract key facts from text
class ExtractFacts(dspy.Signature):
text: str = dspy.InputField()
facts: str = dspy.OutputField()
# Step 2: Answer question using extracted facts
class AnswerQuestion(dspy.Signature):
facts: str = dspy.InputField()
question: str = dspy.InputField()
answer: str = dspy.OutputField()
# Define the multi-hop reasoning chain
class MultiHopReasoning(dspy.ChainOfThought):
extract_facts = ExtractFacts()
answer_question = AnswerQuestion()
def __call__(self, text: str, question: str) -> str:
# Step 1: Extract facts
facts_result = self.extract_facts(text=text)
# Step 2: Answer question using facts
answer_result = self.answer_question(facts=facts_result.facts, question=question)
return answer_result.answer
# Instantiate the chain
chain = MultiHopReasoning()
# Example input
input_text = (
"Marie Curie was a physicist and chemist who conducted pioneering research on radioactivity."
)
input_question = "What field did Marie Curie contribute to?"
# Run multi-hop reasoning
answer = chain(text=input_text, question=input_question)
print("Answer:", answer) output
Answer: Marie Curie contributed to physics and chemistry, specifically in the field of radioactivity.
Common variations
You can extend multi-hop reasoning by adding more Signature classes for additional reasoning steps. Use async calls with dspy by defining async methods and calling await. You can also switch models by changing the LM initialization, e.g., to openai/gpt-4o for stronger reasoning.
import asyncio
async def async_multi_hop():
# Async version of the chain call
answer = await chain.acall(text=input_text, question=input_question)
print("Async answer:", answer)
asyncio.run(async_multi_hop()) output
Async answer: Marie Curie contributed to physics and chemistry, specifically in the field of radioactivity.
Troubleshooting
- If you get API authentication errors, verify
OPENAI_API_KEYis set correctly in your environment. - If outputs are empty or irrelevant, increase
max_tokensor adjust prompt templates inside yourSignatureclasses. - For rate limits, add retry logic or reduce request frequency.
Key Takeaways
- Use multiple
dspy.Signatureclasses to represent each reasoning step. - Chain steps with
dspy.ChainOfThoughtto enable multi-hop reasoning pipelines. - Configure
dspywith an OpenAI LLM for seamless AI calls. - Support async and different models by adjusting
dspy.LMand method calls. - Handle API errors by verifying keys and tuning prompt parameters.