136 lines
5.6 KiB
Python
136 lines
5.6 KiB
Python
import json
|
||
import datetime
|
||
from typing import Tuple
|
||
from src.common.logger import get_module_logger
|
||
from ..models.utils_model import LLMRequest
|
||
from ...config.config import global_config
|
||
from .chat_observer import ChatObserver
|
||
from ..message.message_base import UserInfo
|
||
|
||
logger = get_module_logger("reply_checker")
|
||
|
||
|
||
class ReplyChecker:
|
||
"""回复检查器"""
|
||
|
||
def __init__(self, stream_id: str):
|
||
self.llm = LLMRequest(
|
||
model=global_config.llm_normal, temperature=0.7, max_tokens=1000, request_type="reply_check"
|
||
)
|
||
self.name = global_config.BOT_NICKNAME
|
||
self.chat_observer = ChatObserver.get_instance(stream_id)
|
||
self.max_retries = 2 # 最大重试次数
|
||
|
||
async def check(self, reply: str, goal: str, retry_count: int = 0) -> Tuple[bool, str, bool]:
|
||
"""检查生成的回复是否合适
|
||
|
||
Args:
|
||
reply: 生成的回复
|
||
goal: 对话目标
|
||
retry_count: 当前重试次数
|
||
|
||
Returns:
|
||
Tuple[bool, str, bool]: (是否合适, 原因, 是否需要重新规划)
|
||
"""
|
||
# 获取最新的消息记录
|
||
messages = self.chat_observer.get_cached_messages(limit=5)
|
||
chat_history_text = ""
|
||
for msg in messages:
|
||
time_str = datetime.datetime.fromtimestamp(msg["time"]).strftime("%H:%M:%S")
|
||
user_info = UserInfo.from_dict(msg.get("user_info", {}))
|
||
sender = user_info.user_nickname or f"用户{user_info.user_id}"
|
||
if sender == self.name:
|
||
sender = "你说"
|
||
chat_history_text += f"{time_str},{sender}:{msg.get('processed_plain_text', '')}\n"
|
||
|
||
prompt = f"""请检查以下回复是否合适:
|
||
|
||
当前对话目标:{goal}
|
||
最新的对话记录:
|
||
{chat_history_text}
|
||
|
||
待检查的回复:
|
||
{reply}
|
||
|
||
请检查以下几点:
|
||
1. 回复是否依然符合当前对话目标和实现方式
|
||
2. 回复是否与最新的对话记录保持一致性
|
||
3. 回复是否重复发言,重复表达
|
||
4. 回复是否包含违法违规内容(政治敏感、暴力等)
|
||
5. 回复是否以你的角度发言,不要把"你"说的话当做对方说的话,这是你自己说的话
|
||
|
||
请以JSON格式输出,包含以下字段:
|
||
1. suitable: 是否合适 (true/false)
|
||
2. reason: 原因说明
|
||
3. need_replan: 是否需要重新规划对话目标 (true/false),当发现当前对话目标不再适合时设为true
|
||
|
||
输出格式示例:
|
||
{{
|
||
"suitable": true,
|
||
"reason": "回复符合要求,内容得体",
|
||
"need_replan": false
|
||
}}
|
||
|
||
注意:请严格按照JSON格式输出,不要包含任何其他内容。"""
|
||
|
||
try:
|
||
content, _ = await self.llm.generate_response_async(prompt)
|
||
logger.debug(f"检查回复的原始返回: {content}")
|
||
|
||
# 清理内容,尝试提取JSON部分
|
||
content = content.strip()
|
||
try:
|
||
# 尝试直接解析
|
||
result = json.loads(content)
|
||
except json.JSONDecodeError:
|
||
# 如果直接解析失败,尝试查找和提取JSON部分
|
||
import re
|
||
|
||
json_pattern = r"\{[^{}]*\}"
|
||
json_match = re.search(json_pattern, content)
|
||
if json_match:
|
||
try:
|
||
result = json.loads(json_match.group())
|
||
except json.JSONDecodeError:
|
||
# 如果JSON解析失败,尝试从文本中提取结果
|
||
is_suitable = "不合适" not in content.lower() and "违规" not in content.lower()
|
||
reason = content[:100] if content else "无法解析响应"
|
||
need_replan = "重新规划" in content.lower() or "目标不适合" in content.lower()
|
||
return is_suitable, reason, need_replan
|
||
else:
|
||
# 如果找不到JSON,从文本中判断
|
||
is_suitable = "不合适" not in content.lower() and "违规" not in content.lower()
|
||
reason = content[:100] if content else "无法解析响应"
|
||
need_replan = "重新规划" in content.lower() or "目标不适合" in content.lower()
|
||
return is_suitable, reason, need_replan
|
||
|
||
# 验证JSON字段
|
||
suitable = result.get("suitable", None)
|
||
reason = result.get("reason", "未提供原因")
|
||
need_replan = result.get("need_replan", False)
|
||
|
||
# 如果suitable字段是字符串,转换为布尔值
|
||
if isinstance(suitable, str):
|
||
suitable = suitable.lower() == "true"
|
||
|
||
# 如果suitable字段不存在或不是布尔值,从reason中判断
|
||
if suitable is None:
|
||
suitable = "不合适" not in reason.lower() and "违规" not in reason.lower()
|
||
|
||
# 如果不合适且未达到最大重试次数,返回需要重试
|
||
if not suitable and retry_count < self.max_retries:
|
||
return False, reason, False
|
||
|
||
# 如果不合适且已达到最大重试次数,返回需要重新规划
|
||
if not suitable and retry_count >= self.max_retries:
|
||
return False, f"多次重试后仍不合适: {reason}", True
|
||
|
||
return suitable, reason, need_replan
|
||
|
||
except Exception as e:
|
||
logger.error(f"检查回复时出错: {e}")
|
||
# 如果出错且已达到最大重试次数,建议重新规划
|
||
if retry_count >= self.max_retries:
|
||
return False, "多次检查失败,建议重新规划", True
|
||
return False, f"检查过程出错,建议重试: {str(e)}", False
|