diff --git a/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh b/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh index d83cc339a6faeefe4dd8446a2fc7adc95ef8bd2c..94a6a1a01a09d720d3164e68ec647f7d53fbb7c3 100644 --- a/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh +++ b/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh @@ -475,6 +475,7 @@ check_pip() { ["requests"]="" ["pydantic"]="" ["tiktoken"]="" + ["aiohttp"]="" ) local need_install=0 @@ -534,7 +535,6 @@ install_framework(){ return 1 fi cd "$SCRIPT_DIR" || return 1 - install_minio || return 1 cd "$SCRIPT_DIR" || return 1 install_mongodb || return 1 check_pip || return 1 @@ -560,7 +560,7 @@ install_rag(){ install_pgvector || return 1 cd "$SCRIPT_DIR" || return 1 install_zhparser || return 1 - + install_minio || return 1 } install_web(){ local pkgs=( diff --git a/scripts/deploy/3-install-server/init_config.sh b/scripts/deploy/3-install-server/init_config.sh index 2f2698513938fd6b6f7d05b730fd44c42fa6daa2..27d561ad943543fcb21ef94a4d3502172df52bc7 100644 --- a/scripts/deploy/3-install-server/init_config.sh +++ b/scripts/deploy/3-install-server/init_config.sh @@ -924,7 +924,10 @@ main() { install_components || return 1 install_framework || return 1 cd "$SCRIPT_DIR" || return 1 - ./init_mcpserver.sh || return 1 + ./install_mcpserver.sh + ./init_mcpserver.sh || { + echo -e "\n${COLOR_WARNING} 初始化agent失败,请检查mcp服务是否可用,使用agent初始化工具创建agent,详见部署文档...${COLOR_RESET}" + } } main "$@" diff --git a/scripts/deploy/3-install-server/init_mcpserver.sh b/scripts/deploy/3-install-server/init_mcpserver.sh index dbbc1acce866b74b4ed54d7de17aa82ecc716485..30bde8b1ff9cd8a6543a6ebce4c1de0eb3422669 100644 --- a/scripts/deploy/3-install-server/init_mcpserver.sh +++ b/scripts/deploy/3-install-server/init_mcpserver.sh @@ -6,7 +6,7 @@ COLOR_ERROR='\033[31m' # 红色错误 COLOR_WARNING='\033[33m' # 黄色警告 COLOR_RESET='\033[0m' # 重置颜色 -init_mcp_config(){ +init_mcp_config() { local mcp_config_path="../5-resource/mcp_config" local target_path="/opt/copilot/semantics/mcp/template" @@ -37,7 +37,7 @@ init_mcp_config(){ echo -e "${COLOR_SUCCESS}[Success] MCP配置文件初始化完成${COLOR_RESET}" return 0 } -main(){ +start_bak() { # 获取脚本所在的绝对路径 SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) if [ -z "$SCRIPT_DIR" ]; then @@ -79,7 +79,95 @@ main(){ return 1 fi sleep 10 - python3 "../4-other-script/init_agent.py" > client_info.tmp 2>&1 + python3 "../4-other-script/init_agent.py" >client_info.tmp 2>&1 +} +# 日志输出函数 +info() { + echo -e "${COLOR_INFO}[Info] $1${COLOR_RESET}" +} + +warn() { + echo -e "${COLOR_WARN}[Warn] $1${COLOR_RESET}" +} + +error() { + echo -e "${COLOR_ERROR}[Error] $1${COLOR_RESET}" >&2 # 错误信息输出到 stderr +} +main() { + # 获取脚本所在的绝对路径 + SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + if [ -z "$SCRIPT_DIR" ]; then + error "无法获取脚本所在目录路径" + return 1 + fi + info "脚本所在目录: ${COLOR_BOLD}$SCRIPT_DIR${COLOR_RESET}" + + # 切换到脚本所在目录 + info "切换到脚本目录" + cd "$SCRIPT_DIR" || { + error "无法切换到脚本目录: $SCRIPT_DIR" + return 1 + } + + # 定义配置文件目录和脚本路径 + local mcp_config_root="../5-resource/mcp_config" + local agent_manager_script="../4-other-script/agent_manager.py" + + # 检查配置文件根目录是否存在 + if [ ! -d "$mcp_config_root" ]; then + error "配置文件根目录不存在: $mcp_config_root" + return 1 + fi + + # 检查管理脚本是否存在 + if [ ! -f "$agent_manager_script" ]; then + error "agent_manager.py 脚本不存在: $agent_manager_script" + return 1 + fi + # 遍历所有子目录下的 config.json 文件 + info "开始查找配置文件: $mcp_config_root/**/config.json" + local config_files + config_files=$(find "$mcp_config_root" -type f -name "config.json") + + # 检查是否找到配置文件 + if [ -z "$config_files" ]; then + warn "未在 $mcp_config_root 下找到任何 config.json 文件" + return 0 + fi + + # 统计配置文件数量 + local file_count=$(echo "$config_files" | wc -l | tr -d ' ') + info "共找到 ${COLOR_BOLD}$file_count${COLOR_RESET} 个配置文件,开始处理..." + + # 遍历配置文件并执行初始化和创建操作 + local index=1 + while IFS= read -r config_file; do + # 转换为绝对路径 + local abs_config=$(realpath "$config_file") + info "\n===== 处理第 $index/$file_count 个配置文件 =====" + info "配置文件路径: $abs_config" + # 执行 init 操作 + info "执行初始化: python3 $agent_manager_script init $abs_config" + if python3 "$agent_manager_script" init "$abs_config"; then + info "初始化成功: $abs_config" + else + warn "继续处理下一个配置文件" + fi + + # 执行 create 操作 + info "执行创建: python3 $agent_manager_script create $abs_config" + if python3 "$agent_manager_script" create "$abs_config"; then + info "创建成功: $abs_config" + else + warn "继续处理下一个配置文件" + fi + + index=$((index + 1)) + done <<< "$config_files" + + info "\n===== 所有配置文件处理完成 =====" + return 0 } -main +# 执行主函数 +main "$@" diff --git a/scripts/deploy/3-install-server/install_mcpserver.sh b/scripts/deploy/3-install-server/install_mcpserver.sh new file mode 100644 index 0000000000000000000000000000000000000000..8f77114ec480ccc86b68a400cda007f11561be83 --- /dev/null +++ b/scripts/deploy/3-install-server/install_mcpserver.sh @@ -0,0 +1,127 @@ +#!/bin/bash +set -eo pipefail # 开启严格模式,命令失败时立即退出 + +# 颜色定义 +COLOR_INFO='\033[34m' # 蓝色信息 +COLOR_SUCCESS='\033[32m' # 绿色成功 +COLOR_ERROR='\033[31m' # 红色错误 +COLOR_WARNING='\033[33m' # 黄色警告 +COLOR_RESET='\033[0m' # 重置颜色 + +# 日志输出函数 +info() { + echo -e "${COLOR_INFO}[Info] $1${COLOR_RESET}" +} + +success() { + echo -e "${COLOR_SUCCESS}[Success] $1${COLOR_RESET}" +} + +warn() { + echo -e "${COLOR_WARNING}[Warn] $1${COLOR_RESET}" +} + +error() { + echo -e "${COLOR_ERROR}[Error] $1${COLOR_RESET}" >&2 # 错误信息输出到 stderr +} + +# 检查命令是否存在 +check_command() { + local cmd=$1 + if ! command -v "$cmd" &>/dev/null; then + error "命令 '$cmd' 不存在,请先安装" + return 1 + fi + return 0 +} + +# 安装并启动服务的通用函数 +install_and_start() { + local package_name=$1 + local service_name=${2:-$package_name} # 服务名默认与包名相同 + local description=$3 + + info "开始${description}:${package_name}" + + # 安装包 + info "安装 ${package_name} 包..." + if dnf install -y "$package_name"; then + success "${package_name} 安装成功" + else + error "${package_name} 安装失败" + return 1 + fi + + # 启用并启动服务 + info "启动 ${service_name} 服务..." + if systemctl enable --now "$service_name"; then + success "${service_name} 服务启动并设置为开机自启成功" + else + error "${service_name} 服务启动失败" + return 1 + fi + + # 检查服务状态 + info "检查 ${service_name} 服务状态..." + if systemctl is-active --quiet "$service_name"; then + success "${service_name} 服务当前状态:运行中" + else + warn "${service_name} 服务当前状态:未运行(可能启动需要时间,建议稍后再次检查)" + fi + + return 0 +} + +main() { + info "===== 开始安装服务 =====" + + # 前置检查:确认 dnf 和 systemctl 可用 + info "检查系统工具..." + if ! check_command "dnf" || ! check_command "systemctl"; then + error "缺少必要的系统工具,无法继续" + fi + + # 安装 systrace 相关服务 + if ! install_and_start "systrace" "" "安装基础组件"; then + error "systrace 安装失败,请手动安装,终止流程" + fi + + if ! install_and_start "systrace-failslow" "" "安装故障检测组件"; then + error "systrace-failslow 安装失败,请手动安装,终止流程" + fi + + if ! install_and_start "systrace-mcpserver" "" "安装systrace管理服务"; then + error "systrace-mcpserver 安装失败,请手动安装,终止流程" + fi + + # 安装 perf 服务 + if ! install_and_start "perf-mcpserver" "" "安装性能分析服务"; then + error "perf-mcpserver 安装失败,请手动安装,终止流程" + fi + + # 安装 cve 服务 + if ! install_and_start "cve-mcpserver" "" "安装漏洞检测服务"; then + error "cve-mcpserver 安装失败,请手动安装,终止流程" + fi + + # 等待服务稳定 + info "所有服务安装完成,等待5秒确认服务稳定..." + sleep 5 + + # 汇总状态检查 + info "===== 服务安装状态汇总 =====" + local services=("systrace-mcpserver" "perf-mcpserver" "cve-mcpserver") + for service in "${services[@]}"; do + if systemctl is-active --quiet "$service"; then + success "${service}:运行中" + else + error "${service}:未运行(请检查日志:journalctl -u ${service} -xe)" + fi + done + + success "===== 所有服务安装流程已完成 =====" + exit 0 +} + +# 执行主函数 +main "$@" \ No newline at end of file diff --git a/scripts/deploy/4-other-script/agent_manager.py b/scripts/deploy/4-other-script/agent_manager.py new file mode 100644 index 0000000000000000000000000000000000000000..a038b551e065d0d32d2ee303e132da266539946f --- /dev/null +++ b/scripts/deploy/4-other-script/agent_manager.py @@ -0,0 +1,496 @@ +import json +import sys +import os +import shutil +import argparse +import logging +from typing import Optional, Dict, List, Any +from enum import Enum +from pydantic import BaseModel, Field, ValidationError + +import aiohttp +import asyncio +from aiohttp import ClientError, ClientResponseError + +# 配置日志系统 +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + handlers=[logging.StreamHandler(sys.stdout)] +) +logger = logging.getLogger("mcp_manager") + +# 配置参数 - 可通过环境变量覆盖 +BASE_URL = os.getenv("MCP_BASE_URL", "http://127.0.0.1:8002") +BASE_DIR = os.getenv("MCP_BASE_DIR", "/opt/copilot/semantics/mcp/template") +REQUEST_TIMEOUT = 30 # 请求超时时间(秒) +MAX_RETRY_COUNT = 3 # API请求重试次数 +RETRY_DELAY = 2 # 重试延迟(秒) +SERVICE_WAIT_TIMEOUT = 60 # 服务等待超时时间(秒) + + +class AppType(str, Enum): + """应用中心应用类型""" + FLOW = "flow" + AGENT = "agent" + + +class AppLink(BaseModel): + """App的相关链接""" + title: str = Field(description="链接标题") + url: str = Field(..., description="链接地址", pattern=r"^(https|http)://.*$") + + +class PermissionType(str, Enum): + """权限类型""" + PROTECTED = "protected" + PUBLIC = "public" + PRIVATE = "private" + + +class AppPermissionData(BaseModel): + """应用权限数据结构""" + type: PermissionType = Field( + default=PermissionType.PRIVATE, + alias="visibility", + description="可见性(public/private/protected)", + ) + users: Optional[List[str]] = Field( + None, + alias="authorizedUsers", + description="附加人员名单(如果可见性为部分人可见)", + ) + + +class AppFlowInfo(BaseModel): + """应用工作流数据结构""" + id: str = Field(..., description="工作流ID") + name: str = Field(..., description="工作流名称") + description: str = Field(..., description="工作流简介") + debug: bool = Field(..., description="是否经过调试") + + +class AppData(BaseModel): + """应用信息数据结构""" + app_type: AppType = Field(..., alias="appType", description="应用类型") + icon: str = Field(default="", description="图标") + name: str = Field(..., max_length=20, description="应用名称") + description: str = Field(..., max_length=150, description="应用简介") + links: List[AppLink] = Field(default=[], description="相关链接", max_length=5) + first_questions: List[str] = Field( + default=[], alias="recommendedQuestions", description="推荐问题", max_length=3) + history_len: int = Field(3, alias="dialogRounds", ge=1, le=10, description="对话轮次(1~10)") + permission: AppPermissionData = Field( + default_factory=lambda: AppPermissionData(authorizedUsers=None), description="权限配置") + workflows: List[AppFlowInfo] = Field(default=[], description="工作流信息列表") + mcp_service: List[str] = Field(default=[], alias="mcpService", description="MCP服务id列表") + + +class ApiClient: + """API请求客户端封装""" + + def __init__(self, base_url: str): + self.base_url = base_url + self.session = aiohttp.ClientSession() + + async def close(self): + """关闭客户端会话""" + await self.session.close() + + async def request(self, method: str, path: str, **kwargs) -> Dict[str, Any]: + """ + 带重试机制的异步API请求 + + Args: + method: HTTP方法 + path: API路径 + **kwargs: 传递给aiohttp的参数 + + Returns: + API响应的JSON数据 + + Raises: + ClientError: 当请求失败时 + """ + url = f"{self.base_url}{path}" + for retry in range(MAX_RETRY_COUNT): + try: + async with self.session.request( + method, url, timeout=REQUEST_TIMEOUT, **kwargs + ) as response: + response.raise_for_status() + return await response.json() + + except (ClientResponseError, ClientError) as e: + logger.warning( + f"API请求失败(第{retry + 1}/{MAX_RETRY_COUNT}次) - {method} {url}: {str(e)}" + ) + if retry < MAX_RETRY_COUNT - 1: + await asyncio.sleep(RETRY_DELAY) + + raise RuntimeError(f"API请求多次失败: {method} {url}") + + +def copy_folder(src_dir: str, dest_dir: str) -> None: + """ + 递归复制源文件夹到目标目录 + + Args: + src_dir: 源文件夹路径 + dest_dir: 目标目录路径 + + Raises: + NotADirectoryError: 源路径不是有效的文件夹 + RuntimeError: 复制过程中发生错误 + """ + if not os.path.isdir(src_dir): + raise NotADirectoryError(f"源路径 {src_dir} 不是一个有效的文件夹") + + src_folder_name = os.path.basename(src_dir) + dest_full_path = os.path.join(dest_dir, src_folder_name) + + try: + os.makedirs(dest_full_path, exist_ok=True) + for item in os.listdir(src_dir): + src_item = os.path.join(src_dir, item) + dest_item = os.path.join(dest_full_path, item) + + if os.path.isdir(src_item): + copy_folder(src_item, dest_full_path) + else: + shutil.copy2(src_item, dest_item) + logger.debug(f"已复制文件: {src_item} -> {dest_item}") + + except PermissionError: + raise RuntimeError(f"权限不足,无法操作文件: {src_item}") + except Exception as e: + raise RuntimeError(f"复制文件夹失败: {str(e)}") + + +def get_config(config_path: str) -> dict: + """ + 读取并验证配置文件 + + Args: + config_path: 配置文件路径 + + Returns: + 解析后的配置字典 + + Raises: + FileNotFoundError: 配置文件不存在 + ValueError: 配置文件格式错误 + RuntimeError: 读取文件失败 + """ + if not os.path.exists(config_path): + raise FileNotFoundError(f"配置文件不存在: {config_path}") + + try: + with open(config_path, 'r', encoding='utf-8') as reader: + config = json.load(reader) + + if not isinstance(config, dict): + raise ValueError("配置文件内容必须为JSON对象") + + logger.info(f"成功加载配置文件: {config_path}") + return config + + except json.JSONDecodeError as e: + raise ValueError(f"配置文件格式错误: {str(e)}") + except Exception as e: + raise RuntimeError(f"读取配置文件失败: {str(e)}") + + +async def wait_for_mcp_service(api_client: ApiClient, service_id: str) -> Dict[str, Any]: + """ + 等待MCP服务就绪 + + Args: + api_client: API客户端实例 + service_id: MCP服务ID + + Returns: + 服务信息字典 + + Raises: + RuntimeError: 服务超时未就绪 + """ + logger.info(f"等待MCP服务就绪: {service_id}") + for elapsed in range(SERVICE_WAIT_TIMEOUT): + try: + service = await query_mcp_server(api_client, service_id) + if service and service.get("status") == "ready": + logger.info(f"MCP服务 {service_id} 已就绪 (耗时 {elapsed} 秒)") + return service + + await asyncio.sleep(1) + + except Exception as e: + logger.warning(f"查询服务状态失败: {str(e)},将继续等待") + + raise RuntimeError(f"MCP服务 {service_id} 等待超时 ({SERVICE_WAIT_TIMEOUT}秒) 未就绪") + + +async def delete_mcp_server(api_client: ApiClient, server_id: str) -> Dict[str, Any]: + """删除mcp服务""" + logger.info(f"删除MCP服务: {server_id}") + return await api_client.request("DELETE", f"/api/mcp/{server_id}") + + +async def create_mcp_server(api_client: ApiClient, mcp_config: dict) -> str: + """创建或更新mcp服务状态""" + logger.info("创建MCP服务") + response = await api_client.request("POST", "/api/mcp/", json=mcp_config) + + service_id = response.get("result", {}).get("serviceId") + if not service_id: + raise RuntimeError("创建MCP服务未返回有效的serviceId") + + logger.info(f"MCP服务创建成功,service_id: {service_id}") + return service_id + + +async def process_mcp_config(api_client: ApiClient, config_path: str) -> str: + """ + 处理MCP配置文件:读取配置、创建服务器、保存server_id + + Args: + api_client: API客户端实例 + config_path: 配置文件路径 + + Returns: + 生成的server_id + """ + config = get_config(config_path) + + # 先删除已存在的服务 + if 'serviceId' in config: + try: + await delete_mcp_server(api_client, config['serviceId']) + del config['serviceId'] + logger.info("已删除旧的MCP服务ID") + except Exception as e: + logger.warning(f"删除旧MCP服务失败(可能不存在),继续创建新服务: {str(e)}") + + # 创建新服务 + server_id = await create_mcp_server(api_client, config) + + # 保存更新后的配置文件 + try: + config['serviceId'] = server_id + with open(config_path, 'w', encoding='utf-8') as writer: + json.dump(config, writer, ensure_ascii=False, indent=4) + + logger.info(f"配置文件已更新: {config_path}") + return server_id + + except Exception as e: + raise RuntimeError(f"保存配置文件失败: {str(e)}") + + +async def query_mcp_server(api_client: ApiClient, mcp_id: str) -> Optional[Dict[str, Any]]: + """查询MCP服务状态""" + logger.debug(f"查询MCP服务状态: {mcp_id}") + response = await api_client.request("GET", "/api/mcp") + + if response.get("code") != 200: + raise RuntimeError(f"查询MCP服务失败: {response.get('message', '未知错误')}") + + services = response.get("result", {}).get("services", []) + for service in services: + if service.get("mcpserviceId") == mcp_id: + logger.debug(f"MCP服务 {mcp_id} 状态: {service.get('status')}") + return service + + return None + + +async def install_mcp_server(api_client: ApiClient, mcp_id: str) -> Dict[str, Any]: + """安装mcp服务""" + logger.info(f"安装MCP服务: {mcp_id}") + response = await api_client.request("GET", "/api/mcp") + + if response.get("code") != 200: + raise RuntimeError(f"查询MCP服务列表失败: {response.get('message', '未知错误')}") + + services = response.get("result", {}).get("services", []) + for service in services: + if service.get("mcpserviceId") == mcp_id: + logger.debug(f"MCP服务 {mcp_id} 状态: {service.get('status')}") + if service.get('status') != "ready": + logger.debug(f"开始安装MCP服务{mcp_id}") + return await api_client.request("POST", f"/api/mcp/{mcp_id}/install") + break + + +async def activate_mcp_server(api_client: ApiClient, mcp_id: str) -> Dict[str, Any]: + """激活mcp服务""" + logger.info(f"激活MCP服务: {mcp_id}") + return await api_client.request("POST", f"/api/mcp/{mcp_id}", json={"active": "true"}) + + +async def deploy_app(api_client: ApiClient, app_id: str) -> Dict[str, Any]: + """发布应用""" + logger.info(f"发布应用: {app_id}") + return await api_client.request("POST", f"/api/app/{app_id}", json={}) + + +async def call_app_api(api_client: ApiClient, appdata: AppData) -> str: + """创建智能体应用agent""" + try: + app_data_dict = appdata.model_dump(by_alias=True) + logger.debug(f"创建应用数据: {json.dumps(app_data_dict, ensure_ascii=False)}") + + response = await api_client.request("POST", "/api/app", json=app_data_dict) + app_id = response.get("result", {}).get("appId") + + if not app_id: + raise RuntimeError("创建应用未返回有效的appId") + + logger.info(f"应用创建成功,app_id: {app_id}") + return app_id + + except ValidationError as e: + raise ValueError(f"应用数据验证失败: {str(e)}") + + +async def get_app_list(api_client: ApiClient) -> str: + """查询智能体应用agent list""" + try: + + response = await api_client.request("GET", "/api/app") + return str(response) + + except ValidationError as e: + raise ValueError(f"应用数据验证失败: {str(e)}") + + +async def comb_create(api_client: ApiClient, config_path: str) -> None: + """组合创建流程:安装多个MCP服务并创建应用""" + config = get_config(config_path) + mcp_services = config.get("mcpService", []) + + if not mcp_services: + raise ValueError("配置文件中未找到mcpService列表") + + # 处理所有MCP服务 + for service in mcp_services: + service_id = service.get("id") + await install_mcp_server(api_client, service_id) + mcp_server = await wait_for_mcp_service(api_client, service_id) + + # 激活服务(如果未激活) + if not mcp_server.get("isActive"): + await activate_mcp_server(api_client, service_id) + + # 创建并发布应用 + try: + app_data = AppData(**config) + app_id = await call_app_api(api_client, app_data) + await deploy_app(api_client, app_id) + logger.info("组合创建流程完成") + except Exception as e: + raise RuntimeError(f"组合创建失败: {str(e)}") + + +async def create_agent(api_client: ApiClient, config_path: str) -> None: + """创建agent流程:处理单个MCP服务并创建应用""" + config = get_config(config_path) + service_id = config.get("serviceId") + + if not service_id: + raise ValueError("配置文件中未找到serviceId") + + # 安装并等待服务就绪 + await install_mcp_server(api_client, service_id) + mcp_server = await wait_for_mcp_service(api_client, service_id) + + # 激活服务(如果未激活) + if not mcp_server.get("isActive"): + await activate_mcp_server(api_client, service_id) + + # 创建应用数据 + app_name = mcp_server.get('name', f"agent_{service_id[:6]}")[:20] + app_desc = mcp_server.get('description', f"Auto-created agent for {service_id}")[:150] + + app_data = AppData( + appType=AppType.AGENT, + description=app_desc, + dialogRounds=3, + icon="", + mcpService=[service_id], + name=app_name, + permission=AppPermissionData( + visibility=PermissionType.PUBLIC, + authorizedUsers=[] + ) + ) + + # 创建并发布应用 + app_id = await call_app_api(api_client, app_data) + await deploy_app(api_client, app_id) + logger.info("Agent创建流程完成") + + +async def main_async(): + parser = argparse.ArgumentParser(description='MCP服务器管理工具') + parser.add_argument( + 'operator', + choices=['init', 'create', 'comb'], + help='操作指令:init(初始化mcp server)、create(创建agent)、comb(创建组合mcp的agent)' + ) + parser.add_argument( + 'config_path', + help='MCP配置文件的路径(例如:/opt/mcp-servers/config.json)要求是全路径' + ) + parser.add_argument( + '-v', '--verbose', + action='store_true', + help='显示详细日志信息' + ) + parser.add_argument( + '--url', + help=f'MCP服务基础URL,默认: {BASE_URL}', + default=BASE_URL + ) + + args = parser.parse_args() + + # 调整日志级别 + if args.verbose: + logger.setLevel(logging.DEBUG) + + # 验证配置文件路径 + if not args.config_path or not os.path.isfile(args.config_path): + logger.error(f"无效的配置文件路径: {args.config_path}") + sys.exit(1) + + # 创建API客户端 + api_client = ApiClient(args.url) + + try: + if args.operator == 'init': + await process_mcp_config(api_client, args.config_path) + elif args.operator == 'create': + await create_agent(api_client, args.config_path) + elif args.operator == 'comb': + await comb_create(api_client, args.config_path) + logger.info("操作执行成功") + except Exception as e: + logger.error(f"操作失败: {str(e)}", exc_info=args.verbose) + sys.exit(1) + finally: + await api_client.close() + sys.exit(0) + + +def main(): + try: + asyncio.run(main_async()) + except KeyboardInterrupt: + logger.info("用户中断操作") + sys.exit(1) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/scripts/deploy/4-other-script/get_install_model.sh b/scripts/deploy/4-other-script/get_install_model.sh index e6a4a55c6224af9cae063b747dc24c75860faedc..83148c1f95041cfebcf6b2740e1ce06013231ecf 100644 --- a/scripts/deploy/4-other-script/get_install_model.sh +++ b/scripts/deploy/4-other-script/get_install_model.sh @@ -9,8 +9,6 @@ COLOR_RESET='\033[0m' # 重置颜色 # 存储安装模式的文件路径 INSTALL_MODEL_FILE="/etc/euler_Intelligence_install_model" - - # 询问用户并保存安装模式 ask_install_options() { # 存储安装模式的文件路径 diff --git a/scripts/deploy/4-other-script/init_agent.py b/scripts/deploy/4-other-script/init_agent.py index f260a734276e55b0fb740287fa0abf946abcccd9..4ba80a3880278c0dc1a68b6574d148e47764e4c1 100644 --- a/scripts/deploy/4-other-script/init_agent.py +++ b/scripts/deploy/4-other-script/init_agent.py @@ -2,7 +2,7 @@ import requests from pydantic import BaseModel, Field from enum import Enum -base_url = "http://76.53.17.51:10002" +base_url = "http://127.0.0.1:8002" class AppType(str, Enum): diff --git a/scripts/deploy/5-resource/mcp_config/perf_mcp/config.json b/scripts/deploy/5-resource/mcp_config/perf_mcp/config.json new file mode 100644 index 0000000000000000000000000000000000000000..ec33562a224a03631b83c10d7547b7380d80504e --- /dev/null +++ b/scripts/deploy/5-resource/mcp_config/perf_mcp/config.json @@ -0,0 +1,16 @@ +{ + "name": "perf 分析工具", + "overview": "perf 分析工具", + "description": "perf 分析工具", + "mcpType": "sse", + "author": "root", + "config": { + "env": {}, + "autoApprove": [], + "disabled": false, + "auto_install": false, + "description": "", + "timeout": 60, + "url": "http://127.0.0.1:12141/sse" + } +} \ No newline at end of file diff --git a/scripts/deploy/5-resource/mcp_config/sysTrace_mcp/config.json b/scripts/deploy/5-resource/mcp_config/sysTrace_mcp/config.json new file mode 100644 index 0000000000000000000000000000000000000000..6a7684e6da0799beb11d9d4f80e5a4746c1b5f51 --- /dev/null +++ b/scripts/deploy/5-resource/mcp_config/sysTrace_mcp/config.json @@ -0,0 +1,16 @@ +{ + "name": "systrace_mcp_server", + "overview": "systrace 运维 mcp 服务", + "description": "systrace 运维 mcp 服务", + "mcpType": "sse", + "author": "root", + "config": { + "env": {}, + "autoApprove": [], + "disabled": false, + "auto_install": false, + "description": "", + "timeout": 60, + "url": "http://127.0.0.1:12145/sse" + } +} \ No newline at end of file diff --git a/scripts/deploy/5-resource/mcp_config/sysTrace_mcp_server/config.json b/scripts/deploy/5-resource/mcp_config/sysTrace_mcp_server/config.json deleted file mode 100644 index 74d2beda8e49279bdb73a9c78b8d9b15c0f548ba..0000000000000000000000000000000000000000 --- a/scripts/deploy/5-resource/mcp_config/sysTrace_mcp_server/config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "systrace_mcp_server", - "overview": "sysTrace server 服务器", - "description": "面向运维、开发人员,支持自然语言对接,实现启发式调优,实现3个工具接口,分别为性能劣化感知工具,慢卡定界工具,报告输出工具。\n其中:\n性能劣化感知工具:通过解析sysTrace采集的L0数据,即AI框架关键阶段 dataloader、forward、backward的类step时延数据,检测是否存在性能劣化现象;\n慢卡定界工具:性能劣化后,开启慢卡定界,输入通信算子数据(5分钟左右),输出故障节点,时间,覆盖计算慢、通信慢、算子下发慢、IO慢问题;\n报告输出工具:在上述工具的输出基础上,整理结果,形成报告输出;", - "type": "sse", - "author": "hx", - "config": { - "env": { - }, - "autoApprove": [], - "disabled": false, - "autoInstall": false, - "command": "", - "url": "http://192.168.122.196:12140/sse" - } -} \ No newline at end of file diff --git a/scripts/deploy/5-resource/mcp_config/sysTrace_mcp_server/project/package.json b/scripts/deploy/5-resource/mcp_config/sysTrace_mcp_server/project/package.json deleted file mode 100644 index 33703d582243a41bdebff8ee7dd046a01fc054b9..0000000000000000000000000000000000000000 --- a/scripts/deploy/5-resource/mcp_config/sysTrace_mcp_server/project/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "dependencies": {} -} \ No newline at end of file diff --git a/scripts/deploy/deploy.sh b/scripts/deploy/deploy.sh index d6371d947967111c7c62acdd429cf313a17866c6..3d08b509c62b5f8c654dff89bedf2e39bf6180b4 100644 --- a/scripts/deploy/deploy.sh +++ b/scripts/deploy/deploy.sh @@ -275,6 +275,7 @@ show_help() { # echo " --q 切换安装部署模式" echo " --h 显示本帮助信息" echo " --help 同 --h" + echo " --a 进入agent初始化模式,详见部署文档" echo "" echo -e "${BLUE}服务部署手册查看位置:${COLOR_RESET}" echo " 1. 在线文档: https://gitee.com/openeuler/euler-copilot-shell/blob/dev/scripts/deploy/安装部署手册.md" @@ -287,6 +288,19 @@ show_help() { echo "==============================================================================" exit 0 } +agent_manager() { + # 获取主脚本绝对路径并切换到所在目录 + MAIN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + if [ "${MAIN_DIR}" = "/usr/bin" ]; then + cd /usr/lib/deploy/scripts || exit 1 + else + cd "$MAIN_DIR" || exit 1 + fi + + # 将所有接收的参数传递给Python脚本 + python3 4-other-script/agent_manager.py "$@" + return 0 +} # 检查帮助参数 if [[ "$1" == "--h" || "$1" == "--help" ]]; then @@ -298,7 +312,10 @@ fi # ask_install_options "force" # return 0 #fi - +if [[ "$1" == "--a" ]]; then + agent_manager "${@:2}" + exit 0 +fi # 获取主脚本绝对路径并切换到所在目录 MAIN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) if [ "${MAIN_DIR}" = "/usr/bin" ]; then diff --git "a/scripts/deploy/\345\256\211\350\243\205\351\203\250\347\275\262\346\211\213\345\206\214.md" "b/scripts/deploy/\345\256\211\350\243\205\351\203\250\347\275\262\346\211\213\345\206\214.md" index 048a3ce65d55ba078433f2145978168d7edf2ec7..79093eef26829513a15e1d7f2049acabe0427af0 100644 --- "a/scripts/deploy/\345\256\211\350\243\205\351\203\250\347\275\262\346\211\213\345\206\214.md" +++ "b/scripts/deploy/\345\256\211\350\243\205\351\203\250\347\275\262\346\211\213\345\206\214.md" @@ -112,4 +112,94 @@ domain = '192.168.2.112' #修改ip为部署服务器ip 说明:0 是默认轻量部署,只部署framework框架;1 手动部署支持选择轻量部署还是全量部署,全量部署包含web端和知识库rag。 +~~~ + +#### 4. 初始化mcp agent +~~~bash +#创建mcp服务 +openEuler-Intelligence-Install --a init config.json +#config.json 为copilot对应的mcp服务配置文件,参考如下,传入全路径 +{ + "name": "systrace_mcp_server", + "overview": "systrace 运维 mcp 服务", + "description": "systrace 运维 mcp 服务", + "mcpType": "sse", + "author": "root", + "config": { + "env": {}, + "autoApprove": [], + "disabled": false, + "auto_install": false, + "description": "", + "timeout": 60, + "url": "http://127.0.0.1:12145/sse" + } +} +#说明:init 多次调用会删除之前注册的mcp 服务,重新注册 +#创建一一对应的agent应用 +openEuler-Intelligence-Install --a create config.json +#config.json 同上,是调用init之后,会在原始json里面添加serviceId字段标识mcp服务 +{ + "name": "systrace_mcp_server", + "overview": "systrace 运维 mcp 服务", + "description": "systrace 运维 mcp 服务", + "mcpType": "sse", + "author": "root", + "config": { + "env": {}, + "autoApprove": [], + "disabled": false, + "auto_install": false, + "description": "", + "timeout": 60, + "url": "http://127.0.0.1:12145/sse" + }, + "serviceId":"p2qQke" +} +#创建多对一的agent应用 +openEuler-Intelligence-Install --a comb config.json +#config.json是组合多个mcp创建agent +{ + "appType": "agent", #应用类型,不需要修改 + "icon": "", #图标 + "name": "agent_comb", #创建应用的名字 + "description": "测试agent comb", #创建的应用的描述 + "dialogRounds": 3, #对话轮次,默认3 + "permission": { #权限,不需要修改 + "visibility": "public", + "authorizedUsers": [] + }, + "workflows": [], #工作流预留参数,不需要修改 + "mcpService": [ + { + "id": "jFOWgw" #注册的mcp返回的serviceId + }, + { + "id": "4tA5TO" #注册的mcp返回的serviceId + } + ], + "published": "True" #是否公开 +} +#说明:如果是sse 或者 streamHttp格式 mcp server,在注册之前需要服务端先启动并且可用 +~~~ + +~~~ +[root@localhost deploy]# openEuler-Intelligence-Install --a init /root/mcp_config/perf_mcp/config.json +2025-08-15 09:49:54,874 - mcp_manager - INFO - 成功加载配置文件: /root/mcp_config/perf_mcp/config.json +2025-08-15 09:49:54,874 - mcp_manager - INFO - 删除MCP服务: dJsLV4 +2025-08-15 09:49:54,960 - mcp_manager - INFO - 已删除旧的MCP服务ID +2025-08-15 09:49:54,961 - mcp_manager - INFO - 创建MCP服务 +2025-08-15 09:49:55,060 - mcp_manager - INFO - MCP服务创建成功,service_id: XMZ7Pb +2025-08-15 09:49:55,061 - mcp_manager - INFO - 配置文件已更新: /root/mcp_config/perf_mcp/config.json +2025-08-15 09:49:55,061 - mcp_manager - INFO - 操作执行成功 +[root@localhost deploy]# openEuler-Intelligence-Install --a create /root/mcp_config/perf_mcp/config.json +2025-08-15 09:50:03,819 - mcp_manager - INFO - 成功加载配置文件: /root/mcp_config/perf_mcp/config.json +2025-08-15 09:50:03,819 - mcp_manager - INFO - 安装MCP服务: XMZ7Pb +2025-08-15 09:50:04,052 - mcp_manager - INFO - 等待MCP服务就绪: XMZ7Pb +2025-08-15 09:50:14,955 - mcp_manager - INFO - MCP服务 XMZ7Pb 已就绪 (耗时 9 秒) +2025-08-15 09:50:14,956 - mcp_manager - INFO - 激活MCP服务: XMZ7Pb +2025-08-15 09:50:15,057 - mcp_manager - INFO - 应用创建成功,app_id: cd4a8f3b-9b25-4608-8d4c-d2c435e15ffd +2025-08-15 09:50:15,057 - mcp_manager - INFO - 发布应用: cd4a8f3b-9b25-4608-8d4c-d2c435e15ffd +2025-08-15 09:50:15,149 - mcp_manager - INFO - Agent创建流程完成 +2025-08-15 09:50:15,149 - mcp_manager - INFO - 操作执行成功 ~~~ \ No newline at end of file