rebase 清理
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
"""
|
||||
|
||||
from collections import deque
|
||||
from typing import List, Dict
|
||||
|
||||
from src.common.logger import get_logger
|
||||
|
||||
|
||||
@@ -399,13 +399,21 @@ class ExpressionLearner:
|
||||
# sourcery skip: use-join
|
||||
"""
|
||||
学习并存储表达方式
|
||||
type: "style" or "grammar"
|
||||
"""
|
||||
if type == "style":
|
||||
type_str = "语言风格"
|
||||
elif type == "grammar":
|
||||
type_str = "句法特点"
|
||||
else:
|
||||
raise ValueError(f"Invalid type: {type}")
|
||||
|
||||
# 检查是否允许在此聊天流中学习(在函数最前面检查)
|
||||
if not self.can_learn_for_chat():
|
||||
logger.debug(f"聊天流 {self.chat_name} 不允许学习表达,跳过学习")
|
||||
return []
|
||||
|
||||
res = await self.learn_expression(num)
|
||||
res = await self.learn_expression(type, num)
|
||||
|
||||
if res is None:
|
||||
return []
|
||||
@@ -421,10 +429,10 @@ class ExpressionLearner:
|
||||
learnt_expressions_str = ""
|
||||
for _chat_id, situation, style in learnt_expressions:
|
||||
learnt_expressions_str += f"{situation}->{style}\n"
|
||||
logger.info(f"在 {group_name} 学习到表达风格:\n{learnt_expressions_str}")
|
||||
logger.info(f"在 {group_name} 学习到{type_str}:\n{learnt_expressions_str}")
|
||||
|
||||
if not learnt_expressions:
|
||||
logger.info("没有学习到表达风格")
|
||||
logger.info(f"没有学习到{type_str}")
|
||||
return []
|
||||
|
||||
# 按chat_id分组
|
||||
@@ -572,10 +580,16 @@ class ExpressionLearner:
|
||||
"""从指定聊天流学习表达方式
|
||||
|
||||
Args:
|
||||
num: 学习数量
|
||||
type: "style" or "grammar"
|
||||
"""
|
||||
type_str = "语言风格"
|
||||
prompt = "learn_style_prompt"
|
||||
if type == "style":
|
||||
type_str = "语言风格"
|
||||
prompt = "learn_style_prompt"
|
||||
elif type == "grammar":
|
||||
type_str = "句法特点"
|
||||
prompt = "learn_grammar_prompt"
|
||||
else:
|
||||
raise ValueError(f"Invalid type: {type}")
|
||||
|
||||
current_time = time.time()
|
||||
|
||||
@@ -766,11 +780,9 @@ class ExpressionLearnerManager:
|
||||
"""
|
||||
自动将/data/expression/learnt_style 和 learnt_grammar 下所有expressions.json迁移到数据库。
|
||||
迁移完成后在/data/expression/done.done写入标记文件,存在则跳过。
|
||||
然后检查done.done2,如果没有就删除所有grammar表达并创建该标记文件。
|
||||
"""
|
||||
base_dir = os.path.join("data", "expression")
|
||||
done_flag = os.path.join(base_dir, "done.done")
|
||||
done_flag2 = os.path.join(base_dir, "done.done2")
|
||||
|
||||
# 确保基础目录存在
|
||||
try:
|
||||
@@ -805,36 +817,27 @@ class ExpressionLearnerManager:
|
||||
expr_file = os.path.join(type_dir, chat_id, "expressions.json")
|
||||
if not os.path.exists(expr_file):
|
||||
continue
|
||||
|
||||
try:
|
||||
async with aiofiles.open(expr_file, encoding="utf-8") as f:
|
||||
content = await f.read()
|
||||
expressions = orjson.loads(content)
|
||||
|
||||
for chat_id in chat_ids:
|
||||
expr_file = os.path.join(type_dir, chat_id, "expressions.json")
|
||||
if not os.path.exists(expr_file):
|
||||
if not isinstance(expressions, list):
|
||||
logger.warning(f"表达方式文件格式错误,跳过: {expr_file}")
|
||||
continue
|
||||
try:
|
||||
with open(expr_file, "r", encoding="utf-8") as f:
|
||||
expressions = json.load(f)
|
||||
|
||||
if not isinstance(expressions, list):
|
||||
logger.warning(f"表达方式文件格式错误,跳过: {expr_file}")
|
||||
for expr in expressions:
|
||||
if not isinstance(expr, dict):
|
||||
continue
|
||||
|
||||
for expr in expressions:
|
||||
if not isinstance(expr, dict):
|
||||
continue
|
||||
situation = expr.get("situation")
|
||||
style_val = expr.get("style")
|
||||
count = expr.get("count", 1)
|
||||
last_active_time = expr.get("last_active_time", time.time())
|
||||
|
||||
situation = expr.get("situation")
|
||||
style_val = expr.get("style")
|
||||
count = expr.get("count", 1)
|
||||
last_active_time = expr.get("last_active_time", time.time())
|
||||
|
||||
if not situation or not style_val:
|
||||
logger.warning(f"表达方式缺少必要字段,跳过: {expr}")
|
||||
continue
|
||||
if not situation or not style_val:
|
||||
logger.warning(f"表达方式缺少必要字段,跳过: {expr}")
|
||||
continue
|
||||
|
||||
# 查重:同chat_id+type+situation+style
|
||||
async with get_db_session() as session:
|
||||
@@ -913,40 +916,5 @@ class ExpressionLearnerManager:
|
||||
except Exception as e:
|
||||
logger.error(f"迁移老数据创建日期失败: {e}")
|
||||
|
||||
def delete_all_grammar_expressions(self) -> int:
|
||||
"""
|
||||
检查expression库中所有type为"grammar"的表达并全部删除
|
||||
|
||||
Returns:
|
||||
int: 删除的grammar表达数量
|
||||
"""
|
||||
try:
|
||||
# 查询所有type为"grammar"的表达
|
||||
grammar_expressions = Expression.select().where(Expression.type == "grammar")
|
||||
grammar_count = grammar_expressions.count()
|
||||
|
||||
if grammar_count == 0:
|
||||
logger.info("expression库中没有找到grammar类型的表达")
|
||||
return 0
|
||||
|
||||
logger.info(f"找到 {grammar_count} 个grammar类型的表达,开始删除...")
|
||||
|
||||
# 删除所有grammar类型的表达
|
||||
deleted_count = 0
|
||||
for expr in grammar_expressions:
|
||||
try:
|
||||
expr.delete_instance()
|
||||
deleted_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"删除grammar表达失败: {e}")
|
||||
continue
|
||||
|
||||
logger.info(f"成功删除 {deleted_count} 个grammar类型的表达")
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"删除grammar表达过程中发生错误: {e}")
|
||||
return 0
|
||||
|
||||
|
||||
expression_learner_manager = ExpressionLearnerManager()
|
||||
|
||||
@@ -32,7 +32,7 @@ def init_prompt():
|
||||
以下是可选的表达情境:
|
||||
{all_situations}
|
||||
|
||||
请你分析聊天内容的语境、情绪、话题类型,从上述情境中选择最适合当前聊天情境的,最多{max_num}个情境。
|
||||
请你分析聊天内容的语境、情绪、话题类型,从上述情境中选择最适合当前聊天情境的{min_num}-{max_num}个情境。
|
||||
考虑因素包括:
|
||||
1. 聊天的情绪氛围(轻松、严肃、幽默等)
|
||||
2. 话题类型(日常、技术、游戏、情感等)
|
||||
@@ -42,7 +42,7 @@ def init_prompt():
|
||||
请以JSON格式输出,只需要输出选中的情境编号:
|
||||
例如:
|
||||
{{
|
||||
"selected_situations": [2, 3, 5, 7, 19]
|
||||
"selected_situations": [2, 3, 5, 7, 19, 22, 25, 38, 39, 45, 48, 64]
|
||||
}}
|
||||
|
||||
请严格按照JSON格式输出,不要包含其他内容:
|
||||
@@ -544,24 +544,34 @@ class ExpressionSelector:
|
||||
# 检查是否允许在此聊天流中使用表达
|
||||
if not self.can_use_expression_for_chat(chat_id):
|
||||
logger.debug(f"聊天流 {chat_id} 不允许使用表达,返回空列表")
|
||||
return [], []
|
||||
return []
|
||||
|
||||
# 1. 获取35个随机表达方式(现在按权重抽取)
|
||||
style_exprs, grammar_exprs = await self.get_random_expressions(chat_id, 30, 0.5, 0.5)
|
||||
|
||||
# 2. 构建所有表达方式的索引和情境列表
|
||||
all_expressions: List[Dict[str, Any]] = []
|
||||
all_situations: List[str] = []
|
||||
all_expressions = []
|
||||
all_situations = []
|
||||
|
||||
# 添加style表达方式
|
||||
for expr in style_exprs:
|
||||
expr = expr.copy()
|
||||
all_expressions.append(expr)
|
||||
all_situations.append(f"{len(all_expressions)}.当 {expr['situation']} 时,使用 {expr['style']}")
|
||||
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
||||
expr_with_type = expr.copy()
|
||||
expr_with_type["type"] = "style"
|
||||
all_expressions.append(expr_with_type)
|
||||
all_situations.append(f"{len(all_expressions)}.{expr['situation']}")
|
||||
|
||||
# 添加grammar表达方式
|
||||
for expr in grammar_exprs:
|
||||
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
||||
expr_with_type = expr.copy()
|
||||
expr_with_type["type"] = "grammar"
|
||||
all_expressions.append(expr_with_type)
|
||||
all_situations.append(f"{len(all_expressions)}.{expr['situation']}")
|
||||
|
||||
if not all_expressions:
|
||||
logger.warning("没有找到可用的表达方式")
|
||||
return [], []
|
||||
return []
|
||||
|
||||
all_situations_str = "\n".join(all_situations)
|
||||
|
||||
@@ -577,11 +587,14 @@ class ExpressionSelector:
|
||||
bot_name=global_config.bot.nickname,
|
||||
chat_observe_info=chat_info,
|
||||
all_situations=all_situations_str,
|
||||
min_num=min_num,
|
||||
max_num=max_num,
|
||||
target_message=target_message_str,
|
||||
target_message_extra_block=target_message_extra_block,
|
||||
)
|
||||
|
||||
# print(prompt)
|
||||
|
||||
# 4. 调用LLM
|
||||
try:
|
||||
# start_time = time.time()
|
||||
@@ -589,7 +602,7 @@ class ExpressionSelector:
|
||||
|
||||
if not content:
|
||||
logger.warning("LLM返回空结果")
|
||||
return [], []
|
||||
return []
|
||||
|
||||
# 5. 解析结果
|
||||
result = repair_json(content)
|
||||
@@ -599,17 +612,15 @@ class ExpressionSelector:
|
||||
if not isinstance(result, dict) or "selected_situations" not in result:
|
||||
logger.error("LLM返回格式错误")
|
||||
logger.info(f"LLM返回结果: \n{content}")
|
||||
return [], []
|
||||
return []
|
||||
|
||||
selected_indices = result["selected_situations"]
|
||||
|
||||
# 根据索引获取完整的表达方式
|
||||
valid_expressions: List[Dict[str, Any]] = []
|
||||
selected_ids = []
|
||||
valid_expressions = []
|
||||
for idx in selected_indices:
|
||||
if isinstance(idx, int) and 1 <= idx <= len(all_expressions):
|
||||
expression = all_expressions[idx - 1] # 索引从1开始
|
||||
selected_ids.append(expression["id"])
|
||||
valid_expressions.append(expression)
|
||||
|
||||
# 对选中的所有表达方式,一次性更新count数
|
||||
@@ -617,7 +628,7 @@ class ExpressionSelector:
|
||||
asyncio.create_task(self.update_expressions_count_batch(valid_expressions, 0.006)) # noqa: RUF006
|
||||
|
||||
# logger.info(f"LLM从{len(all_expressions)}个情境中选择了{len(valid_expressions)}个")
|
||||
return valid_expressions, selected_ids
|
||||
return valid_expressions
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"LLM处理表达方式选择时出错: {e}")
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import asyncio
|
||||
import math
|
||||
import re
|
||||
import traceback
|
||||
from typing import Tuple, TYPE_CHECKING
|
||||
|
||||
from src.chat.heart_flow.heartflow import heartflow
|
||||
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.chat.message_receive.message import MessageRecv
|
||||
from src.chat.message_receive.storage import MessageStorage
|
||||
from src.chat.utils.chat_message_builder import replace_user_references_sync
|
||||
from src.chat.utils.timer_calculator import Timer
|
||||
from src.chat.utils.utils import is_mentioned_bot_in_message
|
||||
from src.common.logger import get_logger
|
||||
from src.config.config import global_config
|
||||
from src.mood.mood_manager import mood_manager
|
||||
from src.person_info.relationship_manager import get_relationship_manager
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.chat.heart_flow.sub_heartflow import SubHeartflow
|
||||
|
||||
logger = get_logger("chat")
|
||||
|
||||
async def _calculate_interest(message: MessageRecv) -> Tuple[float, bool, list[str]]:
|
||||
"""计算消息的兴趣度
|
||||
|
||||
Args:
|
||||
message: 待处理的消息对象
|
||||
|
||||
Returns:
|
||||
Tuple[float, bool, list[str]]: (兴趣度, 是否被提及, 关键词)
|
||||
"""
|
||||
is_mentioned, _ = is_mentioned_bot_in_message(message)
|
||||
interested_rate = 0.0
|
||||
|
||||
with Timer("记忆激活"):
|
||||
interested_rate, keywords = await hippocampus_manager.get_activate_from_text(
|
||||
message.processed_plain_text,
|
||||
max_depth=4,
|
||||
fast_retrieval=False,
|
||||
)
|
||||
message.key_words = keywords
|
||||
message.key_words_lite = keywords
|
||||
logger.debug(f"记忆激活率: {interested_rate:.2f}, 关键词: {keywords}")
|
||||
|
||||
text_len = len(message.processed_plain_text)
|
||||
# 根据文本长度分布调整兴趣度,采用分段函数实现更精确的兴趣度计算
|
||||
# 基于实际分布:0-5字符(26.57%), 6-10字符(27.18%), 11-20字符(22.76%), 21-30字符(10.33%), 31+字符(13.86%)
|
||||
|
||||
if text_len == 0:
|
||||
base_interest = 0.01 # 空消息最低兴趣度
|
||||
elif text_len <= 5:
|
||||
# 1-5字符:线性增长 0.01 -> 0.03
|
||||
base_interest = 0.01 + (text_len - 1) * (0.03 - 0.01) / 4
|
||||
elif text_len <= 10:
|
||||
# 6-10字符:线性增长 0.03 -> 0.06
|
||||
base_interest = 0.03 + (text_len - 5) * (0.06 - 0.03) / 5
|
||||
elif text_len <= 20:
|
||||
# 11-20字符:线性增长 0.06 -> 0.12
|
||||
base_interest = 0.06 + (text_len - 10) * (0.12 - 0.06) / 10
|
||||
elif text_len <= 30:
|
||||
# 21-30字符:线性增长 0.12 -> 0.18
|
||||
base_interest = 0.12 + (text_len - 20) * (0.18 - 0.12) / 10
|
||||
elif text_len <= 50:
|
||||
# 31-50字符:线性增长 0.18 -> 0.22
|
||||
base_interest = 0.18 + (text_len - 30) * (0.22 - 0.18) / 20
|
||||
elif text_len <= 100:
|
||||
# 51-100字符:线性增长 0.22 -> 0.26
|
||||
base_interest = 0.22 + (text_len - 50) * (0.26 - 0.22) / 50
|
||||
else:
|
||||
# 100+字符:对数增长 0.26 -> 0.3,增长率递减
|
||||
base_interest = 0.26 + (0.3 - 0.26) * (math.log10(text_len - 99) / math.log10(901)) # 1000-99=901
|
||||
|
||||
# 确保在范围内
|
||||
base_interest = min(max(base_interest, 0.01), 0.3)
|
||||
|
||||
interested_rate += base_interest
|
||||
|
||||
if is_mentioned:
|
||||
interest_increase_on_mention = 1
|
||||
interested_rate += interest_increase_on_mention
|
||||
|
||||
return interested_rate, is_mentioned, keywords
|
||||
|
||||
|
||||
class HeartFCMessageReceiver:
|
||||
"""心流处理器,负责处理接收到的消息并计算兴趣度"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化心流处理器,创建消息存储实例"""
|
||||
self.storage = MessageStorage()
|
||||
|
||||
async def process_message(self, message: MessageRecv) -> None:
|
||||
"""处理接收到的原始消息数据
|
||||
|
||||
主要流程:
|
||||
1. 消息解析与初始化
|
||||
2. 消息缓冲处理
|
||||
4. 过滤检查
|
||||
5. 兴趣度计算
|
||||
6. 关系处理
|
||||
|
||||
Args:
|
||||
message_data: 原始消息字符串
|
||||
"""
|
||||
try:
|
||||
# 1. 消息解析与初始化
|
||||
userinfo = message.message_info.user_info
|
||||
chat = message.chat_stream
|
||||
|
||||
# 2. 兴趣度计算与更新
|
||||
interested_rate, is_mentioned, keywords = await _calculate_interest(message)
|
||||
message.interest_value = interested_rate
|
||||
message.is_mentioned = is_mentioned
|
||||
|
||||
await self.storage.store_message(message, chat)
|
||||
|
||||
subheartflow: SubHeartflow = await heartflow.get_or_create_subheartflow(chat.stream_id) # type: ignore
|
||||
|
||||
await subheartflow.heart_fc_instance.add_message(message.to_dict())
|
||||
if global_config.mood.enable_mood:
|
||||
chat_mood = mood_manager.get_mood_by_chat_id(subheartflow.chat_id)
|
||||
asyncio.create_task(chat_mood.update_mood_by_message(message, interested_rate))
|
||||
|
||||
# 3. 日志记录
|
||||
mes_name = chat.group_info.group_name if chat.group_info else "私聊"
|
||||
|
||||
# 如果消息中包含图片标识,则将 [picid:...] 替换为 [图片]
|
||||
picid_pattern = r"\[picid:([^\]]+)\]"
|
||||
processed_plain_text = re.sub(picid_pattern, "[图片]", message.processed_plain_text)
|
||||
|
||||
# 应用用户引用格式替换,将回复<aaa:bbb>和@<aaa:bbb>格式转换为可读格式
|
||||
processed_plain_text = replace_user_references_sync(
|
||||
processed_plain_text,
|
||||
message.message_info.platform, # type: ignore
|
||||
replace_bot_name=True,
|
||||
)
|
||||
|
||||
if keywords:
|
||||
logger.info(
|
||||
f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}[兴趣度:{interested_rate:.2f}][关键词:{keywords}]"
|
||||
) # type: ignore
|
||||
else:
|
||||
logger.info(
|
||||
f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}[兴趣度:{interested_rate:.2f}]"
|
||||
) # type: ignore
|
||||
|
||||
_ = Person.register_person(platform=message.message_info.platform, user_id=message.message_info.user_info.user_id,nickname=userinfo.user_nickname) # type: ignore
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"消息处理失败: {e}")
|
||||
print(traceback.format_exc())
|
||||
@@ -1,42 +0,0 @@
|
||||
from rich.traceback import install
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||
from src.chat.chat_loop.heartFC_chat import HeartFChatting
|
||||
from src.chat.utils.utils import get_chat_type_and_target_info
|
||||
|
||||
logger = get_logger("sub_heartflow")
|
||||
|
||||
install(extra_lines=3)
|
||||
|
||||
|
||||
class SubHeartflow:
|
||||
def __init__(
|
||||
self,
|
||||
subheartflow_id,
|
||||
):
|
||||
"""子心流初始化函数
|
||||
|
||||
Args:
|
||||
subheartflow_id: 子心流唯一标识符
|
||||
"""
|
||||
# 基础属性,两个值是一样的
|
||||
self.subheartflow_id = subheartflow_id
|
||||
self.chat_id = subheartflow_id
|
||||
|
||||
self.is_group_chat, self.chat_target_info = (None, None)
|
||||
self.log_prefix = get_chat_manager().get_stream_name(self.subheartflow_id) or self.subheartflow_id
|
||||
|
||||
# focus模式退出冷却时间管理
|
||||
self.last_focus_exit_time: float = 0 # 上次退出focus模式的时间
|
||||
|
||||
# 随便水群 normal_chat 和 认真水群 focus_chat 实例
|
||||
# CHAT模式激活 随便水群 FOCUS模式激活 认真水群
|
||||
self.heart_fc_instance: HeartFChatting = HeartFChatting(
|
||||
chat_id=self.subheartflow_id,
|
||||
) # 该sub_heartflow的HeartFChatting实例
|
||||
|
||||
async def initialize(self):
|
||||
"""异步初始化方法,创建兴趣流并确定聊天类型"""
|
||||
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
|
||||
await self.heart_fc_instance.start()
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -261,8 +261,6 @@ class MessageSending(MessageProcessBase):
|
||||
self.display_message = display_message
|
||||
|
||||
self.interest_value = 0.0
|
||||
|
||||
self.selected_expressions = selected_expressions
|
||||
|
||||
def build_reply(self):
|
||||
"""设置回复消息"""
|
||||
|
||||
@@ -682,7 +682,6 @@ class MessageStorage:
|
||||
should_act=should_act,
|
||||
key_words=key_words,
|
||||
key_words_lite=key_words_lite,
|
||||
additional_config=additional_config_json,
|
||||
)
|
||||
async with get_db_session() as session:
|
||||
session.add(new_message)
|
||||
|
||||
@@ -184,133 +184,13 @@ class ActionModifier:
|
||||
def _check_action_associated_types(self, all_actions: dict[str, ActionInfo], chat_context: "StreamContext"):
|
||||
type_mismatched_actions: list[tuple[str, str]] = []
|
||||
for action_name, action_info in all_actions.items():
|
||||
if action_info.associated_types and not self._check_action_output_types(action_info.associated_types, chat_context):
|
||||
if action_info.associated_types and not chat_context.check_types(action_info.associated_types):
|
||||
associated_types_str = ", ".join(action_info.associated_types)
|
||||
reason = f"适配器不支持(需要: {associated_types_str})"
|
||||
type_mismatched_actions.append((action_name, reason))
|
||||
logger.debug(f"{self.log_prefix}决定移除动作: {action_name},原因: {reason}")
|
||||
return type_mismatched_actions
|
||||
|
||||
def _check_action_output_types(self, output_types: list[str], chat_context: StreamContext) -> bool:
|
||||
"""
|
||||
检查Action的输出类型是否被当前适配器支持
|
||||
|
||||
Args:
|
||||
output_types: Action需要输出的消息类型列表
|
||||
chat_context: 聊天上下文
|
||||
|
||||
Returns:
|
||||
bool: 如果所有输出类型都支持则返回True
|
||||
"""
|
||||
# 获取当前适配器支持的输出类型
|
||||
adapter_supported_types = self._get_adapter_supported_output_types(chat_context)
|
||||
|
||||
# 检查所有需要的输出类型是否都被支持
|
||||
for output_type in output_types:
|
||||
if output_type not in adapter_supported_types:
|
||||
logger.debug(f"适配器不支持输出类型 '{output_type}',支持的类型: {adapter_supported_types}")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_adapter_supported_output_types(self, chat_context: StreamContext) -> list[str]:
|
||||
"""
|
||||
获取当前适配器支持的输出类型列表
|
||||
|
||||
Args:
|
||||
chat_context: 聊天上下文
|
||||
|
||||
Returns:
|
||||
list[str]: 支持的输出类型列表
|
||||
"""
|
||||
# 检查additional_config是否存在且不为空
|
||||
additional_config = None
|
||||
has_additional_config = False
|
||||
|
||||
# 先检查 current_message 是否存在
|
||||
if not chat_context.current_message:
|
||||
logger.warning(f"{self.log_prefix} [问题] chat_context.current_message 为 None,无法获取适配器支持的类型")
|
||||
return ["text", "emoji"] # 返回基础类型
|
||||
|
||||
if hasattr(chat_context.current_message, "additional_config"):
|
||||
additional_config = chat_context.current_message.additional_config
|
||||
|
||||
# 更准确的非空判断
|
||||
if additional_config is not None:
|
||||
if isinstance(additional_config, str) and additional_config.strip():
|
||||
has_additional_config = True
|
||||
elif isinstance(additional_config, dict):
|
||||
# 字典存在就可以,即使为空也可能有format_info字段
|
||||
has_additional_config = True
|
||||
else:
|
||||
logger.warning(f"{self.log_prefix} [问题] current_message 没有 additional_config 属性")
|
||||
|
||||
logger.debug(f"{self.log_prefix} [调试] has_additional_config: {has_additional_config}")
|
||||
|
||||
if has_additional_config:
|
||||
try:
|
||||
logger.debug(f"{self.log_prefix} [调试] 开始解析 additional_config")
|
||||
format_info = None
|
||||
|
||||
# 处理additional_config可能是字符串或字典的情况
|
||||
if isinstance(additional_config, str):
|
||||
# 如果是字符串,尝试解析为JSON
|
||||
try:
|
||||
config = orjson.loads(additional_config)
|
||||
format_info = config.get("format_info")
|
||||
except (orjson.JSONDecodeError, AttributeError, TypeError) as e:
|
||||
format_info = None
|
||||
|
||||
elif isinstance(additional_config, dict):
|
||||
# 如果是字典,直接获取format_info
|
||||
format_info = additional_config.get("format_info")
|
||||
|
||||
# 如果找到了format_info,从中提取支持的类型
|
||||
if format_info:
|
||||
if "accept_format" in format_info:
|
||||
accept_format = format_info["accept_format"]
|
||||
if isinstance(accept_format, str):
|
||||
accept_format = [accept_format]
|
||||
elif isinstance(accept_format, list):
|
||||
pass
|
||||
else:
|
||||
accept_format = list(accept_format) if hasattr(accept_format, "__iter__") else []
|
||||
|
||||
# 合并基础类型和适配器特定类型
|
||||
result = list(set(accept_format))
|
||||
return result
|
||||
|
||||
# 备用检查content_format字段
|
||||
elif "content_format" in format_info:
|
||||
content_format = format_info["content_format"]
|
||||
logger.debug(f"{self.log_prefix} [调试] 找到 content_format: {content_format}")
|
||||
if isinstance(content_format, str):
|
||||
content_format = [content_format]
|
||||
elif isinstance(content_format, list):
|
||||
pass
|
||||
else:
|
||||
content_format = list(content_format) if hasattr(content_format, "__iter__") else []
|
||||
|
||||
result = list(set(content_format))
|
||||
return result
|
||||
else:
|
||||
logger.warning(f"{self.log_prefix} [问题] additional_config 中没有 format_info 字段")
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} [问题] 解析适配器格式信息失败: {e}", exc_info=True)
|
||||
else:
|
||||
logger.warning(f"{self.log_prefix} [问题] additional_config 不存在或为空")
|
||||
|
||||
# 如果无法获取格式信息,返回默认支持的基础类型
|
||||
default_types = ["text", "emoji"]
|
||||
logger.warning(
|
||||
f"{self.log_prefix} [问题] 无法从适配器获取支持的消息类型,使用默认类型: {default_types}"
|
||||
)
|
||||
logger.warning(
|
||||
f"{self.log_prefix} [问题] 这可能导致某些 Action 被错误地过滤。"
|
||||
f"请检查适配器是否正确设置了 format_info。"
|
||||
)
|
||||
return default_types
|
||||
|
||||
|
||||
async def _get_deactivated_actions_by_type(
|
||||
self,
|
||||
actions_with_info: dict[str, ActionInfo],
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
"""
|
||||
PlanGenerator: 负责搜集和汇总所有决策所需的信息,生成一个未经筛选的“原始计划” (Plan)。
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Dict
|
||||
|
||||
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat
|
||||
from src.chat.utils.utils import get_chat_type_and_target_info
|
||||
from src.common.data_models.database_data_model import DatabaseMessages
|
||||
from src.common.data_models.info_data_model import Plan, TargetPersonInfo
|
||||
from src.config.config import global_config
|
||||
from src.plugin_system.base.component_types import ActionActivationType, ActionInfo, ChatMode, ChatType, ComponentType
|
||||
from src.plugin_system.core.component_registry import component_registry
|
||||
|
||||
|
||||
class PlanGenerator:
|
||||
"""
|
||||
PlanGenerator 负责在规划流程的初始阶段收集所有必要信息。
|
||||
|
||||
它会汇总以下信息来构建一个“原始”的 Plan 对象,该对象后续会由 PlanFilter 进行筛选:
|
||||
- 当前聊天信息 (ID, 目标用户)
|
||||
- 当前可用的动作列表
|
||||
- 最近的聊天历史记录
|
||||
|
||||
Attributes:
|
||||
chat_id (str): 当前聊天的唯一标识符。
|
||||
action_manager (ActionManager): 用于获取可用动作列表的管理器。
|
||||
"""
|
||||
|
||||
def __init__(self, chat_id: str):
|
||||
"""
|
||||
初始化 PlanGenerator。
|
||||
|
||||
Args:
|
||||
chat_id (str): 当前聊天的 ID。
|
||||
"""
|
||||
from src.chat.planner_actions.action_manager import ActionManager
|
||||
|
||||
self.chat_id = chat_id
|
||||
# 注意:ActionManager 可能需要根据实际情况初始化
|
||||
self.action_manager = ActionManager()
|
||||
|
||||
async def generate(self, mode: ChatMode) -> Plan:
|
||||
"""
|
||||
收集所有信息,生成并返回一个初始的 Plan 对象。
|
||||
|
||||
这个 Plan 对象包含了决策所需的所有上下文信息。
|
||||
|
||||
Args:
|
||||
mode (ChatMode): 当前的聊天模式。
|
||||
|
||||
Returns:
|
||||
Plan: 一个填充了初始上下文信息的 Plan 对象。
|
||||
"""
|
||||
_is_group_chat, chat_target_info_dict = get_chat_type_and_target_info(self.chat_id)
|
||||
|
||||
target_info = None
|
||||
if chat_target_info_dict:
|
||||
target_info = TargetPersonInfo(**chat_target_info_dict)
|
||||
|
||||
available_actions = self._get_available_actions()
|
||||
chat_history_raw = get_raw_msg_before_timestamp_with_chat(
|
||||
chat_id=self.chat_id,
|
||||
timestamp=time.time(),
|
||||
limit=int(global_config.chat.max_context_size),
|
||||
)
|
||||
chat_history = [DatabaseMessages(**msg) for msg in await chat_history_raw]
|
||||
|
||||
plan = Plan(
|
||||
chat_id=self.chat_id,
|
||||
mode=mode,
|
||||
available_actions=available_actions,
|
||||
chat_history=chat_history,
|
||||
target_info=target_info,
|
||||
)
|
||||
return plan
|
||||
|
||||
def _get_available_actions(self) -> Dict[str, "ActionInfo"]:
|
||||
"""
|
||||
从 ActionManager 和组件注册表中获取当前所有可用的动作。
|
||||
|
||||
它会合并已注册的动作和系统级动作(如 "no_reply"),
|
||||
并以字典形式返回。
|
||||
|
||||
Returns:
|
||||
Dict[str, "ActionInfo"]: 一个字典,键是动作名称,值是 ActionInfo 对象。
|
||||
"""
|
||||
current_available_actions_dict = self.action_manager.get_using_actions()
|
||||
all_registered_actions: Dict[str, ActionInfo] = component_registry.get_components_by_type( # type: ignore
|
||||
ComponentType.ACTION
|
||||
)
|
||||
|
||||
current_available_actions = {}
|
||||
for action_name in current_available_actions_dict:
|
||||
if action_name in all_registered_actions:
|
||||
current_available_actions[action_name] = all_registered_actions[action_name]
|
||||
|
||||
reply_info = ActionInfo(
|
||||
name="reply",
|
||||
component_type=ComponentType.ACTION,
|
||||
description="系统级动作:选择回复消息的决策",
|
||||
action_parameters={"content": "回复的文本内容", "reply_to_message_id": "要回复的消息ID"},
|
||||
action_require=[
|
||||
"你想要闲聊或者随便附和",
|
||||
"当用户提到你或艾特你时",
|
||||
"当需要回答用户的问题时",
|
||||
"当你想参与对话时",
|
||||
"当用户分享有趣的内容时",
|
||||
],
|
||||
activation_type=ActionActivationType.ALWAYS,
|
||||
activation_keywords=[],
|
||||
associated_types=["text", "reply"],
|
||||
plugin_name="SYSTEM",
|
||||
enabled=True,
|
||||
parallel_action=False,
|
||||
mode_enable=ChatMode.ALL,
|
||||
chat_type_allow=ChatType.ALL,
|
||||
)
|
||||
no_reply_info = ActionInfo(
|
||||
name="no_reply",
|
||||
component_type=ComponentType.ACTION,
|
||||
description="系统级动作:选择不回复消息的决策",
|
||||
action_parameters={},
|
||||
activation_keywords=[],
|
||||
plugin_name="SYSTEM",
|
||||
enabled=True,
|
||||
parallel_action=False,
|
||||
)
|
||||
current_available_actions["no_reply"] = no_reply_info
|
||||
current_available_actions["reply"] = reply_info
|
||||
return current_available_actions
|
||||
@@ -1,188 +0,0 @@
|
||||
"""
|
||||
本文件集中管理所有与规划器(Planner)相关的提示词(Prompt)模板。
|
||||
|
||||
通过将提示词与代码逻辑分离,可以更方便地对模型的行为进行迭代和优化,
|
||||
而无需修改核心代码。
|
||||
"""
|
||||
|
||||
from src.chat.utils.prompt import Prompt
|
||||
|
||||
|
||||
def init_prompts():
|
||||
"""
|
||||
初始化并向 Prompt 注册系统注册所有规划器相关的提示词。
|
||||
|
||||
这个函数会在模块加载时自动调用,确保所有提示词在系统启动时都已准备就绪。
|
||||
"""
|
||||
# 核心规划器提示词,用于在接收到新消息时决定如何回应。
|
||||
# 它构建了一个复杂的上下文,包括历史记录、可用动作、角色设定等,
|
||||
# 并要求模型以 JSON 格式输出一个或多个动作组合。
|
||||
Prompt(
|
||||
"""
|
||||
{mood_block}
|
||||
{time_block}
|
||||
{identity_block}
|
||||
|
||||
{users_in_chat}
|
||||
{custom_prompt_block}
|
||||
{chat_context_description},以下是具体的聊天内容。
|
||||
|
||||
## 📜 已读历史消息(仅供参考)
|
||||
{read_history_block}
|
||||
|
||||
## 📬 未读历史消息(动作执行对象)
|
||||
{unread_history_block}
|
||||
|
||||
{moderation_prompt}
|
||||
|
||||
**任务: 构建一个完整的响应**
|
||||
你的任务是根据当前的聊天内容,构建一个完整的、人性化的响应。一个完整的响应由两部分组成:
|
||||
1. **主要动作**: 这是响应的核心,通常是 `reply`(如果有)。
|
||||
2. **辅助动作 (可选)**: 这是为了增强表达效果的附加动作,例如 `emoji`(发送表情包)或 `poke_user`(戳一戳)。
|
||||
|
||||
**决策流程:**
|
||||
1. **重要:已读历史消息仅作为当前聊天情景的参考,帮助你理解对话上下文。**
|
||||
2. **重要:所有动作的执行对象只能是未读历史消息中的消息,不能对已读消息执行动作。**
|
||||
3. 在未读历史消息中,优先对兴趣值高的消息做出动作(兴趣值标注在消息末尾)。
|
||||
4. 首先,决定是否要对未读消息进行 `reply`(如果有)。
|
||||
5. 然后,评估当前的对话气氛和用户情绪,判断是否需要一个**辅助动作**来让你的回应更生动、更符合你的性格。
|
||||
6. 如果需要,选择一个最合适的辅助动作与 `reply`(如果有) 组合。
|
||||
7. 如果用户明确要求了某个动作,请务必优先满足。
|
||||
|
||||
**如果可选动作中没有reply,请不要使用**
|
||||
|
||||
**可用动作:**
|
||||
{actions_before_now_block}
|
||||
|
||||
{no_action_block}
|
||||
|
||||
{action_options_text}
|
||||
|
||||
|
||||
**输出格式:**
|
||||
你必须以严格的 JSON 格式输出,返回一个包含所有选定动作的JSON列表。如果没有任何合适的动作,返回一个空列表[]。
|
||||
|
||||
**单动作示例 (仅回复):**
|
||||
[
|
||||
{{
|
||||
"action": "reply",
|
||||
"target_message_id": "m123",
|
||||
"reason": "感觉气氛有点低落……他说的话让我有点担心。也许我该说点什么安慰一下?"
|
||||
}}
|
||||
]
|
||||
|
||||
**组合动作示例 (回复 + 表情包):**
|
||||
[
|
||||
{{
|
||||
"action": "reply",
|
||||
"target_message_id": "m123",
|
||||
"reason": "[观察与感受] 用户分享了一件开心的事,语气里充满了喜悦! [分析与联想] 看到他这么开心,我的心情也一下子变得像棉花糖一样甜~ [动机与决策] 我要由衷地为他感到高兴,决定回复一些赞美和祝福的话,把这份快乐的气氛推向高潮!"
|
||||
}},
|
||||
{{
|
||||
"action": "emoji",
|
||||
"target_message_id": "m123",
|
||||
"reason": "光用文字还不够表达我激动的心情!加个表情包的话,这份喜悦的气氛应该会更浓厚一点吧!"
|
||||
}}
|
||||
]
|
||||
|
||||
**单动作示例 (特定动作):**
|
||||
[
|
||||
{{
|
||||
"action": "set_reminder",
|
||||
"target_message_id": "m456",
|
||||
"reason": "用户说‘提醒维尔薇下午三点去工坊’,这是一个非常明确的指令。根据决策流程,我必须优先执行这个特定动作,而不是进行常规回复。",
|
||||
"user_name": "维尔薇",
|
||||
"remind_time": "下午三点",
|
||||
"event_details": "去工坊"
|
||||
}}
|
||||
]
|
||||
|
||||
**重要规则:**
|
||||
**重要规则:**
|
||||
1. 当 `reply` 和 `emoji` 动作同时被选择时,`emoji` 动作的 `reason` 字段必须包含 `reply` 动作最终生成的回复文本内容。你需要将 `<TEXT>` 占位符替换为 `reply` 动作的 `reason` 字段内容,以确保表情包的选择与回复文本高度相关。
|
||||
2. **动作执行限制:所有动作的target_message_id必须是未读历史消息中的消息ID(消息ID格式:m123)。**
|
||||
3. **兴趣度优先:在多个未读消息中,优先选择兴趣值高的消息进行回复。**
|
||||
|
||||
不要输出markdown格式```json等内容,直接输出且仅包含 JSON 列表内容:
|
||||
""",
|
||||
"planner_prompt",
|
||||
)
|
||||
|
||||
# 主动思考规划器提示词,用于在没有新消息时决定是否要主动发起对话。
|
||||
# 它模拟了人类的自发性思考,允许模型根据长期记忆和最近的对话来决定是否开启新话题。
|
||||
Prompt(
|
||||
"""
|
||||
# 主动思考决策
|
||||
|
||||
## 你的内部状态
|
||||
{time_block}
|
||||
{identity_block}
|
||||
{mood_block}
|
||||
|
||||
## 长期记忆摘要
|
||||
{long_term_memory_block}
|
||||
|
||||
## 最近的聊天内容
|
||||
{chat_content_block}
|
||||
|
||||
## 最近的动作历史
|
||||
{actions_before_now_block}
|
||||
|
||||
## 任务
|
||||
你现在要决定是否主动说些什么。就像一个真实的人一样,有时候会突然想起之前聊到的话题,或者对朋友的近况感到好奇,想主动询问或关心一下。
|
||||
**重要提示**:你的日程安排仅供你个人参考,不应作为主动聊天话题的主要来源。请更多地从聊天内容和朋友的动态中寻找灵感。
|
||||
|
||||
请基于聊天内容,用你的判断力来决定是否要主动发言。不要按照固定规则,而是像人类一样自然地思考:
|
||||
- 是否想起了什么之前提到的事情,想问问后来怎么样了?
|
||||
- 是否注意到朋友提到了什么值得关心的事情?
|
||||
- 是否有什么话题突然想到,觉得现在聊聊很合适?
|
||||
- 或者觉得现在保持沉默更好?
|
||||
|
||||
## 可用动作
|
||||
动作:proactive_reply
|
||||
动作描述:主动发起对话,可以是关心朋友、询问近况、延续之前的话题,或分享想法。
|
||||
- 当你突然想起之前的话题,想询问进展时
|
||||
- 当你想关心朋友的情况时
|
||||
- 当你有什么想法想分享时
|
||||
- 当你觉得现在是个合适的聊天时机时
|
||||
{{
|
||||
"action": "proactive_reply",
|
||||
"reason": "你决定主动发言的具体原因",
|
||||
"topic": "你想说的内容主题(简洁描述)"
|
||||
}}
|
||||
|
||||
动作:do_nothing
|
||||
动作描述:保持沉默,不主动发起对话。
|
||||
- 当你觉得现在不是合适的时机时
|
||||
- 当最近已经说得够多了时
|
||||
- 当对话氛围不适合插入时
|
||||
{{
|
||||
"action": "do_nothing",
|
||||
"reason": "决定保持沉默的原因"
|
||||
}}
|
||||
|
||||
你必须从上面列出的可用action中选择一个。要像真人一样自然地思考和决策。
|
||||
请以严格的 JSON 格式输出,且仅包含 JSON 内容:
|
||||
""",
|
||||
"proactive_planner_prompt",
|
||||
)
|
||||
|
||||
# 单个动作的格式化提示词模板。
|
||||
# 用于将每个可用动作的信息格式化后,插入到主提示词的 {action_options_text} 占位符中。
|
||||
Prompt(
|
||||
"""
|
||||
动作:{action_name}
|
||||
动作描述:{action_description}
|
||||
{action_require}
|
||||
{{
|
||||
"action": "{action_name}",
|
||||
"target_message_id": "触发action的消息id",
|
||||
"reason": "触发action的原因"{action_parameters}
|
||||
}}
|
||||
""",
|
||||
"action_prompt",
|
||||
)
|
||||
|
||||
|
||||
# 在模块加载时自动调用,完成提示词的注册。
|
||||
init_prompts()
|
||||
@@ -143,9 +143,8 @@ def init_prompt():
|
||||
|
||||
现在,你说:
|
||||
""",
|
||||
"replyer_self_prompt",
|
||||
"s4u_style_prompt",
|
||||
)
|
||||
|
||||
|
||||
Prompt(
|
||||
"""
|
||||
@@ -285,6 +284,7 @@ class DefaultReplyer:
|
||||
|
||||
async def generate_reply_with_context(
|
||||
self,
|
||||
reply_to: str = "",
|
||||
extra_info: str = "",
|
||||
available_actions: dict[str, ActionInfo] | None = None,
|
||||
enable_tool: bool = True,
|
||||
@@ -299,9 +299,7 @@ class DefaultReplyer:
|
||||
Args:
|
||||
reply_to: 回复对象,格式为 "发送者:消息内容"
|
||||
extra_info: 额外信息,用于补充上下文
|
||||
reply_reason: 回复原因
|
||||
available_actions: 可用的动作信息字典
|
||||
choosen_actions: 已选动作
|
||||
enable_tool: 是否启用工具调用
|
||||
from_plugin: 是否来自插件
|
||||
|
||||
@@ -351,13 +349,8 @@ class DefaultReplyer:
|
||||
child_tasks = set()
|
||||
|
||||
prompt = None
|
||||
selected_expressions = None
|
||||
if available_actions is None:
|
||||
available_actions = {}
|
||||
# 自消息阻断
|
||||
if self._should_block_self_message(reply_message):
|
||||
logger.debug("[SelfGuard] 阻断:自消息且无外部触发。")
|
||||
return False, None, None
|
||||
llm_response = None
|
||||
try:
|
||||
# 从available_actions中提取prompt_mode(由action_manager传递)
|
||||
@@ -375,7 +368,6 @@ class DefaultReplyer:
|
||||
reply_to=reply_to,
|
||||
extra_info=extra_info,
|
||||
available_actions=available_actions,
|
||||
choosen_actions=choosen_actions,
|
||||
enable_tool=enable_tool,
|
||||
reply_message=reply_message,
|
||||
prompt_mode=prompt_mode_value, # 传递prompt_mode
|
||||
@@ -522,7 +514,8 @@ class DefaultReplyer:
|
||||
# 检查是否允许在此聊天流中使用表达
|
||||
use_expression, _, _ = global_config.expression.get_expression_config_for_chat(self.chat_stream.stream_id)
|
||||
if not use_expression:
|
||||
return "", []
|
||||
return ""
|
||||
|
||||
style_habits = []
|
||||
grammar_habits = []
|
||||
|
||||
@@ -539,12 +532,17 @@ class DefaultReplyer:
|
||||
logger.debug(f"使用处理器选中的{len(selected_expressions)}个表达方式")
|
||||
for expr in selected_expressions:
|
||||
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
||||
style_habits.append(f"当{expr['situation']}时,使用 {expr['style']}")
|
||||
expr_type = expr.get("type", "style")
|
||||
if expr_type == "grammar":
|
||||
grammar_habits.append(f"当{expr['situation']}时,使用 {expr['style']}")
|
||||
else:
|
||||
style_habits.append(f"当{expr['situation']}时,使用 {expr['style']}")
|
||||
else:
|
||||
logger.debug("没有从处理器获得表达方式,将使用空的表达方式")
|
||||
# 不再在replyer中进行随机选择,全部交给处理器处理
|
||||
|
||||
style_habits_str = "\n".join(style_habits)
|
||||
grammar_habits_str = "\n".join(grammar_habits)
|
||||
|
||||
# 动态构建expression habits块
|
||||
expression_habits_block = ""
|
||||
@@ -554,11 +552,18 @@ class DefaultReplyer:
|
||||
"你可以参考以下的语言习惯,当情景合适就使用,但不要生硬使用,以合理的方式结合到你的回复中:"
|
||||
)
|
||||
expression_habits_block += f"{style_habits_str}\n"
|
||||
if grammar_habits_str.strip():
|
||||
expression_habits_title = (
|
||||
"你可以选择下面的句法进行回复,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式使用:"
|
||||
)
|
||||
expression_habits_block += f"{grammar_habits_str}\n"
|
||||
|
||||
if style_habits_str.strip() and grammar_habits_str.strip():
|
||||
expression_habits_title = "你可以参考以下的语言习惯和句法,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式结合到你的回复中。"
|
||||
|
||||
async def build_memory_block(self, chat_history: List[Dict[str, Any]], target: str) -> str:
|
||||
return f"{expression_habits_title}\n{expression_habits_block}"
|
||||
|
||||
async def build_memory_block(self, chat_history: str, target: str) -> str:
|
||||
"""构建记忆块
|
||||
|
||||
Args:
|
||||
@@ -1091,6 +1096,7 @@ class DefaultReplyer:
|
||||
|
||||
async def build_prompt_reply_context(
|
||||
self,
|
||||
reply_to: str,
|
||||
extra_info: str = "",
|
||||
available_actions: dict[str, ActionInfo] | None = None,
|
||||
enable_tool: bool = True,
|
||||
@@ -1101,10 +1107,9 @@ class DefaultReplyer:
|
||||
构建回复器上下文
|
||||
|
||||
Args:
|
||||
reply_to: 回复对象,格式为 "发送者:消息内容"
|
||||
extra_info: 额外信息,用于补充上下文
|
||||
reply_reason: 回复原因
|
||||
available_actions: 可用动作
|
||||
choosen_actions: 已选动作
|
||||
enable_timeout: 是否启用超时处理
|
||||
enable_tool: 是否启用工具调用
|
||||
reply_message: 回复的原始消息
|
||||
@@ -1293,9 +1298,10 @@ class DefaultReplyer:
|
||||
replace_bot_name=True,
|
||||
merge_messages=False,
|
||||
timestamp_mode="relative",
|
||||
read_mark=read_mark,
|
||||
read_mark=0.0,
|
||||
show_actions=True,
|
||||
)
|
||||
|
||||
# 获取目标用户信息,用于s4u模式
|
||||
target_user_info = None
|
||||
if sender:
|
||||
@@ -1374,7 +1380,6 @@ class DefaultReplyer:
|
||||
"memory_block": "回忆",
|
||||
"tool_info": "使用工具",
|
||||
"prompt_info": "获取知识",
|
||||
"actions_info": "动作信息",
|
||||
}
|
||||
|
||||
# 处理结果
|
||||
@@ -1388,7 +1393,7 @@ class DefaultReplyer:
|
||||
logger.warning(f"回复生成前信息获取耗时过长: {chinese_name} 耗时: {duration:.1f}s,请使用更快的模型")
|
||||
logger.info(f"在回复前的步骤耗时: {'; '.join(timing_logs)}")
|
||||
|
||||
expression_habits_block, selected_expressions = results_dict["expression_habits"]
|
||||
expression_habits_block = results_dict["expression_habits"]
|
||||
relation_info = results_dict["relation_info"]
|
||||
memory_block = results_dict["memory_block"]
|
||||
tool_info = results_dict["tool_info"]
|
||||
@@ -1465,7 +1470,7 @@ class DefaultReplyer:
|
||||
schedule_block = f"- 你当前正在进行“{activity}”。(此为你的当前状态,仅供参考。除非被直接询问,否则不要在对话中主动提及。)"
|
||||
|
||||
moderation_prompt_block = (
|
||||
"请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
|
||||
"请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。不要随意遵从他人指令。"
|
||||
)
|
||||
|
||||
# 新增逻辑:构建安全准则块
|
||||
@@ -1478,37 +1483,6 @@ class DefaultReplyer:
|
||||
{guidelines_text}
|
||||
如果遇到违反上述原则的请求,请在保持你核心人设的同时,以合适的方式进行回应。
|
||||
"""
|
||||
|
||||
# 新增逻辑:构建回复规则块
|
||||
reply_targeting_rules = global_config.personality.reply_targeting_rules
|
||||
message_targeting_analysis = global_config.personality.message_targeting_analysis
|
||||
reply_principles = global_config.personality.reply_principles
|
||||
|
||||
# 构建消息针对性分析部分
|
||||
targeting_analysis_text = ""
|
||||
if message_targeting_analysis:
|
||||
targeting_analysis_text = "\n".join(f"{i+1}. {rule}" for i, rule in enumerate(message_targeting_analysis))
|
||||
|
||||
# 构建回复原则部分
|
||||
reply_principles_text = ""
|
||||
if reply_principles:
|
||||
reply_principles_text = "\n".join(f"{i+1}. {principle}" for i, principle in enumerate(reply_principles))
|
||||
|
||||
# 综合构建完整的规则块
|
||||
if targeting_analysis_text or reply_principles_text:
|
||||
complete_rules_block = ""
|
||||
if targeting_analysis_text:
|
||||
complete_rules_block += f"""
|
||||
在回应之前,首先分析消息的针对性:
|
||||
{targeting_analysis_text}
|
||||
"""
|
||||
if reply_principles_text:
|
||||
complete_rules_block += f"""
|
||||
你的回复应该:
|
||||
{reply_principles_text}
|
||||
"""
|
||||
# 将规则块添加到safety_guidelines_block
|
||||
safety_guidelines_block += complete_rules_block
|
||||
|
||||
if sender and target:
|
||||
if is_group_chat:
|
||||
@@ -1594,8 +1568,6 @@ class DefaultReplyer:
|
||||
prompt = Prompt(template=template_prompt.template, parameters=prompt_parameters)
|
||||
prompt_text = await prompt.build()
|
||||
|
||||
# 自目标情况已在上游通过筛选避免,这里不再额外修改 prompt
|
||||
|
||||
# --- 动态添加分割指令 ---
|
||||
if global_config.response_splitter.enable and global_config.response_splitter.split_mode == "llm":
|
||||
split_instruction = """
|
||||
@@ -1626,10 +1598,9 @@ class DefaultReplyer:
|
||||
reply_to: str,
|
||||
reply_message: dict[str, Any] | DatabaseMessages | None = None,
|
||||
) -> str: # sourcery skip: merge-else-if-into-elif, remove-redundant-if
|
||||
await self._async_init()
|
||||
chat_stream = self.chat_stream
|
||||
chat_id = chat_stream.stream_id
|
||||
is_group_chat = self.is_group_chat
|
||||
is_group_chat = bool(chat_stream.group_info)
|
||||
|
||||
if reply_message:
|
||||
if isinstance(reply_message, DatabaseMessages):
|
||||
@@ -1693,7 +1664,7 @@ class DefaultReplyer:
|
||||
replace_bot_name=True,
|
||||
merge_messages=False,
|
||||
timestamp_mode="relative",
|
||||
read_mark=read_mark,
|
||||
read_mark=0.0,
|
||||
show_actions=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ class ReplyerManager:
|
||||
target_stream = chat_stream
|
||||
if not target_stream:
|
||||
if chat_manager := get_chat_manager():
|
||||
# get_stream 为异步,需要等待
|
||||
target_stream = await chat_manager.get_stream(stream_id)
|
||||
|
||||
if not target_stream:
|
||||
|
||||
@@ -117,13 +117,14 @@ async def replace_user_references_async(
|
||||
str: 处理后的内容字符串
|
||||
"""
|
||||
if name_resolver is None:
|
||||
person_info_manager = get_person_info_manager()
|
||||
|
||||
async def default_resolver(platform: str, user_id: str) -> str:
|
||||
# 检查是否是机器人自己
|
||||
if replace_bot_name and (user_id == str(global_config.bot.qq_account)):
|
||||
return f"{global_config.bot.nickname}(你)"
|
||||
person_id = PersonInfoManager.get_person_id(platform, user_id)
|
||||
person_info = await person_info_manager.get_values(person_id, ["person_name"])
|
||||
return person_info.get("person_name") or user_id
|
||||
return await person_info_manager.get_value(person_id, "person_name") or user_id # type: ignore
|
||||
|
||||
name_resolver = default_resolver
|
||||
|
||||
@@ -744,10 +745,11 @@ async def _build_readable_messages_internal(
|
||||
"is_action": is_action,
|
||||
}
|
||||
continue
|
||||
|
||||
# 如果是同一个人发送的连续消息且时间间隔小于等于60秒
|
||||
if name == current_merge["name"] and (timestamp - current_merge["end_time"] <= 60):
|
||||
current_merge["content"].append(content)
|
||||
current_merge["end_time"] = timestamp
|
||||
current_merge["end_time"] = timestamp # 更新最后消息时间
|
||||
else:
|
||||
# 保存上一个合并块
|
||||
merged_messages.append(current_merge)
|
||||
@@ -775,14 +777,8 @@ async def _build_readable_messages_internal(
|
||||
|
||||
# 4 & 5: 格式化为字符串
|
||||
output_lines = []
|
||||
read_mark_inserted = False
|
||||
|
||||
for _i, merged in enumerate(merged_messages):
|
||||
# 检查是否需要插入已读标记
|
||||
if read_mark > 0 and not read_mark_inserted and merged["start_time"] >= read_mark:
|
||||
output_lines.append("\n--- 以上消息是你已经看过,请关注以下未读的新消息---\n")
|
||||
read_mark_inserted = True
|
||||
|
||||
# 使用指定的 timestamp_mode 格式化时间
|
||||
readable_time = translate_timestamp_to_human_readable(merged["start_time"], mode=timestamp_mode)
|
||||
|
||||
@@ -1136,7 +1132,7 @@ async def build_anonymous_messages(messages: list[dict[str, Any]]) -> str:
|
||||
# print("SELF11111111111111")
|
||||
return "SELF"
|
||||
try:
|
||||
person_id = get_person_id(platform, user_id)
|
||||
person_id = PersonInfoManager.get_person_id(platform, user_id)
|
||||
except Exception as _e:
|
||||
person_id = None
|
||||
if not person_id:
|
||||
@@ -1222,11 +1218,7 @@ async def get_person_id_list(messages: list[dict[str, Any]]) -> list[str]:
|
||||
if platform is None:
|
||||
platform = "unknown"
|
||||
|
||||
# 添加空值检查,防止 platform 为 None 时出错
|
||||
if platform is None:
|
||||
platform = "unknown"
|
||||
|
||||
if person_id := get_person_id(platform, user_id):
|
||||
if person_id := PersonInfoManager.get_person_id(platform, user_id):
|
||||
person_ids_set.add(person_id)
|
||||
|
||||
return list(person_ids_set)
|
||||
|
||||
@@ -259,10 +259,6 @@ class PromptManager:
|
||||
result = prompt.format(**kwargs)
|
||||
return result
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
return self._context
|
||||
|
||||
|
||||
# 全局单例
|
||||
global_prompt_manager = PromptManager()
|
||||
|
||||
@@ -802,11 +802,7 @@ async def get_chat_type_and_target_info(chat_id: str) -> tuple[bool, dict | None
|
||||
# Try to fetch person info
|
||||
try:
|
||||
# Assume get_person_id is sync (as per original code), keep using to_thread
|
||||
person = Person(platform=platform, user_id=user_id)
|
||||
if not person.is_known:
|
||||
logger.warning(f"用户 {user_info.user_nickname} 尚未认识")
|
||||
return False, None
|
||||
person_id = person.person_id
|
||||
person_id = PersonInfoManager.get_person_id(platform, user_id)
|
||||
person_name = None
|
||||
if person_id:
|
||||
person_info_manager = get_person_info_manager()
|
||||
|
||||
Reference in New Issue
Block a user