fix: block leaked tool output and thinking text

This commit is contained in:
2026-03-06 21:58:50 +08:00
parent 07053ce1ad
commit 3c52061861
2 changed files with 61 additions and 15 deletions

View File

@@ -27,6 +27,16 @@ _TRANSFER_CALM_REPLIES = [
"在催了在催了,马上哈", "在催了在催了,马上哈",
] ]
_OUTBOUND_BLOCK_MARKERS = (
"【历史记录摘要】",
"【详细记录】",
"【订单摘要】",
"【订单详情】",
"<think",
"think_never_used",
'[{"name":',
)
class SystemOrchestrator: class SystemOrchestrator:
""" """
全系统总编排:具备转接冷却、防抖合并、多消息去重、以及精准日志。 全系统总编排:具备转接冷却、防抖合并、多消息去重、以及精准日志。
@@ -64,6 +74,18 @@ class SystemOrchestrator:
self._user_locks[user_id] = asyncio.Lock() self._user_locks[user_id] = asyncio.Lock()
return self._user_locks[user_id] return self._user_locks[user_id]
@staticmethod
def _sanitize_outbound_text(text: str) -> str:
if not text:
return ""
cleaned = str(text).strip()
if "[转移会话]" in cleaned:
return cleaned
if any(marker in cleaned for marker in _OUTBOUND_BLOCK_MARKERS):
logger.warning("[Orchestrator] 拦截到内部内容外发,替换为安全兜底回复")
return "我在帮你看记录,稍等哈"
return cleaned
async def on_raw_message_received(self, platform: str, raw_data: dict): async def on_raw_message_received(self, platform: str, raw_data: dict):
"""链路入口""" """链路入口"""
try: try:
@@ -286,6 +308,7 @@ class SystemOrchestrator:
# E. 发送并记录时间 # E. 发送并记录时间
if std_res.should_reply: if std_res.should_reply:
std_res.reply_content = self._sanitize_outbound_text(std_res.reply_content)
std_res.metadata = {"acc_id": acc_id, "acc_type": acc_type} std_res.metadata = {"acc_id": acc_id, "acc_type": acc_type}
# 转接场景:先发一句安抚话,再发转接指令 # 转接场景:先发一句安抚话,再发转接指令

View File

@@ -14,6 +14,14 @@ logger = logging.getLogger("cs_agent")
from core.skill_manager import skill_manager from core.skill_manager import skill_manager
_INTERNAL_TOOL_MARKERS = (
"【历史记录摘要】",
"【详细记录】",
"【订单摘要】",
"【订单详情】",
)
def _clip(text: str, limit: int = 1200) -> str: def _clip(text: str, limit: int = 1200) -> str:
if text is None: if text is None:
return "" return ""
@@ -32,6 +40,34 @@ def _fmt_time(ts: Any) -> str:
return s return s
def _sanitize_reply_text(reply_text: str) -> str:
if not reply_text:
return ""
text = str(reply_text)
text = re.sub(r'\[\]<\|[^|]+\|>', '', text)
text = re.sub(r'<\|[^|]*\|>', '', text)
text = re.sub(r'\[Function[^\]]*\]', '', text)
text = re.sub(r'\[/?Tool[^\]]*\]', '', text)
text = re.sub(r'</?tool[_\-]?[^>]*>', '', text, flags=re.IGNORECASE)
text = re.sub(r'<think[_a-zA-Z0-9]*[^>]*>.*?</think[_a-zA-Z0-9]*[^>]*>', '', text, flags=re.DOTALL)
text = re.sub(r'<think[_a-zA-Z0-9]*[^>]*>.*', '', text, flags=re.DOTALL)
text = re.sub(r'</?think[_a-zA-Z0-9]*[^>]*>', '', text)
text = re.sub(r'```[^`]*```', '', text, flags=re.DOTALL)
text = re.sub(r'AgentRunResult\([^)]*\)', '', text)
text = re.sub(r'\[/?[A-Z][a-zA-Z]*(?:Call|End|Start|Result|Return)[^\]]*\]', '', text)
text = re.sub(r'^\s*\[\s*\{.*$', '', text, flags=re.DOTALL)
text = re.sub(r'^\s*[\[{].*"name"\s*:.*$', '', text, flags=re.DOTALL)
text = re.sub(r'[\[\]]{2,}', '', text)
text = text.strip()
if any(marker in text for marker in _INTERNAL_TOOL_MARKERS):
logger.warning("[Brain] 拦截到工具原文泄露,降级为安全兜底回复")
return "我在帮你看记录,稍等哈"
return text.strip()
class CustomerServiceBrain: class CustomerServiceBrain:
""" """
重构后的单一 Agent 大脑: 重构后的单一 Agent 大脑:
@@ -182,21 +218,8 @@ class CustomerServiceBrain:
if isinstance(raw_output, str): if isinstance(raw_output, str):
reply_text = raw_output reply_text = raw_output
# 清理模型泄露的内部标记/乱码(覆盖所有已知格式) # 清理模型泄露的内部标记/工具原文
reply_text = re.sub(r'\[\]<\|[^|]+\|>', '', reply_text) reply_text = _sanitize_reply_text(reply_text)
reply_text = re.sub(r'<\|[^|]*\|>', '', reply_text)
reply_text = re.sub(r'\[Function[^\]]*\]', '', reply_text)
reply_text = re.sub(r'\[/?Tool[^\]]*\]', '', reply_text)
reply_text = re.sub(r'</?tool[_\-]?[^>]*>', '', reply_text, flags=re.IGNORECASE)
reply_text = re.sub(r'<think[_a-zA-Z0-9]*[^>]*>.*?</think[_a-zA-Z0-9]*[^>]*>', '', reply_text, flags=re.DOTALL)
reply_text = re.sub(r'<think[_a-zA-Z0-9]*[^>]*>.*', '', reply_text, flags=re.DOTALL)
reply_text = re.sub(r'</?think[_a-zA-Z0-9]*[^>]*>', '', reply_text)
reply_text = re.sub(r'```[^`]*```', '', reply_text)
reply_text = re.sub(r'\{["\'][^}]+\}', '', reply_text)
reply_text = re.sub(r'AgentRunResult\([^)]*\)', '', reply_text)
reply_text = re.sub(r'\[/?[A-Z][a-zA-Z]*(?:Call|End|Start|Result|Return)[^\]]*\]', '', reply_text)
reply_text = re.sub(r'[\[\]]{2,}', '', reply_text)
reply_text = reply_text.strip()
# 过滤"在呢铁子" # 过滤"在呢铁子"
if "在呢铁子" in reply_text: if "在呢铁子" in reply_text: