fix: prevent outbound echo loops and reduce AgentScope warning noise
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
This commit is contained in:
@@ -27,6 +27,7 @@ class QingjianClient:
|
|||||||
self.pending_images: dict[str, list[str]] = defaultdict(list)
|
self.pending_images: dict[str, list[str]] = defaultdict(list)
|
||||||
self.auto_quote_tasks: dict[str, asyncio.Task] = {}
|
self.auto_quote_tasks: dict[str, asyncio.Task] = {}
|
||||||
self.last_reply_key: dict[str, str] = {}
|
self.last_reply_key: dict[str, str] = {}
|
||||||
|
self.recent_outbound: dict[str, tuple[str, float]] = {}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _customer_key(data: dict) -> str:
|
def _customer_key(data: dict) -> str:
|
||||||
@@ -53,6 +54,9 @@ class QingjianClient:
|
|||||||
self.logger.info("[发送] %s", message.get("msg", ""))
|
self.logger.info("[发送] %s", message.get("msg", ""))
|
||||||
|
|
||||||
async def send_reply(self, data: dict, text: str, trace_id: str = "-") -> None:
|
async def send_reply(self, data: dict, text: str, trace_id: str = "-") -> None:
|
||||||
|
text = str(text or "").strip()
|
||||||
|
if not text:
|
||||||
|
return
|
||||||
msg = {
|
msg = {
|
||||||
"msg_id": "",
|
"msg_id": "",
|
||||||
"acc_id": data.get("acc_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)
|
activity_event(self.logger, "send_reply_attempt", trace_id=trace_id, customer_id=data.get("from_id", "-"), msg=text)
|
||||||
await self.send_message(msg)
|
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)
|
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:
|
async def _handle_decision(self, data: dict, merged_msg: str, *, auto_quote: bool = False) -> None:
|
||||||
key = self._customer_key(data)
|
key = self._customer_key(data)
|
||||||
trace_id = build_trace_id(data.get("acc_id", ""), data.get("from_id", ""), merged_msg)
|
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)
|
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})
|
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:
|
if rule.ignore:
|
||||||
activity_event(self.logger, "inbound_ignored", customer_id=data.get("from_id", "-"), reason=rule.reason)
|
activity_event(self.logger, "inbound_ignored", customer_id=data.get("from_id", "-"), reason=rule.reason)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -11,4 +11,10 @@ def setup_logger() -> logging.Logger:
|
|||||||
formatter = logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s", "%H:%M:%S")
|
formatter = logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s", "%H:%M:%S")
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
logger.addHandler(handler)
|
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
|
return logger
|
||||||
|
|||||||
Reference in New Issue
Block a user