class Riffer::Agent
Riffer::Agent is the base class for all agents in the Riffer framework.
Provides orchestration for LLM calls, tool use, and message management. Subclass this to create your own agents.
See Riffer::Messages and Riffer::Providers.
class MyAgent < Riffer::Agent model 'openai/gpt-4o' instructions 'You are a helpful assistant.' end agent = MyAgent.new agent.generate('Hello!')
Constants
- INTERRUPT_MAX_STEPS
Attributes
The per-instance Riffer::Agent::Config. Either the class-level default or an explicit Config passed to +Agent.new(config:)+.
The mutable runtime context, a Riffer::Agent::Context value object threaded into every Proc-based DSL setting, guardrail, tool runtime, and skills resolution, and shared with every Riffer::Agent::Run this agent executes. Exposes:
-
context.skills— the resolvedRiffer::Skills::Context(when skills are configured), set atAgent.newtime. -
context.token_usage— the cumulativeRiffer::Providers::TokenUsage, updated by eachRunas the loop progresses. -
context[:key]/context.dig(:key)— Hash-style reads for caller-provided keys (e.g.context[:agent],context[:tenant]).:skillsand:token_usageare reserved and cannot be passed by the caller.
The system message built from the configured instructions, or nil when no instructions are configured. Built once at Agent.new using the constructor context: and cached. Useful for persistence flows.
The resolved model name (the part after “provider/”), used as the model argument on every LLM call. Resolved eagerly at Agent.new.
The provider client. Built eagerly at Agent.new from the configured provider class and +Config#provider_options+, then handed to every Riffer::Agent::Run this agent executes. Public so tests can pre-queue responses on Riffer::Providers::Mock before calling generate.
The resolved provider name (the part before “provider/”), e.g. +“openai”+. Resolved eagerly at Agent.new alongside model_name; together they form the provider-neutral model identifier the agent serializes.
The conversation handle. See Riffer::Agent::Session.
The system message describing the configured skills catalog, or nil when skills are unconfigured or the catalog is empty. Built once at Agent.new and cached.
The Riffer::Agent::StructuredOutput wrapping the configured schema, or nil when structured output is not configured. Resolved eagerly at Agent.new.
The tool runtime instance used to execute tool calls. Resolved eagerly at Agent.new (Proc-form tool_runtime is called against context once).
The tool classes the LLM sees on every call this agent makes. Resolved eagerly at Agent.new (Proc-form uses_tools is called against context once; MCP tools and the skill_activate tool are merged in).
Public Class Methods
Source
# File lib/riffer/agent.rb, line 189 def self.all subclasses #: Array[singleton(Riffer::Agent)] end
Returns all agent subclasses.
Source
# File lib/riffer/agent.rb, line 34 def self.config @config ||= Riffer::Agent::Config.new end
Returns the per-class Riffer::Agent::Config value object holding every DSL setting. Lazily initialized on first read; each subclass has its own.
Source
# File lib/riffer/agent.rb, line 181 def self.find(identifier) all.find { |agent_class| agent_class.identifier == identifier.to_s } end
Finds an agent class by identifier.
Source
# File lib/riffer/agent.rb, line 223 def self.from_h(hash, context: nil, session: nil, tool_resolver: Riffer::Agent::Serializer::DEFAULT_TOOL_RESOLVER, tool_runtime: nil) Riffer::Agent::Serializer.from_h(hash, context: context, session: session, tool_resolver: tool_resolver, tool_runtime: tool_runtime) end
Reconstructs a runnable agent from a wire hash produced by to_h.
Delegates to Riffer::Agent::Serializer.from_h. See it for the session seed, the tool_resolver / tool_runtime injection points, and what does not transfer.
Source
# File lib/riffer/agent.rb, line 235 def self.from_json(json, context: nil, session: nil, tool_resolver: Riffer::Agent::Serializer::DEFAULT_TOOL_RESOLVER, tool_runtime: nil) Riffer::Agent::Serializer.from_json(json, context: context, session: session, tool_resolver: tool_resolver, tool_runtime: tool_runtime) end
Reconstructs a runnable agent from a JSON string produced by to_json.
Delegates to Riffer::Agent::Serializer.from_json, which parses the JSON (with symbol keys) for you. See Riffer::Agent::Serializer.from_h for the session seed and the tool_resolver / tool_runtime injection points.
Source
# File lib/riffer/agent.rb, line 200 def self.generate(prompt = nil, files: nil, context: nil) new(context: context).generate(prompt, files: files) end
Generates a response using a new agent instance.
context: is threaded into new; prompt and files: are forwarded to generate.
Source
# File lib/riffer/agent.rb, line 248 def self.guardrail(phase, with:, **options) config.add_guardrail(phase, klass: with, options: options) end
Registers a guardrail for input, output, or both phases.
- phase
-
:before, :after, or :around.
- with
-
the guardrail class (must be subclass of
Riffer::Guardrail). - options
-
additional options passed to the guardrail.
Raises Riffer::ArgumentError if phase is invalid or guardrail is not a Guardrail class.
Source
# File lib/riffer/agent.rb, line 258 def self.guardrails_for(phase) config.guardrails_for(phase) end
Returns the registered guardrail configs for a given phase.
- phase
-
:before or :after.
Source
# File lib/riffer/agent.rb, line 42 def self.identifier(value = nil) value.nil? ? (config.identifier || class_name_to_path(name)) : (config.identifier = value) end
Gets or sets the agent identifier.
Source
# File lib/riffer/agent.rb, line 68 def self.instructions(value = nil) value.nil? ? config.instructions : (config.instructions = value) end
Gets or sets the agent instructions.
Accepts a static string or a Proc for dynamic instructions. When a Proc is given, it is called at generate time and receives the context hash (which may be nil).
instructions "You are a helpful assistant." instructions -> (context) { "You are assisting #{context[:name]}" }
Source
# File lib/riffer/agent.rb, line 115 def self.max_steps(*value) return config.max_steps if value.empty? config.max_steps = value.first end
Gets or sets the maximum number of LLM call steps in the tool-use loop.
Defaults to Riffer::Agent::Config::DEFAULT_MAX_STEPS (16). Set to nil for unlimited steps. The splat distinguishes a getter call (no argument) from setting the limit to nil.
max_steps # reads the current limit max_steps 8 # cap the loop at 8 steps max_steps nil # unlimited
Source
# File lib/riffer/agent.rb, line 141 def self.mcp_configs config.mcp_configs end
Returns the accumulated +use_mcp+ configurations for this agent class.
: () -> Array[Hash[Symbol, untyped]]
Source
# File lib/riffer/agent.rb, line 50 def self.model(value = nil) value.nil? ? config.model : (config.model = value) end
Gets or sets the model string (e.g., “openai/gpt-4o”) or Proc.
Source
# File lib/riffer/agent.rb, line 84 def self.model_options(options = nil) options.nil? ? config.model_options : (config.model_options = options) end
Gets or sets model options passed to generate_text/stream_text.
Source
# File lib/riffer/agent.rb, line 342 def initialize(session: nil, context: nil, config: nil) @config = config || self.class.config @context = Riffer::Agent::Context.new(context || {}) @provider_name, @model_name = resolve_provider_and_model @provider = build_provider @context.skills = resolve_skills @structured_output = resolve_structured_output @tools = resolve_tools @tool_runtime = resolve_tool_runtime @instruction_message = build_instruction_message @skills_message = build_skills_message @session = session || Riffer::Agent::Session.new(messages: [@instruction_message, @skills_message].compact) @session.set(Riffer::Agent::Session::Repair.prune_orphans(@session.messages)) end
Initializes a new agent.
When session: is omitted, a fresh Riffer::Agent::Session is built and seeded with the system instruction message and skills catalog (when configured), using context:. When session: is provided, the agent uses it as-is —the caller is responsible for the session’s contents (typical use case: cross-process resume from persisted history). With Riffer.config.experimental_history_healing on, a provided session is healed at construction time so the tool_use ↔ tool_result invariant holds before the next inference call.
context: flows through Proc-based instructions, model, skills resolution, tool resolution, guardrails, and tool runtime. It is fixed for the lifetime of the agent.
Raises Riffer::ArgumentError if the configured model string is invalid (must be “provider/model” format).
Source
# File lib/riffer/agent.rb, line 76 def self.provider_options(options = nil) options.nil? ? config.provider_options : (config.provider_options = options) end
Gets or sets provider options passed to the provider client.
Source
# File lib/riffer/agent.rb, line 168 def self.skills(&block) if block skills_config = Riffer::Skills::Config.new skills_config.instance_eval(&block) config.skills_config = skills_config end config.skills_config end
Configures skills for this agent via a block DSL.
Returns the current Riffer::Skills::Config when called without a block.
skills do backend Riffer::Skills::FilesystemBackend.new(".skills") adapter Riffer::Skills::XmlAdapter activate ["code-review"] end
Source
# File lib/riffer/agent.rb, line 211 def self.stream(prompt = nil, files: nil, context: nil) new(context: context).stream(prompt, files: files) end
Streams a response using a new agent instance.
context: is threaded into new; prompt and files: are forwarded to stream.
Source
# File lib/riffer/agent.rb, line 94 def self.structured_output(params = nil, &block) if block params = Riffer::Params.new params.instance_eval(&block) end config.structured_output = params if params config.structured_output end
Gets or sets the structured output schema for this agent.
Accepts a Riffer::Params instance or a block evaluated against a new Params.
Source
# File lib/riffer/agent.rb, line 152 def self.tool_runtime(value = nil) value.nil? ? config.tool_runtime : (config.tool_runtime = value) end
Gets or sets the tool runtime for this agent.
Accepts a Riffer::Tools::Runtime subclass, a Riffer::Tools::Runtime instance, or a Proc. Defaults to Riffer.config.tool_runtime when unset.
Source
# File lib/riffer/agent.rb, line 134 def self.use_mcp(tag) config.add_mcp(tag) end
Opts this agent into tools from all MCP registrations that share any of the given tag(s). +tag+ - a String or Symbol; matched against registration manifest tags.
: (String | Symbol) -> void
Source
# File lib/riffer/agent.rb, line 124 def self.uses_tools(value = nil) value.nil? ? config.tools_config : (config.tools_config = value) end
Gets or sets the tools used by this agent.
Public Instance Methods
Source
# File lib/riffer/agent.rb, line 376 def generate(prompt = nil, files: nil) Riffer::Agent::Run.generate(agent: self, prompt: prompt, files: files) end
Generates a response from the agent.
Runs the inference loop via Riffer::Agent::Run.generate. When prompt is given, a new Riffer::Messages::User is appended to the session (silently — on_message does not fire for user inputs) and then the loop runs. When prompt is omitted, the loop runs against the current session — useful for resuming a persisted conversation whose last turn is already a user message, or for picking up pending tool calls after an interrupt.
files: requires prompt. Pass files to attach to the new user message.
Source
# File lib/riffer/agent.rb, line 410 def interrupt!(reason = nil) throw :riffer_interrupt, reason end
Interrupts the agent loop.
Call from an on_message callback to cleanly interrupt the loop. Equivalent to throw :riffer_interrupt, reason.
When Riffer.config.experimental_history_healing is enabled, riffer fills any orphaned tool_use on the way out with a placeholder Riffer::Messages::Tool carrying +error_type: :interrupted+. The filled call_ids are exposed on +Riffer::Agent::Response#healed_tool_call_ids+ (and the streaming Riffer::StreamEvents::Interrupt event).
Source
# File lib/riffer/agent.rb, line 391 def stream(prompt = nil, files: nil) raise Riffer::ArgumentError, "Structured output is not supported with streaming. Use #generate instead." if @structured_output Riffer::Agent::Run.stream(agent: self, prompt: prompt, files: files) end
Streams a response from the agent.
Runs the inference loop via Riffer::Agent::Run.stream, returning an Enumerator of Riffer::StreamEvents.
Raises Riffer::ArgumentError if structured output is configured.
See generate for prompt/files semantics.
Source
# File lib/riffer/agent.rb, line 419 def to_h Riffer::Agent::Serializer.to_h(agent: self) end
Snapshots this resolved agent into a self-contained, provider-neutral wire hash. Delegates to Riffer::Agent::Serializer.to_h.
Source
# File lib/riffer/agent.rb, line 429 def to_json(*) Riffer::Agent::Serializer.to_json(agent: self) end
Snapshots this resolved agent into a wire JSON string. Delegates to Riffer::Agent::Serializer.to_json. The +*+ absorbs the JSON generator state argument so JSON.generate(agent) works too.