Class: Legate::Agentic::Loop
- Inherits:
-
Object
- Object
- Legate::Agentic::Loop
- Defined in:
- lib/legate/agentic/loop.rb
Overview
Drives the observe -> think -> act loop: ask the planner for the next single action, run it via the executor, feed the result back as an observation, and repeat until the model gives a final answer or the iteration cap is hit.
Returns the same { details:, last_result: } shape as PlanExecutor#execute_plan, so Agent#run_task builds the final event identically for both execution strategies.
Constant Summary collapse
- DEFAULT_MAX_ITERATIONS =
8- MAX_OBSERVATION_CHARS =
Long string tool results are truncated to this many characters before being fed back, so one big output doesn’t dominate the prompt each turn.
2_000
Instance Method Summary collapse
-
#initialize(planner:, executor:, logger: nil, max_iterations: DEFAULT_MAX_ITERATIONS) ⇒ Loop
constructor
A new instance of Loop.
-
#run(user_input:, session:, session_service:, invocation_id: nil) ⇒ Hash
{ details: <observations>, last_result: <result hash> }.
Constructor Details
#initialize(planner:, executor:, logger: nil, max_iterations: DEFAULT_MAX_ITERATIONS) ⇒ Loop
Returns a new instance of Loop.
26 27 28 29 30 31 |
# File 'lib/legate/agentic/loop.rb', line 26 def initialize(planner:, executor:, logger: nil, max_iterations: DEFAULT_MAX_ITERATIONS) @planner = planner @executor = executor @logger = logger || Legate.logger @max_iterations = max_iterations end |
Instance Method Details
#run(user_input:, session:, session_service:, invocation_id: nil) ⇒ Hash
Returns { details: <observations>, last_result: <result hash> }.
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 65 |
# File 'lib/legate/agentic/loop.rb', line 34 def run(user_input:, session:, session_service:, invocation_id: nil) observations = [] @max_iterations.times do |i| decision = @planner.reason_next_action(user_input, observations, invocation_id) return success(decision.answer, observations) if decision.final? if decision.invalid? @logger.warn("Agentic loop: model returned an unusable decision at step #{i + 1}; stopping.") return error('The agent could not decide on a valid next action.', observations) end result = execute(decision, session, session_service, invocation_id) observation = { tool: decision.tool, params: decision.params, result: sanitize(result) } spinning = observation == observations.last observations << observation # Loop-breaker: the model just repeated the exact same action and got # the exact same result — re-running won't make progress, so stop and # summarize rather than burn the rest of the iteration budget. next unless spinning @logger.warn("Agentic loop: repeated action '#{decision.tool}' with no change; stopping to avoid spinning.") return finish_without_final(user_input, observations, invocation_id, fallback: "Stopped after repeating the same action ('#{decision.tool}') without progress.") end @logger.warn("Agentic loop: reached the #{@max_iterations}-iteration cap without a final answer.") finish_without_final(user_input, observations, invocation_id, fallback: "Stopped after #{@max_iterations} steps without a final answer.") end |