回调 ai_auto_response 触发频率并放宽群问句参与策略

变更项:
1. 为非定向问句增加 allow_undirected_question 标记,允许在相关话题、心流升温或群接受度较高时进入模型。
2. 保留普通闲聊问句的保守过滤,避免 bot 重新回到见问就抢答的状态。
3. 将 question_requires_at 调整为 false,并下调 flow 与 cooldown 的参与门槛,恢复更自然的群聊回复频率。
This commit is contained in:
liuwei
2026-04-28 17:35:49 +08:00
parent da5cf004f6
commit 6359b11951
3 changed files with 61 additions and 19 deletions

View File

@@ -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]

View File

@@ -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

View File

@@ -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")