Your agent is a courier in a corridor. It has a nine-step plan to deliver a parcel, and at the fourth step the door it has to cross closes. What does it do? It can recompute everything at every step, and then it spends its time thinking while the world keeps changing; it can carry out the plan to the end, and then it bangs into a closed door; it can stop as soon as anything changes, and then it also abandons plans that would have worked. A BDI architecture does not remove the dilemma: it makes it an explicit design choice.
The short answer: the Belief-Desire-Intention architecture separates what the agent believes, what it wants and what it has committed to, and its cycle decides at every turn when it is worth abandoning a commitment. Here you build a minimal interpreter, run it on three commitment strategies and read the numbers. The result is not what you expect: in the door scenario all three deliver at the same instant, and the difference lies in the wasted effort, not in speed.
Three mental states, and why exactly three
The primary source of this article is "BDI Agents: From Theory to Practice" by Anand Rao and Michael Georgeff, proceedings of the first International Conference on Multiagent Systems (ICMAS-95), AAAI 1995. I downloaded it and read the parts on which the text rests: the abstract, the introduction, the section on the system and its environment, the one on commitment strategies and the abstract architecture. I did not read the parts on the application to air traffic (the system called OASIS). The starting mental model is by Michael Bratman, cited in that paper: I did not read his book.
The authors derive the three states from six characteristics of the domain, for example that the environment can be observed only locally and changes unpredictably. Three components of the system state follow from there.
- Beliefs: the informational component. They are needed because the environment cannot be read in a single act of perception, and they must be updated after every percept; they can be a variable, a database or a set of formulas.
- Desires: the motivational component, that is the goals with their priorities. The authors distinguish them from the "goals" of the artificial intelligence literature because there can be many at the same instant and they can be incompatible with each other.
- Intentions: the deliberative component, that is the course of action most recently chosen by the selection function.
The first two components answer "what do I know" and "what would I like"; the third is the one that has no obvious equivalent in ordinary programs, and it is the reason the architecture exists. A desire binds no one: you can have two in conflict. An intention does, because it has already been chosen and it limits what the agent considers in the following steps.
The dilemma that justifies intentions
Rao and Georgeff explain why the third component is needed with an argument worth rereading, because it is the heart of the article. Decision theory asks you to reapply the selection function every time the environment changes; ordinary programs, once started, expect to reach the end without reassessing anything. Neither works when the environment changes while you compute or while you execute: recomputing at every step costs too much and the chosen action may already be invalid, whereas unconditional commitment can make the goal fail.
The way out that the paper indicates is to limit the frequency of reconsideration, looking for a balance between reconsidering too much and too little, and it refers to Kinny and Georgeff (1991) for the details: that work is cited in the paper and I did not read it. The intention is the extra state that makes all this possible: it remembers the choice already made, and reconsideration happens only when an explicit check decides it. In your corridor, that check is the question "is the door still open?".
The interpreter cycle
Rao and Georgeff describe an abstract interpreter with an event queue and the three data structures. At each cycle, the option generator reads the event queue and returns a list of options; the deliberator chooses a subset of them and adds it to the intentions; if there is an intention to execute an atomic action, the agent executes it; the external events that happened in the meantime go into the queue; finally the agent removes from the desires and the intentions those that have succeeded and those that have become impossible. In the code that follows each step has a method with the same name or an equivalent one, in the same order.
The authors warn that this architecture is an idealisation: it rests on logically closed sets of beliefs, desires and intentions, and the proof procedures required are not computable. The practical system they describe simplifies the representation choices. The listing that follows is yet a third thing, poorer than both: an interpreter with a single desire, a hand-built plan and a world of nine squares. It serves to show the cycle and the commitment strategies, not to reproduce the authors' system.
The first block is the world: a corridor with a door on square 5 and a destination at square 8. The closed door makes the "advance" action towards square 5 fail, and "drop" succeeds only in square 8. External events arrive at fixed instants from a dictionary, so every scenario repeats identically. The identifiers are Italian (Mondo is the world, agisci is "act", porta_aperta is "door open"); the strings the program prints and the action names are in English.
"""Minimal BDI interpreter: same cycle as the abstract interpreter of Rao and Georgeff (1995)."""
DESTINAZIONE, PORTA = 8, 5
class Mondo:
"""Corridor 0..8 with a door on square 5. External events per instant."""
def __init__(self, eventi):
self.pos, self.porta_aperta, self.consegnato = 0, True, False
self.eventi = eventi # {instant: event name}
def applica_eventi(self, t):
e = self.eventi.get(t)
if e == "door closes":
self.porta_aperta = False
elif e == "door opens":
self.porta_aperta = True
return e
def agisci(self, azione):
if azione == "advance":
if self.pos + 1 == PORTA and not self.porta_aperta:
return False # bumps into the closed door
self.pos += 1
return True
if azione == "drop":
if self.pos != DESTINAZIONE:
return False
self.consegnato = True
return True
return True # "wait"
The second block contains the three structures and the first steps of the cycle. genera_opzioni (generate options) produces the "deliver" option only if the desire exists, the parcel has not already been delivered, the door is believed open and there is no intention for that goal yet: it is the condition under which the plan applies. delibera (deliberate) chooses the first option (there is at most one), aggiorna_intenzioni (update intentions) builds the plan from the believed position, esegui (execute) extracts an action, or returns "wait" if there is no intention.
class AgenteBDI:
def __init__(self, strategia):
self.strategia = strategia # "blind", "single", "open"
self.credenze = {"pos": 0, "porta_aperta": True, "consegnato": False}
self.desideri = {"deliver"}
self.intenzioni = [] # each intention: {"desiderio", "piano"}
self.eventi = ["start"] # event queue
def genera_opzioni(self):
c = self.credenze
if "deliver" in self.desideri and not c["consegnato"] and c["porta_aperta"] \
and not any(i["desiderio"] == "deliver" for i in self.intenzioni):
return ["deliver"]
return []
def delibera(self, opzioni):
return opzioni[:1] # a single desire: pick the first
def aggiorna_intenzioni(self, scelte):
for d in scelte:
n = DESTINAZIONE - self.credenze["pos"]
self.intenzioni.append({"desiderio": d, "piano": ["advance"] * n + ["drop"]})
def esegui(self):
if not self.intenzioni:
return "wait"
return self.intenzioni[0]["piano"].pop(0)
The third block closes the cycle with two methods. percepisci (perceive) updates the beliefs from the world and, if the "order cancelled" event arrives, removes the desire. lascia_atteggiamenti (drop attitudes) is the point where the three strategies diverge: for each intention it computes whether it has succeeded, whether it has become impossible according to the beliefs (door closed before square 5) and whether the desire has gone, and it uses these three facts in different ways depending on the strategy. Remove that method and the agent never abandons anything: the intentions pile up.
def percepisci(self, mondo, evento):
self.credenze.update(pos=mondo.pos, porta_aperta=mondo.porta_aperta,
consegnato=mondo.consegnato)
if evento == "order cancelled":
self.desideri.discard("deliver")
def lascia_atteggiamenti(self):
c, restanti = self.credenze, []
for i in self.intenzioni:
riuscita = not i["piano"] or c["consegnato"]
impossibile = (not c["porta_aperta"] and c["pos"] < PORTA)
desiderio_caduto = i["desiderio"] not in self.desideri
if self.strategia == "blind":
lasciare = riuscita # only when the plan is finished
elif self.strategia == "single":
lasciare = riuscita or impossibile # listens to the beliefs
else:
lasciare = riuscita or impossibile or desiderio_caduto
if not lasciare:
restanti.append(i)
self.intenzioni = restanti
Three commitment strategies
The section on theory in Rao and Georgeff defines three kinds of agent, according to when a commitment is dropped. The definitions that follow are theirs, in my words.
- Blind (blindly committed): does not admit changes of beliefs or desires that conflict with its commitments.
- Single-minded (single-minded): admits changes of beliefs and drops its commitments accordingly.
- Open-minded (open-minded): admits changes of both beliefs and desires, which may force it to abandon its commitments.
The three definitions are ordered by how willing the agent is to rethink itself: from the blind one, which does not rethink, to the open one, which rethinks at every change. My code is a deliberately simple operationalisation of them: lascia_atteggiamenti makes the blind agent drop the intention only when the plan is finished, the single-minded one also when the closed door makes it impossible, the open one also when the desire no longer exists. It is not the authors' formal semantics, which lies in accessibility relations over possible worlds. One difference is intentional: my blind agent updates its beliefs all the same, and simply does not use them to decide, whereas in the authors' definition it is the agent that does not admit conflicting changes. Another difference is a flaw: the blind agent considers an intention "succeeded" when its plan is exhausted, even if the parcel has not been delivered. In the listing the strategies are the strings "blind", "single" and "open".
What the measurement says
Before the outputs, the timeline of the door scenario, worked out by hand. The agent starts at 0 with a plan of eight "advance" and one "drop". At steps 1-4 it advances to square 4. At instant 4 the door closes, but the agent only finds out at the end of the instant. The single-minded agent drops the intention and waits from instant 5 to 8; the door reopens at instant 8 and at the end of the instant it finds out, and from 9 to 13 it does four "advance" and one "drop": it delivers at instant 13, with zero failed actions and four waits. The blind agent knows nothing: at steps 5, 6 and 7 it bumps into the door, at 8 it gets through, at 9 it tries to drop in square 5 and fails, then redoes the plan from 5 and arrives at instant 13 with four failed actions. The assert statements in the block verify exactly these numbers.
The fourth block contains the simulation cycle, the four scenarios and the comparison. The cycle follows the order of the authors' interpreter: options, deliberation, intention update, execution, new events from the world, abandonment of succeeded or impossible intentions. There is no randomness: the reported output is identical in two consecutive runs.
def simula(strategia, eventi, istanti=20):
mondo, ag = Mondo(eventi), AgenteBDI(strategia)
esito = {"consegna_a": None, "fallite": 0, "dopo_annullo": 0, "attese": 0}
annullato = False
for t in range(1, istanti + 1):
evento = mondo.applica_eventi(t)
opzioni = ag.genera_opzioni() # options from the queue and the desires
ag.aggiorna_intenzioni(ag.delibera(opzioni))
azione = ag.esegui()
ok = mondo.agisci(azione)
if azione == "wait":
if "deliver" in ag.desideri and not mondo.consegnato:
esito["attese"] += 1 # waiting with a desire still open
elif not ok:
esito["fallite"] += 1
elif annullato:
esito["dopo_annullo"] += 1
if mondo.consegnato and esito["consegna_a"] is None:
esito["consegna_a"] = t
ag.percepisci(mondo, evento) # new external events
annullato = annullato or evento == "order cancelled"
ag.lascia_atteggiamenti() # succeeded or impossible intentions
return esito
SCENARI = {
"no events": {},
"door closes t4, opens t8": {4: "door closes", 8: "door opens"},
"door closes t4, does not reopen": {4: "door closes"},
"order cancelled t4": {4: "order cancelled"},
}
if __name__ == "__main__":
risultati = {}
for nome, eventi in SCENARI.items():
print(nome)
for s in ("blind", "single", "open"):
r = simula(s, eventi)
risultati[(nome, s)] = r
print(f" {s:<8} delivered at t={r['consegna_a']} failed={r['fallite']} "
f"waits={r['attese']} actions after cancel={r['dopo_annullo']}")
# Check: the values worked out by hand on the timeline (see text)
assert risultati[("no events", "blind")]["consegna_a"] == 9
assert risultati[("door closes t4, opens t8", "single")] == \
{"consegna_a": 13, "fallite": 0, "dopo_annullo": 0, "attese": 4}
assert risultati[("door closes t4, opens t8", "blind")]["fallite"] == 4
assert risultati[("door closes t4, does not reopen", "blind")]["fallite"] == 5
assert risultati[("door closes t4, does not reopen", "single")] == \
{"consegna_a": None, "fallite": 0, "dopo_annullo": 0, "attese": 16}
assert risultati[("order cancelled t4", "open")]["dopo_annullo"] == 0
assert risultati[("order cancelled t4", "single")]["dopo_annullo"] == 5
print("hand-made timeline checks: ok")
# Output (two identical runs):
# no events
# blind delivered at t=9 failed=0 waits=0 actions after cancel=0
# single delivered at t=9 failed=0 waits=0 actions after cancel=0
# open delivered at t=9 failed=0 waits=0 actions after cancel=0
# door closes t4, opens t8
# blind delivered at t=13 failed=4 waits=0 actions after cancel=0
# single delivered at t=13 failed=0 waits=4 actions after cancel=0
# open delivered at t=13 failed=0 waits=4 actions after cancel=0
# door closes t4, does not reopen
# blind delivered at t=None failed=5 waits=11 actions after cancel=0
# single delivered at t=None failed=0 waits=16 actions after cancel=0
# open delivered at t=None failed=0 waits=16 actions after cancel=0
# order cancelled t4
# blind delivered at t=9 failed=0 waits=0 actions after cancel=5
# single delivered at t=9 failed=0 waits=0 actions after cancel=5
# open delivered at t=None failed=0 waits=0 actions after cancel=0
# hand-made timeline checks: ok
Read the output scenario by scenario. With no events the three agents deliver at 9 and coincide: the commitment strategy does not matter when nothing changes. With the door reopening at instant 8 all deliver at 13, but the blind agent has 4 failed actions against 0: the speed is the same because the door reopens just when the blind agent reaches it, a coincidence of this scenario and not a rule. If the door does not reopen, the blind agent makes 5 failed actions and does not deliver, while the other two wait without bumping into anything. With the order cancelled at instant 4, the blind and the single-minded agents make 5 useless actions and deliver a parcel nobody is waiting for; only the open one stops, and its "delivered at t=None" is the intended outcome.
Two things not to read into the output. The first: the three strategies cannot be ranked by quality. The blind agent is the most expensive when the environment changes, but it saves on checks when nothing changes: in my code this saving is not visible because checks have no price, and in a real system they do, which is exactly the dilemma the authors talk about. The second: the cancelled-order scenario says that the single-minded agent does not reconsider desires. Whether that is a flaw depends on the application: if desires change only by mistake, it is a virtue.
Where the map does not hold, and when not to use BDI
My interpreter has a single desire and a single plan, built to measure: it has nothing of what would make a practical system flexible. The paper by Rao and Georgeff presents its own practical system as a simplified version of the Procedural Reasoning System, and on the language side there is Rao's AgentSpeak(L) (1996). Of the latter I verified in the Crossref registry the title, the venue (Lecture Notes in Computer Science, Agents Breaking Away), the year and pages 42-55; I did not read the text, so I do not describe its syntax or how it treats plans.
BDI is a scaffolding that costs code and decisions: you have an event queue, three structures and a criterion for abandoning. If the world does not change while the plan runs, a sequence of actions computed once and a final check are enough; if it changes in a way you cannot check at every step, you need at least the question "is this commitment still worth it?". The strategy to choose depends on how often the world changes compared with how much checking costs, and only a measurement in your domain tells you.
Frequently asked questions
What is the difference between a desire and an intention?
A desire is a goal the agent can hold together with others, even incompatible ones; an intention is the goal the agent has chosen and committed to. In the listing the desire is the element of desideri, the intention is an entry of intenzioni with its plan.
Is one commitment strategy always better than the others?
No, and the experiment shows it: with the door reopening the three agents deliver at the same instant, with the door closed for ever the blind agent wastes five actions, with the order cancelled only the open-minded agent stops. It depends on what changes in your environment.
Do you need an artificial intelligence model to use BDI?
No. The interpreter in this article is Python with no libraries and no models called. The architecture describes how to decide and when to rethink, not with which technique to generate the plans.
What you can do now
You can say what the three mental states are for and what the dilemma is that makes the intention necessary. You can write a minimal BDI interpreter with the authors' cycle and see where the choice of commitment strategy falls. And you can read its outputs without forcing them: the strategy is not chosen by quality, but according to how often the world changes. The previous article defined the agent, the next one brings an agent's behaviour onto a platform, and the series index lists the rest.
Sources
- Rao, Georgeff - BDI Agents: From Theory to Practice (ICMAS-95, AAAI 1995) - the three components, the reconsideration dilemma, the abstract interpreter, the three commitment strategies; abstract, introduction and sections on system, strategies and abstract architecture read, the OASIS part not read, consulted on 2026-11-13
- Rao - AgentSpeak(L): BDI agents speak out in a logical computable language (Lecture Notes in Computer Science, 1996) - bibliographic data only, verified in the Crossref registry; the text cited, not read, consulted on 2026-11-13
- The numbers in the output come from the command
python3 -I bdi_minimo.pyon the listing of the article (the four blocks are contiguous portions of the same file), run twice with identical output on 2026-11-13. The English strings were translated in the listing and the program was run again twice with the same numbers.
Commenti
Caricamento commenti...
Sign in to comment