Skip to content

Patterns

Patterns are the agent’s loop shape. Built-ins self-register via register_pattern in packages/harness/src/felix/patterns/ (felix-run/felix).

Pattern Kind Behavior
react (default) single Sequential tool-calling loop bounded by recursion_limit
deep single react + plan tools (plan_create / plan_update_step / plan_get) + planning prompt suffix
router multi Classifier picks one sub_agents child; forwards thread_id
parallel multi Fan-out all children, then aggregator synthesis; parent owns the session
groupchat multi Round-robin speakers sharing a parent transcript (max_turns)
reflect single react + verifier model; below-threshold critique replays up to max_iterations
plan_execute single Planner decomposes → per-subtask react executor → synthesis; replans the remainder when a subtask ends early
  1. Open session; SessionStrategy renders the working set (full_replay / windowed:N / summarizing:N / semantic:N / compacting).
  2. Persist new caller turns.
  3. Loop: optional JIT tool retrieval → token-budget checks → model.chat / stream_chat → sequential tool dispatch through governance-wrapped executors → persist events.
  4. Stop on non-tool_use, fatal tool, or recursion exhaustion.

Streaming uses an async generator: text deltas yield; the final structured result (tool calls + usage) is the generator return value — no second non-stream call.

Three kinds of turn, and they do not share a model unless you let them.

  1. Plan — one call to planner_model (or spec.model) for a numbered list, truncated to max_subtasks.
  2. Execute — each subtask runs on a react agent built from executor_model and executor_recursion_limit, with the earlier subtasks’ notes in its prompt.
  3. Synthesise — one call on spec.model composing the answer from the notes. This is the answering turn, so it is the one that carries spec.output_schema; the planning and executing turns stay free-form, because a plan shaped like the answer schema is not a plan.

That split is the reason to reach for the pattern: planning is one call whose quality shapes everything after it, execution is many narrow calls. “Plan with the expensive model, execute with the cheap one” is the lever.

When a subtask ends on refusal or max_tokens — governance replaced the reply, or the model was cut off mid-answer — the note the synthesiser would record is not an answer. With replan_on_failure, the planner is asked again for the remaining work only, described rather than reissued verbatim, and the completed steps keep their notes. max_replans bounds it; max_subtasks still bounds the whole run, so a replan asks only for the budget the ceiling has left.

Deliberately not a failure: an empty or unhelpful answer. That step ran to completion and produced little, which is a planning problem — replanning on it would spend the budget looping on subtasks that are simply hard to say anything about.

Pattern Session ownership
react / deep / reflect Opens {tenant_id}:{suffix}; appends as events are produced
router Forwards thread_id to the chosen child
parallel / groupchat / plan_execute Parent owns the transcript; children run without writing the parent session (avoids concurrent append races)
  • Deny strings, not throws — governance returns model-visible deny content so the loop can adapt.
  • Sequential tool dispatch — deterministic audit order; limit counters don’t race.
  • Orthogonal bounds — recursion_limit caps loop steps; spec.limits caps aggregate work (tool calls, wall clock, peer hops, tokens).
  • Durable — spec.execution.mode: durable wraps any pattern so invoke enqueues a fiber (see architecture).