""" 语音控制技能 - 基于本地AI的语音命令执行 """ import logging import re import sys import os from typing import Dict, Any, List # 兼容独立运行和包导入 _agent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if _agent_dir not in sys.path: sys.path.insert(0, _agent_dir) try: from skills.base import BaseSkill except ImportError: from ..base import BaseSkill logger = logging.getLogger(__name__) class VoiceControlSkill(BaseSkill): """语音控制技能""" PACKAGE = "" # 通用技能,不绑定特定APP NAME = "语音控制" # 常用应用包名映射 APP_PACKAGES = { "微信": "com.tencent.mm", "抖音": "com.ss.android.ugc.aweme", "支付宝": "com.eg.android.AlipayGphone", "淘宝": "com.taobao.taobao", "微博": "com.sina.weibo", "qq": "com.tencent.mobileqq", "QQ": "com.tencent.mobileqq", "设置": "com.android.settings", "相机": "com.android.camera", "浏览器": "com.android.chrome", "豆包": "com.bytedance.doubao", "豆包AI": "com.bytedance.doubao", "小红书": "com.xingin.xhs", "闲鱼": "com.taobao.idlefish", "bilibili": "tv.danmaku.bili", "B站": "tv.danmaku.bili", "知乎": "com.zhihu.android", } def parse_voice_command(self, text: str) -> Dict[str, Any]: """ 解析语音命令,返回操作序列 Args: text: 语音文本 Returns: 解析结果和操作序列 """ cmd = text.lower().strip() actions = [] # 1. 处理复合命令(用逗号、然后、再等分隔) parts = re.split(r'[,,、]|然后|再|接着', cmd) if len(parts) > 1: for part in parts: part_actions = self._parse_single_command(part.strip()) actions.extend(part_actions) return { "success": True, "text": text, "actions": actions, "type": "compound" } # 2. 处理单个命令 actions = self._parse_single_command(cmd) return { "success": len(actions) > 0, "text": text, "actions": actions, "type": "single" } def _parse_single_command(self, cmd: str) -> List[Dict[str, Any]]: """解析单个命令""" actions = [] # 打开应用(可能带搜索) for name, pkg in self.APP_PACKAGES.items(): patterns = [ f"打开{name}", f"启动{name}", f"打开{name.lower()}", f"开{name}" ] for pattern in patterns: if pattern in cmd: actions.append({ "action": "open_app", "params": {"package": pkg, "name": name} }) # 检查是否有搜索关键词 search_text = self._extract_search_text(cmd) if search_text: actions.append({ "action": "wait", "params": {"seconds": 2} }) actions.append({ "action": "search", "params": {"keyword": search_text} }) return actions # 搜索命令 if "搜索" in cmd or "查找" in cmd or "找" in cmd: search_text = self._extract_search_text(cmd) if search_text: actions.append({ "action": "search", "params": {"keyword": search_text} }) return actions # 返回 if "返回" in cmd or "回去" in cmd or "后退" in cmd: actions.append({"action": "back", "params": {}}) return actions # 回到桌面 if "桌面" in cmd or "主页" in cmd or "home" in cmd: actions.append({"action": "home", "params": {}}) return actions # 滑动 if "向上滑" in cmd or "上滑" in cmd: actions.append({ "action": "swipe", "params": {"direction": "up", "scale": 0.5} }) return actions if "向下滑" in cmd or "下滑" in cmd: actions.append({ "action": "swipe", "params": {"direction": "down", "scale": 0.5} }) return actions # 截图 if "截图" in cmd or "截屏" in cmd: actions.append({ "action": "screenshot", "params": {} }) return actions return actions def _extract_search_text(self, cmd: str) -> str: """提取搜索关键词""" patterns = [ r"搜索(.+)", r"查找(.+)", r"找(.+)", r"搜(.+)" ] for pattern in patterns: match = re.search(pattern, cmd) if match: text = match.group(1).strip() # 移除标点 text = re.sub(r'[,,。!?、]', '', text).strip() return text return "" def execute_voice_command(self, text: str) -> Dict[str, Any]: """ 执行语音命令 Args: text: 语音文本 Returns: 执行结果 """ try: # 解析命令 parsed = self.parse_voice_command(text) if not parsed["success"]: return { "success": False, "error": f"无法理解命令: {text}", "text": text } # 执行操作序列 results = [] for action_data in parsed["actions"]: action = action_data["action"] params = action_data.get("params", {}) try: if action == "open_app": pkg = params["package"] self.d.app_start(pkg) self.sleep(2) results.append({"action": action, "success": True}) elif action == "search": keyword = params["keyword"] result = self.search(keyword) results.append(result) elif action == "back": self.back() results.append({"action": action, "success": True}) elif action == "home": self.home() results.append({"action": action, "success": True}) elif action == "swipe": direction = params.get("direction", "up") scale = params.get("scale", 0.5) self.swipe(direction, scale) results.append({"action": action, "success": True}) elif action == "screenshot": filepath = self.screenshot_to_file() results.append({ "action": action, "success": True, "filepath": filepath }) elif action == "wait": seconds = params.get("seconds", 1) self.sleep(seconds) results.append({"action": action, "success": True}) else: results.append({ "action": action, "success": False, "error": f"未知操作: {action}" }) except Exception as e: logger.error(f"执行操作失败 {action}: {e}") results.append({ "action": action, "success": False, "error": str(e) }) success_count = sum(1 for r in results if r.get("success", False)) return { "success": success_count > 0, "text": text, "total_actions": len(results), "success_count": success_count, "results": results } except Exception as e: logger.error(f"执行语音命令失败: {e}") return { "success": False, "error": str(e), "text": text }