Skip to Content

SelfRefine

from loomflow.architecture import SelfRefine

SelfRefine improves an answer by having the model critique its own draft and then revise it, for a few rounds. Use it when a first draft is usually close but benefits from a review pass (writing, summaries, code explanations). By default the same model writes, critiques and revises. Based on Madaan et al. 2023, Self-Refine: Iterative Refinement with Self-Feedback.

For the conceptual page see Self-Refine.


Class signature

class SelfRefine: name: str = "self-refine" def __init__( self, *, base: Architecture | None = None, max_rounds: int = 3, critic_prompt: str | None = None, refiner_prompt: str | None = None, stop_phrase: str = "no issues", critic_model: str | Model | None = None, refiner_model: str | Model | None = None, ) -> None: ...

Constructor parameters

base

TypeArchitecture | None
DefaultNone (resolves to ReAct())

The architecture used for round 0 (the initial draft). Defaults to ReAct. Pass another architecture to compose them: SelfRefine(base=PlanAndExecute(...)) drafts with PlanAndExecute, then refines with the critic. See Recursive composition.

max_rounds

Typeint
Default3

Maximum refinement rounds (after the initial generation). Each round is one critic call + one refiner call. Must be >= 1.

critic_prompt

Typestr | None
DefaultNone (uses built-in default)

Override the critic’s system prompt. The default reviews the current output and produces a critique; if the critique is stop_phrase (the whole reply or its first line), the loop terminates.

refiner_prompt

Typestr | None
DefaultNone (uses built-in default)

Override the refiner’s system prompt. The default takes the current output + the critique and produces a revised output. The refiner’s message also carries every earlier output and critique of the run (Madaan et al., Eq. 4).

stop_phrase

Typestr
Default"no issues"

The reply the critic gives when it considers the output good enough. The loop stops only when the critique is this phrase (the whole reply or its first line, ignoring case, markdown and punctuation), not when it merely mentions it. Customize for non-English critics or different output rubrics. Termination is keyword-based; for numeric-score gating, use ActorCritic.

critic_model / refiner_model

Typestr | Model | None
DefaultNone (use the agent’s model)

Use a different model for the critique or the revision, for example a cheap model to critique while the draft uses the agent’s main model. Accepts a model string or a Model instance; role models are not wrapped with the agent’s retry policy. A model string is resolved with the agent’s secrets= backend, like the main model.


Methods

declared_workers

Returns {}. SelfRefine itself has no sub-agents.

run

  1. Round 0 (draft). Run base.run(session, deps, prompt) to produce an initial output. If the base interrupts itself (max_turns, budget), stop without refining, since refining a partial result would waste tokens.
  2. Refinement rounds (1..max_rounds):
    • Budget check. Stop or warn as needed.
    • Critic. One model call against the current output. Terminate if the critique is stop_phrase (whole reply or first line).
    • Refiner. One model call producing a revised output, given the current output and critique plus every earlier pair. Replaces the current output.

Cost model

For an N-round refinement: base + 2 × (rounds_actually_run) LLM calls. A base=ReAct() with max_rounds=3 is roughly 2–3× a single ReAct run.


When to use ActorCritic instead

When the same model has the same blind spots as critic and generator, gains plateau quickly. For asymmetric blind-spot coverage (different prompts, ideally different models), use ActorCritic. Actor and critic are distinct Agent instances, possibly on different model providers.


Example

from loomflow import Agent from loomflow.architecture import SelfRefine agent = Agent( "Write a tight 3-paragraph product brief.", model="claude-opus-4-7", architecture=SelfRefine(max_rounds=3), ) result = await agent.run("Brief about Acme Corp.")

Or by string:

agent = Agent("...", model="claude-opus-4-7", architecture="self-refine")

Wrapping a planner:

from loomflow.architecture import PlanAndExecute, SelfRefine agent = Agent( "...", model="claude-opus-4-7", architecture=SelfRefine( base=PlanAndExecute(max_steps=5), max_rounds=2, ), )

Source

loomflow/architecture/self_refine.py

Threshold via stop phrase, not score. SelfRefine’s critic emits free-form text; termination is keyword-based. For numeric-score gating (score ≥ threshold), use ActorCritic which returns structured JSON {score, issues, summary}.

Last updated on