diff --git a/README.md b/README.md index 21ebf63..f945db3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ # nonebot-plugin-llmchat -_✨ 支持多API预设、MCP协议、内置工具、联网搜索、视觉模型的AI群聊插件 ✨_ +_✨ 支持多API预设、MCP协议、联网搜索、视觉模型的AI群聊插件 ✨_ @@ -33,11 +33,6 @@ _✨ 支持多API预设、MCP协议、内置工具、联网搜索、视觉模型 - 通过连接一些搜索MCP服务器可以实现在线搜索 - 兼容 Claude.app 的配置格式 -1. **内置工具** - - 内置OneBot群操作工具,LLM可直接进行群管理操作(需模型支持tool_call) - - 支持禁言用户、获取群信息、查看群成员等功能 - - 支持戳一戳、撤回消息等互动功能 - 1. **多API预设支持** - 可配置多个LLM服务预设(如不同模型/API密钥) - 支持运行时通过`API预设`命令热切换API配置 @@ -121,22 +116,6 @@ _✨ 支持多API预设、MCP协议、内置工具、联网搜索、视觉模型 | LLMCHAT__IGNORE_PREFIXES | 否 | [] | 需要忽略的消息前缀列表,匹配到这些前缀的消息不会处理 | | LLMCHAT__MCP_SERVERS | 否 | {} | MCP服务器配置,具体见下表 | -### 内置OneBot工具 - -插件内置了以下工具,LLM可以直接调用这些工具进行群操作(需模型支持tool_call),这些工具不需要额外配置: - -| 工具名称 | 说明 | 权限要求 | -|:-----:|:----:|:----:| -| ob__mute_user | 禁言指定用户 | 机器人需要管理员权限 | -| ob__get_group_info | 获取群信息 | 无 | -| ob__get_group_member_info | 获取指定群成员信息 | 无 | -| ob__get_group_member_list | 获取群成员列表 | 无 | -| ob__poke_user | 戳一戳指定用户 | 无 | -| ob__recall_message | 撤回指定消息 | 机器人需要管理员权限或为消息发送者 | - - -### MCP服务器配置 - 其中LLMCHAT__API_PRESETS为一个列表,每项配置有以下的配置项 | 配置项 | 必填 | 默认值 | 说明 | |:-----:|:----:|:----:|:----:| diff --git a/nonebot_plugin_llmchat/__init__.py b/nonebot_plugin_llmchat/__init__.py index acfa403..ab07224 100755 --- a/nonebot_plugin_llmchat/__init__.py +++ b/nonebot_plugin_llmchat/__init__.py @@ -381,13 +381,8 @@ async def process_messages(group_id: int): # 发送工具调用提示 await handler.send(Message(f"正在使用{mcp_client.get_friendly_name(tool_name)}")) - # 执行工具调用,传递群组和机器人信息用于QQ工具 - result = await mcp_client.call_tool( - tool_name, - tool_args, - group_id=event.group_id, - bot_id=str(event.self_id) - ) + # 执行工具调用 + result = await mcp_client.call_tool(tool_name, tool_args) new_messages.append({ "role": "tool", diff --git a/nonebot_plugin_llmchat/mcpclient.py b/nonebot_plugin_llmchat/mcpclient.py index 929caca..d0bc80e 100644 --- a/nonebot_plugin_llmchat/mcpclient.py +++ b/nonebot_plugin_llmchat/mcpclient.py @@ -7,7 +7,6 @@ from mcp.client.stdio import stdio_client from nonebot import logger from .config import MCPServerConfig -from .onebottools import OneBotTools class MCPClient: @@ -33,8 +32,6 @@ class MCPClient: # 添加工具列表缓存 self._tools_cache: list | None = None self._cache_initialized = False - # 初始化OneBot工具 - self.onebot_tools = OneBotTools() self._initialized = True logger.debug("MCPClient单例初始化成功") @@ -115,12 +112,6 @@ class MCPClient: logger.info(f"初始化工具列表缓存,需要连接{len(self.server_config)}个服务器") available_tools = [] - # 添加OneBot内置工具 - onebot_tools = self.onebot_tools.get_available_tools() - available_tools.extend(onebot_tools) - logger.debug(f"添加了{len(onebot_tools)}个OneBot内置工具") - - # 添加MCP服务器工具 for server_name in self.server_config.keys(): logger.debug(f"正在从服务器[{server_name}]获取工具列表") async with self._create_session_context(server_name) as session: @@ -146,16 +137,8 @@ class MCPClient: logger.info(f"工具列表缓存完成,共缓存{len(available_tools)}个工具") return available_tools - async def call_tool(self, tool_name: str, tool_args: dict, group_id: int | None = None, bot_id: str | None = None): + async def call_tool(self, tool_name: str, tool_args: dict): """按需连接调用工具,调用后立即断开""" - # 检查是否是QQ工具 - if tool_name.startswith("ob__"): - if group_id is None or bot_id is None: - return "QQ工具需要提供group_id和bot_id参数" - logger.info(f"调用OneBot工具[{tool_name}]") - return await self.onebot_tools.call_tool(tool_name, tool_args, group_id, bot_id) - - # MCP工具处理 server_name, real_tool_name = tool_name.split("___") logger.info(f"按需连接到服务器[{server_name}]调用工具[{real_tool_name}]") @@ -170,11 +153,6 @@ class MCPClient: def get_friendly_name(self, tool_name: str): logger.debug(tool_name) - # 检查是否是OneBot工具 - if tool_name.startswith("ob__"): - return self.onebot_tools.get_friendly_name(tool_name) - - # MCP工具处理 server_name, real_tool_name = tool_name.split("___") return (self.server_config[server_name].friendly_name or server_name) + " - " + real_tool_name diff --git a/nonebot_plugin_llmchat/onebottools.py b/nonebot_plugin_llmchat/onebottools.py deleted file mode 100644 index 166e0d4..0000000 --- a/nonebot_plugin_llmchat/onebottools.py +++ /dev/null @@ -1,215 +0,0 @@ -import json -import time -from typing import Any, cast - -from nonebot import get_bot, logger -from nonebot.adapters.onebot.v11 import Bot - - -class OneBotTools: - """内置的OneBot群操作工具类""" - - def __init__(self): - self.tools = [ - { - "type": "function", - "function": { - "name": "ob__mute_user", - "description": "禁言指定用户一段时间。需要机器人有管理员权限。不能随便禁言成员,你应该听从管理员的指令。", - "parameters": { - "type": "object", - "properties": { - "user_id": {"type": "string", "description": "要禁言的用户QQ号"}, - "duration": { - "type": "integer", - "description": "禁言时长(秒),0表示解除禁言,最大2592000(30天)", - "minimum": 0, - "maximum": 2592000, - }, - }, - "required": ["user_id", "duration"], - }, - }, - }, - { - "type": "function", - "function": { - "name": "ob__get_group_info", - "description": "获取群信息,包括群成员数量、群名称等。", - "parameters": {"type": "object", "properties": {}, "required": []}, - }, - }, - { - "type": "function", - "function": { - "name": "ob__get_group_member_info", - "description": "获取指定群成员的信息。", - "parameters": { - "type": "object", - "properties": {"user_id": {"type": "string", "description": "要查询的用户QQ号"}}, - "required": ["user_id"], - }, - }, - }, - { - "type": "function", - "function": { - "name": "ob__get_group_member_list", - "description": "获取群成员列表。", - "parameters": {"type": "object", "properties": {}, "required": []}, - }, - }, - { - "type": "function", - "function": { - "name": "ob__poke_user", - "description": "戳一戳指定用户。", - "parameters": { - "type": "object", - "properties": {"user_id": {"type": "string", "description": "要戳一戳的用户QQ号"}}, - "required": ["user_id"], - }, - }, - }, - { - "type": "function", - "function": { - "name": "ob__recall_message", - "description": "撤回指定消息。需要机器人有管理员权限或者是消息发送者。", - "parameters": { - "type": "object", - "properties": {"message_id": {"type": "integer", "description": "要撤回的消息ID"}}, - "required": ["message_id"], - }, - }, - }, - ] - - def get_friendly_name(self, tool_name: str) -> str: - """获取工具的友好名称""" - friendly_names = { - "ob__mute_user": "OneBot - 禁言用户", - "ob__get_group_info": "OneBot - 获取群信息", - "ob__get_group_member_info": "OneBot - 获取成员信息", - "ob__get_group_member_list": "OneBot - 获取成员列表", - "ob__poke_user": "OneBot - 戳一戳用户", - "ob__recall_message": "OneBot - 撤回消息", - } - return friendly_names.get(tool_name, tool_name) - - def get_available_tools(self) -> list[dict[str, Any]]: - """获取可用的工具列表""" - return self.tools - - async def call_tool(self, tool_name: str, tool_args: dict[str, Any], group_id: int, bot_id: str) -> str: - """调用指定的工具""" - try: - bot = cast(Bot, get_bot(bot_id)) - - if tool_name == "ob__mute_user": - return await self._mute_user(bot, group_id, tool_args) - elif tool_name == "ob__get_group_info": - return await self._get_group_info(bot, group_id, tool_args) - elif tool_name == "ob__get_group_member_info": - return await self._get_group_member_info(bot, group_id, tool_args) - elif tool_name == "ob__get_group_member_list": - return await self._get_group_member_list(bot, group_id, tool_args) - elif tool_name == "ob__poke_user": - return await self._poke_user(bot, group_id, tool_args) - elif tool_name == "ob__recall_message": - return await self._recall_message(bot, group_id, tool_args) - else: - return f"未知的工具: {tool_name}" - - except Exception as e: - logger.error(f"调用OneBot工具 {tool_name} 时出错: {e}") - return f"执行失败: {e!s}" - - async def _mute_user(self, bot: Bot, group_id: int, args: dict[str, Any]) -> str: - """禁言用户""" - user_id = int(args["user_id"]) - duration = args["duration"] - - try: - await bot.set_group_ban(group_id=group_id, user_id=user_id, duration=duration) - if duration > 0: - return f"成功禁言用户 {user_id},时长 {duration} 秒" - else: - return f"成功解除用户 {user_id} 的禁言" - except Exception as e: - return f"禁言操作失败: {e!s}" - - async def _get_group_info(self, bot: Bot, group_id: int, _args: dict[str, Any]) -> str: - """获取群信息""" - try: - group_info = await bot.get_group_info(group_id=group_id) - info = { - "群号": group_info["group_id"], - "群名称": group_info["group_name"], - "群成员数": group_info["member_count"], - "群上限": group_info["max_member_count"], - } - return json.dumps(info, ensure_ascii=False, indent=2) - except Exception as e: - return f"获取群信息失败: {e!s}" - - async def _get_group_member_info(self, bot: Bot, group_id: int, args: dict[str, Any]) -> str: - """获取群成员信息""" - user_id = int(args["user_id"]) - - try: - member_info = await bot.get_group_member_info(group_id=group_id, user_id=user_id) - info = { - "用户QQ": member_info["user_id"], - "昵称": member_info["nickname"], - "群名片": member_info["card"], - "性别": member_info["sex"], - "年龄": member_info["age"], - "地区": member_info["area"], - "加群时间": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(member_info["join_time"])), - "最后发言时间": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(member_info["last_sent_time"])), - "群内等级": member_info["level"], - "角色": member_info["role"], - "专属头衔": member_info["title"], - } - return json.dumps(info, ensure_ascii=False, indent=2) - except Exception as e: - return f"获取成员信息失败: {e!s}" - - async def _get_group_member_list(self, bot: Bot, group_id: int, _args: dict[str, Any]) -> str: - """获取群成员列表""" - try: - member_list = await bot.get_group_member_list(group_id=group_id) - members = [] - for member in member_list: - members.append( - {"QQ": member["user_id"], "昵称": member["nickname"], "群名片": member["card"], "角色": member["role"]} - ) - - result = {"群成员总数": len(members), "成员列表": members} - return json.dumps(result, ensure_ascii=False, indent=2) - except Exception as e: - return f"获取群成员列表失败: {e!s}" - - async def _poke_user(self, bot: Bot, group_id: int, args: dict[str, Any]) -> str: - """戳一戳用户""" - user_id = int(args["user_id"]) - - try: - # 使用OneBot的戳一戳API - await bot.call_api("group_poke", group_id=group_id, user_id=user_id) - return f"成功戳了戳用户 {user_id}" - except Exception as e: - return f"戳一戳失败: {e!s}" - - async def _recall_message(self, bot: Bot, group_id: int, args: dict[str, Any]) -> str: - """撤回消息""" - message_id = int(args["message_id"]) - - try: - await bot.delete_msg(message_id=message_id) - return f"成功撤回消息 {message_id}" - except Exception as e: - return f"撤回消息失败: {e!s}" - - diff --git a/pyproject.toml b/pyproject.toml index 9b3eab7..15b4200 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nonebot-plugin-llmchat" -version = "0.4.0" +version = "0.3.1" description = "Nonebot AI group chat plugin supporting multiple API preset configurations" license = "GPL" authors = ["FuQuan i@fuquan.moe"]