DEV Community

Mukunda Rao Katta
Mukunda Rao Katta

Posted on

My Hermes agent's stop condition was a 40-line if/elif chain. I replaced it with 3 lines.

Hermes Agent Challenge Submission: Write About Hermes Agent

This is a submission for the Hermes Agent Challenge.

My Hermes research agent's stop logic had grown into a 40-line if/elif block. Stop after 20 turns. Stop if cost exceeds $2. Stop if the response contains "FINAL ANSWER". Stop if the last tool called was "write_summary". Each condition was written out longhand, tested independently, and hard to reuse across different agents.

I extracted the pattern into agent-loop-stop.

Three lines replace forty

from agent_loop_stop import any_of, after_n_turns, cost_exceeds, response_contains

stopper = any_of(
    after_n_turns(20),
    cost_exceeds(2.00),
    response_contains("FINAL ANSWER"),
)

for turn in range(1, 100):
    response = call_llm(messages)
    state = {"turn": turn, "cost_usd": running_cost, "response": response.text}
    if stopper.check(state):
        break
Enter fullscreen mode Exit fullscreen mode

That's it. stopper.check(state) returns True when any condition fires. The state dict can have whatever you want in it — built-in conditions read well-known keys.

All built-in conditions

after_n_turns(20)                           # state["turn"] >= 20
cost_exceeds(2.00)                          # state["cost_usd"] > 2.00
response_contains("FINAL ANSWER")          # case-insensitive substring
last_tool_was("write_summary")             # state["last_tool"] == name
custom(lambda s: s.get("retries") > 3)    # any callable
always()                                   # always True (testing)
never()                                    # always False (placeholder)
Enter fullscreen mode Exit fullscreen mode

Compose with operators

# Stop when either fires
c = after_n_turns(20) | cost_exceeds(1.00)

# Stop only when both fire
c = after_n_turns(10) & cost_exceeds(0.50)

# Invert
c = ~response_contains("continue")
Enter fullscreen mode Exit fullscreen mode

Or use the function form:

any_of(after_n_turns(20), cost_exceeds(2.00), response_contains("done"))
all_of(after_n_turns(5), cost_exceeds(0.25))
negate(response_contains("error"))
Enter fullscreen mode Exit fullscreen mode

Both styles work identically. The operator form is more concise; the function form is more explicit about what's happening.

Diagnostic: which condition fired?

from agent_loop_stop import check_all

result = check_all(
    state,
    {
        "turn_limit": after_n_turns(20),
        "cost_limit": cost_exceeds(2.00),
        "done_signal": response_contains("FINAL ANSWER"),
    },
)

if result.stopped:
    log.info(f"Agent stopped. Reason(s): {result.triggered}")
    # ["turn_limit"] or ["done_signal"] or ["cost_limit", "done_signal"]
Enter fullscreen mode Exit fullscreen mode

check_all checks every named condition individually and returns a StopResult with which ones triggered. This is what I log in my Hermes agent — if I see "turn_limit" fired instead of "done_signal", that means the agent ran out of turns without finishing.

Custom conditions

c = custom(lambda s: len(s.get("tool_calls_this_turn", [])) > 5)
Enter fullscreen mode Exit fullscreen mode

Or subclass for reusable predicates:

from agent_loop_stop import StopCondition

class TokenBudgetStop(StopCondition):
    def __init__(self, limit: int):
        self._limit = limit

    def check(self, state):
        return state.get("tokens_used", 0) > self._limit

stopper = any_of(after_n_turns(20), TokenBudgetStop(4000))
Enter fullscreen mode Exit fullscreen mode

Per-agent stop configs

Different Hermes agents have different stop requirements:

SUPERVISOR_STOP = any_of(
    after_n_turns(50),
    cost_exceeds(5.00),
    response_contains("SYNTHESIS COMPLETE"),
)

WORKER_STOP = any_of(
    after_n_turns(15),
    cost_exceeds(0.50),
    last_tool_was("submit_findings"),
)
Enter fullscreen mode Exit fullscreen mode

Named, reusable, composable. Each agent gets its own stop config that says exactly what it means.

Zero dependencies

Standard library only: dataclasses, typing. No third-party packages.

pip install agent-loop-stop
Enter fullscreen mode Exit fullscreen mode

Repo: https://github.com/MukundaKatta/agent-loop-stop

Top comments (0)