mirror of https://github.com/Mai-with-u/MaiBot.git
Merge branch 'MaiM-with-u:dev' into dev
commit
9df7f8d7f1
|
|
@ -1,12 +1,10 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [0.7.0] -2025-6-1
|
## [0.7.0] -2025-6-1
|
||||||
- 重构数据库,弃用MongoDB,采用轻量sqlite,无需额外安装
|
- 你可以选择normal,focus和auto多种不同的聊天方式。normal提供更少的消耗,更快的回复速度。focus提供更好的聊天理解,更多工具使用和插件能力
|
||||||
- 重构HFC,可扩展的聊天模式,支持独立的表达模式
|
- 现在,你可以自定义麦麦的表达方式,并且麦麦也可以学习群友的聊天风格(需要在配置文件中打开)
|
||||||
- HFC,丰富HFC的决策信息,更好的把握聊天内容
|
- 不再需要繁琐的安装MongoDB!弃用MongoDB,采用轻量sqlite,无需额外安装(提供数据迁移脚本)
|
||||||
- HFC初步支持插件v0.1(测试版)
|
- focus模式初步支持了插件,我们提供了两个示例插件(需要手动启用),可以让麦麦实现更丰富的操作。禁言插件和豆包绘图插件是示例用插件。
|
||||||
- 重构表情包模块
|
|
||||||
- 移除日程系统
|
|
||||||
|
|
||||||
**重构专注聊天(HFC - focus_chat)**
|
**重构专注聊天(HFC - focus_chat)**
|
||||||
- 模块化设计,可以自定义不同的部件
|
- 模块化设计,可以自定义不同的部件
|
||||||
|
|
@ -30,10 +28,14 @@
|
||||||
- 为专注模式添加关系线索
|
- 为专注模式添加关系线索
|
||||||
- 在专注模式下,麦麦可以决定自行发送语音消息(需要搭配tts适配器)
|
- 在专注模式下,麦麦可以决定自行发送语音消息(需要搭配tts适配器)
|
||||||
- 优化reply,减少复读
|
- 优化reply,减少复读
|
||||||
|
- 可自定义连续回复次数
|
||||||
|
- 可自定义处理器超时时间
|
||||||
|
|
||||||
**优化普通聊天(normal_chat)**
|
**优化普通聊天(normal_chat)**
|
||||||
|
- 添加可学习的表达方式
|
||||||
- 增加了talk_frequency参数来有效控制回复频率
|
- 增加了talk_frequency参数来有效控制回复频率
|
||||||
- 优化了进入和离开normal_chat的方式
|
- 优化了进入和离开normal_chat的方式
|
||||||
|
- 添加时间信息
|
||||||
|
|
||||||
**新增表达方式学习**
|
**新增表达方式学习**
|
||||||
- 麦麦配置单独表达方式
|
- 麦麦配置单独表达方式
|
||||||
|
|
@ -48,7 +50,6 @@
|
||||||
- 移除聊天限额数量
|
- 移除聊天限额数量
|
||||||
|
|
||||||
**插件系统**
|
**插件系统**
|
||||||
- 添加示例插件
|
|
||||||
- 示例插件:禁言插件
|
- 示例插件:禁言插件
|
||||||
- 示例插件:豆包绘图插件
|
- 示例插件:豆包绘图插件
|
||||||
|
|
||||||
|
|
@ -65,6 +66,9 @@
|
||||||
- 移除日程系统,减少幻觉(将会在未来版本回归)
|
- 移除日程系统,减少幻觉(将会在未来版本回归)
|
||||||
- 移除主心流思考和LLM进入聊天判定
|
- 移除主心流思考和LLM进入聊天判定
|
||||||
- 支持qwen3模型,支持自定义是否思考和思考长度
|
- 支持qwen3模型,支持自定义是否思考和思考长度
|
||||||
|
- 优化提及和at的判定
|
||||||
|
- 添加配置项
|
||||||
|
- 添加临时配置文件读取器
|
||||||
|
|
||||||
|
|
||||||
## [0.6.3-fix-4] - 2025-5-18
|
## [0.6.3-fix-4] - 2025-5-18
|
||||||
|
|
|
||||||
|
|
@ -403,15 +403,15 @@ class ConfigEditor:
|
||||||
# 创建模型名称标签(大字体)
|
# 创建模型名称标签(大字体)
|
||||||
model_name = var.get() if var.get() else providers[0]
|
model_name = var.get() if var.get() else providers[0]
|
||||||
section_translations = {
|
section_translations = {
|
||||||
"model.utils": "工具模型",
|
"model.utils": "麦麦组件模型",
|
||||||
"model.utils_small": "小型工具模型",
|
"model.utils_small": "小型麦麦组件模型",
|
||||||
"model.memory_summary": "记忆概括模型",
|
"model.memory_summary": "记忆概括模型",
|
||||||
"model.vlm": "图像识别模型",
|
"model.vlm": "图像识别模型",
|
||||||
"model.embedding": "嵌入模型",
|
"model.embedding": "嵌入模型",
|
||||||
"model.normal_chat_1": "普通聊天:主要聊天模型",
|
"model.normal_chat_1": "普通聊天:主要聊天模型",
|
||||||
"model.normal_chat_2": "普通聊天:次要聊天模型",
|
"model.normal_chat_2": "普通聊天:次要聊天模型",
|
||||||
"model.focus_working_memory": "专注模式:工作记忆模型",
|
"model.focus_working_memory": "专注模式:工作记忆模型",
|
||||||
"model.focus_chat_mind": "专注模式:聊天规划模型",
|
"model.focus_chat_mind": "专注模式:聊天思考模型",
|
||||||
"model.focus_tool_use": "专注模式:工具调用模型",
|
"model.focus_tool_use": "专注模式:工具调用模型",
|
||||||
"model.focus_planner": "专注模式:决策模型",
|
"model.focus_planner": "专注模式:决策模型",
|
||||||
"model.focus_expressor": "专注模式:表达器模型",
|
"model.focus_expressor": "专注模式:表达器模型",
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,15 @@ description = "思考的时间间隔(秒),可以有效减少消耗"
|
||||||
name = "连续回复能力"
|
name = "连续回复能力"
|
||||||
description = "连续回复能力,值越高,麦麦连续回复的概率越高"
|
description = "连续回复能力,值越高,麦麦连续回复的概率越高"
|
||||||
|
|
||||||
|
[translations.items.parallel_processing]
|
||||||
|
name = "并行处理"
|
||||||
|
description = "是否并行处理回忆和处理器阶段,可以节省时间"
|
||||||
|
|
||||||
|
[translations.items.processor_max_time]
|
||||||
|
name = "处理器最大时间"
|
||||||
|
description = "处理器最大时间,单位秒,如果超过这个时间,处理器会自动停止"
|
||||||
|
|
||||||
|
|
||||||
[translations.items.observation_context_size]
|
[translations.items.observation_context_size]
|
||||||
name = "观察上下文大小"
|
name = "观察上下文大小"
|
||||||
description = "观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖"
|
description = "观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖"
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ def init_prompt():
|
||||||
你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。
|
你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。
|
||||||
请你根据情景使用以下句法:
|
请你根据情景使用以下句法:
|
||||||
{grammar_habbits}
|
{grammar_habbits}
|
||||||
回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,你可以完全重组回复,保留最基本的表达含义就好,但注意回复要简短,但重组后保持语意通顺。
|
{config_expression_style},你可以完全重组回复,保留最基本的表达含义就好,但重组后保持语意通顺。
|
||||||
回复不要浮夸,不要用夸张修辞,平淡一些。不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 ),只输出一条回复就好。
|
不要浮夸,不要夸张修辞,平淡且不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 ),只输出一条回复就好。
|
||||||
现在,你说:
|
现在,你说:
|
||||||
""",
|
""",
|
||||||
"default_expressor_prompt",
|
"default_expressor_prompt",
|
||||||
|
|
@ -63,8 +63,8 @@ def init_prompt():
|
||||||
你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。
|
你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。
|
||||||
请你根据情景使用以下句法:
|
请你根据情景使用以下句法:
|
||||||
{grammar_habbits}
|
{grammar_habbits}
|
||||||
回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,你可以完全重组回复,保留最基本的表达含义就好,但注意回复要简短,但重组后保持语意通顺。
|
{config_expression_style},你可以完全重组回复,保留最基本的表达含义就好,但重组后保持语意通顺。
|
||||||
回复不要浮夸,不要用夸张修辞,平淡一些。不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 ),只输出一条回复就好。
|
不要浮夸,不要夸张修辞,平淡且不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 ),只输出一条回复就好。
|
||||||
现在,你说:
|
现在,你说:
|
||||||
""",
|
""",
|
||||||
"default_expressor_private_prompt", # New template for private FOCUSED chat
|
"default_expressor_private_prompt", # New template for private FOCUSED chat
|
||||||
|
|
@ -128,7 +128,7 @@ class DefaultExpressor:
|
||||||
# 创建思考消息
|
# 创建思考消息
|
||||||
await self._create_thinking_message(anchor_message, thinking_id)
|
await self._create_thinking_message(anchor_message, thinking_id)
|
||||||
|
|
||||||
reply = None # 初始化 reply,防止未定义
|
reply = [] # 初始化 reply,防止未定义
|
||||||
try:
|
try:
|
||||||
has_sent_something = False
|
has_sent_something = False
|
||||||
|
|
||||||
|
|
@ -216,6 +216,7 @@ class DefaultExpressor:
|
||||||
reason=reason,
|
reason=reason,
|
||||||
sender_name=sender_name_for_prompt, # Pass determined name
|
sender_name=sender_name_for_prompt, # Pass determined name
|
||||||
target_message=target_message,
|
target_message=target_message,
|
||||||
|
config_expression_style=global_config.expression.expression_style,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 4. 调用 LLM 生成回复
|
# 4. 调用 LLM 生成回复
|
||||||
|
|
@ -230,7 +231,7 @@ class DefaultExpressor:
|
||||||
with Timer("LLM生成", {}): # 内部计时器,可选保留
|
with Timer("LLM生成", {}): # 内部计时器,可选保留
|
||||||
# TODO: API-Adapter修改标记
|
# TODO: API-Adapter修改标记
|
||||||
# logger.info(f"{self.log_prefix}[Replier-{thinking_id}]\nPrompt:\n{prompt}\n")
|
# logger.info(f"{self.log_prefix}[Replier-{thinking_id}]\nPrompt:\n{prompt}\n")
|
||||||
content, reasoning_content, model_name = await self.express_model.generate_response(prompt)
|
content, (reasoning_content, model_name) = await self.express_model.generate_response_async(prompt)
|
||||||
|
|
||||||
# logger.info(f"{self.log_prefix}\nPrompt:\n{prompt}\n---------------------------\n")
|
# logger.info(f"{self.log_prefix}\nPrompt:\n{prompt}\n---------------------------\n")
|
||||||
|
|
||||||
|
|
@ -275,6 +276,7 @@ class DefaultExpressor:
|
||||||
sender_name,
|
sender_name,
|
||||||
in_mind_reply,
|
in_mind_reply,
|
||||||
target_message,
|
target_message,
|
||||||
|
config_expression_style,
|
||||||
) -> str:
|
) -> str:
|
||||||
is_group_chat = bool(chat_stream.group_info)
|
is_group_chat = bool(chat_stream.group_info)
|
||||||
|
|
||||||
|
|
@ -343,6 +345,7 @@ class DefaultExpressor:
|
||||||
reason=reason,
|
reason=reason,
|
||||||
in_mind_reply=in_mind_reply,
|
in_mind_reply=in_mind_reply,
|
||||||
target_message=target_message,
|
target_message=target_message,
|
||||||
|
config_expression_style=config_expression_style,
|
||||||
)
|
)
|
||||||
else: # Private chat
|
else: # Private chat
|
||||||
template_name = "default_expressor_private_prompt"
|
template_name = "default_expressor_private_prompt"
|
||||||
|
|
@ -358,6 +361,7 @@ class DefaultExpressor:
|
||||||
reason=reason,
|
reason=reason,
|
||||||
in_mind_reply=in_mind_reply,
|
in_mind_reply=in_mind_reply,
|
||||||
target_message=target_message,
|
target_message=target_message,
|
||||||
|
config_expression_style=config_expression_style,
|
||||||
)
|
)
|
||||||
|
|
||||||
return prompt
|
return prompt
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,13 @@ def init_prompt() -> None:
|
||||||
learn_style_prompt = """
|
learn_style_prompt = """
|
||||||
{chat_str}
|
{chat_str}
|
||||||
|
|
||||||
请从上面这段群聊中概括除了人名为"SELF"之外的人的语言风格,只考虑文字,不要考虑表情包和图片
|
请从上面这段群聊中概括除了人名为"SELF"之外的人的语言风格
|
||||||
不要涉及具体的人名,只考虑语言风格
|
1. 只考虑文字,不要考虑表情包和图片
|
||||||
语言风格包含特殊内容和情感
|
2. 不要涉及具体的人名,只考虑语言风格
|
||||||
思考有没有特殊的梗,一并总结成语言风格
|
3. 语言风格包含特殊内容和情感
|
||||||
总结成如下格式的规律,总结的内容要详细,但具有概括性:
|
4. 思考有没有特殊的梗,一并总结成语言风格
|
||||||
|
5. 例子仅供参考,请严格根据群聊内容总结!!!
|
||||||
|
注意:总结成如下格式的规律,总结的内容要详细,但具有概括性:
|
||||||
当"xxx"时,可以"xxx", xxx不超过10个字
|
当"xxx"时,可以"xxx", xxx不超过10个字
|
||||||
|
|
||||||
例如:
|
例如:
|
||||||
|
|
@ -31,7 +33,7 @@ def init_prompt() -> None:
|
||||||
当"表示讽刺的赞同,不想讲道理"时,使用"对对对"
|
当"表示讽刺的赞同,不想讲道理"时,使用"对对对"
|
||||||
当"想说明某个观点,但懒得明说",使用"懂的都懂"
|
当"想说明某个观点,但懒得明说",使用"懂的都懂"
|
||||||
|
|
||||||
注意不要总结你自己的发言
|
注意不要总结你自己(SELF)的发言
|
||||||
现在请你概括
|
现在请你概括
|
||||||
"""
|
"""
|
||||||
Prompt(learn_style_prompt, "learn_style_prompt")
|
Prompt(learn_style_prompt, "learn_style_prompt")
|
||||||
|
|
@ -40,9 +42,10 @@ def init_prompt() -> None:
|
||||||
{chat_str}
|
{chat_str}
|
||||||
|
|
||||||
请从上面这段群聊中概括除了人名为"SELF"之外的人的语法和句法特点,只考虑纯文字,不要考虑表情包和图片
|
请从上面这段群聊中概括除了人名为"SELF"之外的人的语法和句法特点,只考虑纯文字,不要考虑表情包和图片
|
||||||
不要总结【图片】,【动画表情】,[图片],[动画表情],不总结 表情符号 at @ 回复 和[回复]
|
1.不要总结【图片】,【动画表情】,[图片],[动画表情],不总结 表情符号 at @ 回复 和[回复]
|
||||||
不要涉及具体的人名,只考虑语法和句法特点,
|
2.不要涉及具体的人名,只考虑语法和句法特点,
|
||||||
语法和句法特点要包括,句子长短(具体字数),有何种语病,如何拆分句子。
|
3.语法和句法特点要包括,句子长短(具体字数),有何种语病,如何拆分句子。
|
||||||
|
4. 例子仅供参考,请严格根据群聊内容总结!!!
|
||||||
总结成如下格式的规律,总结的内容要简洁,不浮夸:
|
总结成如下格式的规律,总结的内容要简洁,不浮夸:
|
||||||
当"xxx"时,可以"xxx"
|
当"xxx"时,可以"xxx"
|
||||||
|
|
||||||
|
|
@ -51,7 +54,7 @@ def init_prompt() -> None:
|
||||||
当"不用详细说明的一般表达"时,使用"非常简洁的句子"的句法
|
当"不用详细说明的一般表达"时,使用"非常简洁的句子"的句法
|
||||||
当"需要单纯简单的确认"时,使用"单字或几个字的肯定(1-2个字)"的句法
|
当"需要单纯简单的确认"时,使用"单字或几个字的肯定(1-2个字)"的句法
|
||||||
|
|
||||||
注意不要总结你自己的发言
|
注意不要总结你自己(SELF)的发言
|
||||||
现在请你概括
|
现在请你概括
|
||||||
"""
|
"""
|
||||||
Prompt(learn_grammar_prompt, "learn_grammar_prompt")
|
Prompt(learn_grammar_prompt, "learn_grammar_prompt")
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@ import time
|
||||||
import os
|
import os
|
||||||
from typing import List, Optional, Dict, Any
|
from typing import List, Optional, Dict, Any
|
||||||
|
|
||||||
|
log_dir = "log/log_cycle_debug/"
|
||||||
|
|
||||||
class CycleDetail:
|
class CycleDetail:
|
||||||
"""循环信息记录类"""
|
"""循环信息记录类"""
|
||||||
|
|
||||||
def __init__(self, cycle_id: int):
|
def __init__(self, cycle_id: int):
|
||||||
self.cycle_id = cycle_id
|
self.cycle_id = cycle_id
|
||||||
|
self.prefix = ""
|
||||||
self.thinking_id = ""
|
self.thinking_id = ""
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
self.end_time: Optional[float] = None
|
self.end_time: Optional[float] = None
|
||||||
|
|
@ -21,21 +23,80 @@ class CycleDetail:
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
"""将循环信息转换为字典格式"""
|
"""将循环信息转换为字典格式"""
|
||||||
|
def convert_to_serializable(obj, depth=0, seen=None):
|
||||||
|
if seen is None:
|
||||||
|
seen = set()
|
||||||
|
|
||||||
|
# 防止递归过深
|
||||||
|
if depth > 5: # 降低递归深度限制
|
||||||
|
return str(obj)
|
||||||
|
|
||||||
|
# 防止循环引用
|
||||||
|
obj_id = id(obj)
|
||||||
|
if obj_id in seen:
|
||||||
|
return str(obj)
|
||||||
|
seen.add(obj_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if hasattr(obj, 'to_dict'):
|
||||||
|
# 对于有to_dict方法的对象,直接调用其to_dict方法
|
||||||
|
return obj.to_dict()
|
||||||
|
elif isinstance(obj, dict):
|
||||||
|
# 对于字典,只保留基本类型和可序列化的值
|
||||||
|
return {k: convert_to_serializable(v, depth + 1, seen)
|
||||||
|
for k, v in obj.items()
|
||||||
|
if isinstance(k, (str, int, float, bool))}
|
||||||
|
elif isinstance(obj, (list, tuple)):
|
||||||
|
# 对于列表和元组,只保留可序列化的元素
|
||||||
|
return [convert_to_serializable(item, depth + 1, seen)
|
||||||
|
for item in obj
|
||||||
|
if not isinstance(item, (dict, list, tuple)) or
|
||||||
|
isinstance(item, (str, int, float, bool, type(None)))]
|
||||||
|
elif isinstance(obj, (str, int, float, bool, type(None))):
|
||||||
|
return obj
|
||||||
|
else:
|
||||||
|
return str(obj)
|
||||||
|
finally:
|
||||||
|
seen.remove(obj_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"cycle_id": self.cycle_id,
|
"cycle_id": self.cycle_id,
|
||||||
"start_time": self.start_time,
|
"start_time": self.start_time,
|
||||||
"end_time": self.end_time,
|
"end_time": self.end_time,
|
||||||
"timers": self.timers,
|
"timers": self.timers,
|
||||||
"thinking_id": self.thinking_id,
|
"thinking_id": self.thinking_id,
|
||||||
"loop_observation_info": self.loop_observation_info,
|
"loop_observation_info": convert_to_serializable(self.loop_observation_info),
|
||||||
"loop_process_info": self.loop_process_info,
|
"loop_process_info": convert_to_serializable(self.loop_process_info),
|
||||||
"loop_plan_info": self.loop_plan_info,
|
"loop_plan_info": convert_to_serializable(self.loop_plan_info),
|
||||||
"loop_action_info": self.loop_action_info,
|
"loop_action_info": convert_to_serializable(self.loop_action_info),
|
||||||
}
|
}
|
||||||
|
|
||||||
def complete_cycle(self):
|
def complete_cycle(self):
|
||||||
"""完成循环,记录结束时间"""
|
"""完成循环,记录结束时间"""
|
||||||
self.end_time = time.time()
|
self.end_time = time.time()
|
||||||
|
|
||||||
|
# 处理 prefix,只保留中英文字符
|
||||||
|
if not self.prefix:
|
||||||
|
self.prefix = "group"
|
||||||
|
else:
|
||||||
|
# 只保留中文和英文字符
|
||||||
|
self.prefix = ''.join(char for char in self.prefix if '\u4e00' <= char <= '\u9fff' or char.isascii())
|
||||||
|
if not self.prefix:
|
||||||
|
self.prefix = "group"
|
||||||
|
|
||||||
|
current_time_minute = time.strftime("%Y%m%d_%H%M", time.localtime())
|
||||||
|
self.log_cycle_to_file(log_dir + self.prefix + f"/{current_time_minute}_cycle_" + str(self.cycle_id) + ".json")
|
||||||
|
|
||||||
|
def log_cycle_to_file(self, file_path: str):
|
||||||
|
"""将循环信息写入文件"""
|
||||||
|
# 如果目录不存在,则创建目录
|
||||||
|
dir_name = os.path.dirname(file_path)
|
||||||
|
if dir_name and not os.path.exists(dir_name):
|
||||||
|
os.makedirs(dir_name, exist_ok=True)
|
||||||
|
# 写入文件
|
||||||
|
import json
|
||||||
|
with open(file_path, "a", encoding="utf-8") as f:
|
||||||
|
f.write(json.dumps(self.to_dict(), ensure_ascii=False) + "\n")
|
||||||
|
|
||||||
def set_thinking_id(self, thinking_id: str):
|
def set_thinking_id(self, thinking_id: str):
|
||||||
"""设置思考消息ID"""
|
"""设置思考消息ID"""
|
||||||
|
|
@ -47,30 +108,3 @@ class CycleDetail:
|
||||||
self.loop_processor_info = loop_info["loop_processor_info"]
|
self.loop_processor_info = loop_info["loop_processor_info"]
|
||||||
self.loop_plan_info = loop_info["loop_plan_info"]
|
self.loop_plan_info = loop_info["loop_plan_info"]
|
||||||
self.loop_action_info = loop_info["loop_action_info"]
|
self.loop_action_info = loop_info["loop_action_info"]
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def list_cycles(stream_id: str, base_dir: str = "log_debug") -> List[str]:
|
|
||||||
"""
|
|
||||||
列出指定stream_id的所有循环文件
|
|
||||||
|
|
||||||
参数:
|
|
||||||
stream_id: 聊天流ID
|
|
||||||
base_dir: 基础目录,默认为log_debug
|
|
||||||
|
|
||||||
返回:
|
|
||||||
List[str]: 文件路径列表
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
stream_dir = os.path.join(base_dir, stream_id)
|
|
||||||
if not os.path.exists(stream_dir):
|
|
||||||
return []
|
|
||||||
|
|
||||||
files = [
|
|
||||||
os.path.join(stream_dir, f)
|
|
||||||
for f in os.listdir(stream_dir)
|
|
||||||
if f.startswith("cycle_") and f.endswith(".txt")
|
|
||||||
]
|
|
||||||
return sorted(files)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"列出循环文件时出错: {e}")
|
|
||||||
return []
|
|
||||||
|
|
|
||||||
|
|
@ -44,21 +44,10 @@ PROCESSOR_CLASSES = {
|
||||||
"ToolProcessor": (ToolProcessor, "tool_use_processor"),
|
"ToolProcessor": (ToolProcessor, "tool_use_processor"),
|
||||||
"WorkingMemoryProcessor": (WorkingMemoryProcessor, "working_memory_processor"),
|
"WorkingMemoryProcessor": (WorkingMemoryProcessor, "working_memory_processor"),
|
||||||
"SelfProcessor": (SelfProcessor, "self_identify_processor"),
|
"SelfProcessor": (SelfProcessor, "self_identify_processor"),
|
||||||
# "ActionProcessor": (ActionProcessor, "action_processor"), # 这个处理器不需要配置键名,默认启用
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WAITING_TIME_THRESHOLD = 300 # 等待新消息时间阈值,单位秒
|
|
||||||
|
|
||||||
EMOJI_SEND_PRO = 0.3 # 设置一个概率,比如 30% 才真的发
|
|
||||||
|
|
||||||
CONSECUTIVE_NO_REPLY_THRESHOLD = 3 # 连续不回复的阈值
|
|
||||||
|
|
||||||
logger = get_logger("hfc") # Logger Name Changed
|
logger = get_logger("hfc") # Logger Name Changed
|
||||||
|
|
||||||
# 设定处理器超时时间(秒)
|
|
||||||
PROCESSOR_TIMEOUT = 30
|
|
||||||
|
|
||||||
|
|
||||||
async def _handle_cycle_delay(action_taken_this_cycle: bool, cycle_start_time: float, log_prefix: str):
|
async def _handle_cycle_delay(action_taken_this_cycle: bool, cycle_start_time: float, log_prefix: str):
|
||||||
"""处理循环延迟"""
|
"""处理循环延迟"""
|
||||||
|
|
@ -150,7 +139,7 @@ class HeartFChatting:
|
||||||
# 添加循环信息管理相关的属性
|
# 添加循环信息管理相关的属性
|
||||||
self._cycle_counter = 0
|
self._cycle_counter = 0
|
||||||
self._cycle_history: Deque[CycleDetail] = deque(maxlen=10) # 保留最近10个循环的信息
|
self._cycle_history: Deque[CycleDetail] = deque(maxlen=10) # 保留最近10个循环的信息
|
||||||
self._current_cycle: Optional[CycleDetail] = None
|
self._current_cycle_detail: Optional[CycleDetail] = None
|
||||||
self._shutting_down: bool = False # 关闭标志位
|
self._shutting_down: bool = False # 关闭标志位
|
||||||
|
|
||||||
# 存储回调函数
|
# 存储回调函数
|
||||||
|
|
@ -262,12 +251,12 @@ class HeartFChatting:
|
||||||
try:
|
try:
|
||||||
exception = task.exception()
|
exception = task.exception()
|
||||||
if exception:
|
if exception:
|
||||||
logger.error(f"{self.log_prefix} HeartFChatting: 麦麦脱离了聊天(异常): {exception}")
|
logger.error(f"{self.log_prefix} HeartFChatting: 脱离了聊天(异常): {exception}")
|
||||||
logger.error(traceback.format_exc()) # Log full traceback for exceptions
|
logger.error(traceback.format_exc()) # Log full traceback for exceptions
|
||||||
else:
|
else:
|
||||||
logger.info(f"{self.log_prefix} HeartFChatting: 麦麦脱离了聊天 (外部停止)")
|
logger.info(f"{self.log_prefix} HeartFChatting: 脱离了聊天 (外部停止)")
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.info(f"{self.log_prefix} HeartFChatting: 麦麦脱离了聊天(任务取消)")
|
logger.info(f"{self.log_prefix} HeartFChatting: 脱离了聊天(任务取消)")
|
||||||
finally:
|
finally:
|
||||||
self._loop_active = False
|
self._loop_active = False
|
||||||
self._loop_task = None
|
self._loop_task = None
|
||||||
|
|
@ -286,7 +275,8 @@ class HeartFChatting:
|
||||||
|
|
||||||
# 创建新的循环信息
|
# 创建新的循环信息
|
||||||
self._cycle_counter += 1
|
self._cycle_counter += 1
|
||||||
self._current_cycle = CycleDetail(self._cycle_counter)
|
self._current_cycle_detail = CycleDetail(self._cycle_counter)
|
||||||
|
self._current_cycle_detail.prefix = self.log_prefix
|
||||||
|
|
||||||
# 初始化周期状态
|
# 初始化周期状态
|
||||||
cycle_timers = {}
|
cycle_timers = {}
|
||||||
|
|
@ -295,13 +285,12 @@ class HeartFChatting:
|
||||||
# 执行规划和处理阶段
|
# 执行规划和处理阶段
|
||||||
async with self._get_cycle_context():
|
async with self._get_cycle_context():
|
||||||
thinking_id = "tid" + str(round(time.time(), 2))
|
thinking_id = "tid" + str(round(time.time(), 2))
|
||||||
self._current_cycle.set_thinking_id(thinking_id)
|
self._current_cycle_detail.set_thinking_id(thinking_id)
|
||||||
# 主循环:思考->决策->执行
|
# 主循环:思考->决策->执行
|
||||||
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
|
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
|
||||||
logger.debug(f"模板 {self.chat_stream.context.get_template_name()}")
|
logger.debug(f"模板 {self.chat_stream.context.get_template_name()}")
|
||||||
loop_info = await self._observe_process_plan_action_loop(cycle_timers, thinking_id)
|
loop_info = await self._observe_process_plan_action_loop(cycle_timers, thinking_id)
|
||||||
|
|
||||||
print(loop_info["loop_action_info"]["command"])
|
|
||||||
if loop_info["loop_action_info"]["command"] == "stop_focus_chat":
|
if loop_info["loop_action_info"]["command"] == "stop_focus_chat":
|
||||||
logger.info(f"{self.log_prefix} 麦麦决定停止专注聊天")
|
logger.info(f"{self.log_prefix} 麦麦决定停止专注聊天")
|
||||||
# 如果设置了回调函数,则调用它
|
# 如果设置了回调函数,则调用它
|
||||||
|
|
@ -314,10 +303,10 @@ class HeartFChatting:
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
break
|
break
|
||||||
|
|
||||||
self._current_cycle.set_loop_info(loop_info)
|
self._current_cycle_detail.set_loop_info(loop_info)
|
||||||
|
|
||||||
self.hfcloop_observation.add_loop_info(self._current_cycle)
|
self.hfcloop_observation.add_loop_info(self._current_cycle_detail)
|
||||||
self._current_cycle.timers = cycle_timers
|
self._current_cycle_detail.timers = cycle_timers
|
||||||
|
|
||||||
# 防止循环过快消耗资源
|
# 防止循环过快消耗资源
|
||||||
await _handle_cycle_delay(
|
await _handle_cycle_delay(
|
||||||
|
|
@ -325,8 +314,8 @@ class HeartFChatting:
|
||||||
)
|
)
|
||||||
|
|
||||||
# 完成当前循环并保存历史
|
# 完成当前循环并保存历史
|
||||||
self._current_cycle.complete_cycle()
|
self._current_cycle_detail.complete_cycle()
|
||||||
self._cycle_history.append(self._current_cycle)
|
self._cycle_history.append(self._current_cycle_detail)
|
||||||
|
|
||||||
# 记录循环信息和计时器结果
|
# 记录循环信息和计时器结果
|
||||||
timer_strings = []
|
timer_strings = []
|
||||||
|
|
@ -335,7 +324,7 @@ class HeartFChatting:
|
||||||
timer_strings.append(f"{name}: {formatted_time}")
|
timer_strings.append(f"{name}: {formatted_time}")
|
||||||
|
|
||||||
# 新增:输出每个处理器的耗时
|
# 新增:输出每个处理器的耗时
|
||||||
processor_time_costs = self._current_cycle.loop_processor_info.get("processor_time_costs", {})
|
processor_time_costs = self._current_cycle_detail.loop_processor_info.get("processor_time_costs", {})
|
||||||
processor_time_strings = []
|
processor_time_strings = []
|
||||||
for pname, ptime in processor_time_costs.items():
|
for pname, ptime in processor_time_costs.items():
|
||||||
formatted_ptime = f"{ptime * 1000:.2f}毫秒" if ptime < 1 else f"{ptime:.2f}秒"
|
formatted_ptime = f"{ptime * 1000:.2f}毫秒" if ptime < 1 else f"{ptime:.2f}秒"
|
||||||
|
|
@ -345,9 +334,9 @@ class HeartFChatting:
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 第{self._current_cycle.cycle_id}次思考,"
|
f"{self.log_prefix} 第{self._current_cycle_detail.cycle_id}次思考,"
|
||||||
f"耗时: {self._current_cycle.end_time - self._current_cycle.start_time:.1f}秒, "
|
f"耗时: {self._current_cycle_detail.end_time - self._current_cycle_detail.start_time:.1f}秒, "
|
||||||
f"动作: {self._current_cycle.loop_plan_info['action_result']['action_type']}"
|
f"动作: {self._current_cycle_detail.loop_plan_info['action_result']['action_type']}"
|
||||||
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
|
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
|
||||||
+ processor_time_log
|
+ processor_time_log
|
||||||
)
|
)
|
||||||
|
|
@ -384,7 +373,7 @@ class HeartFChatting:
|
||||||
self._processing_lock.release()
|
self._processing_lock.release()
|
||||||
|
|
||||||
async def _process_processors(
|
async def _process_processors(
|
||||||
self, observations: List[Observation], running_memorys: List[Dict[str, Any]], cycle_timers: dict
|
self, observations: List[Observation], running_memorys: List[Dict[str, Any]]
|
||||||
) -> tuple[List[InfoBase], Dict[str, float]]:
|
) -> tuple[List[InfoBase], Dict[str, float]]:
|
||||||
# 记录并行任务开始时间
|
# 记录并行任务开始时间
|
||||||
parallel_start_time = time.time()
|
parallel_start_time = time.time()
|
||||||
|
|
@ -400,7 +389,7 @@ class HeartFChatting:
|
||||||
async def run_with_timeout(proc=processor):
|
async def run_with_timeout(proc=processor):
|
||||||
return await asyncio.wait_for(
|
return await asyncio.wait_for(
|
||||||
proc.process_info(observations=observations, running_memorys=running_memorys),
|
proc.process_info(observations=observations, running_memorys=running_memorys),
|
||||||
timeout=PROCESSOR_TIMEOUT,
|
timeout=global_config.focus_chat.processor_max_time,
|
||||||
)
|
)
|
||||||
|
|
||||||
task = asyncio.create_task(run_with_timeout())
|
task = asyncio.create_task(run_with_timeout())
|
||||||
|
|
@ -429,8 +418,8 @@ class HeartFChatting:
|
||||||
# 记录耗时
|
# 记录耗时
|
||||||
processor_time_costs[processor_name] = duration_since_parallel_start
|
processor_time_costs[processor_name] = duration_since_parallel_start
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
logger.info(f"{self.log_prefix} 处理器 {processor_name} 超时(>{PROCESSOR_TIMEOUT}s),已跳过")
|
logger.info(f"{self.log_prefix} 处理器 {processor_name} 超时(>{global_config.focus_chat.processor_max_time}s),已跳过")
|
||||||
processor_time_costs[processor_name] = PROCESSOR_TIMEOUT
|
processor_time_costs[processor_name] = global_config.focus_chat.processor_max_time
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.log_prefix} 处理器 {processor_name} 执行失败,耗时 (自并行开始): {duration_since_parallel_start:.2f}秒. 错误: {e}",
|
f"{self.log_prefix} 处理器 {processor_name} 执行失败,耗时 (自并行开始): {duration_since_parallel_start:.2f}秒. 错误: {e}",
|
||||||
|
|
@ -473,28 +462,42 @@ class HeartFChatting:
|
||||||
}
|
}
|
||||||
|
|
||||||
self.all_observations = observations
|
self.all_observations = observations
|
||||||
|
|
||||||
with Timer("回忆", cycle_timers):
|
|
||||||
running_memorys = await self.memory_activator.activate_memory(observations)
|
|
||||||
|
|
||||||
with Timer("调整动作", cycle_timers):
|
with Timer("调整动作", cycle_timers):
|
||||||
# 处理特殊的观察
|
# 处理特殊的观察
|
||||||
await self.action_modifier.modify_actions(observations=observations, running_memorys=running_memorys)
|
await self.action_modifier.modify_actions(observations=observations)
|
||||||
await self.action_observation.observe()
|
await self.action_observation.observe()
|
||||||
observations.append(self.action_observation)
|
observations.append(self.action_observation)
|
||||||
|
|
||||||
with Timer("执行 信息处理器", cycle_timers):
|
# 根据配置决定是否并行执行回忆和处理器阶段
|
||||||
all_plan_info, processor_time_costs = await self._process_processors(
|
# print(global_config.focus_chat.parallel_processing)
|
||||||
observations, running_memorys, cycle_timers
|
if global_config.focus_chat.parallel_processing:
|
||||||
)
|
# 并行执行回忆和处理器阶段
|
||||||
|
with Timer("并行回忆和处理", cycle_timers):
|
||||||
|
memory_task = asyncio.create_task(self.memory_activator.activate_memory(observations))
|
||||||
|
processor_task = asyncio.create_task(self._process_processors(observations, []))
|
||||||
|
|
||||||
|
# 等待两个任务完成
|
||||||
|
running_memorys, (all_plan_info, processor_time_costs) = await asyncio.gather(memory_task, processor_task)
|
||||||
|
else:
|
||||||
|
# 串行执行
|
||||||
|
with Timer("回忆", cycle_timers):
|
||||||
|
running_memorys = await self.memory_activator.activate_memory(observations)
|
||||||
|
|
||||||
|
with Timer("执行 信息处理器", cycle_timers):
|
||||||
|
all_plan_info, processor_time_costs = await self._process_processors(
|
||||||
|
observations, running_memorys
|
||||||
|
)
|
||||||
|
|
||||||
|
loop_processor_info = {
|
||||||
|
"all_plan_info": all_plan_info,
|
||||||
|
"processor_time_costs": processor_time_costs,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
loop_processor_info = {
|
|
||||||
"all_plan_info": all_plan_info,
|
|
||||||
"processor_time_costs": processor_time_costs,
|
|
||||||
}
|
|
||||||
|
|
||||||
with Timer("规划器", cycle_timers):
|
with Timer("规划器", cycle_timers):
|
||||||
plan_result = await self.action_planner.plan(all_plan_info, cycle_timers)
|
plan_result = await self.action_planner.plan(all_plan_info, running_memorys)
|
||||||
|
|
||||||
loop_plan_info = {
|
loop_plan_info = {
|
||||||
"action_result": plan_result.get("action_result", {}),
|
"action_result": plan_result.get("action_result", {}),
|
||||||
|
|
@ -526,6 +529,7 @@ class HeartFChatting:
|
||||||
"action_taken": success,
|
"action_taken": success,
|
||||||
"reply_text": reply_text,
|
"reply_text": reply_text,
|
||||||
"command": command,
|
"command": command,
|
||||||
|
"taken_time": time.time(),
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_info = {
|
loop_info = {
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ class ChattingInfoProcessor(BaseProcessor):
|
||||||
if obs.compressor_prompt:
|
if obs.compressor_prompt:
|
||||||
summary = ""
|
summary = ""
|
||||||
try:
|
try:
|
||||||
summary_result, _, _ = await self.model_summary.generate_response(obs.compressor_prompt)
|
summary_result, _ = await self.model_summary.generate_response_async(obs.compressor_prompt)
|
||||||
summary = "没有主题的闲聊"
|
summary = "没有主题的闲聊"
|
||||||
if summary_result:
|
if summary_result:
|
||||||
summary = summary_result
|
summary = summary_result
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,7 @@ logger = get_logger("processor")
|
||||||
def init_prompt():
|
def init_prompt():
|
||||||
group_prompt = """
|
group_prompt = """
|
||||||
你的名字是{bot_name}
|
你的名字是{bot_name}
|
||||||
{memory_str}
|
{memory_str}{extra_info}{relation_prompt}
|
||||||
{extra_info}
|
|
||||||
{relation_prompt}
|
|
||||||
{cycle_info_block}
|
{cycle_info_block}
|
||||||
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
||||||
{chat_observe_info}
|
{chat_observe_info}
|
||||||
|
|
@ -46,15 +44,11 @@ def init_prompt():
|
||||||
|
|
||||||
private_prompt = """
|
private_prompt = """
|
||||||
你的名字是{bot_name}
|
你的名字是{bot_name}
|
||||||
{memory_str}
|
{memory_str}{extra_info}{relation_prompt}
|
||||||
{extra_info}
|
|
||||||
{relation_prompt}
|
|
||||||
{cycle_info_block}
|
{cycle_info_block}
|
||||||
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
||||||
{chat_observe_info}
|
{chat_observe_info}
|
||||||
|
|
||||||
{action_observe_info}
|
{action_observe_info}
|
||||||
|
|
||||||
以下是你之前对聊天的观察和规划,你的名字是{bot_name}:
|
以下是你之前对聊天的观察和规划,你的名字是{bot_name}:
|
||||||
{last_mind}
|
{last_mind}
|
||||||
|
|
||||||
|
|
@ -77,7 +71,7 @@ class MindProcessor(BaseProcessor):
|
||||||
|
|
||||||
self.llm_model = LLMRequest(
|
self.llm_model = LLMRequest(
|
||||||
model=global_config.model.focus_chat_mind,
|
model=global_config.model.focus_chat_mind,
|
||||||
temperature=global_config.model.focus_chat_mind["temp"],
|
# temperature=global_config.model.focus_chat_mind["temp"],
|
||||||
max_tokens=800,
|
max_tokens=800,
|
||||||
request_type="focus.processor.chat_mind",
|
request_type="focus.processor.chat_mind",
|
||||||
)
|
)
|
||||||
|
|
@ -155,14 +149,14 @@ class MindProcessor(BaseProcessor):
|
||||||
|
|
||||||
# ---------- 0. 更新和清理 structured_info ----------
|
# ---------- 0. 更新和清理 structured_info ----------
|
||||||
if self.structured_info:
|
if self.structured_info:
|
||||||
updated_info = []
|
# updated_info = []
|
||||||
for item in self.structured_info:
|
# for item in self.structured_info:
|
||||||
item["ttl"] -= 1
|
# item["ttl"] -= 1
|
||||||
if item["ttl"] > 0:
|
# if item["ttl"] > 0:
|
||||||
updated_info.append(item)
|
# updated_info.append(item)
|
||||||
else:
|
# else:
|
||||||
logger.debug(f"{self.log_prefix} 移除过期的 structured_info 项: {item['id']}")
|
# logger.debug(f"{self.log_prefix} 移除过期的 structured_info 项: {item['id']}")
|
||||||
self.structured_info = updated_info
|
# self.structured_info = updated_info
|
||||||
self._update_structured_info_str()
|
self._update_structured_info_str()
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{self.log_prefix} 当前完整的 structured_info: {safe_json_dumps(self.structured_info, ensure_ascii=False)}"
|
f"{self.log_prefix} 当前完整的 structured_info: {safe_json_dumps(self.structured_info, ensure_ascii=False)}"
|
||||||
|
|
@ -231,7 +225,7 @@ class MindProcessor(BaseProcessor):
|
||||||
# 处理总体异常
|
# 处理总体异常
|
||||||
logger.error(f"{self.log_prefix} 执行LLM请求或处理响应时出错: {e}")
|
logger.error(f"{self.log_prefix} 执行LLM请求或处理响应时出错: {e}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
content = "思考过程中出现错误"
|
content = "注意:思考过程中出现错误,应该是LLM大模型有问题!!你需要告诉别人,检查大模型配置"
|
||||||
|
|
||||||
# 记录初步思考结果
|
# 记录初步思考结果
|
||||||
logger.debug(f"{self.log_prefix} 思考prompt: \n{prompt}\n")
|
logger.debug(f"{self.log_prefix} 思考prompt: \n{prompt}\n")
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,7 @@ def init_prompt():
|
||||||
你是一个AI,但是你伪装成了一个人类,你的人格是,{prompt_personality}
|
你是一个AI,但是你伪装成了一个人类,你的人格是,{prompt_personality}
|
||||||
{indentify_block}
|
{indentify_block}
|
||||||
|
|
||||||
{relation_prompt}
|
{relation_prompt}{memory_str}
|
||||||
{memory_str}
|
|
||||||
|
|
||||||
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
||||||
{chat_observe_info}
|
{chat_observe_info}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,7 @@ def init_prompt():
|
||||||
# 添加工具执行器提示词
|
# 添加工具执行器提示词
|
||||||
tool_executor_prompt = """
|
tool_executor_prompt = """
|
||||||
你是一个专门执行工具的助手。你的名字是{bot_name}。现在是{time_now}。
|
你是一个专门执行工具的助手。你的名字是{bot_name}。现在是{time_now}。
|
||||||
|
|
||||||
你当前的额外信息:
|
|
||||||
{memory_str}
|
{memory_str}
|
||||||
|
|
||||||
群里正在进行的聊天内容:
|
群里正在进行的聊天内容:
|
||||||
{chat_observe_info}
|
{chat_observe_info}
|
||||||
|
|
||||||
|
|
@ -165,8 +162,9 @@ class ToolProcessor(BaseProcessor):
|
||||||
logger.debug(f"开始执行工具调用{prompt}")
|
logger.debug(f"开始执行工具调用{prompt}")
|
||||||
response, _, tool_calls = await self.llm_model.generate_response_tool_async(prompt=prompt, tools=tools)
|
response, _, tool_calls = await self.llm_model.generate_response_tool_async(prompt=prompt, tools=tools)
|
||||||
|
|
||||||
logger.debug(f"获取到工具原始输出:\n{tool_calls}")
|
if tool_calls:
|
||||||
# 处理工具调用和结果收集,类似于SubMind中的逻辑
|
logger.debug(f"获取到工具原始输出:\n{tool_calls}")
|
||||||
|
# 处理工具调用和结果收集,类似于SubMind中的逻辑
|
||||||
new_structured_items = []
|
new_structured_items = []
|
||||||
used_tools = [] # 记录使用了哪些工具
|
used_tools = [] # 记录使用了哪些工具
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ class NoReplyAction(BaseAction):
|
||||||
action_parameters = {}
|
action_parameters = {}
|
||||||
action_require = [
|
action_require = [
|
||||||
"话题无关/无聊/不感兴趣/不懂",
|
"话题无关/无聊/不感兴趣/不懂",
|
||||||
"最后一条消息是你自己发的且无人回应你",
|
"聊天记录中最新一条消息是你自己发的且无人回应你",
|
||||||
"你发送了太多消息,且无人回复",
|
"你连续发送了太多消息,且无人回复",
|
||||||
]
|
]
|
||||||
default = True
|
default = True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,8 @@ class ActionModifier:
|
||||||
async def modify_actions(
|
async def modify_actions(
|
||||||
self,
|
self,
|
||||||
observations: Optional[List[Observation]] = None,
|
observations: Optional[List[Observation]] = None,
|
||||||
running_memorys: Optional[List[Dict]] = None,
|
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
):
|
):
|
||||||
# print(f"observations: {observations}")
|
|
||||||
# processed_infos = []
|
|
||||||
|
|
||||||
# 处理Observation对象
|
# 处理Observation对象
|
||||||
if observations:
|
if observations:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from src.common.logger_manager import get_logger
|
||||||
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||||||
from src.individuality.individuality import individuality
|
from src.individuality.individuality import individuality
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
from src.chat.focus_chat.planners.action_manager import ActionManager
|
||||||
|
from json_repair import repair_json
|
||||||
|
|
||||||
logger = get_logger("planner")
|
logger = get_logger("planner")
|
||||||
|
|
||||||
|
|
@ -26,9 +27,8 @@ def init_prompt():
|
||||||
"""
|
"""
|
||||||
你的自我认知是:
|
你的自我认知是:
|
||||||
{self_info_block}
|
{self_info_block}
|
||||||
|
|
||||||
{extra_info_block}
|
{extra_info_block}
|
||||||
|
{memory_str}
|
||||||
你需要基于以下信息决定如何参与对话
|
你需要基于以下信息决定如何参与对话
|
||||||
这些信息可能会有冲突,请你整合这些信息,并选择一个最合适的action:
|
这些信息可能会有冲突,请你整合这些信息,并选择一个最合适的action:
|
||||||
{chat_content_block}
|
{chat_content_block}
|
||||||
|
|
@ -49,7 +49,7 @@ def init_prompt():
|
||||||
请你以下面格式输出你选择的action:
|
请你以下面格式输出你选择的action:
|
||||||
{{
|
{{
|
||||||
"action": "action_name",
|
"action": "action_name",
|
||||||
"reasoning": "你的决策理由",
|
"reasoning": "说明你做出该action的原因",
|
||||||
"参数1": "参数1的值",
|
"参数1": "参数1的值",
|
||||||
"参数2": "参数2的值",
|
"参数2": "参数2的值",
|
||||||
"参数3": "参数3的值",
|
"参数3": "参数3的值",
|
||||||
|
|
@ -84,13 +84,13 @@ class ActionPlanner:
|
||||||
|
|
||||||
self.action_manager = action_manager
|
self.action_manager = action_manager
|
||||||
|
|
||||||
async def plan(self, all_plan_info: List[InfoBase], cycle_timers: dict) -> Dict[str, Any]:
|
async def plan(self, all_plan_info: List[InfoBase], running_memorys: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
all_plan_info: 所有计划信息
|
all_plan_info: 所有计划信息
|
||||||
cycle_timers: 计时器字典
|
running_memorys: 回忆信息
|
||||||
"""
|
"""
|
||||||
|
|
||||||
action = "no_reply" # 默认动作
|
action = "no_reply" # 默认动作
|
||||||
|
|
@ -169,12 +169,15 @@ class ActionPlanner:
|
||||||
current_available_actions=current_available_actions, # <-- Pass determined actions
|
current_available_actions=current_available_actions, # <-- Pass determined actions
|
||||||
cycle_info=cycle_info, # <-- Pass cycle info
|
cycle_info=cycle_info, # <-- Pass cycle info
|
||||||
extra_info=extra_info,
|
extra_info=extra_info,
|
||||||
|
running_memorys=running_memorys,
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- 调用 LLM (普通文本生成) ---
|
# --- 调用 LLM (普通文本生成) ---
|
||||||
llm_content = None
|
llm_content = None
|
||||||
try:
|
try:
|
||||||
llm_content, reasoning_content, _ = await self.planner_llm.generate_response(prompt=prompt)
|
prompt = f"{prompt}"
|
||||||
|
print(len(prompt))
|
||||||
|
llm_content, (reasoning_content, _) = await self.planner_llm.generate_response_async(prompt=prompt)
|
||||||
logger.debug(f"{self.log_prefix}[Planner] LLM 原始 JSON 响应 (预期): {llm_content}")
|
logger.debug(f"{self.log_prefix}[Planner] LLM 原始 JSON 响应 (预期): {llm_content}")
|
||||||
logger.debug(f"{self.log_prefix}[Planner] LLM 原始理由 响应 (预期): {reasoning_content}")
|
logger.debug(f"{self.log_prefix}[Planner] LLM 原始理由 响应 (预期): {reasoning_content}")
|
||||||
except Exception as req_e:
|
except Exception as req_e:
|
||||||
|
|
@ -184,13 +187,16 @@ class ActionPlanner:
|
||||||
|
|
||||||
if llm_content:
|
if llm_content:
|
||||||
try:
|
try:
|
||||||
# 尝试去除可能的 markdown 代码块标记
|
fixed_json_string = repair_json(llm_content)
|
||||||
cleaned_content = (
|
if isinstance(fixed_json_string, str):
|
||||||
llm_content.strip().removeprefix("```json").removeprefix("```").removesuffix("```").strip()
|
try:
|
||||||
)
|
parsed_json = json.loads(fixed_json_string)
|
||||||
if not cleaned_content:
|
except json.JSONDecodeError as decode_error:
|
||||||
raise json.JSONDecodeError("Cleaned content is empty", cleaned_content, 0)
|
logger.error(f"JSON解析错误: {str(decode_error)}")
|
||||||
parsed_json = json.loads(cleaned_content)
|
parsed_json = {}
|
||||||
|
else:
|
||||||
|
# 如果repair_json直接返回了字典对象,直接使用
|
||||||
|
parsed_json = fixed_json_string
|
||||||
|
|
||||||
# 提取决策,提供默认值
|
# 提取决策,提供默认值
|
||||||
extracted_action = parsed_json.get("action", "no_reply")
|
extracted_action = parsed_json.get("action", "no_reply")
|
||||||
|
|
@ -244,6 +250,7 @@ class ActionPlanner:
|
||||||
"action_result": action_result,
|
"action_result": action_result,
|
||||||
"current_mind": current_mind,
|
"current_mind": current_mind,
|
||||||
"observed_messages": observed_messages,
|
"observed_messages": observed_messages,
|
||||||
|
"action_prompt": prompt,
|
||||||
}
|
}
|
||||||
|
|
||||||
return plan_result
|
return plan_result
|
||||||
|
|
@ -259,10 +266,22 @@ class ActionPlanner:
|
||||||
current_available_actions: Dict[str, ActionInfo],
|
current_available_actions: Dict[str, ActionInfo],
|
||||||
cycle_info: Optional[str],
|
cycle_info: Optional[str],
|
||||||
extra_info: list[str],
|
extra_info: list[str],
|
||||||
|
running_memorys: List[Dict[str, Any]],
|
||||||
) -> str:
|
) -> str:
|
||||||
"""构建 Planner LLM 的提示词 (获取模板并填充数据)"""
|
"""构建 Planner LLM 的提示词 (获取模板并填充数据)"""
|
||||||
try:
|
try:
|
||||||
# --- Determine chat context ---
|
|
||||||
|
memory_str = ""
|
||||||
|
if global_config.focus_chat.parallel_processing:
|
||||||
|
memory_str = ""
|
||||||
|
if running_memorys:
|
||||||
|
memory_str = "以下是当前在聊天中,你回忆起的记忆:\n"
|
||||||
|
for running_memory in running_memorys:
|
||||||
|
memory_str += f"{running_memory['topic']}: {running_memory['content']}\n"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
chat_context_description = "你现在正在一个群聊中"
|
chat_context_description = "你现在正在一个群聊中"
|
||||||
chat_target_name = None # Only relevant for private
|
chat_target_name = None # Only relevant for private
|
||||||
if not is_group_chat and chat_target_info:
|
if not is_group_chat and chat_target_info:
|
||||||
|
|
@ -324,6 +343,7 @@ class ActionPlanner:
|
||||||
planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt")
|
planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt")
|
||||||
prompt = planner_prompt_template.format(
|
prompt = planner_prompt_template.format(
|
||||||
self_info_block=self_info_block,
|
self_info_block=self_info_block,
|
||||||
|
memory_str=memory_str,
|
||||||
# bot_name=global_config.bot.nickname,
|
# bot_name=global_config.bot.nickname,
|
||||||
prompt_personality=personality_block,
|
prompt_personality=personality_block,
|
||||||
chat_context_description=chat_context_description,
|
chat_context_description=chat_context_description,
|
||||||
|
|
|
||||||
|
|
@ -34,3 +34,13 @@ class ActionObservation:
|
||||||
action_info_block += "\n注意,除了上面动作选项之外,你在群聊里不能做其他任何事情,这是你能力的边界\n"
|
action_info_block += "\n注意,除了上面动作选项之外,你在群聊里不能做其他任何事情,这是你能力的边界\n"
|
||||||
|
|
||||||
self.observe_info = action_info_block
|
self.observe_info = action_info_block
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""将观察对象转换为可序列化的字典"""
|
||||||
|
return {
|
||||||
|
"observe_info": self.observe_info,
|
||||||
|
"observe_id": self.observe_id,
|
||||||
|
"last_observe_time": self.last_observe_time,
|
||||||
|
"all_actions": self.all_actions,
|
||||||
|
"all_using_actions": self.all_using_actions
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,24 @@ class ChattingObservation(Observation):
|
||||||
self.oldest_messages_str = ""
|
self.oldest_messages_str = ""
|
||||||
self.compressor_prompt = ""
|
self.compressor_prompt = ""
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""将观察对象转换为可序列化的字典"""
|
||||||
|
return {
|
||||||
|
"chat_id": self.chat_id,
|
||||||
|
"platform": self.platform,
|
||||||
|
"is_group_chat": self.is_group_chat,
|
||||||
|
"chat_target_info": self.chat_target_info,
|
||||||
|
"talking_message_str": self.talking_message_str,
|
||||||
|
"talking_message_str_truncate": self.talking_message_str_truncate,
|
||||||
|
"name": self.name,
|
||||||
|
"nick_name": self.nick_name,
|
||||||
|
"mid_memory_info": self.mid_memory_info,
|
||||||
|
"person_list": self.person_list,
|
||||||
|
"oldest_messages_str": self.oldest_messages_str,
|
||||||
|
"compressor_prompt": self.compressor_prompt,
|
||||||
|
"last_observe_time": self.last_observe_time
|
||||||
|
}
|
||||||
|
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
|
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
|
||||||
logger.debug(f"初始化observation: self.is_group_chat: {self.is_group_chat}")
|
logger.debug(f"初始化observation: self.is_group_chat: {self.is_group_chat}")
|
||||||
|
|
|
||||||
|
|
@ -27,40 +27,70 @@ class HFCloopObservation:
|
||||||
recent_active_cycles: List[CycleDetail] = []
|
recent_active_cycles: List[CycleDetail] = []
|
||||||
for cycle in reversed(self.history_loop):
|
for cycle in reversed(self.history_loop):
|
||||||
# 只关心实际执行了动作的循环
|
# 只关心实际执行了动作的循环
|
||||||
action_taken = cycle.loop_action_info["action_taken"]
|
# action_taken = cycle.loop_action_info["action_taken"]
|
||||||
if action_taken:
|
# if action_taken:
|
||||||
recent_active_cycles.append(cycle)
|
recent_active_cycles.append(cycle)
|
||||||
if len(recent_active_cycles) == 5:
|
if len(recent_active_cycles) == 5:
|
||||||
break
|
break
|
||||||
|
|
||||||
cycle_info_block = ""
|
cycle_info_block = ""
|
||||||
|
action_detailed_str = ""
|
||||||
consecutive_text_replies = 0
|
consecutive_text_replies = 0
|
||||||
responses_for_prompt = []
|
responses_for_prompt = []
|
||||||
|
|
||||||
|
cycle_last_reason = ""
|
||||||
|
|
||||||
# 检查这最近的活动循环中有多少是连续的文本回复 (从最近的开始看)
|
# 检查这最近的活动循环中有多少是连续的文本回复 (从最近的开始看)
|
||||||
for cycle in recent_active_cycles:
|
for cycle in recent_active_cycles:
|
||||||
action_type = cycle.loop_plan_info["action_result"]["action_type"]
|
action_type = cycle.loop_plan_info["action_result"]["action_type"]
|
||||||
|
action_reasoning = cycle.loop_plan_info["action_result"]["reasoning"]
|
||||||
|
is_taken = cycle.loop_action_info["action_taken"]
|
||||||
|
action_taken_time = cycle.loop_action_info["taken_time"]
|
||||||
|
action_taken_time_str = datetime.fromtimestamp(action_taken_time).strftime("%H:%M:%S")
|
||||||
|
# print(action_type)
|
||||||
|
# print(action_reasoning)
|
||||||
|
# print(is_taken)
|
||||||
|
# print(action_taken_time_str)
|
||||||
|
# print("--------------------------------")
|
||||||
|
if action_reasoning != cycle_last_reason:
|
||||||
|
cycle_last_reason = action_reasoning
|
||||||
|
action_reasoning_str = f"你选择这个action的原因是:{action_reasoning}"
|
||||||
|
else:
|
||||||
|
action_reasoning_str = ""
|
||||||
|
|
||||||
if action_type == "reply":
|
if action_type == "reply":
|
||||||
consecutive_text_replies += 1
|
consecutive_text_replies += 1
|
||||||
response_text = cycle.loop_plan_info["action_result"]["action_data"].get("text", "[空回复]")
|
response_text = cycle.loop_plan_info["action_result"]["action_data"].get("text", "[空回复]")
|
||||||
responses_for_prompt.append(response_text)
|
responses_for_prompt.append(response_text)
|
||||||
|
|
||||||
|
if is_taken:
|
||||||
|
action_detailed_str += f"{action_taken_time_str}时,你选择回复(action:{action_type},内容是:'{response_text}')。{action_reasoning_str}\n"
|
||||||
|
else:
|
||||||
|
action_detailed_str += f"{action_taken_time_str}时,你选择回复(action:{action_type},内容是:'{response_text}'),但是动作失败了。{action_reasoning_str}\n"
|
||||||
|
elif action_type == "no_reply":
|
||||||
|
action_detailed_str += f"{action_taken_time_str}时,你选择不回复(action:{action_type}),{action_reasoning_str}\n"
|
||||||
else:
|
else:
|
||||||
break
|
if is_taken:
|
||||||
|
action_detailed_str += f"{action_taken_time_str}时,你选择执行了(action:{action_type}),{action_reasoning_str}\n"
|
||||||
|
else:
|
||||||
|
action_detailed_str += f"{action_taken_time_str}时,你选择执行了(action:{action_type}),但是动作失败了。{action_reasoning_str}\n"
|
||||||
|
|
||||||
|
if action_detailed_str:
|
||||||
|
cycle_info_block = f"\n你最近做的事:\n{action_detailed_str}\n"
|
||||||
|
else:
|
||||||
|
cycle_info_block = "\n"
|
||||||
|
|
||||||
# 根据连续文本回复的数量构建提示信息
|
# 根据连续文本回复的数量构建提示信息
|
||||||
# 注意: responses_for_prompt 列表是从最近到最远排序的
|
|
||||||
if consecutive_text_replies >= 3: # 如果最近的三个活动都是文本回复
|
if consecutive_text_replies >= 3: # 如果最近的三个活动都是文本回复
|
||||||
cycle_info_block = f'你已经连续回复了三条消息(最近: "{responses_for_prompt[0]}",第二近: "{responses_for_prompt[1]}",第三近: "{responses_for_prompt[2]}")。你回复的有点多了,请注意'
|
cycle_info_block = f'你已经连续回复了三条消息(最近: "{responses_for_prompt[0]}",第二近: "{responses_for_prompt[1]}",第三近: "{responses_for_prompt[2]}")。你回复的有点多了,请注意'
|
||||||
elif consecutive_text_replies == 2: # 如果最近的两个活动是文本回复
|
elif consecutive_text_replies == 2: # 如果最近的两个活动是文本回复
|
||||||
cycle_info_block = f'你已经连续回复了两条消息(最近: "{responses_for_prompt[0]}",第二近: "{responses_for_prompt[1]}"),请注意'
|
cycle_info_block = f'你已经连续回复了两条消息(最近: "{responses_for_prompt[0]}",第二近: "{responses_for_prompt[1]}"),请注意'
|
||||||
elif consecutive_text_replies == 1: # 如果最近的一个活动是文本回复
|
|
||||||
cycle_info_block = f'你刚刚已经回复一条消息(内容: "{responses_for_prompt[0]}")'
|
|
||||||
|
|
||||||
# 包装提示块,增加可读性,即使没有连续回复也给个标记
|
# 包装提示块,增加可读性,即使没有连续回复也给个标记
|
||||||
if cycle_info_block:
|
# if cycle_info_block:
|
||||||
cycle_info_block = f"\n你最近的回复\n{cycle_info_block}\n"
|
# cycle_info_block = f"\n你最近的回复\n{cycle_info_block}\n"
|
||||||
else:
|
# else:
|
||||||
cycle_info_block = "\n"
|
# cycle_info_block = "\n"
|
||||||
|
|
||||||
# 获取history_loop中最新添加的
|
# 获取history_loop中最新添加的
|
||||||
if self.history_loop:
|
if self.history_loop:
|
||||||
|
|
@ -70,10 +100,21 @@ class HFCloopObservation:
|
||||||
if start_time is not None and end_time is not None:
|
if start_time is not None and end_time is not None:
|
||||||
time_diff = int(end_time - start_time)
|
time_diff = int(end_time - start_time)
|
||||||
if time_diff > 60:
|
if time_diff > 60:
|
||||||
cycle_info_block += f"\n距离你上一次阅读消息已经过去了{time_diff / 60}分钟\n"
|
cycle_info_block += f"距离你上一次阅读消息并思考和规划,已经过去了{int(time_diff / 60)}分钟\n"
|
||||||
else:
|
else:
|
||||||
cycle_info_block += f"\n距离你上一次阅读消息已经过去了{time_diff}秒\n"
|
cycle_info_block += f"距离你上一次阅读消息并思考和规划,已经过去了{time_diff}秒\n"
|
||||||
else:
|
else:
|
||||||
cycle_info_block += "\n你还没看过消息\n"
|
cycle_info_block += "你还没看过消息\n"
|
||||||
|
|
||||||
self.observe_info = cycle_info_block
|
self.observe_info = cycle_info_block
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""将观察对象转换为可序列化的字典"""
|
||||||
|
# 只序列化基本信息,避免循环引用
|
||||||
|
return {
|
||||||
|
"observe_info": self.observe_info,
|
||||||
|
"observe_id": self.observe_id,
|
||||||
|
"last_observe_time": self.last_observe_time,
|
||||||
|
# 不序列化history_loop,避免循环引用
|
||||||
|
"history_loop_count": len(self.history_loop)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,13 @@ class Observation:
|
||||||
self.observe_id = observe_id
|
self.observe_id = observe_id
|
||||||
self.last_observe_time = datetime.now().timestamp() # 初始化为当前时间
|
self.last_observe_time = datetime.now().timestamp() # 初始化为当前时间
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""将观察对象转换为可序列化的字典"""
|
||||||
|
return {
|
||||||
|
"observe_info": self.observe_info,
|
||||||
|
"observe_id": self.observe_id,
|
||||||
|
"last_observe_time": self.last_observe_time
|
||||||
|
}
|
||||||
|
|
||||||
async def observe(self):
|
async def observe(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,16 @@ class StructureObservation:
|
||||||
self.history_loop = []
|
self.history_loop = []
|
||||||
self.structured_info = []
|
self.structured_info = []
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""将观察对象转换为可序列化的字典"""
|
||||||
|
return {
|
||||||
|
"observe_info": self.observe_info,
|
||||||
|
"observe_id": self.observe_id,
|
||||||
|
"last_observe_time": self.last_observe_time,
|
||||||
|
"history_loop": self.history_loop,
|
||||||
|
"structured_info": self.structured_info
|
||||||
|
}
|
||||||
|
|
||||||
def get_observe_info(self):
|
def get_observe_info(self):
|
||||||
return self.structured_info
|
return self.structured_info
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,13 @@ class WorkingMemoryObservation:
|
||||||
|
|
||||||
async def observe(self):
|
async def observe(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""将观察对象转换为可序列化的字典"""
|
||||||
|
return {
|
||||||
|
"observe_info": self.observe_info,
|
||||||
|
"observe_id": self.observe_id,
|
||||||
|
"last_observe_time": self.last_observe_time,
|
||||||
|
"working_memory": self.working_memory.to_dict() if hasattr(self.working_memory, 'to_dict') else str(self.working_memory),
|
||||||
|
"retrieved_working_memory": [item.to_dict() if hasattr(item, 'to_dict') else str(item) for item in self.retrieved_working_memory]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -278,10 +278,6 @@ class SubHeartflow:
|
||||||
self.update_last_chat_state_time()
|
self.update_last_chat_state_time()
|
||||||
self.history_chat_state.append((current_state, self.chat_state_last_time))
|
self.history_chat_state.append((current_state, self.chat_state_last_time))
|
||||||
|
|
||||||
# logger.info(
|
|
||||||
# f"{log_prefix} 麦麦的聊天状态从 {current_state.value} (持续了 {int(self.chat_state_last_time)} 秒) 变更为 {new_state.value}"
|
|
||||||
# )
|
|
||||||
|
|
||||||
self.chat_state.chat_status = new_state
|
self.chat_state.chat_status = new_state
|
||||||
self.chat_state_last_time = 0
|
self.chat_state_last_time = 0
|
||||||
self.chat_state_changed_time = time.time()
|
self.chat_state_changed_time = time.time()
|
||||||
|
|
|
||||||
|
|
@ -185,20 +185,23 @@ class PromptBuilder:
|
||||||
|
|
||||||
# 关键词检测与反应
|
# 关键词检测与反应
|
||||||
keywords_reaction_prompt = ""
|
keywords_reaction_prompt = ""
|
||||||
for rule in global_config.keyword_reaction.rules:
|
try:
|
||||||
if rule.enable:
|
for rule in global_config.keyword_reaction.rules:
|
||||||
if any(keyword in message_txt for keyword in rule.keywords):
|
if rule.enable:
|
||||||
logger.info(f"检测到以下关键词之一:{rule.keywords},触发反应:{rule.reaction}")
|
if any(keyword in message_txt for keyword in rule.keywords):
|
||||||
keywords_reaction_prompt += f"{rule.reaction},"
|
logger.info(f"检测到以下关键词之一:{rule.keywords},触发反应:{rule.reaction}")
|
||||||
else:
|
keywords_reaction_prompt += f"{rule.reaction},"
|
||||||
for pattern in rule.regex:
|
else:
|
||||||
if result := pattern.search(message_txt):
|
for pattern in rule.regex:
|
||||||
reaction = rule.reaction
|
if result := pattern.search(message_txt):
|
||||||
for name, content in result.groupdict().items():
|
reaction = rule.reaction
|
||||||
reaction = reaction.replace(f"[{name}]", content)
|
for name, content in result.groupdict().items():
|
||||||
logger.info(f"匹配到以下正则表达式:{pattern},触发反应:{reaction}")
|
reaction = reaction.replace(f"[{name}]", content)
|
||||||
keywords_reaction_prompt += reaction + ","
|
logger.info(f"匹配到以下正则表达式:{pattern},触发反应:{reaction}")
|
||||||
break
|
keywords_reaction_prompt += reaction + ","
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"关键词检测与反应时发生异常,可能是配置文件有误,跳过关键词匹配: {str(e)}")
|
||||||
|
|
||||||
# 中文高手(新加的好玩功能)
|
# 中文高手(新加的好玩功能)
|
||||||
prompt_ger = ""
|
prompt_ger = ""
|
||||||
|
|
|
||||||
|
|
@ -143,9 +143,15 @@ class FocusChatConfig(ConfigBase):
|
||||||
|
|
||||||
think_interval: float = 1
|
think_interval: float = 1
|
||||||
"""思考间隔(秒)"""
|
"""思考间隔(秒)"""
|
||||||
|
|
||||||
consecutive_replies: float = 1
|
consecutive_replies: float = 1
|
||||||
"""连续回复能力,值越高,麦麦连续回复的概率越高"""
|
"""连续回复能力,值越高,麦麦连续回复的概率越高"""
|
||||||
|
|
||||||
|
parallel_processing: bool = False
|
||||||
|
"""是否允许处理器阶段和回忆阶段并行执行"""
|
||||||
|
|
||||||
|
processor_max_time: int = 25
|
||||||
|
"""处理器最大时间,单位秒,如果超过这个时间,处理器会自动停止"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -160,6 +166,11 @@ class FocusChatProcessorConfig(ConfigBase):
|
||||||
|
|
||||||
working_memory_processor: bool = True
|
working_memory_processor: bool = True
|
||||||
"""是否启用工作记忆处理器"""
|
"""是否启用工作记忆处理器"""
|
||||||
|
|
||||||
|
lite_chat_mind_processor: bool = False
|
||||||
|
"""是否启用轻量级聊天思维处理器,可以节省token消耗和时间"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@ def init_prompt() -> None:
|
||||||
personality_expression_prompt = """
|
personality_expression_prompt = """
|
||||||
{personality}
|
{personality}
|
||||||
|
|
||||||
请从以上人设中总结出这个角色可能的语言风格
|
请从以上人设中总结出这个角色可能的语言风格,你必须严格根据人设引申,不要输出例子
|
||||||
思考回复的特殊内容和情感
|
思考回复的特殊内容和情感
|
||||||
思考有没有特殊的梗,一并总结成语言风格
|
思考有没有特殊的梗,一并总结成语言风格
|
||||||
总结成如下格式的规律,总结的内容要详细,但具有概括性:
|
总结成如下格式的规律,总结的内容要详细,但具有概括性:
|
||||||
当"xxx"时,可以"xxx", xxx不超过10个字
|
当"xxx"时,可以"xxx", xxx不超过10个字
|
||||||
|
|
||||||
例如:
|
例如(不要输出例子):
|
||||||
当"表示十分惊叹"时,使用"我嘞个xxxx"
|
当"表示十分惊叹"时,使用"我嘞个xxxx"
|
||||||
当"表示讽刺的赞同,不想讲道理"时,使用"对对对"
|
当"表示讽刺的赞同,不想讲道理"时,使用"对对对"
|
||||||
当"想说明某个观点,但懒得明说",使用"懂的都懂"
|
当"想说明某个观点,但懒得明说",使用"懂的都懂"
|
||||||
|
|
@ -34,13 +34,12 @@ class PersonalityExpression:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.express_learn_model: LLMRequest = LLMRequest(
|
self.express_learn_model: LLMRequest = LLMRequest(
|
||||||
model=global_config.model.focus_expressor,
|
model=global_config.model.focus_expressor,
|
||||||
temperature=0.1,
|
max_tokens=512,
|
||||||
max_tokens=256,
|
|
||||||
request_type="expressor.learner",
|
request_type="expressor.learner",
|
||||||
)
|
)
|
||||||
self.meta_file_path = os.path.join("data", "expression", "personality", "expression_style_meta.json")
|
self.meta_file_path = os.path.join("data", "expression", "personality", "expression_style_meta.json")
|
||||||
self.expressions_file_path = os.path.join("data", "expression", "personality", "expressions.json")
|
self.expressions_file_path = os.path.join("data", "expression", "personality", "expressions.json")
|
||||||
self.max_calculations = 5
|
self.max_calculations = 10
|
||||||
|
|
||||||
def _read_meta_data(self):
|
def _read_meta_data(self):
|
||||||
if os.path.exists(self.meta_file_path):
|
if os.path.exists(self.meta_file_path):
|
||||||
|
|
|
||||||
|
|
@ -753,8 +753,13 @@ class LLMRequest:
|
||||||
|
|
||||||
response = await self._execute_request(endpoint="/chat/completions", payload=data, prompt=prompt)
|
response = await self._execute_request(endpoint="/chat/completions", payload=data, prompt=prompt)
|
||||||
# 原样返回响应,不做处理
|
# 原样返回响应,不做处理
|
||||||
|
|
||||||
return response
|
if len(response) == 3:
|
||||||
|
content, reasoning_content, tool_calls = response
|
||||||
|
return content, (reasoning_content, self.model_name, tool_calls)
|
||||||
|
else:
|
||||||
|
content, reasoning_content = response
|
||||||
|
return content, (reasoning_content, self.model_name)
|
||||||
|
|
||||||
async def generate_response_tool_async(self, prompt: str, tools: list, **kwargs) -> tuple[str, str, list]:
|
async def generate_response_tool_async(self, prompt: str, tools: list, **kwargs) -> tuple[str, str, list]:
|
||||||
"""异步方式根据输入的提示生成模型的响应"""
|
"""异步方式根据输入的提示生成模型的响应"""
|
||||||
|
|
|
||||||
101
src/plugins.md
101
src/plugins.md
|
|
@ -1,101 +0,0 @@
|
||||||
# 如何编写MaiBot插件
|
|
||||||
|
|
||||||
## 基本步骤
|
|
||||||
|
|
||||||
1. 在`src/plugins/你的插件名/actions/`目录下创建插件文件
|
|
||||||
2. 继承`PluginAction`基类
|
|
||||||
3. 实现`process`方法
|
|
||||||
|
|
||||||
## 插件结构示例
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.common.logger_manager import get_logger
|
|
||||||
from src.chat.focus_chat.planners.actions.plugin_action import PluginAction, register_action
|
|
||||||
from typing import Tuple
|
|
||||||
|
|
||||||
logger = get_logger("your_action_name")
|
|
||||||
|
|
||||||
@register_action
|
|
||||||
class YourAction(PluginAction):
|
|
||||||
"""你的动作描述"""
|
|
||||||
|
|
||||||
action_name = "your_action_name" # 动作名称,必须唯一
|
|
||||||
action_description = "这个动作的详细描述,会展示给用户"
|
|
||||||
action_parameters = {
|
|
||||||
"param1": "参数1的说明(可选)",
|
|
||||||
"param2": "参数2的说明(可选)"
|
|
||||||
}
|
|
||||||
action_require = [
|
|
||||||
"使用场景1",
|
|
||||||
"使用场景2"
|
|
||||||
]
|
|
||||||
default = False # 是否默认启用
|
|
||||||
|
|
||||||
async def process(self) -> Tuple[bool, str]:
|
|
||||||
"""插件核心逻辑"""
|
|
||||||
# 你的代码逻辑...
|
|
||||||
return True, "执行结果"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 可用的API方法
|
|
||||||
|
|
||||||
插件可以使用`PluginAction`基类提供的以下API:
|
|
||||||
|
|
||||||
### 1. 发送消息
|
|
||||||
|
|
||||||
```python
|
|
||||||
await self.send_message("要发送的文本", target="可选的回复目标")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 获取聊天类型
|
|
||||||
|
|
||||||
```python
|
|
||||||
chat_type = self.get_chat_type() # 返回 "group" 或 "private" 或 "unknown"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 获取最近消息
|
|
||||||
|
|
||||||
```python
|
|
||||||
messages = self.get_recent_messages(count=5) # 获取最近5条消息
|
|
||||||
# 返回格式: [{"sender": "发送者", "content": "内容", "timestamp": 时间戳}, ...]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 获取动作参数
|
|
||||||
|
|
||||||
```python
|
|
||||||
param_value = self.action_data.get("param_name", "默认值")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 日志记录
|
|
||||||
|
|
||||||
```python
|
|
||||||
logger.info(f"{self.log_prefix} 你的日志信息")
|
|
||||||
logger.warning("警告信息")
|
|
||||||
logger.error("错误信息")
|
|
||||||
```
|
|
||||||
|
|
||||||
## 返回值说明
|
|
||||||
|
|
||||||
`process`方法必须返回一个元组,包含两个元素:
|
|
||||||
- 第一个元素(bool): 表示动作是否执行成功
|
|
||||||
- 第二个元素(str): 执行结果的文本描述
|
|
||||||
|
|
||||||
```python
|
|
||||||
return True, "执行成功的消息"
|
|
||||||
# 或
|
|
||||||
return False, "执行失败的原因"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 最佳实践
|
|
||||||
|
|
||||||
1. 使用`action_parameters`清晰定义你的动作需要的参数
|
|
||||||
2. 使用`action_require`描述何时应该使用你的动作
|
|
||||||
3. 使用`action_description`准确描述你的动作功能
|
|
||||||
4. 使用`logger`记录重要信息,方便调试
|
|
||||||
5. 避免操作底层系统,尽量使用`PluginAction`提供的API
|
|
||||||
|
|
||||||
## 注册与加载
|
|
||||||
|
|
||||||
插件会在系统启动时自动加载,只要放在正确的目录并添加了`@register_action`装饰器。
|
|
||||||
|
|
||||||
若设置`default = True`,插件会自动添加到默认动作集;否则需要在系统中手动启用。
|
|
||||||
|
|
@ -23,7 +23,7 @@ class MuteAction(PluginAction):
|
||||||
"当有人戳你两次以上时,防止刷屏,禁言他,必须牢记",
|
"当有人戳你两次以上时,防止刷屏,禁言他,必须牢记",
|
||||||
"当你想回避某个话题时使用",
|
"当你想回避某个话题时使用",
|
||||||
]
|
]
|
||||||
default = True # 默认动作,是否手动添加到使用集
|
default = False # 默认动作,是否手动添加到使用集
|
||||||
associated_types = ["command", "text"]
|
associated_types = ["command", "text"]
|
||||||
# associated_types = ["text"]
|
# associated_types = ["text"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ identity_detail = [
|
||||||
[expression]
|
[expression]
|
||||||
# 表达方式
|
# 表达方式
|
||||||
expression_style = "描述麦麦说话的表达风格,表达习惯,例如:(回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,回复不要浮夸,不要用夸张修辞,平淡一些。不要有额外的符号,尽量简单简短)"
|
expression_style = "描述麦麦说话的表达风格,表达习惯,例如:(回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,回复不要浮夸,不要用夸张修辞,平淡一些。不要有额外的符号,尽量简单简短)"
|
||||||
enable_expression_learning = true # 是否启用表达学习,麦麦会学习人类说话风格
|
enable_expression_learning = false # 是否启用表达学习,麦麦会学习人类说话风格
|
||||||
learning_interval = 600 # 学习间隔 单位秒
|
learning_interval = 600 # 学习间隔 单位秒
|
||||||
|
|
||||||
[relationship]
|
[relationship]
|
||||||
|
|
@ -94,8 +94,11 @@ talk_frequency_down_groups = [] #降低回复频率的群号码
|
||||||
think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗
|
think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗
|
||||||
consecutive_replies = 1 # 连续回复能力,值越高,麦麦连续回复的概率越高
|
consecutive_replies = 1 # 连续回复能力,值越高,麦麦连续回复的概率越高
|
||||||
|
|
||||||
|
parallel_processing = true # 是否并行处理回忆和处理器阶段,可以节省时间
|
||||||
|
|
||||||
observation_context_size = 16 # 观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖
|
processor_max_time = 25 # 处理器最大时间,单位秒,如果超过这个时间,处理器会自动停止
|
||||||
|
|
||||||
|
observation_context_size = 16 # 观察到的最长上下文大小
|
||||||
compressed_length = 8 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度,会压缩,最短压缩长度为5
|
compressed_length = 8 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度,会压缩,最短压缩长度为5
|
||||||
compress_length_limit = 4 #最多压缩份数,超过该数值的压缩上下文会被删除
|
compress_length_limit = 4 #最多压缩份数,超过该数值的压缩上下文会被删除
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue