Skip to content

Filter 7: agentic_loop #26

Description

@leseb

Purpose

Manage the agentic inference loop lifecycle for the Responses API. Observes whether tool calls are present in the response, checks exit conditions, manages iteration state, and signals via filter_results whether to loop back for another inference round or exit to the client.

Does not classify tool calls by type or execute them — classification is handled by tool_parse; execution by sub-filters routed via branch chains.

Behavior

  • Read state.tool_calls from ResponsesState
  • If no tool calls → set action = "done", exit loop
  • Check exit conditions:
    • Response finish_reason == "length" / status == "incomplete" → exit as incomplete
    • Request-level max_tool_calls reached → exit as incomplete
    • Iteration limit reached (max_infer_iters) → exit as incomplete
  • If tool calls present and no exit condition:
    • Increment state.iteration
    • Reset tool_choice to "auto" after first iteration
    • Set action = "loop" to signal branch chain re-entry
  • Loop control via filter_results + branch chain re-entrance:
    • agentic_loop.action = "loop" — branch re-enters at responses_proxy
    • agentic_loop.action = "done" — exit loop

Branch Chain Re-entrance

The loop is driven by praxis-core's ReEnter rejoin mode (proposal #36, released). A branch chain on agentic_loop matches action = loop and rejoins at the named responses_proxy filter:

- filter: responses_proxy
  name: inference
- filter: agentic_loop
  max_infer_iters: 10
  branch_chains:
    - name: tool-loop
      on_result:
        filter: agentic_loop
        key: action
        result: loop
      rejoin: inference
      max_iterations: 15
      chains:
        - name: tool-execution
          filters:
            - filter: headers
              request_add:
                - name: X-Agentic-Loop
                  value: "true"

Two iteration limits serve complementary purposes:

  • max_infer_iters (filter config) — application-level cap that marks the response as incomplete when reached
  • max_iterations (branch chain) — infrastructure-level safety cap that prevents infinite loops; should be ≥ max_infer_iters

Architectural Decision

Renamed from tool_dispatch to agentic_loop to reflect its role as a pure loop controller. Classification (client vs server tool calls) and execution are intentionally excluded — those responsibilities belong to tool_parse and sub-filters routed via branch chains. This avoids duplication between the loop controller and the classify→route→execute pipeline.

Config

filter: agentic_loop
max_infer_iters: 10

Praxis trait methods

  • on_request — check tool calls, exit conditions, manage iteration state, set loop signal

Branch chains only evaluate on_result conditions during the request phase (after on_request), so all loop control must happen here. On the first pass, tool_calls is empty and the filter signals done. After stream_events/tool_parse populate tool_calls during response-body processing, the branch chain re-enters the pipeline and on_request runs again with populated tool calls.

Dependencies

  • ResponsesState in request extensions (created by openai_responses_validate for all Responses API create requests)
  • Branch chain re-entrance in praxis-core (proposal Filter 17: chat_completions_to_responses_sse #36, released)
  • tool_parse for tool call classification (future)
  • Tool execution sub-filters routed via branch chains (future)

Reference

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Fields

No fields configured for Task.

Projects

Status
In Progress

Relationships

None yet

Development

No branches or pull requests

Issue actions