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::Guard::Loop detects repeated tool-call patterns and blocks stuck execution before more tool work is queued. - The tool loop can be bounded with
tool_budget. Once the budget is spent, the agent sends an in-band advisory message back through the model and keeps the loop in-band. By default no budget is set (nil), so the feature is disabled. - 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.
-
.retry_budget(budget = UNDEFINED) ⇒ Integer?
Sets or returns the retry budget for the agent.
-
.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.
-
.tool_budget(budget = UNDEFINED, &block) ⇒ Integer?
Set or get the maximum number of tool calls that are allowed in a single turn.
-
.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
- #compacted? ⇒ Boolean
-
#concurrency ⇒ Symbol, ...
Returns the configured tool execution concurrency.
-
#context_usage ⇒ Rational?
See also: Context#context_usage.
-
#context_used ⇒ Integer?
See also: Context#context_used.
- #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)
-
#record ⇒ Object?
Returns the ORM record this agent is bound to, or nil.
- #registry ⇒ LLM::Registry
-
#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
-
#token_usage ⇒ LLM::Usage
(also: #usage)
See also: Context#token_usage.
-
#tracer ⇒ LLM::Tracer
Returns an LLM tracer.
- #tracer=(other) ⇒ void
- #wait ⇒ Array<LLM::Function::Return>
Constructor Details
#initialize(llm, params = {}) ⇒ Agent
Returns a new instance of Agent.
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
# File 'lib/llm/agent.rb', line 402 def initialize(llm, params = {}) params = {}.merge!(params) @llm = llm fields, fields_ivar = FIELDS, IVARS fields.each do |field| resolvable = params.key?(field) ? params.delete(field) : (self.class.respond_to?(field) ? self.class.public_send(field) : nil) 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: LLM::Guard::Loop}.merge(params)) @path and File.readable?(@path) ? @ctx.restore(path:) : nil end |
Instance Attribute Details
#llm ⇒ LLM::Provider (readonly)
Returns a provider
88 89 90 |
# File 'lib/llm/agent.rb', line 88 def llm @llm end |
Class Method Details
.concurrency(concurrency = nil) ⇒ Symbol, ...
Set or get the tool execution concurrency.
281 282 283 284 |
# File 'lib/llm/agent.rb', line 281 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.
244 245 246 247 248 249 250 251 252 253 |
# File 'lib/llm/agent.rb', line 244 def self.confirm(*tool_names, &block) return @confirm if tool_names.empty? and !block if block @confirm = block elsif single_callable?(tool_names) @confirm = tool_names.first else @confirm = 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
159 160 161 162 163 164 165 |
# File 'lib/llm/agent.rb', line 159 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
261 262 263 264 |
# File 'lib/llm/agent.rb', line 261 def self.instructions(instructions = nil) return @instructions if instructions.nil? @instructions = instructions end |
.model(model = nil, &block) ⇒ String?
Set or get the default model
173 174 175 176 |
# File 'lib/llm/agent.rb', line 173 def self.model(model = nil, &block) return @model if model.nil? and !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
137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/llm/agent.rb', line 137 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.
332 333 334 335 336 337 338 |
# File 'lib/llm/agent.rb', line 332 def self.path(path = UNDEFINED, &block) if path.equal?(UNDEFINED) @path else @path = path || block end end |
.retry_budget(budget = UNDEFINED) ⇒ Integer?
Sets or returns the retry budget for the agent.
The retry budget is the maximum number of times a rate-limited request will be retried before giving up. Each retry sleeps a growing interval, so an exhausted budget surfaces the rate-limit error instead of blocking indefinitely. Enabled (5) by default; a raw Context disables it (0) unless configured.
373 374 375 376 377 378 379 |
# File 'lib/llm/agent.rb', line 373 def self.retry_budget(budget = UNDEFINED) if budget.equal?(UNDEFINED) @retry_budget.nil? ? 5 : @retry_budget else @retry_budget = budget end end |
.schema(schema = nil, &block) ⇒ #to_json?
Set or get the default schema
184 185 186 187 |
# File 'lib/llm/agent.rb', line 184 def self.schema(schema = nil, &block) return @schema if schema.nil? and !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.
117 118 119 120 121 122 123 124 125 |
# File 'lib/llm/agent.rb', line 117 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
212 213 214 215 216 217 218 219 220 221 |
# File 'lib/llm/agent.rb', line 212 def self.skills(*skills, &block) return @skills if skills.empty? and !block if block @skills = block elsif single_callable?(skills) @skills = skills.first else @skills = 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.
321 322 323 324 |
# File 'lib/llm/agent.rb', line 321 def self.stream(stream = nil, &block) return @stream if stream.nil? && !block @stream = block || stream end |
.tool_budget(budget = UNDEFINED, &block) ⇒ Integer?
By default this feature is disabled
(set to nil).
Set or get the maximum number of tool calls that are allowed in a single turn. Once the budget is spent, we will return an in-band message that informs the model it has spent its tool call budget - and usually a model will change course afterwards.
354 355 356 357 358 359 360 |
# File 'lib/llm/agent.rb', line 354 def self.tool_budget(budget = UNDEFINED, &block) if budget.equal?(UNDEFINED) @tool_budget else @tool_budget = budget || block end end |
.tools(*tools, &block) ⇒ Array<LLM::Function>
Set or get the default tools
195 196 197 198 199 200 201 202 203 204 |
# File 'lib/llm/agent.rb', line 195 def self.tools(*tools, &block) return @tools || [] if tools.empty? and !block if block @tools = block elsif single_callable?(tools) @tools = tools.first else @tools = 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.
301 302 303 304 |
# File 'lib/llm/agent.rb', line 301 def self.tracer(tracer = nil, &block) return @tracer if tracer.nil? && !block @tracer = block || tracer end |
Instance Method Details
#ask(prompt, params = {}) ⇒ Object
477 478 479 480 481 |
# File 'lib/llm/agent.rb', line 477 def ask(prompt, params = {}) res = run_loop(prompt, params, :ask) path ? @ctx.save(path:) : nil res end |
#compacted? ⇒ Boolean
642 643 644 |
# File 'lib/llm/agent.rb', line 642 def compacted? @ctx.compacted? end |
#concurrency ⇒ Symbol, ...
Returns the configured tool execution concurrency.
614 615 616 |
# File 'lib/llm/agent.rb', line 614 def concurrency @concurrency end |
#context_usage ⇒ Rational?
See also: Context#context_usage
527 528 529 |
# File 'lib/llm/agent.rb', line 527 def context_usage @ctx.context_usage end |
#context_used ⇒ Integer?
See also: Context#context_used
520 521 522 |
# File 'lib/llm/agent.rb', line 520 def context_used @ctx.context_used end |
#context_window ⇒ Integer
628 629 630 |
# File 'lib/llm/agent.rb', line 628 def context_window @ctx.context_window end |
#description ⇒ String?
Returns the agent's description
448 449 450 |
# File 'lib/llm/agent.rb', line 448 def description @description end |
#deserialize(**kw) ⇒ LLM::Agent Also known as: restore
725 726 727 728 |
# File 'lib/llm/agent.rb', line 725 def deserialize(**kw) @ctx.deserialize(**kw) self end |
#image_url(url) ⇒ LLM::Object
Returns a tagged object
553 554 555 |
# File 'lib/llm/agent.rb', line 553 def image_url(url) @ctx.image_url(url) end |
#inspect ⇒ String
709 710 711 712 |
# File 'lib/llm/agent.rb', line 709 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.
534 535 536 |
# File 'lib/llm/agent.rb', line 534 def interrupt! @ctx.interrupt! end |
#local_file(path) ⇒ LLM::Object
Returns a tagged object
562 563 564 |
# File 'lib/llm/agent.rb', line 562 def local_file(path) @ctx.local_file(path) end |
#mode ⇒ Symbol
607 608 609 |
# File 'lib/llm/agent.rb', line 607 def mode @ctx.mode end |
#model ⇒ String
Returns the model an Agent is actively using
601 602 603 |
# File 'lib/llm/agent.rb', line 601 def model @ctx.model end |
#name ⇒ String
Returns the agent's name
426 427 428 |
# File 'lib/llm/agent.rb', line 426 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.
742 743 744 |
# File 'lib/llm/agent.rb', line 742 def on_tool_confirmation(fn, strategy) fn.cancel end |
#params ⇒ Hash
690 691 692 |
# File 'lib/llm/agent.rb', line 690 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.
434 435 436 |
# File 'lib/llm/agent.rb', line 434 def path @path end |
#pending_functions ⇒ Array<LLM::Function>
491 492 493 |
# File 'lib/llm/agent.rb', line 491 def pending_functions @tracer ? @llm.with_tracer(@tracer) { @ctx.pending_functions } : @ctx.pending_functions end |
#prompt(&b) ⇒ LLM::Prompt Also known as: build_prompt
543 544 545 |
# File 'lib/llm/agent.rb', line 543 def prompt(&b) @ctx.prompt(&b) end |
#record ⇒ Object?
Returns the ORM record this agent is bound to, or nil.
441 442 443 |
# File 'lib/llm/agent.rb', line 441 def record @record end |
#registry ⇒ LLM::Registry
635 636 637 |
# File 'lib/llm/agent.rb', line 635 def registry @ctx.registry end |
#remote_file(res) ⇒ LLM::Object
Returns a tagged object
571 572 573 |
# File 'lib/llm/agent.rb', line 571 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.
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 |
# File 'lib/llm/agent.rb', line 670 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>
498 499 500 |
# File 'lib/llm/agent.rb', line 498 def returns @ctx.returns end |
#serialize(**kw) ⇒ void Also known as: save
This method returns an undefined value.
717 718 719 |
# File 'lib/llm/agent.rb', line 717 def serialize(**kw) @ctx.serialize(**kw) end |
#stream ⇒ LLM::Stream, ...
Returns a stream object, or nil
594 595 596 |
# File 'lib/llm/agent.rb', line 594 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.
469 470 471 472 473 |
# File 'lib/llm/agent.rb', line 469 def talk(prompt, params = {}) res = run_loop(prompt, params, :talk) path ? @ctx.save(path:) : nil res end |
#to_h ⇒ Hash
697 698 699 |
# File 'lib/llm/agent.rb', line 697 def to_h @ctx.to_h end |
#to_json ⇒ String
703 704 705 |
# File 'lib/llm/agent.rb', line 703 def to_json(...) LLM.json.dump(to_h, ...) end |
#token_usage ⇒ LLM::Usage Also known as: usage
See also: Context#token_usage
512 513 514 |
# File 'lib/llm/agent.rb', line 512 def token_usage @ctx.token_usage end |
#tracer ⇒ LLM::Tracer
Returns an LLM tracer
578 579 580 |
# File 'lib/llm/agent.rb', line 578 def tracer @tracer || @ctx.tracer end |
#tracer=(other) ⇒ void
This method returns an undefined value.
586 587 588 589 |
# File 'lib/llm/agent.rb', line 586 def tracer=(other) @ctx.tracer = other @tracer = other end |
#wait ⇒ Array<LLM::Function::Return>
505 506 507 |
# File 'lib/llm/agent.rb', line 505 def wait(...) @tracer ? @llm.with_tracer(@tracer) { @ctx.wait(...) } : @ctx.wait(...) end |