""" 新工具自动发现与集成 — 扫描内外工具源,自动评估和生成集成方案 """ from __future__ import annotations import logging import os import json from typing import Any, Dict, List, Optional from app.services.llm_service import llm_service logger = logging.getLogger(__name__) TOOL_DISCOVERY_PROMPT = """你是一个工具集成分析专家。分析以下新工具是否适合集成到 AI Agent 平台。 当前平台能力: - Agent 工作流编排 (DAG) - LLM 调用 (多模型支持) - API 调用节点 - 代码执行节点 (Python/JS) - 数据库查询节点 - 条件分支节点 - 通知节点 (飞书/Email) 新工具信息: {tool_info} 请分析: 1. 匹配度 (0-1): 该工具与平台现有能力的互补程度 2. 适用场景: 什么情况下 Agent 需要使用此工具 3. 集成复杂度: low/medium/high 4. 建议的 adapter 类型: api_wrapper / code_executor / plugin 5. 集成方案: 简要描述如何接入 6. 潜在风险: 使用此工具需要注意的问题 返回 JSON 格式: {{"match_score": 0.8, "scenarios": ["场景1"], "complexity": "medium", "adapter_type": "api_wrapper", "integration_plan": "方案描述", "risks": ["风险1"]}} """ class ToolDiscovery: """新工具自动发现与集成""" def __init__(self): self._external_sources = [ {"name": "github_trending", "url": "https://github.com/trending/python?since=weekly"}, {"name": "mcp_marketplace", "url": "https://github.com/modelcontextprotocol/servers"}, ] def scan_internal_tools(self, tools_dir: str = "") -> List[Dict[str, Any]]: """扫描平台内部 tools 目录,发现未注册的工具。""" if not tools_dir: base = os.path.dirname(os.path.dirname(__file__)) tools_dir = os.path.join(base, "tools") if not os.path.isdir(tools_dir): tools_dir = os.path.join(os.path.dirname(base), "tools") discovered = [] if not os.path.isdir(tools_dir): return discovered from app.services.tool_registry import tool_registry for filename in os.listdir(tools_dir): if filename.startswith("_") or filename.startswith("__"): continue if filename.endswith(".py") and filename != "__init__.py": tool_name = filename[:-3] registered = tool_registry.get(tool_name) if hasattr(tool_registry, 'get') else None discovered.append({ "tool_name": tool_name, "source": "internal", "registered": registered is not None, "file_path": os.path.join(tools_dir, filename), }) return discovered def scan_external_sources(self) -> List[Dict[str, Any]]: """扫描外部工具源(GitHub trending, MCP marketplace)。""" discovered = [] for source in self._external_sources: try: import urllib.request req = urllib.request.Request(source["url"], headers={"User-Agent": "AI-Agent-Platform"}) # 只记录源信息,实际爬取在 evaluate_and_rank 中按需进行 discovered.append({ "source_name": source["name"], "url": source["url"], "status": "available", }) except Exception as e: logger.warning("外部源 %s 不可用: %s", source["name"], e) discovered.append({ "source_name": source["name"], "url": source["url"], "status": "unavailable", "error": str(e), }) return discovered def evaluate_tool(self, tool_name: str, tool_description: str = "", tool_source: str = "", tool_docs: str = "") -> Dict[str, Any]: """评估单个工具的集成价值(使用 LLM)。""" tool_info = f""" 工具名称: {tool_name} 来源: {tool_source} 描述: {tool_description} 文档/代码摘要: {tool_docs[:3000]} """ prompt = TOOL_DISCOVERY_PROMPT.format(tool_info=tool_info) result = { "tool_name": tool_name, "match_score": 0.0, "scenarios": [], "complexity": "unknown", "adapter_type": "unknown", "integration_plan": "", "risks": [], } try: response = llm_service.chat_sync( messages=[{"role": "user", "content": prompt}], temperature=0.3, max_tokens=1000, ) content = response.get("content", "") if isinstance(response, dict) else str(response) # 提取 JSON json_match = self._extract_json(content) if json_match: evaluation = json.loads(json_match) result.update(evaluation) result["llm_raw"] = content[:500] except Exception as e: logger.error("LLM 评估工具 %s 失败: %s", tool_name, e) result["error"] = str(e) return result def _extract_json(self, text: str) -> Optional[str]: """从文本中提取 JSON 块。""" import re # 尝试提取 ```json ... ``` 块 match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', text, re.DOTALL) if match: return match.group(1) # 尝试直接找 {...} match = re.search(r'\{[^{}]*"match_score"[^{}]*\}', text, re.DOTALL) if match: return match.group(0) return None def discover_and_rank(self, limit: int = 10) -> Dict[str, Any]: """完整的发现+评估+排序流程。""" internal = self.scan_internal_tools() # 聚焦未注册的工具 unregistered = [t for t in internal if not t["registered"]] evaluations = [] for tool in unregistered[:limit]: eval_result = self.evaluate_tool( tool_name=tool["tool_name"], tool_source="internal", ) evaluations.append(eval_result) evaluations.sort(key=lambda x: x.get("match_score", 0), reverse=True) high_match = [e for e in evaluations if e.get("match_score", 0) >= 0.8] medium_match = [e for e in evaluations if 0.5 <= e.get("match_score", 0) < 0.8] return { "total_discovered": len(internal), "unregistered": len(unregistered), "evaluated": len(evaluations), "high_match": high_match, "medium_match": medium_match, "all_ranked": evaluations, "recommendation": ( f"发现 {len(high_match)} 个高匹配工具建议立即集成, " f"{len(medium_match)} 个中匹配工具可进一步评估" ), } def generate_adapter(self, tool_name: str, evaluation: Dict[str, Any]) -> str: """根据评估结果生成 tool adapter 代码框架。""" adapter_type = evaluation.get("adapter_type", "api_wrapper") if adapter_type == "api_wrapper": return self._generate_api_adapter(tool_name, evaluation) elif adapter_type == "code_executor": return self._generate_code_adapter(tool_name, evaluation) else: return self._generate_plugin_adapter(tool_name, evaluation) def _generate_api_adapter(self, tool_name: str, eval_result: Dict[str, Any]) -> str: """生成 API 包装器 adapter。""" return f'''""" {tool_name} — 自动发现的工具适配器 匹配度: {eval_result.get("match_score", "N/A")} 场景: {", ".join(eval_result.get("scenarios", []))} """ from typing import Any, Dict, Optional async def {tool_name}(params: Dict[str, Any]) -> Dict[str, Any]: """Auto-generated adapter for {tool_name}""" try: # TODO: 根据 API 文档实现具体调用逻辑 result = {{ "success": True, "data": None, "message": "Adapter stub — 请根据 API 文档完善", }} return result except Exception as e: return {{"success": False, "error": str(e)}} ''' def _generate_code_adapter(self, tool_name: str, eval_result: Dict[str, Any]) -> str: return f'''""" {tool_name} — 代码执行器适配器 匹配度: {eval_result.get("match_score", "N/A")} """ import subprocess from typing import Any, Dict async def {tool_name}(code: str, language: str = "python") -> Dict[str, Any]: """Auto-generated code executor adapter for {tool_name}""" try: executor = "python" if language == "python" else "node" result = subprocess.run( [executor, "-c", code], capture_output=True, text=True, timeout=30 ) return {{ "success": result.returncode == 0, "stdout": result.stdout, "stderr": result.stderr, }} except Exception as e: return {{"success": False, "error": str(e)}} ''' def _generate_plugin_adapter(self, tool_name: str, eval_result: Dict[str, Any]) -> str: return f'''""" {tool_name} — 插件适配器 匹配度: {eval_result.get("match_score", "N/A")} """ from typing import Any, Dict class {tool_name.title().replace("_", "")}Plugin: """Auto-generated plugin adapter for {tool_name}""" def __init__(self): self.name = "{tool_name}" async def execute(self, params: Dict[str, Any]) -> Dict[str, Any]: """执行插件逻辑""" # TODO: 根据文档实现 return {{"success": True, "data": None}} ''' tool_discovery = ToolDiscovery()