fix(intent_resolver): humanize content-desc for VLM disambiguation (followers vs following)

- Add _humanize_desc() regex to split '991following' → '991 following'
- Add explicit followers/following disambiguation rule to VLM prompt
- E2E test suite: 6 tests proving HD Map avoidance, SpatialParser extraction,
  VLM prompt preparation, and LIVE LLM disambiguation
- Root cause: VLM confused Instagram's concatenated content-desc values
This commit is contained in:
2026-04-27 15:41:29 +02:00
parent 888136f733
commit 36a8683643
2 changed files with 304 additions and 29 deletions

View File

@@ -89,11 +89,22 @@ class IntentResolver:
model = getattr(cfg.args, "ai_telepathic_model", "qwen3.5:latest")
url = getattr(cfg.args, "ai_telepathic_url", "http://localhost:11434/api/generate")
# Prepare context
# Prepare context — humanize concatenated content-desc for VLM clarity
# Instagram concatenates values like "991following" or "140Kfollowers".
# We insert spaces at number→letter boundaries so the VLM can distinguish them.
import re as _re
def _humanize_desc(raw: str) -> str:
if not raw:
return ""
# "991following" → "991 following", "140Kfollowers" → "140K followers"
# Matches digit (with optional K/M/B suffix) directly followed by a lowercase word
return _re.sub(r"(\d[KMBkmb]?)([a-z])", r"\1 \2", raw)
node_context = []
for i, node in enumerate(filtered_candidates):
text = node.text or ""
desc = node.content_desc or ""
desc = _humanize_desc(node.content_desc or "")
res_id = node.resource_id or ""
node_context.append(f"[{i}] text='{text}', desc='{desc}', id='{res_id}', bounds=[{node.y1},{node.y2}]")
@@ -104,6 +115,7 @@ class IntentResolver:
f"- If the intent is about opening the 'post author', STRICTLY require 'row_feed_photo_profile' in the ID. Do not select comment authors.\n"
f"- If the intent is about opening a user profile generally, prioritize nodes containing 'profile_name' or 'profile_image' in their ID, NOT generic action bars or tabs.\n"
f"- Ignore bottom navigation tabs (home, search, profile) UNLESS the intent explicitly asks to navigate to a primary feed.\n"
f"- CRITICAL: 'followers' and 'following' are DIFFERENT concepts. 'followers' = people who follow you. 'following' = people you follow. Read the desc and id fields CAREFULLY to select the correct one.\n"
f"Candidates:\n" + "\n".join(node_context) + "\n\n"
"Reply ONLY with a valid JSON object strictly matching this schema:\n"
'{"selected_index": <integer or null>}\n'