Quick Start
This path uses OpenAI for one small sentiment classifier; model output can vary.
Your First DSPy Program
1. Create the Gemfile
Create a directory for the program and save this as Gemfile:
source 'https://rubygems.org'
gem 'dspy'
gem 'dspy-openai'
dspy provides signatures and modules. dspy-openai provides the adapter used by the openai/* model identifier below. The package and capability matrix lists other packages and their boundaries.
Install both gems:
bundle install
2. Set the API key
Export an OpenAI API key in the same shell:
export OPENAI_API_KEY=sk-your-key-here
DSPy.rb does not load .env files. If your application uses one, load it with your own environment library before reading the key.
3. Save the program
Save this exact program as classify.rb:
require 'dspy'
class Classify < DSPy::Signature
description "Classify the sentiment of a sentence."
class Sentiment < T::Enum
enums do
Positive = new('positive')
Negative = new('negative')
Neutral = new('neutral')
end
end
input do
const :sentence, String
end
output do
const :sentiment, Sentiment
const :confidence, Float
end
end
DSPy.configure do |config|
config.lm = DSPy::LM.new(
'openai/gpt-4o-mini',
api_key: ENV.fetch('OPENAI_API_KEY')
)
end
classifier = DSPy::Predict.new(Classify)
result = classifier.call(sentence: "This book was fun to read!")
puts result.sentiment.serialize
puts result.confidence
4. Run it
bundle exec ruby classify.rb
The program prints a sentiment value followed by a confidence value. Do not depend on a particular label, confidence, or wording: those depend on the model and request. When prediction succeeds, result.sentiment is a Classify::Sentiment and result.confidence is a Float.
What the Boundary Guarantees
The signature supplies the task description plus the input and output schemas. DSPy::Predict builds the provider request, converts the response to the declared Ruby types, and rejects incompatible output.
Typed output validation constrains the result shape; it does not prove that the answer is correct. Use named examples and a metric to gather evidence about model behavior; see Examples and Datasets and Evaluation.
Failure and Testing Boundaries
Because the program uses ENV.fetch('OPENAI_API_KEY'), running it without that variable raises Ruby’s KeyError before DSPy::LM is created. An installed core gem without dspy-openai instead raises DSPy::LM::MissingAdapterError when an openai/* model is configured. Provider authentication, transport, rate-limit, and response-validation failures remain separate application errors; Troubleshooting lists their owning layers.
Represent expected domain uncertainty in the signature, for example with an enum value such as Unknown. Handle configuration and provider failures around the call rather than turning them into a model-generated result.
For deterministic tests, assert the signature schema, result classes, enum membership, and failure boundaries. Record provider calls with VCR or evaluate behavior against examples and a named metric. Avoid tests that require one exact label, confidence, or explanation from a live model.
Key Concepts
Modules and Agents
DSPy::Predict performs one typed prediction. DSPy::ChainOfThought adds a reasoning field, while DSPy::ReAct runs a bounded loop in which the model can select tools. Keep known sequencing and branches in Ruby; use an agent only when a bounded model choice is useful.
Give an Agent a Typed Tool
Tools expose Ruby methods through Sorbet signatures:
class WeatherTool < DSPy::Tools::Base
tool_name 'weather'
tool_description 'Get weather for a location'
sig { params(location: String).returns(String) }
def call(location:)
"72°F and sunny in #{location}"
end
end
The application owns the tool implementation, side effects, permissions, error handling, and iteration limits. ReAct only owns the bounded loop in which the model selects a tool or returns a result.
Choose the Next Abstraction
- Learn how signatures and types define the task contract.
- Choose among predictors.
- Compose fixed steps with modules.
- Add callable capabilities with toolsets.