Agent Architecture and Orchestration
Termination Conditions — Patterns
Termination conditions › Patterns
Make the postcondition the primary rule
Problem. When the only rules are ceilings, the run ends on exhaustion and success is inferred from the absence of an error. Nothing in the system ever states what completion would have looked like.
Approach. Express completion as a checkable predicate over the world, evaluated outside the model: the record exists with these fields, the suite passes, the output validates and its identifiers resolve. Evaluate it after every iteration and end on satisfaction. Ceilings remain, but as the exception rather than the mechanism.
Trade-off. Many valuable tasks have no mechanically checkable postcondition, and forcing one produces a proxy that is gamed — an agent that satisfies the check without doing the work. Writing predicates per task type is also sustained effort that competes with building features.
Layered backstops with distinct reasons
Problem. A single catch-all limit makes every unsuccessful run look the same. Budget exhaustion, an unresponsive dependency, and a genuine impasse are all reported as the run having stopped, so none of them can be measured or acted on.
Approach. Define separate backstops at separate levels — per call, per iteration, per run, per objective — each with its own reason code, and record which fired along with the counters at that moment. Make the reason a required field of every result, and build the run-outcome dashboard on the distribution of reasons rather than on a success rate.
Trade-off. More rules mean more interactions between them, and overlapping limits produce arbitrary attribution when two are reached nearly simultaneously. The reason vocabulary also needs ownership, or it fills with codes that mean nothing to anyone who was not there.
Promise a partial result
Problem. Ending on exhaustion by raising an error throws away everything the run accomplished. The retry begins from nothing, costs the same again, and is just as likely to exhaust.
Approach. Require every run to be able to produce its best current answer on demand, and make exhaustion a request for it rather than a failure. Return the partial result with the reason, the proportion of the work completed, and a resumption handle where resumption is possible, so the caller can decide between using it, continuing it, and discarding it.
Trade-off. Every workload now needs a defined notion of partial, which some genuinely do not have — half a database migration is not a result. Callers also start treating partial answers as complete ones unless the status is impossible to ignore.
Quiescence for collective efforts
Problem. In a coordinated effort, the coordinator deciding it is finished does not make it so. Participants are still executing and still writing, and the result is assembled while contributions are in flight.
Approach. Define completion as quiescence: no participant is running, no message is in flight, no claim is outstanding, and no work item is unfinished. Track outstanding work as an explicit counter incremented on dispatch and decremented on acknowledged completion, and treat a non-zero count at the coordinator's stop as a defect to investigate rather than a race to tolerate.
Trade-off. Counting requires every participant to acknowledge reliably, and one that dies without acknowledging blocks the condition indefinitely, which forces a timeout and therefore a second, weaker rule. Quiescence also delays the result until the slowest straggler finishes or is given up on.