diff --git a/src/control_backend/agents/bdi/bdi_core.py b/src/control_backend/agents/bdi/bdi_core.py index 0be42c9..ecc0b0c 100644 --- a/src/control_backend/agents/bdi/bdi_core.py +++ b/src/control_backend/agents/bdi/bdi_core.py @@ -30,7 +30,7 @@ class BDICoreAgent(BDIAgent): self.add_behaviour(BeliefSetter()) self.add_behaviour(ReceiveLLMResponseBehaviour()) - await self._send_to_llm("Hello we are the Pepper plus team") + await self._send_to_llm("Hi pepper, how are you?") # This is the example message currently sent to the llm at the start of the Program self.logger.info("BDICoreAgent setup complete") @@ -52,7 +52,7 @@ class BDICoreAgent(BDIAgent): self._send_to_llm(message_text) yield - async def _send_to_llm(self, text: str) -> str: + async def _send_to_llm(self, text: str): """ Sends a text query to the LLM Agent asynchronously. """ @@ -61,12 +61,10 @@ class BDICoreAgent(BDIAgent): async def run(self) -> None: msg = Message( to= settings.agent_settings.llm_agent_name + '@' + settings.agent_settings.host, - body= text, - thread= "llm_request", + body= text ) await self.send(msg) self.agent.logger.debug("Message sent to LLM: %s", text) - self.add_behaviour(SendBehaviour()) - return "LLM message dispatch scheduled" \ No newline at end of file + self.add_behaviour(SendBehaviour()) \ No newline at end of file diff --git a/src/control_backend/agents/llm/llm.py b/src/control_backend/agents/llm/llm.py index 38914a1..8dabb0f 100644 --- a/src/control_backend/agents/llm/llm.py +++ b/src/control_backend/agents/llm/llm.py @@ -15,6 +15,7 @@ from spade.message import Message from spade.template import Template from control_backend.core.config import settings +from control_backend.agents.llm.llm_instructions import LLMInstructions class LLMAgent(Agent): @@ -32,7 +33,7 @@ class LLMAgent(Agent): the BDI Core Agent and handle them. """ - async def run(self) -> None: + async def run(self): """ Receives SPADE messages and processes only those originating from the configured BDI agent. @@ -54,7 +55,7 @@ class LLMAgent(Agent): else: self.agent.logger.debug("Message ignored (not from BDI Core Agent)") - async def _process_bdi_message(self, message: Message) -> None: + async def _process_bdi_message(self, message: Message): """ Forwards user text to the LLM and replies with the generated text. """ @@ -62,14 +63,13 @@ class LLMAgent(Agent): llm_response = await self._query_llm(user_text) await self._reply(llm_response) - async def _reply(self, msg: str) -> None: + async def _reply(self, msg: str): """ Sends a response message back to the BDI Core Agent. """ reply = Message( to= settings.agent_settings.bdi_core_agent_name + '@' + settings.agent_settings.host, - body= msg, - thread= "llm_response", + body= msg ) await self.send(reply) self.agent.logger.info("Reply sent to BDI Core Agent") @@ -82,15 +82,30 @@ class LLMAgent(Agent): :return: LLM-generated content or fallback message. """ async with httpx.AsyncClient(timeout=120.0) as client: + # Example dynamic content for future (optional) + + instructions = LLMInstructions() + developer_instruction = instructions.build_developer_instruction() + response = await client.post( settings.llm_settings.local_llm_url, headers={"Content-Type": "application/json"}, json={ "model": settings.llm_settings.local_llm_model, - "messages": [{"role": "user", "content": prompt}], - "temperature": 0.3, + "messages": [ + { + "role": "developer", + "content": developer_instruction + }, + { + "role": "user", + "content": prompt + } + ], + "temperature": 0.3 }, ) + try: response.raise_for_status() data: dict[str, Any] = response.json() @@ -104,15 +119,12 @@ class LLMAgent(Agent): self.agent.logger.error("Unexpected error: %s", err) return "Error processing the request." - async def setup(self) -> None: + async def setup(self): """ Sets up the SPADE behaviour to filter and process messages from the BDI Core Agent. """ self.logger.info("LLMAgent setup complete") - template = Template() - template.sender = settings.agent_settings.bdi_core_agent_name + '@' + settings.agent_settings.host - behaviour = self.ReceiveMessageBehaviour() - self.add_behaviour(behaviour, template) + self.add_behaviour(behaviour) diff --git a/src/control_backend/agents/llm/llm_instructions.py b/src/control_backend/agents/llm/llm_instructions.py new file mode 100644 index 0000000..a35101d --- /dev/null +++ b/src/control_backend/agents/llm/llm_instructions.py @@ -0,0 +1,44 @@ +class LLMInstructions: + """ + Defines structured instructions that are sent along with each request + to the LLM to guide its behavior (norms, goals, etc.). + """ + + @staticmethod + def default_norms() -> str: + return f""" + Be friendly and respectful. + Make the conversation feel natural and engaging. + """.strip() + + @staticmethod + def default_goals() -> str: + return f""" + Try to learn the user's name during conversation. + """.strip() + + def __init__(self, norms: str | None = None, goals: str | None = None): + self.norms = norms if norms is not None else self.default_norms() + self.goals = goals if goals is not None else self.default_goals() + + def build_developer_instruction(self) -> str: + """ + Builds a multi-line formatted instruction string for the LLM. + Includes only non-empty structured fields. + """ + sections = [ + "You are a Pepper robot engaging in natural human conversation.", + "Keep responses between 1–5 sentences, unless instructed otherwise.\n", + ] + + if self.norms: + sections.append("Norms to follow:") + sections.append(self.norms) + sections.append("") + + if self.goals: + sections.append("Goals to reach:") + sections.append(self.goals) + sections.append("") + + return "\n".join(sections).strip() diff --git a/src/control_backend/main.py b/src/control_backend/main.py index de357d8..0050efa 100644 --- a/src/control_backend/main.py +++ b/src/control_backend/main.py @@ -44,10 +44,10 @@ async def lifespan(app: FastAPI): llm_agent = LLMAgent(settings.agent_settings.llm_agent_name + '@' + settings.agent_settings.host, - "secret, ask twirre") + settings.agent_settings.llm_agent_name) await llm_agent.start() bdi_core = BDICoreAgent(settings.agent_settings.bdi_core_agent_name + '@' + settings.agent_settings.host, - "secret, ask twirre", "src/control_backend/agents/bdi/rules.asl") + settings.agent_settings.bdi_core_agent_name, "src/control_backend/agents/bdi/rules.asl") await bdi_core.start() _temp_vad_agent = VADAgent("tcp://localhost:5558", False)