From 2c09fcf9e60dd4d0f7f9d861b879983d462753ed Mon Sep 17 00:00:00 2001 From: jimi <1847930177@qq.com> Date: Mon, 2 Mar 2026 18:59:09 +0800 Subject: [PATCH] fix: prevent outbound echo loops and reduce AgentScope warning noise --- qingjian_cs/app/client.py | 28 ++++++++++++++++++++++++++++ qingjian_cs/app/logger.py | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/qingjian_cs/app/client.py b/qingjian_cs/app/client.py index bd13ade..3cf5d8f 100644 --- a/qingjian_cs/app/client.py +++ b/qingjian_cs/app/client.py @@ -27,6 +27,7 @@ class QingjianClient: self.pending_images: dict[str, list[str]] = defaultdict(list) self.auto_quote_tasks: dict[str, asyncio.Task] = {} self.last_reply_key: dict[str, str] = {} + self.recent_outbound: dict[str, tuple[str, float]] = {} @staticmethod def _customer_key(data: dict) -> str: @@ -53,6 +54,9 @@ class QingjianClient: self.logger.info("[发送] %s", message.get("msg", "")) async def send_reply(self, data: dict, text: str, trace_id: str = "-") -> None: + text = str(text or "").strip() + if not text: + return msg = { "msg_id": "", "acc_id": data.get("acc_id", ""), @@ -66,8 +70,23 @@ class QingjianClient: } activity_event(self.logger, "send_reply_attempt", trace_id=trace_id, customer_id=data.get("from_id", "-"), msg=text) await self.send_message(msg) + self.recent_outbound[self._customer_key(data)] = (text, time.monotonic()) activity_event(self.logger, "send_reply_success", trace_id=trace_id, customer_id=data.get("from_id", "-"), msg=text) + def _is_outbound_echo(self, data: dict, msg: str) -> bool: + """ + 轻简可能会把我方刚发送文本回推为“收到消息”。 + 对同 customer_key 的“短时间完全相同文本”做回环拦截,避免无限对话。 + """ + key = self._customer_key(data) + last = self.recent_outbound.get(key) + if not last: + return False + last_msg, ts = last + if (time.monotonic() - ts) > 120: + return False + return str(msg or "").strip() == last_msg + async def _handle_decision(self, data: dict, merged_msg: str, *, auto_quote: bool = False) -> None: key = self._customer_key(data) trace_id = build_trace_id(data.get("acc_id", ""), data.get("from_id", ""), merged_msg) @@ -192,6 +211,15 @@ class QingjianClient: self.logger.info("[收消息] acc=%s from=%s type=%s msg=%s", data.get("acc_id", ""), data.get("from_id", ""), msg_type, msg) await post_tianwang_callback("message_received", data, extra={"msg_type": msg_type}) + if self._is_outbound_echo(data, msg): + activity_event( + self.logger, + "inbound_ignored", + customer_id=data.get("from_id", "-"), + reason="outbound_echo_loop_guard", + ) + return + if rule.ignore: activity_event(self.logger, "inbound_ignored", customer_id=data.get("from_id", "-"), reason=rule.reason) return diff --git a/qingjian_cs/app/logger.py b/qingjian_cs/app/logger.py index 722522d..a22ccbf 100644 --- a/qingjian_cs/app/logger.py +++ b/qingjian_cs/app/logger.py @@ -11,4 +11,10 @@ def setup_logger() -> logging.Logger: formatter = logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s", "%H:%M:%S") handler.setFormatter(formatter) logger.addHandler(handler) + + # 降低 AgentScope 内部推理/格式器日志噪音,保留本项目活动日志。 + logging.getLogger("agentscope").setLevel(logging.ERROR) + logging.getLogger("agentscope.formatter").setLevel(logging.ERROR) + logging.getLogger("agentscope.agent").setLevel(logging.ERROR) + return logger