Class: LLM::Agent

Inherits:
Object
  • Object
show all
Defined in:
lib/llm/agent.rb

Overview

LLM::Agent provides a class-level DSL for defining reusable, preconfigured assistants with defaults for model, tools, schema, and instructions.

It wraps the same stateful runtime surface as LLM::Context: message history, usage, persistence, streaming parameters, and provider-backed requests still flow through an underlying context. The defining behavior of an agent is that it automatically resolves pending tool calls for you during talk and respond, instead of leaving tool loops to the caller.

Notes:

  • Instructions are injected once unless a system message is already present.
  • An agent automatically executes tool loops (unlike LLM::Context).
  • The automatic tool loop enables the wrapped context's guard by default. The built-in LLM::LoopGuard detects repeated tool-call patterns and blocks stuck execution before more tool work is queued.
  • The default tool attempt budget is 25. After that, the agent sends advisory tool errors back through the model and keeps the loop in-band. Set tool_attempts: nil to disable that advisory behavior.
  • Tool loop execution can be configured with concurrency :call, :thread, :task, :fiber, or :ractor.

Examples:

class SystemAdmin < LLM::Agent
  model "gpt-4.1-nano"
  instructions "You are a Linux system admin"
  tools Shell
  schema Result
end

llm = LLM.openai(key: ENV["KEY"])
agent = SystemAdmin.new(llm)
agent.talk("Run 'date'")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(llm, params = {}) ⇒ Agent

Returns a new instance of Agent.

Parameters:

  • provider (LLM::Provider)

    A provider

  • params (Hash) (defaults to: {})

    The parameters to maintain throughout the conversation. Any parameter the provider supports can be included and not only those listed here.

Options Hash (params):

  • :model (String)

    Defaults to the provider's default model

  • :tools (Array<LLM::Function>, nil)

    Defaults to nil

  • :skills (Array<String>, nil)

    Defaults to nil

  • :schema (#to_json, nil)

    Defaults to nil

  • :stream (Object, Proc, nil)

    Optional stream override for this agent instance

  • :tracer (LLM::Tracer, Proc, nil)

    Optional tracer override for this agent instance

  • :concurrency (Symbol, Array<Symbol>, nil)

    Defaults to the agent class concurrency



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/llm/agent.rb', line 187

def initialize(llm, params = {})
  @llm = llm
  fields = %i[model skills schema tracer stream tools concurrency instructions confirm]
  fields_ivar = %i[tracer concurrency instructions confirm]
  fields.each do |field|
    resolvable = params.key?(field) ? params.delete(field) : self.class.public_send(field)
    resolve_symbol = !%i[concurrency confirm].include?(field)
    resolved = resolvable != nil ? resolve_option(self, resolvable, resolve_symbol:) : resolvable
    resolved = [*resolved].map(&:to_s) if field == :confirm && resolved
    if field == :model
      params[field] = resolved unless resolved.nil? || params.key?(field)
    elsif resolved && !fields_ivar.include?(field)
      params[field] ||= resolved
    elsif fields_ivar.include?(field)
      instance_variable_set(:"@#{field}", resolved)
    end
  end
  @ctx = LLM::Context.new(llm, {guard: true}.merge(params))
end

Instance Attribute Details

#llmLLM::Provider (readonly)

Returns a provider

Returns:



43
44
45
# File 'lib/llm/agent.rb', line 43

def llm
  @llm
end

Class Method Details

.tools(*tools, &block) ⇒ Array<LLM::Function>

Set or get the default tools

Parameters:

Returns:

  • (Array<LLM::Function>)

    Returns the current tools when no argument is provided



62
63
64
65
# File 'lib/llm/agent.rb', line 62

def self.tools(*tools, &block)
  return @tools || [] if tools.empty? && !block
  @tools = block || tools.flatten
end

.skills(*skills, &block) ⇒ Array<String>?

Set or get the default skills

Parameters:

  • skills (Array<String>, nil)

    One or more skill directories

Returns:

  • (Array<String>, nil)

    Returns the current skills when no argument is provided



73
74
75
76
# File 'lib/llm/agent.rb', line 73

def self.skills(*skills, &block)
  return @skills if skills.empty? && !block
  @skills = block || skills.flatten
end

.schema(schema = nil, &block) ⇒ #to_json?

Set or get the default schema

Parameters:

  • schema (#to_json, nil) (defaults to: nil)

    The schema

Returns:

  • (#to_json, nil)

    Returns the current schema when no argument is provided



84
85
86
87
# File 'lib/llm/agent.rb', line 84

def self.schema(schema = nil, &block)
  return @schema if schema.nil? && !block
  @schema = block || schema
end

.instructions(instructions = nil) ⇒ String?

Set or get the default instructions

Parameters:

  • instructions (String, nil) (defaults to: nil)

    The system instructions

Returns:

  • (String, nil)

    Returns the current instructions when no argument is provided



95
96
97
98
# File 'lib/llm/agent.rb', line 95

def self.instructions(instructions = nil)
  return @instructions if instructions.nil?
  @instructions = instructions
end

.concurrency(concurrency = nil) ⇒ Symbol, ...

Set or get the tool execution concurrency.

Parameters:

  • concurrency (Symbol, Array<Symbol>, nil) (defaults to: nil)

    Controls how pending tool loops are executed:

    • :call: sequential calls
    • :thread: concurrent threads
    • :task: concurrent async tasks
    • :fiber: concurrent scheduler-backed fibers
    • :fork: forked child processes
    • :ractor: concurrent Ruby ractors for class-based tools; MCP tools are not supported, and this mode is especially useful for CPU-bound tool work Usually pass a single strategy. Arrays are only for advanced mixed-work cases and are not needed for normal queued stream tool loops.

Returns:

  • (Symbol, Array<Symbol>, nil)


115
116
117
118
# File 'lib/llm/agent.rb', line 115

def self.concurrency(concurrency = nil)
  return @concurrency if concurrency.nil?
  @concurrency = concurrency
end

.tracer(tracer = nil, &block) ⇒ LLM::Tracer, ...

Set or get the default tracer.

When a block is provided, it is stored and evaluated lazily against the agent instance during initialization so it can build a tracer from the resolved provider.

Examples:

class Agent < LLM::Agent
  tracer { LLM::Tracer::Logger.new(llm, io: $stdout) }
end

Parameters:

Yield Returns:

Returns:



135
136
137
138
# File 'lib/llm/agent.rb', line 135

def self.tracer(tracer = nil, &block)
  return @tracer if tracer.nil? && !block
  @tracer = block || tracer
end

.stream(stream = nil, &block) ⇒ Object, ...

Set or get the default stream.

When a block is provided, it is stored and evaluated lazily against the agent instance during initialization so it can build a fresh stream for each agent.

Examples:

class Agent < LLM::Agent
  stream { MyStream.new }
end

Parameters:

  • stream (Object, Proc, nil) (defaults to: nil)

Yield Returns:

Returns:



155
156
157
158
# File 'lib/llm/agent.rb', line 155

def self.stream(stream = nil, &block)
  return @stream if stream.nil? && !block
  @stream = block || stream
end

.confirm(*tool_names, &block) ⇒ Array<String>, ...

Set or get the tool names that require confirmation before they can run.

Parameters:

  • tool_names (String, Symbol, Array<String, Symbol>, Proc)

    One or more tool names.

  • block (Proc)

    An optional, lazy-evaluated Proc

Returns:

  • (Array<String>, Proc, nil)


168
169
170
171
# File 'lib/llm/agent.rb', line 168

def self.confirm(*tool_names, &block)
  return @confirm if tool_names.empty? && !block
  @confirm = block || tool_names.flatten.map(&:to_s)
end

.model(model = nil, &block) ⇒ String?

Set or get the default model

Parameters:

  • model (String, nil) (defaults to: nil)

    The model identifier

Returns:

  • (String, nil)

    Returns the current model when no argument is provided



51
52
53
54
# File 'lib/llm/agent.rb', line 51

def self.model(model = nil, &block)
  return @model if model.nil? && !block
  @model = block || model
end

Instance Method Details

#on_tool_confirmation(fn, strategy) ⇒ LLM::Function::Return

This method is called when confirmation is required before a tool can run.

Parameters:

  • fn (LLM::Function)

    The pending function call. It can be cancelled through the Function#cancel method.

  • strategy (Symbol, Array<Symbol>)

    The execution strategy that would be used for the tool call.

Returns:

  • (LLM::Function::Return)

    Return either fn.spawn(strategy).wait to approve execution or fn.cancel(...) to cancel the call.



411
412
413
# File 'lib/llm/agent.rb', line 411

def on_tool_confirmation(fn, strategy)
  fn.cancel
end

#talk(prompt, params = {}) ⇒ LLM::Response

Maintain a conversation via the chat completions API. This method immediately sends a request to the LLM and returns the response.

Examples:

llm = LLM.openai(key: ENV["KEY"])
agent = LLM::Agent.new(llm)
response = agent.talk("Hello, what is your name?")
puts response.choices[0].content

Parameters:

  • params (Hash) (defaults to: {})

    The params passed to the provider, including optional :stream, :tools, :schema etc.

  • prompt (String)

    The input prompt to be completed

Options Hash (params):

  • :tool_attempts (Integer)

    The maxinum number of tool call iterations before the agent sends in-band advisory tool errors back through the model (default 25). Set to nil to disable advisory tool-limit returns.

Returns:



223
224
225
# File 'lib/llm/agent.rb', line 223

def talk(prompt, params = {})
  run_loop(prompt, params, :talk)
end

#ask(prompt, params = {}) ⇒ Object

See Also:



229
230
231
# File 'lib/llm/agent.rb', line 229

def ask(prompt, params = {})
  run_loop(prompt, params, :ask)
end

#messagesLLM::Buffer<LLM::Message>



235
236
237
# File 'lib/llm/agent.rb', line 235

def messages
  @ctx.messages
end

#functionsArray<LLM::Function>

Returns:



241
242
243
# File 'lib/llm/agent.rb', line 241

def functions
  @tracer ? @llm.with_tracer(@tracer) { @ctx.functions } : @ctx.functions
end

#returnsArray<LLM::Function::Return>

Returns:

See Also:



248
249
250
# File 'lib/llm/agent.rb', line 248

def returns
  @ctx.returns
end

#waitArray<LLM::Function::Return>

Returns:

See Also:



255
256
257
# File 'lib/llm/agent.rb', line 255

def wait(...)
  @tracer ? @llm.with_tracer(@tracer) { @ctx.wait(...) } : @ctx.wait(...)
end

#usageLLM::Object

Returns:



261
262
263
# File 'lib/llm/agent.rb', line 261

def usage
  @ctx.usage
end

#interrupt!nil Also known as: cancel!

Interrupt the active request, if any.

Returns:

  • (nil)


268
269
270
# File 'lib/llm/agent.rb', line 268

def interrupt!
  @ctx.interrupt!
end

#prompt(&b) ⇒ LLM::Prompt Also known as: build_prompt

Parameters:

  • b (Proc)

    A block that composes messages. If it takes one argument, it receives the prompt object. Otherwise it runs in prompt context.

Returns:

See Also:



277
278
279
# File 'lib/llm/agent.rb', line 277

def prompt(&b)
  @ctx.prompt(&b)
end

#image_url(url) ⇒ LLM::Object

Returns a tagged object

Parameters:

  • url (String)

    The URL

Returns:



287
288
289
# File 'lib/llm/agent.rb', line 287

def image_url(url)
  @ctx.image_url(url)
end

#local_file(path) ⇒ LLM::Object

Returns a tagged object

Parameters:

  • path (String)

    The path

Returns:



296
297
298
# File 'lib/llm/agent.rb', line 296

def local_file(path)
  @ctx.local_file(path)
end

#remote_file(res) ⇒ LLM::Object

Returns a tagged object

Parameters:

Returns:



305
306
307
# File 'lib/llm/agent.rb', line 305

def remote_file(res)
  @ctx.remote_file(res)
end

#tracerLLM::Tracer

Returns an LLM tracer

Returns:



312
313
314
# File 'lib/llm/agent.rb', line 312

def tracer
  @tracer || @ctx.tracer
end

#streamLLM::Stream, ...

Returns a stream object, or nil

Returns:

  • (LLM::Stream, #<<, nil)

    Returns a stream object, or nil



319
320
321
# File 'lib/llm/agent.rb', line 319

def stream
  @ctx.stream
end

#modelString

Returns the model an Agent is actively using

Returns:

  • (String)


326
327
328
# File 'lib/llm/agent.rb', line 326

def model
  @ctx.model
end

#modeSymbol

Returns:

  • (Symbol)


332
333
334
# File 'lib/llm/agent.rb', line 332

def mode
  @ctx.mode
end

#concurrencySymbol, ...

Returns the configured tool execution concurrency.

Returns:

  • (Symbol, Array<Symbol>, nil)


339
340
341
# File 'lib/llm/agent.rb', line 339

def concurrency
  @concurrency
end

#costLLM::Cost

Returns:

See Also:



346
347
348
# File 'lib/llm/agent.rb', line 346

def cost
  @ctx.cost
end

#context_windowInteger

Returns:

  • (Integer)

See Also:



353
354
355
# File 'lib/llm/agent.rb', line 353

def context_window
  @ctx.context_window
end

#paramsHash

Returns:

  • (Hash)

See Also:



360
361
362
# File 'lib/llm/agent.rb', line 360

def params
  @ctx.params
end

#to_hHash

Returns:

  • (Hash)

See Also:



367
368
369
# File 'lib/llm/agent.rb', line 367

def to_h
  @ctx.to_h
end

#to_jsonString

Returns:

  • (String)


373
374
375
# File 'lib/llm/agent.rb', line 373

def to_json(...)
  LLM.json.dump(to_h, ...)
end

#inspectString

Returns:

  • (String)


379
380
381
382
# File 'lib/llm/agent.rb', line 379

def inspect
  "#<#{LLM::Utils.object_id(self)} " \
  "@llm=#{@llm.class}, @mode=#{mode.inspect}, @messages=#{messages.inspect}>"
end

#serialize(**kw) ⇒ void Also known as: save

This method returns an undefined value.



387
388
389
# File 'lib/llm/agent.rb', line 387

def serialize(**kw)
  @ctx.serialize(**kw)
end

#deserialize(**kw) ⇒ Object Also known as: restore



395
396
397
# File 'lib/llm/agent.rb', line 395

def deserialize(**kw)
  @ctx.deserialize(**kw)
end