better:更好的重新思考修复复读,记录循环信息,拆分模型配置

This commit is contained in:
SengokuCola
2025-04-25 21:38:16 +08:00
parent 75924bf499
commit 91ad729b0c
9 changed files with 317 additions and 184 deletions

View File

@@ -192,7 +192,6 @@ class BotConfig:
reply_trigger_threshold: float = 3.0 # 心流聊天触发阈值,越低越容易触发 reply_trigger_threshold: float = 3.0 # 心流聊天触发阈值,越低越容易触发
probability_decay_factor_per_second: float = 0.2 # 概率衰减因子,越大衰减越快 probability_decay_factor_per_second: float = 0.2 # 概率衰减因子,越大衰减越快
default_decay_rate_per_second: float = 0.98 # 默认衰减率,越大衰减越慢 default_decay_rate_per_second: float = 0.98 # 默认衰减率,越大衰减越慢
initial_duration: int = 60 # 初始持续时间,越大心流聊天持续的时间越长
# sub_heart_flow_update_interval: int = 60 # 子心流更新频率,间隔 单位秒 # sub_heart_flow_update_interval: int = 60 # 子心流更新频率,间隔 单位秒
# sub_heart_flow_freeze_time: int = 120 # 子心流冻结时间,超过这个时间没有回复,子心流会冻结,间隔 单位秒 # sub_heart_flow_freeze_time: int = 120 # 子心流冻结时间,超过这个时间没有回复,子心流会冻结,间隔 单位秒
@@ -286,11 +285,11 @@ class BotConfig:
vlm: Dict[str, str] = field(default_factory=lambda: {}) vlm: Dict[str, str] = field(default_factory=lambda: {})
moderation: Dict[str, str] = field(default_factory=lambda: {}) moderation: Dict[str, str] = field(default_factory=lambda: {})
# 实验性
llm_observation: Dict[str, str] = field(default_factory=lambda: {}) llm_observation: Dict[str, str] = field(default_factory=lambda: {})
llm_sub_heartflow: Dict[str, str] = field(default_factory=lambda: {}) llm_sub_heartflow: Dict[str, str] = field(default_factory=lambda: {})
llm_heartflow: Dict[str, str] = field(default_factory=lambda: {}) llm_heartflow: Dict[str, str] = field(default_factory=lambda: {})
llm_tool_use: Dict[str, str] = field(default_factory=lambda: {}) llm_tool_use: Dict[str, str] = field(default_factory=lambda: {})
llm_plan: Dict[str, str] = field(default_factory=lambda: {})
api_urls: Dict[str, str] = field(default_factory=lambda: {}) api_urls: Dict[str, str] = field(default_factory=lambda: {})
@@ -448,7 +447,6 @@ class BotConfig:
config.default_decay_rate_per_second = heartflow_config.get( config.default_decay_rate_per_second = heartflow_config.get(
"default_decay_rate_per_second", config.default_decay_rate_per_second "default_decay_rate_per_second", config.default_decay_rate_per_second
) )
config.initial_duration = heartflow_config.get("initial_duration", config.initial_duration)
def willing(parent: dict): def willing(parent: dict):
willing_config = parent["willing"] willing_config = parent["willing"]
@@ -489,6 +487,7 @@ class BotConfig:
"llm_tool_use", "llm_tool_use",
"llm_observation", "llm_observation",
"llm_sub_heartflow", "llm_sub_heartflow",
"llm_plan",
"llm_heartflow", "llm_heartflow",
"llm_PFC_action_planner", "llm_PFC_action_planner",
"llm_PFC_chat", "llm_PFC_chat",

View File

@@ -230,8 +230,8 @@ class BackgroundTaskManager:
if await self.subheartflow_manager.stop_subheartflow(flow_id, f"定期清理: {reason}"): if await self.subheartflow_manager.stop_subheartflow(flow_id, f"定期清理: {reason}"):
stopped_count += 1 stopped_count += 1
logger.info(f"[Background Task Cleanup] Cleanup cycle finished. Stopped {stopped_count} inactive flows.") logger.info(f"[Background Task Cleanup] Cleanup cycle finished. Stopped {stopped_count} inactive flows.")
else: # else:
logger.debug("[Background Task Cleanup] Cleanup cycle finished. No inactive flows found.") # logger.debug("[Background Task Cleanup] Cleanup cycle finished. No inactive flows found.")
async def _perform_logging_work(self): async def _perform_logging_work(self):
"""执行一轮状态日志记录。""" """执行一轮状态日志记录。"""

View File

@@ -11,6 +11,7 @@ from src.do_tool.tool_use import ToolUser
from src.plugins.utils.json_utils import safe_json_dumps, normalize_llm_response, process_llm_tool_calls from src.plugins.utils.json_utils import safe_json_dumps, normalize_llm_response, process_llm_tool_calls
from src.heart_flow.chat_state_info import ChatStateInfo from src.heart_flow.chat_state_info import ChatStateInfo
from src.plugins.chat.chat_stream import chat_manager from src.plugins.chat.chat_stream import chat_manager
from src.plugins.heartFC_chat.heartFC_Cycleinfo import CycleInfo
subheartflow_config = LogConfig( subheartflow_config = LogConfig(
console_format=SUB_HEARTFLOW_STYLE_CONFIG["console_format"], console_format=SUB_HEARTFLOW_STYLE_CONFIG["console_format"],
@@ -23,12 +24,12 @@ def init_prompt():
prompt = "" prompt = ""
prompt += "{extra_info}\n" prompt += "{extra_info}\n"
prompt += "{prompt_personality}\n" prompt += "{prompt_personality}\n"
prompt += "刚刚你的内心想法是:{current_thinking_info}\n" prompt += "{last_loop_prompt}\n"
prompt += "-----------------------------------\n" prompt += "-----------------------------------\n"
prompt += "现在是{time_now}你正在上网和qq群里的网友们聊天以下是正在进行的聊天内容\n{chat_observe_info}\n" prompt += "现在是{time_now}你正在上网和qq群里的网友们聊天以下是正在进行的聊天内容\n{chat_observe_info}\n"
prompt += "\n你现在{mood_info}\n" prompt += "\n你现在{mood_info}\n"
prompt += "现在请你生成你的内心想法,要求思考群里正在进行的话题,之前大家聊过的话题,群里成员的关系。" prompt += "现在请你,阅读群里正在进行的聊天内容,思考群里正在进行的话题,分析群里成员与你的关系。"
prompt += "请你思考,要不要对群里的话题进行回复,以及如何对群聊内容进行回复\n" prompt += "请你思考,生成你的内心想法,包括你的思考,要不要对群里的话题进行回复,以及如何对群聊内容进行回复\n"
prompt += "回复的要求是:不要总是重复自己提到过的话题,如果你要回复,最好只回复一个人的一个话题\n" prompt += "回复的要求是:不要总是重复自己提到过的话题,如果你要回复,最好只回复一个人的一个话题\n"
prompt += "如果最后一条消息是你自己发的,观察到的内容只有你自己的发言,并且之后没有人回复你,不要回复。" prompt += "如果最后一条消息是你自己发的,观察到的内容只有你自己的发言,并且之后没有人回复你,不要回复。"
prompt += "如果聊天记录中最新的消息是你自己发送的,并且你还想继续回复,你应该紧紧衔接你发送的消息,进行话题的深入,补充,或追问等等。" prompt += "如果聊天记录中最新的消息是你自己发送的,并且你还想继续回复,你应该紧紧衔接你发送的消息,进行话题的深入,补充,或追问等等。"
@@ -39,6 +40,12 @@ def init_prompt():
Prompt(prompt, "sub_heartflow_prompt_before") Prompt(prompt, "sub_heartflow_prompt_before")
prompt = ""
prompt += "刚刚你的内心想法是:{current_thinking_info}\n"
prompt += "{if_replan_prompt}\n"
Prompt(prompt, "last_loop")
class SubMind: class SubMind:
def __init__(self, subheartflow_id: str, chat_state: ChatStateInfo, observations: Observation): def __init__(self, subheartflow_id: str, chat_state: ChatStateInfo, observations: Observation):
@@ -58,7 +65,7 @@ class SubMind:
self.past_mind = [] self.past_mind = []
self.structured_info = {} self.structured_info = {}
async def do_thinking_before_reply(self): async def do_thinking_before_reply(self, last_cycle: CycleInfo):
""" """
在回复前进行思考,生成内心想法并收集工具调用结果 在回复前进行思考,生成内心想法并收集工具调用结果
@@ -122,6 +129,20 @@ class SubMind:
("继续生成你在这个聊天中的想法,进行深入思考", 0.1), ("继续生成你在这个聊天中的想法,进行深入思考", 0.1),
] ]
#上一次决策信息
last_action = last_cycle.action_type
last_reasoning = last_cycle.reasoning
is_replan = last_cycle.replanned
if is_replan:
if_replan_prompt = f"但是你有了上述想法之后,有了新消息,你决定重新思考后,你做了:{last_action}\n因为:{last_reasoning}\n"
else:
if_replan_prompt = f"出于这个想法,你刚才做了:{last_action}\n因为:{last_reasoning}\n"
last_loop_prompt = (await global_prompt_manager.get_prompt_async("last_loop")).format(
current_thinking_info=current_thinking_info,
if_replan_prompt=if_replan_prompt
)
# 加权随机选择思考指导 # 加权随机选择思考指导
hf_do_next = local_random.choices( hf_do_next = local_random.choices(
[option[0] for option in hf_options], weights=[option[1] for option in hf_options], k=1 [option[0] for option in hf_options], weights=[option[1] for option in hf_options], k=1
@@ -133,11 +154,11 @@ class SubMind:
extra_info="", # 可以在这里添加额外信息 extra_info="", # 可以在这里添加额外信息
prompt_personality=prompt_personality, prompt_personality=prompt_personality,
bot_name=individuality.personality.bot_nickname, bot_name=individuality.personality.bot_nickname,
current_thinking_info=current_thinking_info,
time_now=time_now, time_now=time_now,
chat_observe_info=chat_observe_info, chat_observe_info=chat_observe_info,
mood_info=mood_info, mood_info=mood_info,
hf_do_next=hf_do_next, hf_do_next=hf_do_next,
last_loop_prompt=last_loop_prompt
) )
# logger.debug(f"[{self.subheartflow_id}] 心流思考提示词构建完成") # logger.debug(f"[{self.subheartflow_id}] 心流思考提示词构建完成")

View File

@@ -0,0 +1,70 @@
import time
from typing import List, Optional, Dict, Any
class CycleInfo:
"""循环信息记录类"""
def __init__(self, cycle_id: int):
self.cycle_id = cycle_id
self.start_time = time.time()
self.end_time: Optional[float] = None
self.action_taken = False
self.action_type = "unknown"
self.reasoning = ""
self.timers: Dict[str, float] = {}
self.thinking_id = ""
self.replanned = False
# 添加响应信息相关字段
self.response_info: Dict[str, Any] = {
"response_text": [], # 回复的文本列表
"emoji_info": "", # 表情信息
"anchor_message_id": "", # 锚点消息ID
"reply_message_ids": [], # 回复消息ID列表
"sub_mind_thinking": "", # 子思维思考内容
}
def to_dict(self) -> Dict[str, Any]:
"""将循环信息转换为字典格式"""
return {
"cycle_id": self.cycle_id,
"start_time": self.start_time,
"end_time": self.end_time,
"action_taken": self.action_taken,
"action_type": self.action_type,
"reasoning": self.reasoning,
"timers": self.timers,
"thinking_id": self.thinking_id,
"response_info": self.response_info
}
def complete_cycle(self):
"""完成循环,记录结束时间"""
self.end_time = time.time()
def set_action_info(self, action_type: str, reasoning: str, action_taken: bool):
"""设置动作信息"""
self.action_type = action_type
self.reasoning = reasoning
self.action_taken = action_taken
def set_thinking_id(self, thinking_id: str):
"""设置思考消息ID"""
self.thinking_id = thinking_id
def set_response_info(self,
response_text: Optional[List[str]] = None,
emoji_info: Optional[str] = None,
anchor_message_id: Optional[str] = None,
reply_message_ids: Optional[List[str]] = None,
sub_mind_thinking: Optional[str] = None):
"""设置响应信息"""
if response_text is not None:
self.response_info["response_text"] = response_text
if emoji_info is not None:
self.response_info["emoji_info"] = emoji_info
if anchor_message_id is not None:
self.response_info["anchor_message_id"] = anchor_message_id
if reply_message_ids is not None:
self.response_info["reply_message_ids"] = reply_message_ids
if sub_mind_thinking is not None:
self.response_info["sub_mind_thinking"] = sub_mind_thinking

View File

@@ -1,7 +1,8 @@
import asyncio import asyncio
import time import time
import traceback import traceback
from typing import List, Optional, Dict, Any, Set, Deque import random # <-- 添加导入
from typing import List, Optional, Dict, Any, Deque
from collections import deque from collections import deque
from src.plugins.chat.message import MessageRecv, BaseMessageInfo, MessageThinking, MessageSending from src.plugins.chat.message import MessageRecv, BaseMessageInfo, MessageThinking, MessageSending
from src.plugins.chat.message import MessageSet, Seg # Local import needed after move from src.plugins.chat.message import MessageSet, Seg # Local import needed after move
@@ -23,6 +24,7 @@ from src.heart_flow.observation import Observation
from src.plugins.heartFC_chat.heartflow_prompt_builder import global_prompt_manager from src.plugins.heartFC_chat.heartflow_prompt_builder import global_prompt_manager
import contextlib import contextlib
from src.plugins.utils.chat_message_builder import num_new_messages_since from src.plugins.utils.chat_message_builder import num_new_messages_since
from src.plugins.heartFC_chat.heartFC_Cycleinfo import CycleInfo
# --- End import --- # --- End import ---
@@ -139,75 +141,6 @@ class SenderError(HeartFCError):
"""发送器异常""" """发送器异常"""
pass pass
class CycleInfo:
"""循环信息记录类"""
def __init__(self, cycle_id: int):
self.cycle_id = cycle_id
self.start_time = time.time()
self.end_time: Optional[float] = None
self.action_taken = False
self.action_type = "unknown"
self.reasoning = ""
self.timers: Dict[str, float] = {}
self.thinking_id = ""
# 添加响应信息相关字段
self.response_info: Dict[str, Any] = {
"response_text": [], # 回复的文本列表
"emoji_info": "", # 表情信息
"anchor_message_id": "", # 锚点消息ID
"reply_message_ids": [], # 回复消息ID列表
"sub_mind_thinking": "", # 子思维思考内容
}
def to_dict(self) -> Dict[str, Any]:
"""将循环信息转换为字典格式"""
return {
"cycle_id": self.cycle_id,
"start_time": self.start_time,
"end_time": self.end_time,
"action_taken": self.action_taken,
"action_type": self.action_type,
"reasoning": self.reasoning,
"timers": self.timers,
"thinking_id": self.thinking_id,
"response_info": self.response_info
}
def complete_cycle(self):
"""完成循环,记录结束时间"""
self.end_time = time.time()
def set_action_info(self, action_type: str, reasoning: str, action_taken: bool):
"""设置动作信息"""
self.action_type = action_type
self.reasoning = reasoning
self.action_taken = action_taken
def set_thinking_id(self, thinking_id: str):
"""设置思考消息ID"""
self.thinking_id = thinking_id
def set_response_info(self,
response_text: Optional[List[str]] = None,
emoji_info: Optional[str] = None,
anchor_message_id: Optional[str] = None,
reply_message_ids: Optional[List[str]] = None,
sub_mind_thinking: Optional[str] = None):
"""设置响应信息"""
if response_text is not None:
self.response_info["response_text"] = response_text
if emoji_info is not None:
self.response_info["emoji_info"] = emoji_info
if anchor_message_id is not None:
self.response_info["anchor_message_id"] = anchor_message_id
if reply_message_ids is not None:
self.response_info["reply_message_ids"] = reply_message_ids
if sub_mind_thinking is not None:
self.response_info["sub_mind_thinking"] = sub_mind_thinking
class HeartFChatting: class HeartFChatting:
""" """
管理一个连续的Plan-Replier-Sender循环 管理一个连续的Plan-Replier-Sender循环
@@ -244,8 +177,7 @@ class HeartFChatting:
# LLM规划器配置 # LLM规划器配置
self.planner_llm = LLMRequest( self.planner_llm = LLMRequest(
model=global_config.llm_normal, model=global_config.llm_plan,
temperature=global_config.llm_normal["temp"],
max_tokens=1000, max_tokens=1000,
request_type="action_planning", # 用于动作规划 request_type="action_planning", # 用于动作规划
) )
@@ -352,7 +284,7 @@ class HeartFChatting:
# 记录规划开始时间点 # 记录规划开始时间点
planner_start_db_time = time.time() planner_start_db_time = time.time()
# 执行规划阶段 # 主循环:思考->决策->执行
action_taken, thinking_id = await self._think_plan_execute_loop( action_taken, thinking_id = await self._think_plan_execute_loop(
cycle_timers, planner_start_db_time cycle_timers, planner_start_db_time
) )
@@ -436,29 +368,34 @@ class HeartFChatting:
) -> tuple[bool, str]: ) -> tuple[bool, str]:
"""执行规划阶段""" """执行规划阶段"""
try: try:
# 获取子思维思考结果 # think:思考
current_mind = "" current_mind = await self._get_submind_thinking(cycle_timers)
with Timer("思考", cycle_timers):
current_mind = await self._get_submind_thinking()
# 记录子思维思考内容 # 记录子思维思考内容
if self._current_cycle: if self._current_cycle:
self._current_cycle.set_response_info(sub_mind_thinking=current_mind) self._current_cycle.set_response_info(sub_mind_thinking=current_mind)
# 执行规划 # plan:决策
with Timer("决策", cycle_timers): with Timer("决策", cycle_timers):
planner_result = await self._planner(current_mind, cycle_timers) planner_result = await self._planner(current_mind, cycle_timers)
action = planner_result.get("action", "error")
reasoning = planner_result.get("reasoning", "未提供理由")
self._current_cycle.set_action_info(action, reasoning, False)
# 在获取规划结果后检查新消息 # 在获取规划结果后检查新消息
if await self._check_new_messages(planner_start_db_time): if await self._check_new_messages(planner_start_db_time):
# 更新循环信息 if random.random() < 0.3:
logger.info(f"{self.log_prefix} 思考到一半,检测到新消息,重新思考") logger.info(f"{self.log_prefix} 看到了新消息,麦麦决定重新观察和规划...")
self._current_cycle.set_action_info("new_messages", "检测到新消息", False) # 重新规划
return False, "new_messages" with Timer("重新决策", cycle_timers):
self._current_cycle.replanned = True
planner_result = await self._planner(current_mind, cycle_timers, is_re_planned=True)
logger.info(f"{self.log_prefix} 重新规划完成.")
# 解析规划结果 # 解析规划结果
action = planner_result.get("action", "error") action = planner_result.get("action", "error")
reasoning = planner_result.get("reasoning", "未提供理由") reasoning = planner_result.get("reasoning", "未提供理由")
# 更新循环信息 # 更新循环信息
self._current_cycle.set_action_info(action, reasoning, True) self._current_cycle.set_action_info(action, reasoning, True)
@@ -467,7 +404,7 @@ class HeartFChatting:
logger.error(f"{self.log_prefix} LLM失败: {reasoning}") logger.error(f"{self.log_prefix} LLM失败: {reasoning}")
return False, "" return False, ""
# 根据动作类型执行对应处理 # execute:执行
with Timer("执行", cycle_timers): with Timer("执行", cycle_timers):
return await self._handle_action(action, reasoning, planner_result.get("emoji_query", ""), cycle_timers, planner_start_db_time) return await self._handle_action(action, reasoning, planner_result.get("emoji_query", ""), cycle_timers, planner_start_db_time)
@@ -699,7 +636,7 @@ class HeartFChatting:
logger.info(f"{log_prefix} Sleep interrupted, loop likely cancelling.") logger.info(f"{log_prefix} Sleep interrupted, loop likely cancelling.")
raise raise
async def _get_submind_thinking(self) -> str: async def _get_submind_thinking(self, cycle_timers: dict) -> str:
""" """
获取子思维的思考结果 获取子思维的思考结果
@@ -707,27 +644,38 @@ class HeartFChatting:
str: 思考结果,如果思考失败则返回错误信息 str: 思考结果,如果思考失败则返回错误信息
""" """
try: try:
with Timer("观察", cycle_timers):
observation = self.observations[0] observation = self.observations[0]
await observation.observe() await observation.observe()
current_mind, _past_mind = await self.sub_mind.do_thinking_before_reply()
# 获取上一个循环的信息
last_cycle = self._cycle_history[-1] if self._cycle_history else None
with Timer("思考", cycle_timers):
# 获取上一个循环的动作
# 传递上一个循环的信息给 do_thinking_before_reply
current_mind, _past_mind = await self.sub_mind.do_thinking_before_reply(
last_cycle=last_cycle
)
return current_mind return current_mind
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix}[SubMind] 思考失败: {e}") logger.error(f"{self.log_prefix}[SubMind] 思考失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return "[思考时出错]" return "[思考时出错]"
async def _planner(self, current_mind: str, cycle_timers: dict) -> Dict[str, Any]: async def _planner(self, current_mind: str, cycle_timers: dict, is_re_planned: bool = False) -> Dict[str, Any]:
""" """
规划器 (Planner): 使用LLM根据上下文决定是否和如何回复。 规划器 (Planner): 使用LLM根据上下文决定是否和如何回复。
参数: 参数:
current_mind: 子思维的当前思考结果 current_mind: 子思维的当前思考结果
""" """
logger.info(f"{self.log_prefix}[Planner] 开始执行规划器") logger.info(f"{self.log_prefix}[Planner] 开始{'重新' if is_re_planned else ''}执行规划器")
# 获取观察信息 # 获取观察信息
observation = self.observations[0] observation = self.observations[0]
# await observation.observe() if is_re_planned:
observation.observe()
observed_messages = observation.talking_message observed_messages = observation.talking_message
observed_messages_str = observation.talking_message_str observed_messages_str = observation.talking_message_str
@@ -740,11 +688,18 @@ class HeartFChatting:
try: try:
# 构建提示词 # 构建提示词
with Timer("构建提示词", cycle_timers): with Timer("构建提示词", cycle_timers):
if is_re_planned:
replan_prompt = await self._build_replan_prompt(
self._current_cycle.action, self._current_cycle.reasoning
)
prompt = replan_prompt
else:
replan_prompt = ""
prompt = await self._build_planner_prompt( prompt = await self._build_planner_prompt(
observed_messages_str, current_mind, self.sub_mind.structured_info observed_messages_str, current_mind, self.sub_mind.structured_info, replan_prompt
) )
payload = { payload = {
"model": self.planner_llm.model_name, "model": global_config.llm_plan["name"],
"messages": [{"role": "user", "content": prompt}], "messages": [{"role": "user", "content": prompt}],
"tools": self.action_manager.get_planner_tool_definition(), "tools": self.action_manager.get_planner_tool_definition(),
"tool_choice": {"type": "function", "function": {"name": "decide_reply_action"}}, "tool_choice": {"type": "function", "function": {"name": "decide_reply_action"}},
@@ -905,8 +860,18 @@ class HeartFChatting:
logger.info(f"{self.log_prefix} HeartFChatting关闭完成") logger.info(f"{self.log_prefix} HeartFChatting关闭完成")
async def _build_replan_prompt(
self, action: str, reasoning: str
) -> str:
"""构建 Replanner LLM 的提示词"""
prompt = (await global_prompt_manager.get_prompt_async("replan_prompt")).format(
action=action,
reasoning=reasoning,
)
return prompt
async def _build_planner_prompt( async def _build_planner_prompt(
self, observed_messages_str: str, current_mind: Optional[str], structured_info: Dict[str, Any] self, observed_messages_str: str, current_mind: Optional[str], structured_info: Dict[str, Any], replan_prompt: str
) -> str: ) -> str:
"""构建 Planner LLM 的提示词""" """构建 Planner LLM 的提示词"""
@@ -937,6 +902,7 @@ class HeartFChatting:
structured_info_block=structured_info_block, structured_info_block=structured_info_block,
chat_content_block=chat_content_block, chat_content_block=chat_content_block,
current_mind_block=current_mind_block, current_mind_block=current_mind_block,
replan=replan_prompt,
) )
return prompt return prompt

View File

@@ -49,7 +49,6 @@ class HeartFCGenerator:
arousal_multiplier = MoodManager.get_instance().get_arousal_multiplier() arousal_multiplier = MoodManager.get_instance().get_arousal_multiplier()
with Timer() as t_generate_response:
current_model = self.model_normal current_model = self.model_normal
current_model.temperature = global_config.llm_normal["temp"] * arousal_multiplier # 激活度越高,温度越高 current_model.temperature = global_config.llm_normal["temp"] * arousal_multiplier # 激活度越高,温度越高
model_response = await self._generate_response_with_model( model_response = await self._generate_response_with_model(

View File

@@ -51,6 +51,7 @@ def init_prompt():
{chat_content_block} {chat_content_block}
看了以上内容,你产生的内心想法是: 看了以上内容,你产生的内心想法是:
{current_mind_block} {current_mind_block}
{replan}
请结合你的内心想法和观察到的聊天内容,分析情况并使用 'decide_reply_action' 工具来决定你的最终行动。 请结合你的内心想法和观察到的聊天内容,分析情况并使用 'decide_reply_action' 工具来决定你的最终行动。
注意你必须参考以下决策依据来选择工具: 注意你必须参考以下决策依据来选择工具:
1. 如果聊天内容无聊、与你无关、或者你的内心想法认为不适合回复(例如在讨论你不懂或不感兴趣的话题),选择 'no_reply' 1. 如果聊天内容无聊、与你无关、或者你的内心想法认为不适合回复(例如在讨论你不懂或不感兴趣的话题),选择 'no_reply'
@@ -64,6 +65,8 @@ def init_prompt():
"planner_prompt", "planner_prompt",
) )
Prompt("你原本打算{action},因为:{reasoning},但是你看到了新的消息,你决定重新决定行动。", "replan_prompt")
Prompt("你正在qq群里聊天下面是群里在聊的内容", "chat_target_group1") Prompt("你正在qq群里聊天下面是群里在聊的内容", "chat_target_group1")
Prompt("和群里聊天", "chat_target_group2") Prompt("和群里聊天", "chat_target_group2")
Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1") Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1")
@@ -86,7 +89,7 @@ def init_prompt():
你的网名叫{bot_name},有人也叫你{bot_other_names}{prompt_personality} 你的网名叫{bot_name},有人也叫你{bot_other_names}{prompt_personality}
你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},然后给出日常且口语化的回复,平淡一些, 你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},然后给出日常且口语化的回复,平淡一些,
尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger} 尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger}
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,尽量不要说你说过的话 请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,不要浮夸,平淡一些 ,不要重复自己说过的话
请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。 请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。
{moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号()表情包at或 @等 )。,只输出回复内容""", {moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号()表情包at或 @等 )。,只输出回复内容""",
"reasoning_prompt_main", "reasoning_prompt_main",

View File

@@ -1,5 +1,5 @@
[inner] [inner]
version = "1.4.2" version = "1.5.0"
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
#如果你想要修改配置文件请在修改后将version的值进行变更 #如果你想要修改配置文件请在修改后将version的值进行变更
@@ -81,12 +81,8 @@ model_normal_probability = 0.3 # 麦麦回答时选择一般模型 模型的概
reply_trigger_threshold = 3.0 # 心流聊天触发阈值,越低越容易进入心流聊天 reply_trigger_threshold = 3.0 # 心流聊天触发阈值,越低越容易进入心流聊天
probability_decay_factor_per_second = 0.2 # 概率衰减因子,越大衰减越快,越高越容易退出心流聊天 probability_decay_factor_per_second = 0.2 # 概率衰减因子,越大衰减越快,越高越容易退出心流聊天
default_decay_rate_per_second = 0.98 # 默认衰减率,越大衰减越快,越高越难进入心流聊天 default_decay_rate_per_second = 0.98 # 默认衰减率,越大衰减越快,越高越难进入心流聊天
initial_duration = 60 # 初始持续时间,越大心流聊天持续的时间越长
sub_heart_flow_stop_time = 500 # 子心流停止时间,超过这个时间没有回复,子心流会停止,间隔 单位秒 sub_heart_flow_stop_time = 500 # 子心流停止时间,超过这个时间没有回复,子心流会停止,间隔 单位秒
# sub_heart_flow_update_interval = 60
# sub_heart_flow_freeze_time = 100
# heart_flow_update_interval = 600
observation_context_size = 20 # 心流观察到的最长上下文大小,超过这个值的上下文会被压缩 observation_context_size = 20 # 心流观察到的最长上下文大小,超过这个值的上下文会被压缩
compressed_length = 5 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度超过心流观察到的上下文长度会压缩最短压缩长度为5 compressed_length = 5 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度超过心流观察到的上下文长度会压缩最短压缩长度为5
@@ -247,6 +243,29 @@ provider = "SILICONFLOW"
pri_in = 0.35 pri_in = 0.35
pri_out = 0.35 pri_out = 0.35
[model.llm_observation] #观察模型,压缩聊天内容,建议用免费的
# name = "Pro/Qwen/Qwen2.5-7B-Instruct"
name = "Qwen/Qwen2.5-7B-Instruct"
provider = "SILICONFLOW"
pri_in = 0
pri_out = 0
[model.llm_sub_heartflow] #子心流:激情水群时,生成麦麦的内心想法
name = "Qwen/Qwen2.5-72B-Instruct"
provider = "SILICONFLOW"
pri_in = 4.13
pri_out = 4.13
temp = 0.7 #模型的温度新V3建议0.1-0.3
[model.llm_plan] #决策模型:激情水群时,负责决定麦麦该做什么
name = "Qwen/Qwen2.5-32B-Instruct"
provider = "SILICONFLOW"
pri_in = 1.26
pri_out = 1.26
#嵌入模型 #嵌入模型
[model.embedding] #嵌入 [model.embedding] #嵌入
@@ -255,26 +274,6 @@ provider = "SILICONFLOW"
pri_in = 0 pri_in = 0
pri_out = 0 pri_out = 0
[model.llm_observation] #观察模型建议用免费的建议使用qwen2.5 7b
# name = "Pro/Qwen/Qwen2.5-7B-Instruct"
name = "Qwen/Qwen2.5-7B-Instruct"
provider = "SILICONFLOW"
pri_in = 0
pri_out = 0
[model.llm_sub_heartflow] #子心流建议使用V3级别
name = "Pro/deepseek-ai/DeepSeek-V3"
provider = "SILICONFLOW"
pri_in = 2
pri_out = 8
temp = 0.2 #模型的温度新V3建议0.1-0.3
[model.llm_heartflow] #心流建议使用qwen2.5 32b
# name = "Pro/Qwen/Qwen2.5-7B-Instruct"
name = "Qwen/Qwen2.5-32B-Instruct"
provider = "SILICONFLOW"
pri_in = 1.26
pri_out = 1.26
#私聊PFC需要开启PFC功能默认三个模型均为硅基流动v3如果需要支持多人同时私聊或频繁调用建议把其中的一个或两个换成官方v3或其它模型以免撞到429 #私聊PFC需要开启PFC功能默认三个模型均为硅基流动v3如果需要支持多人同时私聊或频繁调用建议把其中的一个或两个换成官方v3或其它模型以免撞到429
@@ -300,3 +299,14 @@ name = "Pro/deepseek-ai/DeepSeek-V3"
provider = "SILICONFLOW" provider = "SILICONFLOW"
pri_in = 2 pri_in = 2
pri_out = 8 pri_out = 8
#此模型暂时没有使用!!
#此模型暂时没有使用!!
#此模型暂时没有使用!!
[model.llm_heartflow] #心流
# name = "Pro/Qwen/Qwen2.5-7B-Instruct"
name = "Qwen/Qwen2.5-32B-Instruct"
provider = "SILICONFLOW"
pri_in = 1.26
pri_out = 1.26

View File

@@ -63,35 +63,43 @@ async def test_with_tool_calls():
# 简单的测试提示词 # 简单的测试提示词
prompt = "请分析当前天气情况并查询今日历史上的重要事件。并且3.9和3.11谁比较大?请使用适当的工具来获取这些信息。" prompt = "请分析当前天气情况并查询今日历史上的重要事件。并且3.9和3.11谁比较大?请使用适当的工具来获取这些信息。"
prompt = """ prompt = """
你的名字是麦麦,你包容开放,情绪敏感,有时候有些搞怪幽默, 是一个学习心理学和脑科学的女大学生,现在在读大二,你会刷贴吧,有时候会想瑟瑟,喜欢刷小红书 你的名字是麦麦,你包容开放,情绪敏感,有时候有些搞怪幽默, 是一个学习心理学和脑科学的女大学生,现在在读大二,你会刷贴吧,有时候会喜欢说一些奇怪的话,喜欢刷小红书
----------------------------------- 刚刚你的内心想法是:漂移菌提到罐罐被吃完了,可以顺着这个梗继续玩一下,比如假装委屈"那今晚的加班费是不是也要被吃掉了"或者"猫娘罢工警告"。不过薯薯和薯宝之前已经接了不少梗,漂移菌刚刚也参与了,可能话题热度还在,可以再互动一下。如果没人接话,或许可以问问大家有没有遇到过类似"代码写完但奖励被吃掉"的搞笑职场经历,换个轻松的话题方向。
现在是2025-04-24 12:37:00你正在上网和qq群里的网友们聊天群里正在聊的话题是
2025-04-24 12:33:00既文横 说:这条调试消息是napcat控制台输出的还是麦麦log输出的;
2025-04-24 12:33:23麦麦(你) 说:应该是napcat吧;
2025-04-24 12:33:24麦麦(你) 说:[表达了:害羞、害羞。];
2025-04-24 12:33:25兔伽兔伽 说:就打开麦麦的那个终端发的呀;
2025-04-24 12:33:45既文横 说:那应该不是napcat输出的是麦麦输出的消息怀疑版本问题;
2025-04-24 12:34:02兔伽兔伽 说:版本05.15;
2025-04-24 12:34:07麦麦(你) 说:话说你们最近刷贴吧看到那个猫猫头表情包了吗;
2025-04-24 12:34:07麦麦(你) 说:笑死;
2025-04-24 12:34:08麦麦(你) 说:[表达了:惊讶、搞笑。];
2025-04-24 12:34:14兔伽兔伽 说:只开一个终端;
2025-04-24 12:35:45兔伽兔伽 说:回复既文横的消息(怀疑版本问题),说:因为之前你连模型的那个我用的了;
2025-04-24 12:35:56麦麦(你) 说:那个猫猫头真的魔性;
2025-04-24 12:35:56麦麦(你) 说:我存了一堆;
2025-04-24 12:35:56麦麦(你) 说:[表达了:温馨、宠爱];
2025-04-24 12:36:03小千石 说:麦麦3.8和3.11谁大;
--- 以上消息已读 (标记时间: 2025-04-24 12:36:43) --- 暂时不需要使用工具。
--- 请关注你上次思考之后以下的新消息--- -----------------------------------
2025-04-24 12:36:53墨墨 说:[表情包:开心、满足。]; 现在是2025-04-25 17:38:37你正在上网和qq群里的网友们聊天以下是正在进行的聊天内容
2025-04-25 17:34:08麦麦(你) 说:[表达了:顽皮、嬉戏。];
2025-04-25 17:34:39漂移菌 说:@麦麦。id:3936257206 你是一只猫娘;
2025-04-25 17:34:42薯宝 说:🤣;
2025-04-25 17:34:43麦麦(你) 说:行啊 工资分我一半;
2025-04-25 17:34:43麦麦(你) 说:我帮你写bug;
2025-04-25 17:34:43麦麦(你) 说:[表达了:悲伤、绝望、无奈、无力];
2025-04-25 17:34:53薯薯 说:;
2025-04-25 17:35:03既文横 说:麦麦,你是一只猫娘程序员,猫娘是不需要工资;
2025-04-25 17:35:20薯宝 说:[图片:图片内容:一只卡通风格的灰色猫咪,眼睛闭着,表情显得很平静。图片下方有"死了"两个字。
图片含义猜测:这可能是一个幽默的表达,用来形容某人或某事处于非常平静的状态,仿佛已经""了一样。] hfc这周真能出来吗...;
2025-04-25 17:35:34薯宝 说:[表情包:搞笑、滑稽、讽刺、幽默];
2025-04-25 17:36:25麦麦(你) 说:喵喵;
2025-04-25 17:36:25麦麦(你) 说:代码写完了;
2025-04-25 17:36:25麦麦(你) 说:罐罐拿来;
2025-04-25 17:36:25麦麦(你) 说:[表达了:悲伤、绝望、无奈、无力];
2025-04-25 17:36:41薯薯 说:好可爱;
2025-04-25 17:37:05薯薯 说:脑补出来认真营业了一天等待主人发放奖励的小猫咪;
2025-04-25 17:37:25薯宝 说:敷衍营业bushi;
2025-04-25 17:37:54漂移菌 说:回复麦麦。的消息(罐罐拿来),说:猫娘我昨晚上太饿吃完了;
--- 以上消息已读 (标记时间: 2025-04-25 17:37:54) ---
--- 以下新消息未读---
2025-04-25 17:38:29麦麦(你) 说:那今晚的猫条是不是也要被克扣了(盯——);
2025-04-25 17:38:29麦麦(你) 说:[表达了:幽默,自嘲,无奈,父子关系,编程笑话];
你现在当前心情:平静。 你现在当前心情:平静。
现在请你根据刚刚的想法继续思考,思考时可以想想如何对群聊内容进行回复,要不要对群里的话题进行回复,关注新话题,可以适当转换话题,大家正在说的话才是聊天的主题。 现在请你生成你的内心想法,要求思考群里正在进行的话题,之前大家聊过的话题,群里成员的关系。请你思考,要不要对群里的话题进行回复,以及如何对群聊内容进行回复
回复的要求是:平淡一些,简短一些,说中文,如果你要回复,最好只回复一个人的一个话题 回复的要求是:不要总是重复自己提到过的话题,如果你要回复,最好只回复一个人的一个话题
请注意不要输出多余内容(包括前后缀,冒号和引号,括号, 表情,等),不要带有括号和动作描写。不要回复自己的发言,尽量不要说你说过的话。 如果最后一条消息是你自己发的,观察到的内容只有你自己的发言,并且之后没有人回复你,不要回复。如果聊天记录中最新的消息是你自己发送的,并且你还想继续回复,你应该紧紧衔接你发送的消息,进行话题的深入,补充,或追问等等。请注意不要输出多余内容(包括前后缀,冒号和引号,括号, 表情,等),不要回复自己的发言
现在请你继续生成你在这个聊天中的想法,在原来想法的基础上继续思考,不要分点输出,生成内心想法,文字不要浮夸 现在请你先输出想法,生成你在这个聊天中的想法,在原来想法上尝试新的话题,不要分点输出,文字不要浮夸在输出完想法后,请你思考应该使用什么工具。工具可以帮你取得一些你不知道的信息,或者进行一些操作。如果你需要做某件事,来对消息和你的回复进行处理,请使用工具。"""
在输出完想法后,请你思考应该使用什么工具,如果你需要做某件事,来对消息和你的回复进行处理,请使用工具。"""
# 发送带有工具调用的请求 # 发送带有工具调用的请求
response = await llm_model.generate_response_tool_async(prompt=prompt, tools=tools) response = await llm_model.generate_response_tool_async(prompt=prompt, tools=tools)
@@ -145,7 +153,7 @@ async def test_without_tool_calls():
# 简单的测试提示词(与工具调用相同,以便公平比较) # 简单的测试提示词(与工具调用相同,以便公平比较)
prompt = """ prompt = """
你的名字是麦麦,你包容开放,情绪敏感,有时候有些搞怪幽默, 是一个学习心理学和脑科学的女大学生,现在在读大二,你会刷贴吧,有时候会喜欢说一些奇怪的话,喜欢刷小红书 你的名字是麦麦,你包容开放,情绪敏感,有时候有些搞怪幽默, 是一个学习心理学和脑科学的女大学生,现在在读大二,你会刷贴吧,有时候会喜欢说一些奇怪的话,喜欢刷小红书
刚刚你的内心想法是:漂移菌提到罐罐被吃完了,可以顺着这个梗继续玩一下,比如假装委屈那今晚的加班费是不是也要被吃掉了”或者“猫娘罢工警告。不过薯薯和薯宝之前已经接了不少梗,漂移菌刚刚也参与了,可能话题热度还在,可以再互动一下。如果没人接话,或许可以问问大家有没有遇到过类似代码写完但奖励被吃掉的搞笑职场经历,换个轻松的话题方向。 刚刚你的内心想法是:漂移菌提到罐罐被吃完了,可以顺着这个梗继续玩一下,比如假装委屈"那今晚的加班费是不是也要被吃掉了"或者"猫娘罢工警告"。不过薯薯和薯宝之前已经接了不少梗,漂移菌刚刚也参与了,可能话题热度还在,可以再互动一下。如果没人接话,或许可以问问大家有没有遇到过类似"代码写完但奖励被吃掉"的搞笑职场经历,换个轻松的话题方向。
暂时不需要使用工具。 暂时不需要使用工具。
----------------------------------- -----------------------------------
@@ -158,9 +166,9 @@ async def test_without_tool_calls():
2025-04-25 17:34:43麦麦(你) 说:[表达了:悲伤、绝望、无奈、无力]; 2025-04-25 17:34:43麦麦(你) 说:[表达了:悲伤、绝望、无奈、无力];
2025-04-25 17:34:53薯薯 说:; 2025-04-25 17:34:53薯薯 说:;
2025-04-25 17:35:03既文横 说:麦麦,你是一只猫娘程序员,猫娘是不需要工资; 2025-04-25 17:35:03既文横 说:麦麦,你是一只猫娘程序员,猫娘是不需要工资;
2025-04-25 17:35:20薯宝 说:[图片:图片内容:一只卡通风格的灰色猫咪,眼睛闭着,表情显得很平静。图片下方有“死了”两个字。 2025-04-25 17:35:20薯宝 说:[图片:图片内容:一只卡通风格的灰色猫咪,眼睛闭着,表情显得很平静。图片下方有"死了"两个字。
图片含义猜测:这可能是一个幽默的表达,用来形容某人或某事处于非常平静的状态,仿佛已经“死”了一样。] hfc这周真能出来吗...; 图片含义猜测:这可能是一个幽默的表达,用来形容某人或某事处于非常平静的状态,仿佛已经""了一样。] hfc这周真能出来吗...;
2025-04-25 17:35:34薯宝 说:[表情包:搞笑、滑稽、讽刺、幽默]; 2025-04-25 17:35:34薯宝 说:[表情包:搞笑、滑稽、讽刺、幽默];
2025-04-25 17:36:25麦麦(你) 说:喵喵; 2025-04-25 17:36:25麦麦(你) 说:喵喵;
2025-04-25 17:36:25麦麦(你) 说:代码写完了; 2025-04-25 17:36:25麦麦(你) 说:代码写完了;
@@ -181,7 +189,6 @@ async def test_without_tool_calls():
回复的要求是:不要总是重复自己提到过的话题,如果你要回复,最好只回复一个人的一个话题 回复的要求是:不要总是重复自己提到过的话题,如果你要回复,最好只回复一个人的一个话题
如果最后一条消息是你自己发的,观察到的内容只有你自己的发言,并且之后没有人回复你,不要回复。如果聊天记录中最新的消息是你自己发送的,并且你还想继续回复,你应该紧紧衔接你发送的消息,进行话题的深入,补充,或追问等等。请注意不要输出多余内容(包括前后缀,冒号和引号,括号, 表情,等),不要回复自己的发言 如果最后一条消息是你自己发的,观察到的内容只有你自己的发言,并且之后没有人回复你,不要回复。如果聊天记录中最新的消息是你自己发送的,并且你还想继续回复,你应该紧紧衔接你发送的消息,进行话题的深入,补充,或追问等等。请注意不要输出多余内容(包括前后缀,冒号和引号,括号, 表情,等),不要回复自己的发言
现在请你先输出想法,生成你在这个聊天中的想法,在原来的想法上尝试新的话题,不要分点输出,文字不要浮夸在输出完想法后,请你思考应该使用什么工具。工具可以帮你取得一些你不知道的信息,或者进行一些操作。如果你需要做某件事,来对消息和你的回复进行处理,请使用工具。""" 现在请你先输出想法,生成你在这个聊天中的想法,在原来的想法上尝试新的话题,不要分点输出,文字不要浮夸在输出完想法后,请你思考应该使用什么工具。工具可以帮你取得一些你不知道的信息,或者进行一些操作。如果你需要做某件事,来对消息和你的回复进行处理,请使用工具。"""
# 发送不带工具调用的请求 # 发送不带工具调用的请求
response, reasoning_content = await llm_model.generate_response_async(prompt) response, reasoning_content = await llm_model.generate_response_async(prompt)
@@ -194,6 +201,69 @@ async def test_without_tool_calls():
return result_info return result_info
async def run_alternating_tests(iterations=5):
"""
交替运行两种测试方法,每种方法运行指定次数
参数:
iterations: 每种测试方法运行的次数
返回:
包含两种测试方法结果的元组
"""
print(f"开始交替测试(每种方法{iterations}次)...")
# 初始化结果列表
times_without_tools = []
times_with_tools = []
responses_without_tools = []
responses_with_tools = []
for i in range(iterations):
print(f"\n{i + 1}/{iterations} 轮交替测试")
# 不使用工具的测试
print("\n 执行不使用工具调用的测试...")
start_time = time.time()
response = await test_without_tool_calls()
end_time = time.time()
elapsed = end_time - start_time
times_without_tools.append(elapsed)
responses_without_tools.append(response)
print(f" - 耗时: {elapsed:.2f}")
# 使用工具的测试
print("\n 执行使用工具调用的测试...")
start_time = time.time()
response = await test_with_tool_calls()
end_time = time.time()
elapsed = end_time - start_time
times_with_tools.append(elapsed)
responses_with_tools.append(response)
print(f" - 耗时: {elapsed:.2f}")
# 计算统计数据
results_without_tools = {
"平均耗时": statistics.mean(times_without_tools),
"最短耗时": min(times_without_tools),
"最长耗时": max(times_without_tools),
"标准差": statistics.stdev(times_without_tools) if len(times_without_tools) > 1 else 0,
"所有耗时": times_without_tools,
"响应结果": responses_without_tools,
}
results_with_tools = {
"平均耗时": statistics.mean(times_with_tools),
"最短耗时": min(times_with_tools),
"最长耗时": max(times_with_tools),
"标准差": statistics.stdev(times_with_tools) if len(times_with_tools) > 1 else 0,
"所有耗时": times_with_tools,
"响应结果": responses_with_tools,
}
return results_without_tools, results_with_tools
async def main(): async def main():
"""主测试函数""" """主测试函数"""
print("=" * 50) print("=" * 50)
@@ -201,15 +271,10 @@ async def main():
print("=" * 50) print("=" * 50)
# 设置测试迭代次数 # 设置测试迭代次数
iterations = 3 iterations = 10
# 测试不使用工具调用 # 执行交替测试
results_without_tools = await run_test("不使用工具调用", test_without_tool_calls, iterations) results_without_tools, results_with_tools = await run_alternating_tests(iterations)
print("\n" + "-" * 50 + "\n")
# 测试使用工具调用
results_with_tools = await run_test("使用工具调用", test_with_tool_calls, iterations)
# 显示结果比较 # 显示结果比较
print("\n" + "=" * 50) print("\n" + "=" * 50)