Auditable BDI (belief-desire-intention) agents with LLM deliberation — explicit beliefs, committed intentions, and a trace that answers 'why'
Project description
cetana
Auditable BDI agents — a cognitive architecture for the LLM era.
Most LLM agents are a prompt loop: state lives in a transcript, "goals" are whatever the last completion implied, and nobody can say why the agent did what it did. The classical answer to exactly this problem is 30 years old: the Belief–Desire–Intention architecture (Bratman 1987; Rao & Georgeff 1995) — explicit mental state, deliberate commitment, coherent behaviour over time.
cetana is BDI rebuilt for LLMs, on one principle:
The architecture decides when to think. The LLM decides what to think.
- Beliefs are a revisable, sourced, timestamped store — not lines in a prompt
- Intentions are commitments: the agent does not re-deliberate every cycle, so behaviour is stable, cheap, and predictable
- The LLM is consulted at exactly two points — option generation and
planning — through a provider-agnostic
(prompt) -> strcallable - Plans may only use registered actions; a hallucinated capability is a caught error, not silent improvisation
- Every cycle is recorded:
agent.explain()answers "why did you do that?" with the actual cognitive history
Zero dependencies. Any LLM client. Fully deterministic under test (script the LLM, assert the behaviour).
cetanā (චේතනා) is the Pali term for volition — in Buddhist psychology, the mental factor that directs the mind toward action.
Installation
pip install cetana
Quick start
from cetana import ActionRegistry, BDIAgent, ok, fail
actions = ActionRegistry()
@actions.register("check_fridge", "See what ingredients are available")
def check_fridge():
return ok({"rice": True, "eggs": 2})
@actions.register("cook", "Cook a named dish")
def cook(dish):
return ok(f"{dish} ready")
# Any callable (prompt: str) -> str works: Anthropic, OpenAI, local, or a stub.
import anthropic
client = anthropic.Anthropic()
def llm(prompt):
msg = client.messages.create(
model="claude-opus-4-8", max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return msg.content[0].text
agent = BDIAgent("Feed the household dinner", llm, actions)
agent.believe("time", "evening")
agent.run()
print(agent.explain())
# cycle 1:
# options: cook_dinner: Cook rice for dinner; wait: ...
# committed to: Cook rice for dinner — it's evening and rice is available
# plan: check_fridge -> cook
# executed: check_fridge({}) [ok] obs={'rice': True, 'eggs': 2}
# cycle 2:
# executed: cook({'dish': 'rice'}) [ok] obs='rice ready'
# intention: succeeded
The cycle
percepts ──> revise beliefs ──> committed? ──yes──> execute next step ──> observe
│ no │
└─> deliberate (LLM) ─> commit intention ┘
Deterministic architecture code does perception, belief revision, commitment bookkeeping, execution, and failure accounting. The LLM fills two cognitive gaps — what is worth pursuing? and how? — and on step failure, a bounded replan (still worth it? new plan or give up?).
Why commitment matters
A prompt-loop agent reconsiders everything every turn — one odd completion
and it wanders. A BDI agent deliberates, commits, and then executes until
the intention succeeds, fails, or is deliberately dropped. Fewer LLM calls,
stabler behaviour, and a meaningful answer to "what is the agent doing right
now?": read agent.intention.
Testing your agent
The LLM port makes agents deterministic under test:
class ScriptedLLM:
def __init__(self, *responses):
self.responses = list(responses)
def __call__(self, prompt):
return self.responses.pop(0)
Script deliberation, assert on agent.trace — no API key, no flakiness.
This library's own test suite works exactly this way.
Status & roadmap
Early release (0.1.0) — the core loop, belief store, commitment machinery, trace, and failure handling are complete and tested. Planned: belief decay and confidence-weighted revision, multiple concurrent intentions with priority scheduling, a structured-output deliberation mode, and provider extras. The design is discussed in my Artificial Cognitive Systems series on Medium.
License
MIT — see LICENSE.
Author
Ravindu Pabasara Karunarathna — also the author of tokscope, slnic, and sinhaladate.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cetana-0.1.0.tar.gz.
File metadata
- Download URL: cetana-0.1.0.tar.gz
- Upload date:
- Size: 17.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
144973fb13764e0e9bdb49041d225008e267afe548a4e0a34ece912f117f80b7
|
|
| MD5 |
bab9e19e60fd75f1b427e9077404e6ef
|
|
| BLAKE2b-256 |
4b73772500c3b5dc226c8f887c0ea73eb53614dcf521ecb7b683560c64d84bc4
|
File details
Details for the file cetana-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cetana-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1954d06f481207112b87c8e1fa040c38f890e62798e04a31f57070973776dcbe
|
|
| MD5 |
3a6e6d73402a0911f862a32bde1677e4
|
|
| BLAKE2b-256 |
5a6ec1666ebf13a53bca586bdea5e29646f2526e693d1a894242d08298e8f091
|