feat:精简处理器上下文

This commit is contained in:
SengokuCola
2025-06-23 00:39:13 +08:00
parent 2128eb6bf2
commit 3cb60b03b4
10 changed files with 159 additions and 90 deletions

View File

@@ -660,6 +660,7 @@ class HeartFChatting:
} }
with Timer("执行动作", cycle_timers): with Timer("执行动作", cycle_timers):
action_type, action_data, reasoning = ( action_type, action_data, reasoning = (
plan_result.get("action_result", {}).get("action_type", "error"), plan_result.get("action_result", {}).get("action_type", "error"),
plan_result.get("action_result", {}).get("action_data", {}), plan_result.get("action_result", {}).get("action_data", {}),

View File

@@ -20,7 +20,7 @@ class HFCVersionManager:
"""HFC版本号管理器""" """HFC版本号管理器"""
# 默认版本号 # 默认版本号
DEFAULT_VERSION = "v1.0.0" DEFAULT_VERSION = "v2.0.2"
# 当前运行时版本号 # 当前运行时版本号
_current_version: Optional[str] = None _current_version: Optional[str] = None

View File

@@ -16,6 +16,8 @@ class ObsInfo(InfoBase):
Data Fields: Data Fields:
talking_message (str): 说话消息内容 talking_message (str): 说话消息内容
talking_message_str_truncate (str): 截断后的说话消息内容 talking_message_str_truncate (str): 截断后的说话消息内容
talking_message_str_short (str): 简短版本的说话消息内容(使用最新一半消息)
talking_message_str_truncate_short (str): 截断简短版本的说话消息内容(使用最新一半消息)
chat_type (str): 聊天类型,可以是 "private"(私聊)、"group"(群聊)或 "other"(其他) chat_type (str): 聊天类型,可以是 "private"(私聊)、"group"(群聊)或 "other"(其他)
""" """
@@ -37,6 +39,22 @@ class ObsInfo(InfoBase):
""" """
self.data["talking_message_str_truncate"] = message self.data["talking_message_str_truncate"] = message
def set_talking_message_str_short(self, message: str) -> None:
"""设置简短版本的说话消息
Args:
message (str): 简短版本的说话消息内容
"""
self.data["talking_message_str_short"] = message
def set_talking_message_str_truncate_short(self, message: str) -> None:
"""设置截断简短版本的说话消息
Args:
message (str): 截断简短版本的说话消息内容
"""
self.data["talking_message_str_truncate_short"] = message
def set_previous_chat_info(self, message: str) -> None: def set_previous_chat_info(self, message: str) -> None:
"""设置之前聊天信息 """设置之前聊天信息
@@ -63,6 +81,22 @@ class ObsInfo(InfoBase):
""" """
self.data["chat_target"] = chat_target self.data["chat_target"] = chat_target
def set_chat_id(self, chat_id: str) -> None:
"""设置聊天ID
Args:
chat_id (str): 聊天ID
"""
self.data["chat_id"] = chat_id
def get_chat_id(self) -> Optional[str]:
"""获取聊天ID
Returns:
Optional[str]: 聊天ID如果未设置则返回 None
"""
return self.get_info("chat_id")
def get_talking_message(self) -> Optional[str]: def get_talking_message(self) -> Optional[str]:
"""获取说话消息 """获取说话消息
@@ -79,6 +113,22 @@ class ObsInfo(InfoBase):
""" """
return self.get_info("talking_message_str_truncate") return self.get_info("talking_message_str_truncate")
def get_talking_message_str_short(self) -> Optional[str]:
"""获取简短版本的说话消息
Returns:
Optional[str]: 简短版本的说话消息内容,如果未设置则返回 None
"""
return self.get_info("talking_message_str_short")
def get_talking_message_str_truncate_short(self) -> Optional[str]:
"""获取截断简短版本的说话消息
Returns:
Optional[str]: 截断简短版本的说话消息内容,如果未设置则返回 None
"""
return self.get_info("talking_message_str_truncate_short")
def get_chat_type(self) -> str: def get_chat_type(self) -> str:
"""获取聊天类型 """获取聊天类型

View File

@@ -62,6 +62,10 @@ class ChattingInfoProcessor(BaseProcessor):
# 改为异步任务,不阻塞主流程 # 改为异步任务,不阻塞主流程
# asyncio.create_task(self.chat_compress(obs)) # asyncio.create_task(self.chat_compress(obs))
# 设置聊天ID
if hasattr(obs, "chat_id"):
obs_info.set_chat_id(obs.chat_id)
# 设置说话消息 # 设置说话消息
if hasattr(obs, "talking_message_str"): if hasattr(obs, "talking_message_str"):
# print(f"设置说话消息obs.talking_message_str: {obs.talking_message_str}") # print(f"设置说话消息obs.talking_message_str: {obs.talking_message_str}")
@@ -72,6 +76,14 @@ class ChattingInfoProcessor(BaseProcessor):
# print(f"设置截断后的说话消息obs.talking_message_str_truncate: {obs.talking_message_str_truncate}") # print(f"设置截断后的说话消息obs.talking_message_str_truncate: {obs.talking_message_str_truncate}")
obs_info.set_talking_message_str_truncate(obs.talking_message_str_truncate) obs_info.set_talking_message_str_truncate(obs.talking_message_str_truncate)
# 设置简短版本的说话消息
if hasattr(obs, "talking_message_str_short"):
obs_info.set_talking_message_str_short(obs.talking_message_str_short)
# 设置截断简短版本的说话消息
if hasattr(obs, "talking_message_str_truncate_short"):
obs_info.set_talking_message_str_truncate_short(obs.talking_message_str_truncate_short)
if hasattr(obs, "mid_memory_info"): if hasattr(obs, "mid_memory_info"):
# print(f"设置之前聊天信息obs.mid_memory_info: {obs.mid_memory_info}") # print(f"设置之前聊天信息obs.mid_memory_info: {obs.mid_memory_info}")
obs_info.set_previous_chat_info(obs.mid_memory_info) obs_info.set_previous_chat_info(obs.mid_memory_info)
@@ -82,7 +94,8 @@ class ChattingInfoProcessor(BaseProcessor):
chat_type = "group" chat_type = "group"
else: else:
chat_type = "private" chat_type = "private"
obs_info.set_chat_target(obs.chat_target_info.get("person_name", "某人")) if hasattr(obs, "chat_target_info") and obs.chat_target_info:
obs_info.set_chat_target(obs.chat_target_info.get("person_name", "某人"))
obs_info.set_chat_type(chat_type) obs_info.set_chat_type(chat_type)
# logger.debug(f"聊天信息处理器处理后的信息: {obs_info}") # logger.debug(f"聊天信息处理器处理后的信息: {obs_info}")

View File

@@ -149,7 +149,8 @@ class ExpressionSelectorProcessor(BaseProcessor):
if observations: if observations:
for observation in observations: for observation in observations:
if isinstance(observation, ChattingObservation): if isinstance(observation, ChattingObservation):
chat_info = observation.get_observe_info() # chat_info = observation.get_observe_info()
chat_info = observation.talking_message_str_truncate_short
break break
if not chat_info: if not chat_info:

View File

@@ -144,7 +144,7 @@ def init_prompt():
fetch_info_prompt = """ fetch_info_prompt = """
{name_block} {name_block}
以下是你对{person_name}的了解,请你从中提取用户的有关"{info_type}"的信息如果用户没有相关信息请输出none 以下是你在之前与{person_name}的交流中,产生的{person_name}的了解,请你从中提取用户的有关"{info_type}"的信息如果用户没有相关信息请输出none
{person_impression_block} {person_impression_block}
{points_text_block} {points_text_block}
请严格按照以下json输出格式不要输出多余内容 请严格按照以下json输出格式不要输出多余内容
@@ -547,8 +547,7 @@ class PersonImpressionpProcessor(BaseProcessor):
for observation in observations: for observation in observations:
if isinstance(observation, ChattingObservation): if isinstance(observation, ChattingObservation):
chat_observe_info = observation.get_observe_info() chat_observe_info = observation.get_observe_info()
chat_observe_info = chat_observe_info[-300:] # latest_message_time = observation.last_observe_time
# 从聊天观察中提取用户信息并更新消息段 # 从聊天观察中提取用户信息并更新消息段
# 获取最新的非bot消息来更新消息段 # 获取最新的非bot消息来更新消息段
latest_messages = get_raw_msg_by_timestamp_with_chat( latest_messages = get_raw_msg_by_timestamp_with_chat(

View File

@@ -112,7 +112,8 @@ class ToolProcessor(BaseProcessor):
is_group_chat = observation.is_group_chat is_group_chat = observation.is_group_chat
chat_observe_info = observation.get_observe_info() # chat_observe_info = observation.get_observe_info()
chat_observe_info = observation.talking_message_str_truncate_short
# person_list = observation.person_list # person_list = observation.person_list
# 获取时间信息 # 获取时间信息

View File

@@ -94,7 +94,7 @@ class MemoryActivator:
obs_info_text = "" obs_info_text = ""
for observation in observations: for observation in observations:
if isinstance(observation, ChattingObservation): if isinstance(observation, ChattingObservation):
obs_info_text += observation.get_observe_info() obs_info_text += observation.talking_message_str_truncate_short
elif isinstance(observation, StructureObservation): elif isinstance(observation, StructureObservation):
working_info = observation.get_observe_info() working_info = observation.get_observe_info()
for working_info_item in working_info: for working_info_item in working_info:

View File

@@ -63,6 +63,8 @@ class ChattingObservation(Observation):
self.talking_message = [] self.talking_message = []
self.talking_message_str = "" self.talking_message_str = ""
self.talking_message_str_truncate = "" self.talking_message_str_truncate = ""
self.talking_message_str_short = ""
self.talking_message_str_truncate_short = ""
self.name = global_config.bot.nickname self.name = global_config.bot.nickname
self.nick_name = global_config.bot.alias_names self.nick_name = global_config.bot.alias_names
self.max_now_obs_len = global_config.focus_chat.observation_context_size self.max_now_obs_len = global_config.focus_chat.observation_context_size
@@ -88,6 +90,8 @@ class ChattingObservation(Observation):
"chat_target_info": self.chat_target_info, "chat_target_info": self.chat_target_info,
"talking_message_str": self.talking_message_str, "talking_message_str": self.talking_message_str,
"talking_message_str_truncate": self.talking_message_str_truncate, "talking_message_str_truncate": self.talking_message_str_truncate,
"talking_message_str_short": self.talking_message_str_short,
"talking_message_str_truncate_short": self.talking_message_str_truncate_short,
"name": self.name, "name": self.name,
"nick_name": self.nick_name, "nick_name": self.nick_name,
"last_observe_time": self.last_observe_time, "last_observe_time": self.last_observe_time,
@@ -229,6 +233,24 @@ class ChattingObservation(Observation):
show_actions=True, show_actions=True,
) )
# 构建简短版本 - 使用最新一半的消息
half_count = len(self.talking_message) // 2
recent_messages = self.talking_message[-half_count:] if half_count > 0 else self.talking_message
self.talking_message_str_short = build_readable_messages(
messages=recent_messages,
timestamp_mode="lite",
read_mark=last_obs_time_mark,
show_actions=True,
)
self.talking_message_str_truncate_short = build_readable_messages(
messages=recent_messages,
timestamp_mode="normal_no_YMD",
read_mark=last_obs_time_mark,
truncate=True,
show_actions=True,
)
self.person_list = await get_person_id_list(self.talking_message) self.person_list = await get_person_id_list(self.talking_message)
# logger.debug( # logger.debug(

View File

@@ -55,6 +55,8 @@ class ReplyAction(BaseAction):
start_time = self.action_data.get("loop_start_time", time.time()) start_time = self.action_data.get("loop_start_time", time.time())
try: try:
success, reply_set = await generator_api.generate_reply( success, reply_set = await generator_api.generate_reply(
chat_stream=self.chat_stream, chat_stream=self.chat_stream,
@@ -64,6 +66,7 @@ class ReplyAction(BaseAction):
is_group=self.is_group, is_group=self.is_group,
) )
# 检查从start_time以来的新消息数量 # 检查从start_time以来的新消息数量
# 获取动作触发时间或使用默认值 # 获取动作触发时间或使用默认值
current_time = time.time() current_time = time.time()
@@ -79,16 +82,21 @@ class ReplyAction(BaseAction):
# 构建回复文本 # 构建回复文本
reply_text = "" reply_text = ""
first_reply = False first_replyed = False
for reply_seg in reply_set: for reply_seg in reply_set:
data = reply_seg[1] data = reply_seg[1]
if not first_reply and need_reply: if not first_replyed:
await self.send_text(content=data, reply_to=self.action_data.get("reply_to", "")) if need_reply:
await self.send_text(content=data, reply_to=self.action_data.get("reply_to", ""),typing=False)
first_replyed = True
else:
await self.send_text(content=data,typing=False)
first_replyed = True
else: else:
await self.send_text(content=data) await self.send_text(content=data,typing=True)
first_reply = True
reply_text += data reply_text += data
# 存储动作记录 # 存储动作记录
await self.store_action_info( await self.store_action_info(
action_build_into_prompt=False, action_build_into_prompt=False,
@@ -119,16 +127,17 @@ class NoReplyAction(BaseAction):
action_name = "no_reply" action_name = "no_reply"
action_description = "暂时不回复消息" action_description = "暂时不回复消息"
# 默认超时时间,将由插件在注册时设置
waiting_timeout = 1200
# 连续no_reply计数器 # 连续no_reply计数器
_consecutive_count = 0 _consecutive_count = 0
# random_activation_probability = 0.2 # 概率判定时间点
_probability_check_time = 15 # 15秒时进行概率判定
# 分级等待时间 # 概率判定通过的概率(通过则结束动作)
_waiting_stages = [10, 60, 600] # 第1、2、3次的等待时间 _end_probability = 0.5 # 50%概率结束
# 最大等待超时时间
_max_timeout = 1200 # 1200秒
# 动作参数定义 # 动作参数定义
action_parameters = {"reason": "不回复的原因"} action_parameters = {"reason": "不回复的原因"}
@@ -140,7 +149,9 @@ class NoReplyAction(BaseAction):
associated_types = [] associated_types = []
async def execute(self) -> Tuple[bool, str]: async def execute(self) -> Tuple[bool, str]:
"""执行不回复动作,等待新消息或超时""" """执行不回复动作,在15秒时进行概率判定决定是否继续等待"""
import random
try: try:
# 增加连续计数 # 增加连续计数
NoReplyAction._consecutive_count += 1 NoReplyAction._consecutive_count += 1
@@ -148,25 +159,32 @@ class NoReplyAction(BaseAction):
reason = self.action_data.get("reason", "") reason = self.action_data.get("reason", "")
# 计算本次等待时间 logger.info(f"{self.log_prefix} 选择不回复(第{count}次),开始等待新消息,原因: {reason}")
if count <= len(self._waiting_stages):
# 前3次使用预设时间 # 先等待到概率判定时间点15秒
stage_time = self._waiting_stages[count - 1] logger.info(f"{self.log_prefix} 等待{self._probability_check_time}秒后进行概率判定...")
# 如果WAITING_TIME_THRESHOLD更小则使用它
timeout = min(stage_time, self.waiting_timeout) # 等待15秒或有新消息
result = await self.wait_for_new_message(self._probability_check_time)
# 如果在15秒内有新消息直接返回
if result[0]: # 有新消息
logger.info(f"{self.log_prefix}{self._probability_check_time}秒内收到新消息,结束等待")
return result
# 15秒后进行概率判定
if random.random() < self._end_probability:
# 概率判定通过,结束动作
logger.info(f"{self.log_prefix} 概率判定通过({self._end_probability * 100}%),结束不回复动作")
return True, "概率判定通过,结束等待"
else: else:
# 第4次及以后使用WAITING_TIME_THRESHOLD # 概率判定不通过,继续等待直到最大超时时间
timeout = self.waiting_timeout remaining_time = self._max_timeout - self._probability_check_time
logger.info(f"{self.log_prefix} 概率判定不通过,继续等待{remaining_time}秒直到超时或有新消息...")
logger.info( # 继续等待剩余时间
f"{self.log_prefix} 选择不回复(第{count}次连续),等待新消息中... (超时: {timeout}秒),原因: {reason}" result = await self.wait_for_new_message(remaining_time)
) return result
# 等待新消息或达到时间上限
result = await self.wait_for_new_message(timeout)
# 如果有新消息或者超时都不重置计数器因为可能还会继续no_reply
return result
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}") logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}")
@@ -245,41 +263,6 @@ class EmojiAction(BaseAction):
return False, f"表情发送失败: {str(e)}" return False, f"表情发送失败: {str(e)}"
class ChangeToFocusChatAction(BaseAction):
"""切换到专注聊天动作 - 从普通模式切换到专注模式"""
focus_activation_type = ActionActivationType.NEVER
normal_activation_type = ActionActivationType.NEVER
mode_enable = ChatMode.NORMAL
parallel_action = False
# 动作基本信息
action_name = "change_to_focus_chat"
action_description = "切换到专注聊天,从普通模式切换到专注模式"
# 动作参数定义
action_parameters = {}
apex = 111
# 动作使用场景
action_require = [
"你想要进入专注聊天模式",
"聊天上下文中自己的回复条数较多超过3-4条",
"对话进行得非常热烈活跃",
"用户表现出深入交流的意图",
"话题需要更专注和深入的讨论",
]
async def execute(self) -> Tuple[bool, str]:
"""执行切换到专注聊天动作"""
logger.info(f"{self.log_prefix} 决定切换到专注聊天: {self.reasoning}")
# 重置NoReplyAction的连续计数器
NoReplyAction.reset_consecutive_count()
# 这里只做决策标记,具体切换逻辑由上层管理器处理
return True, "决定切换到专注聊天模式"
class ExitFocusChatAction(BaseAction): class ExitFocusChatAction(BaseAction):
"""退出专注聊天动作 - 从专注模式切换到普通模式""" """退出专注聊天动作 - 从专注模式切换到普通模式"""
@@ -371,7 +354,7 @@ class CoreActionsPlugin(BasePlugin):
config_schema = { config_schema = {
"plugin": { "plugin": {
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
"config_version": ConfigField(type=str, default="0.0.2", description="配置文件版本"), "config_version": ConfigField(type=str, default="0.0.3", description="配置文件版本"),
}, },
"components": { "components": {
"enable_reply": ConfigField(type=bool, default=True, description="是否启用'回复'动作"), "enable_reply": ConfigField(type=bool, default=True, description="是否启用'回复'动作"),
@@ -381,12 +364,11 @@ class CoreActionsPlugin(BasePlugin):
"enable_exit_focus": ConfigField(type=bool, default=True, description="是否启用'退出专注模式'动作"), "enable_exit_focus": ConfigField(type=bool, default=True, description="是否启用'退出专注模式'动作"),
}, },
"no_reply": { "no_reply": {
"waiting_timeout": ConfigField( "probability_check_time": ConfigField(type=int, default=15, description="进行概率判定的时间点(秒)"),
type=int, default=1200, description="连续不回复时,最长的等待超时时间(秒)" "end_probability": ConfigField(
type=float, default=0.5, description="在判定时间点结束等待的概率0.0到1.0", example=0.5
), ),
"stage_1_wait": ConfigField(type=int, default=10, description="第1次连续不回复的等待时间(秒)"), "max_timeout": ConfigField(type=int, default=1200, description="最大等待超时时间(秒)"),
"stage_2_wait": ConfigField(type=int, default=60, description="第2次连续不回复的等待时间"),
"stage_3_wait": ConfigField(type=int, default=600, description="第3次连续不回复的等待时间"),
"random_probability": ConfigField( "random_probability": ConfigField(
type=float, default=0.8, description="Focus模式下随机选择不回复的概率0.0到1.0", example=0.8 type=float, default=0.8, description="Focus模式下随机选择不回复的概率0.0到1.0", example=0.8
), ),
@@ -408,13 +390,14 @@ class CoreActionsPlugin(BasePlugin):
no_reply_probability = self.get_config("no_reply.random_probability", 0.8) no_reply_probability = self.get_config("no_reply.random_probability", 0.8)
NoReplyAction.random_activation_probability = no_reply_probability NoReplyAction.random_activation_probability = no_reply_probability
no_reply_timeout = self.get_config("no_reply.waiting_timeout", 1200) probability_check_time = self.get_config("no_reply.probability_check_time", 15)
NoReplyAction.waiting_timeout = no_reply_timeout NoReplyAction._probability_check_time = probability_check_time
stage1 = self.get_config("no_reply.stage_1_wait", 10) end_probability = self.get_config("no_reply.end_probability", 0.5)
stage2 = self.get_config("no_reply.stage_2_wait", 60) NoReplyAction._end_probability = end_probability
stage3 = self.get_config("no_reply.stage_3_wait", 600)
NoReplyAction._waiting_stages = [stage1, stage2, stage3] max_timeout = self.get_config("no_reply.max_timeout", 1200)
NoReplyAction._max_timeout = max_timeout
# --- 根据配置注册组件 --- # --- 根据配置注册组件 ---
components = [] components = []
@@ -426,8 +409,7 @@ class CoreActionsPlugin(BasePlugin):
components.append((EmojiAction.get_action_info(), EmojiAction)) components.append((EmojiAction.get_action_info(), EmojiAction))
if self.get_config("components.enable_exit_focus", True): if self.get_config("components.enable_exit_focus", True):
components.append((ExitFocusChatAction.get_action_info(), ExitFocusChatAction)) components.append((ExitFocusChatAction.get_action_info(), ExitFocusChatAction))
if self.get_config("components.enable_change_to_focus", True):
components.append((ChangeToFocusChatAction.get_action_info(), ChangeToFocusChatAction))
# components.append((DeepReplyAction.get_action_info(), DeepReplyAction)) # components.append((DeepReplyAction.get_action_info(), DeepReplyAction))
return components return components