补充 ai_auto_response 的完整模型决策日志
变更项: 1. 新增 LLM_RESULT 日志,记录模型输出的 should_reply、reply_mode、topic、reply 预览和原始响应预览。 2. 新增 BLOCKED_REPLY 日志,记录模型原本想回复但被 post_llm_cooldown、过期、覆盖或重复回复拦下的具体原因。 3. 保留原有 SKIP 与 SENT 日志,使模型判定、发送阻断和最终发出三段链路可以串起来排查。
This commit is contained in:
@@ -608,6 +608,21 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
fallback_reply_mode=reply_mode,
|
||||
fallback_topic=trigger.topic or "",
|
||||
)
|
||||
# 这里补一条完整的模型决策日志:
|
||||
# 1. 无论模型最终决定回还是不回,都先把 should_reply / reply_mode / topic / reply 预览记下来;
|
||||
# 2. 同时保留 sanitize 后的原始响应预览,方便排查“模型其实回了什么 JSON / 文本”;
|
||||
# 3. 这样后面即使被 cooldown、过期、去重拦住,也能明确看到“模型本来想怎么做”。
|
||||
self._log_event(
|
||||
"llm_result",
|
||||
room_id=room_id,
|
||||
sender=sender,
|
||||
trigger_type=trigger.trigger_type,
|
||||
should_reply=bool(llm_result.get("should_reply", True)),
|
||||
reply_mode=str(llm_result.get("reply_mode", reply_mode) or reply_mode),
|
||||
topic=str(llm_result.get("topic_summary", "") or llm_result.get("topic_id", "") or trigger.topic or ""),
|
||||
response_preview=preview_text(str(llm_result.get("reply", "") or ""), 120),
|
||||
raw_response_preview=preview_text(response, 160),
|
||||
)
|
||||
if not llm_result.get("should_reply", True):
|
||||
self._log_event(
|
||||
"skip",
|
||||
@@ -639,6 +654,16 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
# 2. 模型先统一判断 should_reply,只有当它明确想回时,才进入频率控制;
|
||||
# 3. 仍然保留冷却,是为了守住群内刷屏风险,但职责已经变成“限制发送”,不是“替模型做语义裁决”。
|
||||
if not self.cooldown.pass_cooldown(room_id, sender, trigger.__dict__):
|
||||
self._log_event(
|
||||
"blocked_reply",
|
||||
room_id=room_id,
|
||||
sender=sender,
|
||||
reason=f"post_llm_{trigger.__dict__.get('_cooldown_reason', 'cooldown')}",
|
||||
trigger_type=trigger.trigger_type,
|
||||
reply_mode=reply_mode,
|
||||
topic=selected_topic,
|
||||
response_preview=preview_text(reply_text, 120),
|
||||
)
|
||||
self._log_event(
|
||||
"skip",
|
||||
room_id=room_id,
|
||||
@@ -657,6 +682,17 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
# 2. 即使消息进模型时还新鲜,等模型回完也可能已经跟不上群聊了;
|
||||
# 3. 这种情况下直接放弃发送,比突然补回旧话更自然。
|
||||
if self._is_message_stale(message):
|
||||
self._log_event(
|
||||
"blocked_reply",
|
||||
room_id=room_id,
|
||||
sender=sender,
|
||||
reason="stale_before_send",
|
||||
trigger_type=trigger.trigger_type,
|
||||
reply_mode=reply_mode,
|
||||
topic=selected_topic,
|
||||
response_preview=preview_text(final_response_text, 120),
|
||||
age_sec=round(self._get_message_queue_age_sec(message), 2),
|
||||
)
|
||||
self._log_event(
|
||||
"skip",
|
||||
room_id=room_id,
|
||||
@@ -673,6 +709,16 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
# 2. 这时即使模型产出了结果,也不应该再把旧回复补发出去;
|
||||
# 3. 直接丢弃旧结果,让群里只看到贴着最新现场的回复。
|
||||
if self._is_message_superseded(message):
|
||||
self._log_event(
|
||||
"blocked_reply",
|
||||
room_id=room_id,
|
||||
sender=sender,
|
||||
reason="superseded_before_send",
|
||||
trigger_type=trigger.trigger_type,
|
||||
reply_mode=reply_mode,
|
||||
topic=selected_topic,
|
||||
response_preview=preview_text(final_response_text, 120),
|
||||
)
|
||||
self._log_event(
|
||||
"skip",
|
||||
room_id=room_id,
|
||||
@@ -690,6 +736,16 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
reply_text=final_response_text,
|
||||
expiry_sec=reply_dedup_expiry,
|
||||
):
|
||||
self._log_event(
|
||||
"blocked_reply",
|
||||
room_id=room_id,
|
||||
sender=sender,
|
||||
reason="duplicate_reply",
|
||||
trigger_type=trigger.trigger_type,
|
||||
reply_mode=reply_mode,
|
||||
topic=selected_topic,
|
||||
response_preview=preview_text(final_response_text, 120),
|
||||
)
|
||||
self._log_event(
|
||||
"skip",
|
||||
room_id=room_id,
|
||||
|
||||
@@ -76,6 +76,31 @@ def build_log_summary(event: str, data: Dict[str, Any]) -> str:
|
||||
f"err={data.get('last_error', '')}"
|
||||
).strip()
|
||||
|
||||
if event == "llm_result":
|
||||
return (
|
||||
f"[XIAONIU] LLM_RESULT room={room} user={sender} "
|
||||
f"trigger={data.get('trigger_type', 'none')} "
|
||||
f"want={yn(data.get('should_reply'))} "
|
||||
f"mode={data.get('reply_mode', '')} "
|
||||
f"topic={data.get('topic', '-') or '-'} "
|
||||
f"reply={data.get('response_preview', '')} "
|
||||
f"raw={data.get('raw_response_preview', '')}"
|
||||
).strip()
|
||||
|
||||
if event == "blocked_reply":
|
||||
age_text = ""
|
||||
if data.get("age_sec") not in (None, ""):
|
||||
age_text = f" age={data.get('age_sec')}"
|
||||
return (
|
||||
f"[XIAONIU] BLOCKED_REPLY room={room} user={sender} "
|
||||
f"reason={data.get('reason', '')} "
|
||||
f"trigger={data.get('trigger_type', 'none')} "
|
||||
f"mode={data.get('reply_mode', '')} "
|
||||
f"topic={data.get('topic', '-') or '-'} "
|
||||
f"reply={data.get('response_preview', '')}"
|
||||
f"{age_text}"
|
||||
).strip()
|
||||
|
||||
if event == "sent":
|
||||
return (
|
||||
f"[XIAONIU] SENT room={room} user={sender_name}/{sender} "
|
||||
|
||||
Reference in New Issue
Block a user