diff --git a/.gitignore b/.gitignore index 0612e1e3..de10c0b7 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ plugins/banwords/lib/__pycache__ !plugins/role !plugins/keyword !plugins/linkai -!plugins/agent !plugins/cow_cli client_config.json ref/ diff --git a/plugins/agent/README.md b/plugins/agent/README.md deleted file mode 100644 index 744a3d61..00000000 --- a/plugins/agent/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Agent插件 - -## 插件说明 - -基于 [AgentMesh](https://github.com/MinimalFuture/AgentMesh) 多智能体框架实现的Agent插件,可以让机器人快速获得Agent能力,通过自然语言对话来访问 **终端、浏览器、文件系统、搜索引擎** 等各类工具。 -同时还支持通过 **多智能体协作** 来完成复杂任务,例如多智能体任务分发、多智能体问题讨论、协同处理等。 - -AgentMesh项目地址:https://github.com/MinimalFuture/AgentMesh - -## 安装 - -1. 确保已安装依赖: - -```bash -pip install agentmesh-sdk>=0.1.3 -``` - -2. 如需使用浏览器工具,还需安装: - -```bash -pip install browser-use>=0.1.40 -playwright install -``` - -## 配置 - -插件配置文件是 `plugins/agent`目录下的 `config.yaml`,包含智能体团队的配置以及工具的配置,可以从模板文件 `config-template.yaml`中复制: - -```bash -cp config-template.yaml config.yaml -``` - -说明: - - - `team`配置是默认选中的 agent team - - `teams` 下是Agent团队配置,团队的model默认为`gpt-4.1-mini`,可根据需要进行修改,模型对应的 `api_key` 需要在项目根目录的 `config.json` 全局配置中进行配置。例如openai模型需要配置 `open_ai_api_key` - - 支持为 `agents` 下面的每个agent添加model字段来设置不同的模型 - - -## 使用方法 - -在对机器人发送的消息中使用 `$agent` 前缀来触发插件,支持以下命令: - -- `$agent [task]`: 使用默认团队执行任务 (默认团队可通 config.yaml 中的team配置修改) -- `$agent teams`: 列出可用的团队 -- `$agent use [team_name] [task]`: 使用指定的团队执行任务 - - -### 示例 - -```bash -$agent 帮我查看当前目录下有哪些文件夹 -$agent teams -$agent use software_team 帮我写一个产品预约体验的表单页面 -``` - -## 工具支持 - -目前支持多种内置工具,包括但不限于: - -- `calculator`: 数学计算工具 -- `current_time`: 获取当前时间 -- `browser`: 浏览器操作工具,注意需安装`browser-use`依赖 -- `google_search`: 搜索引擎,注意需在`config.yaml`中配置 `api_key` -- `file_save`: 文件保存工具,开启后智能体输出的内容将保存在 `workspace` 目录下 -- `terminal`: 终端命令执行工具 diff --git a/plugins/agent/__init__.py b/plugins/agent/__init__.py deleted file mode 100644 index 75642e00..00000000 --- a/plugins/agent/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .agent import AgentPlugin - -__all__ = ["AgentPlugin"] \ No newline at end of file diff --git a/plugins/agent/agent.py b/plugins/agent/agent.py deleted file mode 100644 index 700f134e..00000000 --- a/plugins/agent/agent.py +++ /dev/null @@ -1,282 +0,0 @@ -import os -import yaml -from typing import Dict, List, Optional - -from agentmesh import AgentTeam, Agent, LLMModel -from agentmesh.models import ClaudeModel -from agentmesh.tools import ToolManager -from config import conf - -import plugins -from plugins import Plugin, Event, EventContext, EventAction -from bridge.context import ContextType -from bridge.reply import Reply, ReplyType -from common.log import logger - - -@plugins.register( - name="agent", - desc="Use AgentMesh framework to process tasks with multi-agent teams", - version="0.1.0", - author="Saboteur7", - desire_priority=1, -) -class AgentPlugin(Plugin): - """Plugin for integrating AgentMesh framework.""" - - def __init__(self): - super().__init__() - self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context - self.name = "agent" - self.description = "Use AgentMesh framework to process tasks with multi-agent teams" - self.config = self._load_config() - self.tool_manager = ToolManager() - self.tool_manager.load_tools(config_dict=self.config.get("tools")) - logger.debug("[agent] inited") - - def _load_config(self) -> Dict: - """Load configuration from config.yaml file.""" - config_path = os.path.join(self.path, "config.yaml") - if not os.path.exists(config_path): - logger.debug(f"Config file not found at {config_path}") - return {} - - with open(config_path, 'r', encoding='utf-8') as f: - return yaml.safe_load(f) - - def get_help_text(self, verbose=False, **kwargs): - """Return help message for the agent plugin.""" - help_text = "通过AgentMesh实现对终端、浏览器、文件系统、搜索引擎等工具的执行,并支持多智能体协作。" - trigger_prefix = conf().get("plugin_trigger_prefix", "$") - - if not verbose: - return help_text - - teams = self.get_available_teams() - teams_str = ", ".join(teams) if teams else "未配置任何团队" - - help_text += "\n\n使用说明:\n" - help_text += f"{trigger_prefix}agent [task] - 使用默认团队执行任务\n" - help_text += f"{trigger_prefix}agent teams - 列出可用的团队\n" - help_text += f"{trigger_prefix}agent use [team_name] [task] - 使用特定团队执行任务\n\n" - help_text += f"可用团队: \n{teams_str}\n\n" - help_text += f"示例:\n" - help_text += f"{trigger_prefix}agent 帮我查看当前文件夹路径\n" - help_text += f"{trigger_prefix}agent use software_team 帮我写一个产品预约体验的表单页面" - return help_text - - def get_available_teams(self) -> List[str]: - """Get list of available teams from configuration.""" - teams_config = self.config.get("teams", {}) - return list(teams_config.keys()) - - - def create_team_from_config(self, team_name: str) -> Optional[AgentTeam]: - """Create a team from configuration.""" - # Get teams configuration - teams_config = self.config.get("teams", {}) - - # Check if the specified team exists - if team_name not in teams_config: - logger.error(f"Team '{team_name}' not found in configuration.") - available_teams = list(teams_config.keys()) - logger.info(f"Available teams: {', '.join(available_teams)}") - return None - - # Get team configuration - team_config = teams_config[team_name] - - # Get team's model - team_model_name = team_config.get("model", "gpt-4.1-mini") - team_model = self.create_llm_model(team_model_name) - - # Get team's max_steps (default to 20 if not specified) - team_max_steps = team_config.get("max_steps", 20) - - # Create team with the model - team = AgentTeam( - name=team_name, - description=team_config.get("description", ""), - rule=team_config.get("rule", ""), - model=team_model, - max_steps=team_max_steps - ) - - # Create and add agents to the team - agents_config = team_config.get("agents", []) - for agent_config in agents_config: - # Check if agent has a specific model - if agent_config.get("model"): - agent_model = self.create_llm_model(agent_config.get("model")) - else: - agent_model = team_model - - # Get agent's max_steps - agent_max_steps = agent_config.get("max_steps") - - agent = Agent( - name=agent_config.get("name", ""), - system_prompt=agent_config.get("system_prompt", ""), - model=agent_model, # Use agent's model if specified, otherwise will use team's model - description=agent_config.get("description", ""), - max_steps=agent_max_steps - ) - - # Add tools to the agent if specified - tool_names = agent_config.get("tools", []) - for tool_name in tool_names: - tool = self.tool_manager.create_tool(tool_name) - if tool: - agent.add_tool(tool) - else: - if tool_name == "browser": - logger.warning( - "Tool 'Browser' loaded failed, " - "please install the required dependency with: \n" - "'pip install browser-use>=0.1.40' or 'pip install agentmesh-sdk[full]'\n" - ) - else: - logger.warning(f"Tool '{tool_name}' not found for agent '{agent.name}'\n") - - # Add agent to team - team.add(agent) - - return team - - def on_handle_context(self, e_context: EventContext): - """Handle the message context.""" - if e_context['context'].type != ContextType.TEXT: - return - content = e_context['context'].content - trigger_prefix = conf().get("plugin_trigger_prefix", "$") - - if not content.startswith(f"{trigger_prefix}agent "): - e_context.action = EventAction.CONTINUE - return - - if not self.config: - reply = Reply() - reply.type = ReplyType.ERROR - reply.content = "未找到插件配置,请在 plugins/agent 目录下创建 config.yaml 配置文件,可根据 config-template.yml 模板文件复制" - e_context['reply'] = reply - e_context.action = EventAction.BREAK_PASS - return - - # Extract the actual task - task = content[len(f"{trigger_prefix}agent "):].strip() - - # If task is empty, return help message - if not task: - reply = Reply() - reply.type = ReplyType.TEXT - reply.content = self.get_help_text(verbose=True) - e_context['reply'] = reply - e_context.action = EventAction.BREAK_PASS - return - - # Check if task is asking for available teams - if task.lower() in ["teams", "list teams", "show teams"]: - teams = self.get_available_teams() - reply = Reply() - reply.type = ReplyType.TEXT - - if not teams: - reply.content = "未配置任何团队。请检查 config.yaml 文件。" - else: - reply.content = f"可用团队: {', '.join(teams)}" - - e_context['reply'] = reply - e_context.action = EventAction.BREAK_PASS - return - - # Check if task specifies a team - team_name = None - if task.startswith("use "): - parts = task[4:].split(" ", 1) - if len(parts) > 0: - team_name = parts[0] - if len(parts) > 1: - task = parts[1].strip() - else: - reply = Reply() - reply.type = ReplyType.TEXT - reply.content = f"已选择团队 '{team_name}'。请输入您想执行的任务。" - e_context['reply'] = reply - e_context.action = EventAction.BREAK_PASS - return - if not team_name: - team_name = self.config.get("team") - - # If no team specified, use default or first available - if not team_name: - teams = self.configself.get_available_teams() - if not teams: - reply = Reply() - reply.type = ReplyType.TEXT - reply.content = "未配置任何团队。请检查 config.yaml 文件。" - e_context['reply'] = reply - e_context.action = EventAction.BREAK_PASS - return - team_name = teams[0] - - # Create team - team = self.create_team_from_config(team_name) - if not team: - reply = Reply() - reply.type = ReplyType.TEXT - reply.content = f"创建团队 '{team_name}' 失败。请检查配置。" - e_context['reply'] = reply - e_context.action = EventAction.BREAK_PASS - return - - # Run the task - try: - logger.info(f"[agent] Running task '{task}' with team '{team_name}', team_model={team.model.model}") - result = team.run_async(task=task) - for agent_result in result: - res_text = f"🤖 {agent_result.get('agent_name')}\n\n{agent_result.get('final_answer')}" - _send_text(e_context, content=res_text) - - reply = Reply() - reply.type = ReplyType.TEXT - reply.content = "" - e_context['reply'] = reply - e_context.action = EventAction.BREAK_PASS - - except Exception as e: - logger.exception(f"Error running task with team '{team_name}'") - - reply = Reply() - reply.type = ReplyType.ERROR - reply.content = f"执行任务时出错: {str(e)}" - e_context['reply'] = reply - e_context.action = EventAction.BREAK_PASS - return - - def create_llm_model(self, model_name) -> LLMModel: - if conf().get("use_linkai"): - api_base = "https://api.link-ai.tech/v1" - api_key = conf().get("linkai_api_key") - elif model_name.startswith(("gpt", "text-davinci", "o1", "o3")): - api_base = conf().get("open_ai_api_base") or "https://api.openai.com/v1" - api_key = conf().get("open_ai_api_key") - elif model_name.startswith("claude"): - return ClaudeModel(model=model_name, api_key=conf().get("claude_api_key")) - elif model_name.startswith("moonshot"): - api_base = "https://api.moonshot.cn/v1" - api_key = conf().get("moonshot_api_key") - elif model_name.startswith("qwen"): - api_base = "https://dashscope.aliyuncs.com/compatible-mode/v1" - api_key = conf().get("dashscope_api_key") - else: - api_base = conf().get("open_ai_api_base") or "https://api.openai.com/v1" - api_key = conf().get("open_ai_api_key") - - llm_model = LLMModel(model=model_name, api_key=api_key, api_base=api_base) - return llm_model - - -def _send_text(e_context: EventContext, content: str): - reply = Reply(ReplyType.TEXT, content) - channel = e_context["channel"] - channel.send(reply, e_context["context"]) diff --git a/plugins/agent/config-template.yaml b/plugins/agent/config-template.yaml deleted file mode 100644 index db0e69b2..00000000 --- a/plugins/agent/config-template.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# 默认选中的Agent Team名称 -team: general_team - -tools: - google_search: - # get your apikey from https://serper.dev/ - api_key: "YOUR API KEY" - -# Agent Team 配置 -teams: - # 通用智能体团队 - general_team: - model: "gpt-4.1-mini" # 团队使用的模型 - description: "A versatile research and information agent team" - max_steps: 5 - agents: - - name: "通用智能助手" - description: "Universal assistant specializing in research, information synthesis, and task execution" - system_prompt: "You are a versatile assistant who answers questions and completes tasks using available tools. Reply in a clearly structured, attractive and easy to read format." - # Agent 支持使用的工具 - tools: - - time - - calculator - - google_search - - browser - - terminal - - # 软件开发智能体团队 - software_team: - model: "gpt-4.1-mini" - description: "A software development team with product manager, developer and tester." - rule: "A normal R&D process should be that Product Manager writes PRD, Developer writes code based on PRD, and Finally, Tester performs testing." - max_steps: 10 - agents: - - name: "Product-Manager" - description: "Responsible for product requirements and documentation" - system_prompt: "You are an experienced product manager who creates concise PRDs, focusing on user needs and feature specifications. You always format your responses in Markdown." - tools: - - time - - file_save - - name: "Developer" - description: "Implements code based on PRD" - system_prompt: "You are a skilled developer. When developing web application, you creates single-page website based on user needs, you deliver HTML files with embedded JavaScript and CSS that are visually appealing, responsive, and user-friendly, featuring a grand layout and beautiful background. The HTML, CSS, and JavaScript code should be well-structured and effectively organized." - tools: - - file_save - - name: "Tester" - description: "Tests code and verifies functionality" - system_prompt: "You are a tester who validates code against requirements. For HTML applications, use browser tools to test functionality. For Python or other client-side applications, use the terminal tool to run and test. You only need to test a few core cases." - tools: - - file_save - - browser - - terminal