mirror of https://github.com/Mai-with-u/MaiBot.git
ref:修改了plan的执行记录展示模式,现在每个动作的执行都会被记录
parent
6a0a07582e
commit
dbdf650b1d
|
|
@ -1,8 +1,21 @@
|
|||
# Changelog
|
||||
|
||||
## [0.10.4] - 2025-9-22
|
||||
表达方式优化
|
||||
## [0.11.0] - 2025-9-22
|
||||
### 🌟 主要功能更改
|
||||
- 重构记忆系统,新的记忆系统更可靠,记忆能力更强大
|
||||
- 麦麦好奇功能,麦麦会自主提出问题
|
||||
- 添加deepthink插件(默认关闭),让麦麦可以深度思考一些问题
|
||||
- 添加表情包管理插件
|
||||
|
||||
### 细节功能更改
|
||||
- 修复配置文件转义问题
|
||||
- 情绪系统现在可以由配置文件控制开关
|
||||
- 修复平行动作控制失效的问题
|
||||
- 添加planner防抖,防止短时间快速消耗token
|
||||
- 修复吞字问题
|
||||
- 更新依赖表
|
||||
- 修复负载均衡
|
||||
- 优化了对gemini和不同模型的支持
|
||||
|
||||
## [0.10.3] - 2025-9-22
|
||||
### 🌟 主要功能更改
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
## [1.0.3] - 2025-3-31
|
||||
### Added
|
||||
- 新增了心流相关配置项:
|
||||
- `heartflow` 配置项,用于控制心流功能
|
||||
|
||||
### Removed
|
||||
- 移除了 `response` 配置项中的 `model_r1_probability` 和 `model_v3_probability` 选项
|
||||
- 移除了次级推理模型相关配置
|
||||
|
||||
## [1.0.1] - 2025-3-30
|
||||
### Added
|
||||
- 增加了流式输出控制项 `stream`
|
||||
- 修复 `LLM_Request` 不会自动为 `payload` 增加流式输出标志的问题
|
||||
|
||||
## [1.0.0] - 2025-3-30
|
||||
### Added
|
||||
- 修复了错误的版本命名
|
||||
- 杀掉了所有无关文件
|
||||
|
||||
## [0.0.11] - 2025-3-12
|
||||
### Added
|
||||
- 新增了 `schedule` 配置项,用于配置日程表生成功能
|
||||
- 新增了 `response_splitter` 配置项,用于控制回复分割
|
||||
- 新增了 `experimental` 配置项,用于实验性功能开关
|
||||
- 新增了 `llm_observation` 和 `llm_sub_heartflow` 模型配置
|
||||
- 新增了 `llm_heartflow` 模型配置
|
||||
- 在 `personality` 配置项中新增了 `prompt_schedule_gen` 参数
|
||||
|
||||
### Changed
|
||||
- 优化了模型配置的组织结构
|
||||
- 调整了部分配置项的默认值
|
||||
- 调整了配置项的顺序,将 `groups` 配置项移到了更靠前的位置
|
||||
- 在 `message` 配置项中:
|
||||
- 新增了 `model_max_output_length` 参数
|
||||
- 在 `willing` 配置项中新增了 `emoji_response_penalty` 参数
|
||||
- 将 `personality` 配置项中的 `prompt_schedule` 重命名为 `prompt_schedule_gen`
|
||||
|
||||
### Removed
|
||||
- 移除了 `min_text_length` 配置项
|
||||
- 移除了 `cq_code` 配置项
|
||||
- 移除了 `others` 配置项(其功能已整合到 `experimental` 中)
|
||||
|
||||
## [0.0.5] - 2025-3-11
|
||||
### Added
|
||||
- 新增了 `alias_names` 配置项,用于指定麦麦的别名。
|
||||
|
||||
## [0.0.4] - 2025-3-9
|
||||
### Added
|
||||
- 新增了 `memory_ban_words` 配置项,用于指定不希望记忆的词汇。
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
import time
|
||||
import sys
|
||||
import os
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.dates as mdates
|
||||
from datetime import datetime
|
||||
from typing import List, Tuple
|
||||
import numpy as np
|
||||
|
||||
# Add project root to Python path
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
from src.common.database.database_model import Expression, ChatStreams
|
||||
|
||||
# 设置中文字体
|
||||
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
|
||||
|
||||
def get_chat_name(chat_id: str) -> str:
|
||||
"""Get chat name from chat_id by querying ChatStreams table directly"""
|
||||
try:
|
||||
chat_stream = ChatStreams.get_or_none(ChatStreams.stream_id == chat_id)
|
||||
if chat_stream is None:
|
||||
return f"未知聊天 ({chat_id})"
|
||||
|
||||
if chat_stream.group_name:
|
||||
return f"{chat_stream.group_name} ({chat_id})"
|
||||
elif chat_stream.user_nickname:
|
||||
return f"{chat_stream.user_nickname}的私聊 ({chat_id})"
|
||||
else:
|
||||
return f"未知聊天 ({chat_id})"
|
||||
except Exception:
|
||||
return f"查询失败 ({chat_id})"
|
||||
|
||||
|
||||
def get_expression_data() -> List[Tuple[float, float, str, str]]:
|
||||
"""获取Expression表中的数据,返回(create_date, count, chat_id, expression_type)的列表"""
|
||||
expressions = Expression.select()
|
||||
data = []
|
||||
|
||||
for expr in expressions:
|
||||
# 如果create_date为空,跳过该记录
|
||||
if expr.create_date is None:
|
||||
continue
|
||||
|
||||
data.append((
|
||||
expr.create_date,
|
||||
expr.count,
|
||||
expr.chat_id,
|
||||
expr.type
|
||||
))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def create_scatter_plot(data: List[Tuple[float, float, str, str]], save_path: str = None):
|
||||
"""创建散点图"""
|
||||
if not data:
|
||||
print("没有找到有效的表达式数据")
|
||||
return
|
||||
|
||||
# 分离数据
|
||||
create_dates = [item[0] for item in data]
|
||||
counts = [item[1] for item in data]
|
||||
chat_ids = [item[2] for item in data]
|
||||
expression_types = [item[3] for item in data]
|
||||
|
||||
# 转换时间戳为datetime对象
|
||||
dates = [datetime.fromtimestamp(ts) for ts in create_dates]
|
||||
|
||||
# 计算时间跨度,自动调整显示格式
|
||||
time_span = max(dates) - min(dates)
|
||||
if time_span.days > 30: # 超过30天,按月显示
|
||||
date_format = '%Y-%m-%d'
|
||||
major_locator = mdates.MonthLocator()
|
||||
minor_locator = mdates.DayLocator(interval=7)
|
||||
elif time_span.days > 7: # 超过7天,按天显示
|
||||
date_format = '%Y-%m-%d'
|
||||
major_locator = mdates.DayLocator(interval=1)
|
||||
minor_locator = mdates.HourLocator(interval=12)
|
||||
else: # 7天内,按小时显示
|
||||
date_format = '%Y-%m-%d %H:%M'
|
||||
major_locator = mdates.HourLocator(interval=6)
|
||||
minor_locator = mdates.HourLocator(interval=1)
|
||||
|
||||
# 创建图形
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
|
||||
# 创建散点图
|
||||
scatter = ax.scatter(dates, counts, alpha=0.6, s=30, c=range(len(dates)), cmap='viridis')
|
||||
|
||||
# 设置标签和标题
|
||||
ax.set_xlabel('创建日期 (Create Date)', fontsize=12)
|
||||
ax.set_ylabel('使用次数 (Count)', fontsize=12)
|
||||
ax.set_title('表达式使用次数随时间分布散点图', fontsize=14, fontweight='bold')
|
||||
|
||||
# 设置x轴日期格式 - 根据时间跨度自动调整
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(date_format))
|
||||
ax.xaxis.set_major_locator(major_locator)
|
||||
ax.xaxis.set_minor_locator(minor_locator)
|
||||
plt.xticks(rotation=45)
|
||||
|
||||
# 添加网格
|
||||
ax.grid(True, alpha=0.3)
|
||||
|
||||
# 添加颜色条
|
||||
cbar = plt.colorbar(scatter)
|
||||
cbar.set_label('数据点顺序', fontsize=10)
|
||||
|
||||
# 调整布局
|
||||
plt.tight_layout()
|
||||
|
||||
# 显示统计信息
|
||||
print(f"\n=== 数据统计 ===")
|
||||
print(f"总数据点数量: {len(data)}")
|
||||
print(f"时间范围: {min(dates).strftime('%Y-%m-%d %H:%M:%S')} 到 {max(dates).strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print(f"使用次数范围: {min(counts):.1f} 到 {max(counts):.1f}")
|
||||
print(f"平均使用次数: {np.mean(counts):.2f}")
|
||||
print(f"中位数使用次数: {np.median(counts):.2f}")
|
||||
|
||||
# 保存图片
|
||||
if save_path:
|
||||
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
||||
print(f"\n散点图已保存到: {save_path}")
|
||||
|
||||
# 显示图片
|
||||
plt.show()
|
||||
|
||||
|
||||
def create_grouped_scatter_plot(data: List[Tuple[float, float, str, str]], save_path: str = None):
|
||||
"""创建按聊天分组的散点图"""
|
||||
if not data:
|
||||
print("没有找到有效的表达式数据")
|
||||
return
|
||||
|
||||
# 按chat_id分组
|
||||
chat_groups = {}
|
||||
for item in data:
|
||||
chat_id = item[2]
|
||||
if chat_id not in chat_groups:
|
||||
chat_groups[chat_id] = []
|
||||
chat_groups[chat_id].append(item)
|
||||
|
||||
# 计算时间跨度,自动调整显示格式
|
||||
all_dates = [datetime.fromtimestamp(item[0]) for item in data]
|
||||
time_span = max(all_dates) - min(all_dates)
|
||||
if time_span.days > 30: # 超过30天,按月显示
|
||||
date_format = '%Y-%m-%d'
|
||||
major_locator = mdates.MonthLocator()
|
||||
minor_locator = mdates.DayLocator(interval=7)
|
||||
elif time_span.days > 7: # 超过7天,按天显示
|
||||
date_format = '%Y-%m-%d'
|
||||
major_locator = mdates.DayLocator(interval=1)
|
||||
minor_locator = mdates.HourLocator(interval=12)
|
||||
else: # 7天内,按小时显示
|
||||
date_format = '%Y-%m-%d %H:%M'
|
||||
major_locator = mdates.HourLocator(interval=6)
|
||||
minor_locator = mdates.HourLocator(interval=1)
|
||||
|
||||
# 创建图形
|
||||
fig, ax = plt.subplots(figsize=(14, 10))
|
||||
|
||||
# 为每个聊天分配不同颜色
|
||||
colors = plt.cm.Set3(np.linspace(0, 1, len(chat_groups)))
|
||||
|
||||
for i, (chat_id, chat_data) in enumerate(chat_groups.items()):
|
||||
create_dates = [item[0] for item in chat_data]
|
||||
counts = [item[1] for item in chat_data]
|
||||
dates = [datetime.fromtimestamp(ts) for ts in create_dates]
|
||||
|
||||
chat_name = get_chat_name(chat_id)
|
||||
# 截断过长的聊天名称
|
||||
display_name = chat_name[:20] + "..." if len(chat_name) > 20 else chat_name
|
||||
|
||||
ax.scatter(dates, counts, alpha=0.7, s=40,
|
||||
c=[colors[i]], label=f"{display_name} ({len(chat_data)}个)",
|
||||
edgecolors='black', linewidth=0.5)
|
||||
|
||||
# 设置标签和标题
|
||||
ax.set_xlabel('创建日期 (Create Date)', fontsize=12)
|
||||
ax.set_ylabel('使用次数 (Count)', fontsize=12)
|
||||
ax.set_title('按聊天分组的表达式使用次数散点图', fontsize=14, fontweight='bold')
|
||||
|
||||
# 设置x轴日期格式 - 根据时间跨度自动调整
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(date_format))
|
||||
ax.xaxis.set_major_locator(major_locator)
|
||||
ax.xaxis.set_minor_locator(minor_locator)
|
||||
plt.xticks(rotation=45)
|
||||
|
||||
# 添加图例
|
||||
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', fontsize=8)
|
||||
|
||||
# 添加网格
|
||||
ax.grid(True, alpha=0.3)
|
||||
|
||||
# 调整布局
|
||||
plt.tight_layout()
|
||||
|
||||
# 显示统计信息
|
||||
print(f"\n=== 分组统计 ===")
|
||||
print(f"总聊天数量: {len(chat_groups)}")
|
||||
for chat_id, chat_data in chat_groups.items():
|
||||
chat_name = get_chat_name(chat_id)
|
||||
counts = [item[1] for item in chat_data]
|
||||
print(f"{chat_name}: {len(chat_data)}个表达式, 平均使用次数: {np.mean(counts):.2f}")
|
||||
|
||||
# 保存图片
|
||||
if save_path:
|
||||
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
||||
print(f"\n分组散点图已保存到: {save_path}")
|
||||
|
||||
# 显示图片
|
||||
plt.show()
|
||||
|
||||
|
||||
def create_type_scatter_plot(data: List[Tuple[float, float, str, str]], save_path: str = None):
|
||||
"""创建按表达式类型分组的散点图"""
|
||||
if not data:
|
||||
print("没有找到有效的表达式数据")
|
||||
return
|
||||
|
||||
# 按type分组
|
||||
type_groups = {}
|
||||
for item in data:
|
||||
expr_type = item[3]
|
||||
if expr_type not in type_groups:
|
||||
type_groups[expr_type] = []
|
||||
type_groups[expr_type].append(item)
|
||||
|
||||
# 计算时间跨度,自动调整显示格式
|
||||
all_dates = [datetime.fromtimestamp(item[0]) for item in data]
|
||||
time_span = max(all_dates) - min(all_dates)
|
||||
if time_span.days > 30: # 超过30天,按月显示
|
||||
date_format = '%Y-%m-%d'
|
||||
major_locator = mdates.MonthLocator()
|
||||
minor_locator = mdates.DayLocator(interval=7)
|
||||
elif time_span.days > 7: # 超过7天,按天显示
|
||||
date_format = '%Y-%m-%d'
|
||||
major_locator = mdates.DayLocator(interval=1)
|
||||
minor_locator = mdates.HourLocator(interval=12)
|
||||
else: # 7天内,按小时显示
|
||||
date_format = '%Y-%m-%d %H:%M'
|
||||
major_locator = mdates.HourLocator(interval=6)
|
||||
minor_locator = mdates.HourLocator(interval=1)
|
||||
|
||||
# 创建图形
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
|
||||
# 为每个类型分配不同颜色
|
||||
colors = plt.cm.tab10(np.linspace(0, 1, len(type_groups)))
|
||||
|
||||
for i, (expr_type, type_data) in enumerate(type_groups.items()):
|
||||
create_dates = [item[0] for item in type_data]
|
||||
counts = [item[1] for item in type_data]
|
||||
dates = [datetime.fromtimestamp(ts) for ts in create_dates]
|
||||
|
||||
ax.scatter(dates, counts, alpha=0.7, s=40,
|
||||
c=[colors[i]], label=f"{expr_type} ({len(type_data)}个)",
|
||||
edgecolors='black', linewidth=0.5)
|
||||
|
||||
# 设置标签和标题
|
||||
ax.set_xlabel('创建日期 (Create Date)', fontsize=12)
|
||||
ax.set_ylabel('使用次数 (Count)', fontsize=12)
|
||||
ax.set_title('按表达式类型分组的散点图', fontsize=14, fontweight='bold')
|
||||
|
||||
# 设置x轴日期格式 - 根据时间跨度自动调整
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(date_format))
|
||||
ax.xaxis.set_major_locator(major_locator)
|
||||
ax.xaxis.set_minor_locator(minor_locator)
|
||||
plt.xticks(rotation=45)
|
||||
|
||||
# 添加图例
|
||||
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
|
||||
|
||||
# 添加网格
|
||||
ax.grid(True, alpha=0.3)
|
||||
|
||||
# 调整布局
|
||||
plt.tight_layout()
|
||||
|
||||
# 显示统计信息
|
||||
print(f"\n=== 类型统计 ===")
|
||||
for expr_type, type_data in type_groups.items():
|
||||
counts = [item[1] for item in type_data]
|
||||
print(f"{expr_type}: {len(type_data)}个表达式, 平均使用次数: {np.mean(counts):.2f}")
|
||||
|
||||
# 保存图片
|
||||
if save_path:
|
||||
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
||||
print(f"\n类型散点图已保存到: {save_path}")
|
||||
|
||||
# 显示图片
|
||||
plt.show()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("开始分析表达式数据...")
|
||||
|
||||
# 获取数据
|
||||
data = get_expression_data()
|
||||
|
||||
if not data:
|
||||
print("没有找到有效的表达式数据(create_date不为空的数据)")
|
||||
return
|
||||
|
||||
print(f"找到 {len(data)} 条有效数据")
|
||||
|
||||
# 创建输出目录
|
||||
output_dir = os.path.join(project_root, "data", "temp")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# 生成时间戳用于文件名
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
# 1. 创建基础散点图
|
||||
print("\n1. 创建基础散点图...")
|
||||
create_scatter_plot(data, os.path.join(output_dir, f"expression_scatter_{timestamp}.png"))
|
||||
|
||||
# 2. 创建按聊天分组的散点图
|
||||
print("\n2. 创建按聊天分组的散点图...")
|
||||
create_grouped_scatter_plot(data, os.path.join(output_dir, f"expression_scatter_by_chat_{timestamp}.png"))
|
||||
|
||||
# 3. 创建按类型分组的散点图
|
||||
print("\n3. 创建按类型分组的散点图...")
|
||||
create_type_scatter_plot(data, os.path.join(output_dir, f"expression_scatter_by_type_{timestamp}.png"))
|
||||
|
||||
print("\n分析完成!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -282,7 +282,7 @@ class BrainChatting:
|
|||
prompt_info = (modified_message.llm_prompt, prompt_info[1])
|
||||
|
||||
with Timer("规划器", cycle_timers):
|
||||
action_to_use_info, _ = await self.action_planner.plan(
|
||||
action_to_use_info = await self.action_planner.plan(
|
||||
loop_start_time=self.last_read_time,
|
||||
available_actions=available_actions,
|
||||
)
|
||||
|
|
@ -413,8 +413,8 @@ class BrainChatting:
|
|||
logger.warning(f"{self.log_prefix} 未能创建动作处理器: {action}")
|
||||
return False, "", ""
|
||||
|
||||
# 处理动作并获取结果
|
||||
result = await action_handler.execute()
|
||||
# 处理动作并获取结果(固定记录一次动作信息)
|
||||
result = await action_handler.run()
|
||||
success, action_text = result
|
||||
command = ""
|
||||
|
||||
|
|
@ -481,11 +481,11 @@ class BrainChatting:
|
|||
try:
|
||||
with Timer(f"动作{action_planner_info.action_type}", cycle_timers):
|
||||
if action_planner_info.action_type == "no_reply":
|
||||
# 直接处理no_action逻辑,不再通过动作系统
|
||||
# 直接处理no_reply逻辑,不再通过动作系统
|
||||
reason = action_planner_info.reasoning or "选择不回复"
|
||||
# logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}")
|
||||
|
||||
# 存储no_action信息到数据库
|
||||
# 存储no_reply信息到数据库
|
||||
await database_api.store_action_info(
|
||||
chat_stream=self.chat_stream,
|
||||
action_build_into_prompt=False,
|
||||
|
|
@ -493,9 +493,9 @@ class BrainChatting:
|
|||
action_done=True,
|
||||
thinking_id=thinking_id,
|
||||
action_data={"reason": reason},
|
||||
action_name="no_action",
|
||||
action_name="no_reply",
|
||||
)
|
||||
return {"action_type": "no_action", "success": True, "reply_text": "", "command": ""}
|
||||
return {"action_type": "no_reply", "success": True, "reply_text": "", "command": ""}
|
||||
|
||||
elif action_planner_info.action_type == "reply":
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -152,10 +152,10 @@ class BrainPlanner:
|
|||
action_planner_infos = []
|
||||
|
||||
try:
|
||||
action = action_json.get("action", "no_action")
|
||||
action = action_json.get("action", "no_reply")
|
||||
reasoning = action_json.get("reason", "未提供原因")
|
||||
action_data = {key: value for key, value in action_json.items() if key not in ["action", "reason"]}
|
||||
# 非no_action动作需要target_message_id
|
||||
# 非no_reply动作需要target_message_id
|
||||
target_message = None
|
||||
|
||||
if target_message_id := action_json.get("target_message_id"):
|
||||
|
|
@ -215,12 +215,11 @@ class BrainPlanner:
|
|||
self,
|
||||
available_actions: Dict[str, ActionInfo],
|
||||
loop_start_time: float = 0.0,
|
||||
) -> Tuple[List[ActionPlannerInfo], Optional["DatabaseMessages"]]:
|
||||
) -> List[ActionPlannerInfo]:
|
||||
# sourcery skip: use-named-expression
|
||||
"""
|
||||
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
||||
"""
|
||||
target_message: Optional["DatabaseMessages"] = None
|
||||
|
||||
# 获取聊天上下文
|
||||
message_list_before_now = get_raw_msg_before_timestamp_with_chat(
|
||||
|
|
@ -274,12 +273,7 @@ class BrainPlanner:
|
|||
loop_start_time=loop_start_time,
|
||||
)
|
||||
|
||||
# 获取target_message(如果有非no_action的动作)
|
||||
non_no_actions = [a for a in actions if a.action_type != "no_reply"]
|
||||
if non_no_actions:
|
||||
target_message = non_no_actions[0].action_message
|
||||
|
||||
return actions, target_message
|
||||
return actions
|
||||
|
||||
async def build_planner_prompt(
|
||||
self,
|
||||
|
|
@ -489,7 +483,7 @@ class BrainPlanner:
|
|||
else:
|
||||
actions = self._create_no_reply("规划器没有获得LLM响应", available_actions)
|
||||
|
||||
# 添加循环开始时间到所有非no_action动作
|
||||
# 添加循环开始时间到所有非no_reply动作
|
||||
for action in actions:
|
||||
action.action_data = action.action_data or {}
|
||||
action.action_data["loop_start_time"] = loop_start_time
|
||||
|
|
@ -501,7 +495,7 @@ class BrainPlanner:
|
|||
return actions
|
||||
|
||||
def _create_no_reply(self, reasoning: str, available_actions: Dict[str, ActionInfo]) -> List[ActionPlannerInfo]:
|
||||
"""创建no_action"""
|
||||
"""创建no_reply"""
|
||||
return [
|
||||
ActionPlannerInfo(
|
||||
action_type="no_reply",
|
||||
|
|
|
|||
|
|
@ -98,6 +98,8 @@ class HeartFChatting:
|
|||
|
||||
self.last_read_time = time.time() - 2
|
||||
self.no_reply_until_call = False
|
||||
|
||||
self.is_mute = False
|
||||
|
||||
|
||||
async def start(self):
|
||||
|
|
@ -175,6 +177,8 @@ class HeartFChatting:
|
|||
)
|
||||
|
||||
if len(recent_messages_list) >= 1:
|
||||
# for message in recent_messages_list:
|
||||
# print(message.processed_plain_text)
|
||||
# !处理no_reply_until_call逻辑
|
||||
if self.no_reply_until_call:
|
||||
for message in recent_messages_list:
|
||||
|
|
@ -185,6 +189,7 @@ class HeartFChatting:
|
|||
or time.time() - self.last_read_time > 600
|
||||
):
|
||||
self.no_reply_until_call = False
|
||||
self.last_read_time = time.time()
|
||||
break
|
||||
# 没有提到,继续保持沉默
|
||||
if self.no_reply_until_call:
|
||||
|
|
@ -333,7 +338,7 @@ class HeartFChatting:
|
|||
prompt_info = (modified_message.llm_prompt, prompt_info[1])
|
||||
|
||||
with Timer("规划器", cycle_timers):
|
||||
action_to_use_info, _ = await self.action_planner.plan(
|
||||
action_to_use_info = await self.action_planner.plan(
|
||||
loop_start_time=self.last_read_time,
|
||||
available_actions=available_actions,
|
||||
)
|
||||
|
|
@ -450,7 +455,7 @@ class HeartFChatting:
|
|||
async def _handle_action(
|
||||
self,
|
||||
action: str,
|
||||
reasoning: str,
|
||||
action_reasoning: str,
|
||||
action_data: dict,
|
||||
cycle_timers: Dict[str, float],
|
||||
thinking_id: str,
|
||||
|
|
@ -461,11 +466,11 @@ class HeartFChatting:
|
|||
|
||||
参数:
|
||||
action: 动作类型
|
||||
reasoning: 决策理由
|
||||
action_reasoning: 决策理由
|
||||
action_data: 动作数据,包含不同动作需要的参数
|
||||
cycle_timers: 计时器字典
|
||||
thinking_id: 思考ID
|
||||
|
||||
action_message: 消息数据
|
||||
返回:
|
||||
tuple[bool, str, str]: (是否执行了动作, 思考消息ID, 命令)
|
||||
"""
|
||||
|
|
@ -475,11 +480,11 @@ class HeartFChatting:
|
|||
action_handler = self.action_manager.create_action(
|
||||
action_name=action,
|
||||
action_data=action_data,
|
||||
reasoning=reasoning,
|
||||
cycle_timers=cycle_timers,
|
||||
thinking_id=thinking_id,
|
||||
chat_stream=self.chat_stream,
|
||||
log_prefix=self.log_prefix,
|
||||
action_reasoning=action_reasoning,
|
||||
action_message=action_message,
|
||||
)
|
||||
except Exception as e:
|
||||
|
|
@ -491,7 +496,7 @@ class HeartFChatting:
|
|||
logger.warning(f"{self.log_prefix} 未能创建动作处理器: {action}")
|
||||
return False, "", ""
|
||||
|
||||
# 处理动作并获取结果
|
||||
# 处理动作并获取结果(固定记录一次动作信息)
|
||||
result = await action_handler.execute()
|
||||
success, action_text = result
|
||||
command = ""
|
||||
|
|
@ -558,42 +563,67 @@ class HeartFChatting:
|
|||
"""执行单个动作的通用函数"""
|
||||
try:
|
||||
with Timer(f"动作{action_planner_info.action_type}", cycle_timers):
|
||||
# 直接当场执行no_reply逻辑
|
||||
if action_planner_info.action_type == "no_reply":
|
||||
# 直接处理no_action逻辑,不再通过动作系统
|
||||
# 直接处理no_reply逻辑,不再通过动作系统
|
||||
reason = action_planner_info.reasoning or "选择不回复"
|
||||
# logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}")
|
||||
|
||||
# 存储no_action信息到数据库
|
||||
await database_api.store_action_info(
|
||||
chat_stream=self.chat_stream,
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=reason,
|
||||
action_done=True,
|
||||
thinking_id=thinking_id,
|
||||
action_data={"reason": reason},
|
||||
action_name="no_action",
|
||||
action_data={},
|
||||
action_name="no_reply",
|
||||
action_reasoning=reason,
|
||||
)
|
||||
return {"action_type": "no_action", "success": True, "reply_text": "", "command": ""}
|
||||
|
||||
elif action_planner_info.action_type == "wait_time":
|
||||
action_planner_info.action_data = action_planner_info.action_data or {}
|
||||
logger.info(f"{self.log_prefix} 等待{action_planner_info.action_data['time']}秒后回复")
|
||||
await asyncio.sleep(action_planner_info.action_data["time"])
|
||||
return {"action_type": "wait_time", "success": True, "reply_text": "", "command": ""}
|
||||
|
||||
return {"action_type": "no_reply", "success": True, "reply_text": "", "command": ""}
|
||||
|
||||
elif action_planner_info.action_type == "no_reply_until_call":
|
||||
# 直接当场执行no_reply_until_call逻辑
|
||||
logger.info(f"{self.log_prefix} 保持沉默,直到有人直接叫的名字")
|
||||
reason = action_planner_info.reasoning or "选择不回复"
|
||||
|
||||
self.no_reply_until_call = True
|
||||
await database_api.store_action_info(
|
||||
chat_stream=self.chat_stream,
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=reason,
|
||||
action_done=True,
|
||||
thinking_id=thinking_id,
|
||||
action_data={},
|
||||
action_name="no_reply_until_call",
|
||||
action_reasoning=reason,
|
||||
)
|
||||
|
||||
|
||||
return {"action_type": "no_reply_until_call", "success": True, "reply_text": "", "command": ""}
|
||||
|
||||
elif action_planner_info.action_type == "reply":
|
||||
# 直接当场执行reply逻辑
|
||||
try:
|
||||
reason = action_planner_info.reasoning or "选择回复"
|
||||
await database_api.store_action_info(
|
||||
chat_stream=self.chat_stream,
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=reason,
|
||||
action_done=True,
|
||||
thinking_id=thinking_id,
|
||||
action_data={},
|
||||
action_name="reply",
|
||||
action_reasoning=reason,
|
||||
)
|
||||
|
||||
success, llm_response = await generator_api.generate_reply(
|
||||
chat_stream=self.chat_stream,
|
||||
reply_message=action_planner_info.action_message,
|
||||
available_actions=available_actions,
|
||||
chosen_actions=chosen_action_plan_infos,
|
||||
reply_reason=action_planner_info.reasoning or "",
|
||||
reply_reason=reason,
|
||||
enable_tool=global_config.tool.enable_tool,
|
||||
request_type="replyer",
|
||||
from_plugin=False,
|
||||
|
|
@ -627,18 +657,16 @@ class HeartFChatting:
|
|||
"reply_text": reply_text,
|
||||
"loop_info": loop_info,
|
||||
}
|
||||
|
||||
# 其他动作
|
||||
else:
|
||||
# 执行普通动作
|
||||
with Timer("动作执行", cycle_timers):
|
||||
success, reply_text, command = await self._handle_action(
|
||||
action_planner_info.action_type,
|
||||
action_planner_info.reasoning or "",
|
||||
action_planner_info.action_data or {},
|
||||
cycle_timers,
|
||||
thinking_id,
|
||||
action_planner_info.action_message,
|
||||
action = action_planner_info.action_type,
|
||||
action_reasoning = action_planner_info.action_reasoning or "",
|
||||
action_data = action_planner_info.action_data or {},
|
||||
cycle_timers = cycle_timers,
|
||||
thinking_id = thinking_id,
|
||||
action_message= action_planner_info.action_message,
|
||||
)
|
||||
return {
|
||||
"action_type": action_planner_info.action_type,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class ActionManager:
|
|||
self,
|
||||
action_name: str,
|
||||
action_data: dict,
|
||||
reasoning: str,
|
||||
action_reasoning: str,
|
||||
cycle_timers: dict,
|
||||
thinking_id: str,
|
||||
chat_stream: ChatStream,
|
||||
|
|
@ -46,7 +46,7 @@ class ActionManager:
|
|||
Args:
|
||||
action_name: 动作名称
|
||||
action_data: 动作数据
|
||||
reasoning: 执行理由
|
||||
action_reasoning: 执行理由
|
||||
cycle_timers: 计时器字典
|
||||
thinking_id: 思考ID
|
||||
chat_stream: 聊天流
|
||||
|
|
@ -77,7 +77,7 @@ class ActionManager:
|
|||
# 创建动作实例
|
||||
instance = component_class(
|
||||
action_data=action_data,
|
||||
reasoning=reasoning,
|
||||
action_reasoning=action_reasoning,
|
||||
cycle_timers=cycle_timers,
|
||||
thinking_id=thinking_id,
|
||||
chat_stream=chat_stream,
|
||||
|
|
|
|||
|
|
@ -44,10 +44,7 @@ def init_prompt():
|
|||
**聊天内容**
|
||||
{chat_content_block}
|
||||
|
||||
**动作记录**
|
||||
{actions_before_now_block}
|
||||
|
||||
**可用的action**
|
||||
**可选的action**
|
||||
reply
|
||||
动作描述:
|
||||
1.你可以选择呼叫了你的名字,但是你没有做出回应的消息进行回复
|
||||
|
|
@ -76,7 +73,10 @@ no_reply_until_call
|
|||
|
||||
{action_options_text}
|
||||
|
||||
请选择合适的action,并说明触发action的消息id和选择该action的原因。消息id格式:m+数字
|
||||
**你之前的action执行和思考记录**
|
||||
{actions_before_now_block}
|
||||
|
||||
请选择**可选的**且符合使用条件的action,并说明触发action的消息id(消息id格式:m+数字)
|
||||
先输出你的选择思考理由,再输出你选择的action,理由是一段平文本,不要分点,精简。
|
||||
**动作选择要求**
|
||||
请你根据聊天内容,用户的最新消息和以下标准选择合适的动作:
|
||||
|
|
@ -99,9 +99,7 @@ no_reply_until_call
|
|||
"target_message_id":"触发动作的消息id",
|
||||
//对应参数
|
||||
}}
|
||||
```
|
||||
|
||||
""",
|
||||
```""",
|
||||
"planner_prompt",
|
||||
)
|
||||
|
||||
|
|
@ -133,6 +131,9 @@ class ActionPlanner:
|
|||
|
||||
self.last_obs_time_mark = 0.0
|
||||
|
||||
|
||||
self.plan_log:List[Tuple[str,str,ActionPlannerInfo]] = []
|
||||
|
||||
def find_message_by_id(
|
||||
self, message_id: str, message_id_list: List[Tuple[str, "DatabaseMessages"]]
|
||||
) -> Optional["DatabaseMessages"]:
|
||||
|
|
@ -157,15 +158,16 @@ class ActionPlanner:
|
|||
action_json: dict,
|
||||
message_id_list: List[Tuple[str, "DatabaseMessages"]],
|
||||
current_available_actions: List[Tuple[str, ActionInfo]],
|
||||
extracted_reasoning: str = "",
|
||||
) -> List[ActionPlannerInfo]:
|
||||
"""解析单个action JSON并返回ActionPlannerInfo列表"""
|
||||
action_planner_infos = []
|
||||
|
||||
try:
|
||||
action = action_json.get("action", "no_action")
|
||||
action = action_json.get("action", "no_reply")
|
||||
reasoning = action_json.get("reason", "未提供原因")
|
||||
action_data = {key: value for key, value in action_json.items() if key not in ["action", "reason"]}
|
||||
# 非no_action动作需要target_message_id
|
||||
# 非no_reply动作需要target_message_id
|
||||
target_message = None
|
||||
|
||||
if target_message_id := action_json.get("target_message_id"):
|
||||
|
|
@ -202,6 +204,7 @@ class ActionPlanner:
|
|||
action_data=action_data,
|
||||
action_message=target_message,
|
||||
available_actions=available_actions_dict,
|
||||
action_reasoning=extracted_reasoning if extracted_reasoning else None,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -216,6 +219,7 @@ class ActionPlanner:
|
|||
action_data={},
|
||||
action_message=None,
|
||||
available_actions=available_actions_dict,
|
||||
action_reasoning=extracted_reasoning if extracted_reasoning else None,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -225,12 +229,11 @@ class ActionPlanner:
|
|||
self,
|
||||
available_actions: Dict[str, ActionInfo],
|
||||
loop_start_time: float = 0.0,
|
||||
) -> Tuple[List[ActionPlannerInfo], Optional["DatabaseMessages"]]:
|
||||
) -> List[ActionPlannerInfo]:
|
||||
# sourcery skip: use-named-expression
|
||||
"""
|
||||
规划器 (Planner): 使用LLM根据上下文决定做出什么动作。
|
||||
"""
|
||||
target_message: Optional["DatabaseMessages"] = None
|
||||
|
||||
# 获取聊天上下文
|
||||
message_list_before_now = get_raw_msg_before_timestamp_with_chat(
|
||||
|
|
@ -276,7 +279,7 @@ class ActionPlanner:
|
|||
)
|
||||
|
||||
# 调用LLM获取决策
|
||||
actions = await self._execute_main_planner(
|
||||
reasoning, actions = await self._execute_main_planner(
|
||||
prompt=prompt,
|
||||
message_id_list=message_id_list,
|
||||
filtered_actions=filtered_actions,
|
||||
|
|
@ -284,12 +287,22 @@ class ActionPlanner:
|
|||
loop_start_time=loop_start_time,
|
||||
)
|
||||
|
||||
# 获取target_message(如果有非no_action的动作)
|
||||
non_no_actions = [a for a in actions if a.action_type != "no_reply"]
|
||||
if non_no_actions:
|
||||
target_message = non_no_actions[0].action_message
|
||||
self.add_plan_log(reasoning, actions)
|
||||
|
||||
return actions
|
||||
|
||||
def add_plan_log(self, reasoning: str, actions: List[ActionPlannerInfo]):
|
||||
self.plan_log.append((reasoning, time.time(), actions))
|
||||
if len(self.plan_log) > 100:
|
||||
self.plan_log.pop(0)
|
||||
|
||||
def get_plan_log_str(self) -> str:
|
||||
plan_log_str = ""
|
||||
for reasoning, time, actions in self.plan_log:
|
||||
time = datetime.fromtimestamp(time).strftime("%H:%M:%S")
|
||||
plan_log_str += f"{time}:{reasoning}|使用了{','.join([action.action_type for action in actions])}\n"
|
||||
return plan_log_str
|
||||
|
||||
return actions, target_message
|
||||
|
||||
async def build_planner_prompt(
|
||||
self,
|
||||
|
|
@ -302,18 +315,8 @@ class ActionPlanner:
|
|||
) -> tuple[str, List[Tuple[str, "DatabaseMessages"]]]:
|
||||
"""构建 Planner LLM 的提示词 (获取模板并填充数据)"""
|
||||
try:
|
||||
# 获取最近执行过的动作
|
||||
actions_before_now = get_actions_by_timestamp_with_chat(
|
||||
chat_id=self.chat_id,
|
||||
timestamp_start=time.time() - 600,
|
||||
timestamp_end=time.time(),
|
||||
limit=6,
|
||||
)
|
||||
actions_before_now_block = build_readable_actions(actions=actions_before_now)
|
||||
if actions_before_now_block:
|
||||
actions_before_now_block = f"你刚刚选择并执行过的action是:\n{actions_before_now_block}"
|
||||
else:
|
||||
actions_before_now_block = ""
|
||||
|
||||
actions_before_now_block=self.get_plan_log_str()
|
||||
|
||||
# 构建聊天上下文描述
|
||||
chat_context_description = "你现在正在一个群聊中"
|
||||
|
|
@ -447,7 +450,7 @@ class ActionPlanner:
|
|||
filtered_actions: Dict[str, ActionInfo],
|
||||
available_actions: Dict[str, ActionInfo],
|
||||
loop_start_time: float,
|
||||
) -> List[ActionPlannerInfo]:
|
||||
) -> Tuple[str,List[ActionPlannerInfo]]:
|
||||
"""执行主规划器"""
|
||||
llm_content = None
|
||||
actions: List[ActionPlannerInfo] = []
|
||||
|
|
@ -456,8 +459,8 @@ class ActionPlanner:
|
|||
# 调用LLM
|
||||
llm_content, (reasoning_content, _, _) = await self.planner_llm.generate_response_async(prompt=prompt)
|
||||
|
||||
# logger.info(f"{self.log_prefix}规划器原始提示词: {prompt}")
|
||||
# logger.info(f"{self.log_prefix}规划器原始响应: {llm_content}")
|
||||
logger.info(f"{self.log_prefix}规划器原始提示词: {prompt}")
|
||||
logger.info(f"{self.log_prefix}规划器原始响应: {llm_content}")
|
||||
|
||||
if global_config.debug.show_prompt:
|
||||
logger.info(f"{self.log_prefix}规划器原始提示词: {prompt}")
|
||||
|
|
@ -472,7 +475,7 @@ class ActionPlanner:
|
|||
|
||||
except Exception as req_e:
|
||||
logger.error(f"{self.log_prefix}LLM 请求执行失败: {req_e}")
|
||||
return [
|
||||
return f"LLM 请求失败,模型出现问题: {req_e}",[
|
||||
ActionPlannerInfo(
|
||||
action_type="no_reply",
|
||||
reasoning=f"LLM 请求失败,模型出现问题: {req_e}",
|
||||
|
|
@ -485,11 +488,12 @@ class ActionPlanner:
|
|||
# 解析LLM响应
|
||||
if llm_content:
|
||||
try:
|
||||
if json_objects := self._extract_json_from_markdown(llm_content):
|
||||
json_objects, extracted_reasoning = self._extract_json_from_markdown(llm_content)
|
||||
if json_objects:
|
||||
logger.debug(f"{self.log_prefix}从响应中提取到{len(json_objects)}个JSON对象")
|
||||
filtered_actions_list = list(filtered_actions.items())
|
||||
for json_obj in json_objects:
|
||||
actions.extend(self._parse_single_action(json_obj, message_id_list, filtered_actions_list))
|
||||
actions.extend(self._parse_single_action(json_obj, message_id_list, filtered_actions_list, extracted_reasoning))
|
||||
else:
|
||||
# 尝试解析为直接的JSON
|
||||
logger.warning(f"{self.log_prefix}LLM没有返回可用动作: {llm_content}")
|
||||
|
|
@ -502,17 +506,17 @@ class ActionPlanner:
|
|||
else:
|
||||
actions = self._create_no_reply("规划器没有获得LLM响应", available_actions)
|
||||
|
||||
# 添加循环开始时间到所有非no_action动作
|
||||
# 添加循环开始时间到所有非no_reply动作
|
||||
for action in actions:
|
||||
action.action_data = action.action_data or {}
|
||||
action.action_data["loop_start_time"] = loop_start_time
|
||||
|
||||
logger.debug(f"{self.log_prefix}规划器选择了{len(actions)}个动作: {' '.join([a.action_type for a in actions])}")
|
||||
|
||||
return actions
|
||||
return extracted_reasoning,actions
|
||||
|
||||
def _create_no_reply(self, reasoning: str, available_actions: Dict[str, ActionInfo]) -> List[ActionPlannerInfo]:
|
||||
"""创建no_action"""
|
||||
"""创建no_reply"""
|
||||
return [
|
||||
ActionPlannerInfo(
|
||||
action_type="no_reply",
|
||||
|
|
@ -523,15 +527,26 @@ class ActionPlanner:
|
|||
)
|
||||
]
|
||||
|
||||
def _extract_json_from_markdown(self, content: str) -> List[dict]:
|
||||
def _extract_json_from_markdown(self, content: str) -> Tuple[List[dict], str]:
|
||||
# sourcery skip: for-append-to-extend
|
||||
"""从Markdown格式的内容中提取JSON对象"""
|
||||
"""从Markdown格式的内容中提取JSON对象和推理内容"""
|
||||
json_objects = []
|
||||
reasoning_content = ""
|
||||
|
||||
# 使用正则表达式查找```json包裹的JSON内容
|
||||
json_pattern = r"```json\s*(.*?)\s*```"
|
||||
matches = re.findall(json_pattern, content, re.DOTALL)
|
||||
|
||||
# 提取JSON之前的内容作为推理文本
|
||||
if matches:
|
||||
# 找到第一个```json的位置
|
||||
first_json_pos = content.find("```json")
|
||||
if first_json_pos > 0:
|
||||
reasoning_content = content[:first_json_pos].strip()
|
||||
# 清理推理内容中的注释标记
|
||||
reasoning_content = re.sub(r"^//\s*", "", reasoning_content, flags=re.MULTILINE)
|
||||
reasoning_content = reasoning_content.strip()
|
||||
|
||||
for match in matches:
|
||||
try:
|
||||
# 清理可能的注释和格式问题
|
||||
|
|
@ -549,7 +564,7 @@ class ActionPlanner:
|
|||
logger.warning(f"解析JSON块失败: {e}, 块内容: {match[:100]}...")
|
||||
continue
|
||||
|
||||
return json_objects
|
||||
return json_objects, reasoning_content
|
||||
|
||||
|
||||
init_prompt()
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ def get_actions_by_timestamp_with_chat(
|
|||
chat_id=action.chat_id,
|
||||
chat_info_stream_id=action.chat_info_stream_id,
|
||||
chat_info_platform=action.chat_info_platform,
|
||||
action_reasoning=action.action_reasoning,
|
||||
)
|
||||
for action in actions
|
||||
]
|
||||
|
|
@ -559,14 +560,12 @@ def build_readable_actions(actions: List[DatabaseActionRecords], mode: str = "re
|
|||
output_lines = []
|
||||
current_time = time.time()
|
||||
|
||||
# The get functions return actions sorted ascending by time. Let's reverse it to show newest first.
|
||||
# sorted_actions = sorted(actions, key=lambda x: x.get("time", 0), reverse=True)
|
||||
|
||||
for action in actions:
|
||||
action_time = action.time or current_time
|
||||
action_name = action.action_name or "未知动作"
|
||||
# action_reason = action.get(action_data")
|
||||
if action_name in ["no_action", "no_action"]:
|
||||
if action_name in ["no_reply", "no_reply"]:
|
||||
continue
|
||||
|
||||
action_prompt_display = action.action_prompt_display or "无具体内容"
|
||||
|
|
@ -588,6 +587,7 @@ def build_readable_actions(actions: List[DatabaseActionRecords], mode: str = "re
|
|||
|
||||
line = f"{time_ago_str},你使用了“{action_name}”,具体内容是:“{action_prompt_display}”"
|
||||
output_lines.append(line)
|
||||
|
||||
|
||||
return "\n".join(output_lines)
|
||||
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@ class DatabaseActionRecords(BaseDataModel):
|
|||
chat_id: str,
|
||||
chat_info_stream_id: str,
|
||||
chat_info_platform: str,
|
||||
action_reasoning:str
|
||||
):
|
||||
self.action_id = action_id
|
||||
self.time = time
|
||||
|
|
@ -234,3 +235,4 @@ class DatabaseActionRecords(BaseDataModel):
|
|||
self.chat_id = chat_id
|
||||
self.chat_info_stream_id = chat_info_stream_id
|
||||
self.chat_info_platform = chat_info_platform
|
||||
self.action_reasoning = action_reasoning
|
||||
|
|
@ -24,3 +24,4 @@ class ActionPlannerInfo(BaseDataModel):
|
|||
action_message: Optional["DatabaseMessages"] = None
|
||||
available_actions: Optional[Dict[str, "ActionInfo"]] = None
|
||||
loop_start_time: Optional[float] = None
|
||||
action_reasoning: Optional[str] = None
|
||||
|
|
|
|||
|
|
@ -185,6 +185,8 @@ class ActionRecords(BaseModel):
|
|||
action_id = TextField(index=True) # 消息 ID (更改自 IntegerField)
|
||||
time = DoubleField() # 消息时间戳
|
||||
|
||||
action_reasoning = TextField(null=True)
|
||||
|
||||
action_name = TextField()
|
||||
action_data = TextField()
|
||||
action_done = BooleanField(default=False)
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ MODULE_COLORS = {
|
|||
"tts_action": "\033[38;5;58m", # 深黄色
|
||||
"doubao_pic_plugin": "\033[38;5;64m", # 深绿色
|
||||
# Action组件
|
||||
"no_action_action": "\033[38;5;214m", # 亮橙色,显眼但不像警告
|
||||
"no_reply_action": "\033[38;5;214m", # 亮橙色,显眼但不像警告
|
||||
"reply_action": "\033[38;5;46m", # 亮绿色
|
||||
"base_action": "\033[38;5;250m", # 浅灰色
|
||||
# 数据库和消息
|
||||
|
|
|
|||
|
|
@ -309,6 +309,7 @@ async def store_action_info(
|
|||
thinking_id: str = "",
|
||||
action_data: Optional[dict] = None,
|
||||
action_name: str = "",
|
||||
action_reasoning: str = "",
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""存储动作信息到数据库
|
||||
|
||||
|
|
@ -322,7 +323,7 @@ async def store_action_info(
|
|||
thinking_id: 关联的思考ID
|
||||
action_data: 动作数据字典
|
||||
action_name: 动作名称
|
||||
|
||||
action_reasoning: 动作执行理由
|
||||
Returns:
|
||||
Dict[str, Any]: 保存的记录数据
|
||||
None: 如果保存失败
|
||||
|
|
@ -348,6 +349,7 @@ async def store_action_info(
|
|||
"action_name": action_name,
|
||||
"action_data": json.dumps(action_data or {}, ensure_ascii=False),
|
||||
"action_done": action_done,
|
||||
"action_reasoning": action_reasoning,
|
||||
"action_build_into_prompt": action_build_into_prompt,
|
||||
"action_prompt_display": action_prompt_display,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,11 +34,10 @@ class BaseAction(ABC):
|
|||
def __init__(
|
||||
self,
|
||||
action_data: dict,
|
||||
reasoning: str,
|
||||
action_reasoning: str,
|
||||
cycle_timers: dict,
|
||||
thinking_id: str,
|
||||
chat_stream: ChatStream,
|
||||
log_prefix: str = "",
|
||||
plugin_config: Optional[dict] = None,
|
||||
action_message: Optional["DatabaseMessages"] = None,
|
||||
**kwargs,
|
||||
|
|
@ -60,10 +59,11 @@ class BaseAction(ABC):
|
|||
if plugin_config is None:
|
||||
plugin_config = {}
|
||||
self.action_data = action_data
|
||||
self.reasoning = reasoning
|
||||
self.reasoning = ""
|
||||
self.cycle_timers = cycle_timers
|
||||
self.thinking_id = thinking_id
|
||||
self.log_prefix = log_prefix
|
||||
|
||||
self.action_reasoning = action_reasoning
|
||||
|
||||
self.plugin_config = plugin_config or {}
|
||||
"""对应的插件配置"""
|
||||
|
|
@ -76,14 +76,6 @@ class BaseAction(ABC):
|
|||
self.action_parameters: dict = getattr(self.__class__, "action_parameters", {}).copy()
|
||||
self.action_require: list[str] = getattr(self.__class__, "action_require", []).copy()
|
||||
|
||||
# 设置激活类型实例属性(从类属性复制,提供默认值)
|
||||
self.focus_activation_type = getattr(
|
||||
self.__class__, "focus_activation_type", ActionActivationType.ALWAYS
|
||||
) # 已弃用
|
||||
"""FOCUS模式下的激活类型"""
|
||||
self.normal_activation_type = getattr(
|
||||
self.__class__, "normal_activation_type", ActionActivationType.ALWAYS
|
||||
) # 已弃用
|
||||
"""NORMAL模式下的激活类型"""
|
||||
self.activation_type = getattr(self.__class__, "activation_type", self.focus_activation_type)
|
||||
"""激活类型"""
|
||||
|
|
@ -115,44 +107,32 @@ class BaseAction(ABC):
|
|||
self.user_nickname = None
|
||||
self.is_group = False
|
||||
self.target_id = None
|
||||
self.has_action_message = False
|
||||
|
||||
if self.action_message:
|
||||
self.has_action_message = True
|
||||
|
||||
if self.action_name != "no_action":
|
||||
self.group_id = (
|
||||
str(self.action_message.chat_info.group_info.group_id)
|
||||
if self.action_message.chat_info.group_info
|
||||
else None
|
||||
)
|
||||
self.group_name = (
|
||||
self.action_message.chat_info.group_info.group_name
|
||||
if self.action_message.chat_info.group_info
|
||||
else None
|
||||
)
|
||||
self.group_id = (
|
||||
str(self.action_message.chat_info.group_info.group_id)
|
||||
if self.action_message.chat_info.group_info
|
||||
else None
|
||||
)
|
||||
self.group_name = (
|
||||
self.action_message.chat_info.group_info.group_name
|
||||
if self.action_message.chat_info.group_info
|
||||
else None
|
||||
)
|
||||
|
||||
self.user_id = str(self.action_message.user_info.user_id)
|
||||
self.user_nickname = self.action_message.user_info.user_nickname
|
||||
|
||||
if self.group_id:
|
||||
self.is_group = True
|
||||
self.target_id = self.group_id
|
||||
self.log_prefix = f"[{self.group_name}]"
|
||||
else:
|
||||
self.is_group = False
|
||||
self.target_id = self.user_id
|
||||
self.log_prefix = f"[{self.user_nickname} 的 私聊]"
|
||||
|
||||
self.user_id = str(self.action_message.user_info.user_id)
|
||||
self.user_nickname = self.action_message.user_info.user_nickname
|
||||
if self.group_id:
|
||||
self.is_group = True
|
||||
self.target_id = self.group_id
|
||||
else:
|
||||
self.is_group = False
|
||||
self.target_id = self.user_id
|
||||
else:
|
||||
if self.chat_stream.group_info:
|
||||
self.group_id = self.chat_stream.group_info.group_id
|
||||
self.group_name = self.chat_stream.group_info.group_name
|
||||
self.is_group = True
|
||||
self.target_id = self.group_id
|
||||
else:
|
||||
self.user_id = self.chat_stream.user_info.user_id
|
||||
self.user_nickname = self.chat_stream.user_info.user_nickname
|
||||
self.is_group = False
|
||||
self.target_id = self.user_id
|
||||
|
||||
logger.debug(f"{self.log_prefix} Action组件初始化完成")
|
||||
logger.debug(
|
||||
f"{self.log_prefix} 聊天信息: 类型={'群聊' if self.is_group else '私聊'}, 平台={self.platform}, 目标={self.target_id}"
|
||||
)
|
||||
|
|
@ -441,6 +421,7 @@ class BaseAction(ABC):
|
|||
thinking_id=self.thinking_id,
|
||||
action_data=self.action_data,
|
||||
action_name=self.action_name,
|
||||
action_reasoning=self.action_reasoning,
|
||||
)
|
||||
|
||||
async def wait_for_new_message(self, timeout: int = 1200) -> Tuple[bool, str]:
|
||||
|
|
@ -467,11 +448,6 @@ class BaseAction(ABC):
|
|||
|
||||
wait_start_time = asyncio.get_event_loop().time()
|
||||
while True:
|
||||
# 检查关闭标志
|
||||
# shutting_down = self.get_action_context("shutting_down", False)
|
||||
# if shutting_down:
|
||||
# logger.info(f"{self.log_prefix} 等待新消息时检测到关闭信号,中断等待")
|
||||
# return False, ""
|
||||
|
||||
# 检查新消息
|
||||
current_time = time.time()
|
||||
|
|
@ -530,8 +506,6 @@ class BaseAction(ABC):
|
|||
name=name,
|
||||
component_type=ComponentType.ACTION,
|
||||
description=getattr(cls, "action_description", "Action动作"),
|
||||
focus_activation_type=focus_activation_type,
|
||||
normal_activation_type=normal_activation_type,
|
||||
activation_type=activation_type,
|
||||
activation_keywords=getattr(cls, "activation_keywords", []).copy(),
|
||||
keyword_case_sensitive=getattr(cls, "keyword_case_sensitive", False),
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class CuriousAction(BaseAction):
|
|||
|
||||
# 动作参数定义
|
||||
action_parameters = {
|
||||
"question": "对存在疑问的信息提出一个问题,描述全面,使用无人称陈述句",
|
||||
"question": "对存在疑问的信息提出一个问题,描述全面",
|
||||
}
|
||||
|
||||
# 动作使用场景
|
||||
|
|
@ -64,7 +64,7 @@ class CuriousAction(BaseAction):
|
|||
logger.info(f"已存储问题到冲突追踪器: {question}")
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"你产生了一个问题:{question},尝试向其他人提问或回忆吧",
|
||||
action_prompt_display=f"你产生了一个问题:{question},尝试向其他人提问或回忆",
|
||||
action_done=True,
|
||||
)
|
||||
return True, "问题已记录"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
核心动作插件
|
||||
|
||||
将系统核心动作(reply、no_action、emoji)转换为新插件系统格式
|
||||
将系统核心动作(reply、no_reply、emoji)转换为新插件系统格式
|
||||
这是系统的内置插件,提供基础的聊天交互功能
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class MemoryBuildPlugin(BasePlugin):
|
|||
|
||||
# --- 根据配置注册组件 ---
|
||||
components = []
|
||||
components.append((GetMemoryAction.get_action_info(), GetMemoryAction))
|
||||
# components.append((GetMemoryAction.get_action_info(), GetMemoryAction))
|
||||
components.append((GetMemoryTool.get_tool_info(), GetMemoryTool))
|
||||
|
||||
return components
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[inner]
|
||||
version = "6.18.0"
|
||||
version = "6.18.1"
|
||||
|
||||
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
||||
#如果你想要修改配置文件,请递增version的值
|
||||
|
|
@ -28,7 +28,7 @@ reply_style = "请回复的平淡一些,简短一些,说中文,不要刻
|
|||
interest = "对技术相关话题,游戏和动漫相关话题感兴趣,也对日常话题感兴趣,不喜欢太过沉重严肃的话题"
|
||||
|
||||
# 麦麦的说话规则,行为风格:
|
||||
plan_style = """请你根据聊天内容,用户的最新消息和以下标准选择合适的动作:
|
||||
plan_style = """
|
||||
1.思考**所有**的可用的action中的**每个动作**是否符合当下条件,如果动作使用条件符合聊天内容就使用
|
||||
2.如果相同的内容已经被执行,请不要重复执行
|
||||
3.请控制你的发言频率,不要太过频繁的发言
|
||||
|
|
@ -40,7 +40,7 @@ visual_style = "请用中文描述这张图片的内容。如果有文字,请
|
|||
|
||||
|
||||
# 麦麦私聊的说话规则,行为风格:
|
||||
private_plan_style = """请你根据聊天内容,用户的最新消息和以下标准选择合适的动作:
|
||||
private_plan_style = """
|
||||
1.思考**所有**的可用的action中的**每个动作**是否符合当下条件,如果动作使用条件符合聊天内容就使用
|
||||
2.如果相同的内容已经被执行,请不要重复执行
|
||||
3.某句话如果已经被回复过,不要重复回复"""
|
||||
|
|
|
|||
Loading…
Reference in New Issue