From 6359b11951d8fcf203bea85f732e9c60223373f6 Mon Sep 17 00:00:00 2001 From: liuwei Date: Tue, 28 Apr 2026 17:35:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=9E=E8=B0=83=20ai=5Fauto=5Fresponse=20?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E9=A2=91=E7=8E=87=E5=B9=B6=E6=94=BE=E5=AE=BD?= =?UTF-8?q?=E7=BE=A4=E9=97=AE=E5=8F=A5=E5=8F=82=E4=B8=8E=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 变更项: 1. 为非定向问句增加 allow_undirected_question 标记,允许在相关话题、心流升温或群接受度较高时进入模型。 2. 保留普通闲聊问句的保守过滤,避免 bot 重新回到见问就抢答的状态。 3. 将 question_requires_at 调整为 false,并下调 flow 与 cooldown 的参与门槛,恢复更自然的群聊回复频率。 --- plugins/ai_auto_response/config.toml | 46 ++++++++++++------- .../ai_auto_response/core/response_planner.py | 20 +++++++- plugins/ai_auto_response/core/triggers.py | 14 ++++++ 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/plugins/ai_auto_response/config.toml b/plugins/ai_auto_response/config.toml index 1b27e40..73be5a1 100644 --- a/plugins/ai_auto_response/config.toml +++ b/plugins/ai_auto_response/config.toml @@ -116,7 +116,11 @@ recent_followup_window_minutes = 5 [priority] at_bot = 1.0 explicit_question = 0.95 -question_requires_at = true +# 这里从“必须 @ 才答”放宽到“群问句可以有限参与”: +# 1. 非定向问句不再直接判死刑; +# 2. 但代码层仍会要求命中 topic、或当前心流升温、或群接受度较高时才放行; +# 3. 这样可以把频率拉回正常区间,同时避免回到早期“见问就抢答”。 +question_requires_at = false # 这里允许“点 bot 名字”作为弱定向信号,但不会把普通群问句当成 bot 提问。 # 只有出现这些名字/别名时,问句或社交召唤才会更像在对 bot 说话。 bot_name_keywords = ["小牛", "xiaoniu", "于谦", "谦哥", "林志玲", "志玲"] @@ -129,31 +133,39 @@ casual_topic = 0.35 [flow] enable_flow_state = true -flow_decay_per_minute = 14 -idle_threshold = 32 -warming_threshold = 54 -engaged_threshold = 86 +# 这里把心流门槛从“偏保守”调回“中等保守”: +# 1. 降低衰减速度,避免群里稍微安静一会儿就立刻掉回 idle; +# 2. 下调 warming / engaged 门槛,让 bot 更容易在连续讨论里接住后续话题; +# 3. 仍保留 over_reply 与 ignored_reply 惩罚,避免高频刷存在感。 +flow_decay_per_minute = 10 +idle_threshold = 24 +warming_threshold = 42 +engaged_threshold = 72 at_bot_boost = 40 question_boost = 30 -followup_boost = 16 -topic_boost = 7 -returning_member_boost = 5 -response_accepted_boost = 8 -ignored_reply_penalty = 22 -over_reply_penalty = 26 +followup_boost = 20 +topic_boost = 10 +returning_member_boost = 6 +response_accepted_boost = 10 +ignored_reply_penalty = 18 +over_reply_penalty = 18 night_penalty = 30 -max_bot_reply_streak = 1 +max_bot_reply_streak = 2 [cooldown] -group_reply_cooldown_sec = 110 -same_user_followup_cooldown_sec = 22 +# 这里把冷却从“强压频”回调到“正常拟人”: +# 1. 群级冷却缩短,避免 bot 说完一句后整段讨论都接不上; +# 2. 同人追问冷却同步缩短,方便连续答疑; +# 3. 定向 burst 限流仍在,只是把阈值调回不那么容易误伤的区间。 +group_reply_cooldown_sec = 60 +same_user_followup_cooldown_sec = 12 at_mention_min_interval_sec = 5 at_mention_burst_window_sec = 90 -at_mention_burst_limit = 4 +at_mention_burst_limit = 5 at_mention_silent_sec = 180 directed_burst_window_sec = 240 -directed_burst_limit = 4 -directed_burst_silent_sec = 480 +directed_burst_limit = 5 +directed_burst_silent_sec = 300 night_silent_hours = ["01:00-07:30"] [memory] diff --git a/plugins/ai_auto_response/core/response_planner.py b/plugins/ai_auto_response/core/response_planner.py index 46ca1b4..e8869eb 100644 --- a/plugins/ai_auto_response/core/response_planner.py +++ b/plugins/ai_auto_response/core/response_planner.py @@ -39,6 +39,7 @@ class ResponsePlanner: trigger_type = str(trigger.get("trigger_type", "") or "") directed = bool(trigger.get("is_directed")) question_detected = bool(trigger.get("question_detected")) + allow_undirected_question = bool(trigger.get("allow_undirected_question")) if trigger.get("is_at") or trigger_type == "at_trigger": return True if trigger.get("is_directed_abuse") and directed: @@ -51,13 +52,28 @@ class ResponsePlanner: # 1. 群里的普通问句,哪怕命中了 topic,也不应该因为“当前气氛热”就被 bot 主动接住; # 2. 只要它有明显问句形态,但又没有明确指向 bot,就整体禁止进入模型, # 从根上阻断“别人互相问一句,bot 突然抢答”的尴尬感。 - if question_detected and not directed and not trigger.get("is_followup"): + # 这里额外给 allow_undirected_question 开一个窄口: + # 1. 仅当上游明确配置允许“群问句适度参与”时,才不在这里一刀切拦截; + # 2. 但是否真的放行,还要继续走下面针对 is_question 的更细条件; + # 3. 这样能把“完全不说话”和“见问就答”之间留出一个可调的中间带。 + if question_detected and not directed and not trigger.get("is_followup") and not allow_undirected_question: return False if trigger.get("is_question"): # 策略收敛: # 问答类回复只在“明确指向机器人”时触发,防止把群友之间的疑问句当作对机器人提问。 # 这层作为兜底,即使上游触发器未来被调整,也不会回到“疑问句高频抢答”的状态。 - return directed + if directed: + return True + # 对“非定向问句”的放行继续保持保守,只在以下场景参与: + # 1. 明显命中了当前产品关心的话题/技术域; + # 2. 或群心流已经 warming/engaged,说明 bot 最近参与没有被明显冷处理; + # 3. 或 acceptance_state 已经 warm,说明群里对 bot 的接话接受度较高。 + # 同时保留上面的 has_recent_human_solver 限制,避免大家已经在答了 bot 还硬插话。 + return allow_undirected_question and ( + bool(trigger.get("topic")) + or flow_state in {"warming", "engaged", "deep_engaged"} + or acceptance_state == "warm" + ) if trigger.get("is_followup"): if directed: return True diff --git a/plugins/ai_auto_response/core/triggers.py b/plugins/ai_auto_response/core/triggers.py index 6845a64..e5f4c1b 100644 --- a/plugins/ai_auto_response/core/triggers.py +++ b/plugins/ai_auto_response/core/triggers.py @@ -39,6 +39,13 @@ class TriggerResult: # 以便阻止 topic/主动接话路径误把它当成 bot 可抢答的机会。 question_detected: bool = False is_directed: bool = False + # 这个标记用来表达“虽然当前问句没有明确 @bot / 点 bot 名字, + # 但产品策略允许它在合适场景下进入模型”。 + # 之所以不直接等同于 should_respond,是因为最终是否放行还要结合: + # 1. 当前群心流状态是不是已经热起来; + # 2. 这条消息是不是命中了技术/主题焦点; + # 3. 最近是不是已经有真人在接这个问题。 + allow_undirected_question: bool = False # 这个标记表示“当前消息不是普通情绪话,而是在对 bot 进行定向挑衅/辱骂”。 # 它的作用不是本地写死回复,而是让后续规划层优先走短句回怼场景。 is_directed_abuse: bool = False @@ -103,6 +110,13 @@ class TriggerRouter: result.trigger_type = "question_trigger" result.priority = float(self.config.get("explicit_question", 0.95)) result.is_question = True + # 当 question_requires_at=false 时,这里允许“非定向问句”进入后续规划层, + # 但不直接把它升级成 is_directed,避免 planner 误以为这是明确对 bot 发问。 + # 后续是否真的回复,会由 response_planner 再结合 topic / flow / solver 信号二次筛选。 + if not result.is_directed and not named_to_bot and not message.get("is_at"): + result.allow_undirected_question = not self.question_requires_at + if result.allow_undirected_question: + result.reasons.append("question_open_to_group") if named_to_bot and not result.is_directed: result.is_directed = True result.reasons.append("question_named_bot")