Merge remote-tracking branch 'origin/dev' into feat/cb2ui-robot-connections
This commit is contained in:
0
src/control_backend/agents/bdi/__init__.py
Normal file
0
src/control_backend/agents/bdi/__init__.py
Normal file
35
src/control_backend/agents/bdi/bdi_core.py
Normal file
35
src/control_backend/agents/bdi/bdi_core.py
Normal 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}"
|
||||
|
||||
|
||||
60
src/control_backend/agents/bdi/behaviours/belief_setter.py
Normal file
60
src/control_backend/agents/bdi/behaviours/belief_setter.py
Normal 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)
|
||||
3
src/control_backend/agents/bdi/rules.asl
Normal file
3
src/control_backend/agents/bdi/rules.asl
Normal file
@@ -0,0 +1,3 @@
|
||||
+user_said(Message) : not responded <-
|
||||
+responded;
|
||||
.reply(Message).
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user