diff --git a/plugins/ai_auto_response/core/reply_formatter.py b/plugins/ai_auto_response/core/reply_formatter.py index 059c2dd..79eac1c 100644 --- a/plugins/ai_auto_response/core/reply_formatter.py +++ b/plugins/ai_auto_response/core/reply_formatter.py @@ -12,7 +12,7 @@ def finalize_reply(response: str, reply_mode: str) -> List[str]: text = text.replace("\n", " ").strip() if reply_mode == "social_short": - return [take_first_sentence(text, 12).strip()] + return [take_first_sentence(text, 18).strip()] if reply_mode == "qa_fast": return split_reply_chunks(text, sentence_limit=2, char_limit=28, chunk_limit=2) if reply_mode == "qa_with_context": @@ -76,12 +76,18 @@ def smart_clip(text: str, limit: int) -> str: if len(text) <= limit: return text window = text[:limit] - punctuation = ",,、;;::。!?!?))】]」』 " - split_at = -1 - for idx in range(len(window) - 1, max(len(window) - 10, 0) - 1, -1): - if window[idx] in punctuation: - split_at = idx - break + strong_punctuation = "。!?!?))】]」』 " + weak_punctuation = ",,、;;::" + split_at = _find_split_at(window, strong_punctuation) + if split_at < 0: + split_at = _find_split_at(window, weak_punctuation, lookback=4) if split_at >= 0: return window[:split_at].rstrip(",,、;;::。!?!? ").strip() return window.rstrip(",,、;;:: ").strip() + + +def _find_split_at(window: str, punctuation: str, lookback: int = 10) -> int: + for idx in range(len(window) - 1, max(len(window) - lookback, 0) - 1, -1): + if window[idx] in punctuation: + return idx + return -1 diff --git a/plugins/ai_auto_response/core/response_planner.py b/plugins/ai_auto_response/core/response_planner.py index 5b965f1..8225888 100644 --- a/plugins/ai_auto_response/core/response_planner.py +++ b/plugins/ai_auto_response/core/response_planner.py @@ -8,7 +8,9 @@ class ResponsePlanner: if trigger.get("is_question"): return "qa_with_context" if flow_state in {"engaged", "deep_engaged"} else "qa_fast" if trigger.get("is_followup"): - return "qa_with_context" + if trigger.get("is_directed"): + return "qa_with_context" if flow_state in {"engaged", "deep_engaged"} else "qa_fast" + return "social_short" if trigger.get("is_social_call"): return "social_short" if trigger.get("is_returning_member"): @@ -35,7 +37,13 @@ class ResponsePlanner: if trigger.get("is_question"): return directed or trigger.get("priority", 0) >= 0.9 or flow_state in {"warming", "engaged", "deep_engaged"} if trigger.get("is_followup"): - return directed or flow_state in {"warming", "engaged", "deep_engaged"} or acceptance_state == "warm" + if directed: + return True + return ( + flow_state in {"engaged", "deep_engaged"} + and acceptance_state == "warm" + and bool(trigger.get("topic")) + ) if trigger.get("is_social_call"): if acceptance_state == "cold": return False diff --git a/plugins/ai_auto_response/core/triggers.py b/plugins/ai_auto_response/core/triggers.py index 82cbdcd..14d95c9 100644 --- a/plugins/ai_auto_response/core/triggers.py +++ b/plugins/ai_auto_response/core/triggers.py @@ -57,9 +57,11 @@ class TriggerRouter: result.trigger_type = "followup_trigger" result.priority = float(self.config.get("followup", 0.90)) result.is_followup = True - result.should_respond = True - result.is_directed = True result.reasons.append("followup") + if conversation_hints.get("previous_same_sender_directed") or conversation_hints.get("quote_targets_bot"): + result.should_respond = True + result.is_directed = True + result.reasons.append("followup_directed") topic = self._detect_topic(content_lower) if topic: result.topic = topic