Class: LLM::Agent
- Inherits:
-
Object
- Object
- LLM::Agent
- Defined in:
- lib/llm/agent.rb
Overview
LLM::Agent is the recommended entry point for most use-cases. It 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,
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
guardby 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. Settool_attempts: nilto disable that advisory behavior. - Tool loop execution can be configured with
concurrency :sequential,:thread,:async,:fiber,:fork, or:ractor.
Instance Attribute Summary collapse
-
#llm ⇒ LLM::Provider
readonly
Returns a provider.
Class Method Summary collapse
-
.concurrency(concurrency = nil) ⇒ Symbol, ...
Set or get the tool execution concurrency.
-
.confirm(*tool_names, &block) ⇒ Array<String>, ...
Set or get the tool names that require confirmation before they can run.
-
.description(desc = UNDEFINED, &block) ⇒ String?
Set or get an agent's description.
-
.instructions(instructions = nil) ⇒ String?
Set or get the default instructions.
-
.model(model = nil, &block) ⇒ String?
Set or get the default model.
-
.name(name = UNDEFINED, &block) ⇒ String
Set or get an agent's name.
-
.path(path = UNDEFINED, &block) ⇒ String?
Set the file path where an agent's memory can be restored from, and written to.
-
.schema(schema = nil, &block) ⇒ #to_json?
Set or get the default schema.
-
.set(properties) ⇒ void
Bulk-assign class-level agent defaults from a Hash.
-
.skills(*skills, &block) ⇒ Array<String>?
Set or get the default skills.
-
.stream(stream = nil, &block) ⇒ Object, ...
Set or get the default stream.
-
.tools(*tools, &block) ⇒ Array<LLM::Function>
Set or get the default tools.
-
.tracer(tracer = nil, &block) ⇒ LLM::Tracer, ...
Set or get the default tracer.
Instance Method Summary collapse
- #ask(prompt, params = {}) ⇒ Object
-
#concurrency ⇒ Symbol, ...
Returns the configured tool execution concurrency.
- #context_window ⇒ Integer
- #cost ⇒ LLM::Cost
-
#description ⇒ String?
Returns the agent's description.
- #deserialize(**kw) ⇒ LLM::Agent (also: #restore)
-
#image_url(url) ⇒ LLM::Object
Returns a tagged object.
-
#initialize(llm, params = {}) ⇒ Agent
constructor
A new instance of Agent.
- #inspect ⇒ String
-
#interrupt! ⇒ nil
(also: #cancel!)
Interrupt the active request, if any.
-
#local_file(path) ⇒ LLM::Object
Returns a tagged object.
- #messages ⇒ LLM::Buffer<LLM::Message>
- #mode ⇒ Symbol
-
#model ⇒ String
Returns the model an Agent is actively using.
-
#name ⇒ String
Returns the agent's name.
-
#on_tool_confirmation(fn, strategy) ⇒ LLM::Function::Return
This method is called when confirmation is required before a tool can run.
- #params ⇒ Hash
-
#path ⇒ String?
Returns a file path where an agent's memory is restored from, and written to after each turn.
- #pending_functions ⇒ Array<LLM::Function>
- #prompt(&b) ⇒ LLM::Prompt (also: #build_prompt)
-
#remote_file(res) ⇒ LLM::Object
Returns a tagged object.
-
#repl(name: self.name, path: nil, tools: [], skills: [], tracer: false, trace: nil) ⇒ void
Start a minimalist repl that can interact with the agent and its current state.
- #returns ⇒ Array<LLM::Function::Return>
- #serialize(**kw) ⇒ void (also: #save)
-
#stream ⇒ LLM::Stream, ...
Returns a stream object, or nil.
-
#talk(prompt, params = {}) ⇒ LLM::Response
Maintain a conversation via the chat completions API.
- #to_h ⇒ Hash
- #to_json ⇒ String
-
#tracer ⇒ LLM::Tracer
Returns an LLM tracer.
- #tracer=(other) ⇒ void
- #usage ⇒ LLM::Object
- #wait ⇒ Array<LLM::Function::Return>
Constructor Details
#initialize(llm, params = {}) ⇒ Agent
Returns a new instance of Agent.
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/llm/agent.rb', line 328 def initialize(llm, params = {}) @llm = llm fields = %i[name description path model skills schema tracer stream tools concurrency instructions confirm] fields_ivar = %i[name description path tracer concurrency instructions confirm] fields.each do |field| resolvable = params.key?(field) ? params.delete(field) : self.class.public_send(field) resolve_symbol = !%i[concurrency].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)) @path and File.readable?(@path) ? @ctx.restore(path:) : nil end |
Instance Attribute Details
#llm ⇒ LLM::Provider (readonly)
Returns a provider
68 69 70 |
# File 'lib/llm/agent.rb', line 68 def llm @llm end |
Class Method Details
.concurrency(concurrency = nil) ⇒ Symbol, ...
Set or get the tool execution concurrency.
225 226 227 228 |
# File 'lib/llm/agent.rb', line 225 def self.concurrency(concurrency = nil) return @concurrency if concurrency.nil? @concurrency = concurrency end |
.confirm(*tool_names, &block) ⇒ Array<String>, ...
Set or get the tool names that require confirmation before they can run.
When a single Symbol is given, it is stored as-is and resolved at initialization time by calling the method with that name on the agent instance. This allows dynamic tool confirmation lists.
291 292 293 294 295 296 297 298 |
# File 'lib/llm/agent.rb', line 291 def self.confirm(*tool_names, &block) return @confirm if tool_names.empty? && !block if tool_names.size == 1 && tool_names.grep(Symbol).any? @confirm = tool_names.first else @confirm = block || tool_names.flatten.map(&:to_s) end end |
.description(desc = UNDEFINED, &block) ⇒ String?
This method serves as a self-documenting string. It is optional but recommended.
Set or get an agent's description
139 140 141 142 143 144 145 |
# File 'lib/llm/agent.rb', line 139 def self.description(desc = UNDEFINED, &block) if desc.equal?(UNDEFINED) @desc else @desc = block || desc end end |
.instructions(instructions = nil) ⇒ String?
Set or get the default instructions
205 206 207 208 |
# File 'lib/llm/agent.rb', line 205 def self.instructions(instructions = nil) return @instructions if instructions.nil? @instructions = instructions end |
.model(model = nil, &block) ⇒ String?
Set or get the default model
153 154 155 156 |
# File 'lib/llm/agent.rb', line 153 def self.model(model = nil, &block) return @model if model.nil? && !block @model = block || model end |
.name(name = UNDEFINED, &block) ⇒ String
This method serves as a self-documenting string and it is used by LLM::Repl. It is optional but recommended.
Set or get an agent's name
117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/llm/agent.rb', line 117 def self.name(name = UNDEFINED, &block) if name.equal?(UNDEFINED) if @name.nil? name = to_s.split("::").last @name = name.gsub(CASE_PATTERN, "-").downcase else @name end else @name = block || name end end |
.path(path = UNDEFINED, &block) ⇒ String?
Set the file path where an agent's memory can be restored from, and written to.
306 307 308 309 310 311 312 |
# File 'lib/llm/agent.rb', line 306 def self.path(path = UNDEFINED, &block) if path.equal?(UNDEFINED) @path else @path = path || block end end |
.schema(schema = nil, &block) ⇒ #to_json?
Set or get the default schema
194 195 196 197 |
# File 'lib/llm/agent.rb', line 194 def self.schema(schema = nil, &block) return @schema if schema.nil? && !block @schema = block || schema end |
.set(properties) ⇒ void
This method returns an undefined value.
Bulk-assign class-level agent defaults from a Hash.
Each key is resolved by calling the corresponding class method on the agent subclass. An error is raised for unknown keys so that typos are caught early.
97 98 99 100 101 102 103 104 105 |
# File 'lib/llm/agent.rb', line 97 def self.set(properties) properties.each do if respond_to?(_1) public_send(_1, _2) else raise KeyError, "key not found: #{_1}" end end end |
.skills(*skills, &block) ⇒ Array<String>?
Set or get the default skills
179 180 181 182 183 184 185 186 |
# File 'lib/llm/agent.rb', line 179 def self.skills(*skills, &block) return @skills if skills.empty? && !block if skills.size == 1 and skills.grep(Symbol).any? @skills = skills.first else @skills = block || skills.flatten end 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.
265 266 267 268 |
# File 'lib/llm/agent.rb', line 265 def self.stream(stream = nil, &block) return @stream if stream.nil? && !block @stream = block || stream end |
.tools(*tools, &block) ⇒ Array<LLM::Function>
Set or get the default tools
164 165 166 167 168 169 170 171 |
# File 'lib/llm/agent.rb', line 164 def self.tools(*tools, &block) return @tools || [] if tools.empty? && !block if tools.size == 1 and tools.grep(Symbol).any? @tools = tools.first else @tools = block || tools.flatten end 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.
245 246 247 248 |
# File 'lib/llm/agent.rb', line 245 def self.tracer(tracer = nil, &block) return @tracer if tracer.nil? && !block @tracer = block || tracer end |
Instance Method Details
#ask(prompt, params = {}) ⇒ Object
395 396 397 398 399 |
# File 'lib/llm/agent.rb', line 395 def ask(prompt, params = {}) res = run_loop(prompt, params, :ask) path ? @ctx.save(path:) : nil res end |
#concurrency ⇒ Symbol, ...
Returns the configured tool execution concurrency.
516 517 518 |
# File 'lib/llm/agent.rb', line 516 def concurrency @concurrency end |
#context_window ⇒ Integer
530 531 532 |
# File 'lib/llm/agent.rb', line 530 def context_window @ctx.context_window end |
#description ⇒ String?
Returns the agent's description
367 368 369 |
# File 'lib/llm/agent.rb', line 367 def description @description end |
#deserialize(**kw) ⇒ LLM::Agent Also known as: restore
613 614 615 616 |
# File 'lib/llm/agent.rb', line 613 def deserialize(**kw) @ctx.deserialize(**kw) self end |
#image_url(url) ⇒ LLM::Object
Returns a tagged object
455 456 457 |
# File 'lib/llm/agent.rb', line 455 def image_url(url) @ctx.image_url(url) end |
#inspect ⇒ String
597 598 599 600 |
# File 'lib/llm/agent.rb', line 597 def inspect "#<#{LLM::Utils.object_id(self)} " \ "@llm=#{@llm.class}, @mode=#{mode.inspect}, @messages=#{.inspect}>" end |
#interrupt! ⇒ nil Also known as: cancel!
Interrupt the active request, if any.
436 437 438 |
# File 'lib/llm/agent.rb', line 436 def interrupt! @ctx.interrupt! end |
#local_file(path) ⇒ LLM::Object
Returns a tagged object
464 465 466 |
# File 'lib/llm/agent.rb', line 464 def local_file(path) @ctx.local_file(path) end |
#mode ⇒ Symbol
509 510 511 |
# File 'lib/llm/agent.rb', line 509 def mode @ctx.mode end |
#model ⇒ String
Returns the model an Agent is actively using
503 504 505 |
# File 'lib/llm/agent.rb', line 503 def model @ctx.model end |
#name ⇒ String
Returns the agent's name
352 353 354 |
# File 'lib/llm/agent.rb', line 352 def name @name end |
#on_tool_confirmation(fn, strategy) ⇒ LLM::Function::Return
This method is called when confirmation is required before a tool can run.
630 631 632 |
# File 'lib/llm/agent.rb', line 630 def on_tool_confirmation(fn, strategy) fn.cancel end |
#params ⇒ Hash
578 579 580 |
# File 'lib/llm/agent.rb', line 578 def params @ctx.params end |
#path ⇒ String?
Returns a file path where an agent's memory is restored from, and written to after each turn.
360 361 362 |
# File 'lib/llm/agent.rb', line 360 def path @path end |
#pending_functions ⇒ Array<LLM::Function>
409 410 411 |
# File 'lib/llm/agent.rb', line 409 def pending_functions @tracer ? @llm.with_tracer(@tracer) { @ctx.pending_functions } : @ctx.pending_functions end |
#prompt(&b) ⇒ LLM::Prompt Also known as: build_prompt
445 446 447 |
# File 'lib/llm/agent.rb', line 445 def prompt(&b) @ctx.prompt(&b) end |
#remote_file(res) ⇒ LLM::Object
Returns a tagged object
473 474 475 |
# File 'lib/llm/agent.rb', line 473 def remote_file(res) @ctx.remote_file(res) end |
#repl(name: self.name, path: nil, tools: [], skills: [], tracer: false, trace: nil) ⇒ void
By default this method disables the tracer for the duration of the repl session, and restores it afterwards.
This method returns an undefined value.
Start a minimalist repl that can interact with the agent and its current state. This method requires the 'curses' gem to be installed and available to require.
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 |
# File 'lib/llm/agent.rb', line 558 def repl(name: self.name, path: nil, tools: [], skills: [], tracer: false, trace: nil) if trace != nil warn "llm.rb: trace option is deprecated, use tracer instead" tracer = trace end if !tracer previous = self.tracer self.tracer = nil end require_relative "repl" unless defined?(::LLM::Repl) LLM::Repl.new(agent: self, name:, path:, tools:, skills:).start ensure if !tracer self.tracer = previous end end |
#returns ⇒ Array<LLM::Function::Return>
416 417 418 |
# File 'lib/llm/agent.rb', line 416 def returns @ctx.returns end |
#serialize(**kw) ⇒ void Also known as: save
This method returns an undefined value.
605 606 607 |
# File 'lib/llm/agent.rb', line 605 def serialize(**kw) @ctx.serialize(**kw) end |
#stream ⇒ LLM::Stream, ...
Returns a stream object, or nil
496 497 498 |
# File 'lib/llm/agent.rb', line 496 def stream @ctx.stream 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.
387 388 389 390 391 |
# File 'lib/llm/agent.rb', line 387 def talk(prompt, params = {}) res = run_loop(prompt, params, :talk) path ? @ctx.save(path:) : nil res end |
#to_h ⇒ Hash
585 586 587 |
# File 'lib/llm/agent.rb', line 585 def to_h @ctx.to_h end |
#to_json ⇒ String
591 592 593 |
# File 'lib/llm/agent.rb', line 591 def to_json(...) LLM.json.dump(to_h, ...) end |
#tracer ⇒ LLM::Tracer
Returns an LLM tracer
480 481 482 |
# File 'lib/llm/agent.rb', line 480 def tracer @tracer || @ctx.tracer end |
#tracer=(other) ⇒ void
This method returns an undefined value.
488 489 490 491 |
# File 'lib/llm/agent.rb', line 488 def tracer=(other) @ctx.tracer = other @tracer = other end |
#wait ⇒ Array<LLM::Function::Return>
423 424 425 |
# File 'lib/llm/agent.rb', line 423 def wait(...) @tracer ? @llm.with_tracer(@tracer) { @ctx.wait(...) } : @ctx.wait(...) end |