newtw66
This commit is contained in:
@@ -2,15 +2,20 @@ import os
|
||||
import re
|
||||
import hashlib
|
||||
import logging
|
||||
import time
|
||||
from typing import List, Optional, Any, Dict
|
||||
from pydantic_ai import Agent, RunContext
|
||||
from pydantic_ai.models.openai import OpenAIChatModel
|
||||
from pydantic_ai.providers.openai import OpenAIProvider
|
||||
from core.schema import StandardMessage, StandardResponse
|
||||
from core.agent_tools import register_agent_tools
|
||||
from core.agent_tools import register_agent_tools, TransferSuccessException
|
||||
|
||||
logger = logging.getLogger("cs_agent")
|
||||
|
||||
# 日志详细程度:设置环境变量 AI_LOG_LEVEL=debug 可获得完整日志
|
||||
_LOG_FULL_PROMPT = os.getenv("AI_LOG_LEVEL", "").lower() == "debug"
|
||||
_LOG_CLIP_LIMIT = int(os.getenv("AI_LOG_CLIP", "2000")) # 日志截断长度
|
||||
|
||||
from core.skill_manager import skill_manager
|
||||
|
||||
|
||||
@@ -21,6 +26,18 @@ _INTERNAL_TOOL_MARKERS = (
|
||||
"【订单详情】",
|
||||
)
|
||||
|
||||
# 历史记录格式检测模式(AI 转述历史时容易泄露)
|
||||
_HISTORY_LEAK_PATTERNS = [
|
||||
r'\[\d{4}-\d{2}-\d{2}[^\]]*\]\s*(客户|客服)[::]', # [2026-03-07 12:00:00] 客户:
|
||||
r'\[\d{2}:\d{2}:\d{2}\]\s*(客户|客服|我)[::]', # [12:00:00] 客户:
|
||||
r'(根据|查看|查询|翻看)(历史|聊天|对话)(记录|内容)', # 根据历史记录
|
||||
r'历史(记录|对话|消息)(显示|表明|中)', # 历史记录显示
|
||||
r'之前的(聊天|对话|记录)(中|里|显示)', # 之前的聊天中
|
||||
r'共\d+条(历史|对话)?消息', # 共30条历史消息
|
||||
r'订单号[::]\s*\d{10,}', # 订单号:xxxxxxxxxx
|
||||
r'(状态|金额|数量)[::].*(状态|金额|数量)[::]', # 状态:xxx 金额:xxx 连续出现
|
||||
]
|
||||
|
||||
|
||||
def _clip(text: str, limit: int = 1200) -> str:
|
||||
if text is None:
|
||||
@@ -61,10 +78,17 @@ def _sanitize_reply_text(reply_text: str) -> str:
|
||||
text = re.sub(r'[\[\]]{2,}', '', text)
|
||||
text = text.strip()
|
||||
|
||||
# 检查固定标记
|
||||
if any(marker in text for marker in _INTERNAL_TOOL_MARKERS):
|
||||
logger.warning("[Brain] 拦截到工具原文泄露,降级为安全兜底回复")
|
||||
return "我在帮你看记录,稍等哈"
|
||||
|
||||
# 检查历史记录泄露模式(AI 转述历史内容)
|
||||
for pattern in _HISTORY_LEAK_PATTERNS:
|
||||
if re.search(pattern, text):
|
||||
logger.warning(f"[Brain] 检测到历史记录泄露模式: {pattern[:30]}...")
|
||||
return "我在帮你看记录,稍等哈"
|
||||
|
||||
return text.strip()
|
||||
|
||||
|
||||
@@ -187,12 +211,51 @@ class CustomerServiceBrain:
|
||||
recent_context = "【近期对话回顾】\n" + "\n".join(lines) + "\n----------------\n"
|
||||
|
||||
full_input = f"【当前客户ID:{msg.user_id}】\n{recent_context}现在的对话:{user_content}"
|
||||
logger.info(
|
||||
f"[PROMPT->AI] user={msg.user_id} acc={msg.acc_id} images={len(msg.image_urls)}\n"
|
||||
f"{_clip(full_input)}"
|
||||
)
|
||||
start_time = time.time()
|
||||
|
||||
# ===== 详细日志:发给 AI 的提示词 =====
|
||||
logger.info(f"[AI提示词] user={msg.user_id} acc={msg.acc_id} images={len(msg.image_urls)}\n{full_input}")
|
||||
if history:
|
||||
history_preview = "\n".join([f" {h.get('role','?')}: {str(h.get('content',''))[:50]}" for h in history[-4:]])
|
||||
logger.info(f"[AI历史上下文] 共{len(history)}条:\n{history_preview}")
|
||||
|
||||
result = await self.agent.run(full_input, message_history=history)
|
||||
# 尝试运行 AI,捕获转接成功异常以提前终止
|
||||
try:
|
||||
result = await self.agent.run(full_input, message_history=history)
|
||||
except TransferSuccessException as e:
|
||||
# 转接工具成功后立即返回,无需等待 AI 继续生成
|
||||
elapsed = time.time() - start_time
|
||||
logger.info(f"[Brain] 转接成功(提前终止,耗时{elapsed:.1f}s): {e.transfer_cmd[:60]}")
|
||||
return StandardResponse(
|
||||
reply_content=e.transfer_cmd,
|
||||
need_transfer=True,
|
||||
metadata={"acc_id": msg.acc_id, "acc_type": msg.acc_type}
|
||||
)
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
logger.info(f"[Brain] AI处理完成,总耗时{elapsed:.1f}s")
|
||||
|
||||
# ===== 详细日志:AI 的思考过程和工具调用 =====
|
||||
try:
|
||||
all_msgs = result.all_messages()
|
||||
for idx, m in enumerate(all_msgs):
|
||||
msg_kind = getattr(m, 'kind', type(m).__name__)
|
||||
if hasattr(m, 'parts'):
|
||||
for part in m.parts:
|
||||
part_kind = getattr(part, 'part_kind', '')
|
||||
if part_kind == 'tool-call':
|
||||
tool_name = getattr(part, 'tool_name', '?')
|
||||
tool_args = getattr(part, 'args', {})
|
||||
logger.info(f"[AI思考] 步骤{idx+1} 调用工具: {tool_name}({tool_args})")
|
||||
elif part_kind == 'tool-return':
|
||||
content = str(getattr(part, 'content', ''))[:200]
|
||||
logger.info(f"[AI思考] 步骤{idx+1} 工具返回: {content}")
|
||||
elif part_kind == 'text':
|
||||
content = str(getattr(part, 'content', ''))[:150]
|
||||
if content.strip():
|
||||
logger.info(f"[AI思考] 步骤{idx+1} 文本输出: {content}")
|
||||
except Exception as log_err:
|
||||
logger.debug(f"[AI思考日志] 解析失败: {log_err}")
|
||||
|
||||
# --- 转接指令:直接从工具返回截获,不经过 AI 二次加工 ---
|
||||
transfer_cmd = ""
|
||||
|
||||
Reference in New Issue
Block a user