refactor(perception): replace XML length heuristic with VLM screenshot verification
This commit is contained in:
@@ -381,7 +381,7 @@ class GoalExecutor:
|
||||
else:
|
||||
# For interactions (like, follow) or unknown goals, use XML delta + semantic verify
|
||||
if ui_changed:
|
||||
verification = engine.verify_success(action, post_xml)
|
||||
verification = engine.verify_success(action, post_xml, device=self.device)
|
||||
if verification is True:
|
||||
action_success = True
|
||||
logger.info(f"✅ [GOAP Step] Interaction '{action}' successful.")
|
||||
|
||||
@@ -77,9 +77,9 @@ class ActionMemory:
|
||||
|
||||
self._last_click_context = None
|
||||
|
||||
def verify_success(self, intent: str, pre_click_xml: str, post_click_xml: str) -> Optional[bool]:
|
||||
def verify_success(self, intent: str, pre_click_xml: str, post_click_xml: str, device=None) -> Optional[bool]:
|
||||
"""
|
||||
Structural verification: Did the UI actually change after the click?
|
||||
Structural and Visual verification: Did the UI actually change after the click?
|
||||
"""
|
||||
# Specific check for explore grid
|
||||
if "first image in explore grid" in intent or "grid item" in intent:
|
||||
@@ -88,17 +88,48 @@ class ActionMemory:
|
||||
if "explore_action_bar" in post_click_xml and "row_feed_button_like" not in post_click_xml:
|
||||
return None # Still on grid, inconclusive
|
||||
|
||||
# State toggles (like, save, follow) rarely change XML length by > 50 chars
|
||||
# State toggles (like, save, follow) rarely change XML length predictably
|
||||
state_toggles = ["like", "save", "follow", "heart"]
|
||||
if any(t in intent.lower() for t in state_toggles):
|
||||
if device:
|
||||
logger.info(f"👁️ [ActionMemory] Handing over verification for '{intent}' to VLM visual analysis...")
|
||||
from GramAddict.core.perception.semantic_evaluator import SemanticEvaluator
|
||||
evaluator = SemanticEvaluator()
|
||||
|
||||
# Ask VLM to be the absolute source of truth
|
||||
prompt = (
|
||||
f"The user just attempted to perform the action: '{intent}'. "
|
||||
f"Look at the current screen carefully. Was the action successful? "
|
||||
f"If the intent was 'follow', does the button now indicate 'Following' or 'Requested'? "
|
||||
f"If it was 'like', is the heart icon clearly active/red? "
|
||||
f"If the screen shifted completely to a profile when you just wanted to like/follow from a feed, it FAILED. "
|
||||
f"Answer ONLY with the word YES or NO."
|
||||
)
|
||||
|
||||
try:
|
||||
screenshot = device.screenshot_b64()
|
||||
response = evaluator._query_vlm(prompt, screenshot)
|
||||
|
||||
if response and "yes" in response.lower() and "no" not in response.lower():
|
||||
logger.debug(f"🧠 [ActionMemory] VLM visually confirmed success for '{intent}'.")
|
||||
return True
|
||||
else:
|
||||
logger.warning(f"⚠️ [ActionMemory] VLM visual verification FAILED for '{intent}'. VLM replied: '{response}'")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to query VLM for visual verification: {e}")
|
||||
# Fallthrough to structural delta if VLM crashes
|
||||
|
||||
# Fallback to structural delta if no device or VLM fails
|
||||
diff = abs(len(pre_click_xml) - len(post_click_xml))
|
||||
if diff > 1000:
|
||||
logger.warning(f"⚠️ [ActionMemory] Massive structural shift ({diff} chars) for state-toggle '{intent}'. Navigated away by mistake? Verification FAIL.")
|
||||
return False
|
||||
if diff > 0:
|
||||
logger.debug(f"🧠 [ActionMemory] State toggle detected for '{intent}'. Verification PASS.")
|
||||
logger.debug(f"🧠 [ActionMemory] Structural delta detected for '{intent}'. Verification PASS.")
|
||||
return True
|
||||
|
||||
|
||||
if abs(len(pre_click_xml) - len(post_click_xml)) > 50:
|
||||
logger.debug(f"🧠 [ActionMemory] Structural change detected for '{intent}'. Verification PASS.")
|
||||
return True
|
||||
|
||||
@@ -180,11 +180,11 @@ class TelepathicEngine:
|
||||
def decay_click(self, intent: str = None):
|
||||
self._memory.reject_click(intent) # Alias to reject
|
||||
|
||||
def verify_success(self, intent: str, post_click_xml: str) -> bool:
|
||||
def verify_success(self, intent: str, post_click_xml: str, device=None) -> bool:
|
||||
pre_click_xml = ""
|
||||
if self._memory._last_click_context:
|
||||
pre_click_xml = self._memory._last_click_context.get("xml_context", "")
|
||||
return self._memory.verify_success(intent, pre_click_xml, post_click_xml)
|
||||
return self._memory.verify_success(intent, pre_click_xml, post_click_xml, device=device)
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Semantic Evaluator Delegation
|
||||
|
||||
Reference in New Issue
Block a user