Class: Yorishiro::SubAgent
- Inherits:
-
Object
- Object
- Yorishiro::SubAgent
- Defined in:
- lib/yorishiro/sub_agent.rb
Overview
Runs a delegated task in its own fresh Conversation so exploratory tool output never enters the parent conversation — only the final assistant text is returned. The caller decides which tools the subagent may use; Tools::Task passes read-only tools only, so no permission prompts fire.
Constant Summary collapse
- MAX_ITERATIONS =
15- SYSTEM_PROMPT =
<<~PROMPT You are a subagent handling a task delegated by a coding assistant. Investigate using the available read-only tools, then reply with your findings as plain text. Only your final message is returned to the caller, so make it complete and self-contained. Do not ask questions — nobody can answer them. PROMPT
Instance Method Summary collapse
-
#initialize(provider:, tools:, output: $stdout, hooks: Yorishiro.configuration.hooks, max_iterations: MAX_ITERATIONS) ⇒ SubAgent
constructor
A new instance of SubAgent.
-
#run(prompt) ⇒ Object
Run the agent loop for
promptand return the final assistant text.
Constructor Details
#initialize(provider:, tools:, output: $stdout, hooks: Yorishiro.configuration.hooks, max_iterations: MAX_ITERATIONS) ⇒ SubAgent
Returns a new instance of SubAgent.
19 20 21 22 23 24 25 26 27 |
# File 'lib/yorishiro/sub_agent.rb', line 19 def initialize(provider:, tools:, output: $stdout, hooks: Yorishiro.configuration.hooks, max_iterations: MAX_ITERATIONS) @provider = provider @tools = tools @output = output @hooks = hooks @max_iterations = max_iterations end |
Instance Method Details
#run(prompt) ⇒ Object
Run the agent loop for prompt and return the final assistant text.
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 |
# File 'lib/yorishiro/sub_agent.rb', line 30 def run(prompt) conversation = Conversation.new(system_prompt: SYSTEM_PROMPT) conversation.(:user, prompt) last_content = nil @max_iterations.times do |iteration| manage_context!(conversation) begin result = @provider.chat(conversation, tools: @tools.map(&:definition)) rescue StandardError => e # A failed completion must not throw away the investigation done so # far — salvage it the same way the iteration limit does. return provider_error_notice(last_content, e) end content = result[:content] tool_calls = result[:tool_calls] conversation.(:assistant, content, tool_calls: tool_calls.empty? ? nil : tool_calls) last_content = content unless content.to_s.empty? return final_answer(content) if tool_calls.empty? # Pending tool calls on the last iteration are pointless to execute — # there is no follow-up completion to consume their results. break if iteration == @max_iterations - 1 execute_tool_calls(conversation, tool_calls) end iteration_limit_notice(last_content) end |