docs: add docs to CB
Pretty much every class and method should have documentation now. ref: N25B-295
This commit is contained in:
@@ -19,6 +19,27 @@ DELIMITER = ";\n" # TODO: temporary until we support lists in AgentSpeak
|
||||
|
||||
|
||||
class BDICoreAgent(BaseAgent):
|
||||
"""
|
||||
BDI Core Agent.
|
||||
|
||||
This is the central reasoning agent of the system, powered by the **AgentSpeak(L)** language.
|
||||
It maintains a belief base (representing the state of the world) and a set of plans (rules).
|
||||
|
||||
It runs an internal BDI (Belief-Desire-Intention) cycle using the ``agentspeak`` library.
|
||||
When beliefs change (e.g., via :meth:`_apply_beliefs`), the agent evaluates its plans to
|
||||
determine the best course of action.
|
||||
|
||||
**Custom Actions:**
|
||||
It defines custom actions (like ``.reply``) that allow the AgentSpeak code to interact with
|
||||
external Python agents (e.g., querying the LLM).
|
||||
|
||||
:ivar bdi_agent: The internal AgentSpeak agent instance.
|
||||
:ivar asl_file: Path to the AgentSpeak source file (.asl).
|
||||
:ivar env: The AgentSpeak environment.
|
||||
:ivar actions: A registry of custom actions available to the AgentSpeak code.
|
||||
:ivar _wake_bdi_loop: Event used to wake up the reasoning loop when new beliefs arrive.
|
||||
"""
|
||||
|
||||
bdi_agent: agentspeak.runtime.Agent
|
||||
|
||||
def __init__(self, name: str, asl: str):
|
||||
@@ -30,6 +51,13 @@ class BDICoreAgent(BaseAgent):
|
||||
self._wake_bdi_loop = asyncio.Event()
|
||||
|
||||
async def setup(self) -> None:
|
||||
"""
|
||||
Initialize the BDI agent.
|
||||
|
||||
1. Registers custom actions (like ``.reply``).
|
||||
2. Loads the .asl source file.
|
||||
3. Starts the reasoning loop (:meth:`_bdi_loop`) in the background.
|
||||
"""
|
||||
self.logger.debug("Setup started.")
|
||||
|
||||
self._add_custom_actions()
|
||||
@@ -42,6 +70,9 @@ class BDICoreAgent(BaseAgent):
|
||||
self.logger.debug("Setup complete.")
|
||||
|
||||
async def _load_asl(self):
|
||||
"""
|
||||
Load and parse the AgentSpeak source file.
|
||||
"""
|
||||
try:
|
||||
with open(self.asl_file) as source:
|
||||
self.bdi_agent = self.env.build_agent(source, self.actions)
|
||||
@@ -51,7 +82,11 @@ class BDICoreAgent(BaseAgent):
|
||||
|
||||
async def _bdi_loop(self):
|
||||
"""
|
||||
Runs the AgentSpeak BDI loop. Efficiently checks for when the next expected work will be.
|
||||
The main BDI reasoning loop.
|
||||
|
||||
It waits for the ``_wake_bdi_loop`` event (set when beliefs change or actions complete).
|
||||
When awake, it steps through the AgentSpeak interpreter. It also handles sleeping if
|
||||
the agent has deferred intentions (deadlines).
|
||||
"""
|
||||
while self._running:
|
||||
await (
|
||||
@@ -78,7 +113,12 @@ class BDICoreAgent(BaseAgent):
|
||||
|
||||
async def handle_message(self, msg: InternalMessage):
|
||||
"""
|
||||
Route incoming messages (Beliefs or LLM responses).
|
||||
Handle incoming messages.
|
||||
|
||||
- **Beliefs**: Updates the internal belief base.
|
||||
- **LLM Responses**: Forwards the generated text to the Robot Speech Agent (actuation).
|
||||
|
||||
:param msg: The received internal message.
|
||||
"""
|
||||
self.logger.debug("Processing message from %s.", msg.sender)
|
||||
|
||||
@@ -106,6 +146,12 @@ class BDICoreAgent(BaseAgent):
|
||||
await self.send(out_msg)
|
||||
|
||||
def _apply_beliefs(self, beliefs: list[Belief]):
|
||||
"""
|
||||
Update the belief base with a list of new beliefs.
|
||||
|
||||
If ``replace=True`` is set on a belief, it removes all existing beliefs with that name
|
||||
before adding the new one.
|
||||
"""
|
||||
if not beliefs:
|
||||
return
|
||||
|
||||
@@ -115,6 +161,12 @@ class BDICoreAgent(BaseAgent):
|
||||
self._add_belief(belief.name, belief.arguments)
|
||||
|
||||
def _add_belief(self, name: str, args: Iterable[str] = []):
|
||||
"""
|
||||
Add a single belief to the BDI agent.
|
||||
|
||||
:param name: The functor/name of the belief (e.g., "user_said").
|
||||
:param args: Arguments for the belief.
|
||||
"""
|
||||
# new_args = (agentspeak.Literal(arg) for arg in args) # TODO: Eventually support multiple
|
||||
merged_args = DELIMITER.join(arg for arg in args)
|
||||
new_args = (agentspeak.Literal(merged_args),)
|
||||
|
||||
@@ -11,8 +11,14 @@ from control_backend.schemas.program import Program
|
||||
|
||||
class BDIProgramManager(BaseAgent):
|
||||
"""
|
||||
Will interpret programs received from the HTTP endpoint. Extracts norms, goals, triggers and
|
||||
forwards them to the BDI as beliefs.
|
||||
BDI Program Manager Agent.
|
||||
|
||||
This agent is responsible for receiving high-level programs (sequences of instructions/goals)
|
||||
from the external HTTP API (via ZMQ) and translating them into core beliefs (norms and goals)
|
||||
for the BDI Core Agent. In the future, it will be responsible for determining when goals are
|
||||
met, and passing on new norms and goals accordingly.
|
||||
|
||||
:ivar sub_socket: The ZMQ SUB socket used to receive program updates.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@@ -20,6 +26,18 @@ class BDIProgramManager(BaseAgent):
|
||||
self.sub_socket = None
|
||||
|
||||
async def _send_to_bdi(self, program: Program):
|
||||
"""
|
||||
Convert a received program into BDI beliefs and send them to the BDI Core Agent.
|
||||
|
||||
Currently, it takes the **first phase** of the program and extracts:
|
||||
- **Norms**: Constraints or rules the agent must follow.
|
||||
- **Goals**: Objectives the agent must achieve.
|
||||
|
||||
These are sent as a ``BeliefMessage`` with ``replace=True``, meaning they will
|
||||
overwrite any existing norms/goals of the same name in the BDI agent.
|
||||
|
||||
:param program: The program object received from the API.
|
||||
"""
|
||||
first_phase = program.phases[0]
|
||||
norms_belief = Belief(
|
||||
name="norms",
|
||||
@@ -44,7 +62,10 @@ class BDIProgramManager(BaseAgent):
|
||||
|
||||
async def _receive_programs(self):
|
||||
"""
|
||||
Continuously receive programs from the HTTP endpoint, sent to us over ZMQ.
|
||||
Continuous loop that receives program updates from the HTTP endpoint.
|
||||
|
||||
It listens to the ``program`` topic on the internal ZMQ SUB socket.
|
||||
When a program is received, it is validated and forwarded to BDI via :meth:`_send_to_bdi`.
|
||||
"""
|
||||
while True:
|
||||
topic, body = await self.sub_socket.recv_multipart()
|
||||
@@ -58,6 +79,12 @@ class BDIProgramManager(BaseAgent):
|
||||
await self._send_to_bdi(program)
|
||||
|
||||
async def setup(self):
|
||||
"""
|
||||
Initialize the agent.
|
||||
|
||||
Connects the internal ZMQ SUB socket and subscribes to the 'program' topic.
|
||||
Starts the background behavior to receive programs.
|
||||
"""
|
||||
context = Context.instance()
|
||||
|
||||
self.sub_socket = context.socket(zmq.SUB)
|
||||
|
||||
@@ -10,14 +10,33 @@ from control_backend.schemas.belief_message import Belief, BeliefMessage
|
||||
|
||||
class BDIBeliefCollectorAgent(BaseAgent):
|
||||
"""
|
||||
Continuously collects beliefs/emotions from extractor agents and forwards a
|
||||
unified belief packet to the BDI agent.
|
||||
BDI Belief Collector Agent.
|
||||
|
||||
This agent acts as a central aggregator for beliefs derived from various sources (e.g., text,
|
||||
emotion, vision). It receives raw extracted data from other agents,
|
||||
normalizes them into valid :class:`Belief` objects, and forwards them as a unified packet to the
|
||||
BDI Core Agent.
|
||||
|
||||
It serves as a funnel to ensure the BDI agent receives a consistent stream of beliefs.
|
||||
"""
|
||||
|
||||
async def setup(self):
|
||||
"""
|
||||
Initialize the agent.
|
||||
"""
|
||||
self.logger.info("Setting up %s", self.name)
|
||||
|
||||
async def handle_message(self, msg: InternalMessage):
|
||||
"""
|
||||
Handle incoming messages from other extractor agents.
|
||||
|
||||
Routes the message to specific handlers based on the 'type' field in the JSON body.
|
||||
Supported types:
|
||||
- ``belief_extraction_text``: Handled by :meth:`_handle_belief_text`
|
||||
- ``emotion_extraction_text``: Handled by :meth:`_handle_emo_text`
|
||||
|
||||
:param msg: The received internal message.
|
||||
"""
|
||||
sender_node = msg.sender
|
||||
|
||||
# Parse JSON payload
|
||||
@@ -49,12 +68,22 @@ class BDIBeliefCollectorAgent(BaseAgent):
|
||||
|
||||
async def _handle_belief_text(self, payload: dict, origin: str):
|
||||
"""
|
||||
Expected payload:
|
||||
{
|
||||
"type": "belief_extraction_text",
|
||||
"beliefs": {"user_said": ["Can you help me?"]}
|
||||
Process text-based belief extraction payloads.
|
||||
|
||||
}
|
||||
Expected payload format::
|
||||
|
||||
{
|
||||
"type": "belief_extraction_text",
|
||||
"beliefs": {
|
||||
"user_said": ["Can you help me?"],
|
||||
"intention": ["ask_help"]
|
||||
}
|
||||
}
|
||||
|
||||
Validates and converts the dictionary items into :class:`Belief` objects.
|
||||
|
||||
:param payload: The dictionary payload containing belief data.
|
||||
:param origin: The name of the sender agent.
|
||||
"""
|
||||
beliefs = payload.get("beliefs", {})
|
||||
|
||||
@@ -90,12 +119,24 @@ class BDIBeliefCollectorAgent(BaseAgent):
|
||||
await self._send_beliefs_to_bdi(beliefs, origin=origin)
|
||||
|
||||
async def _handle_emo_text(self, payload: dict, origin: str):
|
||||
"""TODO: implement (after we have emotional recognition)"""
|
||||
"""
|
||||
Process emotion extraction payloads.
|
||||
|
||||
**TODO**: Implement this method once emotion recognition is integrated.
|
||||
|
||||
:param payload: The dictionary payload containing emotion data.
|
||||
:param origin: The name of the sender agent.
|
||||
"""
|
||||
pass
|
||||
|
||||
async def _send_beliefs_to_bdi(self, beliefs: list[Belief], origin: str | None = None):
|
||||
"""
|
||||
Sends a unified belief packet to the BDI agent.
|
||||
Send a list of aggregated beliefs to the BDI Core Agent.
|
||||
|
||||
Wraps the beliefs in a :class:`BeliefMessage` and sends it via the 'beliefs' thread.
|
||||
|
||||
:param beliefs: The list of Belief objects to send.
|
||||
:param origin: (Optional) The original source of the beliefs (unused currently).
|
||||
"""
|
||||
if not beliefs:
|
||||
return
|
||||
|
||||
@@ -6,12 +6,31 @@ from control_backend.core.config import settings
|
||||
|
||||
|
||||
class TextBeliefExtractorAgent(BaseAgent):
|
||||
"""
|
||||
Text Belief Extractor Agent.
|
||||
|
||||
This agent is responsible for processing raw text (e.g., from speech transcription) and
|
||||
extracting semantic beliefs from it.
|
||||
|
||||
In the current demonstration version, it performs a simple wrapping of the user's input
|
||||
into a ``user_said`` belief. In a full implementation, this agent would likely interact
|
||||
with an LLM or NLU engine to extract intent, entities, and other structured information.
|
||||
"""
|
||||
|
||||
async def setup(self):
|
||||
"""
|
||||
Initialize the agent and its resources.
|
||||
"""
|
||||
self.logger.info("Settting up %s.", self.name)
|
||||
# Setup LLM belief context if needed (currently demo is just passthrough)
|
||||
self.beliefs = {"mood": ["X"], "car": ["Y"]}
|
||||
|
||||
async def handle_message(self, msg: InternalMessage):
|
||||
"""
|
||||
Handle incoming messages, primarily from the Transcription Agent.
|
||||
|
||||
:param msg: The received message containing transcribed text.
|
||||
"""
|
||||
sender = msg.sender
|
||||
if sender == settings.agent_settings.transcription_name:
|
||||
self.logger.debug("Received text from transcriber: %s", msg.body)
|
||||
@@ -21,7 +40,15 @@ class TextBeliefExtractorAgent(BaseAgent):
|
||||
|
||||
async def _process_transcription_demo(self, txt: str):
|
||||
"""
|
||||
Demo version to process the transcription input to beliefs.
|
||||
Process the transcribed text and generate beliefs.
|
||||
|
||||
**Demo Implementation:**
|
||||
Currently, this method takes the raw text ``txt`` and wraps it into a belief structure:
|
||||
``user_said("txt")``.
|
||||
|
||||
This belief is then sent to the :class:`BDIBeliefCollectorAgent`.
|
||||
|
||||
:param txt: The raw transcribed text string.
|
||||
"""
|
||||
# For demo, just wrapping user text as user_said belief
|
||||
belief = {"beliefs": {"user_said": [txt]}, "type": "belief_extraction_text"}
|
||||
|
||||
Reference in New Issue
Block a user