Class: LLM::Agent
- Inherits:
-
Object
- Object
- LLM::Agent
- Defined in:
- lib/scout/llm/agent.rb,
lib/scout/llm/agent/chat.rb,
lib/scout/llm/agent/iterate.rb,
lib/scout/llm/agent/delegate.rb
Instance Attribute Summary collapse
-
#knowledge_base ⇒ Object
Returns the value of attribute knowledge_base.
-
#other_options ⇒ Object
Returns the value of attribute other_options.
-
#process_exception ⇒ Object
Returns the value of attribute process_exception.
-
#start_chat ⇒ Object
Returns the value of attribute start_chat.
-
#workflow ⇒ Object
Returns the value of attribute workflow.
Class Method Summary collapse
Instance Method Summary collapse
-
#ask(messages, options = {}) ⇒ Object
function: takes an array of messages and calls LLM.ask with them.
- #chat(options = {}) ⇒ Object
- #current_chat ⇒ Object
- #delegate(agent, name, description, &block) ⇒ Object
- #format_message(message, prefix = "user") ⇒ Object
- #get_previous_response_id ⇒ Object
-
#initialize(workflow: nil, knowledge_base: nil, start_chat: nil, **kwargs) ⇒ Agent
constructor
A new instance of Agent.
- #iterate(prompt = nil, &block) ⇒ Object
- #iterate_dictionary(prompt = nil, &block) ⇒ Object
- #json ⇒ Object
- #json_format(format) ⇒ Object
- #method_missing(name) ⇒ Object
- #prompt(messages) ⇒ Object
- #respond ⇒ Object
- #start(chat = nil) ⇒ Object
- #system_prompt ⇒ Object
Constructor Details
#initialize(workflow: nil, knowledge_base: nil, start_chat: nil, **kwargs) ⇒ Agent
Returns a new instance of Agent.
10 11 12 13 14 15 16 |
# File 'lib/scout/llm/agent.rb', line 10 def initialize(workflow: nil, knowledge_base: nil, start_chat: nil, **kwargs) @workflow = workflow @workflow = Workflow.require_workflow @workflow if String === @workflow @knowledge_base = knowledge_base = IndiferentHash.setup(kwargs.dup) @start_chat = start_chat end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name) ⇒ Object
22 23 24 |
# File 'lib/scout/llm/agent/chat.rb', line 22 def method_missing(name,...) current_chat.send(name, ...) end |
Instance Attribute Details
#knowledge_base ⇒ Object
Returns the value of attribute knowledge_base.
9 10 11 |
# File 'lib/scout/llm/agent.rb', line 9 def knowledge_base @knowledge_base end |
#other_options ⇒ Object
Returns the value of attribute other_options.
9 10 11 |
# File 'lib/scout/llm/agent.rb', line 9 def end |
#process_exception ⇒ Object
Returns the value of attribute process_exception.
9 10 11 |
# File 'lib/scout/llm/agent.rb', line 9 def process_exception @process_exception end |
#start_chat ⇒ Object
Returns the value of attribute start_chat.
9 10 11 |
# File 'lib/scout/llm/agent.rb', line 9 def start_chat @start_chat end |
#workflow ⇒ Object
Returns the value of attribute workflow.
9 10 11 |
# File 'lib/scout/llm/agent.rb', line 9 def workflow @workflow end |
Class Method Details
.load_from_path(path, workflow: nil, knowledge_base: nil, chat: nil) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/scout/llm/agent.rb', line 83 def self.load_from_path(path, workflow: nil, knowledge_base: nil, chat: nil) workflow_path = path['workflow.rb'].find knowledge_base_path = path['knowledge_base'] chat_path = path['start_chat'] workflow = Workflow.require_workflow workflow_path if workflow_path.exists? knowledge_base = KnowledgeBase.new knowledge_base_path if knowledge_base_path.exists? chat = Chat.setup LLM.chat(chat_path.find) if chat_path.exists? LLM::Agent.new workflow: workflow, knowledge_base: knowledge_base, start_chat: chat end |
Instance Method Details
#ask(messages, options = {}) ⇒ Object
function: takes an array of messages and calls LLM.ask with them
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/scout/llm/agent.rb', line 52 def ask(, = {}) = [] unless .is_a? Array model ||= @model if model tools = [:tools] || {} tools = tools.merge [:tools] if [:tools] [:tools] = tools begin if workflow || knowledge_base tools.merge!(LLM.workflow_tools(workflow)) if workflow tools.merge!(LLM.knowledge_base_tool_definition(knowledge_base)) if knowledge_base and knowledge_base.all_databases.any? [:tools] = tools LLM.ask , .merge(log_errors: true).merge() else LLM.ask , .merge(log_errors: true).merge() end rescue exception = $! if Proc === self.process_exception try_again = self.process_exception.call exception if try_again retry else raise exception end else raise exception end end end |
#chat(options = {}) ⇒ Object
31 32 33 34 35 36 37 38 39 40 |
# File 'lib/scout/llm/agent/chat.rb', line 31 def chat( = {}) response = ask(current_chat, .merge(return_messages: true)) if Array === response current_chat.concat(response) current_chat.answer else current_chat.push({role: :assistant, content: response}) response end end |
#current_chat ⇒ Object
18 19 20 |
# File 'lib/scout/llm/agent/chat.rb', line 18 def current_chat @current_chat ||= start end |
#delegate(agent, name, description, &block) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/scout/llm/agent/delegate.rb', line 4 def delegate(agent, name, description, &block) [:tools] ||= {} task_name = "hand_off_to_#{name}" block ||= Proc.new do |name, parameters| = parameters[:message] new_conversation = parameters[:new_conversation] Log.medium "Delegated to #{agent}: " + Log.fingerprint() if new_conversation agent.start else agent.purge end agent.user agent.chat end properties = { message: { "type": :string, "description": "Message to pass to the agent" }, new_conversation: { "type": :boolean, "description": "Erase conversation history and start a new conversation with this message", "default": false } } required_inputs = [:message] function = { name: task_name, description: description, parameters: { type: "object", properties: properties, required: required_inputs } } definition = IndiferentHash.setup function.merge(type: 'function', function: function) [:tools][task_name] = [block, definition] end |
#format_message(message, prefix = "user") ⇒ Object
18 19 20 21 22 |
# File 'lib/scout/llm/agent.rb', line 18 def (, prefix = "user") .split(/\n\n+/).reject{|line| line.empty? }.collect do |line| prefix + "\t" + line.gsub("\n", ' ') end * "\n" end |
#get_previous_response_id ⇒ Object
65 66 67 68 |
# File 'lib/scout/llm/agent/chat.rb', line 65 def get_previous_response_id msg = current_chat.reverse.find{|msg| msg[:role].to_sym == :previous_response_id } msg.nil? ? nil : msg['content'] end |
#iterate(prompt = nil, &block) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/scout/llm/agent/iterate.rb', line 4 def iterate(prompt = nil, &block) self.endpoint :responses self.user prompt if prompt obj = self.json_format({ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "content": { "type": "array", "items": { "type": "string" } } }, "required": ["content"], "additionalProperties": false }) self.option :format, :text list = Hash === obj ? obj['content'] : obj list.each &block end |
#iterate_dictionary(prompt = nil, &block) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/scout/llm/agent/iterate.rb', line 28 def iterate_dictionary(prompt = nil, &block) self.endpoint :responses self.user prompt if prompt dict = self.json_format({ name: 'dictionary', type: 'object', properties: {}, additionalProperties: {type: :string} }) self.option :format, :text dict.each &block end |
#json ⇒ Object
43 44 45 46 47 48 49 50 51 52 |
# File 'lib/scout/llm/agent/chat.rb', line 43 def json(...) current_chat.format :json output = ask(current_chat, ...) obj = JSON.parse output if (Hash === obj) and obj.keys == ['content'] obj['content'] else obj end end |
#json_format(format) ⇒ Object
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/scout/llm/agent/chat.rb', line 54 def json_format(format, ...) current_chat.format format output = ask(current_chat, ...) obj = JSON.parse output if (Hash === obj) and obj.keys == ['content'] obj['content'] else obj end end |
#prompt(messages) ⇒ Object
43 44 45 46 47 48 49 |
# File 'lib/scout/llm/agent.rb', line 43 def prompt() if system_prompt [(system_prompt, "system"), .collect{|m| (m)}.flatten] * "\n" else .collect{|m| (m)}.flatten end end |
#respond ⇒ Object
26 27 28 |
# File 'lib/scout/llm/agent/chat.rb', line 26 def respond(...) self.ask(current_chat, ...) end |
#start(chat = nil) ⇒ Object
7 8 9 10 11 12 13 14 15 16 |
# File 'lib/scout/llm/agent/chat.rb', line 7 def start(chat=nil) if chat (@current_chat || start_chat).annotate chat unless Chat === chat @current_chat = chat else start_chat = self.start_chat Chat.setup(start_chat) unless Chat === start_chat @current_chat = start_chat.branch end end |
#system_prompt ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/scout/llm/agent.rb', line 24 def system_prompt system = @system system = [] if system.nil? system = [system] unless system.nil? || system.is_a?(Array) system = [] if system.nil? if @knowledge_base and @knowledge_base.all_databases.any? system << "You have access to the following databases associating entities:\n EOF\n\n knowledge_base.all_databases.each do |database|\n system << knowledge_base.markdown(database)\n end\n end\n\n system * \"\\n\"\nend\n" |