FailurePolicy¶
FailurePolicy is a dataclass that maps each FailureType to a strategy callable.
It is the primary configuration object passed to Agent.
FailurePolicy¶
FailurePolicy
dataclass
¶
Maps each FailureType to a recovery strategy callable.
Usage::
from triage import FailurePolicy
from triage.strategies.retry import retry_with_tool_manifest, backoff_and_retry
from triage.strategies.replan import replan, resume_from_subgoal
from triage.strategies.rollback import rollback_to_checkpoint
policy = FailurePolicy(
WRONG_TOOL_CALLED = retry_with_tool_manifest(max_attempts=3),
CONSTRAINT_IGNORED = replan(hint="re-read the constraints carefully"),
LOOP_DETECTED = replan(max_replans=2),
PLAN_INCOMPLETE = resume_from_subgoal(),
EXTERNAL_FAULT = backoff_and_retry(max_attempts=5),
default = FailurePolicy.escalate_by_default(),
)
Any FailureType not explicitly listed falls through to default.
__init__ ¶
__init__(WRONG_TOOL_CALLED: StrategyFn | None = None, CONSTRAINT_IGNORED: StrategyFn | None = None, LOOP_DETECTED: StrategyFn | None = None, PLAN_INCOMPLETE: StrategyFn | None = None, SCHEMA_MISMATCH: StrategyFn | None = None, CONTEXT_OVERFLOW: StrategyFn | None = None, EXTERNAL_FAULT: StrategyFn | None = None, TIMEOUT: StrategyFn | None = None, UNKNOWN: StrategyFn | None = None, default: StrategyFn | None = None) -> None
escalate_by_default
staticmethod
¶
Default strategy: always escalate to human.
abort_by_default
staticmethod
¶
Default strategy: hard abort on any unhandled failure.
chain
staticmethod
¶
chain(primary: StrategyFn, fallback: StrategyFn, after_kinds: tuple[str, ...] = ('escalate',)) -> StrategyFn
Return a strategy that tries primary and falls through to fallback
when primary returns an action whose kind is in after_kinds.
The default after_kinds=("escalate",) means: use primary normally, but
if primary decides to escalate, try fallback first instead.
Example — replan first, rollback if replan has already been tried::
from triage.strategies.replan import replan
from triage.strategies.rollback import rollback_to_checkpoint
policy = FailurePolicy(
LOOP_DETECTED=FailurePolicy.chain(
replan(hint="Try a different approach."),
rollback_to_checkpoint(),
after_kinds=("escalate",),
),
)
Example — retry up to 2 times, then replan::
policy = FailurePolicy(
EXTERNAL_FAULT=FailurePolicy.chain(
backoff_and_retry(max_attempts=2),
replan(hint="External service is down, try a different approach."),
after_kinds=("escalate",),
),
)
sequence
staticmethod
¶
Return a strategy that steps through strategies in order across
successive failures of the same type.
On the first failure, strategies[0] is called. On the second
failure of the same type, strategies[1], and so on. Once all
strategies are exhausted, returns RecoveryAction.ESCALATE.
The current position is derived from ctx.attempt_history — the
number of prior attempts that share ctx.failure_type — so no
external state is needed and the sequence is safe to share across
concurrent runs.
Example — replan once, then rollback, then escalate::
from triage.strategies.replan import replan
from triage.strategies.rollback import rollback_to_checkpoint
policy = FailurePolicy(
LOOP_DETECTED=FailurePolicy.sequence(
replan(hint="Try a different approach."),
rollback_to_checkpoint(),
),
)
Example — retry twice, then replan, then escalate::
policy = FailurePolicy(
EXTERNAL_FAULT=FailurePolicy.sequence(
backoff_and_retry(),
backoff_and_retry(),
replan(hint="External service may be down."),
),
)
from_yaml
classmethod
¶
Load a FailurePolicy from a TOML or YAML config file.
File format is determined by extension:
- .toml — parsed with stdlib tomllib (Python 3.11+)
- .yaml / .yml — parsed with pyyaml (install triage-agent[yaml])
TOML example (policy.toml)::
[EXTERNAL_FAULT]
strategy = "backoff_and_retry"
max_attempts = 5
[TIMEOUT]
strategy = "backoff_and_retry"
max_attempts = 3
[WRONG_TOOL_CALLED]
strategy = "retry_with_tool_manifest"
max_attempts = 2
default = "escalate"
YAML example (policy.yaml)::
EXTERNAL_FAULT:
strategy: backoff_and_retry
max_attempts: 5
default: escalate
Built-in strategy names: backoff_and_retry, retry_with_tool_manifest,
replan, resume_from_subgoal, rollback_to_checkpoint,
escalate, abort.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Path to the config file. |
required |
strategy_registry
|
dict[str, Any] | None
|
Optional dict mapping name → callable (or factory). When provided, merged with the built-in registry (custom names take precedence). |
None
|
Conceptual notes¶
Declaration¶
Pass strategy factories as keyword arguments matching FailureType member names.
default is a catch-all for any type without an explicit entry:
from triage.policy import FailurePolicy
from triage.strategies.retry import backoff_and_retry, retry_with_tool_manifest
from triage.strategies.replan import replan, resume_from_subgoal
policy = FailurePolicy(
WRONG_TOOL_CALLED=retry_with_tool_manifest(max_attempts=3),
SCHEMA_MISMATCH=retry_with_tool_manifest(max_attempts=2),
EXTERNAL_FAULT=backoff_and_retry(max_attempts=5),
LOOP_DETECTED=replan(hint="Try a different approach."),
CONSTRAINT_IGNORED=replan(hint="Re-read the constraints carefully."),
PLAN_INCOMPLETE=resume_from_subgoal(),
default=FailurePolicy.escalate_by_default(),
)
Custom strategy callables¶
Any async def that takes a FailureContext and returns a RecoveryAction is a valid
strategy:
async def smart_external_fault(ctx: FailureContext) -> RecoveryAction:
external_faults = sum(1 for ft, _ in ctx.attempt_history if ft == FailureType.EXTERNAL_FAULT)
if external_faults >= 3:
return RecoveryAction.ESCALATE(message="Service unavailable after 3 retries.")
return RecoveryAction.RETRY(delay=2.0 ** len(ctx.attempt_history))
Default actions¶
# Escalate to human on any unhandled failure
default = FailurePolicy.escalate_by_default()
# Hard stop on any unhandled failure
default = FailurePolicy.abort_by_default()
YAML / TOML loading¶
Policies can be loaded from a .yaml or .toml file (requires pyyaml for YAML):
Built-in strategy names: backoff_and_retry, retry_with_tool_manifest, replan,
resume_from_subgoal, rollback_to_checkpoint, escalate, abort.
Pass strategy_registry={"my_fn": my_fn} for custom strategies.