feat(orchestrator): wire GoalDecomposer into bot_flow.py

Replace the old dual-path orchestrator (abstract goals vs legacy desires)
with unified GoalDecomposer-driven task routing:

1. GoalDecomposer reads mission.strategy + plugins config
2. Generates weighted Task objects (verb, target_screen, budget)
3. GrowthBrain.select_task() picks one probabilistically
4. Selected Task's target_screen routes through existing nav_graph
5. Feed loops + PluginRegistry handle the actual interactions

The abstract goals path (goal_executor.achieve('Nurture community'))
that caused infinite scrolling is now eliminated entirely.

Legacy desire fallback preserved for configs without plugins.

22/22 tests passing.
This commit is contained in:
2026-04-29 17:20:04 +02:00
parent b6846ab0fe
commit 0e43996ccd
3 changed files with 161 additions and 59 deletions

View File

@@ -445,56 +445,68 @@ def start_bot(**kwargs):
has_scanned_own_profile = True
while not dopamine.is_app_session_over():
# 1. Ask the Growth Brain for a Strategic Objective
success_rates = getattr(session_state, "successfulInteractions", {})
current_goal = growth_brain.get_current_goal(
dopamine, getattr(configs.args, "goals", []), success_rates=success_rates
# ── 1. Generate available tasks from mission + plugins ──
from GramAddict.core.goal_decomposer import GoalDecomposer
decomposer = GoalDecomposer(
plugins=configs.config.get("plugins", {}) if configs.config else {},
actions={
k: getattr(configs.args, k, None)
for k in ("feed", "explore", "reels")
if getattr(configs.args, k, None)
},
mission=configs.config.get("mission", {}) if configs.config else {},
)
available_tasks = decomposer.generate_tasks()
if current_goal == "ShiftContext":
logger.info("🧠 [Free Will] Boredom critical. Forcing app restart to clear context.")
device.app_stop(device.app_id)
random_sleep(2.0, 4.0)
device.app_start(device.app_id, use_monkey=True)
random_sleep(4.0, 6.0)
dopamine.boredom = max(0.0, dopamine.boredom * 0.2)
continue
if not available_tasks:
# No plugins enabled = nothing to do. Fall back to legacy desire system.
current_desire = growth_brain.get_current_desire(dopamine)
if current_desire == "ShiftContext":
logger.info("🧠 [Free Will] Boredom critical. Forcing app restart.")
device.app_stop(device.app_id)
random_sleep(2.0, 4.0)
device.app_start(device.app_id, use_monkey=True)
random_sleep(4.0, 6.0)
dopamine.boredom = max(0.0, dopamine.boredom * 0.2)
continue
# 2. Execution: GOAP Plan & Execute (Autonomous Mode)
if getattr(configs.args, "goals", None):
logger.info(f"🤖 Autonomous Mode Active. Delegating to GoalExecutor for: {current_goal}")
# Legacy desire → target mapping (kept for backward compatibility)
target_map = {
"DiscoverNewContent": ["ExploreFeed", "ReelsFeed"],
"NurtureCommunity": ["HomeFeed", "StoriesFeed"],
"SocialReciprocity": ["FollowingList"],
}
goal_executor = GoalExecutor(device=device, bot_username=getattr(configs.args, "username", ""))
dm_config = configs.get_plugin_config("dm_reply")
if dm_config.get("enabled", False):
target_map["SocialReciprocity"].append("MessageInbox")
result = goal_executor.achieve(current_goal)
import secrets
if result:
logger.info("✅ Goal achieved autonomously!")
else:
logger.warning(f"⚠️ Goal execution failed for: {current_goal}")
options = target_map.get(current_desire, ["HomeFeed"])
current_target = secrets.choice(options)
else:
# ── 2. Select a concrete Task ──
selected_task = growth_brain.select_task(dopamine, available_tasks)
continue # The GoalExecutor handles navigation internally
if selected_task is None:
# ShiftContext signal from high boredom
logger.info("🧠 [Free Will] Boredom critical. Forcing app restart to clear context.")
device.app_stop(device.app_id)
random_sleep(2.0, 4.0)
device.app_start(device.app_id, use_monkey=True)
random_sleep(4.0, 6.0)
dopamine.boredom = max(0.0, dopamine.boredom * 0.2)
continue
# --- LEGACY PROCEDURAL FALLBACK (For config without goals) ---
current_desire = current_goal
current_target = selected_task.target_screen
logger.info(
f"🎯 [GoalDecomposer] Task: {selected_task.intent} "
f"{current_target} (budget={selected_task.budget_posts})"
)
# 2. Map Desire to Sub-Feed
target_map = {
"DiscoverNewContent": ["ExploreFeed", "ReelsFeed"],
"NurtureCommunity": ["HomeFeed", "StoriesFeed"],
"SocialReciprocity": ["FollowingList"],
}
dm_config = configs.get_plugin_config("dm_reply")
if dm_config.get("enabled", False):
target_map["SocialReciprocity"].append("MessageInbox")
import secrets
options = target_map.get(current_desire, ["HomeFeed"])
current_target = secrets.choice(options)
logger.info(f"🧠 [Agent Orchestrator] Desire '{current_desire}' -> Routed to {current_target}")
logger.info(f"🧠 [Agent Orchestrator] Routed to {current_target}")
logger.info(f"⚡ Navigating to {current_target}")
success = nav_graph.navigate_to(current_target, zero_engine)