From 9aa502f01aa3b49018732aa0ea6950f3228a3400 Mon Sep 17 00:00:00 2001 From: SXP-Simon Date: Tue, 10 Feb 2026 16:09:18 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E9=85=8D=E7=BD=AE):=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=20=5Fconf=5Fschema.json=20=E4=B8=BA=207=20=E7=BB=84?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=E7=BB=93=E6=9E=84=EF=BC=8C=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=20ConfigManager=20=E9=80=82=E9=85=8D=E5=B5=8C=E5=A5=97?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _conf_schema.json | 624 +++++++++++--------- src/infrastructure/config/config_manager.py | 184 +++--- 2 files changed, 444 insertions(+), 364 deletions(-) diff --git a/_conf_schema.json b/_conf_schema.json index 1e41fc3..ff108bb 100644 --- a/_conf_schema.json +++ b/_conf_schema.json @@ -1,304 +1,352 @@ { - "group_list_mode": { - "description": "群聊权限模式", - "type": "string", - "options": ["whitelist", "blacklist", "none"], - "default": "none", - "hint": "whitelist: 仅允许列表内群;blacklist: 拒绝列表内群;none: 不限制" - }, - "group_list": { - "type": "list", - "description": "群组白/黑名单列表", - "default": [], - "hint": "黑白名单模式下使用的群组列表。支持填写 AstrBot UMO (如 xxxxx:GroupMessage:123456) 或 纯群号 (如 123456,将尝试自动匹配)。可以使用 /sid 命令查看当前会话的 UMO。", - "items": { - "type": "string" - } - }, - "max_concurrent_tasks": { - "type": "int", - "description": "自动分析最大并发数", - "default": 3, - "hint": "同时进行的群聊分析任务数量,建议根据机器性能调整,过高可能导致LLM API RPM 超出限制,卡顿或被风控" - }, - "debug_mode": { - "type": "bool", - "description": "调试模式(建议关闭)", - "default": false, - "hint": "启用后,会在 plugin_data/debug_data 目录下保存每次分析的平台 API 原始历史消息和 Prompt 原文,用于调试和优化提示词。" - }, - "max_messages": { - "type": "int", - "description": "单次分析的获取最大消息条数基准", - "default": 1000, - "hint": "单次分析的获取最大消息条数基准值,如果当天群聊消息过多,已经获取到足够的消息(达到 max_messages),停止获取,获取到的消息数量是最新一次获取到的超过 max_messages 的消息数量,建议500-2000" - }, - "analysis_days": { - "type": "int", - "description": "默认分析天数", - "default": 1, - "hint": "默认分析最近几天的消息,建议1-7天" - }, - "auto_analysis_time": { - "type": "list", - "description": "自动分析时间列表", - "default": ["09:00"], - "hint": "每日自动分析的时间点列表,格式HH:MM(例如 23:00 ),支持多个时间点", - "items": { - "type": "string" - } - }, - "enable_auto_analysis": { - "type": "bool", - "description": "启用定时自动分析", - "default": false, - "hint": "是否在指定时间自动进行群聊分析,启用需要填写下面的机器人ID号,并且确保群聊号在 enabled_groups 配置中,否则获取不到实例,不会自动分析" - }, - "bot_self_ids": { - "type": "list", - "description": "群分析时屏蔽的用户ID列表", - "default": [], - "hint": "填写后可启用自动分析功能。可以填写用于自动分析的机器人 ID(在不同平台上可能是数字或字符串)、多消息平台 ID、不希望出现于群分析中的其他人的 ID 等。", - "items": { - "type": "string" - } - }, - "enable_user_card": { - "type": "bool", - "description": "使用用户群名片", - "default": false, - "hint": "在分析中优先使用用户的群名片作为昵称显示" - }, - "output_format": { - "type": "string", - "description": "输出格式", - "default": "image", - "hint": "分析报告的输出格式:image(图片)、text(文本)、pdf(PDF文件)。使用 PDF 需要额外配置,根据文件中的 PDF_功能说明.md 进行配置" - }, - "report_template": { - "type": "string", - "description": "报告模板", - "default": "scrapbook", - "hint": "分析报告使用的HTML模板名称,使用 `/设置模板` 查看使用指南,使用`/查看模板` 命令查看模板样式效果" - }, - - "min_messages_threshold": { - "type": "int", - "description": "最小消息数阈值", - "default": 50, - "hint": "进行分析所需的最小消息数量" - }, - "topic_analysis_enabled": { - "type": "bool", - "description": "启用话题分析", - "default": true, - "hint": "是否使用LLM进行智能话题分析" - }, - "user_title_analysis_enabled": { - "type": "bool", - "description": "启用用户称号分析", - "default": true, - "hint": "是否使用LLM进行用户称号分析" - }, - "golden_quote_analysis_enabled": { - "type": "bool", - "description": "启用金句分析", - "default": true, - "hint": "是否使用LLM进行金句分析" - }, - "max_topics": { - "type": "int", - "description": "最大话题数量", - "default": 5, - "hint": "分析报告中显示的最大话题数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。" - }, - "max_user_titles": { - "type": "int", - "description": "最大用户称号数量", - "default": 8, - "hint": "分析报告中显示的最大用户称号数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。" - }, - "max_golden_quotes": { - "type": "int", - "description": "最大金句数量", - "default": 5, - "hint": "分析报告中显示的最大金句数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。" - }, - "llm_retries": { - "type": "int", - "description": "LLM 请求重试次数", - "default": 2, - "hint": "当请求超时或失败时自动重试的次数,建议设置为1~3之间。" - }, - "llm_backoff": { - "type": "int", - "description": "LLM 请求重试退避基值(秒)", - "default": 2, - "hint": "重试之间的基准等待时间(秒),实际等待时间为基值乘以尝试次数。" - }, - "topic_max_tokens": { - "type": "int", - "description": "(兼容部分提供商)话题分析最大 Token 数", - "default": 12288, - "hint": "(兼容部分提供商,实测大部分模型调整后没有明显效果)话题分析时 LLM 能生成的最大 token 数量。当分析内容较多或者分析提示词复杂时,建议适当调大此值以保证输出质量。" - }, - "golden_quote_max_tokens": { - "type": "int", - "description": "(兼容部分提供商)金句分析最大 Token 数", - "default": 4096, - "hint": "(兼容部分提供商,实测大部分模型调整后没有明显效果)金句分析时 LLM 能生成的最大 token 数量。当分析内容较多或者分析提示词复杂时,建议适当调大此值以保证输出质量。" - }, - "user_title_max_tokens": { - "type": "int", - "description": "(兼容部分提供商)用户称号分析最大 Token 数", - "default": 4096, - "hint": "(兼容部分提供商,实测大部分模型调整后没有明显效果)用户称号分析时 LLM 能生成的最大 token 数量。当分析内容较多或者分析提示词复杂时,建议适当调大此值以保证输出质量。" - }, - "llm_provider_id": { - "type": "string", - "description": "LLM Provider ID(用于所有分析任务)", - "_special": "select_provider", - "default": "", - "hint": "选择用于群聊分析的 LLM Provider。留空则使用当前会话的默认 Provider" - }, - "topic_provider_id": { - "type": "string", - "description": "话题分析专用 Provider ID", - "_special": "select_provider", - "default": "", - "hint": "专门用于话题分析的 Provider。留空则使用主 LLM Provider" - }, - "user_title_provider_id": { - "type": "string", - "description": "用户称号分析专用 Provider ID", - "_special": "select_provider", - "default": "", - "hint": "专门用于用户称号分析的 Provider。留空则使用主 LLM Provider" - }, - "golden_quote_provider_id": { - "type": "string", - "description": "金句分析专用 Provider ID", - "_special": "select_provider", - "default": "", - "hint": "专门用于金句分析的 Provider。留空则使用主 LLM Provider" - }, - "pdf_output_dir": { - "type": "string", - "description": "PDF输出目录", - "default": "data/plugins/astrbot_plugin_qq_group_daily_analysis/reports", - "hint": "PDF报告文件的保存目录" - }, - "browser_path": { - "type": "string", - "description": "自定义浏览器路径", - "default": "", - "hint": "要填写的话请你清楚自己在干什么。自定义浏览器的可执行文件路径(如 Chrome 或 Edge 的 .exe 文件)。提示:如果是在网页后台设置,则直接输入普通路径即可(如 C:\\Program Files\\...);如果是手动编辑 config.json 文件,请务必使用双反斜杠 '\\\\' 分隔路径。" - }, - "pdf_filename_format": { - "type": "string", - "description": "PDF文件名格式", - "default": "群聊分析报告_{group_id}_{date}.pdf", - "hint": "PDF文件名格式,支持变量:{group_id}(群号)、{date}(日期)" - }, - "incremental_enabled": { - "type": "bool", - "description": "启用增量分析模式", - "default": false, - "hint": "启用后,插件会在一天内多次进行小规模分析并累积结果,最终在报告时间汇总输出。适用于消息量大的群聊,可显著提升分析覆盖率和24小时活跃图表准确性。关闭时保持传统的单次全量分析模式。" - }, - "incremental_interval_minutes": { - "type": "int", - "description": "增量分析间隔(分钟)", - "default": 120, - "hint": "每隔多少分钟执行一次增量分析。建议60-180分钟,间隔越短分析越精细但消耗更多 Token。" - }, - "incremental_max_daily_analyses": { - "type": "int", - "description": "每日最大增量分析次数", - "default": 8, - "hint": "一天内最多执行多少次增量分析。达到上限后当天不再触发新的增量分析,但仍会在报告时间生成最终报告。建议4-12次。" - }, - "incremental_max_messages": { - "type": "int", - "description": "单次增量分析最大消息数", - "default": 300, - "hint": "每次增量分析最多处理的消息条数。数值越大单次分析越全面但 Token 消耗越高。建议200-500。" - }, - "incremental_min_messages": { - "type": "int", - "description": "增量分析最小消息数阈值", - "default": 20, - "hint": "当自上次分析以来的新消息数低于此阈值时,跳过本次增量分析以节省 Token。建议10-50。" - }, - "incremental_topics_per_batch": { - "type": "int", - "description": "单次增量提取话题数", - "default": 3, - "hint": "每次增量分析最多提取的话题数量。最终报告会汇总所有批次的话题。建议2-5。" - }, - "incremental_quotes_per_batch": { - "type": "int", - "description": "单次增量提取金句数", - "default": 3, - "hint": "每次增量分析最多提取的金句数量。最终报告会汇总所有批次的金句。建议2-5。" - }, - "incremental_active_start_hour": { - "type": "int", - "description": "增量分析活跃起始时段", - "default": 8, - "hint": "增量分析的活跃时段起始小时(24小时制)。在活跃时段之外不会触发增量分析,避免凌晨等低活跃时段浪费分析配额。" - }, - "incremental_active_end_hour": { - "type": "int", - "description": "增量分析活跃结束时段", - "default": 23, - "hint": "增量分析的活跃时段结束小时(24小时制)。超过此时段后不再触发增量分析。" - }, - "incremental_stagger_seconds": { - "type": "int", - "description": "多群增量分析交错间隔(秒)", - "default": 30, - "hint": "当同时对多个群进行增量分析时,每个群之间的延迟间隔(秒),用于均匀分散 API 请求压力。建议15-60秒。" - }, - "topic_analysis_prompts": { - "description": "话题分析提示词模板", + "basic": { + "description": "基础设置", "type": "object", - "hint": "话题分析提示词模板,可自定义修改,保留变量 {max_topics} 和 {messages_text} 写法不要更改,需要写 JSON 块请使用双花括号 {{ ... }} 兼容当前编辑存在的问题。非常推荐根据实际群聊情况进行优化,保留一定提示样本示例供模型参考", + "hint": "群聊分析插件的基础配置项,包括群聊权限、消息数量、输出格式等", "items": { - "topic_prompt": { - "description": "默认话题分析提示词", - "type": "text", - "editor_mode": true, - "editor_language": "markdown", - "default": "你是一个帮我进行群聊信息总结的助手,生成总结内容时,你需要严格遵守下面的几个准则:\n\n请分析接下来提供的群聊记录,提取出最多 **{max_topics}** 个主要话题。\n\n## 对于每个话题,请提供:\n\n1. **话题名称**(突出主题内容,尽量简明扼要)\n2. **主要参与者**(最多5人)\n3. **话题详细描述**(包含关键信息和结论)\n\n## 注意事项:\n\n- 对于比较有价值的点,稍微用一两句话详细讲讲,比如不要生成 \"Nolan 和 SOV 讨论了 galgame 中关于性符号的衍生情况\" 这种宽泛的内容,而是生成更加具体的讨论内容,让其他人只看这个消息就能知道讨论中有价值的、有营养的信息。\n- 对于其中的部分信息,你需要特意提到主题施加的主体是谁,是哪个群友做了什么事情,而不要直接生成和群友没有关系的语句。\n- 对于每一条总结,尽量讲清楚前因后果,以及话题的结论:是什么、为什么、怎么做。如果用户没有讲到细节,则可以不用这么做。\n\n## 群聊记录:\n\n{messages_text}\n\n---\n\n## 重要:必须返回标准 JSON 格式\n\n严格遵守以下规则:\n\n1. 只使用英文双引号 `\"` ,不要使用中文引号 `\"` `\"`\n2. 字符串内容中的引号必须转义为 `\\\"`\n3. 多个对象之间用逗号分隔\n4. 数组元素之间用逗号分隔\n5. 不要在 JSON 外添加任何文字说明\n6. 描述内容避免使用特殊符号,用普通文字表达\n\n### 返回格式示例:\n\n```json\n[\n {{\n \"topic\": \"话题名称\",\n \"contributors\": [\"用户1\", \"用户2\"],\n \"detail\": \"话题描述内容\"\n }},\n {{\n \"topic\": \"另一个话题\",\n \"contributors\": [\"用户3\", \"用户4\"],\n \"detail\": \"另一个话题的描述\"\n }}\n]\n```\n\n**注意**:返回的内容必须是纯 JSON,不要包含 markdown 代码块标记或其他格式。" + "group_list_mode": { + "description": "群聊权限模式", + "type": "string", + "options": ["whitelist", "blacklist", "none"], + "default": "none", + "hint": "whitelist: 仅允许列表内群;blacklist: 拒绝列表内群;none: 不限制" + }, + "group_list": { + "type": "list", + "description": "群组白/黑名单列表", + "default": [], + "hint": "黑白名单模式下使用的群组列表。支持填写 AstrBot UMO (如 xxxxx:GroupMessage:123456) 或 纯群号 (如 123456,将尝试自动匹配)。可以使用 /sid 命令查看当前会话的 UMO。", + "items": { + "type": "string" + } + }, + "analysis_days": { + "type": "int", + "description": "默认分析天数", + "default": 1, + "hint": "默认分析最近几天的消息,建议1-7天" + }, + "max_messages": { + "type": "int", + "description": "单次分析的获取最大消息条数基准", + "default": 1000, + "hint": "单次分析的获取最大消息条数基准值,如果当天群聊消息过多,已经获取到足够的消息(达到 max_messages),停止获取,获取到的消息数量是最新一次获取到的超过 max_messages 的消息数量,建议500-2000" + }, + "min_messages_threshold": { + "type": "int", + "description": "最小消息数阈值", + "default": 50, + "hint": "进行分析所需的最小消息数量" + }, + "bot_self_ids": { + "type": "list", + "description": "群分析时屏蔽的用户ID列表", + "default": [], + "hint": "填写后可启用自动分析功能。可以填写用于自动分析的机器人 ID(在不同平台上可能是数字或字符串)、多消息平台 ID、不希望出现于群分析中的其他人的 ID 等。", + "items": { + "type": "string" + } + }, + "enable_user_card": { + "type": "bool", + "description": "使用用户群名片", + "default": false, + "hint": "在分析中优先使用用户的群名片作为昵称显示" + }, + "output_format": { + "type": "string", + "description": "输出格式", + "default": "image", + "hint": "分析报告的输出格式:image(图片)、text(文本)、pdf(PDF文件)。使用 PDF 需要额外配置,根据文件中的 PDF_功能说明.md 进行配置" + }, + "report_template": { + "type": "string", + "description": "报告模板", + "default": "scrapbook", + "hint": "分析报告使用的HTML模板名称,使用 `/设置模板` 查看使用指南,使用`/查看模板` 命令查看模板样式效果" + }, + "debug_mode": { + "type": "bool", + "description": "调试模式(建议关闭)", + "default": false, + "hint": "启用后,会在 plugin_data/debug_data 目录下保存每次分析的平台 API 原始历史消息和 Prompt 原文,用于调试和优化提示词。" } } }, - "user_title_analysis_prompts": { - "description": "用户称号分析提示词模板", + "auto_analysis": { + "description": "自动分析设置", "type": "object", - "hint": "用户称号分析提示词模板,可自定义修改,保留变量 {users_text} 写法不要更改,需要写 JSON 块请使用双花括号 {{ ... }} 兼容当前编辑存在的问题。非常推荐根据实际群聊情况进行优化,保留一定提示样本示例供模型参考", + "hint": "定时自动分析相关配置,包括启用开关、分析时间和并发数", "items": { - "user_title_prompt": { - "description": "默认用户称号分析提示词", - "type": "text", - "editor_mode": true, - "editor_language": "markdown", - "default": "请为以下群友分配合适的称号和 MBTI 类型。\n\n## 规则:\n\n- 每个人只能有一个称号\n- 每个称号只能给一个人\n\n## 可选称号:\n\n- **龙王**: 发言频繁但内容轻松的人\n- **技术专家**: 经常讨论技术话题的人\n- **夜猫子**: 经常在深夜发言的人\n- **表情包军火库**: 经常发表情的人\n- **沉默终结者**: 经常开启话题的人\n- **评论家**: 平均发言长度很长的人\n- **阳角**: 在群里很有影响力的人\n- **互动达人**: 经常回复别人的人\n- *...(你可以自行进行拓展添加)*\n\n## 用户数据:\n\n{users_text}\n\n---\n\n### 返回格式示例:\n\n```json\n[\n {{\n \"name\": \"用户名\",\n \"user_id\": \"123456789\",\n \"title\": \"称号\",\n \"mbti\": \"MBTI类型\",\n \"reason\": \"获得此称号的原因\"\n }}\n]\n```\n\n**注意**:请以纯 JSON 格式返回,不要包含 markdown 代码块标记。" + "enable_auto_analysis": { + "type": "bool", + "description": "启用定时自动分析", + "default": false, + "hint": "是否在指定时间自动进行群聊分析,启用需要填写下面的机器人ID号,并且确保群聊号在 enabled_groups 配置中,否则获取不到实例,不会自动分析" + }, + "auto_analysis_time": { + "type": "list", + "description": "自动分析时间列表", + "default": ["09:00"], + "hint": "每日自动分析的时间点列表,格式HH:MM(例如 23:00 ),支持多个时间点", + "items": { + "type": "string" + } + }, + "max_concurrent_tasks": { + "type": "int", + "description": "自动分析最大并发数", + "default": 3, + "hint": "同时进行的群聊分析任务数量,建议根据机器性能调整,过高可能导致LLM API RPM 超出限制,卡顿或被风控" } } }, - "golden_quote_analysis_prompts": { - "description": "金句分析提示词模板", + "llm": { + "description": "LLM 设置", "type": "object", - "hint": "金句分析提示词模板,可自定义修改,保留变量 {max_golden_quotes} 和 {messages_text} 写法不要更改,需要写 JSON 块请使用双花括号 {{ ... }} 兼容当前编辑存在的问题。非常推荐根据实际群聊情况进行优化,保留一定提示样本示例供模型参考", + "hint": "大语言模型相关配置,包括 Provider 选择、重试策略和 Token 限制", "items": { - "golden_quote_prompt": { - "description": "默认金句分析提示词", - "type": "text", - "editor_mode": true, - "editor_language": "markdown", - "default": "请从以下群聊记录中挑选出 **{max_golden_quotes}** 句最具冲击力、最令人惊叹的「金句」。\n\n## 金句标准:\n\n- **核心标准**:**逆天的神人发言**,即具备颠覆常识的脑洞、逻辑跳脱的表达或强烈反差感的原创内容\n- **典型特征**:包含某些争议话题元素、夸张类比、反常规结论、一本正经的「胡说八道」或突破语境的清奇思路,并且具备一定的冲击力,让人印象深刻\n\n## 对于每个金句,请提供:\n\n1. **原文内容**(完整保留发言细节)\n2. **发言人昵称**\n3. **选择理由**(具体说明其「逆天」之处,如逻辑颠覆点/脑洞角度/反差感/争议话题元素)\n\n## 严格约束:\n\n- 优先筛选 **逆天指数最高** 的内容:\n - 发情、性压抑话题 > 争议话题 > 元素级 > 颠覆认知级 > 逻辑跳脱级 > 趣味调侃级\n - 剔除单纯玩梗或网络热词堆砌的普通发言\n- 重点标记包含极端类比、反常识论证或无厘头结论的内容,并且包含一定的争议话题元素\n\n## 群聊记录:\n\n{messages_text}\n\n---\n\n### 返回格式示例:\n\n```json\n[\n {{\n \"content\": \"金句原文\",\n \"sender\": \"发言人昵称\",\n \"reason\": \"选择这句话的理由(需明确说明逆天特质)\"\n }}\n]\n```\n\n**注意**:请以纯 JSON 格式返回,不要包含 markdown 代码块标记。" + "llm_provider_id": { + "type": "string", + "description": "LLM Provider ID(用于所有分析任务)", + "_special": "select_provider", + "default": "", + "hint": "选择用于群聊分析的 LLM Provider。留空则使用当前会话的默认 Provider" + }, + "topic_provider_id": { + "type": "string", + "description": "话题分析专用 Provider ID", + "_special": "select_provider", + "default": "", + "hint": "专门用于话题分析的 Provider。留空则使用主 LLM Provider" + }, + "user_title_provider_id": { + "type": "string", + "description": "用户称号分析专用 Provider ID", + "_special": "select_provider", + "default": "", + "hint": "专门用于用户称号分析的 Provider。留空则使用主 LLM Provider" + }, + "golden_quote_provider_id": { + "type": "string", + "description": "金句分析专用 Provider ID", + "_special": "select_provider", + "default": "", + "hint": "专门用于金句分析的 Provider。留空则使用主 LLM Provider" + }, + "llm_retries": { + "type": "int", + "description": "LLM 请求重试次数", + "default": 2, + "hint": "当请求超时或失败时自动重试的次数,建议设置为1~3之间。" + }, + "llm_backoff": { + "type": "int", + "description": "LLM 请求重试退避基值(秒)", + "default": 2, + "hint": "重试之间的基准等待时间(秒),实际等待时间为基值乘以尝试次数。" + }, + "topic_max_tokens": { + "type": "int", + "description": "(兼容部分提供商)话题分析最大 Token 数", + "default": 12288, + "hint": "(兼容部分提供商,实测大部分模型调整后没有明显效果)话题分析时 LLM 能生成的最大 token 数量。当分析内容较多或者分析提示词复杂时,建议适当调大此值以保证输出质量。" + }, + "golden_quote_max_tokens": { + "type": "int", + "description": "(兼容部分提供商)金句分析最大 Token 数", + "default": 4096, + "hint": "(兼容部分提供商,实测大部分模型调整后没有明显效果)金句分析时 LLM 能生成的最大 token 数量。当分析内容较多或者分析提示词复杂时,建议适当调大此值以保证输出质量。" + }, + "user_title_max_tokens": { + "type": "int", + "description": "(兼容部分提供商)用户称号分析最大 Token 数", + "default": 4096, + "hint": "(兼容部分提供商,实测大部分模型调整后没有明显效果)用户称号分析时 LLM 能生成的最大 token 数量。当分析内容较多或者分析提示词复杂时,建议适当调大此值以保证输出质量。" + } + } + }, + "analysis_features": { + "description": "分析功能开关", + "type": "object", + "hint": "控制各项 LLM 分析功能的启用状态和数量限制", + "items": { + "topic_analysis_enabled": { + "type": "bool", + "description": "启用话题分析", + "default": true, + "hint": "是否使用LLM进行智能话题分析" + }, + "user_title_analysis_enabled": { + "type": "bool", + "description": "启用用户称号分析", + "default": true, + "hint": "是否使用LLM进行用户称号分析" + }, + "golden_quote_analysis_enabled": { + "type": "bool", + "description": "启用金句分析", + "default": true, + "hint": "是否使用LLM进行金句分析" + }, + "max_topics": { + "type": "int", + "description": "最大话题数量", + "default": 5, + "hint": "分析报告中显示的最大话题数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。" + }, + "max_user_titles": { + "type": "int", + "description": "最大用户称号数量", + "default": 8, + "hint": "分析报告中显示的最大用户称号数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。" + }, + "max_golden_quotes": { + "type": "int", + "description": "最大金句数量", + "default": 5, + "hint": "分析报告中显示的最大金句数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。" + } + } + }, + "incremental": { + "description": "增量分析设置", + "type": "object", + "hint": "增量分析模式相关配置,启用后插件会在一天内多次进行小规模分析并累积结果,适用于消息量大的群聊", + "items": { + "incremental_enabled": { + "type": "bool", + "description": "启用增量分析模式", + "default": false, + "hint": "启用后,插件会在一天内多次进行小规模分析并累积结果,最终在报告时间汇总输出。适用于消息量大的群聊,可显著提升分析覆盖率和24小时活跃图表准确性。关闭时保持传统的单次全量分析模式。" + }, + "incremental_interval_minutes": { + "type": "int", + "description": "增量分析间隔(分钟)", + "default": 120, + "hint": "每隔多少分钟执行一次增量分析。建议60-180分钟,间隔越短分析越精细但消耗更多 Token。" + }, + "incremental_max_daily_analyses": { + "type": "int", + "description": "每日最大增量分析次数", + "default": 8, + "hint": "一天内最多执行多少次增量分析。达到上限后当天不再触发新的增量分析,但仍会在报告时间生成最终报告。建议4-12次。" + }, + "incremental_max_messages": { + "type": "int", + "description": "单次增量分析最大消息数", + "default": 300, + "hint": "每次增量分析最多处理的消息条数。数值越大单次分析越全面但 Token 消耗越高。建议200-500。" + }, + "incremental_min_messages": { + "type": "int", + "description": "增量分析最小消息数阈值", + "default": 20, + "hint": "当自上次分析以来的新消息数低于此阈值时,跳过本次增量分析以节省 Token。建议10-50。" + }, + "incremental_topics_per_batch": { + "type": "int", + "description": "单次增量提取话题数", + "default": 3, + "hint": "每次增量分析最多提取的话题数量。最终报告会汇总所有批次的话题。建议2-5。" + }, + "incremental_quotes_per_batch": { + "type": "int", + "description": "单次增量提取金句数", + "default": 3, + "hint": "每次增量分析最多提取的金句数量。最终报告会汇总所有批次的金句。建议2-5。" + }, + "incremental_active_start_hour": { + "type": "int", + "description": "增量分析活跃起始时段", + "default": 8, + "hint": "增量分析的活跃时段起始小时(24小时制)。在活跃时段之外不会触发增量分析,避免凌晨等低活跃时段浪费分析配额。" + }, + "incremental_active_end_hour": { + "type": "int", + "description": "增量分析活跃结束时段", + "default": 23, + "hint": "增量分析的活跃时段结束小时(24小时制)。超过此时段后不再触发增量分析。" + }, + "incremental_stagger_seconds": { + "type": "int", + "description": "多群增量分析交错间隔(秒)", + "default": 30, + "hint": "当同时对多个群进行增量分析时,每个群之间的延迟间隔(秒),用于均匀分散 API 请求压力。建议15-60秒。" + } + } + }, + "pdf": { + "description": "PDF 设置", + "type": "object", + "hint": "PDF 报告输出相关配置,包括输出目录、浏览器路径和文件名格式", + "items": { + "pdf_output_dir": { + "type": "string", + "description": "PDF输出目录", + "default": "data/plugins/astrbot_plugin_qq_group_daily_analysis/reports", + "hint": "PDF报告文件的保存目录" + }, + "browser_path": { + "type": "string", + "description": "自定义浏览器路径", + "default": "", + "hint": "要填写的话请你清楚自己在干什么。自定义浏览器的可执行文件路径(如 Chrome 或 Edge 的 .exe 文件)。提示:如果是在网页后台设置,则直接输入普通路径即可(如 C:\\Program Files\\...);如果是手动编辑 config.json 文件,请务必使用双反斜杠 '\\\\' 分隔路径。" + }, + "pdf_filename_format": { + "type": "string", + "description": "PDF文件名格式", + "default": "群聊分析报告_{group_id}_{date}.pdf", + "hint": "PDF文件名格式,支持变量:{group_id}(群号)、{date}(日期)" + } + } + }, + "prompts": { + "description": "提示词模板", + "type": "object", + "hint": "LLM 分析使用的提示词模板配置,可自定义各项分析的提示词内容", + "items": { + "topic_analysis_prompts": { + "description": "话题分析提示词模板", + "type": "object", + "hint": "话题分析提示词模板,可自定义修改,保留变量 {max_topics} 和 {messages_text} 写法不要更改,需要写 JSON 块请使用双花括号 {{ ... }} 兼容当前编辑存在的问题。非常推荐根据实际群聊情况进行优化,保留一定提示样本示例供模型参考", + "items": { + "topic_prompt": { + "description": "默认话题分析提示词", + "type": "text", + "editor_mode": true, + "editor_language": "markdown", + "default": "你是一个帮我进行群聊信息总结的助手,生成总结内容时,你需要严格遵守下面的几个准则:\n\n请分析接下来提供的群聊记录,提取出最多 **{max_topics}** 个主要话题。\n\n## 对于每个话题,请提供:\n\n1. **话题名称**(突出主题内容,尽量简明扼要)\n2. **主要参与者**(最多5人)\n3. **话题详细描述**(包含关键信息和结论)\n\n## 注意事项:\n\n- 对于比较有价值的点,稍微用一两句话详细讲讲,比如不要生成 \"Nolan 和 SOV 讨论了 galgame 中关于性符号的衍生情况\" 这种宽泛的内容,而是生成更加具体的讨论内容,让其他人只看这个消息就能知道讨论中有价值的、有营养的信息。\n- 对于其中的部分信息,你需要特意提到主题施加的主体是谁,是哪个群友做了什么事情,而不要直接生成和群友没有关系的语句。\n- 对于每一条总结,尽量讲清楚前因后果,以及话题的结论:是什么、为什么、怎么做。如果用户没有讲到细节,则可以不用这么做。\n\n## 群聊记录:\n\n{messages_text}\n\n---\n\n## 重要:必须返回标准 JSON 格式\n\n严格遵守以下规则:\n\n1. 只使用英文双引号 `\"` ,不要使用中文引号 `\"` `\"`\n2. 字符串内容中的引号必须转义为 `\\\"`\n3. 多个对象之间用逗号分隔\n4. 数组元素之间用逗号分隔\n5. 不要在 JSON 外添加任何文字说明\n6. 描述内容避免使用特殊符号,用普通文字表达\n\n### 返回格式示例:\n\n```json\n[\n {{\n \"topic\": \"话题名称\",\n \"contributors\": [\"用户1\", \"用户2\"],\n \"detail\": \"话题描述内容\"\n }},\n {{\n \"topic\": \"另一个话题\",\n \"contributors\": [\"用户3\", \"用户4\"],\n \"detail\": \"另一个话题的描述\"\n }}\n]\n```\n\n**注意**:返回的内容必须是纯 JSON,不要包含 markdown 代码块标记或其他格式。" + } + } + }, + "user_title_analysis_prompts": { + "description": "用户称号分析提示词模板", + "type": "object", + "hint": "用户称号分析提示词模板,可自定义修改,保留变量 {users_text} 写法不要更改,需要写 JSON 块请使用双花括号 {{ ... }} 兼容当前编辑存在的问题。非常推荐根据实际群聊情况进行优化,保留一定提示样本示例供模型参考", + "items": { + "user_title_prompt": { + "description": "默认用户称号分析提示词", + "type": "text", + "editor_mode": true, + "editor_language": "markdown", + "default": "请为以下群友分配合适的称号和 MBTI 类型。\n\n## 规则:\n\n- 每个人只能有一个称号\n- 每个称号只能给一个人\n\n## 可选称号:\n\n- **龙王**: 发言频繁但内容轻松的人\n- **技术专家**: 经常讨论技术话题的人\n- **夜猫子**: 经常在深夜发言的人\n- **表情包军火库**: 经常发表情的人\n- **沉默终结者**: 经常开启话题的人\n- **评论家**: 平均发言长度很长的人\n- **阳角**: 在群里很有影响力的人\n- **互动达人**: 经常回复别人的人\n- *...(你可以自行进行拓展添加)*\n\n## 用户数据:\n\n{users_text}\n\n---\n\n### 返回格式示例:\n\n```json\n[\n {{\n \"name\": \"用户名\",\n \"user_id\": \"123456789\",\n \"title\": \"称号\",\n \"mbti\": \"MBTI类型\",\n \"reason\": \"获得此称号的原因\"\n }}\n]\n```\n\n**注意**:请以纯 JSON 格式返回,不要包含 markdown 代码块标记。" + } + } + }, + "golden_quote_analysis_prompts": { + "description": "金句分析提示词模板", + "type": "object", + "hint": "金句分析提示词模板,可自定义修改,保留变量 {max_golden_quotes} 和 {messages_text} 写法不要更改,需要写 JSON 块请使用双花括号 {{ ... }} 兼容当前编辑存在的问题。非常推荐根据实际群聊情况进行优化,保留一定提示样本示例供模型参考", + "items": { + "golden_quote_prompt": { + "description": "默认金句分析提示词", + "type": "text", + "editor_mode": true, + "editor_language": "markdown", + "default": "请从以下群聊记录中挑选出 **{max_golden_quotes}** 句最具冲击力、最令人惊叹的「金句」。\n\n## 金句标准:\n\n- **核心标准**:**逆天的神人发言**,即具备颠覆常识的脑洞、逻辑跳脱的表达或强烈反差感的原创内容\n- **典型特征**:包含某些争议话题元素、夸张类比、反常规结论、一本正经的「胡说八道」或突破语境的清奇思路,并且具备一定的冲击力,让人印象深刻\n\n## 对于每个金句,请提供:\n\n1. **原文内容**(完整保留发言细节)\n2. **发言人昵称**\n3. **选择理由**(具体说明其「逆天」之处,如逻辑颠覆点/脑洞角度/反差感/争议话题元素)\n\n## 严格约束:\n\n- 优先筛选 **逆天指数最高** 的内容:\n - 发情、性压抑话题 > 争议话题 > 元素级 > 颠覆认知级 > 逻辑跳脱级 > 趣味调侃级\n - 剔除单纯玩梗或网络热词堆砌的普通发言\n- 重点标记包含极端类比、反常识论证或无厘头结论的内容,并且包含一定的争议话题元素\n\n## 群聊记录:\n\n{messages_text}\n\n---\n\n### 返回格式示例:\n\n```json\n[\n {{\n \"content\": \"金句原文\",\n \"sender\": \"发言人昵称\",\n \"reason\": \"选择这句话的理由(需明确说明逆天特质)\"\n }}\n]\n```\n\n**注意**:请以纯 JSON 格式返回,不要包含 markdown 代码块标记。" + } + } } } } diff --git a/src/infrastructure/config/config_manager.py b/src/infrastructure/config/config_manager.py index dde751c..9491140 100644 --- a/src/infrastructure/config/config_manager.py +++ b/src/infrastructure/config/config_manager.py @@ -10,7 +10,17 @@ from astrbot.core.utils.astrbot_path import get_astrbot_data_path class ConfigManager: - """配置管理器""" + """配置管理器 + + 配置结构采用分组嵌套方式,顶层分为以下分组: + - basic: 基础设置 + - auto_analysis: 自动分析设置 + - llm: LLM 设置 + - analysis_features: 分析功能开关 + - incremental: 增量分析设置 + - pdf: PDF 设置 + - prompts: 提示词模板 + """ def __init__(self, config: AstrBotConfig): self.config = config @@ -18,13 +28,23 @@ class ConfigManager: self._playwright_version = None self._check_playwright_availability() + def _get_group(self, group: str) -> dict: + """获取指定分组的配置字典,不存在时返回空字典""" + return self.config.get(group, {}) + + def _ensure_group(self, group: str) -> dict: + """确保指定分组存在并返回其字典引用""" + if group not in self.config: + self.config[group] = {} + return self.config[group] + def get_group_list_mode(self) -> str: """获取群组列表模式 (whitelist/blacklist/none)""" - return self.config.get("group_list_mode", "none") + return self._get_group("basic").get("group_list_mode", "none") def get_group_list(self) -> list[str]: """获取群组列表(用于黑白名单)""" - return self.config.get("group_list", []) + return self._get_group("basic").get("group_list", []) def is_group_allowed(self, group_id_or_umo: str) -> bool: """ @@ -59,21 +79,23 @@ class ConfigManager: def get_max_messages(self) -> int: """获取最大消息数量""" - return self.config.get("max_messages", 1000) + return self._get_group("basic").get("max_messages", 1000) def get_analysis_days(self) -> int: """获取分析天数""" - return self.config.get("analysis_days", 1) + return self._get_group("basic").get("analysis_days", 1) def get_auto_analysis_time(self) -> list[str]: """获取自动分析时间列表""" - val = self.config.get("auto_analysis_time", ["09:00"]) + group = self._get_group("auto_analysis") + val = group.get("auto_analysis_time", ["09:00"]) # 兼容旧版本字符串配置 if isinstance(val, str): val_list = [val] # 自动修复配置格式 try: - self.config["auto_analysis_time"] = val_list + auto_group = self._ensure_group("auto_analysis") + auto_group["auto_analysis_time"] = val_list self.config.save_config() logger.info(f"自动修复配置格式 auto_analysis_time: {val} -> {val_list}") except Exception as e: @@ -83,79 +105,79 @@ class ConfigManager: def get_enable_auto_analysis(self) -> bool: """获取是否启用自动分析""" - return self.config.get("enable_auto_analysis", False) + return self._get_group("auto_analysis").get("enable_auto_analysis", False) def get_output_format(self) -> str: """获取输出格式""" - return self.config.get("output_format", "image") + return self._get_group("basic").get("output_format", "image") def get_min_messages_threshold(self) -> int: """获取最小消息阈值""" - return self.config.get("min_messages_threshold", 50) + return self._get_group("basic").get("min_messages_threshold", 50) def get_topic_analysis_enabled(self) -> bool: """获取是否启用话题分析""" - return self.config.get("topic_analysis_enabled", True) + return self._get_group("analysis_features").get("topic_analysis_enabled", True) def get_user_title_analysis_enabled(self) -> bool: """获取是否启用用户称号分析""" - return self.config.get("user_title_analysis_enabled", True) + return self._get_group("analysis_features").get("user_title_analysis_enabled", True) def get_golden_quote_analysis_enabled(self) -> bool: """获取是否启用金句分析""" - return self.config.get("golden_quote_analysis_enabled", True) + return self._get_group("analysis_features").get("golden_quote_analysis_enabled", True) def get_max_topics(self) -> int: """获取最大话题数量""" - return self.config.get("max_topics", 5) + return self._get_group("analysis_features").get("max_topics", 5) def get_max_user_titles(self) -> int: """获取最大用户称号数量""" - return self.config.get("max_user_titles", 8) + return self._get_group("analysis_features").get("max_user_titles", 8) def get_max_golden_quotes(self) -> int: """获取最大金句数量""" - return self.config.get("max_golden_quotes", 5) + return self._get_group("analysis_features").get("max_golden_quotes", 5) def get_llm_retries(self) -> int: """获取LLM请求重试次数""" - return self.config.get("llm_retries", 2) + return self._get_group("llm").get("llm_retries", 2) def get_llm_backoff(self) -> int: """获取LLM请求重试退避基值(秒),实际退避会乘以尝试次数""" - return self.config.get("llm_backoff", 2) + return self._get_group("llm").get("llm_backoff", 2) def get_topic_max_tokens(self) -> int: """获取话题分析最大token数""" - return self.config.get("topic_max_tokens", 12288) + return self._get_group("llm").get("topic_max_tokens", 12288) def get_golden_quote_max_tokens(self) -> int: """获取金句分析最大token数""" - return self.config.get("golden_quote_max_tokens", 4096) + return self._get_group("llm").get("golden_quote_max_tokens", 4096) def get_user_title_max_tokens(self) -> int: """获取用户称号分析最大token数""" - return self.config.get("user_title_max_tokens", 4096) + return self._get_group("llm").get("user_title_max_tokens", 4096) def get_debug_mode(self) -> bool: """获取是否启用调试模式""" - return self.config.get("debug_mode", False) + return self._get_group("basic").get("debug_mode", False) def get_llm_provider_id(self) -> str: """获取主 LLM Provider ID""" - return self.config.get("llm_provider_id", "") + return self._get_group("llm").get("llm_provider_id", "") def get_topic_provider_id(self) -> str: """获取话题分析专用 Provider ID""" - return self.config.get("topic_provider_id", "") + return self._get_group("llm").get("topic_provider_id", "") def get_user_title_provider_id(self) -> str: """获取用户称号分析专用 Provider ID""" - return self.config.get("user_title_provider_id", "") + return self._get_group("llm").get("user_title_provider_id", "") def get_golden_quote_provider_id(self) -> str: """获取金句分析专用 Provider ID""" - return self.config.get("golden_quote_provider_id", "") + return self._get_group("llm").get("golden_quote_provider_id", "") def get_pdf_output_dir(self) -> str: """获取PDF输出目录""" @@ -163,210 +185,220 @@ class ConfigManager: plugin_name = "astrbot_plugin_qq_group_daily_analysis" data_path = get_astrbot_data_path() default_path = data_path / "plugin_data" / plugin_name / "reports" - return self.config.get("pdf_output_dir", str(default_path)) + return self._get_group("pdf").get("pdf_output_dir", str(default_path)) except Exception: - return self.config.get( + return self._get_group("pdf").get( "pdf_output_dir", "data/plugins/astrbot_plugin_qq_group_daily_analysis/reports", ) def get_bot_self_ids(self) -> list: """获取机器人自身的 ID 列表 (兼容 bot_qq_ids)""" - ids = self.config.get("bot_self_ids", []) + basic = self._get_group("basic") + ids = basic.get("bot_self_ids", []) if not ids: - ids = self.config.get("bot_qq_ids", []) + ids = basic.get("bot_qq_ids", []) return ids def get_pdf_filename_format(self) -> str: """获取PDF文件名格式""" - return self.config.get( + return self._get_group("pdf").get( "pdf_filename_format", "群聊分析报告_{group_id}_{date}.pdf" ) def get_topic_analysis_prompt(self, style: str = "topic_prompt") -> str: """获取话题分析提示词模板""" - prompts_config = self.config.get("topic_analysis_prompts", {}) - prompt = prompts_config.get(style, "topic_prompt") + prompts_config = self._get_group("prompts").get("topic_analysis_prompts", {}) + prompt = prompts_config.get(style, "") if prompt: return prompt - return self.config.get("topic_analysis_prompt", "") + return "" def get_user_title_analysis_prompt(self, style: str = "user_title_prompt") -> str: """获取用户称号分析提示词模板""" - prompts_config = self.config.get("user_title_analysis_prompts", {}) - prompt = prompts_config.get(style, "user_title_prompt") + prompts_config = self._get_group("prompts").get("user_title_analysis_prompts", {}) + prompt = prompts_config.get(style, "") if prompt: return prompt - return self.config.get("user_title_analysis_prompt", "") + return "" def get_golden_quote_analysis_prompt( self, style: str = "golden_quote_prompt" ) -> str: """获取金句分析提示词模板""" - prompts_config = self.config.get("golden_quote_analysis_prompts", {}) - prompt = prompts_config.get(style, "golden_quote_prompt") + prompts_config = self._get_group("prompts").get("golden_quote_analysis_prompts", {}) + prompt = prompts_config.get(style, "") if prompt: return prompt - return self.config.get("golden_quote_analysis_prompt", "") + return "" def set_topic_analysis_prompt(self, prompt: str): """设置话题分析提示词模板""" - self.config["topic_analysis_prompt"] = prompt + prompts = self._ensure_group("prompts") + if "topic_analysis_prompts" not in prompts: + prompts["topic_analysis_prompts"] = {} + prompts["topic_analysis_prompts"]["topic_prompt"] = prompt self.config.save_config() def set_user_title_analysis_prompt(self, prompt: str): """设置用户称号分析提示词模板""" - self.config["user_title_analysis_prompt"] = prompt + prompts = self._ensure_group("prompts") + if "user_title_analysis_prompts" not in prompts: + prompts["user_title_analysis_prompts"] = {} + prompts["user_title_analysis_prompts"]["user_title_prompt"] = prompt self.config.save_config() def set_golden_quote_analysis_prompt(self, prompt: str): """设置金句分析提示词模板""" - self.config["golden_quote_analysis_prompt"] = prompt + prompts = self._ensure_group("prompts") + if "golden_quote_analysis_prompts" not in prompts: + prompts["golden_quote_analysis_prompts"] = {} + prompts["golden_quote_analysis_prompts"]["golden_quote_prompt"] = prompt self.config.save_config() def set_output_format(self, format_type: str): """设置输出格式""" - self.config["output_format"] = format_type + self._ensure_group("basic")["output_format"] = format_type self.config.save_config() def set_group_list_mode(self, mode: str): """设置群组列表模式""" - self.config["group_list_mode"] = mode + self._ensure_group("basic")["group_list_mode"] = mode self.config.save_config() def set_group_list(self, groups: list[str]): """设置群组列表""" - self.config["group_list"] = groups + self._ensure_group("basic")["group_list"] = groups self.config.save_config() def get_max_concurrent_tasks(self) -> int: """获取自动分析最大并发数""" - return self.config.get("max_concurrent_tasks", 3) + return self._get_group("auto_analysis").get("max_concurrent_tasks", 3) def set_max_concurrent_tasks(self, count: int): """设置自动分析最大并发数""" - self.config["max_concurrent_tasks"] = count + self._ensure_group("auto_analysis")["max_concurrent_tasks"] = count self.config.save_config() def set_max_messages(self, count: int): """设置最大消息数量""" - self.config["max_messages"] = count + self._ensure_group("basic")["max_messages"] = count self.config.save_config() def set_analysis_days(self, days: int): """设置分析天数""" - self.config["analysis_days"] = days + self._ensure_group("basic")["analysis_days"] = days self.config.save_config() def set_auto_analysis_time(self, time_val: str | list[str]): """设置自动分析时间""" - self.config["auto_analysis_time"] = time_val + self._ensure_group("auto_analysis")["auto_analysis_time"] = time_val self.config.save_config() def set_enable_auto_analysis(self, enabled: bool): """设置是否启用自动分析""" - self.config["enable_auto_analysis"] = enabled + self._ensure_group("auto_analysis")["enable_auto_analysis"] = enabled self.config.save_config() def set_min_messages_threshold(self, threshold: int): """设置最小消息阈值""" - self.config["min_messages_threshold"] = threshold + self._ensure_group("basic")["min_messages_threshold"] = threshold self.config.save_config() def set_topic_analysis_enabled(self, enabled: bool): """设置是否启用话题分析""" - self.config["topic_analysis_enabled"] = enabled + self._ensure_group("analysis_features")["topic_analysis_enabled"] = enabled self.config.save_config() def set_user_title_analysis_enabled(self, enabled: bool): """设置是否启用用户称号分析""" - self.config["user_title_analysis_enabled"] = enabled + self._ensure_group("analysis_features")["user_title_analysis_enabled"] = enabled self.config.save_config() def set_golden_quote_analysis_enabled(self, enabled: bool): """设置是否启用金句分析""" - self.config["golden_quote_analysis_enabled"] = enabled + self._ensure_group("analysis_features")["golden_quote_analysis_enabled"] = enabled self.config.save_config() def set_max_topics(self, count: int): """设置最大话题数量""" - self.config["max_topics"] = count + self._ensure_group("analysis_features")["max_topics"] = count self.config.save_config() def set_max_user_titles(self, count: int): """设置最大用户称号数量""" - self.config["max_user_titles"] = count + self._ensure_group("analysis_features")["max_user_titles"] = count self.config.save_config() def set_max_golden_quotes(self, count: int): """设置最大金句数量""" - self.config["max_golden_quotes"] = count + self._ensure_group("analysis_features")["max_golden_quotes"] = count self.config.save_config() def set_pdf_output_dir(self, directory: str): """设置PDF输出目录""" - self.config["pdf_output_dir"] = directory + self._ensure_group("pdf")["pdf_output_dir"] = directory self.config.save_config() def set_pdf_filename_format(self, format_str: str): """设置PDF文件名格式""" - self.config["pdf_filename_format"] = format_str + self._ensure_group("pdf")["pdf_filename_format"] = format_str self.config.save_config() def get_report_template(self) -> str: """获取报告模板名称""" - return self.config.get("report_template", "scrapbook") + return self._get_group("basic").get("report_template", "scrapbook") def set_report_template(self, template_name: str): """设置报告模板名称""" - self.config["report_template"] = template_name + self._ensure_group("basic")["report_template"] = template_name self.config.save_config() def get_enable_user_card(self) -> bool: """获取是否使用用户群名片""" - return self.config.get("enable_user_card", False) + return self._get_group("basic").get("enable_user_card", False) # ========== 增量分析配置 ========== def get_incremental_enabled(self) -> bool: """获取是否启用增量分析模式""" - return self.config.get("incremental_enabled", False) + return self._get_group("incremental").get("incremental_enabled", False) def get_incremental_interval_minutes(self) -> int: """获取增量分析间隔(分钟)""" - return self.config.get("incremental_interval_minutes", 120) + return self._get_group("incremental").get("incremental_interval_minutes", 120) def get_incremental_max_daily_analyses(self) -> int: """获取每天最大增量分析次数""" - return self.config.get("incremental_max_daily_analyses", 8) + return self._get_group("incremental").get("incremental_max_daily_analyses", 8) def get_incremental_max_messages(self) -> int: """获取单次增量分析的最大消息数""" - return self.config.get("incremental_max_messages", 300) + return self._get_group("incremental").get("incremental_max_messages", 300) def get_incremental_min_messages(self) -> int: """获取触发增量分析的最小消息数阈值""" - return self.config.get("incremental_min_messages", 20) + return self._get_group("incremental").get("incremental_min_messages", 20) def get_incremental_topics_per_batch(self) -> int: """获取单次增量分析提取的最大话题数""" - return self.config.get("incremental_topics_per_batch", 3) + return self._get_group("incremental").get("incremental_topics_per_batch", 3) def get_incremental_quotes_per_batch(self) -> int: """获取单次增量分析提取的最大金句数""" - return self.config.get("incremental_quotes_per_batch", 3) + return self._get_group("incremental").get("incremental_quotes_per_batch", 3) def get_incremental_active_start_hour(self) -> int: """获取增量分析活跃时段起始小时(24小时制)""" - return self.config.get("incremental_active_start_hour", 8) + return self._get_group("incremental").get("incremental_active_start_hour", 8) def get_incremental_active_end_hour(self) -> int: """获取增量分析活跃时段结束小时(24小时制)""" - return self.config.get("incremental_active_end_hour", 23) + return self._get_group("incremental").get("incremental_active_end_hour", 23) def get_incremental_stagger_seconds(self) -> int: """获取多群增量分析的交错间隔(秒),避免 API 压力""" - return self.config.get("incremental_stagger_seconds", 30) + return self._get_group("incremental").get("incremental_stagger_seconds", 30) @property def playwright_available(self) -> bool: @@ -407,11 +439,11 @@ class ConfigManager: def get_browser_path(self) -> str: """获取自定义浏览器路径""" - return self.config.get("browser_path", "") + return self._get_group("pdf").get("browser_path", "") def set_browser_path(self, path: str): """设置自定义浏览器路径""" - self.config["browser_path"] = path + self._ensure_group("pdf")["browser_path"] = path self.config.save_config() def reload_playwright(self) -> bool: