Give an agent one task and it does it. Give it three, and you find out whether you built a planner or a to-do list with anxiety.
Because the three tasks compete. The customer escalation is urgent but small. The data migration is huge but can wait. The report is due at five and half-done. The agent has finite time, finite tokens, finite tool calls — it cannot do everything at once, so something has to decide what comes first. And if you didn't design that something, the agent improvises: it does them in the order they arrived, or whichever it read last, or the easiest one, and calls that a strategy.
Most agents don't prioritize. They have an implicit, accidental order and you only notice it's wrong when the urgent thing waits behind the trivial one.
What "important" even means here
The trap is treating prioritization as one number. It's at least two, and they pull apart constantly.
Urgency is about time — how soon does this need doing before the window closes? The escalation is urgent; the customer is waiting now. Value (or importance) is about payoff — how much does doing this matter? The migration is high-value; it unblocks everything downstream. The two are independent, which is the whole problem. The most dangerous task is high-urgency, low-value: the thing screaming for attention that doesn't actually matter much, and that an agent without a value axis will always do first, because loud reads as important.
The old Eisenhower matrix carves this into four boxes, and it ports to agents almost unchanged:
The top-right — urgent and valuable, the security patch — is unambiguous: do it first. The bottom-left — neither — should be dropped or deferred indefinitely, and an agent that keeps "tidy the logs" alive in its queue is wasting cycles it owes to the patch. The two off-diagonal boxes are where judgment lives, and where a naive agent reliably misallocates.
Turning judgment into a number
Boxes are intuition. To make an agent act on it, you usually score. Give each task a priority value and let the agent work the highest score first:
def priority(task):
# crude but honest: weigh value against urgency, discount by effort
return (task.value * task.urgency) / max(task.effort, 1)
queue.sort(key=priority, reverse=True)
next_task = queue[0]
This is a stripped-down utility function, and the lineage is old — expected-utility theory is decades of formal work on choosing among options by their weighted payoff. The weights are where the real decisions hide. Weight urgency too heavily and the agent becomes a reactive firefighter that never touches the important slow work. Weight value too heavily and it ignores the customer waiting right now to chip at the migration. There's no universally correct weighting; there's only the one that matches what your system is actually for, and you'll tune it by watching what the agent neglects.
Early autonomous-agent experiments like BabyAGI made this concrete and visible: the loop literally had a task-prioritization step that reordered its own to-do list after every action, before picking the next thing. Worth studying precisely because watching it reprioritize badly — chasing shiny subtasks while the actual goal drifted — teaches you what a prioritizer is for.
The two failures that bite
Starvation. A pure priority queue has a cruelty in it: a task that's always slightly lower-priority than whatever just arrived never runs. It sits there forever, outranked by a steady drip of small urgent things. The classic fix is aging — let a task's priority creep up the longer it's been waiting, so eventually even a low-value task surfaces. Without it, "low priority" silently means "never," and the slow important work quietly starves behind an endless parade of small fires.
Dependencies break naive ordering. Sometimes the highest-priority task can't go first because it depends on a lower-priority one finishing. You want to deploy (high value) but the tests have to pass first (lower value, but blocking). Priority alone gives the wrong order here — you need the dependency graph layered on top, so the agent does the boring prerequisite before the exciting payoff. An agent that grabs the top of the queue without checking what it's waiting on will keep reaching for a task it isn't allowed to do yet.
Re-decide, constantly
The last thing, and the one that separates a scheduler from a planner: priority isn't set once. It shifts as the world moves. A low-priority task becomes urgent when its deadline nears. A high-priority one becomes pointless when the thing it was for gets cancelled. New tasks arrive mid-run. A good agent re-prioritizes its queue continuously — after every action, it asks again what matters most now, because the answer from two steps ago may already be stale.
This is also where preemption earns its place. If the agent is two hours into the data migration and a critical security alert lands, the right move isn't to finish the migration first because it was "in progress." It's to checkpoint the migration, handle the alert, and come back — the same interrupt-and-resume machinery a human-in-the-loop gate uses, pointed at the agent's own priorities instead of a person's approval. An agent that can't be preempted by something genuinely more urgent isn't prioritizing; it's just committed to whatever it happened to start.
Which is the uncomfortable core of it. Prioritization isn't a sort you run at the start and forget. It's a decision the agent remakes at every step, with incomplete information, under constraints that keep changing — and the agent that nails it isn't the one that picks a perfect order once. It's the one that keeps noticing the order it picked is no longer right, and quietly fixes it before you have to.
Leave a Reply
Your email address will not be published.