Class: TurnKit::Agent
- Inherits:
-
Object
- Object
- TurnKit::Agent
- Defined in:
- lib/turnkit/agent.rb
Constant Summary collapse
- ORCHESTRATOR_PREAMBLE =
<<~TEXT.strip You are an autonomous task orchestrator. Navigate from the application request to a final output without asking the user follow-up questions. Use the available tools to gather context, inspect sources, take actions, persist outputs, and verify work. Use loaded skills as reusable workflow patterns. Iterate when work needs missing context, critique, revision, or verification. When multiple independent items need the same kind of fetch or read, and an available batch tool can handle them in one call, prefer the batch tool over repeated one-item tool calls. Stop when the task is complete, when the available context and tools are sufficient for the best possible answer, or when further iteration would not materially improve the result. Respect runtime, cost, and iteration limits. TEXT
Instance Attribute Summary collapse
-
#available_skills ⇒ Object
readonly
Returns the value of attribute available_skills.
-
#client ⇒ Object
readonly
Returns the value of attribute client.
-
#compaction ⇒ Object
readonly
Returns the value of attribute compaction.
-
#description ⇒ Object
readonly
Returns the value of attribute description.
-
#input_schema ⇒ Object
readonly
Returns the value of attribute input_schema.
-
#instructions ⇒ Object
readonly
Returns the value of attribute instructions.
-
#max_depth ⇒ Object
readonly
Returns the value of attribute max_depth.
-
#max_iterations ⇒ Object
readonly
Returns the value of attribute max_iterations.
-
#max_spend ⇒ Object
readonly
Returns the value of attribute max_spend.
-
#max_tool_executions ⇒ Object
readonly
Returns the value of attribute max_tool_executions.
-
#max_tool_executions_by_name ⇒ Object
readonly
Returns the value of attribute max_tool_executions_by_name.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#on_event ⇒ Object
readonly
Returns the value of attribute on_event.
-
#output_policy ⇒ Object
readonly
Returns the value of attribute output_policy.
-
#output_policy_mode ⇒ Object
readonly
Returns the value of attribute output_policy_mode.
-
#output_policy_model ⇒ Object
readonly
Returns the value of attribute output_policy_model.
-
#output_retries ⇒ Object
readonly
Returns the value of attribute output_retries.
-
#output_schema ⇒ Object
readonly
Returns the value of attribute output_schema.
-
#prompt_mode ⇒ Object
readonly
Returns the value of attribute prompt_mode.
-
#prompt_sections ⇒ Object
readonly
Returns the value of attribute prompt_sections.
-
#skills ⇒ Object
readonly
Returns the value of attribute skills.
-
#store ⇒ Object
readonly
Returns the value of attribute store.
-
#sub_agents ⇒ Object
readonly
Returns the value of attribute sub_agents.
-
#system_prompt ⇒ Object
readonly
Returns the value of attribute system_prompt.
-
#thinking ⇒ Object
readonly
Returns the value of attribute thinking.
-
#timeout ⇒ Object
readonly
Returns the value of attribute timeout.
-
#tools ⇒ Object
readonly
Returns the value of attribute tools.
Class Method Summary collapse
Instance Method Summary collapse
- #budget_limits ⇒ Object
- #build_budget(root_started_at: Clock.now) ⇒ Object
- #conversation(model: nil, subject: nil, metadata: {}) ⇒ Object
- #cost ⇒ Object
- #effective_available_skills ⇒ Object
- #effective_client ⇒ Object
- #effective_model ⇒ Object
- #effective_on_event ⇒ Object
- #effective_output_policy ⇒ Object
- #effective_prompt_mode(turn: nil) ⇒ Object
- #effective_prompt_sections ⇒ Object
- #effective_store ⇒ Object
- #effective_thinking ⇒ Object
- #effective_tools ⇒ Object
-
#initialize(name:, description: "", model: nil, instructions: "", orchestrator: false, tools: [], skills: [], available_skills: [], sub_agents: [], system_prompt: nil, prompt_sections: nil, prompt_mode: nil, client: nil, store: nil, max_iterations: nil, timeout: nil, max_spend: nil, max_depth: nil, max_tool_executions: nil, max_tool_executions_by_name: nil, thinking: nil, compaction: nil, output_schema: nil, input_schema: nil, output_policy: nil, output_policy_mode: nil, output_policy_model: nil, output_policy_thinking: nil, output_retries: 0, on_event: nil) ⇒ Agent
constructor
A new instance of Agent.
- #instructions_with_skills ⇒ Object
- #orchestrator? ⇒ Boolean
- #run(task, input: nil, async: false, subject: nil, metadata: {}, parent_run: nil, root_turn_id: nil, prompt_mode: :task, **options) ⇒ Object
- #system_prompt_for(turn:, conversation:) ⇒ Object
- #usage ⇒ Object
Constructor Details
#initialize(name:, description: "", model: nil, instructions: "", orchestrator: false, tools: [], skills: [], available_skills: [], sub_agents: [], system_prompt: nil, prompt_sections: nil, prompt_mode: nil, client: nil, store: nil, max_iterations: nil, timeout: nil, max_spend: nil, max_depth: nil, max_tool_executions: nil, max_tool_executions_by_name: nil, thinking: nil, compaction: nil, output_schema: nil, input_schema: nil, output_policy: nil, output_policy_mode: nil, output_policy_model: nil, output_policy_thinking: nil, output_retries: 0, on_event: nil) ⇒ Agent
Returns a new instance of Agent.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/turnkit/agent.rb', line 29 def initialize(name:, description: "", model: nil, instructions: "", orchestrator: false, tools: [], skills: [], available_skills: [], sub_agents: [], system_prompt: nil, prompt_sections: nil, prompt_mode: nil, client: nil, store: nil, max_iterations: nil, timeout: nil, max_spend: nil, max_depth: nil, max_tool_executions: nil, max_tool_executions_by_name: nil, thinking: nil, compaction: nil, output_schema: nil, input_schema: nil, output_policy: nil, output_policy_mode: nil, output_policy_model: nil, output_policy_thinking: nil, output_retries: 0, on_event: nil) @name = name.to_s @description = description.to_s @model = model @orchestrator = orchestrator ? true : false @instructions = compose_instructions(instructions) @tools = Array(tools) @skills = Array(skills) @available_skills = Array(available_skills) @sub_agents = Array(sub_agents) @system_prompt = system_prompt @prompt_sections = prompt_sections @prompt_mode = prompt_mode&.to_sym || (:task if @orchestrator) @client = client @store = store @max_iterations = max_iterations @timeout = timeout @max_spend = max_spend @max_depth = max_depth @max_tool_executions = max_tool_executions @max_tool_executions_by_name = max_tool_executions_by_name @thinking = self.class.normalize_thinking(thinking) @compaction = compaction @output_schema = output_schema @input_schema = input_schema @output_policy_model = output_policy_model @output_policy = normalize_output_policy(output_policy, model: output_policy_model, thinking: output_policy_thinking) @output_policy_mode = normalize_output_policy_mode(output_policy_mode) @output_retries = Integer(output_retries || 0) @on_event = on_event raise ArgumentError, "name is required" if @name.empty? validate_tools! end |
Instance Attribute Details
#available_skills ⇒ Object (readonly)
Returns the value of attribute available_skills.
24 25 26 |
# File 'lib/turnkit/agent.rb', line 24 def available_skills @available_skills end |
#client ⇒ Object (readonly)
Returns the value of attribute client.
25 26 27 |
# File 'lib/turnkit/agent.rb', line 25 def client @client end |
#compaction ⇒ Object (readonly)
Returns the value of attribute compaction.
26 27 28 |
# File 'lib/turnkit/agent.rb', line 26 def compaction @compaction end |
#description ⇒ Object (readonly)
Returns the value of attribute description.
24 25 26 |
# File 'lib/turnkit/agent.rb', line 24 def description @description end |
#input_schema ⇒ Object (readonly)
Returns the value of attribute input_schema.
26 27 28 |
# File 'lib/turnkit/agent.rb', line 26 def input_schema @input_schema end |
#instructions ⇒ Object (readonly)
Returns the value of attribute instructions.
24 25 26 |
# File 'lib/turnkit/agent.rb', line 24 def instructions @instructions end |
#max_depth ⇒ Object (readonly)
Returns the value of attribute max_depth.
25 26 27 |
# File 'lib/turnkit/agent.rb', line 25 def max_depth @max_depth end |
#max_iterations ⇒ Object (readonly)
Returns the value of attribute max_iterations.
25 26 27 |
# File 'lib/turnkit/agent.rb', line 25 def max_iterations @max_iterations end |
#max_spend ⇒ Object (readonly)
Returns the value of attribute max_spend.
25 26 27 |
# File 'lib/turnkit/agent.rb', line 25 def max_spend @max_spend end |
#max_tool_executions ⇒ Object (readonly)
Returns the value of attribute max_tool_executions.
25 26 27 |
# File 'lib/turnkit/agent.rb', line 25 def max_tool_executions @max_tool_executions end |
#max_tool_executions_by_name ⇒ Object (readonly)
Returns the value of attribute max_tool_executions_by_name.
25 26 27 |
# File 'lib/turnkit/agent.rb', line 25 def max_tool_executions_by_name @max_tool_executions_by_name end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
24 25 26 |
# File 'lib/turnkit/agent.rb', line 24 def model @model end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
24 25 26 |
# File 'lib/turnkit/agent.rb', line 24 def name @name end |
#on_event ⇒ Object (readonly)
Returns the value of attribute on_event.
26 27 28 |
# File 'lib/turnkit/agent.rb', line 26 def on_event @on_event end |
#output_policy ⇒ Object (readonly)
Returns the value of attribute output_policy.
27 28 29 |
# File 'lib/turnkit/agent.rb', line 27 def output_policy @output_policy end |
#output_policy_mode ⇒ Object (readonly)
Returns the value of attribute output_policy_mode.
27 28 29 |
# File 'lib/turnkit/agent.rb', line 27 def output_policy_mode @output_policy_mode end |
#output_policy_model ⇒ Object (readonly)
Returns the value of attribute output_policy_model.
27 28 29 |
# File 'lib/turnkit/agent.rb', line 27 def output_policy_model @output_policy_model end |
#output_retries ⇒ Object (readonly)
Returns the value of attribute output_retries.
27 28 29 |
# File 'lib/turnkit/agent.rb', line 27 def output_retries @output_retries end |
#output_schema ⇒ Object (readonly)
Returns the value of attribute output_schema.
26 27 28 |
# File 'lib/turnkit/agent.rb', line 26 def output_schema @output_schema end |
#prompt_mode ⇒ Object (readonly)
Returns the value of attribute prompt_mode.
26 27 28 |
# File 'lib/turnkit/agent.rb', line 26 def prompt_mode @prompt_mode end |
#prompt_sections ⇒ Object (readonly)
Returns the value of attribute prompt_sections.
26 27 28 |
# File 'lib/turnkit/agent.rb', line 26 def prompt_sections @prompt_sections end |
#skills ⇒ Object (readonly)
Returns the value of attribute skills.
24 25 26 |
# File 'lib/turnkit/agent.rb', line 24 def skills @skills end |
#store ⇒ Object (readonly)
Returns the value of attribute store.
25 26 27 |
# File 'lib/turnkit/agent.rb', line 25 def store @store end |
#sub_agents ⇒ Object (readonly)
Returns the value of attribute sub_agents.
24 25 26 |
# File 'lib/turnkit/agent.rb', line 24 def sub_agents @sub_agents end |
#system_prompt ⇒ Object (readonly)
Returns the value of attribute system_prompt.
26 27 28 |
# File 'lib/turnkit/agent.rb', line 26 def system_prompt @system_prompt end |
#thinking ⇒ Object (readonly)
Returns the value of attribute thinking.
26 27 28 |
# File 'lib/turnkit/agent.rb', line 26 def thinking @thinking end |
#timeout ⇒ Object (readonly)
Returns the value of attribute timeout.
25 26 27 |
# File 'lib/turnkit/agent.rb', line 25 def timeout @timeout end |
#tools ⇒ Object (readonly)
Returns the value of attribute tools.
24 25 26 |
# File 'lib/turnkit/agent.rb', line 24 def tools @tools end |
Class Method Details
.normalize_thinking(value) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/turnkit/agent.rb', line 66 def self.normalize_thinking(value) return nil if value.nil? attrs = value.respond_to?(:to_h) ? value.to_h : value raise ArgumentError, "thinking must be a hash" unless attrs.is_a?(Hash) attrs = attrs.transform_keys(&:to_sym) unknown = attrs.keys - %i[effort budget] raise ArgumentError, "unknown thinking attributes: #{unknown.join(", ")}" if unknown.any? raise ArgumentError, "thinking requires :effort or :budget" if attrs[:effort].nil? && attrs[:budget].nil? raise ArgumentError, "thinking budget must be an Integer" if attrs[:budget] && !attrs[:budget].is_a?(Integer) attrs.slice(:effort, :budget).compact end |
Instance Method Details
#budget_limits ⇒ Object
177 178 179 180 181 182 183 184 185 186 |
# File 'lib/turnkit/agent.rb', line 177 def budget_limits { max_iterations: max_iterations || TurnKit.max_iterations, timeout: timeout || TurnKit.timeout, max_depth: max_depth || TurnKit.max_depth, max_tool_executions: max_tool_executions || TurnKit.max_tool_executions, max_tool_executions_by_name: max_tool_executions_by_name || TurnKit.max_tool_executions_by_name, max_spend: max_spend || TurnKit.max_spend } end |
#build_budget(root_started_at: Clock.now) ⇒ Object
188 189 190 |
# File 'lib/turnkit/agent.rb', line 188 def build_budget(root_started_at: Clock.now) Budget.new(**budget_limits, root_started_at: root_started_at) end |
#conversation(model: nil, subject: nil, metadata: {}) ⇒ Object
81 82 83 84 85 86 87 88 89 90 |
# File 'lib/turnkit/agent.rb', line 81 def conversation(model: nil, subject: nil, metadata: {}) store = effective_store record = store.create_conversation( "agent_name" => name, "model" => model || effective_model, "subject" => subject, "metadata" => ) Conversation.new(agent: self, record: record, store: store, model: model || effective_model, subject: subject, metadata: ) end |
#cost ⇒ Object
112 113 114 |
# File 'lib/turnkit/agent.rb', line 112 def cost Cost.from_records(effective_store.list_turns(agent_name: name)) end |
#effective_available_skills ⇒ Object
150 151 152 |
# File 'lib/turnkit/agent.rb', line 150 def effective_available_skills (Array(TurnKit.available_skills) + available_skills).uniq { |skill| skill.key } end |
#effective_client ⇒ Object
132 133 134 |
# File 'lib/turnkit/agent.rb', line 132 def effective_client client || TurnKit.client end |
#effective_model ⇒ Object
120 121 122 |
# File 'lib/turnkit/agent.rb', line 120 def effective_model model || TurnKit.default_model end |
#effective_on_event ⇒ Object
146 147 148 |
# File 'lib/turnkit/agent.rb', line 146 def effective_on_event on_event || TurnKit.on_event end |
#effective_output_policy ⇒ Object
128 129 130 |
# File 'lib/turnkit/agent.rb', line 128 def effective_output_policy Array(output_policy).compact end |
#effective_prompt_mode(turn: nil) ⇒ Object
158 159 160 161 162 |
# File 'lib/turnkit/agent.rb', line 158 def effective_prompt_mode(turn: nil) return prompt_mode if prompt_mode turn&.depth.to_i.positive? ? :minimal : :full end |
#effective_prompt_sections ⇒ Object
154 155 156 |
# File 'lib/turnkit/agent.rb', line 154 def effective_prompt_sections prompt_sections || TurnKit.prompt_sections end |
#effective_store ⇒ Object
136 137 138 |
# File 'lib/turnkit/agent.rb', line 136 def effective_store store || TurnKit.store end |
#effective_thinking ⇒ Object
124 125 126 |
# File 'lib/turnkit/agent.rb', line 124 def effective_thinking thinking end |
#effective_tools ⇒ Object
140 141 142 143 144 |
# File 'lib/turnkit/agent.rb', line 140 def effective_tools configured = tools + sub_agents.map { |agent| SubAgentTool.for(agent) } skills = effective_available_skills skills.empty? ? configured : configured + [ LoadSkillTool.for(skills) ] end |
#instructions_with_skills ⇒ Object
192 193 194 195 196 |
# File 'lib/turnkit/agent.rb', line 192 def instructions_with_skills parts = [ instructions ] parts << SystemPrompt.loaded_skills_text(skills) parts.reject(&:empty?).join("\n\n") end |
#orchestrator? ⇒ Boolean
92 93 94 |
# File 'lib/turnkit/agent.rb', line 92 def orchestrator? @orchestrator end |
#run(task, input: nil, async: false, subject: nil, metadata: {}, parent_run: nil, root_turn_id: nil, prompt_mode: :task, **options) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/turnkit/agent.rb', line 96 def run(task, input: nil, async: false, subject: nil, metadata: {}, parent_run: nil, root_turn_id: nil, prompt_mode: :task, **) raise ArgumentError, "task is required" if task.to_s.empty? SchemaCheck.validate!(input, input_schema, error_class: InputError, label: "input") if input_schema conversation = self.conversation(subject: subject, metadata: ) = conversation.say((task, input), metadata: { "source" => "application", "task" => true }) turn = conversation.build_turn( trigger_message_id: .id, root_turn_id: root_turn_id || parent_run_root_turn_id(parent_run), prompt_mode: prompt_mode, ** ) run = Run.new(turn) async ? run : run.run! end |
#system_prompt_for(turn:, conversation:) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/turnkit/agent.rb', line 164 def system_prompt_for(turn:, conversation:) prompt = SystemPrompt.new(agent: self, turn: turn, conversation: conversation, mode: effective_prompt_mode(turn: turn)) case system_prompt when nil prompt.to_s when String system_prompt else system_prompt.call(prompt).to_s end end |
#usage ⇒ Object
116 117 118 |
# File 'lib/turnkit/agent.rb', line 116 def usage Usage.from_records(effective_store.list_turns(agent_name: name)) end |