fix:修复竞争bug

This commit is contained in:
SengokuCola
2025-04-26 23:22:59 +08:00
parent 236451d246
commit 0ee2c04e06
3 changed files with 86 additions and 8 deletions

View File

@@ -141,6 +141,7 @@ c HeartFChatting工作方式
- `MaiState.PEEKING` (看一眼手机): 有限度地参与聊天(由 `MaiStateInfo` 定义具体的普通/专注群数量限制)。 - `MaiState.PEEKING` (看一眼手机): 有限度地参与聊天(由 `MaiStateInfo` 定义具体的普通/专注群数量限制)。
- `MaiState.NORMAL_CHAT` (正常看手机): 正常参与聊天,允许 `SubHeartflow` 进入 `CHAT``FOCUSED` 状态(数量受限)。 - `MaiState.NORMAL_CHAT` (正常看手机): 正常参与聊天,允许 `SubHeartflow` 进入 `CHAT``FOCUSED` 状态(数量受限)。
* `MaiState.FOCUSED_CHAT` (专心看手机): 更积极地参与聊天,通常允许更多或更高优先级的 `FOCUSED` 状态子心流。 * `MaiState.FOCUSED_CHAT` (专心看手机): 更积极地参与聊天,通常允许更多或更高优先级的 `FOCUSED` 状态子心流。
- **当前转换逻辑**: 目前,`MaiState` 之间的转换由 `MaiStateManager` 管理,主要基于状态持续时间和随机概率。这是一种临时的实现方式,未来计划进行改进。
- **作用**: `Heartflow` 的状态直接影响 `SubHeartflowManager` 如何管理子心流(如激活数量、允许的状态等)。 - **作用**: `Heartflow` 的状态直接影响 `SubHeartflowManager` 如何管理子心流(如激活数量、允许的状态等)。
### 2.3. 聊天流状态 (`ChatState`) 与转换 ### 2.3. 聊天流状态 (`ChatState`) 与转换
@@ -222,4 +223,73 @@ c HeartFChatting工作方式
- `sub_heart_flow_stop_time`: (已废弃,现在由 `INACTIVE_THRESHOLD_SECONDS` in `subheartflow_manager.py` 控制) 子心流在 `ABSENT` 状态持续多久后被后台任务清理,默认为 3600 秒 (1 小时)。 - `sub_heart_flow_stop_time`: (已废弃,现在由 `INACTIVE_THRESHOLD_SECONDS` in `subheartflow_manager.py` 控制) 子心流在 `ABSENT` 状态持续多久后被后台任务清理,默认为 3600 秒 (1 小时)。
- `sub_heart_flow_freeze_time`: 子心流冻结时间 (当前文档未明确体现,可能需要审阅代码确认)。 - `sub_heart_flow_freeze_time`: 子心流冻结时间 (当前文档未明确体现,可能需要审阅代码确认)。
- `heart_flow_update_interval`: 主心流更新其状态或执行管理操作的频率 (需要审阅 `Heartflow` 代码确认)。 - `heart_flow_update_interval`: 主心流更新其状态或执行管理操作的频率 (需要审阅 `Heartflow` 代码确认)。
- `
### 5.3. 之后可以做的 (Future Work)
- **智能化 MaiState 状态转换**:
- 当前 `MaiState` (整体状态,如 `OFFLINE`, `NORMAL_CHAT` 等) 的转换逻辑 (`MaiStateManager`) 较为简单,主要依赖时间和随机性。
- 未来的计划是让主心流 (`Heartflow`) 负责决策自身的 `MaiState`。
- 该决策将综合考虑以下信息:
- 各个子心流 (`SubHeartflow`) 的活动状态和信息摘要。
- 主心流自身的状态和历史信息。
- (可能) 结合预设的日程安排 (Schedule) 信息。
- 目标是让 Mai 的整体状态变化更符合逻辑和上下文。 (计划在 064 实现)
- **参数化与动态调整聊天行为**:
- 将 `NormalChatInstance` 和 `HeartFlowChatInstance` 中的关键行为参数(例如:回复概率、思考频率、兴趣度阈值、状态转换条件等)提取出来,使其更易于配置。
- 允许每个 `SubHeartflow` (即每个聊天场景) 拥有其独立的参数配置,实现"千群千面"。
- 开发机制,使得这些参数能够被动态调整:
- 基于外部反馈:例如,根据用户评价("话太多"或"太冷淡")调整回复频率。
- 基于环境分析:例如,根据群消息的活跃度自动调整参与度。
- 基于学习:通过分析历史交互数据,优化特定群聊下的行为模式。
- 目标是让 Mai 在不同群聊中展现出更适应环境、更个性化的交互风格。
- **动态 Prompt 生成与人格塑造**:
- 当前 Prompt (提示词) 相对静态。计划实现动态或半结构化的 Prompt 生成。
- Prompt 内容可根据以下因素调整:
- **人格特质**: 通过参数化配置(如友善度、严谨性等),影响 Prompt 的措辞、语气和思考倾向,塑造更稳定和独特的人格。
- **当前情绪**: 将实时情绪状态融入 Prompt使回复更符合当下心境。
- 目标:提升 `HeartFlowChatInstance` (HFC) 回复的多样性、一致性和真实感。
- 前置:需要重构 Prompt 构建逻辑,可能引入 `PromptBuilder` 并提供标准接口 (认为是必须步骤)。
- **扩展观察系统 (Observation System)**:
- 目前主要依赖 `ChattingObservation` 获取消息。
- 计划引入更多 `Observation` 类型,为 `SubHeartflow` 提供更丰富的上下文:
- Mai 的全局状态 (`MaiStateInfo`)。
- `SubHeartflow` 自身的聊天状态 (`ChatStateInfo`) 和参数配置。
- Mai 的系统配置、连接平台信息。
- 其他相关聊天或系统的聚合信息。
- 目标:让 `SubHeartflow` 基于更全面的信息进行决策。
- **增强工具调用能力 (Enhanced Tool Usage)**:
- 扩展 `HeartFlowChatInstance` (HFC) 可用的工具集。
- 考虑引入"元工具"或分层工具机制,允许 HFC 在需要时(如深度思考)访问更强大的工具,例如:
- 修改自身或其他 `SubHeartflow` 的聊天参数。
- 请求改变 Mai 的全局状态 (`MaiState`)。
- 管理日程或执行更复杂的分析任务。
- 目标:提升 HFC 的自主决策和行动能力,即使会增加一定的延迟。
- **基于历史学习的行为模式应用**:
- **学习**: 分析过往聊天记录,提取和学习具体的行为模式(如特定梗的用法、情境化回应风格等)。可能需要专门的分析模块。
- **存储与匹配**: 需要有效的方法存储学习到的行为模式,并开发强大的 **匹配** 机制,在运行时根据当前情境检索最合适的模式。**(匹配的准确性是关键)**
- **应用与评估**: 将匹配到的行为模式融入 HFC 的决策和回复生成(例如,将其整合进 Prompt。之后需评估该行为模式应用的实际效果。
- **人格塑造**: 通过学习到的实际行为来动态塑造人格,作为静态人设描述的补充或替代,使其更生动自然。
- **标准化人设生成 (Standardized Persona Generation)**:
- **目标**: 解决手动配置 `人设` 文件缺乏标准、难以全面描述个性的问题,并生成更丰富、可操作的人格资源。
- **方法**: 利用大型语言模型 (LLM) 辅助生成标准化的、结构化的人格**资源包**。
- **生成内容**: 不仅生成描述性文本(替代现有 `individual` 配置),还可以同时生成与该人格配套的:
- **相关工具 (Tools)**: 该人格倾向于使用的工具或能力。
- **初始记忆/知识库 (Memories/Knowledge)**: 定义其背景和知识基础。
- **核心行为模式 (Core Behavior Patterns)**: 预置一些典型的行为方式,可作为行为学习的起点。
- **实现途径**:
- 通过与 LLM 的交互式对话来定义和细化人格及其配套资源。
- 让 LLM 分析提供的文本材料(如小说、背景故事)来提取人格特质和相关信息。
- **优势**: 替代易出错且标准不一的手动配置,生成更丰富、一致、包含配套资源且易于系统理解和应用的人格包。
- **优化表情包处理与理解 (Enhanced Emoji Handling and Understanding)**:
- **面临挑战**:
- **历史记录表示**: 如何在聊天历史中有效表示表情包,供 LLM 理解。
- **语义理解**: 如何让 LLM 准确把握表情包的含义、情感和语境。
- **场景判断与选择**: 如何让 LLM 判断何时适合使用表情包,并选择最贴切的一个。
- **目标**: 提升 Mai 理解和运用表情包的能力,使交互更自然生动。
- **说明**: 可能需要较多时间进行数据处理和模型调优,但对改善体验潜力巨大。

View File

@@ -63,7 +63,7 @@ def init_prompt():
- 话题无关/无聊/不感兴趣 - 话题无关/无聊/不感兴趣
- 最后一条消息是你自己发的且无人回应你 - 最后一条消息是你自己发的且无人回应你
- 讨论你不懂的专业话题 - 讨论你不懂的专业话题
- 你发送了太多消息 - 你发送了太多消息,且无人回复
2. 文字回复(text_reply)适用: 2. 文字回复(text_reply)适用:
- 有实质性内容需要表达 - 有实质性内容需要表达
@@ -81,6 +81,7 @@ def init_prompt():
- 不要和自己聊天 - 不要和自己聊天
【必须遵守】 【必须遵守】
- 遵守回复原则
- 必须调用工具并包含action和reasoning - 必须调用工具并包含action和reasoning
- 你可以选择文字回复(text_reply),纯表情回复(emoji_reply),不回复(no_reply) - 你可以选择文字回复(text_reply),纯表情回复(emoji_reply),不回复(no_reply)
- 选择text_reply或emoji_reply时必须提供emoji_query - 选择text_reply或emoji_reply时必须提供emoji_query

View File

@@ -357,17 +357,24 @@ class NormalChat:
# 处理需要回复的消息 # 处理需要回复的消息
processed_count = 0 processed_count = 0
for item in messages_to_reply: # --- 修改迭代前创建要处理的ID列表副本防止迭代时修改 ---
messages_to_process_initially = list(messages_to_reply) # 创建副本
# --- 修改结束 ---
for item in messages_to_process_initially: # 使用副本迭代
msg_id, (message, interest_value, is_mentioned) = item msg_id, (message, interest_value, is_mentioned) = item
# --- 修改:在处理前尝试 pop防止竞争 ---
popped_item = self.interest_dict.pop(msg_id, None)
if popped_item is None:
logger.warning(f"[{self.stream_name}] 初始兴趣消息 {msg_id} 在处理前已被移除,跳过。")
continue # 如果消息已被其他任务处理pop则跳过
# --- 修改结束 ---
try: try:
logger.info(f"[{self.stream_name}] 处理初始高兴趣消息 {msg_id} (兴趣值: {interest_value:.2f})") logger.info(f"[{self.stream_name}] 处理初始高兴趣消息 {msg_id} (兴趣值: {interest_value:.2f})")
await self.normal_response(message=message, is_mentioned=is_mentioned, interested_rate=interest_value) await self.normal_response(message=message, is_mentioned=is_mentioned, interested_rate=interest_value)
processed_count += 1 processed_count += 1
except Exception as e: except Exception as e:
logger.error(f"[{self.stream_name}] 处理初始兴趣消息 {msg_id} 时出错: {e}\n{traceback.format_exc()}") logger.error(f"[{self.stream_name}] 处理初始兴趣消息 {msg_id} 时出错: {e}\\n{traceback.format_exc()}")
finally:
# 无论成功与否都清空兴趣字典
self.interest_dict.clear()
logger.info( logger.info(
f"[{self.stream_name}] 初始高兴趣消息处理完毕,共处理 {processed_count} 条。剩余 {len(self.interest_dict)} 条待轮询。" f"[{self.stream_name}] 初始高兴趣消息处理完毕,共处理 {processed_count} 条。剩余 {len(self.interest_dict)} 条待轮询。"
@@ -414,7 +421,7 @@ class NormalChat:
# --- 修改:使用 create_task 启动初始消息处理 --- # --- 修改:使用 create_task 启动初始消息处理 ---
logger.info(f"[{self.stream_name}] 开始后台处理初始兴趣消息...") logger.info(f"[{self.stream_name}] 开始后台处理初始兴趣消息...")
# 创建一个任务来处理初始消息,不阻塞当前流程 # 创建一个任务来处理初始消息,不阻塞当前流程
initial_process_task = asyncio.create_task(self._process_initial_interest_messages()) _initial_process_task = asyncio.create_task(self._process_initial_interest_messages())
# 可以考虑给这个任务也添加完成回调来记录日志或处理错误 # 可以考虑给这个任务也添加完成回调来记录日志或处理错误
# initial_process_task.add_done_callback(...) # initial_process_task.add_done_callback(...)
# --- 修改结束 --- # --- 修改结束 ---