Ruby-native agents on DSPy's programming model · v1.0.2
Build typed AI agents in Ruby
Define task contracts with Sorbet. Give models typed tools. Keep state, limits, errors, and side effects in Ruby.
- Typed agent contracts with Sorbet
- Evaluation and prompt optimization
- OpenAI, Anthropic, Gemini, Ollama
class AnswerWeather < DSPy::Signature
description "Answer weather questions with tools"
input { const :question, String }
output { const :answer, String }
end
agent = DSPy::ReAct.new(
AnswerWeather,
tools: [WeatherTool.new],
max_iterations: 3
)
agent.call(question: "Weather in Valencia?").answer
# => "72°F and sunny in Valencia"
A signature types the boundary; Ruby owns the tools and the loop.
The shape of an agent
A contract, a tool, a bounded loop
A signature defines the task and result. Ruby implements the tools and owns permissions, errors, side effects, and iteration limits — the same shape every program on this page is built from.
The model chooses whether to call a tool or finish; Ruby executes each tool and enforces the loop limit. Evaluate complete runs with examples and metrics, then use an optimizer to search for better instructions and demonstrations.
The same shape, real programs
Each runs from a checkout of the repository. Here is the piece that matters; the rest is in the example's README.
Agents & tools
A read-only GitHub agent
A ReAct agent handed the GitHub CLI as read-only tools — it inspects repos, issues, and pull requests, and cannot write.
class GitHubAssistant < DSPy::Signature
description "Operate on a repo with the GitHub CLI"
input do
const :task, String
const :repository, String, default: ""
end
output { const :result, String }
end
# Read-only GitHub CLI tools — inspect only, no writes
tools = DSPy::Tools::GitHubCLIToolset.to_tools
agent = DSPy::ReAct.new(
GitHubAssistant,
tools: tools,
max_iterations: 15
)
agent.call(
task: "List open PRs and flag those ready for review",
repository: "vicentereig/dspy.rb"
).result
Type-driven control
Union types choose the action
The model returns one of several typed actions in a single union field; Ruby pattern matching dispatches on the T::Struct it chose.
class CoffeeShopSignature < DSPy::Signature
description "Analyze a request and pick an action"
input { const :customer_request, String }
output do
# one typed action from a union
const :action, T.any(
CoffeeShopActions::MakeDrink,
CoffeeShopActions::RefundOrder,
CoffeeShopActions::CallManager
)
end
end
# Ruby pattern-matches the action the model chose
case (action = result.action)
when CoffeeShopActions::MakeDrink
"Making a #{action.size.serialize} #{action.drink_type}"
when CoffeeShopActions::RefundOrder
"Refunding $#{action.refund_amount}"
when CoffeeShopActions::CallManager
"Escalating: #{action.issue}"
end
Optimization
Compile a classifier with MIPROv2
Give the optimizer a program, a metric, and labelled examples; it searches instructions and demonstrations and keeps the best.
examples/ade_optimizer_miprov2class ADETextClassifier < DSPy::Signature
description "Flag adverse drug events in clinical text"
input { const :text, String }
output { const :label, ADELabel }
end
# Search instructions + demonstrations against a metric
optimizer = DSPy::Teleprompt::MIPROv2.new(metric: metric)
result = optimizer.compile(
baseline_program,
trainset: train_examples,
valset: val_examples
)
optimized_program = result.optimized_program
More in the repository
ReAct loop.
examples/sentiment-evaluation
A sentiment classifier compared across a built-in, a custom, and a weighted-demonstration metric.
examples/multimodal
Image analysis and bounding-box extraction returned as typed outputs.
Built for Ruby developers
Ruby types and control flow for agents and model-backed programs.
- Type-safe from the start
- Signatures validate inputs and convert provider responses into declared Ruby types. Invalid outputs fail before application code uses them.
- Test like normal code
- Use RSpec for deterministic behavior and evaluation sets for model behavior. Tests and metrics answer different questions.
- Optimize with data
- Give an optimizer examples and a metric. It can search instructions and demonstrations, then persist the resulting prompt artifacts.
- Compose and reuse
- Compose modules with Ruby control flow. Keep fixed steps deterministic; use an agent when the model has a useful choice among tools or actions.
- Control the runtime
- Ruby owns state, permissions, budgets, errors, and termination. Traces and persisted prompt artifacts make executions inspectable.
- Observe behavior
- Modules emit events and tracing attributes. Optional integrations export spans; evaluation measures behavior against examples and metrics.
Choose the adapter for the model you deploy
DSPy.rb keeps provider SDKs in separate packages. Model capabilities still depend on the selected provider, endpoint, and SDK version.
- OpenAI
- Install
dspy-openai. Check the selected model and endpoint for structured output, tools, media, and streaming support. - Google Gemini
- Install
dspy-gemini. Verify model capabilities before relying on structured output, tools, media, or streaming. - Anthropic Claude
- Install
dspy-anthropic. Verify model capabilities before relying on structured output, tools, media, or streaming. - RubyLLM
- Install
dspy-ruby_llmto route models through RubyLLM’s registry with theruby_llm/…prefix. It reaches every provider RubyLLM supports and reuses an existing RubyLLM configuration. - Local & compatible
- Use Ollama or an OpenAI-compatible endpoint. Confirm the server and model support each capability your agent needs.
One agent, different models: keep the signature, tools, and Ruby control flow stable when changing adapters. Re-evaluate the agent — model behavior and capabilities can change.
Build your first typed program
Define the contract, evaluate the output, and run an optimizer when examples, a metric, and a budget are ready.