Merge remote-tracking branch 'origin/dev' into feat/cb2ui-robot-connections

This commit is contained in:
Björn Otgaar
2025-10-23 16:48:04 +02:00
13 changed files with 487 additions and 9 deletions

View File

@@ -0,0 +1,35 @@
import logging
import agentspeak
from spade_bdi.bdi import BDIAgent
from control_backend.agents.bdi.behaviours.belief_setter import BeliefSetter
class BDICoreAgent(BDIAgent):
"""
This is the Brain agent that does the belief inference with AgentSpeak.
This is a continous process that happens automatically in the background.
This class contains all the actions that can be called from AgentSpeak plans.
It has the BeliefSetter behaviour.
"""
logger = logging.getLogger("BDI Core")
async def setup(self):
belief_setter = BeliefSetter()
self.add_behaviour(belief_setter)
def add_custom_actions(self, actions):
@actions.add(".reply", 1)
def _reply(agent, term, intention):
message = agentspeak.grounded(term.args[0], intention.scope)
self.logger.info(f"Replying to message: {message}")
reply = self._send_to_llm(message)
self.logger.info(f"Received reply: {reply}")
yield
def _send_to_llm(self, message) -> str:
"""TODO: implement"""
return f"This is a reply to {message}"

View File

@@ -0,0 +1,60 @@
import asyncio
import json
import logging
from spade.agent import Message
from spade.behaviour import CyclicBehaviour
from spade_bdi.bdi import BDIAgent
from control_backend.core.config import settings
class BeliefSetter(CyclicBehaviour):
"""
This is the behaviour that the BDI agent runs.
This behaviour waits for incoming message and processes it based on sender.
Currently, t only waits for messages containing beliefs from Belief Collector and adds these to its KB.
"""
agent: BDIAgent
logger = logging.getLogger("BDI/Belief Setter")
async def run(self):
msg = await self.receive(timeout=0.1)
if msg:
self.logger.info(f"Received message {msg.body}")
self._process_message(msg)
await asyncio.sleep(1)
def _process_message(self, message: Message):
sender = message.sender.node # removes host from jid and converts to str
self.logger.debug("Sender: %s", sender)
match sender:
case settings.agent_settings.belief_collector_agent_name:
self.logger.debug("Processing message from belief collector.")
self._process_belief_message(message)
case _:
pass
def _process_belief_message(self, message: Message):
if not message.body: return
match message.thread:
case "beliefs":
try:
beliefs: dict[str, list[list[str]]] = json.loads(message.body)
self._set_beliefs(beliefs)
except json.JSONDecodeError as e:
self.logger.error("Could not decode beliefs into JSON format: %s", e)
case _:
pass
def _set_beliefs(self, beliefs: dict[str, list[list[str]]]):
if self.agent.bdi is None:
self.logger.warning("Cannot set beliefs, since agent's BDI is not yet initialized.")
return
for belief, arguments_list in beliefs.items():
for arguments in arguments_list:
self.agent.bdi.set_belief(belief, *arguments)
self.logger.info("Set belief %s with arguments %s", belief, arguments)

View File

@@ -0,0 +1,3 @@
+user_said(Message) : not responded <-
+responded;
.reply(Message).

View File

@@ -1,19 +1,25 @@
# Standard library imports
import asyncio
import json
# External imports
import contextlib
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import logging
from spade.agent import Agent, Message
from spade.behaviour import OneShotBehaviour
import zmq
# Internal imports
from control_backend.agents.test_agent import TestAgent
from control_backend.agents.ri_communication_agent import RICommunicationAgent
from control_backend.agents.bdi.bdi_core import BDICoreAgent
from control_backend.api.v1.router import api_router
from control_backend.core.config import settings
from control_backend.core.config import AgentSettings, settings
from control_backend.core.zmq_context import context
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.DEBUG)
@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
@@ -32,10 +38,12 @@ async def lifespan(app: FastAPI):
address="tcp://*:5555", bind=True)
await ri_communication_agent.start()
bdi_core = BDICoreAgent(settings.agent_settings.bdi_core_agent_name + '@' + settings.agent_settings.host, settings.agent_settings.bdi_core_agent_name, "src/control_backend/agents/bdi/rules.asl")
await bdi_core.start()
yield
logger.info("%s shutting down.", app.title)
# if __name__ == "__main__":
app = FastAPI(title=settings.app_title, lifespan=lifespan)