feat: publish workphone SDK deployment and API docs
This commit is contained in:
294
sdk/agent/skill_executor.py
Normal file
294
sdk/agent/skill_executor.py
Normal file
@@ -0,0 +1,294 @@
|
||||
"""
|
||||
技能执行器 - 根据命令自动选择合适的Skill执行
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# 兼容包内导入和独立运行
|
||||
_agent_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
if _agent_dir not in sys.path:
|
||||
sys.path.insert(0, _agent_dir)
|
||||
|
||||
try:
|
||||
from skills import SKILL_REGISTRY, VoiceControlSkill, AppManagerSkill, SearchSkill
|
||||
from skill_bus import SkillChatBus
|
||||
except ImportError:
|
||||
from .skills import SKILL_REGISTRY, VoiceControlSkill, AppManagerSkill, SearchSkill
|
||||
from .skill_bus import SkillChatBus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SkillExecutor:
|
||||
"""技能执行器"""
|
||||
|
||||
def __init__(self, device, anti_ban_ctx=None):
|
||||
"""
|
||||
初始化
|
||||
|
||||
Args:
|
||||
device: uiautomator2设备对象
|
||||
anti_ban_ctx: 防护上下文 dict
|
||||
"""
|
||||
self.device = device
|
||||
self.anti_ban_ctx = anti_ban_ctx or {}
|
||||
self.skill_bus = SkillChatBus()
|
||||
self.skills = {}
|
||||
self._init_skills()
|
||||
|
||||
def _init_skills(self):
|
||||
"""初始化所有技能,注入共享聊天总线和防护上下文"""
|
||||
for name, skill_class in SKILL_REGISTRY.items():
|
||||
try:
|
||||
sig = getattr(skill_class, "__init__")
|
||||
varnames = sig.__code__.co_varnames
|
||||
kwargs = {}
|
||||
if "bus" in varnames:
|
||||
kwargs["bus"] = self.skill_bus
|
||||
if "anti_ban_ctx" in varnames:
|
||||
kwargs["anti_ban_ctx"] = self.anti_ban_ctx
|
||||
self.skills[name] = skill_class(self.device, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(f"初始化技能失败 {name}: {e}")
|
||||
|
||||
def execute_command(self, command: str, context: Dict[str, Any] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
执行命令(自动选择合适的Skill)
|
||||
|
||||
Args:
|
||||
command: 命令文本(如"打开微信给张三发消息:你好")
|
||||
context: 上下文信息
|
||||
|
||||
Returns:
|
||||
执行结果
|
||||
"""
|
||||
try:
|
||||
self.skill_bus.clear()
|
||||
self.skill_bus.append("executor", f"开始执行: {command[:80]}")
|
||||
voice_skill = self.skills.get("voice_control")
|
||||
if not voice_skill:
|
||||
voice_skill = VoiceControlSkill(self.device)
|
||||
|
||||
# 解析命令
|
||||
parsed = voice_skill.parse_voice_command(command)
|
||||
|
||||
if not parsed.get("success"):
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"无法理解命令: {command}",
|
||||
"command": command
|
||||
}
|
||||
|
||||
# 根据命令类型选择技能
|
||||
actions = parsed.get("actions", [])
|
||||
|
||||
if not actions:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "未找到可执行的操作",
|
||||
"command": command
|
||||
}
|
||||
|
||||
# 执行操作序列
|
||||
results = []
|
||||
current_skill = None
|
||||
|
||||
for action_data in actions:
|
||||
action = action_data.get("action")
|
||||
params = action_data.get("params", {})
|
||||
|
||||
try:
|
||||
# 打开应用
|
||||
if action == "open_app":
|
||||
app_manager = self.skills.get("app_manager")
|
||||
if not app_manager:
|
||||
app_manager = AppManagerSkill(self.device)
|
||||
|
||||
package = params.get("package")
|
||||
name = params.get("name", "")
|
||||
|
||||
result = app_manager.open_app(name if name else package)
|
||||
results.append(result)
|
||||
if package == "com.tencent.mm":
|
||||
current_skill = self.skills.get("wechat")
|
||||
elif package == "com.ss.android.ugc.aweme":
|
||||
current_skill = self.skills.get("douyin")
|
||||
elif package == "com.xingin.xhs":
|
||||
current_skill = self.skills.get("xhs")
|
||||
elif package == "com.taobao.idlefish":
|
||||
current_skill = self.skills.get("xianyu")
|
||||
elif package == "cn.soulapp.android":
|
||||
current_skill = self.skills.get("soul")
|
||||
else:
|
||||
current_skill = None
|
||||
self.skill_bus.append(
|
||||
current_skill.NAME if current_skill else "app_manager",
|
||||
f"open_app {name or package}",
|
||||
data=result if isinstance(result, dict) else {"result": result},
|
||||
)
|
||||
|
||||
# 搜索
|
||||
elif action == "search":
|
||||
if current_skill:
|
||||
# 使用当前APP的技能搜索
|
||||
keyword = params.get("keyword", "")
|
||||
result = current_skill.search(keyword)
|
||||
else:
|
||||
# 使用通用搜索技能
|
||||
search_skill = self.skills.get("search")
|
||||
if not search_skill:
|
||||
search_skill = SearchSkill(self.device)
|
||||
keyword = params.get("keyword", "")
|
||||
result = search_skill.search_in_app(keyword)
|
||||
|
||||
results.append(result)
|
||||
sender = current_skill.NAME if current_skill else "search"
|
||||
self.skill_bus.append(sender, f"search {keyword}", data=result if isinstance(result, dict) else {})
|
||||
|
||||
# 其他操作使用当前技能或基础操作
|
||||
elif action == "back":
|
||||
if current_skill:
|
||||
current_skill.back()
|
||||
else:
|
||||
self.device.press("back")
|
||||
results.append({"action": action, "success": True})
|
||||
sender = current_skill.NAME if current_skill else "device"
|
||||
self.skill_bus.append(sender, "back")
|
||||
|
||||
elif action == "home":
|
||||
if current_skill:
|
||||
current_skill.home()
|
||||
else:
|
||||
self.device.press("home")
|
||||
results.append({"action": action, "success": True})
|
||||
sender = current_skill.NAME if current_skill else "device"
|
||||
self.skill_bus.append(sender, "home")
|
||||
|
||||
elif action == "swipe":
|
||||
direction = params.get("direction", "up")
|
||||
scale = params.get("scale", 0.5)
|
||||
if current_skill:
|
||||
current_skill.swipe(direction, scale)
|
||||
else:
|
||||
self.device.swipe_ext(direction, scale=scale)
|
||||
results.append({"action": action, "success": True})
|
||||
sender = current_skill.NAME if current_skill else "device"
|
||||
self.skill_bus.append(sender, f"swipe {direction}")
|
||||
|
||||
elif action == "screenshot":
|
||||
if current_skill:
|
||||
filepath = current_skill.screenshot_to_file()
|
||||
else:
|
||||
filepath = f"/sdcard/screenshot_{int(time.time() * 1000)}.png"
|
||||
self.device.screenshot(filepath)
|
||||
results.append({
|
||||
"action": action,
|
||||
"success": True,
|
||||
"filepath": filepath
|
||||
})
|
||||
sender = current_skill.NAME if current_skill else "device"
|
||||
self.skill_bus.append(sender, "screenshot", data={"filepath": filepath})
|
||||
|
||||
elif action == "wait":
|
||||
seconds = params.get("seconds", 1)
|
||||
import time
|
||||
time.sleep(seconds)
|
||||
results.append({"action": action, "success": True})
|
||||
self.skill_bus.append("executor", f"wait {seconds}s")
|
||||
|
||||
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))
|
||||
self.skill_bus.append("executor", f"完成 {success_count}/{len(results)} 步", data={"results": results})
|
||||
return {
|
||||
"success": success_count > 0,
|
||||
"command": command,
|
||||
"total_actions": len(results),
|
||||
"success_count": success_count,
|
||||
"results": results,
|
||||
"skill_chat": self.skill_bus.get_messages(),
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"执行命令失败: {e}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"command": command
|
||||
}
|
||||
|
||||
def execute_wechat_task(self, task: str) -> Dict[str, Any]:
|
||||
"""
|
||||
执行微信任务(智能解析)
|
||||
|
||||
Args:
|
||||
task: 任务描述(如"给张三发消息:你好")
|
||||
|
||||
Returns:
|
||||
执行结果
|
||||
"""
|
||||
wechat_skill = self.skills.get("wechat")
|
||||
if not wechat_skill:
|
||||
wechat_skill = SKILL_REGISTRY["wechat"](self.device)
|
||||
|
||||
# 尝试使用复合任务功能
|
||||
if hasattr(wechat_skill, "execute_compound_task"):
|
||||
return wechat_skill.execute_compound_task(task)
|
||||
else:
|
||||
# 降级到普通命令执行
|
||||
return self.execute_command(task)
|
||||
|
||||
def execute_douyin_task(self, task: str) -> Dict[str, Any]:
|
||||
"""执行抖音任务"""
|
||||
douyin_skill = self.skills.get("douyin")
|
||||
if not douyin_skill:
|
||||
douyin_skill = SKILL_REGISTRY["douyin"](self.device)
|
||||
return self.execute_command(task)
|
||||
|
||||
def execute_xhs_task(self, task: str) -> Dict[str, Any]:
|
||||
"""执行小红书任务"""
|
||||
xhs_skill = self.skills.get("xhs")
|
||||
if not xhs_skill:
|
||||
xhs_skill = SKILL_REGISTRY["xhs"](self.device)
|
||||
return self.execute_command(task)
|
||||
|
||||
def execute_xianyu_task(self, task: str) -> Dict[str, Any]:
|
||||
"""执行闲鱼任务"""
|
||||
xianyu_skill = self.skills.get("xianyu")
|
||||
if not xianyu_skill:
|
||||
xianyu_skill = SKILL_REGISTRY["xianyu"](self.device)
|
||||
return self.execute_command(task)
|
||||
|
||||
def execute_soul_task(self, task: str) -> Dict[str, Any]:
|
||||
"""执行 Soul 任务"""
|
||||
soul_skill = self.skills.get("soul")
|
||||
if not soul_skill:
|
||||
soul_skill = SKILL_REGISTRY["soul"](self.device)
|
||||
return self.execute_command(task)
|
||||
|
||||
def execute_network_reconnect(self) -> Dict[str, Any]:
|
||||
"""执行网络恢复(委托 Hawk:开 WiFi、打开设置、连接已保存网络等合法操作)"""
|
||||
net_skill = self.skills.get("network_reconnect")
|
||||
if not net_skill:
|
||||
try:
|
||||
net_skill = SKILL_REGISTRY["network_reconnect"](self.device, self.skill_bus)
|
||||
except KeyError:
|
||||
return {"success": False, "error": "network_reconnect 技能未注册"}
|
||||
return net_skill.try_reconnect()
|
||||
Reference in New Issue
Block a user