feat: semantically determine goal completion

ref: N25B-432
This commit is contained in:
Twirre Meulenbelt
2026-01-07 17:08:23 +01:00
parent 3189b9fee3
commit aa5b386f65
8 changed files with 380 additions and 163 deletions

View File

@@ -7,9 +7,9 @@ from zmq.asyncio import Context
from control_backend.agents import BaseAgent
from control_backend.agents.bdi.agentspeak_generator import AgentSpeakGenerator
from control_backend.core.config import settings
from control_backend.schemas.belief_list import BeliefList
from control_backend.schemas.belief_list import BeliefList, GoalList
from control_backend.schemas.internal_message import InternalMessage
from control_backend.schemas.program import Belief, ConditionalNorm, InferredBelief, Program
from control_backend.schemas.program import Belief, ConditionalNorm, Goal, InferredBelief, Program
class BDIProgramManager(BaseAgent):
@@ -63,24 +63,23 @@ class BDIProgramManager(BaseAgent):
def _extract_beliefs_from_program(program: Program) -> list[Belief]:
beliefs: list[Belief] = []
def extract_beliefs_from_belief(belief: Belief) -> list[Belief]:
if isinstance(belief, InferredBelief):
return extract_beliefs_from_belief(belief.left) + extract_beliefs_from_belief(
belief.right
)
return [belief]
for phase in program.phases:
for norm in phase.norms:
if isinstance(norm, ConditionalNorm):
beliefs += BDIProgramManager._extract_beliefs_from_belief(norm.condition)
beliefs += extract_beliefs_from_belief(norm.condition)
for trigger in phase.triggers:
beliefs += BDIProgramManager._extract_beliefs_from_belief(trigger.condition)
beliefs += extract_beliefs_from_belief(trigger.condition)
return beliefs
@staticmethod
def _extract_beliefs_from_belief(belief: Belief) -> list[Belief]:
if isinstance(belief, InferredBelief):
return BDIProgramManager._extract_beliefs_from_belief(
belief.left
) + BDIProgramManager._extract_beliefs_from_belief(belief.right)
return [belief]
async def _send_beliefs_to_semantic_belief_extractor(self, program: Program):
"""
Extract beliefs from the program and send them to the Semantic Belief Extractor Agent.
@@ -98,6 +97,46 @@ class BDIProgramManager(BaseAgent):
await self.send(message)
@staticmethod
def _extract_goals_from_program(program: Program) -> list[Goal]:
"""
Extract all goals from the program, including subgoals.
:param program: The program received from the API.
:return: A list of Goal objects.
"""
goals: list[Goal] = []
def extract_goals_from_goal(goal_: Goal) -> list[Goal]:
goals_: list[Goal] = [goal]
for plan in goal_.plan:
if isinstance(plan, Goal):
goals_.extend(extract_goals_from_goal(plan))
return goals_
for phase in program.phases:
for goal in phase.goals:
goals.extend(extract_goals_from_goal(goal))
return goals
async def _send_goals_to_semantic_belief_extractor(self, program: Program):
"""
Extract goals from the program and send them to the Semantic Belief Extractor Agent.
:param program: The program received from the API.
"""
goals = GoalList(goals=self._extract_goals_from_program(program))
message = InternalMessage(
to=settings.agent_settings.text_belief_extractor_name,
sender=self.name,
body=goals.model_dump_json(),
thread="goals",
)
await self.send(message)
async def _receive_programs(self):
"""
Continuous loop that receives program updates from the HTTP endpoint.
@@ -117,6 +156,7 @@ class BDIProgramManager(BaseAgent):
await asyncio.gather(
self._create_agentspeak_and_send_to_bdi(program),
self._send_beliefs_to_semantic_belief_extractor(program),
self._send_goals_to_semantic_belief_extractor(program),
)
async def setup(self):