diff --git a/scripts/070configexe.py b/scripts/070configexe.py new file mode 100644 index 00000000..88871aaf --- /dev/null +++ b/scripts/070configexe.py @@ -0,0 +1,660 @@ +import tkinter as tk +from tkinter import ttk, messagebox +import tomli +import tomli_w +import os +from typing import Any, Dict, List +import threading +import time + + +class ConfigEditor: + def __init__(self, root): + self.root = root + self.root.title("麦麦配置编辑器") + + # 加载编辑器配置 + self.load_editor_config() + + # 设置窗口大小 + self.root.geometry(f"{self.window_width}x{self.window_height}") + + # 加载配置 + self.load_config() + + # 自动保存相关 + self.last_save_time = time.time() + self.save_timer = None + self.save_lock = threading.Lock() + self.current_section = None # 当前编辑的节 + self.pending_save = False # 是否有待保存的更改 + + # 存储控件的字典 + self.widgets = {} + + # 创建主框架 + self.main_frame = ttk.Frame(self.root, padding="10") + self.main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # 创建版本号显示 + self.create_version_label() + + # 创建左侧导航栏 + self.create_navbar() + + # 创建右侧编辑区 + self.create_editor() + + # 创建底部按钮 + self.create_buttons() + + # 配置网格权重 + self.root.columnconfigure(0, weight=1) + self.root.rowconfigure(0, weight=1) + self.main_frame.columnconfigure(1, weight=1) + self.main_frame.rowconfigure(1, weight=1) # 修改为1,因为第0行是版本号 + + def load_editor_config(self): + """加载编辑器配置""" + try: + editor_config_path = os.path.join(os.path.dirname(__file__), "configexe.toml") + with open(editor_config_path, "rb") as f: + self.editor_config = tomli.load(f) # 保存整个配置对象 + + # 设置配置路径 + self.config_path = self.editor_config["config"]["bot_config_path"] + # 如果路径是相对路径,转换为绝对路径 + if not os.path.isabs(self.config_path): + self.config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), self.config_path) + + # 设置编辑器参数 + self.window_width = self.editor_config["editor"]["window_width"] + self.window_height = self.editor_config["editor"]["window_height"] + self.save_delay = self.editor_config["editor"]["save_delay"] + + # 加载翻译 + self.translations = self.editor_config.get("translations", {}) + + except Exception as e: + messagebox.showerror("错误", f"加载编辑器配置失败: {str(e)}") + # 使用默认值 + self.editor_config = {} # 初始化空配置 + self.config_path = "config/bot_config.toml" + self.window_width = 1000 + self.window_height = 800 + self.save_delay = 1.0 + self.translations = {} + + def load_config(self): + try: + with open(self.config_path, "rb") as f: + self.config = tomli.load(f) + except Exception as e: + messagebox.showerror("错误", f"加载配置文件失败: {str(e)}") + self.config = {} + + def create_version_label(self): + """创建版本号显示标签""" + version = self.config.get("inner", {}).get("version", "未知版本") + version_frame = ttk.Frame(self.main_frame) + version_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10)) + + version_label = ttk.Label(version_frame, text=f"麦麦版本:{version}", font=("", 10, "bold")) + version_label.pack(side=tk.LEFT, padx=5) + + def create_navbar(self): + # 创建左侧导航栏 + self.nav_frame = ttk.Frame(self.main_frame, padding="5") + self.nav_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # 创建导航树 + self.tree = ttk.Treeview(self.nav_frame) + self.tree.pack(fill=tk.BOTH, expand=True) + + # 添加快捷设置节 + self.tree.insert("", "end", text="快捷设置", values=("quick_settings",)) + + # 添加配置项到树 + for section in self.config: + if section != "inner": # 跳过inner部分 + # 获取节的中文名称 + section_trans = self.translations.get("sections", {}).get(section, {}) + section_name = section_trans.get("name", section) + self.tree.insert("", "end", text=section_name, values=(section,)) + + # 绑定选择事件 + self.tree.bind("<>", self.on_section_select) + + def create_editor(self): + # 创建右侧编辑区 + self.editor_frame = ttk.Frame(self.main_frame, padding="5") + self.editor_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # 创建编辑区标题 + self.editor_title = ttk.Label(self.editor_frame, text="") + self.editor_title.pack(fill=tk.X) + + # 创建编辑区内容 + self.editor_content = ttk.Frame(self.editor_frame) + self.editor_content.pack(fill=tk.BOTH, expand=True) + + # 创建滚动条 + self.scrollbar = ttk.Scrollbar(self.editor_content) + self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + # 创建画布和框架 + self.canvas = tk.Canvas(self.editor_content, yscrollcommand=self.scrollbar.set) + self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + self.scrollbar.config(command=self.canvas.yview) + + # 创建内容框架 + self.content_frame = ttk.Frame(self.canvas) + self.canvas.create_window((0, 0), window=self.content_frame, anchor=tk.NW) + + # 绑定画布大小变化事件 + self.content_frame.bind("", self.on_frame_configure) + self.canvas.bind("", self.on_canvas_configure) + + def on_frame_configure(self, event=None): + self.canvas.configure(scrollregion=self.canvas.bbox("all")) + + def on_canvas_configure(self, event): + # 更新内容框架的宽度以适应画布 + self.canvas.itemconfig(self.canvas.find_withtag("all")[0], width=event.width) + + def create_buttons(self): + # 创建底部按钮区 + self.button_frame = ttk.Frame(self.main_frame, padding="5") + self.button_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E)) + + # 刷新按钮 + self.refresh_button = ttk.Button(self.button_frame, text="刷新", command=self.refresh_config) + self.refresh_button.pack(side=tk.RIGHT, padx=5) + + def create_widget_for_value(self, parent: ttk.Frame, key: str, value: Any, path: List[str]) -> None: + """为不同类型的值创建对应的编辑控件""" + frame = ttk.Frame(parent) + frame.pack(fill=tk.X, padx=5, pady=2) + + # --- 修改开始: 改进翻译查找逻辑 --- + full_config_path_key = ".".join(path + [key]) # 例如 "chinese_typo.enable" + + item_name_to_display = key # 默认显示原始键名 + item_desc_to_display = "" # 默认无描述 + + # 1. 尝试使用完整路径的特定翻译 + specific_translation = self.translations.get("items", {}).get(full_config_path_key) + if specific_translation and specific_translation.get("name"): + item_name_to_display = specific_translation.get("name") + item_desc_to_display = specific_translation.get("description", "") + else: + # 2. 如果特定翻译未找到或没有name,尝试使用通用键名的翻译 + generic_translation = self.translations.get("items", {}).get(key) + if generic_translation and generic_translation.get("name"): + item_name_to_display = generic_translation.get("name") + item_desc_to_display = generic_translation.get("description", "") + # --- 修改结束 --- + + # 配置名(大号字体) + label = ttk.Label(frame, text=item_name_to_display, font=("", 20, "bold")) + label.grid(row=0, column=0, sticky=tk.W, padx=5, pady=(0, 0)) + + # 星星图标快捷设置(与配置名同一行) + content_col_offset_for_star = 1 # 星标按钮占一列 + quick_settings = self.editor_config.get("editor", {}).get("quick_settings", {}).get("items", []) + already_in_quick = any(item.get("path") == full_config_path_key for item in quick_settings) + icon = "★" if already_in_quick else "☆" + icon_fg = "#FFD600" # 始终金色 + + def on_star_click(): + self.toggle_quick_setting( + full_config_path_key, widget_type, item_name_to_display, item_desc_to_display, already_in_quick + ) + # 立即刷新本分组 + for widget in parent.winfo_children(): + widget.destroy() + self.widgets.clear() + # 重新渲染本分组 + if hasattr(self, "current_section") and self.current_section and self.current_section != "quick_settings": + self.create_section_widgets( + parent, self.current_section, self.config[self.current_section], [self.current_section] + ) + elif hasattr(self, "current_section") and self.current_section == "quick_settings": + self.create_quick_settings_widgets() # 如果当前是快捷设置,也刷新它 + + pin_btn = ttk.Button(frame, text=icon, width=2, command=on_star_click) + pin_btn.grid(row=0, column=content_col_offset_for_star, sticky=tk.W, padx=5) + try: + pin_btn.configure(style="Pin.TButton") + style = ttk.Style() + style.configure("Pin.TButton", foreground=icon_fg) + except Exception: + pass + + # 配置项描述(第二行) + desc_row = 1 + if item_desc_to_display: + desc_label = ttk.Label(frame, text=item_desc_to_display, foreground="gray", font=("", 16)) + desc_label.grid( + row=desc_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5, pady=(0, 4) + ) + widget_row = desc_row + 1 # 内容控件在描述下方 + else: + widget_row = desc_row # 内容控件直接在第二行 + + # 配置内容控件(第三行或第二行) + if path[0] == "inner": + value_label = ttk.Label(frame, text=str(value), font=("", 20)) + value_label.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5) + return + + if isinstance(value, bool): + # 布尔值使用复选框 + var = tk.BooleanVar(value=value) + checkbox = ttk.Checkbutton(frame, variable=var, command=lambda: self.on_value_changed()) + checkbox.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5) + self.widgets[tuple(path + [key])] = var + widget_type = "bool" + + elif isinstance(value, (int, float)): + # 数字使用数字输入框 + var = tk.StringVar(value=str(value)) + entry = ttk.Entry(frame, textvariable=var, font=("", 20)) + entry.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5) + var.trace_add("write", lambda *args: self.on_value_changed()) + self.widgets[tuple(path + [key])] = var + widget_type = "number" + + elif isinstance(value, list): + # 列表使用每行一个输入框的形式 + frame_list = ttk.Frame(frame) + frame_list.grid( + row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5 + ) + + # 创建添加和删除按钮 + button_frame = ttk.Frame(frame_list) + button_frame.pack(side=tk.RIGHT, padx=5) + + add_button = ttk.Button( + button_frame, text="+", width=3, command=lambda p=path + [key]: self.add_list_item(frame_list, p) + ) + add_button.pack(side=tk.TOP, pady=2) + + # 创建列表项框架 + items_frame = ttk.Frame(frame_list) + items_frame.pack(side=tk.LEFT, fill=tk.X, expand=True) + + # 存储所有输入框的变量 + entry_vars = [] + + # 为每个列表项创建输入框 + for i, item in enumerate(value): + self.create_list_item(items_frame, item, i, entry_vars, path + [key]) + + # 存储控件引用 + self.widgets[tuple(path + [key])] = (items_frame, entry_vars) + widget_type = "list" + + else: + # 其他类型(字符串等)使用普通文本框 + var = tk.StringVar(value=str(value)) + entry = ttk.Entry(frame, textvariable=var, font=("", 20)) + entry.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5) + var.trace_add("write", lambda *args: self.on_value_changed()) + self.widgets[tuple(path + [key])] = var + widget_type = "text" + + def create_section_widgets(self, parent: ttk.Frame, section: str, data: Dict, path=None) -> None: + """为配置节创建编辑控件""" + if path is None: + path = [section] + # 获取节的中文名称和描述 + section_trans = self.translations.get("sections", {}).get(section, {}) + section_name = section_trans.get("name", section) + section_desc = section_trans.get("description", "") + + # 创建节的标签框架 + section_frame = ttk.Frame(parent) + section_frame.pack(fill=tk.X, padx=5, pady=10) + + # 创建节的名称标签 + section_label = ttk.Label(section_frame, text=f"[{section_name}]", font=("", 12, "bold")) + section_label.pack(side=tk.LEFT, padx=5) + + # 创建节的描述标签 + if section_desc: + desc_label = ttk.Label(section_frame, text=f"({section_desc})", foreground="gray") + desc_label.pack(side=tk.LEFT, padx=5) + + # 为每个配置项创建对应的控件 + for key, value in data.items(): + if isinstance(value, dict): + self.create_section_widgets(parent, key, value, path + [key]) + else: + self.create_widget_for_value(parent, key, value, path) + + def on_value_changed(self): + """当值改变时触发自动保存""" + self.pending_save = True + current_time = time.time() + if current_time - self.last_save_time > self.save_delay: + if self.save_timer: + self.root.after_cancel(self.save_timer) + self.save_timer = self.root.after(int(self.save_delay * 1000), self.save_config) + + def on_section_select(self, event): + # 如果有待保存的更改,先保存 + if self.pending_save: + self.save_config() + + selection = self.tree.selection() + if not selection: + return + + section = self.tree.item(selection[0])["values"][0] # 使用values中的原始节名 + self.current_section = section + + # 获取节的中文名称 + if section == "quick_settings": + section_name = "快捷设置" + else: + section_trans = self.translations.get("sections", {}).get(section, {}) + section_name = section_trans.get("name", section) + self.editor_title.config(text=f"编辑 {section_name}") + + # 清空编辑器 + for widget in self.content_frame.winfo_children(): + widget.destroy() + + # 清空控件字典 + self.widgets.clear() + + # 创建编辑控件 + if section == "quick_settings": + self.create_quick_settings_widgets() + elif section in self.config: + self.create_section_widgets(self.content_frame, section, self.config[section]) + + def create_quick_settings_widgets(self): + """创建快捷设置编辑界面""" + # 获取快捷设置配置 + quick_settings = self.editor_config.get("editor", {}).get("quick_settings", {}).get("items", []) + + # 创建快捷设置控件 + for setting in quick_settings: + frame = ttk.Frame(self.content_frame) + frame.pack(fill=tk.X, padx=5, pady=2) + + # 获取当前值 + path = setting["path"].split(".") + current = self.config + for key in path[:-1]: # 除了最后一个键 + current = current.get(key, {}) + value = current.get(path[-1]) # 获取最后一个键的值 + + # 创建名称标签 + name_label = ttk.Label(frame, text=setting["name"], font=("", 18)) + name_label.pack(fill=tk.X, padx=5, pady=(2, 0)) + + # 创建描述标签 + if setting.get("description"): + desc_label = ttk.Label(frame, text=setting["description"], foreground="gray", font=("", 16)) + desc_label.pack(fill=tk.X, padx=5, pady=(0, 2)) + + # 根据类型创建不同的控件 + setting_type = setting.get("type", "bool") + + if setting_type == "bool": + value = bool(value) if value is not None else False + var = tk.BooleanVar(value=value) + checkbox = ttk.Checkbutton( + frame, text="", variable=var, command=lambda p=path, v=var: self.on_quick_setting_changed(p, v) + ) + checkbox.pack(anchor=tk.W, padx=5, pady=(0, 5)) + + elif setting_type == "text": + value = str(value) if value is not None else "" + var = tk.StringVar(value=value) + entry = ttk.Entry(frame, textvariable=var, width=40, font=("", 18)) + entry.pack(fill=tk.X, padx=5, pady=(0, 5)) + var.trace_add("write", lambda *args, p=path, v=var: self.on_quick_setting_changed(p, v)) + + elif setting_type == "number": + value = str(value) if value is not None else "0" + var = tk.StringVar(value=value) + entry = ttk.Entry(frame, textvariable=var, width=10, font=("", 18)) + entry.pack(fill=tk.X, padx=5, pady=(0, 5)) + var.trace_add("write", lambda *args, p=path, v=var: self.on_quick_setting_changed(p, v)) + + elif setting_type == "list": + # 对于列表类型,创建一个按钮来打开编辑窗口 + button = ttk.Button( + frame, text="编辑列表", command=lambda p=path, s=setting: self.open_list_editor(p, s) + ) + button.pack(anchor=tk.W, padx=5, pady=(0, 5)) + + def create_list_item(self, parent, value, index, entry_vars, path): + """创建单个列表项的输入框""" + item_frame = ttk.Frame(parent) + item_frame.pack(fill=tk.X, pady=1) + + # 创建输入框 + var = tk.StringVar(value=str(value)) + entry = ttk.Entry(item_frame, textvariable=var) + entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) + var.trace_add("write", lambda *args: self.on_value_changed()) + + # 创建删除按钮 + del_button = ttk.Button( + item_frame, + text="-", + width=3, + command=lambda: self.remove_list_item(parent, item_frame, entry_vars, index, path), + ) + del_button.pack(side=tk.RIGHT, padx=5) + + # 存储变量引用 + entry_vars.append(var) + + def add_list_item(self, parent, path): + """添加新的列表项""" + items_frame = parent.winfo_children()[1] # 获取列表项框架 + entry_vars = self.widgets[tuple(path)][1] # 获取变量列表 + + # 创建新的列表项 + self.create_list_item(items_frame, "", len(entry_vars), entry_vars, path) + self.on_value_changed() + + def remove_list_item(self, parent, item_frame, entry_vars, index, path): + """删除列表项""" + item_frame.destroy() + entry_vars.pop(index) + self.on_value_changed() + + def get_widget_value(self, widget) -> Any: + """获取控件的值""" + if isinstance(widget, tk.BooleanVar): + return widget.get() + elif isinstance(widget, tk.StringVar): + value = widget.get() + try: + # 尝试转换为数字 + if "." in value: + return float(value) + return int(value) + except ValueError: + return value + elif isinstance(widget, tuple): # 列表类型 + items_frame, entry_vars = widget + # 获取所有非空输入框的值 + return [var.get() for var in entry_vars if var.get().strip()] + return None + + def save_config(self): + """保存配置到文件""" + if not self.pending_save: + return + + with self.save_lock: + try: + # 获取所有控件的值 + for path, widget in self.widgets.items(): + value = self.get_widget_value(widget) + # 更新配置 + current = self.config + for key in path[:-1]: + current = current[key] + final_key = path[-1] # 直接用最后一个key + current[final_key] = value + + # 保存到文件 + with open(self.config_path, "wb") as f: + tomli_w.dump(self.config, f) + + self.last_save_time = time.time() + self.pending_save = False + self.editor_title.config(text=f"{self.editor_title.cget('text')} (已保存)") + self.root.after( + 2000, lambda: self.editor_title.config(text=self.editor_title.cget("text").replace(" (已保存)", "")) + ) + except Exception as e: + messagebox.showerror("错误", f"保存配置失败: {str(e)}") + + def refresh_config(self): + # 如果有待保存的更改,先保存 + if self.pending_save: + self.save_config() + + self.load_config() + self.tree.delete(*self.tree.get_children()) + for section in self.config: + # 获取节的中文名称 + section_trans = self.translations.get("sections", {}).get(section, {}) + section_name = section_trans.get("name", section) + self.tree.insert("", "end", text=section_name, values=(section,)) + messagebox.showinfo("成功", "配置已刷新") + + def open_list_editor(self, path, setting): + """打开列表编辑窗口""" + # 创建新窗口 + dialog = tk.Toplevel(self.root) + dialog.title(f"编辑 {setting['name']}") + dialog.geometry("400x300") + + # 获取当前值 + current = self.config + for key in path[:-1]: + current = current.get(key, {}) + value = current.get(path[-1], []) + + # 创建编辑区 + frame = ttk.Frame(dialog, padding="10") + frame.pack(fill=tk.BOTH, expand=True) + + # 创建列表项框架 + items_frame = ttk.Frame(frame) + items_frame.pack(fill=tk.BOTH, expand=True) + + # 存储所有输入框的变量 + entry_vars = [] + + # 为每个列表项创建输入框 + for i, item in enumerate(value): + self.create_list_item(items_frame, item, i, entry_vars, path) + + # 创建按钮框架 + button_frame = ttk.Frame(frame) + button_frame.pack(fill=tk.X, pady=10) + + # 添加按钮 + add_button = ttk.Button(button_frame, text="添加", command=lambda: self.add_list_item(items_frame, path)) + add_button.pack(side=tk.LEFT, padx=5) + + # 保存按钮 + save_button = ttk.Button( + button_frame, text="保存", command=lambda: self.save_list_editor(dialog, path, entry_vars) + ) + save_button.pack(side=tk.RIGHT, padx=5) + + def save_list_editor(self, dialog, path, entry_vars): + """保存列表编辑窗口的内容""" + # 获取所有非空输入框的值 + values = [var.get() for var in entry_vars if var.get().strip()] + + # 更新配置 + current = self.config + for key in path[:-1]: + if key not in current: + current[key] = {} + current = current[key] + current[path[-1]] = values + + # 触发保存 + self.on_value_changed() + + # 关闭窗口 + dialog.destroy() + + def on_quick_setting_changed(self, path, var): + """快捷设置值改变时的处理""" + # 更新配置 + current = self.config + for key in path[:-1]: + if key not in current: + current[key] = {} + current = current[key] + # 根据变量类型设置值 + if isinstance(var, tk.BooleanVar): + current[path[-1]] = var.get() + elif isinstance(var, tk.StringVar): + value = var.get() + try: + # 尝试转换为数字 + if "." in value: + current[path[-1]] = float(value) + else: + current[path[-1]] = int(value) + except ValueError: + current[path[-1]] = value + # 触发保存 + self.on_value_changed() + + def toggle_quick_setting(self, full_path, widget_type, name, desc, already_in_quick): + quick_settings = ( + self.editor_config.setdefault("editor", {}).setdefault("quick_settings", {}).setdefault("items", []) + ) + if already_in_quick: + # 移除 + self.editor_config["editor"]["quick_settings"]["items"] = [ + item for item in quick_settings if item.get("path") != full_path + ] + else: + # 添加 + quick_settings.append({"name": name, "description": desc, "path": full_path, "type": widget_type}) + # 保存到configexe.toml + import tomli_w + import os + + config_path = os.path.join(os.path.dirname(__file__), "configexe.toml") + with open(config_path, "wb") as f: + tomli_w.dump(self.editor_config, f) + self.refresh_quick_settings() + + def refresh_quick_settings(self): + # 重新渲染快捷设置栏(如果当前在快捷设置页) + if self.current_section == "quick_settings": + for widget in self.content_frame.winfo_children(): + widget.destroy() + self.widgets.clear() + self.create_quick_settings_widgets() + + +def main(): + root = tk.Tk() + _app = ConfigEditor(root) + root.mainloop() + + +if __name__ == "__main__": + main() diff --git a/scripts/configexe.toml b/scripts/configexe.toml new file mode 100644 index 00000000..de966519 --- /dev/null +++ b/scripts/configexe.toml @@ -0,0 +1,497 @@ +[config] +bot_config_path = "config/bot_config.toml" + +[editor] +window_width = 1000 +window_height = 800 +save_delay = 1.0 + +[[editor.quick_settings.items]] +name = "性格细节" +description = "麦麦性格的细节描述,条数任意,不能为0" +path = "personality.personality_sides" +type = "list" + +[[editor.quick_settings.items]] +name = "身份细节" +description = "麦麦的身份特征描述,可以描述外貌、性别、身高、职业、属性等" +path = "identity.identity_detail" +type = "list" + +[[editor.quick_settings.items]] +name = "表达风格" +description = "麦麦说话的表达风格,表达习惯" +path = "expression.expression_style" +type = "text" + +[[editor.quick_settings.items]] +name = "聊天模式" +description = "麦麦的聊天模式:normal(普通模式)、focus(专注模式)、auto(自动模式)" +path = "chat.chat_mode" +type = "text" + +[[editor.quick_settings.items]] +name = "退出专注阈值" +description = "自动退出专注聊天的阈值,越低越容易退出专注聊天" +path = "chat.exit_focus_threshold" +type = "number" + +[[editor.quick_settings.items]] +name = "偷取表情包" +description = "是否偷取表情包,让麦麦可以发送她保存的这些表情包" +path = "emoji.steal_emoji" +type = "bool" + +[[editor.quick_settings.items]] +name = "核心性格" +description = "麦麦的核心性格描述,建议50字以内" +path = "personality.personality_core" +type = "text" + +[[editor.quick_settings.items]] +name = "自动专注阈值" +description = "自动切换到专注聊天的阈值,越低越容易进入专注聊天" +path = "chat.auto_focus_threshold" +type = "number" + +[[editor.quick_settings.items]] +name = "自我识别处理器" +description = "是否启用自我识别处理器" +path = "focus_chat_processor.self_identify_processor" +type = "bool" + +[[editor.quick_settings.items]] +name = "工具使用处理器" +description = "是否启用工具使用处理器" +path = "focus_chat_processor.tool_use_processor" +type = "bool" + +[[editor.quick_settings.items]] +name = "工作记忆处理器" +description = "是否启用工作记忆处理器,不稳定,消耗量大" +path = "focus_chat_processor.working_memory_processor" +type = "bool" + +[translations.sections.inner] +name = "版本" +description = "麦麦的内部配置,包含版本号等信息。此部分仅供显示,不可编辑。" + +[translations.sections.bot] +name = "麦麦bot配置" +description = "麦麦的基本配置,包括QQ号、昵称和别名等基础信息" + +[translations.sections.personality] +name = "人格" +description = "麦麦的性格设定,包括核心性格(建议50字以内)和细节描述" + +[translations.sections.identity] +name = "身份特点" +description = "麦麦的身份特征,包括年龄、性别、外貌等描述,可以描述外貌、性别、身高、职业、属性等" + +[translations.sections.expression] +name = "表达方式" +description = "麦麦的表达方式和学习设置,包括表达风格和表达学习功能" + +[translations.sections.relationship] +name = "关系" +description = "麦麦与用户的关系设置,包括取名功能等" + +[translations.sections.chat] +name = "聊天模式" +description = "麦麦的聊天模式和行为设置,包括普通模式、专注模式和自动模式" + +[translations.sections.message_receive] +name = "消息接收" +description = "消息过滤和接收设置,可以根据规则过滤特定消息" + +[translations.sections.normal_chat] +name = "普通聊天配置" +description = "普通聊天模式下的行为设置,包括回复概率、上下文长度、表情包使用等" + +[translations.sections.focus_chat] +name = "专注聊天配置" +description = "专注聊天模式下的行为设置,包括思考间隔、上下文大小等" + +[translations.sections.focus_chat_processor] +name = "专注聊天处理器" +description = "专注聊天模式下的处理器设置,包括自我识别、工具使用、工作记忆等功能" + +[translations.sections.emoji] +name = "表情包" +description = "表情包相关的设置,包括最大注册数量、替换策略、检查间隔等" + +[translations.sections.memory] +name = "记忆" +description = "麦麦的记忆系统设置,包括记忆构建、遗忘、整合等参数" + +[translations.sections.mood] +name = "情绪" +description = "麦麦的情绪系统设置,仅在普通聊天模式下有效" + +[translations.sections.keyword_reaction] +name = "关键词反应" +description = "针对特定关键词作出反应的设置,仅在普通聊天模式下有效" + +[translations.sections.chinese_typo] +name = "错别字生成器" +description = "中文错别字生成器的设置,可以控制错别字生成的概率" + +[translations.sections.response_splitter] +name = "回复分割器" +description = "回复分割器的设置,用于控制回复的长度和句子数量" + +[translations.sections.model] +name = "模型" +description = "各种AI模型的设置,包括组件模型、普通聊天模型、专注聊天模型等" + +[translations.sections.maim_message] +name = "消息服务" +description = "消息服务的设置,包括认证令牌、服务器配置等" + +[translations.sections.telemetry] +name = "遥测" +description = "统计信息发送设置,用于统计全球麦麦的数量" + +[translations.sections.experimental] +name = "实验功能" +description = "实验性功能的设置,包括调试显示、好友聊天等功能" + +[translations.items.version] +name = "版本号" +description = "麦麦的版本号,格式:主版本号.次版本号.修订号。主版本号用于不兼容的API修改,次版本号用于向下兼容的功能性新增,修订号用于向下兼容的问题修正" + +[translations.items.qq_account] +name = "QQ账号" +description = "麦麦的QQ账号" + +[translations.items.nickname] +name = "昵称" +description = "麦麦的昵称" + +[translations.items.alias_names] +name = "别名" +description = "麦麦的其他称呼" + +[translations.items.personality_core] +name = "核心性格" +description = "麦麦的核心性格描述,建议50字以内" + +[translations.items.personality_sides] +name = "性格细节" +description = "麦麦性格的细节描述,条数任意,不能为0" + +[translations.items.identity_detail] +name = "身份细节" +description = "麦麦的身份特征描述,可以描述外貌、性别、身高、职业、属性等,条数任意,不能为0" + +[translations.items.expression_style] +name = "表达风格" +description = "麦麦说话的表达风格,表达习惯" + +[translations.items.enable_expression_learning] +name = "启用表达学习" +description = "是否启用表达学习功能,麦麦会学习人类说话风格" + +[translations.items.learning_interval] +name = "学习间隔" +description = "表达学习的间隔时间(秒)" + +[translations.items.give_name] +name = "取名功能" +description = "麦麦是否给其他人取名,关闭后无法使用禁言功能" + +[translations.items.chat_mode] +name = "聊天模式" +description = "麦麦的聊天模式:normal(普通模式,token消耗较低)、focus(专注模式,token消耗较高)、auto(自动模式,根据消息内容自动切换)" + +[translations.items.auto_focus_threshold] +name = "自动专注阈值" +description = "自动切换到专注聊天的阈值,越低越容易进入专注聊天" + +[translations.items.exit_focus_threshold] +name = "退出专注阈值" +description = "自动退出专注聊天的阈值,越低越容易退出专注聊天" + +[translations.items.ban_words] +name = "禁用词" +description = "需要过滤的词语列表" + +[translations.items.ban_msgs_regex] +name = "禁用消息正则" +description = "需要过滤的消息正则表达式,匹配到的消息将被过滤" + +[translations.items.normal_chat_first_probability] +name = "首要模型概率" +description = "麦麦回答时选择首要模型的概率(与之相对的,次要模型的概率为1 - normal_chat_first_probability)" + +[translations.items.max_context_size] +name = "最大上下文长度" +description = "聊天上下文的最大长度" + +[translations.items.emoji_chance] +name = "表情包概率" +description = "麦麦一般回复时使用表情包的概率,设置为1让麦麦自己决定发不发" + +[translations.items.thinking_timeout] +name = "思考超时" +description = "麦麦最长思考时间,超过这个时间的思考会放弃(往往是api反应太慢)" + +[translations.items.willing_mode] +name = "回复意愿模式" +description = "回复意愿的计算模式:经典模式(classical)、mxp模式(mxp)、自定义模式(custom)" + +[translations.items.talk_frequency] +name = "回复频率" +description = "麦麦回复频率,一般为1,默认频率下,30分钟麦麦回复30条(约数)" + +[translations.items.response_willing_amplifier] +name = "回复意愿放大系数" +description = "麦麦回复意愿放大系数,一般为1" + +[translations.items.response_interested_rate_amplifier] +name = "兴趣度放大系数" +description = "麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数" + +[translations.items.emoji_response_penalty] +name = "表情包回复惩罚" +description = "表情包回复惩罚系数,设为0为不回复单个表情包,减少单独回复表情包的概率" + +[translations.items.mentioned_bot_inevitable_reply] +name = "提及必回" +description = "被提及时是否必然回复" + +[translations.items.at_bot_inevitable_reply] +name = "@必回" +description = "被@时是否必然回复" + +[translations.items.down_frequency_rate] +name = "降低频率系数" +description = "降低回复频率的群组回复意愿降低系数(除法)" + +[translations.items.talk_frequency_down_groups] +name = "降低频率群组" +description = "需要降低回复频率的群组列表" + +[translations.items.think_interval] +name = "思考间隔" +description = "思考的时间间隔(秒),可以有效减少消耗" + +[translations.items.observation_context_size] +name = "观察上下文大小" +description = "观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖" + +[translations.items.compressed_length] +name = "压缩长度" +description = "不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度会压缩,最短压缩长度为5" + +[translations.items.compress_length_limit] +name = "压缩限制" +description = "最多压缩份数,超过该数值的压缩上下文会被删除" + +[translations.items.self_identify_processor] +name = "自我识别处理器" +description = "是否启用自我识别处理器" + +[translations.items.tool_use_processor] +name = "工具使用处理器" +description = "是否启用工具使用处理器" + +[translations.items.working_memory_processor] +name = "工作记忆处理器" +description = "是否启用工作记忆处理器,不稳定,消耗量大" + +[translations.items.max_reg_num] +name = "最大注册数" +description = "表情包最大注册数量" + +[translations.items.do_replace] +name = "启用替换" +description = "开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包" + +[translations.items.check_interval] +name = "检查间隔" +description = "检查表情包(注册,破损,删除)的时间间隔(分钟)" + +[translations.items.save_pic] +name = "保存图片" +description = "是否保存表情包图片" + +[translations.items.cache_emoji] +name = "缓存表情包" +description = "是否缓存表情包" + +[translations.items.steal_emoji] +name = "偷取表情包" +description = "是否偷取表情包,让麦麦可以发送她保存的这些表情包" + +[translations.items.content_filtration] +name = "内容过滤" +description = "是否启用表情包过滤,只有符合该要求的表情包才会被保存" + +[translations.items.filtration_prompt] +name = "过滤要求" +description = "表情包过滤要求,只有符合该要求的表情包才会被保存" + +[translations.items.memory_build_interval] +name = "记忆构建间隔" +description = "记忆构建间隔(秒),间隔越低,麦麦学习越多,但是冗余信息也会增多" + +[translations.items.memory_build_distribution] +name = "记忆构建分布" +description = "记忆构建分布,参数:分布1均值,标准差,权重,分布2均值,标准差,权重" + +[translations.items.memory_build_sample_num] +name = "采样数量" +description = "采样数量,数值越高记忆采样次数越多" + +[translations.items.memory_build_sample_length] +name = "采样长度" +description = "采样长度,数值越高一段记忆内容越丰富" + +[translations.items.memory_compress_rate] +name = "记忆压缩率" +description = "记忆压缩率,控制记忆精简程度,建议保持默认,调高可以获得更多信息,但是冗余信息也会增多" + +[translations.items.forget_memory_interval] +name = "记忆遗忘间隔" +description = "记忆遗忘间隔(秒),间隔越低,麦麦遗忘越频繁,记忆更精简,但更难学习" + +[translations.items.memory_forget_time] +name = "遗忘时间" +description = "多长时间后的记忆会被遗忘(小时)" + +[translations.items.memory_forget_percentage] +name = "遗忘比例" +description = "记忆遗忘比例,控制记忆遗忘程度,越大遗忘越多,建议保持默认" + +[translations.items.consolidate_memory_interval] +name = "记忆整合间隔" +description = "记忆整合间隔(秒),间隔越低,麦麦整合越频繁,记忆更精简" + +[translations.items.consolidation_similarity_threshold] +name = "整合相似度阈值" +description = "相似度阈值" + +[translations.items.consolidation_check_percentage] +name = "整合检查比例" +description = "检查节点比例" + +[translations.items.memory_ban_words] +name = "记忆禁用词" +description = "不希望记忆的词,已经记忆的不会受到影响" + +[translations.items.mood_update_interval] +name = "情绪更新间隔" +description = "情绪更新间隔(秒),仅在普通聊天模式下有效" + +[translations.items.mood_decay_rate] +name = "情绪衰减率" +description = "情绪衰减率" + +[translations.items.mood_intensity_factor] +name = "情绪强度因子" +description = "情绪强度因子" + +[translations.items.enable] +name = "启用关键词反应" +description = "关键词反应功能的总开关,仅在普通聊天模式下有效" + +[translations.items.chinese_typo_enable] +name = "启用错别字" +description = "是否启用中文错别字生成器" + +[translations.items.error_rate] +name = "错误率" +description = "单字替换概率" + +[translations.items.min_freq] +name = "最小字频" +description = "最小字频阈值" + +[translations.items.tone_error_rate] +name = "声调错误率" +description = "声调错误概率" + +[translations.items.word_replace_rate] +name = "整词替换率" +description = "整词替换概率" + +[translations.items.splitter_enable] +name = "启用分割器" +description = "是否启用回复分割器" + +[translations.items.max_length] +name = "最大长度" +description = "回复允许的最大长度" + +[translations.items.max_sentence_num] +name = "最大句子数" +description = "回复允许的最大句子数" + +[translations.items.enable_kaomoji_protection] +name = "启用颜文字保护" +description = "是否启用颜文字保护" + +[translations.items.model_max_output_length] +name = "最大输出长度" +description = "模型单次返回的最大token数" + +[translations.items.auth_token] +name = "认证令牌" +description = "用于API验证的令牌列表,为空则不启用验证" + +[translations.items.use_custom] +name = "使用自定义" +description = "是否启用自定义的maim_message服务器,注意这需要设置新的端口,不能与.env重复" + +[translations.items.host] +name = "主机地址" +description = "服务器主机地址" + +[translations.items.port] +name = "端口" +description = "服务器端口" + +[translations.items.mode] +name = "模式" +description = "连接模式:ws或tcp" + +[translations.items.use_wss] +name = "使用WSS" +description = "是否使用WSS安全连接,只支持ws模式" + +[translations.items.cert_file] +name = "证书文件" +description = "SSL证书文件路径,仅在use_wss=true时有效" + +[translations.items.key_file] +name = "密钥文件" +description = "SSL密钥文件路径,仅在use_wss=true时有效" + +[translations.items.telemetry_enable] +name = "启用遥测" +description = "是否发送统计信息,主要是看全球有多少只麦麦" + +[translations.items.debug_show_chat_mode] +name = "显示聊天模式" +description = "是否在回复后显示当前聊天模式" + +[translations.items.enable_friend_chat] +name = "启用好友聊天" +description = "是否启用好友聊天功能" + +[translations.items.pfc_chatting] +name = "PFC聊天" +description = "暂时无效" + +[translations.items."response_splitter.enable"] +name = "启用分割器" +description = "是否启用回复分割器" + +[translations.items."chinese_typo.enable"] +name = "启用错别字" +description = "是否启用中文错别字生成器" + +[translations.items."keyword_reaction.enable"] +name = "启用关键词反应" +description = "关键词反应功能的总开关,仅在普通聊天模式下有效" diff --git a/src/chat/focus_chat/expressors/default_expressor.py b/src/chat/focus_chat/expressors/default_expressor.py index d44d5a6c..d8cc1064 100644 --- a/src/chat/focus_chat/expressors/default_expressor.py +++ b/src/chat/focus_chat/expressors/default_expressor.py @@ -79,7 +79,7 @@ class DefaultExpressor: model=global_config.model.focus_expressor, # temperature=global_config.model.focus_expressor["temp"], max_tokens=256, - request_type="focus_expressor", + request_type="focus.expressor", ) self.heart_fc_sender = HeartFCSender() diff --git a/src/chat/focus_chat/expressors/exprssion_learner.py b/src/chat/focus_chat/expressors/exprssion_learner.py index 96d4e231..817a188f 100644 --- a/src/chat/focus_chat/expressors/exprssion_learner.py +++ b/src/chat/focus_chat/expressors/exprssion_learner.py @@ -64,7 +64,7 @@ class ExpressionLearner: model=global_config.model.focus_expressor, temperature=0.1, max_tokens=256, - request_type="learn_expression", + request_type="expressor.learner", ) async def get_expression_by_chat_id(self, chat_id: str) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]: diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index abf89479..3835b2da 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -16,16 +16,19 @@ from src.chat.focus_chat.info.info_base import InfoBase from src.chat.focus_chat.info_processors.chattinginfo_processor import ChattingInfoProcessor from src.chat.focus_chat.info_processors.mind_processor import MindProcessor from src.chat.focus_chat.info_processors.working_memory_processor import WorkingMemoryProcessor -from src.chat.focus_chat.info_processors.action_processor import ActionProcessor + +# from src.chat.focus_chat.info_processors.action_processor import ActionProcessor from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation from src.chat.heart_flow.observation.working_observation import WorkingMemoryObservation from src.chat.heart_flow.observation.structure_observation import StructureObservation +from src.chat.heart_flow.observation.actions_observation import ActionObservation from src.chat.focus_chat.info_processors.tool_processor import ToolProcessor from src.chat.focus_chat.expressors.default_expressor import DefaultExpressor from src.chat.focus_chat.memory_activator import MemoryActivator from src.chat.focus_chat.info_processors.base_processor import BaseProcessor from src.chat.focus_chat.info_processors.self_processor import SelfProcessor from src.chat.focus_chat.planners.planner import ActionPlanner +from src.chat.focus_chat.planners.modify_actions import ActionModifier from src.chat.focus_chat.planners.action_manager import ActionManager from src.chat.focus_chat.working_memory.working_memory import WorkingMemory from src.config.config import global_config @@ -41,7 +44,7 @@ PROCESSOR_CLASSES = { "ToolProcessor": (ToolProcessor, "tool_use_processor"), "WorkingMemoryProcessor": (WorkingMemoryProcessor, "working_memory_processor"), "SelfProcessor": (SelfProcessor, "self_identify_processor"), - "ActionProcessor": (ActionProcessor, "action_processor"), # 这个处理器不需要配置键名,默认启用 + # "ActionProcessor": (ActionProcessor, "action_processor"), # 这个处理器不需要配置键名,默认启用 } @@ -129,8 +132,10 @@ class HeartFChatting: self.expressor = DefaultExpressor(chat_id=self.stream_id) self.action_manager = ActionManager() self.action_planner = ActionPlanner(log_prefix=self.log_prefix, action_manager=self.action_manager) + self.action_modifier = ActionModifier(action_manager=self.action_manager) + self.action_observation = ActionObservation(observe_id=self.stream_id) - self.hfcloop_observation.set_action_manager(self.action_manager) + self.action_observation.set_action_manager(self.action_manager) self.all_observations = observations @@ -472,6 +477,12 @@ class HeartFChatting: with Timer("回忆", cycle_timers): running_memorys = await self.memory_activator.activate_memory(observations) + with Timer("调整动作", cycle_timers): + # 处理特殊的观察 + await self.action_modifier.modify_actions(observations=observations, running_memorys=running_memorys) + await self.action_observation.observe() + observations.append(self.action_observation) + with Timer("执行 信息处理器", cycle_timers): all_plan_info, processor_time_costs = await self._process_processors( observations, running_memorys, cycle_timers diff --git a/src/chat/focus_chat/info_processors/chattinginfo_processor.py b/src/chat/focus_chat/info_processors/chattinginfo_processor.py index a93ce35e..ca084d2a 100644 --- a/src/chat/focus_chat/info_processors/chattinginfo_processor.py +++ b/src/chat/focus_chat/info_processors/chattinginfo_processor.py @@ -11,6 +11,7 @@ from datetime import datetime from typing import Dict from src.llm_models.utils_model import LLMRequest from src.config.config import global_config +import asyncio logger = get_logger("processor") @@ -28,7 +29,10 @@ class ChattingInfoProcessor(BaseProcessor): super().__init__() # TODO: API-Adapter修改标记 self.model_summary = LLMRequest( - model=global_config.model.utils_small, temperature=0.7, max_tokens=300, request_type="chat_observation" + model=global_config.model.utils_small, + temperature=0.7, + max_tokens=300, + request_type="focus.observation.chat", ) async def process_info( @@ -59,7 +63,8 @@ class ChattingInfoProcessor(BaseProcessor): obs_info = ObsInfo() - await self.chat_compress(obs) + # 改为异步任务,不阻塞主流程 + asyncio.create_task(self.chat_compress(obs)) # 设置说话消息 if hasattr(obs, "talking_message_str"): @@ -95,15 +100,20 @@ class ChattingInfoProcessor(BaseProcessor): return processed_infos async def chat_compress(self, obs: ChattingObservation): + log_msg = "" if obs.compressor_prompt: summary = "" try: summary_result, _, _ = await self.model_summary.generate_response(obs.compressor_prompt) - summary = "没有主题的闲聊" # 默认值 - if summary_result: # 确保结果不为空 + summary = "没有主题的闲聊" + if summary_result: summary = summary_result except Exception as e: - logger.error(f"总结主题失败 for chat {obs.chat_id}: {e}") + log_msg = f"总结主题失败 for chat {obs.chat_id}: {e}" + logger.error(log_msg) + else: + log_msg = f"chat_compress 完成 for chat {obs.chat_id}, summary: {summary}" + logger.info(log_msg) mid_memory = { "id": str(int(datetime.now().timestamp())), @@ -130,3 +140,5 @@ class ChattingInfoProcessor(BaseProcessor): obs.compressor_prompt = "" obs.oldest_messages = [] obs.oldest_messages_str = "" + + return log_msg diff --git a/src/chat/focus_chat/info_processors/mind_processor.py b/src/chat/focus_chat/info_processors/mind_processor.py index 5f8c9866..12671169 100644 --- a/src/chat/focus_chat/info_processors/mind_processor.py +++ b/src/chat/focus_chat/info_processors/mind_processor.py @@ -14,6 +14,7 @@ from .base_processor import BaseProcessor from src.chat.focus_chat.info.mind_info import MindInfo from typing import List, Optional from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation +from src.chat.heart_flow.observation.actions_observation import ActionObservation from typing import Dict from src.chat.focus_chat.info.info_base import InfoBase @@ -30,6 +31,8 @@ def init_prompt(): 现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容: {chat_observe_info} +{action_observe_info} + 以下是你之前对聊天的观察和规划,你的名字是{bot_name}: {last_mind} @@ -50,6 +53,8 @@ def init_prompt(): 现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容: {chat_observe_info} +{action_observe_info} + 以下是你之前对聊天的观察和规划,你的名字是{bot_name}: {last_mind} @@ -74,7 +79,7 @@ class MindProcessor(BaseProcessor): model=global_config.model.focus_chat_mind, temperature=global_config.model.focus_chat_mind["temp"], max_tokens=800, - request_type="focus_chat_mind", + request_type="focus.processor.chat_mind", ) self.current_mind = "" @@ -191,6 +196,8 @@ class MindProcessor(BaseProcessor): person_list = observation.person_list if isinstance(observation, HFCloopObservation): hfcloop_observe_info = observation.get_observe_info() + if isinstance(observation, ActionObservation): + action_observe_info = observation.get_observe_info() # ---------- 3. 准备个性化数据 ---------- # 获取个性化信息 @@ -211,6 +218,7 @@ class MindProcessor(BaseProcessor): chat_observe_info=chat_observe_info, last_mind=previous_mind, cycle_info_block=hfcloop_observe_info, + action_observe_info=action_observe_info, chat_target_name=chat_target_name, ) diff --git a/src/chat/focus_chat/info_processors/self_processor.py b/src/chat/focus_chat/info_processors/self_processor.py index cecaf308..d489efa2 100644 --- a/src/chat/focus_chat/info_processors/self_processor.py +++ b/src/chat/focus_chat/info_processors/self_processor.py @@ -58,7 +58,7 @@ class SelfProcessor(BaseProcessor): model=global_config.model.focus_self_recognize, temperature=global_config.model.focus_self_recognize["temp"], max_tokens=800, - request_type="focus_self_identify", + request_type="focus.processor.self_identify", ) name = chat_manager.get_stream_name(self.subheartflow_id) diff --git a/src/chat/focus_chat/info_processors/tool_processor.py b/src/chat/focus_chat/info_processors/tool_processor.py index 2d52a04a..3c0dc116 100644 --- a/src/chat/focus_chat/info_processors/tool_processor.py +++ b/src/chat/focus_chat/info_processors/tool_processor.py @@ -51,7 +51,7 @@ class ToolProcessor(BaseProcessor): self.llm_model = LLMRequest( model=global_config.model.focus_tool_use, max_tokens=500, - request_type="focus_tool", + request_type="focus.processor.tool", ) self.structured_info = [] diff --git a/src/chat/focus_chat/info_processors/working_memory_processor.py b/src/chat/focus_chat/info_processors/working_memory_processor.py index 27af1325..da720398 100644 --- a/src/chat/focus_chat/info_processors/working_memory_processor.py +++ b/src/chat/focus_chat/info_processors/working_memory_processor.py @@ -64,7 +64,7 @@ class WorkingMemoryProcessor(BaseProcessor): model=global_config.model.focus_chat_mind, temperature=global_config.model.focus_chat_mind["temp"], max_tokens=800, - request_type="focus_working_memory", + request_type="focus.processor.working_memory", ) name = chat_manager.get_stream_name(self.subheartflow_id) diff --git a/src/chat/focus_chat/memory_activator.py b/src/chat/focus_chat/memory_activator.py index a9f2c8ee..18a38f33 100644 --- a/src/chat/focus_chat/memory_activator.py +++ b/src/chat/focus_chat/memory_activator.py @@ -70,7 +70,10 @@ class MemoryActivator: def __init__(self): # TODO: API-Adapter修改标记 self.summary_model = LLMRequest( - model=global_config.model.memory_summary, temperature=0.7, max_tokens=50, request_type="chat_observation" + model=global_config.model.memory_summary, + temperature=0.7, + max_tokens=50, + request_type="focus.memory_activator", ) self.running_memory = [] self.cached_keywords = set() # 用于缓存历史关键词 diff --git a/src/chat/focus_chat/info_processors/action_processor.py b/src/chat/focus_chat/planners/modify_actions.py similarity index 75% rename from src/chat/focus_chat/info_processors/action_processor.py rename to src/chat/focus_chat/planners/modify_actions.py index f4aa7c1d..c376426a 100644 --- a/src/chat/focus_chat/info_processors/action_processor.py +++ b/src/chat/focus_chat/planners/modify_actions.py @@ -1,8 +1,5 @@ from typing import List, Optional, Any from src.chat.heart_flow.observation.observation import Observation -from src.chat.focus_chat.info.info_base import InfoBase -from src.chat.focus_chat.info.action_info import ActionInfo -from .base_processor import BaseProcessor from src.common.logger_manager import get_logger from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation from src.chat.heart_flow.observation.chatting_observation import ChattingObservation @@ -10,11 +7,12 @@ from src.chat.message_receive.chat_stream import chat_manager from typing import Dict from src.config.config import global_config import random +from src.chat.focus_chat.planners.action_manager import ActionManager -logger = get_logger("processor") +logger = get_logger("action_manager") -class ActionProcessor(BaseProcessor): +class ActionModifier: """动作处理器 用于处理Observation对象,将其转换为ObsInfo对象。 @@ -22,33 +20,24 @@ class ActionProcessor(BaseProcessor): log_prefix = "动作处理" - def __init__(self): + def __init__(self, action_manager: ActionManager): """初始化观察处理器""" - super().__init__() + self.action_manager = action_manager + self.all_actions = self.action_manager.get_registered_actions() - async def process_info( + async def modify_actions( self, observations: Optional[List[Observation]] = None, running_memorys: Optional[List[Dict]] = None, **kwargs: Any, - ) -> List[InfoBase]: - """处理Observation对象 - - Args: - infos: InfoBase对象列表 - observations: 可选的Observation对象列表 - **kwargs: 其他可选参数 - - Returns: - List[InfoBase]: 处理后的ObsInfo实例列表 - """ + ): # print(f"observations: {observations}") - processed_infos = [] + # processed_infos = [] # 处理Observation对象 if observations: - action_info = ActionInfo() - all_actions = None + # action_info = ActionInfo() + # all_actions = None hfc_obs = None chat_obs = None @@ -66,7 +55,7 @@ class ActionProcessor(BaseProcessor): # 处理HFCloopObservation if hfc_obs: obs = hfc_obs - all_actions = obs.all_actions + all_actions = self.all_actions action_changes = await self.analyze_loop_actions(obs) if action_changes["add"] or action_changes["remove"]: # 合并动作变更 @@ -74,13 +63,13 @@ class ActionProcessor(BaseProcessor): merged_action_changes["remove"].extend(action_changes["remove"]) # 收集变更原因 - if action_changes["add"]: - reasons.append(f"添加动作{action_changes['add']}因为检测到大量无回复") - if action_changes["remove"]: - reasons.append(f"移除动作{action_changes['remove']}因为检测到连续回复") + # if action_changes["add"]: + # reasons.append(f"添加动作{action_changes['add']}因为检测到大量无回复") + # if action_changes["remove"]: + # reasons.append(f"移除动作{action_changes['remove']}因为检测到连续回复") # 处理ChattingObservation - if chat_obs and all_actions is not None: + if chat_obs: obs = chat_obs # 检查动作的关联类型 chat_context = chat_manager.get_stream(obs.chat_id).context @@ -98,14 +87,23 @@ class ActionProcessor(BaseProcessor): merged_action_changes["remove"].extend(type_mismatched_actions) reasons.append(f"移除动作{type_mismatched_actions}因为关联类型不匹配") + for action_name in merged_action_changes["add"]: + if action_name in self.action_manager.get_registered_actions(): + self.action_manager.add_action_to_using(action_name) + logger.debug(f"{self.log_prefix} 添加动作: {action_name}, 原因: {reasons}") + + for action_name in merged_action_changes["remove"]: + self.action_manager.remove_action_from_using(action_name) + logger.debug(f"{self.log_prefix} 移除动作: {action_name}, 原因: {reasons}") + # 如果有任何动作变更,设置到action_info中 - if merged_action_changes["add"] or merged_action_changes["remove"]: - action_info.set_action_changes(merged_action_changes) - action_info.set_reason(" | ".join(reasons)) + # if merged_action_changes["add"] or merged_action_changes["remove"]: + # action_info.set_action_changes(merged_action_changes) + # action_info.set_reason(" | ".join(reasons)) - processed_infos.append(action_info) + # processed_infos.append(action_info) - return processed_infos + # return processed_infos async def analyze_loop_actions(self, obs: HFCloopObservation) -> Dict[str, List[str]]: """分析最近的循环内容并决定动作的增减 diff --git a/src/chat/focus_chat/planners/planner.py b/src/chat/focus_chat/planners/planner.py index 61a33990..528f7a7f 100644 --- a/src/chat/focus_chat/planners/planner.py +++ b/src/chat/focus_chat/planners/planner.py @@ -79,7 +79,7 @@ class ActionPlanner: self.planner_llm = LLMRequest( model=global_config.model.focus_planner, max_tokens=1000, - request_type="focus_planner", # 用于动作规划 + request_type="focus.planner", # 用于动作规划 ) self.action_manager = action_manager @@ -101,35 +101,24 @@ class ActionPlanner: # 获取观察信息 extra_info: list[str] = [] - # 首先处理动作变更 - for info in all_plan_info: - if isinstance(info, ActionInfo) and info.has_changes(): - add_actions = info.get_add_actions() - remove_actions = info.get_remove_actions() - reason = info.get_reason() - print(f"{self.log_prefix} 动作变更: {add_actions} {remove_actions} {reason}") + # 设置默认值 + nickname_str = "" + for nicknames in global_config.bot.alias_names: + nickname_str += f"{nicknames}," + name_block = f"你的名字是{global_config.bot.nickname},你的昵称有{nickname_str},有人也会用这些昵称称呼你。" - # 处理动作的增加 - for action_name in add_actions: - if action_name in self.action_manager.get_registered_actions(): - self.action_manager.add_action_to_using(action_name) - logger.debug(f"{self.log_prefix}添加动作: {action_name}, 原因: {reason}") + personality_block = individuality.get_personality_prompt(x_person=2, level=2) + identity_block = individuality.get_identity_prompt(x_person=2, level=2) - # 处理动作的移除 - for action_name in remove_actions: - self.action_manager.remove_action_from_using(action_name) - logger.debug(f"{self.log_prefix}移除动作: {action_name}, 原因: {reason}") + self_info = name_block + personality_block + identity_block + current_mind = "你思考了很久,没有想清晰要做什么" - # 如果当前选择的动作被移除了,更新为no_reply - if action in remove_actions: - action = "no_reply" - reasoning = f"之前选择的动作{action}已被移除,原因: {reason}" - - # 继续处理其他信息 - self_info = "" - current_mind = "" cycle_info = "" structured_info = "" + extra_info = [] + observed_messages = [] + observed_messages_str = "" + chat_type = "group" is_group_chat = True for info in all_plan_info: if isinstance(info, ObsInfo): @@ -146,8 +135,8 @@ class ActionPlanner: elif isinstance(info, StructuredInfo): structured_info = info.get_processed_info() # print(f"structured_info: {structured_info}") - elif not isinstance(info, ActionInfo): # 跳过已处理的ActionInfo - extra_info.append(info.get_processed_info()) + # elif not isinstance(info, ActionInfo): # 跳过已处理的ActionInfo + # extra_info.append(info.get_processed_info()) # 获取当前可用的动作 current_available_actions = self.action_manager.get_using_actions() diff --git a/src/chat/focus_chat/working_memory/memory_manager.py b/src/chat/focus_chat/working_memory/memory_manager.py index 9ecbe610..bdbb429e 100644 --- a/src/chat/focus_chat/working_memory/memory_manager.py +++ b/src/chat/focus_chat/working_memory/memory_manager.py @@ -36,7 +36,7 @@ class MemoryManager: model=global_config.model.focus_working_memory, temperature=0.3, max_tokens=512, - request_type="memory_summarization", + request_type="focus.processor.working_memory", ) @property diff --git a/src/chat/heart_flow/observation/actions_observation.py b/src/chat/heart_flow/observation/actions_observation.py new file mode 100644 index 00000000..8310a17b --- /dev/null +++ b/src/chat/heart_flow/observation/actions_observation.py @@ -0,0 +1,36 @@ +# 定义了来自外部世界的信息 +# 外部世界可以是某个聊天 不同平台的聊天 也可以是任意媒体 +from datetime import datetime +from src.common.logger_manager import get_logger +from src.chat.focus_chat.planners.action_manager import ActionManager + +logger = get_logger("observation") + + +# 特殊的观察,专门用于观察动作 +# 所有观察的基类 +class ActionObservation: + def __init__(self, observe_id): + self.observe_info = "" + self.observe_id = observe_id + self.last_observe_time = datetime.now().timestamp() # 初始化为当前时间 + self.action_manager: ActionManager = None + + self.all_actions = {} + self.all_using_actions = {} + + def get_observe_info(self): + return self.observe_info + + def set_action_manager(self, action_manager: ActionManager): + self.action_manager = action_manager + self.all_actions = self.action_manager.get_registered_actions() + + async def observe(self): + action_info_block = "" + self.all_using_actions = self.action_manager.get_using_actions() + for action_name, action_info in self.all_using_actions.items(): + action_info_block += f"\n{action_name}: {action_info.get('description', '')}" + action_info_block += "\n注意,除了上面动作选项之外,你在群聊里不能做其他任何事情,这是你能力的边界\n" + + self.observe_info = action_info_block diff --git a/src/chat/heart_flow/observation/hfcloop_observation.py b/src/chat/heart_flow/observation/hfcloop_observation.py index 171aaeb7..7c782bfd 100644 --- a/src/chat/heart_flow/observation/hfcloop_observation.py +++ b/src/chat/heart_flow/observation/hfcloop_observation.py @@ -3,7 +3,6 @@ from datetime import datetime from src.common.logger_manager import get_logger from src.chat.focus_chat.heartFC_Cycleinfo import CycleDetail -from src.chat.focus_chat.planners.action_manager import ActionManager from typing import List # Import the new utility function @@ -17,9 +16,6 @@ class HFCloopObservation: self.observe_id = observe_id self.last_observe_time = datetime.now().timestamp() # 初始化为当前时间 self.history_loop: List[CycleDetail] = [] - self.action_manager: ActionManager = None - - self.all_actions = {} def get_observe_info(self): return self.observe_info @@ -27,10 +23,6 @@ class HFCloopObservation: def add_loop_info(self, loop_info: CycleDetail): self.history_loop.append(loop_info) - def set_action_manager(self, action_manager: ActionManager): - self.action_manager = action_manager - self.all_actions = self.action_manager.get_registered_actions() - async def observe(self): recent_active_cycles: List[CycleDetail] = [] for cycle in reversed(self.history_loop): diff --git a/src/chat/normal_chat/normal_chat_generator.py b/src/chat/normal_chat/normal_chat_generator.py index 28df6f18..5d17d22a 100644 --- a/src/chat/normal_chat/normal_chat_generator.py +++ b/src/chat/normal_chat/normal_chat_generator.py @@ -21,13 +21,13 @@ class NormalChatGenerator: model=global_config.model.normal_chat_1, # temperature=0.7, max_tokens=3000, - request_type="normal_chat_1", + request_type="normal.chat_1", ) self.model_normal = LLMRequest( model=global_config.model.normal_chat_2, # temperature=global_config.model.normal_chat_2["temp"], max_tokens=256, - request_type="normal_chat_2", + request_type="normal.chat_2", ) self.model_sum = LLMRequest( diff --git a/src/chat/normal_chat/normal_prompt.py b/src/chat/normal_chat/normal_prompt.py index 9618987a..5b82b8b8 100644 --- a/src/chat/normal_chat/normal_prompt.py +++ b/src/chat/normal_chat/normal_prompt.py @@ -34,6 +34,7 @@ def init_prompt(): {relation_prompt} {prompt_info} {chat_target} +现在时间是:{now_time} {chat_talking_prompt} 现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言或者回复这条消息。\n 你的网名叫{bot_name},有人也叫你{bot_other_names},{prompt_personality}。 @@ -221,6 +222,8 @@ class PromptBuilder: logger.debug("开始构建 normal prompt") + now_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + # --- Choose template and format based on chat type --- if is_group_chat: template_name = "reasoning_prompt_main" @@ -249,6 +252,7 @@ class PromptBuilder: prompt_ger=prompt_ger, # moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"), moderation_prompt=moderation_prompt_block, + now_time=now_time, ) else: template_name = "reasoning_prompt_private_main" @@ -273,6 +277,7 @@ class PromptBuilder: prompt_ger=prompt_ger, # moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"), moderation_prompt=moderation_prompt_block, + now_time=now_time, ) # --- End choosing template --- diff --git a/src/chat/utils/utils.py b/src/chat/utils/utils.py index c7a45675..3952d3dc 100644 --- a/src/chat/utils/utils.py +++ b/src/chat/utils/utils.py @@ -62,11 +62,22 @@ def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, float]: f"消息中包含不合理的设置 is_mentioned: {message.message_info.additional_config.get('is_mentioned')}" ) + if global_config.bot.nickname in message.processed_plain_text: + is_mentioned = True + + for alias_name in global_config.bot.alias_names: + if alias_name in message.processed_plain_text: + is_mentioned = True + # 判断是否被@ if re.search(rf"@<(.+?):{global_config.bot.qq_account}>", message.processed_plain_text): is_at = True is_mentioned = True + # print(f"message.processed_plain_text: {message.processed_plain_text}") + # print(f"is_mentioned: {is_mentioned}") + # print(f"is_at: {is_at}") + if is_at and global_config.normal_chat.at_bot_inevitable_reply: reply_probability = 1.0 logger.info("被@,回复概率设置为100%") diff --git a/src/experimental/only_message_process.py b/src/experimental/only_message_process.py index 8fb1e3bf..6dd70ca7 100644 --- a/src/experimental/only_message_process.py +++ b/src/experimental/only_message_process.py @@ -2,7 +2,11 @@ from src.common.logger_manager import get_logger from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.storage import MessageStorage from src.config.config import global_config +from src.chat.message_receive.chat_stream import ChatStream + +from maim_message import UserInfo from datetime import datetime +import re logger = get_logger("pfc") @@ -14,7 +18,7 @@ class MessageProcessor: self.storage = MessageStorage() @staticmethod - def _check_ban_words(text: str, chat, userinfo) -> bool: + def _check_ban_words(text: str, chat: ChatStream, userinfo: UserInfo) -> bool: """检查消息中是否包含过滤词""" for word in global_config.message_receive.ban_words: if word in text: @@ -26,13 +30,12 @@ class MessageProcessor: return False @staticmethod - def _check_ban_regex(text: str, chat, userinfo) -> bool: + def _check_ban_regex(text: str, chat: ChatStream, userinfo: UserInfo) -> bool: """检查消息是否匹配过滤正则表达式""" - for pattern in global_config.chat.ban_msgs_regex: - if pattern.search(text): - logger.info( - f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" - ) + for pattern in global_config.message_receive.ban_msgs_regex: + if re.search(pattern, text): + chat_name = chat.group_info.group_name if chat.group_info else "私聊" + logger.info(f"[{chat_name}]{userinfo.user_nickname}:{text}") logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered") return True return False diff --git a/src/individuality/expression_style.py b/src/individuality/expression_style.py index 841d44e5..f4eed60b 100644 --- a/src/individuality/expression_style.py +++ b/src/individuality/expression_style.py @@ -36,7 +36,7 @@ class PersonalityExpression: model=global_config.model.focus_expressor, temperature=0.1, max_tokens=256, - request_type="learn_expression", + request_type="expressor.learner", ) 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") diff --git a/src/person_info/person_info.py b/src/person_info/person_info.py index b0682078..11f8dd2b 100644 --- a/src/person_info/person_info.py +++ b/src/person_info/person_info.py @@ -60,7 +60,7 @@ class PersonInfoManager: self.qv_name_llm = LLMRequest( model=global_config.model.utils, max_tokens=256, - request_type="qv_name", + request_type="relation.qv_name", ) try: db.connect(reuse_if_open=True) diff --git a/src/plugins/test_plugin_pic/actions/pic_action.py b/src/plugins/test_plugin_pic/actions/pic_action.py index e2eeb918..abf869e0 100644 --- a/src/plugins/test_plugin_pic/actions/pic_action.py +++ b/src/plugins/test_plugin_pic/actions/pic_action.py @@ -22,12 +22,15 @@ class PicAction(PluginAction): """根据描述使用火山引擎HTTP API生成图片的动作处理类""" action_name = "pic_action" - action_description = "可以根据特定的描述,生成并发送一张图片,如果没提供描述,就根据聊天内容生成" + action_description = ( + "可以根据特定的描述,生成并发送一张图片,如果没提供描述,就根据聊天内容生成,你可以立刻画好,不用等待" + ) action_parameters = { "description": "图片描述,输入你想要生成并发送的图片的描述,必填", "size": "图片尺寸,例如 '1024x1024' (可选, 默认从配置或 '1024x1024')", } action_require = [ + "当有人让你画东西时使用,你可以立刻画好,不用等待", "当有人要求你生成并发送一张图片时使用", "当有人让你画一张图时使用", ] @@ -45,6 +48,8 @@ class PicAction(PluginAction): ): super().__init__(action_data, reasoning, cycle_timers, thinking_id, global_config, **kwargs) + logger.info(f"{self.log_prefix} 开始绘图!原因是:{self.reasoning}") + http_base_url = self.config.get("base_url") http_api_key = self.config.get("volcano_generate_api_key") diff --git a/src/tools/tool_can_use/lpmm_get_knowledge.py b/src/tools/tool_can_use/lpmm_get_knowledge.py index f7c0bd75..fc2dc072 100644 --- a/src/tools/tool_can_use/lpmm_get_knowledge.py +++ b/src/tools/tool_can_use/lpmm_get_knowledge.py @@ -1,5 +1,4 @@ from src.tools.tool_can_use.base_tool import BaseTool -from src.chat.utils.utils import get_embedding # from src.common.database import db from src.common.logger_manager import get_logger @@ -38,125 +37,19 @@ class SearchKnowledgeFromLPMMTool(BaseTool): # threshold = function_args.get("threshold", 0.4) # 调用知识库搜索 - embedding = await get_embedding(query, request_type="info_retrieval") - if embedding: - knowledge_info = qa_manager.get_knowledge(query) - logger.debug(f"知识库查询结果: {knowledge_info}") - if knowledge_info: - content = f"你知道这些知识: {knowledge_info}" - else: - content = f"你不太了解有关{query}的知识" - return {"type": "lpmm_knowledge", "id": query, "content": content} - # 如果获取嵌入失败 - return {"type": "info", "id": query, "content": f"无法获取关于'{query}'的嵌入向量,你lpmm知识库炸了"} + + knowledge_info = qa_manager.get_knowledge(query) + + logger.debug(f"知识库查询结果: {knowledge_info}") + + if knowledge_info: + content = f"你知道这些知识: {knowledge_info}" + else: + content = f"你不太了解有关{query}的知识" + return {"type": "lpmm_knowledge", "id": query, "content": content} except Exception as e: + # 捕获异常并记录错误 logger.error(f"知识库搜索工具执行失败: {str(e)}") # 在其他异常情况下,确保 id 仍然是 query (如果它被定义了) query_id = query if "query" in locals() else "unknown_query" return {"type": "info", "id": query_id, "content": f"lpmm知识库搜索失败,炸了: {str(e)}"} - - # def get_info_from_db( - # self, query_embedding: list, limit: int = 1, threshold: float = 0.5, return_raw: bool = False - # ) -> Union[str, list]: - # """从数据库中获取相关信息 - - # Args: - # query_embedding: 查询的嵌入向量 - # limit: 最大返回结果数 - # threshold: 相似度阈值 - # return_raw: 是否返回原始结果 - - # Returns: - # Union[str, list]: 格式化的信息字符串或原始结果列表 - # """ - # if not query_embedding: - # return "" if not return_raw else [] - - # # 使用余弦相似度计算 - # pipeline = [ - # { - # "$addFields": { - # "dotProduct": { - # "$reduce": { - # "input": {"$range": [0, {"$size": "$embedding"}]}, - # "initialValue": 0, - # "in": { - # "$add": [ - # "$$value", - # { - # "$multiply": [ - # {"$arrayElemAt": ["$embedding", "$$this"]}, - # {"$arrayElemAt": [query_embedding, "$$this"]}, - # ] - # }, - # ] - # }, - # } - # }, - # "magnitude1": { - # "$sqrt": { - # "$reduce": { - # "input": "$embedding", - # "initialValue": 0, - # "in": {"$add": ["$$value", {"$multiply": ["$$this", "$$this"]}]}, - # } - # } - # }, - # "magnitude2": { - # "$sqrt": { - # "$reduce": { - # "input": query_embedding, - # "initialValue": 0, - # "in": {"$add": ["$$value", {"$multiply": ["$$this", "$$this"]}]}, - # } - # } - # }, - # } - # }, - # {"$addFields": {"similarity": {"$divide": ["$dotProduct", {"$multiply": ["$magnitude1", "$magnitude2"]}]}}}, - # { - # "$match": { - # "similarity": {"$gte": threshold} # 只保留相似度大于等于阈值的结果 - # } - # }, - # {"$sort": {"similarity": -1}}, - # {"$limit": limit}, - # {"$project": {"content": 1, "similarity": 1}}, - # ] - - # results = list(db.knowledges.aggregate(pipeline)) - # logger.debug(f"知识库查询结果数量: {len(results)}") - - # if not results: - # return "" if not return_raw else [] - - # if return_raw: - # return results - # else: - # # 返回所有找到的内容,用换行分隔 - # return "\n".join(str(result["content"]) for result in results) - - def _format_results(self, results: list) -> str: - """格式化结果""" - if not results: - return "未找到相关知识。" - - formatted_string = "我找到了一些相关知识:\n" - for i, result in enumerate(results): - # chunk_id = result.get("chunk_id") - text = result.get("text", "") - source = result.get("source", "未知来源") - source_type = result.get("source_type", "未知类型") - similarity = result.get("similarity", 0.0) - - formatted_string += ( - f"{i + 1}. (相似度: {similarity:.2f}) 类型: {source_type}, 来源: {source} \n内容片段: {text}\n\n" - ) - # 暂时去掉chunk_id - # formatted_string += f"{i + 1}. (相似度: {similarity:.2f}) 类型: {source_type}, 来源: {source}, Chunk ID: {chunk_id} \n内容片段: {text}\n\n" - - return formatted_string - - -# 注册工具 -# register_tool(SearchKnowledgeTool) diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 0ca1e40a..100e0c6d 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -13,9 +13,9 @@ version = "2.6.0" #----以上是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- [bot] -qq_account = 1145141919810 -nickname = "麦麦" -alias_names = ["麦叠", "牢麦"] +qq_account = 1145141919810 # 麦麦的QQ账号 +nickname = "麦麦" # 麦麦的昵称 +alias_names = ["麦叠", "牢麦"] # 麦麦的别名 [personality] personality_core = "是一个积极向上的女大学生" # 建议50字以内 @@ -84,8 +84,8 @@ response_willing_amplifier = 1 # 麦麦回复意愿放大系数,一般为1 response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数 emoji_response_penalty = 0 # 表情包回复惩罚系数,设为0为不回复单个表情包,减少单独回复表情包的概率 -mentioned_bot_inevitable_reply = false # 提及 bot 必然回复 -at_bot_inevitable_reply = false # @bot 必然回复 +mentioned_bot_inevitable_reply = true # 提及 bot 必然回复 +at_bot_inevitable_reply = true # @bot 必然回复 down_frequency_rate = 3 # 降低回复频率的群组回复意愿降低系数 除法 talk_frequency_down_groups = [] #降低回复频率的群号码 @@ -93,14 +93,14 @@ talk_frequency_down_groups = [] #降低回复频率的群号码 [focus_chat] #专注聊天 think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗 -observation_context_size = 15 # 观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖 -compressed_length = 5 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度,会压缩,最短压缩长度为5 -compress_length_limit = 5 #最多压缩份数,超过该数值的压缩上下文会被删除 +observation_context_size = 16 # 观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖 +compressed_length = 8 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度,会压缩,最短压缩长度为5 +compress_length_limit = 4 #最多压缩份数,超过该数值的压缩上下文会被删除 [focus_chat_processor] # 专注聊天处理器,打开可以实现更多功能,但是会增加token消耗 self_identify_processor = true # 是否启用自我识别处理器 tool_use_processor = false # 是否启用工具使用处理器 -working_memory_processor = false # 是否启用工作记忆处理器 +working_memory_processor = false # 是否启用工作记忆处理器,不稳定,消耗量大 [emoji] max_reg_num = 40 # 表情包最大注册数量 diff --git a/template/template.env b/template/template.env index 3800e7d5..dd63a5f4 100644 --- a/template/template.env +++ b/template/template.env @@ -2,14 +2,16 @@ HOST=127.0.0.1 PORT=8000 #key and url -CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 +CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 +xxxxxxx_BASE_URL=https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 定义你要用的api的key(需要去对应网站申请哦) DEEP_SEEK_KEY= CHAT_ANY_WHERE_KEY= SILICONFLOW_KEY= +xxxxxxx_KEY= # 定义日志相关配置 @@ -22,8 +24,8 @@ CONSOLE_LOG_LEVEL=INFO # 自定义日志的默认文件输出日志级别 FILE_LOG_LEVEL=DEBUG -# 原生日志的控制台输出日志级别(nonebot就是这一类) +# 原生日志的控制台输出日志级别 DEFAULT_CONSOLE_LOG_LEVEL=SUCCESS -# 原生日志的默认文件输出日志级别(nonebot就是这一类) +# 原生日志的默认文件输出日志级别 DEFAULT_FILE_LOG_LEVEL=DEBUG diff --git a/tests/test_config.py b/tests/test_config.py deleted file mode 100644 index 1a123960..00000000 --- a/tests/test_config.py +++ /dev/null @@ -1,7 +0,0 @@ -from src.config.config import global_config - - -class TestConfig: - def test_load(self): - config = global_config - print(config)