Class: AIA::PromptDecomposer

Inherits:
Object
  • Object
show all
Includes:
ContentExtractor
Defined in:
lib/aia/prompt_decomposer.rb

Constant Summary collapse

DECOMPOSITION_PROMPT =
<<~PROMPT
  Analyze this user request and determine if it can be broken into independent sub-tasks.

  Rules:
  - Only decompose if sub-tasks are truly independent (can run in parallel)
  - Each sub-task should be self-contained
  - 2-5 sub-tasks maximum
  - Keep sub-task descriptions clear and specific
  - Use an empty subtasks array if the request cannot be meaningfully decomposed

  User request: %{prompt}
PROMPT
FALLBACK_JSON_INSTRUCTION =

Appended to DECOMPOSITION_PROMPT when the model does not support structured output and with_schema cannot be used.

<<~INSTRUCTION

  Respond with ONLY a JSON object — no explanation, no markdown, no prose:
  {"subtasks": ["sub-task description 1", "sub-task description 2"]}
INSTRUCTION
SUBTASKS_SCHEMA =

JSON schema for structured-output-capable models. Uses a wrapper object because Claude requires a top-level object.

{
  name: 'subtask_decomposition',
  schema: {
    type: 'object',
    properties: {
      subtasks: {
        type: 'array',
        items: { type: 'string' },
        description: 'Independent sub-task descriptions. Empty array if not decomposable.'
      }
    },
    required: ['subtasks'],
    additionalProperties: false
  },
  strict: true
}.freeze
SYNTHESIS_TEMPLATE =
<<~PROMPT
  Original request: %{prompt}

  Sub-task results:
  %{results}

  Synthesize these results into a coherent final response that fully addresses the original request.
PROMPT

Instance Method Summary collapse

Methods included from ContentExtractor

#extract_content, #extract_network_content, #format_duration, #output_to_file, #present_result, #store_results_in_memory

Constructor Details

#initialize(robot) ⇒ PromptDecomposer

Returns a new instance of PromptDecomposer.



64
65
66
# File 'lib/aia/prompt_decomposer.rb', line 64

def initialize(robot)
  @robot = robot
end

Instance Method Details

#decompose(prompt) ⇒ Array<String>

Attempt to decompose a prompt into sub-tasks.

Uses a temporary probe robot so the decomposition prompt and response do not pollute the main conversation history. When the configured model supports structured output (structured_output? == true) with_schema is used to get a guaranteed Hash back. Otherwise a JSON instruction is appended to the prompt and the text response is parsed manually.

Parameters:

  • prompt (String)

    the user's prompt

Returns:

  • (Array<String>)

    sub-task descriptions (empty if not decomposable)



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/aia/prompt_decomposer.rb', line 78

def decompose(prompt)
  probe      = build_probe_robot
  use_schema = structured_output?

  if use_schema
    probe.with_schema(SUBTASKS_SCHEMA)
    full_prompt = DECOMPOSITION_PROMPT % { prompt: prompt }
  else
    AIA.logger.warn("PromptDecomposer: configured model does not support structured output — using JSON prompt fallback")
    full_prompt = (DECOMPOSITION_PROMPT + FALLBACK_JSON_INSTRUCTION) % { prompt: prompt }
  end

  result   = probe.run(full_prompt, mcp: :none, tools: :none)
  content  = extract_content(result)
  AIA.logger.debug("PromptDecomposer#decompose content class=#{content.class}")
  subtasks = extract_subtasks(content)
  subtasks.is_a?(Array) ? subtasks.select { |t| t.is_a?(String) && !t.empty? } : []
rescue StandardError => e
  AIA.logger.warn("PromptDecomposer#decompose failed: #{e.class}: #{e.message}")
  []
end

#synthesize(prompt, results) ⇒ Object

Synthesize sub-task results into a final response.

Parameters:

  • prompt (String)

    the original user prompt

  • results (Array<String>)

    results from each sub-task

Returns:

  • the synthesized response



105
106
107
108
109
110
111
112
113
114
# File 'lib/aia/prompt_decomposer.rb', line 105

def synthesize(prompt, results)
  formatted_results = results.each_with_index.map do |r, i|
    "Sub-task #{i + 1}:\n#{r}"
  end.join("\n\n")

  @robot.run(
    SYNTHESIS_TEMPLATE % { prompt: prompt, results: formatted_results },
    mcp: :none, tools: :none
  )
end