From 269c434778dba21204e186bbf0d32f30aa602cf9 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 21 Apr 2026 13:43:13 +0800 Subject: [PATCH 1/4] =?UTF-8?q?remove:=E7=A7=BB=E9=99=A4=E5=86=85=E9=83=A8?= =?UTF-8?q?=E5=9B=BA=E5=AE=9A=E6=B8=A9=E5=BA=A6=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/image_system/image_manager.py | 2 - src/emoji_system/emoji_manager.py | 7 +-- src/emoji_system/maisaka_tool.py | 61 ++------------------------ src/maisaka/builtin_tool/reply.py | 1 - src/maisaka/chat_loop_service.py | 4 -- src/maisaka/reasoning_engine.py | 1 - src/maisaka/runtime.py | 3 -- 7 files changed, 5 insertions(+), 74 deletions(-) diff --git a/src/chat/image_system/image_manager.py b/src/chat/image_system/image_manager.py index 4ab7dbf5..d30cb8ae 100644 --- a/src/chat/image_system/image_manager.py +++ b/src/chat/image_system/image_manager.py @@ -14,7 +14,6 @@ from src.common.database.database import get_db_session from src.common.database.database_model import Images, ImageType from src.common.data_models.image_data_model import MaiImage from src.config.config import global_config -from src.common.data_models.llm_service_data_models import LLMImageOptions from src.services.llm_service import LLMServiceClient install(extra_lines=3) @@ -382,7 +381,6 @@ class ImageManager: prompt, image_base64, image_format, - options=LLMImageOptions(temperature=0.4), ) description = generation_result.response if not description: diff --git a/src/emoji_system/emoji_manager.py b/src/emoji_system/emoji_manager.py index ac31ea3a..f3d6d879 100644 --- a/src/emoji_system/emoji_manager.py +++ b/src/emoji_system/emoji_manager.py @@ -14,7 +14,7 @@ from sqlmodel import select import Levenshtein from src.common.data_models.image_data_model import MaiEmoji -from src.common.data_models.llm_service_data_models import LLMGenerationOptions, LLMImageOptions +from src.common.data_models.llm_service_data_models import LLMGenerationOptions from src.common.database.database import get_db_session, get_db_session_manual from src.common.database.database_model import Images, ImageType from src.common.logger import get_logger @@ -778,7 +778,7 @@ class EmojiManager: decision_result = await emoji_manager_emotion_judge_llm.generate_response( emoji_replace_prompt, - options=LLMGenerationOptions(temperature=0.8, max_tokens=600), + options=LLMGenerationOptions(max_tokens=600), ) decision = decision_result.response logger.info(f"[决策] 结果: {decision}") @@ -853,7 +853,6 @@ class EmojiManager: prompt, image_base64, "jpg", - options=LLMImageOptions(temperature=0.5), ) description = description_result.response else: @@ -865,7 +864,6 @@ class EmojiManager: prompt, image_base64, image_format, - options=LLMImageOptions(temperature=0.5), ) description = description_result.response except Exception as e: @@ -886,7 +884,6 @@ class EmojiManager: filtration_prompt, image_base64, image_format, - options=LLMImageOptions(temperature=0.3), ) llm_response = filtration_result.response except Exception as e: diff --git a/src/emoji_system/maisaka_tool.py b/src/emoji_system/maisaka_tool.py index d9a9cca7..75c0bb19 100644 --- a/src/emoji_system/maisaka_tool.py +++ b/src/emoji_system/maisaka_tool.py @@ -9,7 +9,6 @@ import random from src.chat.message_receive.chat_manager import chat_manager from src.cli.maisaka_cli_sender import CLI_PLATFORM_NAME, render_cli_message from src.common.data_models.image_data_model import MaiEmoji -from src.common.data_models.llm_service_data_models import LLMGenerationOptions from src.common.logger import get_logger from src.common.utils.utils_image import ImageUtils from src.services import send_service @@ -18,7 +17,6 @@ from .emoji_manager import ( _normalize_emoji_tag_text, _serialize_emoji_for_hook, emoji_manager, - emoji_manager_emotion_judge_llm, ) logger = get_logger("emoji_maisaka_tool") @@ -123,56 +121,6 @@ def _normalize_emotions(emoji: MaiEmoji) -> list[str]: return [] -def _build_recent_context_text(context_texts: Sequence[str], max_items: int = 5) -> str: - """构建供情绪判断使用的最近上下文文本。""" - - normalized_items = [str(item).strip() for item in context_texts if str(item).strip()] - if not normalized_items: - return "" - return "\n".join(normalized_items[-max_items:]) - - -async def _select_emoji_with_llm( - *, - sampled_emojis: Sequence[MaiEmoji], - reasoning: str, - context_text: str, -) -> tuple[MaiEmoji, str]: - """让模型在采样表情中选择更合适的情绪标签。""" - - emotion_map: dict[str, list[MaiEmoji]] = {} - for emoji in sampled_emojis: - for emotion in _normalize_emotions(emoji): - emotion_map.setdefault(emotion, []).append(emoji) - - available_emotions = list(emotion_map.keys()) - if not available_emotions: - return random.choice(list(sampled_emojis)), "" - - prompt = ( - "你正在为聊天场景选择一个最合适的表情包情绪标签。\n" - f"发送原因:{reasoning or '辅助表达当前语气和情绪'}\n" - f"最近聊天记录:\n{context_text or '(暂无额外上下文)'}\n\n" - "可选情绪标签如下:\n" - f"{chr(10).join(available_emotions)}\n\n" - "请只返回一个最匹配的情绪标签,不要解释。" - ) - - try: - llm_result = await emoji_manager_emotion_judge_llm.generate_response( - prompt, - options=LLMGenerationOptions(temperature=0.3, max_tokens=60), - ) - chosen_emotion = (llm_result.response or "").strip().strip("\"'") - except Exception as exc: - logger.warning(f"使用 LLM 选择表情情绪失败,将回退为随机选择: {exc}") - chosen_emotion = "" - - if chosen_emotion and chosen_emotion in emotion_map: - return random.choice(emotion_map[chosen_emotion]), chosen_emotion - return random.choice(list(sampled_emojis)), "" - - async def select_emoji_for_maisaka( *, requested_emotion: str = "", @@ -182,6 +130,8 @@ async def select_emoji_for_maisaka( ) -> tuple[MaiEmoji | None, str]: """为 Maisaka 选择一个合适的表情。""" + del reasoning, context_texts + available_emojis = list(emoji_manager.emojis) if not available_emojis: return None, "" @@ -200,12 +150,7 @@ async def select_emoji_for_maisaka( available_emojis, min(max(sample_size, 1), len(available_emojis)), ) - context_text = _build_recent_context_text(context_texts or []) - return await _select_emoji_with_llm( - sampled_emojis=sampled_emojis, - reasoning=reasoning, - context_text=context_text, - ) + return random.choice(sampled_emojis), "" async def send_emoji_for_maisaka( diff --git a/src/maisaka/builtin_tool/reply.py b/src/maisaka/builtin_tool/reply.py index f03d9d2c..eb5cdf46 100644 --- a/src/maisaka/builtin_tool/reply.py +++ b/src/maisaka/builtin_tool/reply.py @@ -24,7 +24,6 @@ async def _run_expression_selector(tool_ctx: BuiltinToolRuntimeContext, system_p system_prompt=system_prompt, request_kind="expression_selector", max_tokens=256, - temperature=0.1, ) return (response.content or "").strip() diff --git a/src/maisaka/chat_loop_service.py b/src/maisaka/chat_loop_service.py index 377eee9c..e14dd0c0 100644 --- a/src/maisaka/chat_loop_service.py +++ b/src/maisaka/chat_loop_service.py @@ -187,7 +187,6 @@ class MaisakaChatLoopService: chat_system_prompt: Optional[str] = None, session_id: Optional[str] = None, is_group_chat: Optional[bool] = None, - temperature: float = 0.5, max_tokens: int = 2048, ) -> None: """初始化 Maisaka 对话循环服务。 @@ -196,11 +195,9 @@ class MaisakaChatLoopService: chat_system_prompt: 可选的系统提示词。 session_id: 当前会话 ID,用于匹配会话级额外提示。 is_group_chat: 当前会话是否为群聊。 - temperature: 规划器温度参数。 max_tokens: 规划器最大输出长度。 """ - self._temperature = temperature self._max_tokens = max_tokens self._is_group_chat = is_group_chat self._session_id = session_id or "" @@ -546,7 +543,6 @@ class MaisakaChatLoopService: message_factory=message_factory, options=LLMGenerationOptions( tool_options=all_tools if all_tools else None, - temperature=self._temperature, max_tokens=self._max_tokens, response_format=response_format, interrupt_flag=self._interrupt_flag, diff --git a/src/maisaka/reasoning_engine.py b/src/maisaka/reasoning_engine.py index a155eb3c..603ee2a3 100644 --- a/src/maisaka/reasoning_engine.py +++ b/src/maisaka/reasoning_engine.py @@ -138,7 +138,6 @@ class MaisakaReasoningEngine: request_kind="timing_gate", interrupt_flag=None, max_tokens=TIMING_GATE_MAX_TOKENS, - temperature=0.1, tool_definitions=tool_definitions, ) diff --git a/src/maisaka/runtime.py b/src/maisaka/runtime.py index 6ae6cb66..c595a537 100644 --- a/src/maisaka/runtime.py +++ b/src/maisaka/runtime.py @@ -566,7 +566,6 @@ class MaisakaHeartFlowChatting: interrupt_flag: asyncio.Event | None = None, max_tokens: int = 512, response_format: RespFormat | None = None, - temperature: float = 0.2, tool_definitions: Optional[Sequence[ToolDefinitionInput]] = None, ) -> ChatResponse: """运行一个复制上下文的临时子代理,并在完成后立即销毁。""" @@ -584,7 +583,6 @@ class MaisakaHeartFlowChatting: chat_system_prompt=system_prompt, session_id=self.session_id, is_group_chat=self.chat_stream.is_group_session, - temperature=temperature, max_tokens=max_tokens, ) sub_agent.set_interrupt_flag(interrupt_flag) @@ -611,7 +609,6 @@ class MaisakaHeartFlowChatting: request_kind="reply_effect_judge", extra_messages=[judge_message], max_tokens=900, - temperature=0.1, tool_definitions=[], ) return (response.content or "").strip() From 31dc71f0149593cd876c944f7a28ed90b0c3f679 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 21 Apr 2026 16:00:22 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix=EF=BC=9Adocker=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 + docker-compose.yml | 1 + docs-src/Bing.md | 51 --- docs-src/lpmm_parameters_guide.md | 156 ------- docs-src/lpmm_pipelines_guide.md | 326 ------------- docs-src/lpmm_user_guide.md | 411 ----------------- docs-src/plugins/action-components.md | 271 ----------- docs-src/plugins/api/chat-api.md | 130 ------ docs-src/plugins/api/component-manage-api.md | 194 -------- docs-src/plugins/api/config-api.md | 52 --- docs-src/plugins/api/database-api.md | 216 --------- docs-src/plugins/api/emoji-api.md | 141 ------ docs-src/plugins/api/generator-api.md | 198 -------- docs-src/plugins/api/llm-api.md | 65 --- docs-src/plugins/api/logging-api.md | 29 -- docs-src/plugins/api/message-api.md | 372 --------------- docs-src/plugins/api/person-api.md | 119 ----- docs-src/plugins/api/plugin-manage-api.md | 105 ----- docs-src/plugins/api/send-api.md | 175 ------- docs-src/plugins/api/tool-api.md | 55 --- docs-src/plugins/command-components.md | 89 ---- docs-src/plugins/configuration-guide.md | 347 -------------- docs-src/plugins/dependency-management.md | 40 -- .../image/quick-start/1750326700269.png | Bin 4240 -> 0 bytes .../image/quick-start/1750332508760.png | Bin 10886 -> 0 bytes docs-src/plugins/index.md | 81 ---- docs-src/plugins/manifest-guide.md | 205 --------- docs-src/plugins/quick-start.md | 428 ------------------ docs-src/plugins/tool-components.md | 246 ---------- {docs-src => docs}/CONTRIBUTE.md | 0 30 files changed, 3 insertions(+), 4502 deletions(-) delete mode 100644 docs-src/Bing.md delete mode 100644 docs-src/lpmm_parameters_guide.md delete mode 100644 docs-src/lpmm_pipelines_guide.md delete mode 100644 docs-src/lpmm_user_guide.md delete mode 100644 docs-src/plugins/action-components.md delete mode 100644 docs-src/plugins/api/chat-api.md delete mode 100644 docs-src/plugins/api/component-manage-api.md delete mode 100644 docs-src/plugins/api/config-api.md delete mode 100644 docs-src/plugins/api/database-api.md delete mode 100644 docs-src/plugins/api/emoji-api.md delete mode 100644 docs-src/plugins/api/generator-api.md delete mode 100644 docs-src/plugins/api/llm-api.md delete mode 100644 docs-src/plugins/api/logging-api.md delete mode 100644 docs-src/plugins/api/message-api.md delete mode 100644 docs-src/plugins/api/person-api.md delete mode 100644 docs-src/plugins/api/plugin-manage-api.md delete mode 100644 docs-src/plugins/api/send-api.md delete mode 100644 docs-src/plugins/api/tool-api.md delete mode 100644 docs-src/plugins/command-components.md delete mode 100644 docs-src/plugins/configuration-guide.md delete mode 100644 docs-src/plugins/dependency-management.md delete mode 100644 docs-src/plugins/image/quick-start/1750326700269.png delete mode 100644 docs-src/plugins/image/quick-start/1750332508760.png delete mode 100644 docs-src/plugins/index.md delete mode 100644 docs-src/plugins/manifest-guide.md delete mode 100644 docs-src/plugins/quick-start.md delete mode 100644 docs-src/plugins/tool-components.md rename {docs-src => docs}/CONTRIBUTE.md (100%) diff --git a/Dockerfile b/Dockerfile index b6a9cd1b..6061ad44 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,8 @@ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # 工作目录 WORKDIR /MaiMBot +ENV MAIBOT_LEGACY_0X_UPGRADE_CONFIRMED=1 + # 复制依赖列表 COPY requirements.txt . diff --git a/docker-compose.yml b/docker-compose.yml index 0016e726..5602ae65 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,6 +27,7 @@ services: # image: infinitycat/maibot:dev environment: - TZ=Asia/Shanghai + - MAIBOT_LEGACY_0X_UPGRADE_CONFIRMED=1 # Docker 无法交互确认旧版升级迁移,默认跳过确认提示 # - EULA_AGREE=1b662741904d7155d1ce1c00b3530d0d # 同意EULA # - PRIVACY_AGREE=9943b855e72199d0f5016ea39052f1b6 # 同意EULA ports: diff --git a/docs-src/Bing.md b/docs-src/Bing.md deleted file mode 100644 index 5836b157..00000000 --- a/docs-src/Bing.md +++ /dev/null @@ -1,51 +0,0 @@ -- **参数化与动态调整聊天行为**: - - 将 `NormalChatInstance` 和 `HeartFlowChatInstance` 中的关键行为参数(例如:回复概率、思考频率、兴趣度阈值、状态转换条件等)提取出来,使其更易于配置。 - - 允许每个 `SubHeartflow` (即每个聊天场景) 拥有其独立的参数配置,实现"千群千面"。 - - 开发机制,使得这些参数能够被动态调整: - - 基于外部反馈:例如,根据用户评价("话太多"或"太冷淡")调整回复频率。 - - 基于环境分析:例如,根据群消息的活跃度自动调整参与度。 - - 基于学习:通过分析历史交互数据,优化特定群聊下的行为模式。 - - 目标是让 Mai 在不同群聊中展现出更适应环境、更个性化的交互风格。 - -- **动态 Prompt 生成与人格塑造**: - - 当前 Prompt (提示词) 相对静态。计划实现动态或半结构化的 Prompt 生成。 - - Prompt 内容可根据以下因素调整: - - **人格特质**: 通过参数化配置(如友善度、严谨性等),影响 Prompt 的措辞、语气和思考倾向,塑造更稳定和独特的人格。 - - **当前情绪**: 将实时情绪状态融入 Prompt,使回复更符合当下心境。 - - 目标:提升 `HeartFlowChatInstance` (HFC) 回复的多样性、一致性和真实感。 - - 前置:需要重构 Prompt 构建逻辑,可能引入 `PromptBuilder` 并提供标准接口 (认为是必须步骤)。 - - -- **增强工具调用能力 (Enhanced Tool Usage)**: - - 扩展 `HeartFlowChatInstance` (HFC) 可用的工具集。 - - 考虑引入"元工具"或分层工具机制,允许 HFC 在需要时(如深度思考)访问更强大的工具,例如: - - 修改自身或其他 `SubHeartflow` 的聊天参数。 - - 请求改变 Mai 的全局状态 (`MaiState`)。 - - 管理日程或执行更复杂的分析任务。 - - 目标:提升 HFC 的自主决策和行动能力,即使会增加一定的延迟。 - -- **标准化人设生成 (Standardized Persona Generation)**: - - **目标**: 解决手动配置 `人设` 文件缺乏标准、难以全面描述个性的问题,并生成更丰富、可操作的人格资源。 - - **方法**: 利用大型语言模型 (LLM) 辅助生成标准化的、结构化的人格**资源包**。 - - **生成内容**: 不仅生成描述性文本(替代现有 `individual` 配置),还可以同时生成与该人格配套的: - - **相关工具 (Tools)**: 该人格倾向于使用的工具或能力。 - - **初始记忆/知识库 (Memories/Knowledge)**: 定义其背景和知识基础。 - - **核心行为模式 (Core Behavior Patterns)**: 预置一些典型的行为方式,可作为行为学习的起点。 - - **实现途径**: - - 通过与 LLM 的交互式对话来定义和细化人格及其配套资源。 - - 让 LLM 分析提供的文本材料(如小说、背景故事)来提取人格特质和相关信息。 - - **优势**: 替代易出错且标准不一的手动配置,生成更丰富、一致、包含配套资源且易于系统理解和应用的人格包。 - - -- **探索高级记忆检索机制 (GE 系统概念):** - - 研究超越简单关键词/近期性检索的记忆模型。 - - 考虑引入基于事件关联、相对时间线索和绝对时间锚点的检索方式。 - - 可能涉及设计新的事件表示或记忆结构。 - -- **基于人格生成预设知识:** - - 开发利用 LLM 和人格配置生成背景知识的功能。 - - 这些知识应符合角色的行为风格和可能的经历。 - - 作为一种"冷启动"或丰富角色深度的方式。 - - -1.更nb的工作记忆,直接开一个play_ground,通过llm进行内容检索,这个play_ground可以容纳巨量信息,并且十分通用化,十分好。 \ No newline at end of file diff --git a/docs-src/lpmm_parameters_guide.md b/docs-src/lpmm_parameters_guide.md deleted file mode 100644 index 5d190589..00000000 --- a/docs-src/lpmm_parameters_guide.md +++ /dev/null @@ -1,156 +0,0 @@ -# LPMM 关键参数调节指南(进阶版) - -> 本文是对 `config/bot_config.toml` 中 `[lpmm_knowledge]` 段的补充说明。 -> 如果你只想使用默认配置,可以不改这些参数,脚本仍然可以正常工作。 -> -> 重要提醒:无论是修改 `[lpmm_knowledge]` 段的参数,还是通过脚本导入 / 删除 LPMM 知识库数据,主程序都需要重启(或在内部调用一次 `lpmm_start_up()`)后,新的参数和知识才会真正生效到聊天侧。 - -所有与 LPMM 相关的参数,都集中在: - -```toml -[lpmm_knowledge] # lpmm知识库配置 -enable = true -lpmm_mode = "agent" -... -``` - -下面按功能将常用参数分为三组介绍。 - ---- - -## 一、检索相关参数(影响答案质量与风格) - -```toml -qa_relation_search_top_k = 10 # 关系检索TopK -qa_relation_threshold = 0.5 # 关系阈值,相似度高于该值才认为“命中关系” -qa_paragraph_search_top_k = 1000 # 段落检索TopK,越小可能影响召回 -qa_paragraph_node_weight = 0.05 # 段落节点权重,在图检索&PPR中的权重 -qa_ent_filter_top_k = 10 # 实体过滤TopK -qa_ppr_damping = 0.8 # PPR阻尼系数 -qa_res_top_k = 3 # 最终提供给问答模型的段落数 -``` - -- `qa_relation_search_top_k` - 控制“最多考虑多少条关系向量候选”。 - - 数值大:召回更全面,但略慢; - - 数值小:更快,可能遗漏部分隐含关系。 - -- `qa_relation_threshold` - 关系相似度的阈值: - - 数值高:只信任非常相关的关系,系统更可能退化为纯段落向量检索; - - 数值低:图结构影响更大,适合实体关系较丰富的场景。 - -- `qa_paragraph_search_top_k` - 控制“最多考虑多少段落候选”。 - - 太小:可能召回不全,导致答案缺失; - - 太大:略微增加计算量,一般 1000 为安全默认。 - -- `qa_paragraph_node_weight` - 文段节点在图检索中的权重: - - 数值大:更依赖段落向量相似度(传统向量检索); - - 数值小:更依赖图结构和实体网络。 - -- `qa_ppr_damping` - Personalized PageRank 的阻尼系数: - - 通常保持在 0.8 左右即可; - - 越接近 1:偏向长路径探索,结果更发散; - - 略低:更集中在与问题直接相关的节点附近。 - -- `qa_res_top_k` - LPMM 最终会把相关度最高的前 `qa_res_top_k` 条段落组合成“知识上下文”给问答模型。 - - 太多:增加模型负担、阅读更多文字; - - 太少:信息不够充分,一般 3–5 比较平衡。 - -> 调参建议: -> - 优先在 `qa_relation_threshold`、`qa_paragraph_node_weight` 上做小幅调整; -> - 每次调整后,用 `scripts/test_lpmm_retrieval.py` 跑一遍固定问题,感受回答变化。 - ---- - -## 二、性能与硬件相关参数 - -```toml -embedding_dimension = 1024 # 嵌入向量维度,应与模型输出维度一致 -max_embedding_workers = 12 # 嵌入/抽取并发线程数 -embedding_chunk_size = 16 # 每批嵌入的条数 -info_extraction_workers = 3 # 实体抽取同时执行线程数 -enable_ppr = true # 是否启用PPR,低配机器可关闭 -``` - -- `embedding_dimension` - 必须与所选嵌入模型的输出维度一致(比如 768、1024 等)。**不要随意修改,除非你知道你在做什么!!!** - -- `max_embedding_workers` - 决定导入/抽取阶段的并行线程数: - - 机器配置好:可以适当调大,加快导入速度; - - 机器配置弱:建议调低(如 2 或 4),避免 CPU 长时间 100%。 - -- `embedding_chunk_size` - 每批发送给嵌入 API 的段落数量: - - 数值大:请求次数少,但单次请求更“重”; - - 数值小:请求次数多,但对网络和 API 的单次压力小。 - -- `info_extraction_workers` - `scripts/info_extraction.py` 中实体抽取的并行线程数: - - 使用 Pro/贵价模型时建议不要太大,避免并行费用过高; - - 一般 2–4 就能取得较好平衡。 - -- `enable_ppr` - 是否启用个性化 PageRank(PPR)图检索: - - `true`:检索会结合向量+知识图,效果更好,但略慢; - - `false`:只用向量检索,牺牲一定效果,性能更稳定。 - - -> 调参建议: -> - 若导入/检索阶段机器明显“顶不住”(>=1MB的大文本,且分配配置<4C),优先调低: -> - `max_embedding_workers` -> - `embedding_chunk_size` -> - `info_extraction_workers` -> - 或暂时将 `enable_ppr = false` (除非真的出现问题,否则不建议禁用此项,大幅影响检索效果) -> - 调整后重新执行导入或检索,观察日志与系统资源占用。 - -> 小提示:每次大改参数或批量删除知识后,建议用 -> - `scripts/test_lpmm_retrieval.py` 看回答风格是否如预期; -> - 如需确认当前磁盘数据能否正常初始化,可执行 `scripts/refresh_lpmm_knowledge.py` 做一次快速自检。 - ---- - -## 三、开启/关闭 LPMM 与模式说明 - -```toml -enable = true # 是否开启lpmm知识库 -lpmm_mode = "agent" # 可选 classic / agent -``` - -- `enable` - - `true`:LPMM 知识库启用,检索和问答会使用知识库; - - `false`:LPMM 完全关闭,脚本仍可导入/删除数据,但对聊天问答不生效。 - -- `lpmm_mode` - - `classic`:传统模式,仅使用 LPMM 知识库本身; - - `agent`:与新的记忆系统联动,用于更复杂的记忆+知识混合场景。 - -> 修改 `enable` 或 `lpmm_mode` 后,需要重启主程序,让配置生效。 - ---- - -## 四、推荐的调参流程 - -1. **保持默认配置,先跑一轮完整流程** - - 导入 → `inspect_lpmm_global.py` → `test_lpmm_retrieval.py`; - - 记录当前“答案风格”和“响应速度”。 - -2. **每次只调整一到两个参数** - - 例如先调 `qa_relation_threshold`、`qa_paragraph_node_weight`; - - 或在性能不佳时调整 `max_embedding_workers`、`enable_ppr`。 - -3. **调整后重复同一组测试问题** - - 使用 `scripts/test_lpmm_retrieval.py`; - - 对比不同配置下的答案,选择更符合需求的组合。 - -4. **出现“怎么调都不对”时** - - 将 `[lpmm_knowledge]` 段恢复为仓库中的默认配置; - - 重启主程序,即可回到“出厂设置”。 - -通过本指南中的参数调节,你可以在“检索质量”“响应速度”“系统资源占用”之间找到适合自己麦麦和机器的平衡点! - diff --git a/docs-src/lpmm_pipelines_guide.md b/docs-src/lpmm_pipelines_guide.md deleted file mode 100644 index 6539a2fc..00000000 --- a/docs-src/lpmm_pipelines_guide.md +++ /dev/null @@ -1,326 +0,0 @@ -## LPMM 知识库流水线使用指南(命令行版) - -本文档介绍如何使用 `scripts/lpmm_manager.py` 及相关子脚本,完成 **导入 / 删除 / 自检 / 刷新 / 回归测试** 等常见流水线操作,并说明各参数在交互式与非交互(脚本化)场景下的用法。 - -所有命令均假设在项目根目录 `MaiBot/` 下执行: - -```bash -cd MaiBot -``` - ---- - -## 1. 管理脚本总览:`scripts/lpmm_manager.py` - -### 1.1 基本用法 - -```bash -python scripts/lpmm_manager.py [--interactive] [-a ACTION] [--non-interactive] [-- ...子脚本参数...] -``` - -- `--interactive` / `-i`:进入交互式菜单模式(推荐人工运维时使用)。 -- `--action` / `-a`:直接执行指定操作(非交互入口),可选值: - - `prepare_raw`:预处理 `data/lpmm_raw_data/*.txt`。 - - `info_extract`:信息抽取,生成 OpenIE JSON 批次。 - - `import_openie`:导入 OpenIE 批次到向量库与知识图。 - - `delete`:删除/回滚知识(封装 `delete_lpmm_items.py`)。 - - `batch_inspect`:检查指定 OpenIE 批次的存在情况。 - - `global_inspect`:全库状态统计。 - - `refresh`:刷新 LPMM 磁盘数据到内存。 - - `test`:检索效果回归测试。 - - `full_import`:一键执行「预处理原始语料 → 信息抽取 → 导入 → 刷新」。 -- `--non-interactive`: - - 启用 **非交互模式**:`lpmm_manager` 自身不会再调用 `input()` 询问确认; - - 同时自动向子脚本透传 `--non-interactive`(若子脚本支持),用于在 CI / 定时任务中实现无人值守。 -- `--` 之后的内容会原样传递给对应子脚本的 `main()`,用于设置更细粒度参数。 - -> 注意:`--interactive` 与 `--non-interactive` 互斥,不能同时使用。 - ---- - -## 2. 典型流水线一:全量导入(从原始 txt 到可用 LPMM) - -### 2.1 前置条件 - -- 将待导入的原始文本放入: - -```text -data/lpmm_raw_data/*.txt -``` - -- 文本按「空行分段」,每个段落为一条候选知识。 - -### 2.2 一键全流程(交互式) - -```bash -python scripts/lpmm_manager.py --interactive -``` - -菜单中依次: - -1. 选择 `9. full_import`(预处理 → 信息抽取 → 导入 → 刷新)。 -2. 按提示确认可能的费用与时间消耗。 -3. 等待脚本执行完成。 - -### 2.3 一键全流程(非交互 / CI 友好) - -```bash -python scripts/lpmm_manager.py -a full_import --non-interactive -``` - -执行顺序: - -1. `prepare_raw`:调用 `raw_data_preprocessor.load_raw_data()`,统计段落与去重哈希数。 -2. `info_extract`:调用 `info_extraction.main(--non-interactive)`,从 `data/lpmm_raw_data` 读取段落,生成 OpenIE JSON 并写入 `data/openie/`。 -3. `import_openie`:调用 `import_openie.main(--non-interactive)`,导入 OpenIE 批次到嵌入库与 KG。 -4. `refresh`:调用 `refresh_lpmm_knowledge.main()`,刷新 LPMM 知识库到内存。 - -在 `--non-interactive` 模式下: - -- 若 `data/lpmm_raw_data` 中没有 `.txt` 文件,或 `data/openie` 中没有 `.json` 文件,将直接报错退出,并在日志中说明缺少的目录/文件。 -- 若 OpenIE 批次中存在非法文段,导入脚本会 **直接报错退出**,不会卡在交互确认上。 - ---- - -## 3. 典型流水线二:分步导入 - -若需要逐步调试或只执行部分步骤,可以分开调用: - -### 3.1 预处理原始语料:`prepare_raw` - -```bash -python scripts/lpmm_manager.py -a prepare_raw -``` - -行为: -- 使用 `raw_data_preprocessor.load_raw_data()` 读取 `data/lpmm_raw_data/*.txt`; -- 输出段落总数与去重后的哈希数,供人工检查原始数据质量。 - -### 3.2 信息抽取:`info_extract` - -#### 交互式(带费用提示) - -```bash -python scripts/lpmm_manager.py -a info_extract -``` - -脚本会: -- 打印预计费用/时间提示; -- 询问 `确认继续执行?(y/n)`; -- 然后开始从 `data/lpmm_raw_data` 中读取段落,调用 LLM 提取实体与三元组,并生成 OpenIE JSON。 - -#### 非交互式(无人工确认) - -```bash -python scripts/lpmm_manager.py -a info_extract --non-interactive -``` - -行为差异: -- 跳过`确认继续执行`的交互提示,直接开始抽取; -- 若 `data/lpmm_raw_data` 下没有 `.txt` 文件,会打印告警并以错误方式退出。 - -### 3.3 导入 OpenIE 批次:`import_openie` - -#### 交互式 - -```bash -python scripts/lpmm_manager.py -a import_openie -``` - -脚本会: -- 提示导入开销与资源占用情况; -- 询问是否继续; -- 调用 `OpenIE.load()` 加载批次,再将其导入嵌入库与 KG。 - -#### 非交互式 - -```bash -python scripts/lpmm_manager.py -a import_openie --non-interactive -``` - -- 跳过导入开销确认; -- 若数据存在非法文段: - - 在交互模式下会询问是否删除这些非法文段并继续; - - 在非交互模式下,会直接 `logger.error` 并 `sys.exit(1)`,防止导入不完整数据。 - -> 提示:当前 `OpenIE.load()` 仍可能在内部要求你选择具体批次文件,若需完全无交互的导入,可后续扩展为显式指定文件路径。 - -### 3.4 刷新 LPMM 知识库:`refresh` - -```bash -python scripts/lpmm_manager.py -a refresh -# 或 -python scripts/lpmm_manager.py -a refresh --non-interactive -``` - -两者行为相同: -- 调用 `refresh_lpmm_knowledge.main()`,内部执行 `lpmm_start_up()`; -- 日志中输出当前向量与 KG 规模,验证导入是否成功。 - ---- - -## 4. 典型流水线三:删除 / 回滚 - -删除操作通过 `lpmm_manager.py -a delete` 封装 `scripts/delete_lpmm_items.py`。 - -### 4.1 交互式删除(推荐人工操作) - -```bash -python scripts/lpmm_manager.py --interactive -``` - -菜单中选择: - -1. `4. delete - 删除/回滚知识` -2. 再选择删除方式: - - 按哈希文件(`--hash-file`) - - 按 OpenIE 批次(`--openie-file`) - - 按原始语料 + 段落索引(`--raw-file + --raw-index`) - - 按关键字搜索现有段落(`--search-text`) -3. 管理脚本会根据你的选择自动拼好常用参数(是否删除实体/关系、是否删除孤立实体、是否 dry-run、是否自动确认等),最后调用 `delete_lpmm_items.py` 执行。 - -### 4.2 非交互删除(CI / 脚本场景) - -#### 示例:按哈希文件删除(带完整保护参数) - -```bash -python scripts/lpmm_manager.py -a delete --non-interactive -- \ - --hash-file data/lpmm_delete_hashes.txt \ - --delete-entities \ - --delete-relations \ - --remove-orphan-entities \ - --max-delete-nodes 2000 \ - --yes -``` - -- `--non-interactive`(manager):禁止任何 `input()` 询问; -- 子脚本 `delete_lpmm_items.py` 中: - - `--hash-file`:指定待删段落哈希列表; - - `--delete-entities` / `--delete-relations` / `--remove-orphan-entities`:同步清理实体与关系; - - `--max-delete-nodes`:单次删除节点数上限,避免误删过大规模; - - `--yes`:跳过终极确认,适合已验证的自动流水线。 - -#### 按 OpenIE 批次删除(常用于批次回滚) - -```bash -python scripts/lpmm_manager.py -a delete --non-interactive -- \ - --openie-file data/openie/2025-01-01-12-00-openie.json \ - --delete-entities \ - --delete-relations \ - --remove-orphan-entities \ - --yes -``` - -### 4.3 非交互模式下的安全限制 - -在 `delete_lpmm_items.py` 中: - -- 若使用 `--search-text`,需要用户通过输入序号选择要删条目; - - 在 `--non-interactive` 模式下,这一步会直接报错退出,提示改用 `--hash-file / --openie-file / --raw-file` 等纯参数方式。 -- 若未指定 `--yes`: - - 非交互模式下会报错退出,提示「非交互模式且未指定 --yes,出于安全考虑删除操作已被拒绝」。 - ---- - -## 5. 典型流水线四:自检与状态检查 - -### 5.1 检查指定 OpenIE 批次状态:`batch_inspect` - -```bash -python scripts/lpmm_manager.py -a batch_inspect -- --openie-file data/openie/xx.json -``` - -输出该批次在当前库中的: -- 段落向量数量 / KG 段落节点数量; -- 实体向量数量 / KG 实体节点数量; -- 关系向量数量; -- 少量仍存在的样例内容。 - -常用于: -- 导入后确认是否完全成功; -- 删除后确认是否完全回滚。 - -### 5.2 查看整库状态:`global_inspect` - -```bash -python scripts/lpmm_manager.py -a global_inspect -``` - -输出: -- 段落 / 实体 / 关系向量条数; -- KG 节点/边总数,段落节点数、实体节点数; -- 实体计数表 `ent_appear_cnt` 的条目数; -- 少量剩余段落/实体样例,便于快速 sanity check。 - ---- - -## 6. 典型流水线五:检索效果回归测试 - -### 6.1 使用默认测试用例 - -```bash -python scripts/lpmm_manager.py -a test -``` - -- 调用 `test_lpmm_retrieval.py` 内置的 `DEFAULT_TEST_CASES`; -- 对每条用例输出: - - 原始结果; - - 状态(`PASS` / `WARN` / `NO_HIT` / `ERROR`); - - 期望关键字与命中关键字列表。 - -### 6.2 自定义测试问题与期望关键字 - -```bash -python scripts/lpmm_manager.py -a test -- --query "LPMM 是什么?" \ - --expect-keyword 哈希列表 \ - --expect-keyword 删除脚本 -``` - -也可以直接调用子脚本: - -```bash -python scripts/test_lpmm_retrieval.py \ - --query "LPMM 是什么?" \ - --expect-keyword 哈希列表 \ - --expect-keyword 删除脚本 -``` - ---- - -## 7. 推荐组合示例 - -### 7.1 导入 + 刷新 + 简单回归 - -```bash -# 1. 执行全量导入(支持非交互) -python scripts/lpmm_manager.py -a full_import --non-interactive - -# 2. 使用内置用例做一次检索回归 -python scripts/lpmm_manager.py -a test -``` - -### 7.2 批次回滚 + 自检 - -```bash -TARGET_BATCH=data/openie/2025-01-01-12-00-openie.json - -# 1. 按批次删除(非交互) -python scripts/lpmm_manager.py -a delete --non-interactive -- \ - --openie-file "$TARGET_BATCH" \ - --delete-entities \ - --delete-relations \ - --remove-orphan-entities \ - --yes - -# 2. 检查该批次是否彻底删除 -python scripts/lpmm_manager.py -a batch_inspect -- --openie-file "$TARGET_BATCH" - -# 3. 查看全库状态 -python scripts/lpmm_manager.py -a global_inspect -``` - ---- - -如需扩展更多流水线(例如「导入特定批次后自动跑自定义测试用例」),可以在 `scripts/lpmm_manager.py` 中新增对应的 `ACTION_INFO` 条目和 `run_action` 分支,或直接在 CI / shell 脚本中串联上述命令。该管理脚本已支持参数化与非交互调用,适合作为二次封装的基础入口。 - - diff --git a/docs-src/lpmm_user_guide.md b/docs-src/lpmm_user_guide.md deleted file mode 100644 index 147ebcab..00000000 --- a/docs-src/lpmm_user_guide.md +++ /dev/null @@ -1,411 +0,0 @@ -# LPMM 知识库脚本使用指南(零基础用户版) - -本指南面向不熟悉命令行和代码的 C 端用户,帮助你完成: - -- LPMM 知识库的初始部署(从本地 txt 到可检索知识库) -- 安全删除知识(按批次、按原文、按哈希、按关键字) -- 导入 / 删除后的自检与检索效果验证 - -> 说明:本文默认你已经完成 MaiBot 的基础安装,并能在项目根目录打开命令行终端。 -> 重要提醒:每次使用导入 / 删除相关脚本(如 `import_openie.py`、`delete_lpmm_items.py`)修改 LPMM 知识库后,聊天机器人 / WebUI 端要想看到最新知识,需要重启主程序,或在主程序内部显式调用一次 `lpmm_start_up()` 重新初始化 LPMM - ---- -。 - - -## 一、需要用到的脚本一览 - -在项目根目录(`MaiBot-dev`)下,这些脚本是 LPMM 相关的“工具箱”: - -- 导入相关: - - `scripts/raw_data_preprocessor.py` - 从 `data/lpmm_raw_data` 目录读取 `.txt` 文件,按空行拆分为一个个段落,并做去重。 - - `scripts/info_extraction.py` - 调用大模型,从每个段落里抽取实体和三元组,生成中间的 OpenIE JSON 文件。 - - `scripts/import_openie.py` - 把 `data/openie` 目录中的 OpenIE JSON 文件导入到 LPMM 知识库(向量库 + 知识图)。 -- 删除相关: - - `scripts/delete_lpmm_items.py` - LPMM 知识库删除入口,支持按批次、按原始文本段落、按哈希列表、按关键字模糊搜索删除。 -- 自检相关: - - `scripts/inspect_lpmm_global.py` - 查看整个知识库的当前状态:段落/实体/关系条数、知识图节点/边数量、示例内容等。 - - `scripts/inspect_lpmm_batch.py` - 针对某个 OpenIE JSON 批次,检查它在向量库和知识图中的“残留情况”(导入与删除前后对比)。 - - `scripts/test_lpmm_retrieval.py` - 使用几条预设问题测试 LPMM 检索能力,帮助你判断知识库是否正常工作。 - - `scripts/refresh_lpmm_knowledge.py` - 手动重新加载 `data/embedding` 和 `data/rag` 到内存,用来确认当前磁盘上的 LPMM 知识库能正常初始化。 - -> 注意:所有命令示例都假设你已经在虚拟环境中,命令行前缀类似 `(.venv)`,并且当前目录是项目根目录。 - ---- - -## 二、LPMM 知识库的初始部署 - -### 2.1 准备原始 txt 文本 - -1. 把要导入的知识文档放到: - - ```text - data/lpmm_raw_data - ``` - -2. 文件要求: - - - 必须是 `.txt` 文件,建议使用 UTF-8 编码; - - 用**空行**分隔段落:一段话后空一行,即视为一条独立知识。 - -示例文件: - -- `data/lpmm_raw_data/lpmm_large_sample.txt`:仓库内已经提供了一份大样本测试文本,可以直接用来练习。 - -### 2.2 第一步:预处理原始文本(拆段 + 去重) - -在项目根目录执行: - -```bash -.\.venv\Scripts\python.exe scripts/raw_data_preprocessor.py -``` - -成功时通常会看到日志类似: - -- 正在处理文件: `lpmm_large_sample.txt` -- 共读取到 XX 条数据 - -这一步不会调用大模型,仅做拆段和去重。 - -### 2.3 第二步:进行信息抽取(生成 OpenIE JSON) - -执行: - -```bash -.\.venv\Scripts\python.exe scripts/info_extraction.py -``` - -你会看到一个“重要操作确认”提示,说明: - -- 信息抽取会调用大模型,消耗 API 费用和时间; -- 如果确认无误,输入 `y` 回车继续。 - -提取过程中可能出现: - -- 类似“模型 ... 网络错误(可重试)”这样的日志; - 这表示脚本在遇到网络问题时自动重试,一般无需手动干预。 - -运行结束后,会有类似提示: - -```text -信息提取结果已保存到: data/openie/11-27-10-06-openie.json -``` - -- 请记住这个文件名,比如:`11-27-10-06-openie.json` - 接下来我们会用 `` 来代指这类文件。 - -### 2.4 第三步:导入 OpenIE 数据到 LPMM 知识库 - -执行: - -```bash -.\.venv\Scripts\python.exe scripts/import_openie.py -``` - -这个脚本会: - -- 从 `data/openie` 目录读取所有 `*.json` 文件,并合并导入; -- 将新段落的嵌入向量写入 `data/embedding`; -- 将三元组构建为知识图写入 `data/rag`。 - -> 提示:如果你希望“只导入某几批数据”,可以暂时把不需要的 JSON 文件移出 `data/openie`,导入结束后再移回。 - -### 2.5 第四步:全局自检(确认导入成功) - -执行: - -```bash -.\.venv\Scripts\python.exe scripts/inspect_lpmm_global.py -``` - -你会看到类似输出: - -- 段落向量条数: `52` -- 实体向量条数: `260` -- 关系向量条数: `299` -- KG 节点总数 / 边总数 / 段落节点数 / 实体节点数 -- 若干条示例段落与实体内容预览 - -只要这些数字大于 0,就表示 LPMM 知识库已经有可用的数据了。 - -### 2.6 第五步:用脚本测试 LPMM 检索效果(可选但推荐) - -执行: - -```bash -.\.venv\Scripts\python.exe scripts/test_lpmm_retrieval.py -``` - -脚本会: - -- 自动初始化 LPMM(加载向量库与知识图); -- 用几条预设问题查询 LPMM; -- 打印原始检索结果和关键词命中情况。 - -你可以通过观察“RAW RESULT”里的内容,粗略判断: - -- 能否命中与问题高度相关的知识; -- 删除或导入新知识后,回答内容是否发生变化。 - ---- - -## 三、安全删除知识的几种方式 - -> 强烈建议:删除前先备份以下目录,以便“回档”: -> -> - `data/embedding`(向量库) -> - `data/rag`(知识图) - -所有删除操作使用同一个脚本: - -```bash -.\.venv\Scripts\python.exe scripts/delete_lpmm_items.py [参数...] -``` - -脚本特点: - -- 删除前会打印“待删除段落数量 / 实体数量 / 关系数量 / 预计删除节点数”等摘要; -- 需要你输入大写 `YES` 确认才会真正执行; -- 支持多种删除策略,可灵活组合。 - -### 3.1 按批次删除(推荐:整批回滚) - -适用场景:某次导入的整批知识有问题,希望整体回滚。 - -1. 删除前,先检查该批次状态: - - ```bash - .\.venv\Scripts\python.exe scripts/inspect_lpmm_batch.py ^ - --openie-file data/openie/.json - ``` - - 你会看到该批次: - - - 段落:总计多少条、向量库剩余多少、KG 中剩余多少; - - 实体、关系的类似统计; - - 少量示例段落/实体内容预览。 - -2. 确认无误后,按批次删除: - - ```bash - .\.venv\Scripts\python.exe scripts/delete_lpmm_items.py ^ - --openie-file data/openie/.json ^ - --delete-entities --delete-relations --remove-orphan-entities - ``` - - 参数含义: - - - `--delete-entities`:删除该批次涉及的实体向量; - - `--delete-relations`:删除该批次涉及的关系向量; - - `--remove-orphan-entities`:顺带清理删除后不再参与任何边的“孤立实体”节点。 - -3. 删除后再检查: - - ```bash - .\.venv\Scripts\python.exe scripts/inspect_lpmm_batch.py ^ - --openie-file data/openie/.json - - .\.venv\Scripts\python.exe scripts/inspect_lpmm_global.py - ``` - - 若批次检查显示“向量库剩余 0 / KG 中剩余 0”,则说明该批次已被彻底删除。 - -### 3.2 按原始文本段落删除(精确定位某一段) - -适用场景:某个原始 txt 的特定段落写错了,只想删这段对应的知识。 - -命令示例: - -```bash -.\.venv\Scripts\python.exe scripts/delete_lpmm_items.py ^ - --raw-file data/lpmm_raw_data/lpmm_large_sample.txt ^ - --raw-index 2 -``` - -说明: - -- `--raw-index` 从 1 开始计数,可用逗号多选,例如:`1,3,5`; -- 脚本会展示该段落的内容预览和哈希值,再请求你确认。 - -### 3.3 按哈希列表删除(进阶用法) - -适用场景:你有一份“需要删除的段落哈希列表”(比如从其他系统导出)。 - -示例哈希列表文件: - -- `data/openie/lpmm_delete_test_hashes.txt` - -命令: - -```bash -.\.venv\Scripts\python.exe scripts/delete_lpmm_items.py ^ - --hash-file data/openie/lpmm_delete_test_hashes.txt -``` - -说明: - -- 文件中每行一条,可以是 `paragraph-xxxx` 或纯哈希,脚本会自动识别; -- 适合“精确控制删除哪些段落”,但准备哈希列表需要一定技术基础。 - -### 3.4 按关键字模糊搜索删除(对非技术用户最友好) - -适用场景:只知道某段话里包含某个关键词,不知道它在哪个 txt 或批次里。 - -示例 1:删除与“近义词扩展”相关的段落 - -```bash -.\.venv\Scripts\python.exe scripts/delete_lpmm_items.py --search-text "近义词扩展" --search-limit 5 -``` - -示例 2:删除与“LPMM”强相关的一些段落 - -```bash -.\.venv\Scripts\python.exe scripts/delete_lpmm_items.py --search-text "LPMM" --search-limit 20 - -``` - -执行过程: - -1. 脚本在当前段落库中查找包含该关键字的段落; -2. 列出前 N 条候选(`--search-limit` 决定数量); -3. 提示你输入要删除的序号列表,例如:`1,2,5`; -4. 再次提示你输入 `YES` 确认,才会真正执行删除。 - -> 建议: -> -> - 第一次使用时可以先加 `--dry-run` 看看效果: -> ```bash -> .\.venv\Scripts\python.exe scripts/delete_lpmm_items.py ^ -> --search-text "LPMM" ^ -> --search-limit 20 ^ -> --dry-run -> ``` -> - 确认候选列表确实是你要删的内容后,再去掉 `--dry-run` 正式执行。 - ---- - -## 四、自检:如何确认导入 / 删除是否“生效” - -### 4.1 全局状态检查 - -每次导入或删除之后,建议跑一次: - -```bash -.\.venv\Scripts\python.exe scripts/inspect_lpmm_global.py -``` - -你可以在这里看到: - -- 段落向量条数、实体向量条数、关系向量条数; -- 知识图的节点总数、边总数、段落节点和实体节点数量; -- 若干条“剩余段落示例”和“剩余实体示例”。 - -观察方式: - -- 导入后:数字应该明显上升(说明新增数据生效); -- 删除后:数字应该明显下降(说明删除操作生效)。 - -### 4.2 某个批次的局部状态 - -如果你想确认“某一个 OpenIE 文件对应的那一批知识”是否存在,可以使用: - -```bash -.\.venv\Scripts\python.exe scripts/inspect_lpmm_batch.py --openie-file data/openie/.json -``` - -输出中会包含: - -- 该批次的段落 / 实体 / 关系的总数; -- 在向量库中还剩多少条,在 KG 中还剩多少条; -- 若干条仍存在的段落/实体示例。 - -典型用法: - -- 导入后立刻检查一次:确认这一批已经“写入”; -- 删除后再检查一次:确认这一批是否已经“清空”。 - -### 4.3 检索效果回归测试 - -每次做完导入或删除,你都可以用这条命令快速验证检索效果: - -```bash -.\.venv\Scripts\python.exe scripts/test_lpmm_retrieval.py -``` - -它会: - -- 初始化 LPMM(加载当前向量库和知识图); -- 用几条预设问题(包括与 LPMM 和配置相关的问题)进行检索; -- 打印检索结果以及命中关键词情况。 - -通过对比不同时间点的输出,你可以判断: - -- 某些知识是否已经被成功删除(不再出现在回答中); - -- 新增的知识是否已经能被检索到。 - -### 4.4 进阶:一键刷新(可选) - -- 想简单确认“现在这份 data/embedding + data/rag 是否健康”?执行: - - `.\.venv\Scripts\python.exe scripts/refresh_lpmm_knowledge.py ` - - 它会尝试初始化 LPMM,并打印当前段落/实体/关系条数和图大小。 - - - - - ---- - -## 五、常见提示与注意事项 - -1. **看到“网络错误(可重试)”需要担心吗?** - - - 不需要。 - - 这些日志说明脚本在自动处理网络抖动,多数情况下会在重试后成功返回结果。 - - 只要脚本最后没有报“重试耗尽并退出”,一般导入/提取结果是有效的。 - -2. **删除操作会不会“一删全没”?** - - - 不会直接“一删全没”: - - 每次删除会打印摘要信息; - - 必须输入 `YES` 才会真正执行; - - 大批次时还有 `--max-delete-nodes` 保护,超过阈值会警告。 - - 但仍然建议: - - 在大规模删除前备份 `data/embedding` 和 `data/rag`; - - 先通过 `--dry-run` 看看待删列表。 - -3. **可以多次导入吗?需要先清空吗?** - - - 可以多次导入,系统会根据段落内容的哈希做去重; - - 不需要每次都清空,只要你希望老数据仍然保留即可; - - 如果你确实想“重来一遍”,可以: - - 先备份,然后删除 `data/embedding` 和 `data/rag`; - - 再重新跑导入流程。 - -4. **LPMM 开关在哪里?** - - - 配置文件:`config/bot_config.toml`; - - 小节:`[lpmm_knowledge]`; - - 其中有 `enable = true/false` 开关: - - 为 `true`:LPMM 知识库启用,问答时会使用; - - 为 `false`:LPMM 关闭,即使知识库有数据,也不会参与回答。 - - 修改后需要重启主程序,让设置生效。 - ---- - -如果你是普通用户,只需要记住一句话: - -> “导入三步走:预处理 → 信息抽取 → 导入 OpenIE; -> 删除三步走:先检查 → 再删除 → 然后再检查。” - -照着本指南中的命令一步一步执行,就可以安全地管理你的 LPMM 知识库。*** diff --git a/docs-src/plugins/action-components.md b/docs-src/plugins/action-components.md deleted file mode 100644 index b7d3d6bd..00000000 --- a/docs-src/plugins/action-components.md +++ /dev/null @@ -1,271 +0,0 @@ -# ⚡ Action组件详解 - -## 📖 什么是Action - -Action是给麦麦在回复之外提供额外功能的智能组件,**由麦麦的决策系统自主选择是否使用**,具有随机性和拟人化的调用特点。Action不是直接响应用户命令,而是让麦麦根据聊天情境智能地选择合适的动作,使其行为更加自然和真实。 - -### Action的特点 - -- 🧠 **智能激活**:麦麦根据多种条件智能判断是否使用 -- 🎲 **可随机性**:可以使用随机数激活,增加行为的不可预测性,更接近真人交流 -- 🤖 **拟人化**:让麦麦的回应更自然、更有个性 -- 🔄 **情境感知**:基于聊天上下文做出合适的反应 - ---- - -## 🎯 Action组件的基本结构 -首先,所有的Action都应该继承`BaseAction`类。 - -其次,每个Action组件都应该实现以下基本信息: -```python -class ExampleAction(BaseAction): - action_name = "example_action" # 动作的唯一标识符 - action_description = "这是一个示例动作" # 动作描述 - activation_type = ActionActivationType.ALWAYS # 这里以 ALWAYS 为例 - associated_types = ["text", "emoji", ...] # 关联类型 - parallel_action = False # 是否允许与其他Action并行执行 - action_parameters = {"param1": "参数1的说明", "param2": "参数2的说明", ...} - # Action使用场景描述 - 帮助LLM判断何时"选择"使用 - action_require = ["使用场景描述1", "使用场景描述2", ...] - - async def execute(self) -> Tuple[bool, str]: - """ - 执行Action的主要逻辑 - - Returns: - Tuple[bool, str]: (是否成功, 执行结果描述) - """ - # ---- 执行动作的逻辑 ---- - return True, "执行成功" -``` -#### associated_types: 该Action会发送的消息类型,例如文本、表情等。 - -这部分由Adapter传递给处理器。 - -以 MaiBot-Napcat-Adapter 为例,可选项目如下: -| 类型 | 说明 | 格式 | -| --- | --- | --- | -| text | 文本消息 | str | -| emoji | 表情消息 | str: 表情包的无头base64| -| image | 图片消息 | str: 图片的无头base64 | -| reply | 回复消息 | str: 回复的消息ID | -| voice | 语音消息 | str: wav格式语音的无头base64 | -| command | 命令消息 | 参见Adapter文档 | -| voiceurl | 语音URL消息 | str: wav格式语音的URL | -| music | 音乐消息 | str: 这首歌在网易云音乐的音乐id | -| videourl | 视频URL消息 | str: 视频的URL | -| file | 文件消息 | str: 文件的路径 | - -**请知悉,对于不同的处理器,其支持的消息类型可能会有所不同。在开发时请注意。** - -#### action_parameters: 该Action的参数说明。 -这是一个字典,键为参数名,值为参数说明。这个字段可以帮助LLM理解如何使用这个Action,并由LLM返回对应的参数,最后传递到 Action 的 **`action_data`** 属性中。其格式与你定义的格式完全相同 **(除非LLM哈气了,返回了错误的内容)**。 - ---- - -## 🎯 Action 调用的决策机制 - -Action采用**两层决策机制**来优化性能和决策质量: - -> 设计目的:在加载许多插件的时候降低LLM决策压力,避免让麦麦在过多的选项中纠结。 - -**第一层:激活控制(Activation Control)** - -激活决定麦麦是否 **“知道”** 这个Action的存在,即这个Action是否进入决策候选池。不被激活的Action麦麦永远不会选择。 - -**第二层:使用决策(Usage Decision)** - -在Action被激活后,使用条件决定麦麦什么时候会 **“选择”** 使用这个Action。 - -### 决策参数详解 🔧 - -#### 第一层:ActivationType 激活类型说明 - -| 激活类型 | 说明 | 使用场景 | -| ----------- | ---------------------------------------- | ---------------------- | -| [`NEVER`](#never-激活) | 从不激活,Action对麦麦不可见 | 临时禁用某个Action | -| [`ALWAYS`](#always-激活) | 永远激活,Action总是在麦麦的候选池中 | 核心功能,如回复、不回复 | -| `RANDOM` | 基于随机概率决定是否激活 | 增加行为随机性的功能 | -| `KEYWORD` | 当检测到特定关键词时激活 | 明确触发条件的功能 | - -#### `NEVER` 激活 - -`ActionActivationType.NEVER` 会使得 Action 永远不会被激活 - -```python -class DisabledAction(BaseAction): - activation_type = ActionActivationType.NEVER # 永远不激活 - - async def execute(self) -> Tuple[bool, str]: - # 这个Action永远不会被执行 - return False, "这个Action被禁用" -``` - -#### `ALWAYS` 激活 - -`ActionActivationType.ALWAYS` 会使得 Action 永远会被激活,即一直在 Action 候选池中 - -这种激活方式常用于核心功能,如回复或不回复。 - -```python -class AlwaysActivatedAction(BaseAction): - activation_type = ActionActivationType.ALWAYS # 永远激活 - - async def execute(self) -> Tuple[bool, str]: - # 执行核心功能 - return True, "执行了核心功能" -``` - -#### `RANDOM` 激活 - -`ActionActivationType.RANDOM`会使得这个 Action 根据随机概率决定是否加入候选池。 - -概率则由代码中的`random_activation_probability`控制。在内部实现中我们使用了`random.random()`来生成一个0到1之间的随机数,并与这个概率进行比较。 - -因此使用这个方法需要实现`random_activation_probability`属性。 - -```python -class SurpriseAction(BaseAction): - activation_type = ActionActivationType.RANDOM # 基于随机概率激活 - # 随机激活概率 - random_activation_probability = 0.1 # 10%概率激活 - - async def execute(self) -> Tuple[bool, str]: - # 执行惊喜动作 - return True, "发送了惊喜内容" -``` - -#### `KEYWORD` 激活 - -`ActionActivationType.KEYWORD`会使得这个 Action 在检测到特定关键词时激活。 - -关键词由代码中的`activation_keywords`定义,而`keyword_case_sensitive`则控制关键词匹配时是否区分大小写。在内部实现中,我们使用了`in`操作符来检查消息内容是否包含这些关键词。 - -因此,使用此种方法需要实现`activation_keywords`和`keyword_case_sensitive`属性。 - -```python -class GreetingAction(BaseAction): - activation_type = ActionActivationType.KEYWORD # 关键词激活 - activation_keywords = ["你好", "hello", "hi", "嗨"] # 关键词配置 - keyword_case_sensitive = False # 不区分大小写 - - async def execute(self) -> Tuple[bool, str]: - # 执行问候逻辑 - return True, "发送了问候" -``` - -一个完整的使用`ActionActivationType.KEYWORD`的例子请参考`plugins/hello_world_plugin`中的`ByeAction`。 - -#### 第二层:使用决策 - -**在Action被激活后,使用条件决定麦麦什么时候会"选择"使用这个Action**。 - -这一层由以下因素综合决定: - -- `action_require`:使用场景描述,帮助LLM判断何时选择 -- `action_parameters`:所需参数,影响Action的可执行性 -- 当前聊天上下文和麦麦的决策逻辑 - ---- - -### 决策流程示例 - -```python -class EmojiAction(BaseAction): - # 第一层:激活控制 - activation_type = ActionActivationType.RANDOM # 随机激活 - random_activation_probability = 0.1 # 10%概率激活 - - # 第二层:使用决策 - action_require = [ - "表达情绪时可以选择使用", - "增加聊天趣味性", - "不要连续发送多个表情" - ] -``` - -**决策流程**: - -1. **第一层激活判断**: - - - 使用随机数进行决策,当`random.random() < self.random_activation_probability`时,麦麦才"知道"可以使用这个Action -2. **第二层使用决策**: - - - 即使Action被激活,麦麦还会根据 `action_require` 中的条件判断是否真正选择使用 - - 例如:如果刚刚已经发过表情,根据"不要连续发送多个表情"的要求,麦麦可能不会选择这个Action - ---- - -## Action 内置属性说明 -```python -class BaseAction: - def __init__(self): - # 消息相关属性 - self.log_prefix: str # 日志前缀 - self.group_id: str # 群组ID - self.group_name: str # 群组名称 - self.user_id: str # 用户ID - self.user_nickname: str # 用户昵称 - self.platform: str # 平台类型 (qq, telegram等) - self.chat_id: str # 聊天ID - self.chat_stream: ChatStream # 聊天流对象 - self.is_group: bool # 是否群聊 - - # 消息体 - self.action_message: dict # 消息数据 - - # Action相关属性 - self.action_data: dict # Action执行时的数据 - self.thinking_id: str # 思考ID -``` -action_message为一个字典,包含的键值对如下(省略了不必要的键值对) - -```python -{ - "message_id": "1234567890", # 消息id,str - "time": 1627545600.0, # 时间戳,float - "chat_id": "abcdef123456", # 聊天ID,str - "reply_to": None, # 回复消息id,str或None - "interest_value": 0.85, # 兴趣值,float - "is_mentioned": True, # 是否被提及,bool - "chat_info_last_active_time": 1627548600.0, # 最后活跃时间,float - "processed_plain_text": None, # 处理后的文本,str或None - "additional_config": None, # Adapter传来的additional_config,dict或None - "is_emoji": False, # 是否为表情,bool - "is_picid": False, # 是否为图片ID,bool - "is_command": False # 是否为命令,bool -} -``` - -部分值的格式请自行查询数据库。 - ---- - -## Action 内置方法说明 -```python -class BaseAction: - def get_config(self, key: str, default=None): - """获取插件配置值,使用嵌套键访问""" - - async def wait_for_new_message(self, timeout: int = 1200) -> Tuple[bool, str]: - """等待新消息或超时""" - - async def send_text(self, content: str, reply_to: str = "", reply_to_platform_id: str = "", typing: bool = False) -> bool: - """发送文本消息""" - - async def send_emoji(self, emoji_base64: str) -> bool: - """发送表情包""" - - async def send_image(self, image_base64: str) -> bool: - """发送图片""" - - async def send_custom(self, message_type: str, content: str, typing: bool = False, reply_to: str = "") -> bool: - """发送自定义类型消息""" - - async def store_action_info(self, action_build_into_prompt: bool = False, action_prompt_display: str = "", action_done: bool = True) -> None: - """存储动作信息到数据库""" - - async def send_command(self, command_name: str, args: Optional[dict] = None, display_message: str = "", storage_message: bool = True) -> bool: - """发送命令消息""" -``` -具体参数与用法参见`BaseAction`基类的定义。 \ No newline at end of file diff --git a/docs-src/plugins/api/chat-api.md b/docs-src/plugins/api/chat-api.md deleted file mode 100644 index b9b95e27..00000000 --- a/docs-src/plugins/api/chat-api.md +++ /dev/null @@ -1,130 +0,0 @@ -# 聊天API - -聊天API模块专门负责聊天信息的查询和管理,帮助插件获取和管理不同的聊天流。 - -## 导入方式 - -```python -from src.plugin_system import chat_api -# 或者 -from src.plugin_system.apis import chat_api -``` - -一种**Deprecated**方式: -```python -from src.plugin_system.apis.chat_api import ChatManager -``` - -## 主要功能 - -### 1. 获取所有的聊天流 - -```python -def get_all_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]: -``` - -**Args**: -- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的聊天流。 - -**Returns**: -- `List[ChatStream]`:聊天流列表 - -### 2. 获取群聊聊天流 - -```python -def get_group_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]: -``` - -**Args**: -- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的群聊流。 - -**Returns**: -- `List[ChatStream]`:群聊聊天流列表 - -### 3. 获取私聊聊天流 - -```python -def get_private_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]: -``` - -**Args**: -- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的私聊流。 - -**Returns**: -- `List[ChatStream]`:私聊聊天流列表 - -### 4. 根据群ID获取聊天流 - -```python -def get_stream_by_group_id(group_id: str, platform: Optional[str] | SpecialTypes = "qq") -> Optional[ChatStream]: -``` - -**Args**: -- `group_id`:群聊ID -- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的群聊流。 - -**Returns**: -- `Optional[ChatStream]`:聊天流对象,如果未找到返回None - -### 5. 根据用户ID获取私聊流 - -```python -def get_stream_by_user_id(user_id: str, platform: Optional[str] | SpecialTypes = "qq") -> Optional[ChatStream]: -``` - -**Args**: -- `user_id`:用户ID -- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的私聊流。 - -**Returns**: -- `Optional[ChatStream]`:聊天流对象,如果未找到返回None - -### 6. 获取聊天流类型 - -```python -def get_stream_type(chat_stream: ChatStream) -> str: -``` - -**Args**: -- `chat_stream`:聊天流对象 - -**Returns**: -- `str`:聊天流类型,可能的值包括`private`(私聊流),`group`(群聊流)以及`unknown`(未知类型)。 - -### 7. 获取聊天流信息 - -```python -def get_stream_info(chat_stream: ChatStream) -> Dict[str, Any]: -``` - -**Args**: -- `chat_stream`:聊天流对象 - -**Returns**: -- `Dict[str, Any]`:聊天流的详细信息,包括但不限于: - - `stream_id`:聊天流ID - - `platform`:平台名称 - - `type`:聊天流类型 - - `group_id`:群聊ID - - `group_name`:群聊名称 - - `user_id`:用户ID - - `user_name`:用户名称 - -### 8. 获取聊天流统计摘要 - -```python -def get_streams_summary() -> Dict[str, int]: -``` - -**Returns**: -- `Dict[str, int]`:聊天流统计信息摘要,包含以下键: - - `total_streams`:总聊天流数量 - - `group_streams`:群聊流数量 - - `private_streams`:私聊流数量 - - `qq_streams`:QQ平台流数量 - - -## 注意事项 - -1. 大部分函数在参数不合法时候会抛出异常,请确保你的程序进行了捕获。 -2. `ChatStream`对象包含了聊天的完整信息,包括用户信息、群信息等。 \ No newline at end of file diff --git a/docs-src/plugins/api/component-manage-api.md b/docs-src/plugins/api/component-manage-api.md deleted file mode 100644 index a857fb27..00000000 --- a/docs-src/plugins/api/component-manage-api.md +++ /dev/null @@ -1,194 +0,0 @@ -# 组件管理API - -组件管理API模块提供了对插件组件的查询和管理功能,使得插件能够获取和使用组件相关的信息。 - -## 导入方式 -```python -from src.plugin_system.apis import component_manage_api -# 或者 -from src.plugin_system import component_manage_api -``` - -## 功能概述 - -组件管理API主要提供以下功能: -- **插件信息查询** - 获取所有插件或指定插件的信息。 -- **组件查询** - 按名称或类型查询组件信息。 -- **组件管理** - 启用或禁用组件,支持全局和局部操作。 - -## 主要功能 - -### 1. 获取所有插件信息 -```python -def get_all_plugin_info() -> Dict[str, PluginInfo]: -``` -获取所有插件的信息。 - -**Returns:** -- `Dict[str, PluginInfo]` - 包含所有插件信息的字典,键为插件名称,值为 `PluginInfo` 对象。 - -### 2. 获取指定插件信息 -```python -def get_plugin_info(plugin_name: str) -> Optional[PluginInfo]: -``` -获取指定插件的信息。 - -**Args:** -- `plugin_name` (str): 插件名称。 - -**Returns:** -- `Optional[PluginInfo]`: 插件信息对象,如果插件不存在则返回 `None`。 - -### 3. 获取指定组件信息 -```python -def get_component_info(component_name: str, component_type: ComponentType) -> Optional[Union[CommandInfo, ActionInfo, EventHandlerInfo]]: -``` -获取指定组件的信息。 - -**Args:** -- `component_name` (str): 组件名称。 -- `component_type` (ComponentType): 组件类型。 - -**Returns:** -- `Optional[Union[CommandInfo, ActionInfo, EventHandlerInfo]]`: 组件信息对象,如果组件不存在则返回 `None`。 - -### 4. 获取指定类型的所有组件信息 -```python -def get_components_info_by_type(component_type: ComponentType) -> Dict[str, Union[CommandInfo, ActionInfo, EventHandlerInfo]]: -``` -获取指定类型的所有组件信息。 - -**Args:** -- `component_type` (ComponentType): 组件类型。 - -**Returns:** -- `Dict[str, Union[CommandInfo, ActionInfo, EventHandlerInfo]]`: 包含指定类型组件信息的字典,键为组件名称,值为对应的组件信息对象。 - -### 5. 获取指定类型的所有启用的组件信息 -```python -def get_enabled_components_info_by_type(component_type: ComponentType) -> Dict[str, Union[CommandInfo, ActionInfo, EventHandlerInfo]]: -``` -获取指定类型的所有启用的组件信息。 - -**Args:** -- `component_type` (ComponentType): 组件类型。 - -**Returns:** -- `Dict[str, Union[CommandInfo, ActionInfo, EventHandlerInfo]]`: 包含指定类型启用组件信息的字典,键为组件名称,值为对应的组件信息对象。 - -### 6. 获取指定 Action 的注册信息 -```python -def get_registered_action_info(action_name: str) -> Optional[ActionInfo]: -``` -获取指定 Action 的注册信息。 - -**Args:** -- `action_name` (str): Action 名称。 - -**Returns:** -- `Optional[ActionInfo]` - Action 信息对象,如果 Action 不存在则返回 `None`。 - -### 7. 获取指定 Command 的注册信息 -```python -def get_registered_command_info(command_name: str) -> Optional[CommandInfo]: -``` -获取指定 Command 的注册信息。 - -**Args:** -- `command_name` (str): Command 名称。 - -**Returns:** -- `Optional[CommandInfo]` - Command 信息对象,如果 Command 不存在则返回 `None`。 - -### 8. 获取指定 Tool 的注册信息 -```python -def get_registered_tool_info(tool_name: str) -> Optional[ToolInfo]: -``` -获取指定 Tool 的注册信息。 - -**Args:** -- `tool_name` (str): Tool 名称。 - -**Returns:** -- `Optional[ToolInfo]` - Tool 信息对象,如果 Tool 不存在则返回 `None`。 - -### 9. 获取指定 EventHandler 的注册信息 -```python -def get_registered_event_handler_info(event_handler_name: str) -> Optional[EventHandlerInfo]: -``` -获取指定 EventHandler 的注册信息。 - -**Args:** -- `event_handler_name` (str): EventHandler 名称。 - -**Returns:** -- `Optional[EventHandlerInfo]` - EventHandler 信息对象,如果 EventHandler 不存在则返回 `None`。 - -### 10. 全局启用指定组件 -```python -def globally_enable_component(component_name: str, component_type: ComponentType) -> bool: -``` -全局启用指定组件。 - -**Args:** -- `component_name` (str): 组件名称。 -- `component_type` (ComponentType): 组件类型。 - -**Returns:** -- `bool` - 启用成功返回 `True`,否则返回 `False`。 - -### 11. 全局禁用指定组件 -```python -async def globally_disable_component(component_name: str, component_type: ComponentType) -> bool: -``` -全局禁用指定组件。 - -**此函数是异步的,确保在异步环境中调用。** - -**Args:** -- `component_name` (str): 组件名称。 -- `component_type` (ComponentType): 组件类型。 - -**Returns:** -- `bool` - 禁用成功返回 `True`,否则返回 `False`。 - -### 12. 局部启用指定组件 -```python -def locally_enable_component(component_name: str, component_type: ComponentType, stream_id: str) -> bool: -``` -局部启用指定组件。 - -**Args:** -- `component_name` (str): 组件名称。 -- `component_type` (ComponentType): 组件类型。 -- `stream_id` (str): 消息流 ID。 - -**Returns:** -- `bool` - 启用成功返回 `True`,否则返回 `False`。 - -### 13. 局部禁用指定组件 -```python -def locally_disable_component(component_name: str, component_type: ComponentType, stream_id: str) -> bool: -``` -局部禁用指定组件。 - -**Args:** -- `component_name` (str): 组件名称。 -- `component_type` (ComponentType): 组件类型。 -- `stream_id` (str): 消息流 ID。 - -**Returns:** -- `bool` - 禁用成功返回 `True`,否则返回 `False`。 - -### 14. 获取指定消息流中禁用的组件列表 -```python -def get_locally_disabled_components(stream_id: str, component_type: ComponentType) -> list[str]: -``` -获取指定消息流中禁用的组件列表。 - -**Args:** -- `stream_id` (str): 消息流 ID。 -- `component_type` (ComponentType): 组件类型。 - -**Returns:** -- `list[str]` - 禁用的组件名称列表。 diff --git a/docs-src/plugins/api/config-api.md b/docs-src/plugins/api/config-api.md deleted file mode 100644 index 2ee1cdfc..00000000 --- a/docs-src/plugins/api/config-api.md +++ /dev/null @@ -1,52 +0,0 @@ -# 配置API - -配置API模块提供了配置读取功能,让插件能够安全地访问全局配置和插件配置。 - -## 导入方式 - -```python -from src.plugin_system.apis import config_api -# 或者 -from src.plugin_system import config_api -``` - -## 主要功能 - -### 1. 访问全局配置 - -```python -def get_global_config(key: str, default: Any = None) -> Any: -``` - -**Args**: -- `key`: 命名空间式配置键名,使用嵌套访问,如 "section.subsection.key",大小写敏感 -- `default`: 如果配置不存在时返回的默认值 - -**Returns**: -- `Any`: 配置值或默认值 - -#### 示例: -获取机器人昵称 -```python -bot_name = config_api.get_global_config("bot.nickname", "MaiBot") -``` - -### 2. 获取插件配置 - -```python -def get_plugin_config(plugin_config: dict, key: str, default: Any = None) -> Any: -``` -**Args**: -- `plugin_config`: 插件配置字典 -- `key`: 配置键名,支持嵌套访问如 "section.subsection.key",大小写敏感 -- `default`: 如果配置不存在时返回的默认值 - -**Returns**: -- `Any`: 配置值或默认值 - -## 注意事项 - -1. **只读访问**:配置API只提供读取功能,插件不能修改全局配置 -2. **错误处理**:所有函数都有错误处理,失败时会记录日志并返回默认值 -3. **安全性**:插件通过此API访问配置是安全和隔离的 -4. **性能**:频繁访问的配置建议在插件初始化时获取并缓存 \ No newline at end of file diff --git a/docs-src/plugins/api/database-api.md b/docs-src/plugins/api/database-api.md deleted file mode 100644 index 5b6b4468..00000000 --- a/docs-src/plugins/api/database-api.md +++ /dev/null @@ -1,216 +0,0 @@ -# 数据库API - -数据库API模块提供通用的数据库操作功能,支持查询、创建、更新和删除记录,采用Peewee ORM模型。 - -## 导入方式 - -```python -from src.plugin_system.apis import database_api -# 或者 -from src.plugin_system import database_api -``` - -## 主要功能 - -### 1. 通用数据库操作 - -```python -async def db_query( - model_class: Type[Model], - data: Optional[Dict[str, Any]] = None, - query_type: Optional[str] = "get", - filters: Optional[Dict[str, Any]] = None, - limit: Optional[int] = None, - order_by: Optional[List[str]] = None, - single_result: Optional[bool] = False, -) -> Union[List[Dict[str, Any]], Dict[str, Any], None]: -``` -执行数据库查询操作的通用接口。 - -**Args:** -- `model_class`: Peewee模型类。 - - Peewee模型类可以在`src.common.database.database_model`模块中找到,如`ActionRecords`、`Messages`等。 -- `data`: 用于创建或更新的数据 -- `query_type`: 查询类型 - - 可选值: `get`, `create`, `update`, `delete`, `count`。 -- `filters`: 过滤条件字典,键为字段名,值为要匹配的值。 -- `limit`: 限制结果数量。 -- `order_by`: 排序字段列表,使用字段名,前缀'-'表示降序。 - - 排序字段,前缀`-`表示降序,例如`-time`表示按时间字段(即`time`字段)降序 -- `single_result`: 是否只返回单个结果。 - -**Returns:** -- 根据查询类型返回不同的结果: - - `get`: 返回查询结果列表或单个结果。(如果 `single_result=True`) - - `create`: 返回创建的记录。 - - `update`: 返回受影响的行数。 - - `delete`: 返回受影响的行数。 - - `count`: 返回记录数量。 - -#### 示例 - -1. 查询最近10条消息 -```python -messages = await database_api.db_query( - Messages, - query_type="get", - filters={"chat_id": chat_stream.stream_id}, - limit=10, - order_by=["-time"] -) -``` -2. 创建一条记录 -```python -new_record = await database_api.db_query( - ActionRecords, - data={"action_id": "123", "time": time.time(), "action_name": "TestAction"}, - query_type="create", -) -``` -3. 更新记录 -```python -updated_count = await database_api.db_query( - ActionRecords, - data={"action_done": True}, - query_type="update", - filters={"action_id": "123"}, -) -``` -4. 删除记录 -```python -deleted_count = await database_api.db_query( - ActionRecords, - query_type="delete", - filters={"action_id": "123"} -) -``` -5. 计数 -```python -count = await database_api.db_query( - Messages, - query_type="count", - filters={"chat_id": chat_stream.stream_id} -) -``` - -### 2. 数据库保存 -```python -async def db_save( - model_class: Type[Model], data: Dict[str, Any], key_field: Optional[str] = None, key_value: Optional[Any] = None -) -> Optional[Dict[str, Any]]: -``` -保存数据到数据库(创建或更新) - -如果提供了key_field和key_value,会先尝试查找匹配的记录进行更新; - -如果没有找到匹配记录,或未提供key_field和key_value,则创建新记录。 - -**Args:** -- `model_class`: Peewee模型类。 -- `data`: 要保存的数据字典。 -- `key_field`: 用于查找现有记录的字段名,例如"action_id"。 -- `key_value`: 用于查找现有记录的字段值。 - -**Returns:** -- `Optional[Dict[str, Any]]`: 保存后的记录数据,失败时返回None。 - -#### 示例 -创建或更新一条记录 -```python -record = await database_api.db_save( - ActionRecords, - { - "action_id": "123", - "time": time.time(), - "action_name": "TestAction", - "action_done": True - }, - key_field="action_id", - key_value="123" -) -``` - -### 3. 数据库获取 -```python -async def db_get( - model_class: Type[Model], - filters: Optional[Dict[str, Any]] = None, - limit: Optional[int] = None, - order_by: Optional[str] = None, - single_result: Optional[bool] = False, -) -> Union[List[Dict[str, Any]], Dict[str, Any], None]: -``` - -从数据库获取记录 - -这是db_query方法的简化版本,专注于数据检索操作。 - -**Args:** -- `model_class`: Peewee模型类。 -- `filters`: 过滤条件字典,键为字段名,值为要匹配的值。 -- `limit`: 限制结果数量。 -- `order_by`: 排序字段,使用字段名,前缀'-'表示降序。 -- `single_result`: 是否只返回单个结果,如果为True,则返回单个记录字典或None;否则返回记录字典列表或空列表 - -**Returns:** -- `Union[List[Dict], Dict, None]`: 查询结果列表或单个结果(如果`single_result=True`),失败时返回None。 - -#### 示例 -1. 获取单个记录 -```python -record = await database_api.db_get( - ActionRecords, - filters={"action_id": "123"}, - limit=1 -) -``` -2. 获取最近10条记录 -```python -records = await database_api.db_get( - Messages, - filters={"chat_id": chat_stream.stream_id}, - limit=10, - order_by="-time", -) -``` - -### 4. 动作信息存储 -```python -async def store_action_info( - chat_stream=None, - action_build_into_prompt: bool = False, - action_prompt_display: str = "", - action_done: bool = True, - thinking_id: str = "", - action_data: Optional[dict] = None, - action_name: str = "", -) -> Optional[Dict[str, Any]]: -``` -存储动作信息到数据库,是一种针对 Action 的 `db_save()` 的封装函数。 - -将Action执行的相关信息保存到ActionRecords表中,用于后续的记忆和上下文构建。 - -**Args:** -- `chat_stream`: 聊天流对象,包含聊天ID等信息。 -- `action_build_into_prompt`: 是否将动作信息构建到提示中。 -- `action_prompt_display`: 动作提示的显示文本。 -- `action_done`: 动作是否完成。 -- `thinking_id`: 思考过程的ID。 -- `action_data`: 动作的数据字典。 -- `action_name`: 动作的名称。 - -**Returns:** -- `Optional[Dict[str, Any]]`: 存储后的记录数据,失败时返回None。 - -#### 示例 -```python -record = await database_api.store_action_info( - chat_stream=chat_stream, - action_build_into_prompt=True, - action_prompt_display="执行了回复动作", - action_done=True, - thinking_id="thinking_123", - action_data={"content": "Hello"}, - action_name="reply_action" -) -``` \ No newline at end of file diff --git a/docs-src/plugins/api/emoji-api.md b/docs-src/plugins/api/emoji-api.md deleted file mode 100644 index ce9dd0c8..00000000 --- a/docs-src/plugins/api/emoji-api.md +++ /dev/null @@ -1,141 +0,0 @@ -# 表情包API - -表情包API模块提供表情包的获取、查询和管理功能,让插件能够智能地选择和使用表情包。 - -## 导入方式 - -```python -from src.plugin_system.apis import emoji_api -# 或者 -from src.plugin_system import emoji_api -``` - -## 二步走识别优化 - -从新版本开始,表情包识别系统采用了**二步走识别 + 智能缓存**的优化方案: - -### **收到表情包时的识别流程** -1. **第一步**:VLM视觉分析 - 生成详细描述 -2. **第二步**:LLM情感分析 - 基于详细描述提取核心情感标签 -3. **缓存机制**:将情感标签缓存到数据库,详细描述保存到Images表 - -### **注册表情包时的优化** -- **智能复用**:优先从Images表获取已有的详细描述 -- **避免重复**:如果表情包之前被收到过,跳过VLM调用 -- **性能提升**:减少不必要的AI调用,降低延时和成本 - -### **缓存策略** -- **ImageDescriptions表**:缓存最终的情感标签(用于快速显示) -- **Images表**:保存详细描述(用于注册时复用) -- **双重检查**:防止并发情况下的重复生成 - -## 主要功能 - -### 1. 表情包获取 -```python -async def get_by_description(description: str) -> Optional[Tuple[str, str, str]]: -``` -根据场景描述选择表情包 - -**Args:** -- `description`:表情包的描述文本,例如"开心"、"难过"、"愤怒"等 - -**Returns:** -- `Optional[Tuple[str, str, str]]`:一个元组: (表情包的base64编码, 描述, 情感标签),如果未找到匹配的表情包则返回None - -#### 示例 -```python -emoji_result = await emoji_api.get_by_description("大笑") -if emoji_result: - emoji_base64, description, matched_scene = emoji_result - print(f"获取到表情包: {description}, 场景: {matched_scene}") - # 可以将emoji_base64用于发送表情包 -``` - -### 2. 随机获取表情包 -```python -async def get_random(count: Optional[int] = 1) -> List[Tuple[str, str, str]]: -``` -随机获取指定数量的表情包 - -**Args:** -- `count`:要获取的表情包数量,默认为1 - -**Returns:** -- `List[Tuple[str, str, str]]`:一个包含多个表情包的列表,每个元素是一个元组: (表情包的base64编码, 描述, 情感标签),如果未找到或出错则返回空列表 - -### 3. 根据情感获取表情包 -```python -async def get_by_emotion(emotion: str) -> Optional[Tuple[str, str, str]]: -``` -根据情感标签获取表情包 - -**Args:** -- `emotion`:情感标签,例如"开心"、"悲伤"、"愤怒"等 - -**Returns:** -- `Optional[Tuple[str, str, str]]`:一个元组: (表情包的base64编码, 描述, 情感标签),如果未找到则返回None - -### 4. 获取表情包数量 -```python -def get_count() -> int: -``` -获取当前可用表情包的数量 - -### 5. 获取表情包系统信息 -```python -def get_info() -> Dict[str, Any]: -``` -获取表情包系统的基本信息 - -**Returns:** -- `Dict[str, Any]`:包含表情包数量、描述等信息的字典,包含以下键: - - `current_count`:当前表情包数量 - - `max_count`:最大表情包数量 - - `available_emojis`:当前可用的表情包数量 - -### 6. 获取所有可用的情感标签 -```python -def get_emotions() -> List[str]: -``` -获取所有可用的情感标签 **(已经去重)** - -### 7. 获取所有表情包描述 -```python -def get_descriptions() -> List[str]: -``` -获取所有表情包的描述列表 - -## 场景描述说明 - -### 常用场景描述 -表情包系统支持多种具体的场景描述,举例如下: - -- **开心类场景**:开心的大笑、满意的微笑、兴奋的手舞足蹈 -- **无奈类场景**:表示无奈和沮丧、轻微的讽刺、无语的摇头 -- **愤怒类场景**:愤怒和不满、生气的瞪视、暴躁的抓狂 -- **惊讶类场景**:震惊的表情、意外的发现、困惑的思考 -- **可爱类场景**:卖萌的表情、撒娇的动作、害羞的样子 - -### 情感关键词示例 -系统支持的情感关键词举例如下: -- 大笑、微笑、兴奋、手舞足蹈 -- 无奈、沮丧、讽刺、无语、摇头 -- 愤怒、不满、生气、瞪视、抓狂 -- 震惊、意外、困惑、思考 -- 卖萌、撒娇、害羞、可爱 - -### 匹配机制 -- **精确匹配**:优先匹配完整的场景描述,如"开心的大笑" -- **关键词匹配**:如果没有精确匹配,则根据关键词进行模糊匹配 -- **语义匹配**:系统会理解场景的语义含义进行智能匹配 - -## 注意事项 - -1. **异步函数**:部分函数是异步的,需要使用 `await` -2. **返回格式**:表情包以base64编码返回,可直接用于发送 -3. **错误处理**:所有函数都有错误处理,失败时返回None,空列表或默认值 -4. **使用统计**:系统会记录表情包的使用次数 -5. **文件依赖**:表情包依赖于本地文件,确保表情包文件存在 -6. **编码格式**:返回的是base64编码的图片数据,可直接用于网络传输 -7. **场景理解**:系统能理解具体的场景描述,比简单的情感分类更准确 diff --git a/docs-src/plugins/api/generator-api.md b/docs-src/plugins/api/generator-api.md deleted file mode 100644 index e0ea28cb..00000000 --- a/docs-src/plugins/api/generator-api.md +++ /dev/null @@ -1,198 +0,0 @@ -# 回复生成器API - -回复生成器API模块提供智能回复生成功能,让插件能够使用系统的回复生成器来产生自然的聊天回复。 - -## 导入方式 - -```python -from src.plugin_system.apis import generator_api -# 或者 -from src.plugin_system import generator_api -``` - -## 主要功能 - -### 1. 回复器获取 -```python -def get_replyer( - chat_stream: Optional[ChatStream] = None, - chat_id: Optional[str] = None, - model_set_with_weight: Optional[List[Tuple[TaskConfig, float]]] = None, - request_type: str = "replyer", -) -> Optional[DefaultReplyer]: -``` -获取回复器对象 - -优先使用chat_stream,如果没有则使用chat_id直接查找。 - -使用 ReplyerManager 来管理实例,避免重复创建。 - -**Args:** -- `chat_stream`: 聊天流对象 -- `chat_id`: 聊天ID(实际上就是`stream_id`) -- `model_set_with_weight`: 模型配置列表,每个元素为 `(TaskConfig, weight)` 元组 -- `request_type`: 请求类型,用于记录LLM使用情况,可以不写 - -**Returns:** -- `DefaultReplyer`: 回复器对象,如果获取失败则返回None - -#### 示例 -```python -# 使用聊天流获取回复器 -replyer = generator_api.get_replyer(chat_stream=chat_stream) - -# 使用平台和ID获取回复器 -replyer = generator_api.get_replyer(chat_id="123456789") -``` - -### 2. 回复生成 -```python -async def generate_reply( - chat_stream: Optional[ChatStream] = None, - chat_id: Optional[str] = None, - action_data: Optional[Dict[str, Any]] = None, - reply_to: str = "", - extra_info: str = "", - available_actions: Optional[Dict[str, ActionInfo]] = None, - enable_splitter: bool = True, - enable_chinese_typo: bool = True, - return_prompt: bool = False, - model_set_with_weight: Optional[List[Tuple[TaskConfig, float]]] = None, - request_type: str = "generator_api", -) -> Tuple[bool, List[Tuple[str, Any]], Optional[str]]: -``` -生成回复 - -优先使用chat_stream,如果没有则使用chat_id直接查找。 - -**Args:** -- `chat_stream`: 聊天流对象 -- `chat_id`: 聊天ID(实际上就是`stream_id`) -- `action_data`: 动作数据(向下兼容,包含`reply_to`和`extra_info`) -- `reply_to`: 回复目标,格式为 `{发送者的person_name:消息内容}` -- `extra_info`: 附加信息 -- `available_actions`: 可用动作字典,格式为 `{"action_name": ActionInfo}` -- `enable_splitter`: 是否启用分割器 -- `enable_chinese_typo`: 是否启用中文错别字 -- `return_prompt`: 是否返回提示词 -- `model_set_with_weight`: 模型配置列表,每个元素为 `(TaskConfig, weight)` 元组 -- `request_type`: 请求类型(可选,记录LLM使用) -- `request_type`: 请求类型,用于记录LLM使用情况 - -**Returns:** -- `Tuple[bool, List[Tuple[str, Any]], Optional[str]]`: (是否成功, 回复集合, 提示词) - -#### 示例 -```python -success, reply_set, prompt = await generator_api.generate_reply( - chat_stream=chat_stream, - action_data=action_data, - reply_to="麦麦:你好", - available_actions=action_info, - return_prompt=True -) -if success: - for reply_type, reply_content in reply_set: - print(f"回复类型: {reply_type}, 内容: {reply_content}") - if prompt: - print(f"使用的提示词: {prompt}") -``` - -### 3. 回复重写 -```python -async def rewrite_reply( - chat_stream: Optional[ChatStream] = None, - reply_data: Optional[Dict[str, Any]] = None, - chat_id: Optional[str] = None, - enable_splitter: bool = True, - enable_chinese_typo: bool = True, - model_set_with_weight: Optional[List[Tuple[TaskConfig, float]]] = None, - raw_reply: str = "", - reason: str = "", - reply_to: str = "", - return_prompt: bool = False, -) -> Tuple[bool, List[Tuple[str, Any]], Optional[str]]: -``` -重写回复,使用新的内容替换旧的回复内容。 - -优先使用chat_stream,如果没有则使用chat_id直接查找。 - -**Args:** -- `chat_stream`: 聊天流对象 -- `reply_data`: 回复数据,包含`raw_reply`, `reason`和`reply_to`,**(向下兼容备用,当其他参数缺失时从此获取)** -- `chat_id`: 聊天ID(实际上就是`stream_id`) -- `enable_splitter`: 是否启用分割器 -- `enable_chinese_typo`: 是否启用中文错别字 -- `model_set_with_weight`: 模型配置列表,每个元素为 (TaskConfig, weight) 元组 -- `raw_reply`: 原始回复内容 -- `reason`: 重写原因 -- `reply_to`: 回复目标,格式为 `{发送者的person_name:消息内容}` - -**Returns:** -- `Tuple[bool, List[Tuple[str, Any]], Optional[str]]`: (是否成功, 回复集合, 提示词) - -#### 示例 -```python -success, reply_set, prompt = await generator_api.rewrite_reply( - chat_stream=chat_stream, - raw_reply="原始回复内容", - reason="重写原因", - reply_to="麦麦:你好", - return_prompt=True -) -if success: - for reply_type, reply_content in reply_set: - print(f"回复类型: {reply_type}, 内容: {reply_content}") - if prompt: - print(f"使用的提示词: {prompt}") -``` - -## 回复集合`reply_set`格式 - -### 回复类型 -生成的回复集合包含多种类型的回复: - -- `"text"`:纯文本回复 -- `"emoji"`:表情包回复 -- `"image"`:图片回复 -- `"mixed"`:混合类型回复 - -### 回复集合结构 -```python -# 示例回复集合 -reply_set = [ - ("text", "很高兴见到你!"), - ("emoji", "emoji_base64_data"), - ("text", "有什么可以帮助你的吗?") -] -``` - -### 4. 自定义提示词回复 -```python -async def generate_response_custom( - chat_stream: Optional[ChatStream] = None, - chat_id: Optional[str] = None, - model_set_with_weight: Optional[List[Tuple[TaskConfig, float]]] = None, - prompt: str = "", -) -> Optional[str]: -``` -生成自定义提示词回复 - -优先使用chat_stream,如果没有则使用chat_id直接查找。 - -**Args:** -- `chat_stream`: 聊天流对象 -- `chat_id`: 聊天ID(备用) -- `model_set_with_weight`: 模型集合配置列表 -- `prompt`: 自定义提示词 - -**Returns:** -- `Optional[str]`: 生成的自定义回复内容,如果生成失败则返回None - -## 注意事项 - -1. **异步操作**:部分函数是异步的,须使用`await` -2. **聊天流依赖**:需要有效的聊天流对象才能正常工作 -3. **性能考虑**:回复生成可能需要一些时间,特别是使用LLM时 -4. **回复格式**:返回的回复集合是元组列表,包含类型和内容 -5. **上下文感知**:生成器会考虑聊天上下文和历史消息,除非你用的是自定义提示词。 diff --git a/docs-src/plugins/api/llm-api.md b/docs-src/plugins/api/llm-api.md deleted file mode 100644 index d35ea68b..00000000 --- a/docs-src/plugins/api/llm-api.md +++ /dev/null @@ -1,65 +0,0 @@ -# LLM API - -LLM API模块提供与大语言模型交互的功能,让插件能够使用系统配置的LLM模型进行内容生成。 - -## 导入方式 - -```python -from src.plugin_system.apis import llm_api -# 或者 -from src.plugin_system import llm_api -``` - -## 主要功能 - -### 1. 查询可用模型 -```python -def get_available_models() -> Dict[str, TaskConfig]: -``` -获取所有可用的模型配置。 - -**Return:** -- `Dict[str, TaskConfig]`:模型配置字典,key为模型名称,value为模型配置对象。 - -### 2. 使用模型生成内容 -```python -async def generate_with_model( - prompt: str, - model_config: TaskConfig, - request_type: str = "plugin.generate", - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, -) -> Tuple[bool, str, str, str]: -``` -使用指定模型生成内容。 - -**Args:** -- `prompt`:提示词。 -- `model_config`:模型配置对象(从 `get_available_models` 获取)。 -- `request_type`:请求类型标识,默认为 `"plugin.generate"`。 -- `temperature`:生成内容的温度设置,影响输出的随机性。 -- `max_tokens`:生成内容的最大token数。 - -**Return:** -- `Tuple[bool, str, str, str]`:返回一个元组,包含(是否成功, 生成的内容, 推理过程, 模型名称)。 - -### 3. 有Tool情况下使用模型生成内容 -```python -async def generate_with_model_with_tools( - prompt: str, - model_config: TaskConfig, - tool_options: List[Dict[str, Any]] | None = None, - request_type: str = "plugin.generate", - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, -) -> Tuple[bool, str, str, str, List[ToolCall] | None]: -``` -使用指定模型生成内容,并支持工具调用。 - -**Args:** -- `prompt`:提示词。 -- `model_config`:模型配置对象(从 `get_available_models` 获取)。 -- `tool_options`:工具选项列表,包含可用工具的配置,字典为每一个工具的定义,参见[tool-components.md](../tool-components.md#属性说明),可用`tool_api.get_llm_available_tool_definitions()`获取并选择。 -- `request_type`:请求类型标识,默认为 `"plugin.generate"`。 -- `temperature`:生成内容的温度设置,影响输出的随机性。 -- `max_tokens`:生成内容的最大token数。 \ No newline at end of file diff --git a/docs-src/plugins/api/logging-api.md b/docs-src/plugins/api/logging-api.md deleted file mode 100644 index 5576bf5c..00000000 --- a/docs-src/plugins/api/logging-api.md +++ /dev/null @@ -1,29 +0,0 @@ -# Logging API - -Logging API模块提供了获取本体logger的功能,允许插件记录日志信息。 - -## 导入方式 - -```python -from src.plugin_system.apis import get_logger -# 或者 -from src.plugin_system import get_logger -``` - -## 主要功能 -### 1. 获取本体logger -```python -def get_logger(name: str) -> structlog.stdlib.BoundLogger: -``` -获取本体logger实例。 - -**Args:** -- `name` (str): 日志记录器的名称。 - -**Returns:** -- 一个logger实例,有以下方法: - - `debug` - - `info` - - `warning` - - `error` - - `critical` \ No newline at end of file diff --git a/docs-src/plugins/api/message-api.md b/docs-src/plugins/api/message-api.md deleted file mode 100644 index 85d83a9b..00000000 --- a/docs-src/plugins/api/message-api.md +++ /dev/null @@ -1,372 +0,0 @@ -# 消息API - -消息API提供了强大的消息查询、计数和格式化功能,让你轻松处理聊天消息数据。 - -## 导入方式 - -```python -from src.plugin_system.apis import message_api -# 或者 -from src.plugin_system import message_api -``` - -## 功能概述 - -消息API主要提供三大类功能: -- **消息查询** - 按时间、聊天、用户等条件查询消息 -- **消息计数** - 统计新消息数量 -- **消息格式化** - 将消息转换为可读格式 - -## 主要功能 - -### 1. 按照事件查询消息 -```python -def get_messages_by_time( - start_time: float, end_time: float, limit: int = 0, limit_mode: str = "latest", filter_mai: bool = False -) -> List[Dict[str, Any]]: -``` -获取指定时间范围内的消息。 - -**Args:** -- `start_time` (float): 开始时间戳 -- `end_time` (float): 结束时间戳 -- `limit` (int): 限制返回消息数量,0为不限制 -- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录 -- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - -消息列表中包含的键与`Messages`类的属性一致。(位于`src.common.database.database_model`) - -### 2. 获取指定聊天中指定时间范围内的信息 -```python -def get_messages_by_time_in_chat( - chat_id: str, - start_time: float, - end_time: float, - limit: int = 0, - limit_mode: str = "latest", - filter_mai: bool = False, -) -> List[Dict[str, Any]]: -``` -获取指定聊天中指定时间范围内的消息。 - -**Args:** -- `chat_id` (str): 聊天ID -- `start_time` (float): 开始时间戳 -- `end_time` (float): 结束时间戳 -- `limit` (int): 限制返回消息数量,0为不限制 -- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录 -- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 3. 获取指定聊天中指定时间范围内的信息(包含边界) -```python -def get_messages_by_time_in_chat_inclusive( - chat_id: str, - start_time: float, - end_time: float, - limit: int = 0, - limit_mode: str = "latest", - filter_mai: bool = False, - filter_command: bool = False, -) -> List[Dict[str, Any]]: -``` -获取指定聊天中指定时间范围内的消息(包含边界)。 - -**Args:** -- `chat_id` (str): 聊天ID -- `start_time` (float): 开始时间戳(包含) -- `end_time` (float): 结束时间戳(包含) -- `limit` (int): 限制返回消息数量,0为不限制 -- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录 -- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False -- `filter_command` (bool): 是否过滤命令消息,默认False - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 4. 获取指定聊天中指定用户在指定时间范围内的消息 -```python -def get_messages_by_time_in_chat_for_users( - chat_id: str, - start_time: float, - end_time: float, - person_ids: List[str], - limit: int = 0, - limit_mode: str = "latest", -) -> List[Dict[str, Any]]: -``` -获取指定聊天中指定用户在指定时间范围内的消息。 - -**Args:** -- `chat_id` (str): 聊天ID -- `start_time` (float): 开始时间戳 -- `end_time` (float): 结束时间戳 -- `person_ids` (List[str]): 用户ID列表 -- `limit` (int): 限制返回消息数量,0为不限制 -- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录 - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 5. 随机选择一个聊天,返回该聊天在指定时间范围内的消息 -```python -def get_random_chat_messages( - start_time: float, - end_time: float, - limit: int = 0, - limit_mode: str = "latest", - filter_mai: bool = False, -) -> List[Dict[str, Any]]: -``` -随机选择一个聊天,返回该聊天在指定时间范围内的消息。 - -**Args:** -- `start_time` (float): 开始时间戳 -- `end_time` (float): 结束时间戳 -- `limit` (int): 限制返回消息数量,0为不限制 -- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录 -- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 6. 获取指定用户在所有聊天中指定时间范围内的消息 -```python -def get_messages_by_time_for_users( - start_time: float, - end_time: float, - person_ids: List[str], - limit: int = 0, - limit_mode: str = "latest", -) -> List[Dict[str, Any]]: -``` -获取指定用户在所有聊天中指定时间范围内的消息。 - -**Args:** -- `start_time` (float): 开始时间戳 -- `end_time` (float): 结束时间戳 -- `person_ids` (List[str]): 用户ID列表 -- `limit` (int): 限制返回消息数量,0为不限制 -- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录 - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 7. 获取指定时间戳之前的消息 -```python -def get_messages_before_time( - timestamp: float, - limit: int = 0, - filter_mai: bool = False, -) -> List[Dict[str, Any]]: -``` -获取指定时间戳之前的消息。 - -**Args:** -- `timestamp` (float): 时间戳 -- `limit` (int): 限制返回消息数量,0为不限制 -- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 8. 获取指定聊天中指定时间戳之前的消息 -```python -def get_messages_before_time_in_chat( - chat_id: str, - timestamp: float, - limit: int = 0, - filter_mai: bool = False, -) -> List[Dict[str, Any]]: -``` -获取指定聊天中指定时间戳之前的消息。 - -**Args:** -- `chat_id` (str): 聊天ID -- `timestamp` (float): 时间戳 -- `limit` (int): 限制返回消息数量,0为不限制 -- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 9. 获取指定用户在指定时间戳之前的消息 -```python -def get_messages_before_time_for_users( - timestamp: float, - person_ids: List[str], - limit: int = 0, -) -> List[Dict[str, Any]]: -``` -获取指定用户在指定时间戳之前的消息。 - -**Args:** -- `timestamp` (float): 时间戳 -- `person_ids` (List[str]): 用户ID列表 -- `limit` (int): 限制返回消息数量,0为不限制 - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 10. 获取指定聊天中最近一段时间的消息 -```python -def get_recent_messages( - chat_id: str, - hours: float = 24.0, - limit: int = 100, - limit_mode: str = "latest", - filter_mai: bool = False, -) -> List[Dict[str, Any]]: -``` -获取指定聊天中最近一段时间的消息。 - -**Args:** -- `chat_id` (str): 聊天ID -- `hours` (float): 最近多少小时,默认24小时 -- `limit` (int): 限制返回消息数量,默认100条 -- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录 -- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False - -**Returns:** -- `List[Dict[str, Any]]` - 消息列表 - - -### 11. 计算指定聊天中从开始时间到结束时间的新消息数量 -```python -def count_new_messages( - chat_id: str, - start_time: float = 0.0, - end_time: Optional[float] = None, -) -> int: -``` -计算指定聊天中从开始时间到结束时间的新消息数量。 - -**Args:** -- `chat_id` (str): 聊天ID -- `start_time` (float): 开始时间戳 -- `end_time` (Optional[float]): 结束时间戳,如果为None则使用当前时间 - -**Returns:** -- `int` - 新消息数量 - - -### 12. 计算指定聊天中指定用户从开始时间到结束时间的新消息数量 -```python -def count_new_messages_for_users( - chat_id: str, - start_time: float, - end_time: float, - person_ids: List[str], -) -> int: -``` -计算指定聊天中指定用户从开始时间到结束时间的新消息数量。 - -**Args:** -- `chat_id` (str): 聊天ID -- `start_time` (float): 开始时间戳 -- `end_time` (float): 结束时间戳 -- `person_ids` (List[str]): 用户ID列表 - -**Returns:** -- `int` - 新消息数量 - - -### 13. 将消息列表构建成可读的字符串 -```python -def build_readable_messages_to_str( - messages: List[Dict[str, Any]], - replace_bot_name: bool = True, - merge_messages: bool = False, - timestamp_mode: str = "relative", - read_mark: float = 0.0, - truncate: bool = False, - show_actions: bool = False, -) -> str: -``` -将消息列表构建成可读的字符串。 - -**Args:** -- `messages` (List[Dict[str, Any]]): 消息列表 -- `replace_bot_name` (bool): 是否将机器人的名称替换为"你" -- `merge_messages` (bool): 是否合并连续消息 -- `timestamp_mode` (str): 时间戳显示模式,`"relative"`或`"absolute"` -- `read_mark` (float): 已读标记时间戳,用于分割已读和未读消息 -- `truncate` (bool): 是否截断长消息 -- `show_actions` (bool): 是否显示动作记录 - -**Returns:** -- `str` - 格式化后的可读字符串 - - -### 14. 将消息列表构建成可读的字符串,并返回详细信息 -```python -async def build_readable_messages_with_details( - messages: List[Dict[str, Any]], - replace_bot_name: bool = True, - merge_messages: bool = False, - timestamp_mode: str = "relative", - truncate: bool = False, -) -> Tuple[str, List[Tuple[float, str, str]]]: -``` -将消息列表构建成可读的字符串,并返回详细信息。 - -**Args:** -- `messages` (List[Dict[str, Any]]): 消息列表 -- `replace_bot_name` (bool): 是否将机器人的名称替换为"你" -- `merge_messages` (bool): 是否合并连续消息 -- `timestamp_mode` (str): 时间戳显示模式,`"relative"`或`"absolute"` -- `truncate` (bool): 是否截断长消息 - -**Returns:** -- `Tuple[str, List[Tuple[float, str, str]]]` - 格式化后的可读字符串和详细信息元组列表(时间戳, 昵称, 内容) - - -### 15. 从消息列表中提取不重复的用户ID列表 -```python -async def get_person_ids_from_messages( - messages: List[Dict[str, Any]], -) -> List[str]: -``` -从消息列表中提取不重复的用户ID列表。 - -**Args:** -- `messages` (List[Dict[str, Any]]): 消息列表 - -**Returns:** -- `List[str]` - 用户ID列表 - - -### 16. 从消息列表中移除机器人的消息 -```python -def filter_mai_messages( - messages: List[Dict[str, Any]], -) -> List[Dict[str, Any]]: -``` -从消息列表中移除机器人的消息。 - -**Args:** -- `messages` (List[Dict[str, Any]]): 消息列表,每个元素是消息字典 - -**Returns:** -- `List[Dict[str, Any]]` - 过滤后的消息列表 - -## 注意事项 - -1. **时间戳格式**:所有时间参数都使用Unix时间戳(float类型) -2. **异步函数**:部分函数是异步函数,需要使用 `await` -3. **性能考虑**:查询大量消息时建议设置合理的 `limit` 参数 -4. **消息格式**:返回的消息是字典格式,包含时间戳、发送者、内容等信息 -5. **用户ID**:`person_ids` 参数接受字符串列表,用于筛选特定用户的消息 \ No newline at end of file diff --git a/docs-src/plugins/api/person-api.md b/docs-src/plugins/api/person-api.md deleted file mode 100644 index f97498dc..00000000 --- a/docs-src/plugins/api/person-api.md +++ /dev/null @@ -1,119 +0,0 @@ -# 个人信息API - -个人信息API模块提供用户信息查询和管理功能,让插件能够获取和使用用户的相关信息。 - -## 导入方式 - -```python -from src.plugin_system.apis import person_api -# 或者 -from src.plugin_system import person_api -``` - -## 主要功能 - -### 1. Person ID 获取 -```python -def get_person_id(platform: str, user_id: int) -> str: -``` -根据平台和用户ID获取person_id - -**Args:** -- `platform`:平台名称,如 "qq", "telegram" 等 -- `user_id`:用户ID - -**Returns:** -- `str`:唯一的person_id(MD5哈希值) - -#### 示例 -```python -person_id = person_api.get_person_id("qq", 123456) -``` - -### 2. 用户信息查询 -```python -async def get_person_value(person_id: str, field_name: str, default: Any = None) -> Any: -``` -查询单个用户信息字段值 - -**Args:** -- `person_id`:用户的唯一标识ID -- `field_name`:要获取的字段名 -- `default`:字段值不存在时的默认值 - -**Returns:** -- `Any`:字段值或默认值 - -#### 示例 -```python -nickname = await person_api.get_person_value(person_id, "nickname", "未知用户") -impression = await person_api.get_person_value(person_id, "impression") -``` - -### 3. 批量用户信息查询 -```python -async def get_person_values(person_id: str, field_names: list, default_dict: Optional[dict] = None) -> dict: -``` -批量获取用户信息字段值 - -**Args:** -- `person_id`:用户的唯一标识ID -- `field_names`:要获取的字段名列表 -- `default_dict`:默认值字典,键为字段名,值为默认值 - -**Returns:** -- `dict`:字段名到值的映射字典 - -#### 示例 -```python -values = await person_api.get_person_values( - person_id, - ["nickname", "impression", "know_times"], - {"nickname": "未知用户", "know_times": 0} -) -``` - -### 4. 判断用户是否已知 -```python -async def is_person_known(platform: str, user_id: int) -> bool: -``` -判断是否认识某个用户 - -**Args:** -- `platform`:平台名称 -- `user_id`:用户ID - -**Returns:** -- `bool`:是否认识该用户 - -### 5. 根据用户名获取Person ID -```python -def get_person_id_by_name(person_name: str) -> str: -``` -根据用户名获取person_id - -**Args:** -- `person_name`:用户名 - -**Returns:** -- `str`:person_id,如果未找到返回空字符串 - -## 常用字段说明 - -### 基础信息字段 -- `nickname`:用户昵称 -- `platform`:平台信息 -- `user_id`:用户ID - -### 关系信息字段 -- `impression`:对用户的印象 -- `points`: 用户特征点 - -其他字段可以参考`PersonInfo`类的属性(位于`src.common.database.database_model`) - -## 注意事项 - -1. **异步操作**:部分查询函数都是异步的,需要使用`await` -2. **性能考虑**:批量查询优于单个查询 -3. **隐私保护**:确保用户信息的使用符合隐私政策 -4. **数据一致性**:person_id是用户的唯一标识,应妥善保存和使用 \ No newline at end of file diff --git a/docs-src/plugins/api/plugin-manage-api.md b/docs-src/plugins/api/plugin-manage-api.md deleted file mode 100644 index 688ea9ef..00000000 --- a/docs-src/plugins/api/plugin-manage-api.md +++ /dev/null @@ -1,105 +0,0 @@ -# 插件管理API - -插件管理API模块提供了对插件的加载、卸载、重新加载以及目录管理功能。 - -## 导入方式 -```python -from src.plugin_system.apis import plugin_manage_api -# 或者 -from src.plugin_system import plugin_manage_api -``` - -## 功能概述 - -插件管理API主要提供以下功能: -- **插件查询** - 列出当前加载的插件或已注册的插件。 -- **插件管理** - 加载、卸载、重新加载插件。 -- **插件目录管理** - 添加插件目录并重新扫描。 - -## 主要功能 - -### 1. 列出当前加载的插件 -```python -def list_loaded_plugins() -> List[str]: -``` -列出所有当前加载的插件。 - -**Returns:** -- `List[str]` - 当前加载的插件名称列表。 - -### 2. 列出所有已注册的插件 -```python -def list_registered_plugins() -> List[str]: -``` -列出所有已注册的插件。 - -**Returns:** -- `List[str]` - 已注册的插件名称列表。 - -### 3. 获取插件路径 -```python -def get_plugin_path(plugin_name: str) -> str: -``` -获取指定插件的路径。 - -**Args:** -- `plugin_name` (str): 要查询的插件名称。 -**Returns:** -- `str` - 插件的路径,如果插件不存在则 raise ValueError。 - -### 4. 卸载指定的插件 -```python -async def remove_plugin(plugin_name: str) -> bool: -``` -卸载指定的插件。 - -**Args:** -- `plugin_name` (str): 要卸载的插件名称。 - -**Returns:** -- `bool` - 卸载是否成功。 - -### 5. 重新加载指定的插件 -```python -async def reload_plugin(plugin_name: str) -> bool: -``` -重新加载指定的插件。 - -**Args:** -- `plugin_name` (str): 要重新加载的插件名称。 - -**Returns:** -- `bool` - 重新加载是否成功。 - -### 6. 加载指定的插件 -```python -def load_plugin(plugin_name: str) -> Tuple[bool, int]: -``` -加载指定的插件。 - -**Args:** -- `plugin_name` (str): 要加载的插件名称。 - -**Returns:** -- `Tuple[bool, int]` - 加载是否成功,成功或失败的个数。 - -### 7. 添加插件目录 -```python -def add_plugin_directory(plugin_directory: str) -> bool: -``` -添加插件目录。 - -**Args:** -- `plugin_directory` (str): 要添加的插件目录路径。 - -**Returns:** -- `bool` - 添加是否成功。 - -### 8. 重新扫描插件目录 -```python -def rescan_plugin_directory() -> Tuple[int, int]: -``` -重新扫描插件目录,加载新插件。 - -**Returns:** -- `Tuple[int, int]` - 成功加载的插件数量和失败的插件数量。 \ No newline at end of file diff --git a/docs-src/plugins/api/send-api.md b/docs-src/plugins/api/send-api.md deleted file mode 100644 index 8b3c607f..00000000 --- a/docs-src/plugins/api/send-api.md +++ /dev/null @@ -1,175 +0,0 @@ -# 消息发送API - -消息发送API模块专门负责发送各种类型的消息,支持文本、表情包、图片等多种消息类型。 - -## 导入方式 - -```python -from src.plugin_system.apis import send_api -# 或者 -from src.plugin_system import send_api -``` - -## 主要功能 - -### 1. 发送文本消息 -```python -async def text_to_stream( - text: str, - stream_id: str, - typing: bool = False, - reply_to: str = "", - storage_message: bool = True, -) -> bool: -``` -发送文本消息到指定的流 - -**Args:** -- `text` (str): 要发送的文本内容 -- `stream_id` (str): 聊天流ID -- `typing` (bool): 是否显示正在输入 -- `reply_to` (str): 回复消息,格式为"发送者:消息内容" -- `storage_message` (bool): 是否存储消息到数据库 - -**Returns:** -- `bool` - 是否发送成功 - -### 2. 发送表情包 -```python -async def emoji_to_stream(emoji_base64: str, stream_id: str, storage_message: bool = True) -> bool: -``` -向指定流发送表情包。 - -**Args:** -- `emoji_base64` (str): 表情包的base64编码 -- `stream_id` (str): 聊天流ID -- `storage_message` (bool): 是否存储消息到数据库 - -**Returns:** -- `bool` - 是否发送成功 - -### 3. 发送图片 -```python -async def image_to_stream(image_base64: str, stream_id: str, storage_message: bool = True) -> bool: -``` -向指定流发送图片。 - -**Args:** -- `image_base64` (str): 图片的base64编码 -- `stream_id` (str): 聊天流ID -- `storage_message` (bool): 是否存储消息到数据库 - -**Returns:** -- `bool` - 是否发送成功 - -### 4. 发送命令 -```python -async def command_to_stream(command: Union[str, dict], stream_id: str, storage_message: bool = True, display_message: str = "") -> bool: -``` -向指定流发送命令。 - -**Args:** -- `command` (Union[str, dict]): 命令内容 -- `stream_id` (str): 聊天流ID -- `storage_message` (bool): 是否存储消息到数据库 -- `display_message` (str): 显示消息 - -**Returns:** -- `bool` - 是否发送成功 - -### 5. 发送自定义类型消息 -```python -async def custom_to_stream( - message_type: str, - content: str, - stream_id: str, - display_message: str = "", - typing: bool = False, - reply_to: str = "", - storage_message: bool = True, - show_log: bool = True, -) -> bool: -``` -向指定流发送自定义类型消息。 - -**Args:** -- `message_type` (str): 消息类型,如"text"、"image"、"emoji"、"video"、"file"等 -- `content` (str): 消息内容(通常是base64编码或文本) -- `stream_id` (str): 聊天流ID -- `display_message` (str): 显示消息 -- `typing` (bool): 是否显示正在输入 -- `reply_to` (str): 回复消息,格式为"发送者:消息内容" -- `storage_message` (bool): 是否存储消息到数据库 -- `show_log` (bool): 是否显示日志 - -**Returns:** -- `bool` - 是否发送成功 - -## 使用示例 - -### 1. 基础文本发送,并回复消息 - -```python -from src.plugin_system.apis import send_api - -async def send_hello(chat_stream): - """发送问候消息""" - - success = await send_api.text_to_stream( - text="Hello, world!", - stream_id=chat_stream.stream_id, - typing=True, - reply_to="User:How are you?", - storage_message=True - ) - - return success -``` - -### 2. 发送表情包 - -```python -from src.plugin_system.apis import emoji_api -async def send_emoji_reaction(chat_stream, emotion): - """根据情感发送表情包""" - # 获取表情包 - emoji_result = await emoji_api.get_by_emotion(emotion) - if not emoji_result: - return False - - emoji_base64, description, matched_emotion = emoji_result - - # 发送表情包 - success = await send_api.emoji_to_stream( - emoji_base64=emoji_base64, - stream_id=chat_stream.stream_id, - storage_message=False # 不存储到数据库 - ) - - return success -``` - -## 消息类型说明 - -### 支持的消息类型 -- `"text"`:纯文本消息 -- `"emoji"`:表情包消息 -- `"image"`:图片消息 -- `"command"`:命令消息 -- `"video"`:视频消息(如果支持) -- `"audio"`:音频消息(如果支持) - -### 回复格式 -回复消息使用格式:`"发送者:消息内容"` 或 `"发送者:消息内容"` - -系统会自动查找匹配的原始消息并进行回复。 - -## 注意事项 - -1. **异步操作**:所有发送函数都是异步的,必须使用`await` -2. **错误处理**:发送失败时返回False,成功时返回True -3. **发送频率**:注意控制发送频率,避免被平台限制 -4. **内容限制**:注意平台对消息内容和长度的限制 -5. **权限检查**:确保机器人有发送消息的权限 -6. **编码格式**:图片和表情包需要使用base64编码 -7. **存储选项**:可以选择是否将发送的消息存储到数据库 \ No newline at end of file diff --git a/docs-src/plugins/api/tool-api.md b/docs-src/plugins/api/tool-api.md deleted file mode 100644 index bd6e7d2e..00000000 --- a/docs-src/plugins/api/tool-api.md +++ /dev/null @@ -1,55 +0,0 @@ -# 工具API - -工具API模块提供了获取和管理工具实例的功能,让插件能够访问系统中注册的工具。 - -## 导入方式 - -```python -from src.plugin_system.apis import tool_api -# 或者 -from src.plugin_system import tool_api -``` - -## 主要功能 - -### 1. 获取工具实例 - -```python -def get_tool_instance(tool_name: str) -> Optional[BaseTool]: -``` - -获取指定名称的工具实例。 - -**Args**: -- `tool_name`: 工具名称字符串 - -**Returns**: -- `Optional[BaseTool]`: 工具实例,如果工具不存在则返回 None - -### 2. 获取LLM可用的工具定义 - -```python -def get_llm_available_tool_definitions(): -``` - -获取所有LLM可用的工具定义列表。 - -**Returns**: -- `List[Tuple[str, Dict[str, Any]]]`: 工具定义列表,每个元素为 `(工具名称, 工具定义字典)` 的元组 - - 其具体定义请参照[tool-components.md](../tool-components.md#属性说明)中的工具定义格式。 -#### 示例: - -```python -# 获取所有LLM可用的工具定义 -tools = tool_api.get_llm_available_tool_definitions() -for tool_name, tool_definition in tools: - print(f"工具: {tool_name}") - print(f"定义: {tool_definition}") -``` - -## 注意事项 - -1. **工具存在性检查**:使用前请检查工具实例是否为 None -2. **权限控制**:某些工具可能有使用权限限制 -3. **异步调用**:大多数工具方法是异步的,需要使用 await -4. **错误处理**:调用工具时请做好异常处理 diff --git a/docs-src/plugins/command-components.md b/docs-src/plugins/command-components.md deleted file mode 100644 index 77cc8acc..00000000 --- a/docs-src/plugins/command-components.md +++ /dev/null @@ -1,89 +0,0 @@ -# 💻 Command组件详解 - -## 📖 什么是Command - -Command是直接响应用户明确指令的组件,与Action不同,Command是**被动触发**的,当用户输入特定格式的命令时立即执行。 - -Command通过正则表达式匹配用户输入,提供确定性的功能服务。 - -### 🎯 Command的特点 - -- 🎯 **确定性执行**:匹配到命令立即执行,无随机性 -- ⚡ **即时响应**:用户主动触发,快速响应 -- 🔍 **正则匹配**:通过正则表达式精确匹配用户输入 -- 🛑 **拦截控制**:可以控制是否阻止消息继续处理 -- 📝 **参数解析**:支持从用户输入中提取参数 - ---- - -## 🛠️ Command组件的基本结构 - -首先,Command组件需要继承自`BaseCommand`类,并实现必要的方法。 - -```python -class ExampleCommand(BaseCommand): - command_name = "example" # 命令名称,作为唯一标识符 - command_description = "这是一个示例命令" # 命令描述 - command_pattern = r"" # 命令匹配的正则表达式 - - async def execute(self) -> Tuple[bool, Optional[str], bool]: - """ - 执行Command的主要逻辑 - - Returns: - Tuple[bool, str, bool]: - - 第一个bool表示是否成功执行 - - 第二个str是执行结果消息 - - 第三个bool表示是否需要阻止消息继续处理 - """ - # ---- 执行命令的逻辑 ---- - return True, "执行成功", False -``` -**`command_pattern`**: 该Command匹配的正则表达式,用于精确匹配用户输入。 - -请注意:如果希望能获取到命令中的参数,请在正则表达式中使用有命名的捕获组,例如`(?Ppattern)`。 - -这样在匹配时,内部实现可以使用`re.match.groupdict()`方法获取到所有捕获组的参数,并以字典的形式存储在`self.matched_groups`中。 - -### 匹配样例 -假设我们有一个命令`/example param1=value1 param2=value2`,对应的正则表达式可以是: - -```python -class ExampleCommand(BaseCommand): - command_name = "example" - command_description = "这是一个示例命令" - command_pattern = r"/example (?P\w+) (?P\w+)" - - async def execute(self) -> Tuple[bool, Optional[str], bool]: - # 获取匹配的参数 - param1 = self.matched_groups.get("param1") - param2 = self.matched_groups.get("param2") - - # 执行逻辑 - return True, f"参数1: {param1}, 参数2: {param2}", False -``` - ---- - -## Command 内置方法说明 -```python -class BaseCommand: - def get_config(self, key: str, default=None): - """获取插件配置值,使用嵌套键访问""" - - async def send_text(self, content: str, reply_to: str = "") -> bool: - """发送回复消息""" - - async def send_type(self, message_type: str, content: str, display_message: str = "", typing: bool = False, reply_to: str = "") -> bool: - """发送指定类型的回复消息到当前聊天环境""" - - async def send_command(self, command_name: str, args: Optional[dict] = None, display_message: str = "", storage_message: bool = True) -> bool: - """发送命令消息""" - - async def send_emoji(self, emoji_base64: str) -> bool: - """发送表情包""" - - async def send_image(self, image_base64: str) -> bool: - """发送图片""" -``` -具体参数与用法参见`BaseCommand`基类的定义。 \ No newline at end of file diff --git a/docs-src/plugins/configuration-guide.md b/docs-src/plugins/configuration-guide.md deleted file mode 100644 index ef334472..00000000 --- a/docs-src/plugins/configuration-guide.md +++ /dev/null @@ -1,347 +0,0 @@ -# ⚙️ 插件配置完整指南 - -本文档将全面指导你如何为你的插件**定义配置**和在组件中**访问配置**,帮助你构建一个健壮、规范且自带文档的配置系统。 - -> **🚨 重要原则:任何时候都不要手动创建 config.toml 文件!** -> -> 系统会根据你在代码中定义的 `config_schema` 自动生成配置文件。手动创建配置文件会破坏自动化流程,导致配置不一致、缺失注释和文档等问题。 - -## 配置版本管理 - -### 🎯 版本管理概述 - -插件系统提供了强大的**配置版本管理机制**,可以在插件升级时自动处理配置文件的迁移和更新,确保配置结构始终与代码保持同步。 - -### 🔄 配置版本管理工作流程 - -```mermaid -graph TD - A[插件加载] --> B[检查配置文件] - B --> C{配置文件存在?} - C -->|不存在| D[生成默认配置] - C -->|存在| E[读取当前版本] - E --> F{有版本信息?} - F -->|无版本| G[跳过版本检查
直接加载配置] - F -->|有版本| H{版本匹配?} - H -->|匹配| I[直接加载配置] - H -->|不匹配| J[配置迁移] - J --> K[生成新配置结构] - K --> L[迁移旧配置值] - L --> M[保存迁移后配置] - M --> N[配置加载完成] - D --> N - G --> N - I --> N - - style J fill:#FFB6C1 - style K fill:#90EE90 - style G fill:#87CEEB - style N fill:#DDA0DD -``` - -### 📊 版本管理策略 - -#### 1. 配置版本定义 - -在 `config_schema` 的 `plugin` 节中定义 `config_version`: - -```python -config_schema = { - "plugin": { - "enabled": ConfigField(type=bool, default=False, description="是否启用插件"), - "config_version": ConfigField(type=str, default="1.2.0", description="配置文件版本"), - }, - # 其他配置... -} -``` - -#### 2. 版本检查行为 - -- **无版本信息** (`config_version` 不存在) - - 系统会**跳过版本检查**,直接加载现有配置 - - 适用于旧版本插件的兼容性处理 - - 日志显示:`配置文件无版本信息,跳过版本检查` - -- **有版本信息** (存在 `config_version` 字段) - - 比较当前版本与期望版本 - - 版本不匹配时自动执行配置迁移 - - 版本匹配时直接加载配置 - -#### 3. 配置迁移过程 - -当检测到版本不匹配时,系统会: - -1. **生成新配置结构** - 根据最新的 `config_schema` 生成新的配置结构 -2. **迁移配置值** - 将旧配置文件中的值迁移到新结构中 -3. **处理新增字段** - 新增的配置项使用默认值 -4. **更新版本号** - `config_version` 字段自动更新为最新版本 -5. **保存配置文件** - 迁移后的配置直接覆盖原文件**(不保留备份)** - -### 🔧 实际使用示例 - -#### 版本升级场景 - -假设你的插件从 v1.0 升级到 v1.1,新增了权限管理功能: - -**旧版本配置 (v1.0.0):** -```toml -[plugin] -enabled = true -config_version = "1.0.0" - -[mute] -min_duration = 60 -max_duration = 3600 -``` - -**新版本Schema (v1.1.0):** -```python -config_schema = { - "plugin": { - "enabled": ConfigField(type=bool, default=False, description="是否启用插件"), - "config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"), - }, - "mute": { - "min_duration": ConfigField(type=int, default=60, description="最短禁言时长(秒)"), - "max_duration": ConfigField(type=int, default=2592000, description="最长禁言时长(秒)"), - }, - "permissions": { # 新增的配置节 - "allowed_users": ConfigField(type=list, default=[], description="允许的用户列表"), - "allowed_groups": ConfigField(type=list, default=[], description="允许的群组列表"), - } -} -``` - -**迁移后配置 (v1.1.0):** -```toml -[plugin] -enabled = true # 保留原值 -config_version = "1.1.0" # 自动更新 - -[mute] -min_duration = 60 # 保留原值 -max_duration = 3600 # 保留原值 - -[permissions] # 新增节,使用默认值 -allowed_users = [] -allowed_groups = [] -``` - -#### 无版本配置的兼容性 - -对于没有版本信息的旧配置文件: - -**旧配置文件(无版本):** -```toml -[plugin] -enabled = true -# 没有 config_version 字段 - -[mute] -min_duration = 120 -``` - -**系统行为:** -- 检测到无版本信息 -- 跳过版本检查和迁移 -- 直接加载现有配置 -- 新增的配置项在代码中使用默认值访问 -- 系统会详细记录配置迁移过程。 - -### ⚠️ 重要注意事项 - -#### 1. 版本号管理 -- 当你修改 `config_schema` 时,**必须同步更新** `config_version` -- 请使用语义化版本号 (例如:`1.0.0`, `1.1.0`, `2.0.0`) - -#### 2. 迁移策略 -- **保留原值优先**: 迁移时优先保留用户的原有配置值 -- **新增字段默认值**: 新增的配置项使用Schema中定义的默认值 -- **移除字段警告**: 如果某个配置项在新版本中被移除,会在日志中显示警告 - -#### 3. 兼容性考虑 -- **旧版本兼容**: 无版本信息的配置文件会跳过版本检查 -- **不保留备份**: 迁移后直接覆盖原配置文件,不保留备份 -- **失败安全**: 如果迁移过程中出现错误,会回退到原配置 - -## 配置定义 - -配置的定义在你的插件主类(继承自 `BasePlugin`)中完成,主要通过两个类属性: - -1. `config_section_descriptions`: 一个字典,用于描述配置文件的各个区段(`[section]`)。 -2. `config_schema`: 核心部分,一个嵌套字典,用于定义每个区段下的具体配置项。 - -### `ConfigField`:配置项的基石 - -每个配置项都通过一个 `ConfigField` 对象来定义。 - -```python -from dataclasses import dataclass -from src.plugin_system.base.config_types import ConfigField - -@dataclass -class ConfigField: - """配置字段定义""" - type: type # 字段类型 (例如 str, int, float, bool, list) - default: Any # 默认值 - description: str # 字段描述 (将作为注释生成到配置文件中) - example: Optional[str] = None # 示例值 (可选) - required: bool = False # 是否必需 (可选, 主要用于文档提示) - choices: Optional[List[Any]] = None # 可选值列表 (可选) -``` - -### 配置示例 - -让我们以一个功能丰富的 `MutePlugin` 为例,看看如何定义它的配置。 - -```python -# src/plugins/built_in/mute_plugin/plugin.py - -from src.plugin_system import BasePlugin, register_plugin, ConfigField -from typing import List, Tuple, Type - -@register_plugin -class MutePlugin(BasePlugin): - """禁言插件""" - - # 这里是插件基本信息,略去 - - # 步骤1: 定义配置节的描述 - config_section_descriptions = { - "plugin": "插件启用配置", - "components": "组件启用控制", - "mute": "核心禁言功能配置", - "smart_mute": "智能禁言Action的专属配置", - "logging": "日志记录相关配置" - } - - # 步骤2: 使用ConfigField定义详细的配置Schema - config_schema = { - "plugin": { - "enabled": ConfigField(type=bool, default=False, description="是否启用插件") - }, - "components": { - "enable_smart_mute": ConfigField(type=bool, default=True, description="是否启用智能禁言Action"), - "enable_mute_command": ConfigField(type=bool, default=False, description="是否启用禁言命令Command") - }, - "mute": { - "min_duration": ConfigField(type=int, default=60, description="最短禁言时长(秒)"), - "max_duration": ConfigField(type=int, default=2592000, description="最长禁言时长(秒),默认30天"), - "templates": ConfigField( - type=list, - default=["好的,禁言 {target} {duration},理由:{reason}", "收到,对 {target} 执行禁言 {duration}"], - description="成功禁言后发送的随机消息模板" - ) - }, - "smart_mute": { - "keyword_sensitivity": ConfigField( - type=str, - default="normal", - description="关键词激活的敏感度", - choices=["low", "normal", "high"] # 定义可选值 - ), - }, - "logging": { - "level": ConfigField( - type=str, - default="INFO", - description="日志记录级别", - choices=["DEBUG", "INFO", "WARNING", "ERROR"] - ), - "prefix": ConfigField(type=str, default="[MutePlugin]", description="日志记录前缀", example="[MyMutePlugin]") - } - } - - # 这里是插件方法,略去 -``` - -当 `mute_plugin` 首次加载且其目录中不存在 `config.toml` 时,系统会自动创建以下文件: - -```toml -# mute_plugin - 自动生成的配置文件 -# 群聊禁言管理插件,提供智能禁言功能 - -# 插件启用配置 -[plugin] - -# 是否启用插件 -enabled = false - - -# 组件启用控制 -[components] - -# 是否启用智能禁言Action -enable_smart_mute = true - -# 是否启用禁言命令Command -enable_mute_command = false - - -# 核心禁言功能配置 -[mute] - -# 最短禁言时长(秒) -min_duration = 60 - -# 最长禁言时长(秒),默认30天 -max_duration = 2592000 - -# 成功禁言后发送的随机消息模板 -templates = ["好的,禁言 {target} {duration},理由:{reason}", "收到,对 {target} 执行禁言 {duration}"] - - -# 智能禁言Action的专属配置 -[smart_mute] - -# 关键词激活的敏感度 -# 可选值: low, normal, high -keyword_sensitivity = "normal" - - -# 日志记录相关配置 -[logging] - -# 日志记录级别 -# 可选值: DEBUG, INFO, WARNING, ERROR -level = "INFO" - -# 日志记录前缀 -# 示例: [MyMutePlugin] -prefix = "[MutePlugin]" -``` - ---- - -## 配置访问 - -如果你想要在你的组件中访问配置,可以通过组件内置的 `get_config()` 方法访问配置。 - -其参数为一个命名空间化的字符串。以上面的 `MutePlugin` 为例,你可以这样访问配置: - -```python -enable_smart_mute = self.get_config("components.enable_smart_mute", True) -``` - -如果尝试访问了一个不存在的配置项,系统会自动返回默认值(你传递的)或者 `None`。 - ---- - -## 最佳实践与注意事项 - - -**🚨 核心原则:永远不要手动创建 config.toml 文件!** - -1. **🔥 绝不手动创建配置文件**: **任何时候都不要手动创建 `config.toml` 文件**!必须通过在 `plugin.py` 中定义 `config_schema` 让系统自动生成。 - - ❌ **禁止**:`touch config.toml`、手动编写配置文件 - - ✅ **正确**:定义 `config_schema`,启动插件,让系统自动生成 - -2. **Schema优先**: 所有配置项都必须在 `config_schema` 中声明,包括类型、默认值和描述。 - -3. **描述清晰**: 为每个 `ConfigField` 和 `config_section_descriptions` 编写清晰、准确的描述。这会直接成为你的插件文档的一部分。 - -4. **提供合理默认值**: 确保你的插件在默认配置下就能正常运行(或处于一个安全禁用的状态)。 - -5. **gitignore**: 将 `plugins/*/config.toml` 或 `src/plugins/built_in/*/config.toml` 加入 `.gitignore`,以避免提交个人敏感信息。 - -6. **配置文件只供修改**: 自动生成的 `config.toml` 文件只应该被用户**修改**,而不是从零创建。 \ No newline at end of file diff --git a/docs-src/plugins/dependency-management.md b/docs-src/plugins/dependency-management.md deleted file mode 100644 index 4bb4ed00..00000000 --- a/docs-src/plugins/dependency-management.md +++ /dev/null @@ -1,40 +0,0 @@ -# 📦 插件依赖管理系统 - -现在的Python依赖包管理依然存在问题,请保留你的`python_dependencies`属性,等待后续重构。 - -## 📚 详细教程 - -### PythonDependency 类详解 - -`PythonDependency`是依赖声明的核心类: - -```python -PythonDependency( - package_name="PIL", # 导入时的包名 - version=">=11.2.0", # 版本要求 - optional=False, # 是否为可选依赖 - description="图像处理库", # 依赖描述 - install_name="pillow" # pip安装时的包名(可选) -) -``` - -#### 参数说明 - -| 参数 | 类型 | 必需 | 说明 | -|------|------|------|------| -| `package_name` | str | ✅ | Python导入时使用的包名(如`requests`) | -| `version` | str | ❌ | 版本要求,使用pip格式(如`>=1.0.0`, `==2.1.3`) | -| `optional` | bool | ❌ | 是否为可选依赖,默认`False` | -| `description` | str | ❌ | 依赖的用途描述 | -| `install_name` | str | ❌ | pip安装时的包名,默认与`package_name`相同,用于处理安装名称和导入名称不一致的情况 | - -#### 版本格式示例 - -```python -# 常用版本格式 -PythonDependency("requests", ">=2.25.0") # 最小版本 -PythonDependency("numpy", ">=1.20.0,<2.0.0") # 版本范围 -PythonDependency("pillow", "==8.3.2") # 精确版本 -PythonDependency("scipy", ">=1.7.0,!=1.8.0") # 排除特定版本 -``` - diff --git a/docs-src/plugins/image/quick-start/1750326700269.png b/docs-src/plugins/image/quick-start/1750326700269.png deleted file mode 100644 index 1dc4f19b5fc03cb1cc786931cf5aaef9432fdd42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4240 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 diff --git a/docs-src/plugins/index.md b/docs-src/plugins/index.md deleted file mode 100644 index 2454c98a..00000000 --- a/docs-src/plugins/index.md +++ /dev/null @@ -1,81 +0,0 @@ -# MaiBot插件开发文档 - -> 欢迎来到MaiBot插件系统开发文档!这里是你开始插件开发旅程的最佳起点。 - -## 新手入门 - -- [📖 快速开始指南](quick-start.md) - 快速创建你的第一个插件 - -## 组件功能详解 - -- [🧱 Action组件详解](action-components.md) - 掌握最核心的Action组件 -- [💻 Command组件详解](command-components.md) - 学习直接响应命令的组件 -- [🔧 Tool组件详解](tool-components.md) - 了解如何扩展信息获取能力 -- [⚙️ 配置文件系统指南](configuration-guide.md) - 学会使用自动生成的插件配置文件 -- [📄 Manifest系统指南](manifest-guide.md) - 了解插件元数据管理和配置架构 - -Command vs Action 选择指南 - -1. 使用Command的场景 - -- ✅ 用户需要明确调用特定功能 -- ✅ 需要精确的参数控制 -- ✅ 管理和配置操作 -- ✅ 查询和信息显示 -- ✅ 系统维护命令 - -2. 使用Action的场景 - -- ✅ 增强麦麦的智能行为 -- ✅ 根据上下文自动触发 -- ✅ 情绪和表情表达 -- ✅ 智能建议和帮助 -- ✅ 随机化的互动 - - -## API浏览 - -### 消息发送与处理API -- [📤 发送API](api/send-api.md) - 各种类型消息发送接口 -- [消息API](api/message-api.md) - 消息获取,消息构建,消息查询接口 -- [聊天流API](api/chat-api.md) - 聊天流管理和查询接口 - -### AI与生成API -- [LLM API](api/llm-api.md) - 大语言模型交互接口,可以使用内置LLM生成内容 -- [✨ 回复生成器API](api/generator-api.md) - 智能回复生成接口,可以使用内置风格化生成器 - -### 表情包API -- [😊 表情包API](api/emoji-api.md) - 表情包选择和管理接口 - -### 关系系统API -- [人物信息API](api/person-api.md) - 用户信息,处理麦麦认识的人和关系的接口 - -### 数据与配置API -- [🗄️ 数据库API](api/database-api.md) - 数据库操作接口 -- [⚙️ 配置API](api/config-api.md) - 配置读取和用户信息接口 - -### 插件和组件管理API -- [🔌 插件API](api/plugin-manage-api.md) - 插件加载和管理接口 -- [🧩 组件API](api/component-manage-api.md) - 组件注册和管理接口 - -### 日志API -- [📜 日志API](api/logging-api.md) - logger实例获取接口 -### 工具API -- [🔧 工具API](api/tool-api.md) - tool获取接口 - - - -## 支持 - -> 如果你在文档中发现错误或需要补充,请: - -1. 检查最新的文档版本 -2. 查看相关示例代码 -3. 参考其他类似插件 -4. 提交文档仓库issue - -## 一个方便的小设计 - -我们在`__init__.py`中定义了一个`__all__`变量,包含了所有需要导出的类和函数。 -这样在其他地方导入时,可以直接使用 `from src.plugin_system import *` 来导入所有插件相关的类和函数。 -或者你可以直接使用 `from src.plugin_system import BasePlugin, register_plugin, ComponentInfo` 之类的方式来导入你需要的部分。 \ No newline at end of file diff --git a/docs-src/plugins/manifest-guide.md b/docs-src/plugins/manifest-guide.md deleted file mode 100644 index d3dd746a..00000000 --- a/docs-src/plugins/manifest-guide.md +++ /dev/null @@ -1,205 +0,0 @@ -# 📄 插件Manifest系统指南 - -## 概述 - -MaiBot插件系统现在强制要求每个插件都必须包含一个 `_manifest.json` 文件。这个文件描述了插件的基本信息、依赖关系、组件等重要元数据。 - -### 🔄 配置架构:Manifest与Config的职责分离 - -为了避免信息重复和提高维护性,我们采用了**双文件架构**: - -- **`_manifest.json`** - 插件的**静态元数据** - - 插件身份信息(名称、版本、描述) - - 开发者信息(作者、许可证、仓库) - - 系统信息(兼容性、组件列表、分类) - -- **`config.toml`** - 插件的**运行时配置** - - 启用状态 (`enabled`) - - 功能参数配置 - - 用户可调整的行为设置 - -这种分离确保了: -- ✅ 元数据信息统一管理 -- ✅ 运行时配置灵活调整 -- ✅ 避免重复维护 -- ✅ 更清晰的职责划分 - -## 🔧 Manifest文件结构 - -### 必需字段 - -以下字段是必需的,不能为空: - -```json -{ - "manifest_version": 1, - "name": "插件显示名称", - "version": "1.0.0", - "description": "插件功能描述", - "author": { - "name": "作者名称" - } -} -``` - -### 可选字段 - -以下字段都是可选的,可以根据需要添加: - -```json -{ - "license": "MIT", - "host_application": { - "min_version": "1.0.0", - "max_version": "4.0.0" - }, - "homepage_url": "https://github.com/your-repo", - "repository_url": "https://github.com/your-repo", - "keywords": ["关键词1", "关键词2"], - "categories": ["分类1", "分类2"], - "default_locale": "zh-CN", - "locales_path": "_locales", - "plugin_info": { - "is_built_in": false, - "plugin_type": "general", - "components": [ - { - "type": "action", - "name": "组件名称", - "description": "组件描述" - } - ] - } -} -``` - -## 🛠️ 管理工具 - -### 使用manifest_tool.py - -我们提供了一个命令行工具来帮助管理manifest文件: - -```bash -# 扫描缺少manifest的插件 -python scripts/manifest_tool.py scan src/plugins - -# 为插件创建最小化manifest文件 -python scripts/manifest_tool.py create-minimal src/plugins/my_plugin --name "我的插件" --author "作者" - -# 为插件创建完整manifest模板 -python scripts/manifest_tool.py create-complete src/plugins/my_plugin --name "我的插件" - -# 验证manifest文件 -python scripts/manifest_tool.py validate src/plugins/my_plugin -``` - -### 验证示例 - -验证通过的示例: -``` -✅ Manifest文件验证通过 -``` - -验证失败的示例: -``` -❌ 验证错误: - - 缺少必需字段: name - - 作者信息缺少name字段或为空 -⚠️ 验证警告: - - 建议填写字段: license - - 建议填写字段: keywords -``` - -## 🔄 迁移指南 - -### 对于现有插件 - -1. **检查缺少manifest的插件**: - ```bash - python scripts/manifest_tool.py scan src/plugins - ``` - -2. **为每个插件创建manifest**: - ```bash - python scripts/manifest_tool.py create-minimal src/plugins/your_plugin - ``` - -3. **编辑manifest文件**,填写正确的信息。 - -4. **验证manifest**: - ```bash - python scripts/manifest_tool.py validate src/plugins/your_plugin - ``` - -### 对于新插件 - -创建新插件时,建议的步骤: - -1. **创建插件目录和基本文件** -2. **创建完整manifest模板**: - ```bash - python scripts/manifest_tool.py create-complete src/plugins/new_plugin - ``` -3. **根据实际情况修改manifest文件** -4. **编写插件代码** -5. **验证manifest文件** - -## 📋 字段说明 - -### 基本信息 -- `manifest_version`: manifest格式版本,当前为1 -- `name`: 插件显示名称(必需) -- `version`: 插件版本号(必需) -- `description`: 插件功能描述(必需) -- `author`: 作者信息(必需) - - `name`: 作者名称(必需) - - `url`: 作者主页(可选) - -### 许可和URL -- `license`: 插件许可证(可选,建议填写) -- `homepage_url`: 插件主页(可选) -- `repository_url`: 源码仓库地址(可选) - -### 分类和标签 -- `keywords`: 关键词数组(可选,建议填写) -- `categories`: 分类数组(可选,建议填写) - -### 兼容性 -- `host_application`: 主机应用兼容性(可选,建议填写) - - `min_version`: 最低兼容版本 - - `max_version`: 最高兼容版本 - -⚠️ 在不填写的情况下,插件将默认支持所有版本。**(由于我们在不同版本对插件系统进行了大量的重构,这种情况几乎不可能。)** - -### 国际化 -- `default_locale`: 默认语言(可选) -- `locales_path`: 语言文件目录(可选) - -### 插件特定信息 -- `plugin_info`: 插件详细信息(可选) - - `is_built_in`: 是否为内置插件 - - `plugin_type`: 插件类型 - - `components`: 组件列表 - -## ⚠️ 注意事项 - -1. **强制要求**:所有插件必须包含`_manifest.json`文件,否则无法加载 -2. **编码格式**:manifest文件必须使用UTF-8编码 -3. **JSON格式**:文件必须是有效的JSON格式 -4. **必需字段**:`manifest_version`、`name`、`version`、`description`、`author.name`是必需的 -5. **版本兼容**:当前只支持`manifest_version = 1` - -## 🔍 常见问题 - -### Q: 可以不填写可选字段吗? -A: 可以。所有标记为"可选"的字段都可以不填写,但建议至少填写`license`和`keywords`。 - -### Q: manifest验证失败怎么办? -A: 根据验证器的错误提示修复相应问题。错误会导致插件加载失败,警告不会。 - -## 📚 参考示例 - -查看内置插件的manifest文件作为参考: -- `src/plugins/built_in/core_actions/_manifest.json` -- `src/plugins/built_in/tts_plugin/_manifest.json` -- `src/plugins/hello_world_plugin/_manifest.json` diff --git a/docs-src/plugins/quick-start.md b/docs-src/plugins/quick-start.md deleted file mode 100644 index 48eff603..00000000 --- a/docs-src/plugins/quick-start.md +++ /dev/null @@ -1,428 +0,0 @@ -# 🚀 快速开始指南 - -本指南将带你从零开始创建一个功能完整的MaiCore插件。 - -## 📖 概述 - -这个指南将带你快速创建你的第一个MaiCore插件。我们将创建一个简单的问候插件,展示插件系统的基本概念。 - -以下代码都在我们的`plugins/hello_world_plugin/`目录下。 - -### 一个方便的小设计 - -在开发中,我们在`__init__.py`中定义了一个`__all__`变量,包含了所有需要导出的类和函数。 -这样在其他地方导入时,可以直接使用 `from src.plugin_system import *` 来导入所有插件相关的类和函数。 -或者你可以直接使用 `from src.plugin_system import BasePlugin, register_plugin, ComponentInfo` 之类的方式来导入你需要的部分。 - -### 📂 准备工作 - -确保你已经: - -1. 克隆了MaiCore项目 -2. 安装了Python依赖 -3. 了解基本的Python语法 - -## 🏗️ 创建插件 - -### 1. 创建插件目录 - -在项目根目录的 `plugins/` 文件夹下创建你的插件目录 - -这里我们创建一个名为 `hello_world_plugin` 的目录 - -### 2. 创建`_manifest.json`文件 - -在插件目录下面创建一个 `_manifest.json` 文件,内容如下: - -```json -{ - "manifest_version": 1, - "name": "Hello World 插件", - "version": "1.0.0", - "description": "一个简单的 Hello World 插件", - "author": { - "name": "你的名字" - } -} -``` - -有关 `_manifest.json` 的详细说明,请参考 [Manifest文件指南](./manifest-guide.md)。 - -### 3. 创建最简单的插件 - -让我们从最基础的开始!创建 `plugin.py` 文件: - -```python -from typing import List, Tuple, Type -from src.plugin_system import BasePlugin, register_plugin, ComponentInfo - -@register_plugin # 注册插件 -class HelloWorldPlugin(BasePlugin): - """Hello World插件 - 你的第一个MaiCore插件""" - - # 以下是插件基本信息和方法(必须填写) - plugin_name = "hello_world_plugin" - enable_plugin = True # 启用插件 - dependencies = [] # 插件依赖列表(目前为空) - python_dependencies = [] # Python依赖列表(目前为空) - config_file_name = "config.toml" # 配置文件名 - config_schema = {} # 配置文件模式(目前为空) - - def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: # 获取插件组件 - """返回插件包含的组件列表(目前是空的)""" - return [] -``` - -🎉 恭喜!你刚刚创建了一个最简单但完整的MaiCore插件! - -**解释一下这些代码:** - -- 首先,我们在`plugin.py`中定义了一个HelloWorldPlugin插件类,继承自 `BasePlugin` ,提供基本功能。 -- 通过给类加上,`@register_plugin` 装饰器,我们告诉系统"这是一个插件" -- `plugin_name` 等是插件的基本信息,必须填写 -- `get_plugin_components()` 返回插件的功能组件,现在我们没有定义任何 Action, Command 或者 EventHandler,所以返回空列表。 - -### 4. 测试基础插件 - -现在就可以测试这个插件了!启动MaiCore: - -直接通过启动器运行MaiCore或者 `python bot.py` - -在日志中你应该能看到插件被加载的信息。虽然插件还没有任何功能,但它已经成功运行了! - -![1750326700269](image/quick-start/1750326700269.png) - -### 5. 添加第一个功能:问候Action - -现在我们要给插件加入一个有用的功能,我们从最好玩的Action做起 - -Action是一类可以让MaiCore根据自身意愿选择使用的“动作”,在MaiCore中,不论是“回复”还是“不回复”,或者“发送表情”以及“禁言”等等,都是通过Action实现的。 - -你可以通过编写动作,来拓展MaiCore的能力,包括发送语音,截图,甚至操作文件,编写代码...... - -现在让我们给插件添加第一个简单的功能。这个Action可以对用户发送一句问候语。 - -在 `plugin.py` 文件中添加Action组件,完整代码如下: - -```python -from typing import List, Tuple, Type -from src.plugin_system import ( - BasePlugin, register_plugin, BaseAction, - ComponentInfo, ActionActivationType, ChatMode -) - -# ===== Action组件 ===== - -class HelloAction(BaseAction): - """问候Action - 简单的问候动作""" - - # === 基本信息(必须填写)=== - action_name = "hello_greeting" - action_description = "向用户发送问候消息" - activation_type = ActionActivationType.ALWAYS # 始终激活 - - # === 功能描述(必须填写)=== - action_parameters = {"greeting_message": "要发送的问候消息"} - action_require = ["需要发送友好问候时使用", "当有人向你问好时使用", "当你遇见没有见过的人时使用"] - associated_types = ["text"] - - async def execute(self) -> Tuple[bool, str]: - """执行问候动作 - 这是核心功能""" - # 发送问候消息 - greeting_message = self.action_data.get("greeting_message", "") - base_message = self.get_config("greeting.message", "嗨!很开心见到你!😊") - message = base_message + greeting_message - await self.send_text(message) - - return True, "发送了问候消息" - -@register_plugin -class HelloWorldPlugin(BasePlugin): - """Hello World插件 - 你的第一个MaiCore插件""" - - # 插件基本信息 - plugin_name = "hello_world_plugin" - enable_plugin = True - dependencies = [] - python_dependencies = [] - config_file_name = "config.toml" - config_schema = {} - - def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: - """返回插件包含的组件列表""" - return [ - # 添加我们的问候Action - (HelloAction.get_action_info(), HelloAction), - ] -``` - -**解释一下这些代码:** - -- `HelloAction` 是我们定义的问候动作类,继承自 `BaseAction`,并实现了核心功能。 -- 在 `HelloWorldPlugin` 中,我们通过 `get_plugin_components()` 方法,通过调用`get_action_info()`这个内置方法将 `HelloAction` 注册为插件的一个组件。 -- 这样一来,当插件被加载时,问候动作也会被一并加载,并可以在MaiCore中使用。 -- `execute()` 函数是Action的核心,定义了当Action被MaiCore选择后,具体要做什么 -- `self.send_text()` 是发送文本消息的便捷方法 - -Action 组件中有关`activation_type`、`action_parameters`、`action_require`、`associated_types` 等的详细说明请参考 [Action组件指南](./action-components.md)。 - -### 6. 测试问候Action - -重启MaiCore,然后在聊天中发送任意消息,比如: - -``` -你好 -``` - -MaiCore可能会选择使用你的问候Action,发送回复: - -``` -嗨!很开心见到你!😊 -``` - -![1750332508760](image/quick-start/1750332508760.png) - -> **💡 小提示**:MaiCore会智能地决定什么时候使用它。如果没有立即看到效果,多试几次不同的消息。 - -🎉 太棒了!你的插件已经有实际功能了! - -### 7. 添加第二个功能:时间查询Command - -现在让我们添加一个Command组件。Command和Action不同,它是直接响应用户命令的: - -Command是最简单,最直接的响应,不由LLM判断选择使用 - -```python -# 在现有代码基础上,添加Command组件 -import datetime -from src.plugin_system import BaseCommand -#导入Command基类 - -class TimeCommand(BaseCommand): - """时间查询Command - 响应/time命令""" - - command_name = "time" - command_description = "查询当前时间" - - # === 命令设置(必须填写)=== - command_pattern = r"^/time$" # 精确匹配 "/time" 命令 - - async def execute(self) -> Tuple[bool, Optional[str], bool]: - """执行时间查询""" - # 获取当前时间 - time_format: str = "%Y-%m-%d %H:%M:%S" - now = datetime.datetime.now() - time_str = now.strftime(time_format) - - # 发送时间信息 - message = f"⏰ 当前时间:{time_str}" - await self.send_text(message) - - return True, f"显示了当前时间: {time_str}", True - -@register_plugin -class HelloWorldPlugin(BasePlugin): - """Hello World插件 - 你的第一个MaiCore插件""" - - # 插件基本信息 - plugin_name = "hello_world_plugin" - enable_plugin = True - dependencies = [] - python_dependencies = [] - config_file_name = "config.toml" - config_schema = {} - - def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: - return [ - (HelloAction.get_action_info(), HelloAction), - (TimeCommand.get_command_info(), TimeCommand), - ] -``` - -同样的,我们通过 `get_plugin_components()` 方法,通过调用`get_action_info()`这个内置方法将 `TimeCommand` 注册为插件的一个组件。 - -**Command组件解释:** - -- `command_pattern` 使用正则表达式匹配用户输入 -- `^/time$` 表示精确匹配 "/time" - -有关 Command 组件的更多信息,请参考 [Command组件指南](./command-components.md)。 - -### 8. 测试时间查询Command - -重启MaiCore,发送命令: - -``` -/time -``` - -你应该会收到回复: - -``` -⏰ 当前时间:2024-01-01 12:00:00 -``` - -🎉 太棒了!现在你已经了解了基本的 Action 和 Command 组件的使用方法。你可以根据自己的需求,继续扩展插件的功能,添加更多的 Action 和 Command 组件,让你的插件更加丰富和强大! - ---- - -## 进阶教程 - -如果你想让插件更加灵活和强大,可以参考接下来的进阶教程。 - -### 1. 添加配置文件 - -想要为插件添加配置文件吗?让我们一起来配置`config_schema`属性! - -> **🚨 重要:不要手动创建config.toml文件!** -> -> 我们需要在插件代码中定义配置Schema,让系统自动生成配置文件。 - -首先,在插件类中定义配置Schema: - -```python -from src.plugin_system import ConfigField - -@register_plugin -class HelloWorldPlugin(BasePlugin): - """Hello World插件 - 你的第一个MaiCore插件""" - - # 插件基本信息 - plugin_name: str = "hello_world_plugin" # 内部标识符 - enable_plugin: bool = True - dependencies: List[str] = [] # 插件依赖列表 - python_dependencies: List[str] = [] # Python包依赖列表 - config_file_name: str = "config.toml" # 配置文件名 - - # 配置Schema定义 - config_schema: dict = { - "plugin": { - "name": ConfigField(type=str, default="hello_world_plugin", description="插件名称"), - "version": ConfigField(type=str, default="1.0.0", description="插件版本"), - "enabled": ConfigField(type=bool, default=False, description="是否启用插件"), - }, - "greeting": { - "message": ConfigField(type=str, default="嗨!很开心见到你!😊", description="默认问候消息"), - "enable_emoji": ConfigField(type=bool, default=True, description="是否启用表情符号"), - }, - "time": {"format": ConfigField(type=str, default="%Y-%m-%d %H:%M:%S", description="时间显示格式")}, - } - - def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: - return [ - (HelloAction.get_action_info(), HelloAction), - (TimeCommand.get_command_info(), TimeCommand), - ] -``` - -这会生成一个如下的 `config.toml` 文件: - -```toml -# hello_world_plugin - 自动生成的配置文件 -# 我的第一个MaiCore插件,包含问候功能和时间查询等基础示例 - -# 插件基本信息 -[plugin] - -# 插件名称 -name = "hello_world_plugin" - -# 插件版本 -version = "1.0.0" - -# 是否启用插件 -enabled = false - - -# 问候功能配置 -[greeting] - -# 默认问候消息 -message = "嗨!很开心见到你!😊" - -# 是否启用表情符号 -enable_emoji = true - - -# 时间查询配置 -[time] - -# 时间显示格式 -format = "%Y-%m-%d %H:%M:%S" -``` - -然后修改Action和Command代码,通过 `get_config()` 方法让它们读取配置(配置的键是命名空间式的): - -```python -class HelloAction(BaseAction): - """问候Action - 简单的问候动作""" - - # === 基本信息(必须填写)=== - action_name = "hello_greeting" - action_description = "向用户发送问候消息" - activation_type = ActionActivationType.ALWAYS # 始终激活 - - # === 功能描述(必须填写)=== - action_parameters = {"greeting_message": "要发送的问候消息"} - action_require = ["需要发送友好问候时使用", "当有人向你问好时使用", "当你遇见没有见过的人时使用"] - associated_types = ["text"] - - async def execute(self) -> Tuple[bool, str]: - """执行问候动作 - 这是核心功能""" - # 发送问候消息 - greeting_message = self.action_data.get("greeting_message", "") - base_message = self.get_config("greeting.message", "嗨!很开心见到你!😊") - message = base_message + greeting_message - await self.send_text(message) - - return True, "发送了问候消息" - -class TimeCommand(BaseCommand): - """时间查询Command - 响应/time命令""" - - command_name = "time" - command_description = "查询当前时间" - - # === 命令设置(必须填写)=== - command_pattern = r"^/time$" # 精确匹配 "/time" 命令 - - async def execute(self) -> Tuple[bool, str, bool]: - """执行时间查询""" - import datetime - - # 获取当前时间 - time_format: str = self.get_config("time.format", "%Y-%m-%d %H:%M:%S") # type: ignore - now = datetime.datetime.now() - time_str = now.strftime(time_format) - - # 发送时间信息 - message = f"⏰ 当前时间:{time_str}" - await self.send_text(message) - - return True, f"显示了当前时间: {time_str}", True -``` - -**配置系统工作流程:** - -1. **定义Schema**: 在插件代码中定义配置结构 -2. **自动生成**: 启动插件时,系统会自动生成 `config.toml` 文件 -3. **用户修改**: 用户可以修改生成的配置文件 -4. **代码读取**: 使用 `self.get_config()` 读取配置值 - -**绝对不要手动创建 `config.toml` 文件!** - -更详细的配置系统介绍请参考 [配置指南](./configuration-guide.md)。 - -### 2. 创建说明文档 - -你可以创建一个 `README.md` 文件,描述插件的功能和使用方法。 - -### 3. 发布到插件市场 - -如果你想让更多人使用你的插件,可以将它发布到MaiCore的插件市场。 - -这部分请参考 [plugin-repo](https://github.com/Maim-with-u/plugin-repo) 的文档。 - ---- - -🎉 恭喜你!你已经成功的创建了自己的插件了! diff --git a/docs-src/plugins/tool-components.md b/docs-src/plugins/tool-components.md deleted file mode 100644 index b9dc3570..00000000 --- a/docs-src/plugins/tool-components.md +++ /dev/null @@ -1,246 +0,0 @@ -# 🔧 工具组件详解 - -## 📖 什么是工具 - -工具是MaiBot的信息获取能力扩展组件。如果说Action组件功能五花八门,可以拓展麦麦能做的事情,那么Tool就是在某个过程中拓宽了麦麦能够获得的信息量。 - -### 🎯 工具的特点 - -- 🔍 **信息获取增强**:扩展麦麦获取外部信息的能力 -- 📊 **数据丰富**:帮助麦麦获得更多背景信息和实时数据 -- 🔌 **插件式架构**:支持独立开发和注册新工具 -- ⚡ **自动发现**:工具会被系统自动识别和注册 - -### 🆚 Tool vs Action vs Command 区别 - -| 特征 | Action | Command | Tool | -|-----|-------|---------|------| -| **主要用途** | 扩展麦麦行为能力 | 响应用户指令 | 扩展麦麦信息获取 | -| **触发方式** | 麦麦智能决策 | 用户主动触发 | LLM根据需要调用 | -| **目标** | 让麦麦做更多事情 | 提供具体功能 | 让麦麦知道更多信息 | -| **使用场景** | 增强交互体验 | 功能服务 | 信息查询和分析 | - -## 🏗️ Tool组件的基本结构 - -每个工具必须继承 `BaseTool` 基类并实现以下属性和方法: -```python -from src.plugin_system import BaseTool, ToolParamType - -class MyTool(BaseTool): - # 工具名称,必须唯一 - name = "my_tool" - - # 工具描述,告诉LLM这个工具的用途 - description = "这个工具用于获取特定类型的信息" - - # 参数定义,仅定义参数 - # 比如想要定义一个类似下面的openai格式的参数表,则可以这么定义: - # { - # "type": "object", - # "properties": { - # "query": { - # "type": "string", - # "description": "查询参数" - # }, - # "limit": { - # "type": "integer", - # "description": "结果数量限制" - # "enum": [10, 20, 50] # 可选值 - # } - # }, - # "required": ["query"] - # } - parameters = [ - ("query", ToolParamType.STRING, "查询参数", True, None), # 必填参数 - ("limit", ToolParamType.INTEGER, "结果数量限制", False, ["10", "20", "50"]) # 可选参数 - ] - - available_for_llm = True # 是否对LLM可用 - - async def execute(self, function_args: Dict[str, Any]): - """执行工具逻辑""" - # 实现工具功能 - result = f"查询结果: {function_args.get('query')}" - - return { - "name": self.name, - "content": result - } -``` - -### 属性说明 - -| 属性 | 类型 | 说明 | -|-----|------|------| -| `name` | str | 工具的唯一标识名称 | -| `description` | str | 工具功能描述,帮助LLM理解用途 | -| `parameters` | list[tuple] | 参数定义 | - -其构造而成的工具定义为: -```python -definition: Dict[str, Any] = {"name": cls.name, "description": cls.description, "parameters": cls.parameters} -``` - -### 方法说明 - -| 方法 | 参数 | 返回值 | 说明 | -|-----|------|--------|------| -| `execute` | `function_args` | `dict` | 执行工具核心逻辑 | - ---- - -## 🎨 完整工具示例 - -完成一个天气查询工具 - -```python -from src.plugin_system import BaseTool -import aiohttp -import json - -class WeatherTool(BaseTool): - """天气查询工具 - 获取指定城市的实时天气信息""" - - name = "weather_query" - description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况等" - available_for_llm = True # 允许LLM调用此工具 - parameters = [ - ("city", ToolParamType.STRING, "要查询天气的城市名称,如:北京、上海、纽约", True, None), - ("country", ToolParamType.STRING, "国家代码,如:CN、US,可选参数", False, None) - ] - - async def execute(self, function_args: dict): - """执行天气查询""" - try: - city = function_args.get("city") - country = function_args.get("country", "") - - # 构建查询参数 - location = f"{city},{country}" if country else city - - # 调用天气API(示例) - weather_data = await self._fetch_weather(location) - - # 格式化结果 - result = self._format_weather_data(weather_data) - - return { - "name": self.name, - "content": result - } - - except Exception as e: - return { - "name": self.name, - "content": f"天气查询失败: {str(e)}" - } - - async def _fetch_weather(self, location: str) -> dict: - """获取天气数据""" - # 这里是示例,实际需要接入真实的天气API - api_url = f"http://api.weather.com/v1/current?q={location}" - - async with aiohttp.ClientSession() as session: - async with session.get(api_url) as response: - return await response.json() - - def _format_weather_data(self, data: dict) -> str: - """格式化天气数据""" - if not data: - return "暂无天气数据" - - # 提取关键信息 - city = data.get("location", {}).get("name", "未知城市") - temp = data.get("current", {}).get("temp_c", "未知") - condition = data.get("current", {}).get("condition", {}).get("text", "未知") - humidity = data.get("current", {}).get("humidity", "未知") - - # 格式化输出 - return f""" -🌤️ {city} 实时天气 -━━━━━━━━━━━━━━━━━━ -🌡️ 温度: {temp}°C -☁️ 天气: {condition} -💧 湿度: {humidity}% -━━━━━━━━━━━━━━━━━━ - """.strip() -``` - ---- - -## 🚨 注意事项和限制 - -### 当前限制 - -1. **适用范围**:主要适用于信息获取场景 -2. **配置要求**:必须开启工具处理器 - -### 开发建议 - -1. **功能专一**:每个工具专注单一功能 -2. **参数明确**:清晰定义工具参数和用途 -3. **错误处理**:完善的异常处理和错误反馈 -4. **性能考虑**:避免长时间阻塞操作 -5. **信息准确**:确保获取信息的准确性和时效性 - -## 🎯 最佳实践 - -### 1. 工具命名规范 -#### ✅ 好的命名 -```python -name = "weather_query" # 清晰表达功能 -name = "knowledge_search" # 描述性强 -name = "stock_price_check" # 功能明确 -``` -#### ❌ 避免的命名 -```python -name = "tool1" # 无意义 -name = "wq" # 过于简短 -name = "weather_and_news" # 功能过于复杂 -``` - -### 2. 描述规范 -#### ✅ 良好的描述 -```python -description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况" -``` -#### ❌ 避免的描述 -```python -description = "天气" # 过于简单 -description = "获取信息" # 不够具体 -``` - -### 3. 参数设计 - -#### ✅ 合理的参数设计 -```python -parameters = [ - ("city", ToolParamType.STRING, "城市名称,如:北京、上海", True, None), - ("unit", ToolParamType.STRING, "温度单位:celsius 或 fahrenheit", False, ["celsius", "fahrenheit"]) -] -``` -#### ❌ 避免的参数设计 -```python -parameters = [ - ("data", "string", "数据", True) # 参数过于模糊 -] -``` - -### 4. 结果格式化 -#### ✅ 良好的结果格式 -```python -def _format_result(self, data): - return f""" -🔍 查询结果 -━━━━━━━━━━━━ -📊 数据: {data['value']} -📅 时间: {data['timestamp']} -📝 说明: {data['description']} -━━━━━━━━━━━━ - """.strip() -``` -#### ❌ 避免的结果格式 -```python -def _format_result(self, data): - return str(data) # 直接返回原始数据 -``` diff --git a/docs-src/CONTRIBUTE.md b/docs/CONTRIBUTE.md similarity index 100% rename from docs-src/CONTRIBUTE.md rename to docs/CONTRIBUTE.md From 9ff86825bd604c52ffcfded99c5016866751d9d8 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 21 Apr 2026 17:21:43 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix=EF=BC=9Asaka.py=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- saka.py | 14 ++++++------- src/config/legacy_migration.py | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/saka.py b/saka.py index 5d17870b..880ab0fd 100644 --- a/saka.py +++ b/saka.py @@ -10,21 +10,19 @@ MaiSaka - 程序入口 ENABLE_THINKING - 是否启用思考模式 (可选, true/false, 不设置则不发送该参数) """ -import asyncio -import sys from pathlib import Path -# 添加项目根目录和 src/maisaka 到 Python 路径 +import asyncio +import sys + +# 添加项目根目录到 Python 路径 _root = Path(__file__).parent -_maisaka_path = _root / "src" / "maisaka" if str(_root) not in sys.path: sys.path.insert(0, str(_root)) -if str(_maisaka_path) not in sys.path: - sys.path.insert(0, str(_maisaka_path)) +from src.cli.console import console # noqa: E402 +from src.cli.maisaka_cli import BufferCLI # noqa: E402 from src.prompt.prompt_manager import prompt_manager # noqa: E402 -from src.maisaka.cli import BufferCLI # noqa: E402 -from src.maisaka.config import console # noqa: E402 def main(): diff --git a/src/config/legacy_migration.py b/src/config/legacy_migration.py index e7d9f85c..eae7366b 100644 --- a/src/config/legacy_migration.py +++ b/src/config/legacy_migration.py @@ -170,6 +170,38 @@ def _migrate_expression_learning_list(expr: dict[str, Any]) -> bool: return True +def _migrate_chat_talk_value_rules(chat: dict[str, Any]) -> bool: + """ + 将旧版 target 字段迁移为当前运行时使用的 platform/item_id/rule_type 结构。 + """ + talk_value_rules = _as_list(chat.get("talk_value_rules")) + if talk_value_rules is None: + return False + + migrated = False + for rule in talk_value_rules: + rule_item = _as_dict(rule) + if rule_item is None or "target" not in rule_item: + continue + + target_raw = rule_item.get("target") + target = "" if target_raw is None else str(target_raw).strip() + if not target: + parsed = {"platform": "", "item_id": "", "rule_type": "group"} + else: + parsed = _parse_triplet_target(target) + if parsed is None: + continue + + rule_item["platform"] = parsed["platform"] + rule_item["item_id"] = parsed["item_id"] + rule_item["rule_type"] = parsed["rule_type"] + rule_item.pop("target", None) + migrated = True + + return migrated + + def _migrate_expression_groups(expr: dict[str, Any]) -> bool: """ 将旧版 expression.expression_groups 转成当前结构。 @@ -316,6 +348,11 @@ def try_migrate_legacy_bot_config_dict(data: dict[str, Any]) -> MigrationResult: migrated_any = True reasons.append("bot.qq_account_empty") + chat = _as_dict(data.get("chat")) + if chat is not None and _migrate_chat_talk_value_rules(chat): + migrated_any = True + reasons.append("chat.talk_value_rules_target") + expr = _as_dict(data.get("expression")) if expr is not None: if _migrate_expression_learning_list(expr): From 565aedf9eff39e4582d89188500914bc0dc2b0b2 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 21 Apr 2026 17:35:12 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E7=B2=89=E6=AF=9B=E5=BF=85=E5=AE=9A?= =?UTF-8?q?=E5=A5=B3=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 57130a5b..22627ced 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,11 @@ MaiSaka 不仅仅是一个机器人,不仅仅是一个可以帮你完成任务 contributors +### 🤝 开源项目友链 +Open Source Friends + +- **[AstrBot](https://github.com/AstrBotDevs/AstrBot)**: 优秀的LLM Agent项目 + ### ❤️ 特别致谢 Special Thanks