feat: 优化agent插件及webUI对话页面

This commit is contained in:
Saboteur7
2025-05-22 17:31:32 +08:00
parent 8e6afa5614
commit 70d7e52df0
10 changed files with 602 additions and 100 deletions

77
plugins/agent/README.md Normal file
View File

@@ -0,0 +1,77 @@
# AgentMesh Plugin
这个插件集成了 AgentMesh 多智能体框架,允许用户通过简单的命令使用多智能体团队来完成各种任务。
## 功能介绍
AgentMesh 是一个开源的多智能体平台,提供开箱即用的 Agent 开发框架、多 Agent 间的协同策略、任务规划和自主决策能力。通过这个插件,你可以:
- 使用预配置的智能体团队处理复杂任务
- 利用多智能体协作能力解决问题
- 访问各种工具,如搜索引擎、浏览器、文件系统等
## 安装
1. 确保已安装 AgentMesh SDK
```bash
pip install agentmesh-sdk>=0.1.0
```
2. 如需使用浏览器工具,还需安装:
```bash
pip install browser-use>=0.1.40
playwright install
```
## 配置
插件从项目根目录的 `config.yaml` 文件中读取配置。请确保该文件包含正确的团队配置。
配置示例:
```yaml
teams:
general_team:
description: "通用智能体团队,擅长于搜索、研究和执行各种任务"
model: "gpt-4o"
max_steps: 20
agents:
- name: "通用助手"
description: "全能的通用智能体"
system_prompt: "你是全能的通用智能体,可以帮助用户解决工作、生活、学习上的任何问题,以及使用工具解决各类复杂问题"
tools: ["google_search", "calculator", "current_time"]
```
## 使用方法
使用 `$agent` 前缀触发插件,支持以下命令:
- `$agent teams` - 列出可用的团队
- `$agent use [team_name] [task]` - 使用特定团队执行任务
- `$agent [task]` - 使用默认团队执行任务
### 示例
```
$agent teams
$agent use general_team 帮我分析多智能体技术发展趋势
$agent 帮我查看当前文件夹路径
```
## 工具支持
AgentMesh 支持多种工具,包括但不限于:
- `calculator`: 数学计算工具
- `current_time`: 获取当前时间
- `browser`: 浏览器操作工具
- `google_search`: 搜索引擎
- `file_save`: 文件保存工具
- `terminal`: 终端命令执行工具
## 注意事项
1. 确保 `config.yaml` 文件中包含正确的团队配置
2. 如果需要使用浏览器工具,请确保安装了相关依赖

View File

@@ -0,0 +1,3 @@
from .agent import AgentPlugin
__all__ = ["AgentPlugin"]

283
plugins/agent/agent.py Normal file
View File

@@ -0,0 +1,283 @@
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.info("[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.warning(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 teams - 列出可用的团队\n"
help_text += f"{trigger_prefix}agent use [team_name] [task] - 使用特定团队执行任务\n"
help_text += f"{trigger_prefix}agent [task] - 使用默认团队执行任务\n\n"
help_text += f"可用团队: {teams_str}\n\n"
help_text += f"示例:\n"
help_text += f"{trigger_prefix}agent use general_team 帮我分析多智能体技术发展趋势\n"
help_text += f"{trigger_prefix}agent 帮我查看当前文件夹路径"
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"])

View File

@@ -0,0 +1,49 @@
# 选中的Agent Team
team: general_team
tools:
google_search:
# get your apikey from https://serper.dev/
api_key: "e7a21d840d6bb0ba832d850bb5aa4dee337415c4"
# Team config
teams:
general_team:
model: "qwen-plus"
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."
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

View File

@@ -155,7 +155,7 @@ def get_help_text(isadmin, isgroup):
for plugin in plugins:
if plugins[plugin].enabled and not plugins[plugin].hidden:
namecn = plugins[plugin].namecn
help_text += "\n%s:" % namecn
help_text += "\n%s: " % namecn
help_text += PluginManager().instances[plugin].get_help_text(verbose=False).strip()
if ADMIN_COMMANDS and isadmin: