From 1c510c661e2337287317b6d0827749745deb8c18 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 21 Nov 2025 12:08:53 +0100 Subject: [PATCH] feat: more robust belief management ref: N25B-316 --- .logging_config.yaml | 2 +- .../bdi/bdi_core_agent/bdi_core_agent.py | 75 +++++++++++++++---- .../agents/bdi/bdi_core_agent/rules.asl | 6 +- .../agents/perception/vad_agent.py | 4 +- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/.logging_config.yaml b/.logging_config.yaml index 0403c77..a244917 100644 --- a/.logging_config.yaml +++ b/.logging_config.yaml @@ -8,7 +8,7 @@ formatters: # Console output colored: (): "colorlog.ColoredFormatter" - format: "{log_color}{asctime} | {levelname:11} | {name:70} | {message}" + format: "{log_color}{asctime}.{msecs:03.0f} | {levelname:11} | {name:70} | {message}" style: "{" datefmt: "%H:%M:%S" diff --git a/src/control_backend/agents/bdi/bdi_core_agent/bdi_core_agent.py b/src/control_backend/agents/bdi/bdi_core_agent/bdi_core_agent.py index 8de6204..73d187a 100644 --- a/src/control_backend/agents/bdi/bdi_core_agent/bdi_core_agent.py +++ b/src/control_backend/agents/bdi/bdi_core_agent/bdi_core_agent.py @@ -13,11 +13,12 @@ from control_backend.schemas.ri_message import SpeechCommand class BDICoreAgent(BaseAgent): + bdi_agent: agentspeak.runtime.Agent + def __init__(self, name: str, asl: str): super().__init__(name) self.asl_file = asl self.env = agentspeak.runtime.Environment() - self.bdi_agent = None self.actions = agentspeak.stdlib.actions async def setup(self) -> None: @@ -42,7 +43,6 @@ class BDICoreAgent(BaseAgent): async def _bdi_loop(self): """Runs the AgentSpeak BDI loop.""" while self._running: - assert self.bdi_agent is not None self.bdi_agent.step() await asyncio.sleep(0.01) @@ -74,30 +74,72 @@ class BDICoreAgent(BaseAgent): ) await self.send(out_msg) - # TODO: test way of adding beliefs def _add_beliefs(self, beliefs: dict[str, list[str]]): if not beliefs: return - for belief_name, args in beliefs.items(): - self._add_belief(belief_name, args) + for name, args in beliefs.items(): + self._add_belief(name, args) - if belief_name == "user_said": - self._add_belief("new_message") - - def _add_belief(self, belief_name: str, arguments: Iterable[str] = []): - args = (agentspeak.Literal(arg) for arg in arguments) - literal_belief = agentspeak.Literal(belief_name, args) + def _add_belief(self, name: str, args: Iterable[str] = []): + new_args = (agentspeak.Literal(arg) for arg in args) + term = agentspeak.Literal(name, new_args) assert self.bdi_agent is not None self.bdi_agent.call( agentspeak.Trigger.addition, agentspeak.GoalType.belief, - literal_belief, + term, agentspeak.runtime.Intention(), ) - self.logger.debug(f"Added belief {belief_name}({','.join(arguments)})") + self.logger.debug(f"Added belief {self.format_belief_string(name, args)}") + + def _remove_belief(self, name: str, args: Iterable[str]): + """ + Removes a specific belief (with arguments), if it exists. + """ + new_args = (agentspeak.Literal(arg) for arg in args) + term = agentspeak.Literal(name, new_args) + + assert self.bdi_agent is not None + + result = self.bdi_agent.call( + agentspeak.Trigger.removal, + agentspeak.GoalType.belief, + term, + agentspeak.runtime.Intention(), + ) + + if result: + self.logger.debug(f"Removed belief {self.format_belief_string(name, args)}") + else: + self.logger.debug("Failed to remove belief (it was not in the belief base).") + + # TODO: decide if this is needed + def _remove_all_with_name(self, name: str): + """ + Removes all beliefs that match the given `name`. + """ + assert self.bdi_agent is not None + + relevant_groups = [] + for key in self.bdi_agent.beliefs: + if key[0] == name: + relevant_groups.append(key) + + removed_count = 0 + for group in relevant_groups: + for belief in self.bdi_agent.beliefs[group]: + self.bdi_agent.call( + agentspeak.Trigger.removal, + agentspeak.GoalType.belief, + belief, + agentspeak.runtime.Intention(), + ) + removed_count += 1 + + self.logger.debug(f"Removed {removed_count} beliefs.") def _add_custom_actions(self) -> None: """Add any custom actions here.""" @@ -119,3 +161,10 @@ class BDICoreAgent(BaseAgent): msg = InternalMessage(to=settings.agent_settings.llm_name, sender=self.name, body=text) await self.send(msg) self.logger.info("Message sent to LLM agent: %s", text) + + @staticmethod + def format_belief_string(name: str, args: Iterable[str] = []): + """ + Given a belief's name and its args, return a string of the form "name(*args)" + """ + return f"{name}{'(' if args else ''}{','.join(args)}{')' if args else ''}" diff --git a/src/control_backend/agents/bdi/bdi_core_agent/rules.asl b/src/control_backend/agents/bdi/bdi_core_agent/rules.asl index 0001d3c..d88858d 100644 --- a/src/control_backend/agents/bdi/bdi_core_agent/rules.asl +++ b/src/control_backend/agents/bdi/bdi_core_agent/rules.asl @@ -1,3 +1,3 @@ -+new_message : user_said(Message) <- - -new_message; - .reply(Message). ++user_said(NewMessage) <- + -user_said(NewMessage); + .reply(NewMessage). diff --git a/src/control_backend/agents/perception/vad_agent.py b/src/control_backend/agents/perception/vad_agent.py index 37117c2..667d6db 100644 --- a/src/control_backend/agents/perception/vad_agent.py +++ b/src/control_backend/agents/perception/vad_agent.py @@ -67,7 +67,7 @@ class VADAgent(BaseAgent): self.model = None async def setup(self): - self.logger.info("Setting up %s", self.jid) + self.logger.info("Setting up %s", self.name) self._connect_audio_in_socket() @@ -99,7 +99,7 @@ class VADAgent(BaseAgent): transcriber = TranscriptionAgent(audio_out_address) await transcriber.start() - self.logger.info("Finished setting up %s", self.jid) + self.logger.info("Finished setting up %s", self.name) async def stop(self): """