Skip to content

ReAct: make reflection adaptive (gate on stall signal, not fixed interval) #97

Description

@VarunGitGood

Why

The ReAct loop runs reflection on a fixed cadence:

# repi/investigation/react_loop.py:408-414, defaults at __init__ ~747-748
enable_reflection=True, reflection_interval=3, max_reflections=2

A reflection is a full LLM round-trip that produces no tool output — it is pure latency (3-10s each) and token cost. The model then needs a further turn to actually act on the reflection. With the defaults that is up to 2 free round-trips per investigation.

For straightforward RCAs — a clear entity ID, or an obvious time window — reflection adds nothing; the loop was already converging. Reflection only earns its cost when the loop is stalling (repeated tool calls returning no new evidence).

Note: the loop already tracks the signal we'd want to gate on — state.consecutive_empty_tool_calls (used today for stall-based early exit at react_loop.py:590).

Options

  1. Lower the default max_reflections to 1.
  2. Make reflection adaptive: only enter the reflection phase when the loop shows stall signals (e.g. consecutive_empty_tool_calls >= 1 or a run of low-yield tool calls), instead of every reflection_interval actions.

(2) is the more principled fix — reflection fires exactly when it can help and is free otherwise.

Scope (in)

  • Gate the Phase.REFLECTING transition in handle_gathering on a stall signal in addition to / instead of the fixed interval.
  • Keep enable_reflection and the cap as escape hatches.

Scope (out)

  • Redesigning the reflection prompt itself.

Acceptance

  • On clear-cut eval scenarios, zero reflections fire and answer quality is unchanged.
  • On stalling scenarios, reflection still fires.
  • Token usage / wall-clock drops measurably on the straightforward eval scenarios.

Relationship to #93

#93 refactors the loop into an explicit FSM where reflection is a phase. This change alters the transition condition into that phase, so it should land on top of / be coordinated with the FSM work to avoid rewriting the same transition twice.

Files

  • repi/investigation/react_loop.pyhandle_gathering (reflection gate ~408-416), defaults ~747-748.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestreAct improvementImprovements to the ReAct investigation loop / LLM prompt behaviorreact-qualityReAct loop reasoning improvements

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions