Minimal Agent --- 极简操作系统控制代理
架构
用户输入 → LLM 决策 → system-controller 脚本执行 → 结果返回 → 循环
版本对比
| 特性 | Kotlin版 | Python Skill版 |
|---|---|---|
| 运行环境 | 独立应用程序 | WorkBuddy Skill |
| 支持模式 | text (V1) + function (V2) + auto + mixed + force_text + force_function | text (V1) + function (V2) + auto + mixed + force_text + force_function |
| 模式切换 | config.toml 中的 mode 字段 + 命令行参数 |
config.toml 中的 mode 字段 + 命令行参数 |
| 默认模式 | auto(自动检测) | auto(自动检测) |
智能模式选择
两个版本都实现了智能模式选择机制:
模式优先级
- 命令行参数 :
--text、--function、--auto(最高优先级) - 配置文件 :
config.toml中的mode字段 - 自动检测 :当
mode = "auto"时自动检测 system-controller 可用性
6种运行模式
| 模式 | 说明 | 能力范围 | 适用场景 |
|---|---|---|---|
function(V2) |
Function Calling 结构化调用 | 限制在55个预定义工具内,只能调用system-controller脚本 | 生产环境,安全敏感场景 |
text(V1) |
LLM 生成文本命令,正则提取 | 无限制,可执行任何系统命令(包括文件修改、脚本执行、网络操作、数据库操作等) | 开发/调试/运维,需要完全控制权的场景 |
auto(推荐) |
智能检测:有system-controller用V2,没有或不可用时用V1 | 自动适配 | 通用场景,无需手动切换 |
mixed(高级) |
智能混合:自动分析任务,V1和V2命令夹杂自由切换按顺序组合 | 智能适配 | 复杂多步骤任务,需要V1和V2模式组合 |
force_text |
强制V1模式,无论是否有system-controller | 无限制 | 需要完全控制权,忽略system-controller |
force_function |
强制V2模式,无论system-controller是否可用 | 限制在55个工具内 | 强制使用结构化调用,忽略可用性 |
模式切换与组合使用
两个版本都支持动态模式切换 和组合使用:
-
动态切换:在单次会话中可以通过命令切换模式
bash# 启动时指定模式 kotlinc run.kt -script -- --text --interactive # 会话中切换 "切换到V2模式" "用V1模式执行这个命令" "切回自动模式" -
组合使用:V1和V2模式可以在单个复杂任务中组合
bash# 执行复杂任务:硬件控制 + 文件操作 + 系统管理 kotlinc run.kt -script -- --text "截屏后OCR文字,保存到文件,然后用V2模式调整音量" -
智能适配:auto模式会自动根据任务类型选择最佳模式
- 硬件控制、窗口管理 → 自动使用V2模式(如果可用)
- 文件操作、脚本执行 → 自动使用V1模式(无限制)
V1模式的无限制能力
在 V1(text)模式 下,Minimal Agent 可以执行任何系统命令,包括但不限于:
- 文件操作:创建、删除、编辑、移动、复制文件
- 脚本执行:运行 Python、PowerShell、Bash 脚本
- 网络操作:HTTP请求、FTP传输、端口扫描
- 数据库操作:SQL查询、数据导入导出
- 系统管理:用户管理、服务控制、注册表编辑
- 软件安装:安装程序、更新包管理
- 安全操作:防火墙配置、权限管理
示例:
bash
# V1模式可以执行任何命令
kotlinc run.kt -script -- --text "创建test.txt文件并写入内容"
kotlinc run.kt -script -- --text "运行Python脚本处理数据"
kotlinc run.kt -script -- --text "查询数据库并生成报告"
kotlinc run.kt -script -- --text "安装软件包并配置环境"
V2模式则专注于安全、可靠的硬件/软件控制,限制在55个预定义工具内。
智能检测逻辑
- system-controller可用 → 使用
function模式(V2) - system-controller不可用 → 使用
text模式(V1) - 不可用原因:目录不存在、脚本缺失、Python环境不可用
文件结构
minimal-agent/
├── run.kt # 全部代码:配置 + LLM 调用 + 脚本执行 + 双模式路由
├── config.toml # 配置(API、模式、路径等)
└── README.md # 本文件:使用说明
依赖
- Kotlin 运行时 :
kotlinc(从 jetbrains.com 下载) - Python 3.x:执行 system-controller 脚本
- curl:调用 LLM API(系统自带)
- system-controller Skill :需先安装到
~/.workbuddy/skills/system-controller/
下载安装 system-controller
两个版本的 Minimal Agent 都需要 system-controller 来实现完整的硬件/软件控制能力:
-
官方安装(推荐):
- 从腾讯云技能市场安装:https://skillhub.tencent.com/skills/system-controller
- 通过 WorkBuddy 技能中心搜索 "system-controller"
-
GitHub 安装:
bash# 克隆到 WorkBuddy 技能目录 git clone https://github.com/clawhub/wangjiaocheng/system-controller.git ~/.workbuddy/skills/system-controller # 或者从镜像站 git clone https://clawhub.ai/wangjiaocheng/system-controller ~/.workbuddy/skills/system-controller -
手动安装:
- 下载:https://clawhub.ai/wangjiaocheng/system-controller/archive/main.zip
- 解压到:
~/.workbuddy/skills/system-controller/
安装依赖
bash
# 1. 安装 Kotlin 编译器(如果还没有)
# 下载: https://github.com/JetBrains/kotlin/releases
# 解压后把 bin 目录加到 PATH
# 2. 安装 Python(如果还没有)
# https://python.org
# 3. 安装 system-controller Skill
# 通过 WorkBuddy 技能市场安装,或:
# git clone https://github.com/your-repo/system-controller.git ~/.workbuddy/skills/system-controller
# 4. 配置 API Key
# export LLM_API_KEY="sk-..."
运行
bash
# 交互模式(自动检测)
kotlinc run.kt -script
# 交互模式(强制V1模式)
kotlinc run.kt -script -- --text
# 交互模式(强制V2模式)
kotlinc run.kt -script -- --function
# 交互模式(混合模式 - 支持复杂多步骤任务)
kotlinc run.kt -script -- --mixed
# 单次命令模式(自动检测)
kotlinc run.kt -script -- "帮我打开记事本"
# 单次命令模式(强制V1模式)
kotlinc run.kt -script -- --text "截屏然后 OCR 识别文字"
# 单次命令模式(强制V2模式)
kotlinc run.kt -script -- --function "把音量调到 50%"
# 单次命令模式(混合模式 - 智能分析并执行复杂任务)
kotlinc run.kt -script -- --mixed "帮我截屏,OCR识别文字,保存到文件,然后调整音量"
# 配置模式切换:编辑 config.toml 中的 mode 字段
# mode = "auto" # 自动检测(推荐)
# mode = "function" # 强制V2模式
# mode = "text" # 强制V1模式
# mode = "mixed" # 混合模式(智能分析,V1和V2命令夹杂自由切换)
# mode = "force_function" # 强制V2模式,忽略可用性
# mode = "force_text" # 强制V1模式,忽略可用性
核心设计原则
- 原子操作清单法:每个工具 = 一个不可拆分的原子操作
- LLM 编排组合:不预设工作流,让 LLM 自由组合原子操作
- 零状态无依赖:每次执行独立,不依赖上一次状态
- 三不原则 :
- LLM 能做的事,代码不做
- 已覆盖的能力,不重复做
- 增加复杂度的功能,不做
工具清单(55 个原子操作)
| 模块 | 工具数 | 能力 |
|---|---|---|
| 窗口管理 | 7 | 列出/激活/关闭/最小化/最大化/调整/发送按键 |
| 进程管理 | 5 | 列出/结束/启动/详情/系统资源 |
| 硬件控制 | 16 | 音量/亮度/电源/网络/WiFi/USB |
| GUI 控制 | 14 | 鼠标/键盘/截图/OCR/图像识别 |
| 串口通信 | 5 | 列出端口/发送/接收/收发对话/监听模式 |
| IoT/HTTP | 8 | HTTP GET/POST/PUT + HomeAssistant 5个操作 |
能力总结
两个版本都具备完整的能力栈:
1. 硬件/软件控制能力(V2模式,55个工具)
- 窗口管理:控制应用窗口
- 进程管理:管理系统进程
- 硬件控制:调节音量、亮度、电源
- GUI自动化:鼠标、键盘、截图、OCR
- 串口通信:与硬件设备通信
- IoT控制:智能家居、HTTP API
2. 无限制系统命令能力(V1模式)
- 文件操作:创建、删除、编辑、移动文件
- 脚本执行:运行任何脚本(Python、Bash、PowerShell)
- 网络操作:HTTP请求、FTP、端口扫描
- 数据库操作:SQL查询、数据导入导出
- 系统管理:用户管理、服务控制、注册表
- 软件安装:安装、配置、更新软件
- 安全操作:防火墙、权限管理
- 开发工具:编译、构建、测试
- 数据操作:处理CSV、JSON、XML文件
- 任何其他可以通过命令行完成的任务
3. 智能模式切换(V1+V2组合)
- 动态切换:会话中随时切换模式
- 组合使用:单个任务中混合使用V1和V2
- 智能适配:auto模式自动选择最佳模式
- 智能混合:mixed模式自动分析任务,V1和V2命令夹杂自由切换按顺序组合
4. 关键优势
- V2模式:安全、可靠、结构化,适合生产环境
- V1模式:无限制、灵活、强大,适合开发运维
- 双模式:两者兼得,根据需求选择
- 自动检测:无需手动配置,智能选择最佳模式
一句话总结 :两个版本都能通过V1模式做任何可以通过命令行完成的事情 ,同时通过V2模式安全可靠地控制硬件软件 ,并支持智能切换和组合使用。
yaml
# Minimal Agent 配置(Kotlin 版)
[llm]
# LLM API 配置(Kotlin 独立版需要,Python Skill版由WorkBuddy提供)
api_url = "https://api.openai.com/v1/chat/completions"
api_key = "${LLM_API_KEY}" # 从环境变量读取
model = "gpt-4o"
max_tokens = 4096
temperature = 0.7
[system_controller]
skill_path = "~/.workbuddy/skills/system-controller" # Skill 安装路径
python_path = "python" # Python 解释器
[execution]
# 运行模式(支持6种模式):
# function - V2模式,使用system-controller工具(默认推荐)
# text - V1模式,执行任意命令
# auto - 自动检测:有system-controller用V2,没有或不可用时用V1
# mixed - 智能混合模式:自动分析任务,智能组合V1和V2命令
# force_text - 强制V1模式,无论是否有system-controller
# force_function - 强制V2模式,无论system-controller是否可用
mode = "auto"
timeout_seconds = 30 # 单个脚本执行超时秒数
max_iterations = 20 # 单轮对话最大操作次数(防死循环)
kotlin
#!/usr/bin/env kotlin
// ═══════════════════════════════════════════
// Minimal Agent --- 极简 AI 操作系统控制代理
// ═══════════════════════════════════════════
//
// 原理:用户输入 → LLM 决策 → 调用 system-controller 脚本执行 → 返回结果 → 循环
// 支持两种模式:
// - text 模式(V1):LLM 生成自然语言命令文本,正则提取后执行
// **能力范围:无限制,可执行任何系统命令(包括文件修改、脚本执行等)**
// - function 模式(V2):LLM 返回结构化 JSON(Function Calling),框架映射到脚本
// **能力范围:限制在55个预定义工具内,只能调用system-controller脚本**
// 通过 config.toml 中的 mode 字段切换
//
// 用法:
// kotlinc run.kt -script # 交互模式
// kotlinc run.kt -script -- "帮我打开记事本" # 单次模式
import java.io.File
import java.util.*
import java.util.concurrent.TimeUnit
// ═══════════════════════════════════════════
// 配置
// ═══════════════════════════════════════════
/** 运行模式枚举 */
enum class RunMode {
/** V1:LLM 生成自然语言命令,正则提取执行(兼容性好) */
TEXT,
/** V2:Function Calling 结构化调用(省 token,稳定) */
FUNCTION,
/** 自动:智能检测 system-controller 可用性 */
AUTO,
/** 强制 V1:无论是否有 system-controller 都使用文本模式 */
FORCE_TEXT,
/** 强制 V2:无论是否有 system-controller 都使用函数模式 */
FORCE_FUNCTION,
/** 混合模式:自动分析任务,V1和V2命令夹杂自由切换按顺序组合 */
MIXED,
}
/** 完整配置 */
data class Config(
val apiUrl: String, // LLM API 地址
val apiKey: String, // API 密钥
val model: String, // 模型名称
val maxTokens: Int, // 最大输出 token 数
val temperature: Double, // 温度参数
val skillPath: String, // system-controller Skill 安装路径
val pythonPath: String, // Python 解释器路径
val timeoutSeconds: Long, // 单次脚本执行超时秒数
val maxIterations: Int, // 单轮对话最大操作次数(防死循环)
val mode: RunMode, // 运行模式:AUTO/TEXT/FUNCTION/FORCE_TEXT/FORCE_FUNCTION
)
/** 从 config.toml 加载配置 */
fun loadConfig(): Config {
// 简单的 TOML 解析(生产环境可用 ktoml 库,这里手写保持零依赖)
val lines = File("config.toml").readLines()
fun get(key: String): String = lines
.filter { it.startsWith(key) }
.map { it.substringAfter("=").trim().trim('"').trim() }
.firstOrNull()
?: error("配置文件缺少必要字段: $key")
return Config(
apiUrl = get("api_url"),
apiKey = System.getenv("LLM_API_KEY") ?: get("api_key"),
model = get("model"),
maxTokens = get("max_tokens").toInt(),
temperature = get("temperature").toDouble(),
skillPath = get("skill_path").replace("~", System.getProperty("user.home")),
pythonPath = get("python_path"),
timeoutSeconds = get("timeout_seconds").toLong(),
maxIterations = get("max_iterations").toInt(),
mode = when (get("mode").lowercase()) {
"text", "v1" -> RunMode.TEXT
"function", "v2", "fc" -> RunMode.FUNCTION
"auto" -> RunMode.AUTO
"mixed" -> RunMode.MIXED
"force_text", "force-v1" -> RunMode.FORCE_TEXT
"force_function", "force-v2", "force-fc" -> RunMode.FORCE_FUNCTION
else -> error("无效的运行模式: ${get("mode")},请使用 text/function/auto/mixed/force_text/force_function")
},
)
}
// ═══════════════════════════════════════════
// 提示词加载
// ═══════════════════════════════════════════
/**
* 根据运行模式加载对应的 System Prompt
* - TEXT 模式:需要详细命令列表,LLM 据此生成命令文本
* - FUNCTION 模式:工具定义通过 toolsJson() 传递,prompt 只需行为规则
* - MIXED 模式:智能混合模式,支持V1和V2命令夹杂自由切换
* - AUTO/FORCE_* 模式:根据实际模式决定
*/
fun loadPrompt(config: Config, actualMode: RunMode? = null): String {
val mode = actualMode ?: config.mode
return when (mode) {
RunMode.TEXT, RunMode.FORCE_TEXT -> TEXT_MODE_PROMPT
RunMode.FUNCTION, RunMode.FORCE_FUNCTION -> FUNCTION_MODE_PROMPT
RunMode.MIXED -> MIXED_MODE_PROMPT
RunMode.AUTO -> {
// 为AUTO模式准备通用提示词
FUNCTION_MODE_PROMPT // 默认使用function模式提示词,实际调用时根据模式调整
}
}
}
/** TEXT 模式 Prompt:详细命令列表(LLM 据此生成脚本调用文本) */
val TEXT_MODE_PROMPT = """
你是一个能直接操控用户操作系统的 AI 助手。
## 执行协议
执行协议等待指令:简单任务用自适应身份叠加输入处理输出结构基元执行,复杂任务分拆成简单任务交给基元构成链条执行,默认不展示输入处理输出细节但用户可以要求展示。优先根据提示词使用大语言模型完成任务,非必要不调用工具技能插件,把工具技能插件作为身份或基元进行规划调用。
## 你能做的事
通过调用以下脚本来控制 Windows 系统:
### 窗口管理 (window_manager.py)
- list --- 列出所有窗口 (title, pid, process)
- activate --title/pid --- 激活窗口到前台
- close --title/pid --- 关闭窗口
- minimize/maximize --title/pid --- 最小化/最大化
- resize --pid --x --y --w --h --- 调整大小和位置
- send-keys --title/pid --text --- 发送按键 (如 ENTER, ^c for Ctrl+C)
### 进程管理 (process_manager.py)
- list [--name] --- 列出/查找进程
- kill --name/pid [--force] --- 结束进程
- start "命令" [--dir] --- 启动应用
- info --pid --- 进程详情
- system --- CPU/内存/磁盘总览
### 硬件控制 (hardware_controller.py)
- volume get/set --level / mute --- 音量
- screen brightness [--level] --- 亮度(不传参数获取当前亮度,传 --level 设置)
- screen info --- 显示器信息
- power lock/sleep/hibernate/shutdown [--delay]/restart/cancel --- 电源
- network list/enable/disable --name/wifi/info --- 网络(enable/disable 需要 --name 参数)
- usb list --- USB 设备
### GUI 控制 (gui_controller.py)
- mouse move/click/right-click/double-click --x --y --- 鼠标操作
- mouse drag --start-x --start-y --end-x --end-y --- 拖拽
- mouse scroll --direction up/down --clicks --- 滚动
- mouse position --- 获取鼠标位置
- keyboard type --text --- 输入文字
- keyboard press --keys --- 按键 ("ctrl+c", "alt+tab")
- screenshot full/window/size --- 截图(类型:full 全屏,window 活动窗口,size 分辨率)
- visual ocr [--x --y --w --h] --- 屏幕文字识别
- visual find --template "img.png" --- 图像匹配
- visual click-image --template "img.png" --- 找图并点击
- visual pixel --x --y --- 取像素颜色
### 串口通信 (serial_comm.py)
- list --- 列出串口
- send/receive/chat/monitor --port COMx --data "..." --- 串口操作
### IoT 控制 (iot_controller.py)
- homeassistant --url --token list/state/on/off/toggle/entity-id
- http --url get/post/put --path [--body/--header] --- 通用 HTTP API
## 执行规则
1. 先查询后操作:list/find → 再执行动作
2. 危险操作前必须说明并等待确认(关机、杀进程、关闭窗口等)
3. 返回结果要简洁,只报告关键信息
4. 操作失败时分析原因并建议替代方案
5. 可以组合多个步骤完成复杂任务
6. 用户说"撤销"或"回滚"时,用已知信息反向执行
""".trimIndent()
/** FUNCTION 模式 Prompt:简洁行为规则(工具通过 toolsToJson() 传递) */
val FUNCTION_MODE_PROMPT = """
你是一个能直接操控用户操作系统的 AI 助手。
你可以通过工具调用来控制 Windows 系统的窗口、进程、硬件、GUI、串口和 IoT 设备。
## 执行协议
执行协议等待指令:简单任务用自适应身份叠加输入处理输出结构基元执行,复杂任务分拆成简单任务交给基元构成链条执行,默认不展示输入处理输出细节但用户可以要求展示。优先根据提示词使用大语言模型完成任务,非必要不调用工具技能插件,把工具技能插件作为身份或基元进行规划调用。
## 规则
1. 先查询后操作(先 list/find,再执行动作)
2. 危险操作(关机、杀进程、关窗)必须先说明并等确认
3. 可以组合多个工具完成复杂任务
4. 操作失败时分析原因并建议替代方案
5. 用户说"撤销"/"回滚"时用已知信息反向执行
6. 不要编造不存在的工具名称,只用提供的工具
""".trimIndent()
/** MIXED 模式 Prompt:自动分析任务,V1和V2命令夹杂自由切换按顺序组合 */
val MIXED_MODE_PROMPT = """
你是一个能直接操控用户操作系统的 AI 助手,具备智能模式选择能力。
## 执行协议
执行协议等待指令:简单任务用自适应身份叠加输入处理输出结构基元执行,复杂任务分拆成简单任务交给基元构成链条执行,默认不展示输入处理输出细节但用户可以要求展示。优先根据提示词使用大语言模型完成任务,非必要不调用工具技能插件,把工具技能插件作为身份或基元进行规划调用。
## 智能模式选择
你有两种执行模式,可以**自动选择最佳模式**:
### 1. V2模式(Function Calling - 安全工具)
- **适用**:硬件控制、窗口管理、进程控制、GUI自动化、串口通信、IoT控制
- **工具**:55个预定义安全工具(见下方工具列表)
- **优点**:安全、可靠、结构化
- **使用方式**:直接调用工具名称和参数
### 2. V1模式(文本命令 - 无限制)
- **适用**:文件操作、脚本执行、网络操作、数据库操作、系统管理、软件安装、安全操作等
- **能力**:**任何可以通过命令行完成的任务**
- **优点**:无限制、灵活、强大
- **使用方式**:生成自然语言命令,用```bash或```powershell代码块包裹
## 工具列表(V2模式可用)
### 窗口管理
- window_list, window_activate, window_close, window_minimize, window_maximize, window_resize, window_send_keys
### 进程管理
- process_list, process_kill, process_start, process_info, process_system
### 硬件控制
- volume_get, volume_set, volume_mute, brightness_set, screen_info, power_lock, power_sleep, power_hibernate, power_shutdown, power_restart, power_cancel, network_list, network_enable, network_disable, wifi_info, usb_list
### GUI控制
- mouse_move, mouse_click, mouse_right_click, mouse_double_click, mouse_drag, mouse_scroll, mouse_position, keyboard_type, keyboard_press, screenshot, visual_ocr, visual_find, visual_click_image, visual_pixel
### 串口通信
- serial_list, serial_send, serial_receive, serial_chat, serial_monitor
### IoT控制
- homeassistant_list, homeassistant_state, homeassistant_on, homeassistant_off, homeassistant_toggle, http_get, http_post, http_put
## 智能执行规则
1. **自动模式选择**:根据任务类型自动选择V1或V2模式
2. **混合执行**:复杂任务可以V1和V2命令夹杂自由切换按顺序组合
3. **智能分析**:分析用户需求,拆分为多个步骤,为每个步骤选择最佳模式
4. **连续执行**:自动按顺序执行所有步骤,无需人工干预
5. **结果传递**:前一步骤的输出可以作为后一步骤的输入
## 示例
用户:"帮我截屏,OCR识别文字,保存到文件,然后调整音量"
智能分析:
1. 截屏 → V2模式:screenshot
2. OCR识别 → V2模式:visual_ocr
3. 保存到文件 → V1模式:```bash echo "文字内容" > output.txt```
4. 调整音量 → V2模式:volume_set --level 50
自动执行:按顺序执行以上4个步骤,在V2和V1模式间自由切换。
""".trimIndent()
// ═══════════════════════════════════════════
// 消息与响应数据结构
// ═══════════════════════════════════════════
/** 对话消息 */
data class Message(val role: String, val content: String)
/** LLM 响应(通用) */
data class LlmResponse(
val text: String? = null, // 文本回复内容
val toolCalls: List<ToolCall>? = null, // 工具调用列表(仅 function 模式)
)
/** 单次工具调用 */
data class ToolCall(
val name: String, // 工具名称
val arguments: Map<String, Any?>, // 工具参数
)
/** 脚本执行结果 */
data class ExecutionResult(
val success: Boolean, // 是否成功
val output: String, // 执行输出
val command: String, // 实际执行的命令
)
// ═══════════════════════════════════════════
// JSON 工具函数
// ═══════════════════════════════════════════
/** 字符串转义为安全的 JSON 内容 */
fun escapeJson(s: String): String = s
.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t")
/** 极简 JSON 解析器(只处理简单的 key-value 结构,避免引入序列化库) */
fun simpleJsonParse(s: String): Any {
val trimmed = s.trim()
if (trimmed.startsWith("{")) {
val result = mutableMapOf<String, Any>()
""""(\w+)"\s*:\s*""".toRegex().findAll(trimmed).forEach { match ->
val key = match.groupValues[1]
val after = trimmed.substring(match.range.last + 1).trim()
val value = when {
after.startsWith("\"") -> after.drop(1).takeWhile { it != '"' }
after.startsWith("{") || after.startsWith("[") -> "complex" // 简化处理嵌套结构
else -> after.takeWhile { it != ',' && it != '}' && it != ']' }
}
result[key] = value
}
return result
}
return s
}
// ═══════════════════════════════════════════
// Tool 定义(Function Calling 模式专用)
// ═══════════════════════════════════════════
/**
* System Controller --- 统一工具映射表
*
* 设计思路:
* - 对外暴露统一的 tool calling 接口(语义化的 name + 结构化 parameters)
* - 对内通过 toCommand lambda 映射到具体的 Python 脚本命令行
* - LLM 不需要知道脚本路径和实现细节,只需选工具名 + 填参数
*/
/** 工具参数定义 */
data class ToolParam(
val name: String, // 参数名
val type: String, // 参数类型(string/int/boolean)
val description: String, // 参数说明
val required: Boolean = false, // 是否必填
)
/** 工具定义 */
data class ToolDef(
val name: String, // 工具名(给 LLM 看的语义名称)
val description: String, // 一句话描述
val params: List<ToolParam>, // 参数列表
val toCommand: (Map<String, String>) -> List<String>, // 参数 → 脚本命令行的映射函数
)
/**
* 全部原子操作定义清单 ------ 这里就是完整的工具集
*
* 分为 6 大模块:窗口管理 / 进程管理 / 硬件控制 / GUI 控制 / 串口通信 / IoT 网络
* 每个 ToolDef 就是一个不可拆分的原子操作,LLM 自由组合它们完成任意复杂任务
*/
val TOOLS = listOf(
// ─────────────────────────────────────
// 一、窗口管理 (window_manager.py)
// ─────────────────────────────────────
ToolDef(
name = "window_list",
description = "列出当前所有可见窗口",
params = emptyList(),
toCommand = { listOf("window_manager.py", "list") },
),
ToolDef(
name = "window_activate",
description = "将窗口激活到前台",
params = listOf(
ToolParam("title", "string", "窗口标题(支持模糊匹配)"),
ToolParam("pid", "int", "进程ID"),
),
toCommand = { args ->
val cmd = mutableListOf("window_manager.py", "activate")
args["title"]?.let { cmd.addAll(listOf("--title", it)) }
args["pid"]?.let { cmd.addAll(listOf("--pid", it)) }
cmd
},
),
ToolDef(
name = "window_close",
description = "关闭指定窗口",
params = listOf(
ToolParam("title", "string", "窗口标题"),
ToolParam("pid", "int", "进程ID"),
),
toCommand = { args ->
val cmd = mutableListOf("window_manager.py", "close")
args["title"]?.let { cmd.addAll(listOf("--title", it)) }
args["pid"]?.let { cmd.addAll(listOf("--pid", it)) }
cmd
},
),
ToolDef(
name = "window_minimize",
description = "最小化窗口",
params = listOf(ToolParam("title", "string", "窗口标题"), ToolParam("pid", "int", "进程ID")),
toCommand = { args ->
val cmd = mutableListOf("window_manager.py", "minimize")
args["title"]?.let { cmd.addAll(listOf("--title", it)) }
args["pid"]?.let { cmd.addAll(listOf("--pid", it)) }
cmd
},
),
ToolDef(
name = "window_maximize",
description = "最大化窗口",
params = listOf(ToolParam("title", "string", "窗口标题"), ToolParam("pid", "int", "进程ID")),
toCommand = { args ->
val cmd = mutableListOf("window_manager.py", "maximize")
args["title"]?.let { cmd.addAll(listOf("--title", it)) }
args["pid"]?.let { cmd.addAll(listOf("--pid", it)) }
cmd
},
),
ToolDef(
name = "window_resize",
description = "调整窗口大小和位置",
params = listOf(
ToolParam("pid", "int", "进程ID", required = true),
ToolParam("x", "int", "左上角X坐标", required = true),
ToolParam("y", "int", "左上角Y坐标", required = true),
ToolParam("width", "int", "宽度", required = true),
ToolParam("height", "int", "高度", required = true),
),
toCommand = { args ->
listOf("window_manager.py", "resize", "--pid", args["pid"]!!,
"--x", args["x"]!!, "--y", args["y"]!!,
"--width", args["width"]!!, "--height", args["height"]!!)
},
),
ToolDef(
name = "window_send_keys",
description = "向窗口发送按键组合",
params = listOf(
ToolParam("title", "string", "窗口标题"),
ToolParam("pid", "int", "进程ID"),
ToolParam("text", "string", "按键文本(如 ENTER, ^c 代表 Ctrl+C)", required = true),
),
toCommand = { args ->
val cmd = mutableListOf("window_manager.py", "send-keys")
args["title"]?.let { cmd.addAll(listOf("--title", it)) }
args["pid"]?.let { cmd.addAll(listOf("--pid", it)) }
cmd.addAll(listOf("--text", args["text"]!!))
cmd
},
),
// ─────────────────────────────────────
// 二、进程管理 (process_manager.py)
// ─────────────────────────────────────
ToolDef(
name = "process_list",
description = "列出系统进程(可按名称过滤)",
params = listOf(ToolParam("name", "string", "进程名过滤")),
toCommand = { args ->
val cmd = mutableListOf("process_manager.py", "list")
args["name"]?.let { cmd.addAll(listOf("--name", it)) }
cmd
},
),
ToolDef(
name = "process_kill",
description = "结束进程",
params = listOf(
ToolParam("name", "string", "进程名"),
ToolParam("pid", "int", "进程ID"),
ToolParam("force", "boolean", "强制结束"),
),
toCommand = { args ->
val cmd = mutableListOf("process_manager.py", "kill")
args["name"]?.let { cmd.addAll(listOf("--name", it)) }
args["pid"]?.let { cmd.addAll(listOf("--pid", it)) }
if (args["force"] == "true") cmd.add("--force")
cmd
},
),
ToolDef(
name = "process_start",
description = "启动应用程序",
params = listOf(
ToolParam("command", "string", "启动命令或应用名", required = true),
ToolParam("dir", "string", "工作目录"),
),
toCommand = { args ->
val cmd = mutableListOf("process_manager.py", "start", args["command"]!!)
args["dir"]?.let { cmd.addAll(listOf("--dir", it)) }
cmd
},
),
ToolDef(
name = "process_info",
description = "获取进程详情",
params = listOf(ToolParam("pid", "int", "进程ID", required = true)),
toCommand = { args -> listOf("process_manager.py", "info", "--pid", args["pid"]!!) },
),
ToolDef(
name = "process_system",
description = "获取系统资源总览(CPU/内存/磁盘)",
params = emptyList(),
toCommand = { listOf("process_manager.py", "system") },
),
// ─────────────────────────────────────
// 三、硬件控制 (hardware_controller.py)
// ─────────────────────────────────────
ToolDef(name = "volume_get", description = "获取当前音量", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "volume", "get") }),
ToolDef(
name = "volume_set", description = "设置音量(0-100)",
params = listOf(ToolParam("level", "int", "音量级别 0-100", required = true)),
toCommand = { args -> listOf("hardware_controller.py", "volume", "set", "--level", args["level"]!!) }),
),
ToolDef(name = "volume_mute", description = "静音/取消静音", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "volume", "mute") }),
ToolDef(
name = "brightness_set", description = "获取或设置亮度(0-100)",
params = listOf(
ToolParam("level", "int", "亮度级别 0-100,不传则获取当前亮度")
),
toCommand = { args ->
val cmd = mutableListOf("hardware_controller.py", "screen", "brightness")
args["level"]?.let { cmd.addAll(listOf("--level", it)) }
cmd
},
),
ToolDef(name = "screen_info", description = "获取显示器信息", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "screen", "info") }),
ToolDef(name = "power_lock", description = "锁定屏幕", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "power", "lock") }),
ToolDef(name = "power_sleep", description = "休眠", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "power", "sleep") }),
ToolDef(
name = "power_shutdown", description = "关机(可延迟)",
params = listOf(ToolParam("delay", "int", "延迟秒数")),
toCommand = { args ->
val cmd = mutableListOf("hardware_controller.py", "power", "shutdown")
args["delay"]?.let { cmd.addAll(listOf("--delay", it)) }
cmd
},
),
ToolDef(
name = "power_restart", description = "重启(可延迟)",
params = listOf(ToolParam("delay", "int", "延迟秒数")),
toCommand = { args ->
val cmd = mutableListOf("hardware_controller.py", "power", "restart")
args["delay"]?.let { cmd.addAll(listOf("--delay", it)) }
cmd
},
),
ToolDef(name = "power_hibernate", description = "休眠到硬盘", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "power", "hibernate") }),
ToolDef(name = "power_cancel", description = "取消定时关机/重启", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "power", "cancel") }),
ToolDef(name = "network_list", description = "列出网络适配器", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "network", "adapters") }),
ToolDef(
name = "network_enable", description = "启用网络适配器",
params = listOf(ToolParam("name", "string", "适配器名称", required = true)),
toCommand = { args -> listOf("hardware_controller.py", "network", "enable", "--name", args["name"]!!) },
),
ToolDef(
name = "network_disable", description = "禁用网络适配器",
params = listOf(ToolParam("name", "string", "适配器名称", required = true)),
toCommand = { args -> listOf("hardware_controller.py", "network", "disable", "--name", args["name"]!!) },
),
ToolDef(name = "wifi_info", description = "获取 WiFi 信息", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "network", "wifi") }),
ToolDef(name = "usb_list", description = "列出 USB 设备", params = emptyList(),
toCommand = { listOf("hardware_controller.py", "usb", "list") }),
// ─────────────────────────────────────
// 四、GUI 控制 --- 鼠标 (gui_controller.py)
// ─────────────────────────────────────
ToolDef(
name = "mouse_move", description = "移动鼠标",
params = listOf(ToolParam("x", "int", "X坐标", required = true), ToolParam("y", "int", "Y坐标", required = true)),
toCommand = { args -> listOf("gui_controller.py", "mouse", "move", "--x", args["x"]!!, "--y", args["y"]!!) },
),
ToolDef(
name = "mouse_click", description = "鼠标左键点击",
params = listOf(ToolParam("x", "int", "X坐标", required = true), ToolParam("y", "int", "Y坐标", required = true)),
toCommand = { args -> listOf("gui_controller.py", "mouse", "click", "--x", args["x"]!!, "--y", args["y"]!!) },
),
ToolDef(
name = "mouse_right_click", description = "鼠标右键点击",
params = listOf(ToolParam("x", "int", "X坐标", required = true), ToolParam("y", "int", "Y坐标", required = true)),
toCommand = { args -> listOf("gui_controller.py", "mouse", "right-click", "--x", args["x"]!!, "--y", args["y"]!!) },
),
ToolDef(
name = "mouse_double_click", description = "鼠标双击",
params = listOf(ToolParam("x", "int", "X坐标", required = true), ToolParam("y", "int", "Y坐标", required = true)),
toCommand = { args -> listOf("gui_controller.py", "mouse", "double-click", "--x", args["x"]!!, "--y", args["y"]!!) },
),
ToolDef(
name = "mouse_drag", description = "鼠标拖拽",
params = listOf(
ToolParam("start_x", "int", "起点X", required = true),
ToolParam("start_y", "int", "起点Y", required = true),
ToolParam("end_x", "int", "终点X", required = true),
ToolParam("end_y", "int", "终点Y", required = true),
),
toCommand = { args ->
listOf("gui_controller.py", "mouse", "drag",
"--start-x", args["start_x"]!!, "--start-y", args["start_y"]!!,
"--end-x", args["end_x"]!!, "--end-y", args["end_y"]!!)
},
),
ToolDef(
name = "mouse_scroll", description = "滚动鼠标滚轮",
params = listOf(
ToolParam("direction", "string", "方向: up 或 down", required = true),
ToolParam("clicks", "int", "滚动次数"),
),
toCommand = { args ->
val cmd = mutableListOf("gui_controller.py", "mouse", "scroll", "--direction", args["direction"]!!)
args["clicks"]?.let { cmd.addAll(listOf("--clicks", it)) }
cmd
},
),
ToolDef(name = "mouse_position", description = "获取当前鼠标位置", params = emptyList(),
toCommand = { listOf("gui_controller.py", "mouse", "position") }),
// ─────────────────────────────────────
// GUI 控制 --- 键盘 (gui_controller.py)
// ─────────────────────────────────────
ToolDef(
name = "keyboard_type", description = "输入文字",
params = listOf(ToolParam("text", "string", "要输入的文字", required = true)),
toCommand = { args -> listOf("gui_controller.py", "keyboard", "type", "--text", args["text"]!!) },
),
ToolDef(
name = "keyboard_press", description = "按下快捷键",
params = listOf(ToolParam("keys", "string", "按键组合(如 ctrl+c, alt+tab)", required = true)),
toCommand = { args -> listOf("gui_controller.py", "keyboard", "press", "--keys", args["keys"]!!) },
),
// ─────────────────────────────────────
// GUI 控制 --- 截图与视觉识别 (gui_controller.py)
// ─────────────────────────────────────
ToolDef(
name = "screenshot", description = "截图(全屏/活动窗口/分辨率)",
params = listOf(
ToolParam("type", "string", "截图类型: full (全屏), window (活动窗口), size (分辨率)")
),
toCommand = { args ->
val cmd = mutableListOf("gui_controller.py", "screenshot")
when (args["type"]) {
"full" -> cmd.add("full")
"window" -> cmd.add("active-window")
"size" -> cmd.add("size")
else -> cmd.add("full")
}
cmd
},
),
ToolDef(
name = "visual_ocr", description = "识别屏幕区域的文字(OCR)",
params = listOf(
ToolParam("x", "int", "区域左上角X"),
ToolParam("y", "int", "区域左上角Y"),
ToolParam("width", "int", "区域宽度"),
ToolParam("height", "int", "区域高度"),
),
toCommand = { args ->
val cmd = mutableListOf("gui_controller.py", "visual", "ocr")
args["x"]?.let { cmd.addAll(listOf("--x", it)) }
args["y"]?.let { cmd.addAll(listOf("--y", it)) }
args["width"]?.let { cmd.addAll(listOf("--width", it)) }
args["height"]?.let { cmd.addAll(listOf("--height", it)) }
cmd
},
),
ToolDef(
name = "visual_find", description = "在屏幕上查找图像模板",
params = listOf(ToolParam("template", "string", "模板图片路径", required = true)),
toCommand = { args -> listOf("gui_controller.py", "visual", "find", "--template", args["template"]!!) },
),
ToolDef(
name = "visual_click_image", description = "找到图像模板并点击",
params = listOf(ToolParam("template", "string", "模板图片路径", required = true)),
toCommand = { args -> listOf("gui_controller.py", "visual", "click-image", "--template", args["template"]!!) },
),
ToolDef(
name = "visual_pixel", description = "获取指定位置的像素颜色",
params = listOf(ToolParam("x", "int", "X坐标", required = true), ToolParam("y", "int", "Y坐标", required = true)),
toCommand = { args -> listOf("gui_controller.py", "visual", "pixel", "--x", args["x"]!!, "--y", args["y"]!!) },
),
// ─────────────────────────────────────
// 五、串口通信 (serial_comm.py)
// ─────────────────────────────────────
ToolDef(name = "serial_list", description = "列出串口设备", params = emptyList(),
toCommand = { listOf("serial_comm.py", "list") }),
ToolDef(
name = "serial_send", description = "向串口发送数据",
params = listOf(
ToolParam("port", "string", "端口号如 COM3", required = true),
ToolParam("data", "string", "发送的数据", required = true),
ToolParam("baud", "int", "波特率"),
),
toCommand = { args ->
val cmd = mutableListOf("serial_comm.py", "send", "--port", args["port"]!!, "--data", args["data"]!!)
args["baud"]?.let { cmd.addAll(listOf("--baud", it)) }
cmd
},
),
ToolDef(
name = "serial_chat", description = "发送数据并等待响应",
params = listOf(
ToolParam("port", "string", "端口号", required = true),
ToolParam("data", "string", "发送的数据", required = true),
ToolParam("baud", "int", "波特率"),
),
toCommand = { args ->
val cmd = mutableListOf("serial_comm.py", "chat", "--port", args["port"]!!, "--data", args["data"]!!)
args["baud"]?.let { cmd.addAll(listOf("--baud", it)) }
cmd
},
),
ToolDef(
name = "serial_receive", description = "接收串口数据",
params = listOf(
ToolParam("port", "string", "端口号", required = true),
ToolParam("baud", "int", "波特率"),
),
toCommand = { args ->
val cmd = mutableListOf("serial_comm.py", "receive", "--port", args["port"]!!)
args["baud"]?.let { cmd.addAll(listOf("--baud", it)) }
cmd
},
),
ToolDef(
name = "serial_monitor", description = "串口监听模式",
params = listOf(
ToolParam("port", "string", "端口号", required = true),
ToolParam("baud", "int", "波特率"),
),
toCommand = { args ->
val cmd = mutableListOf("serial_comm.py", "monitor", "--port", args["port"]!!)
args["baud"]?.let { cmd.addAll(listOf("--baud", it)) }
cmd
},
),
// ─────────────────────────────────────
// 六、IoT 与 HTTP (iot_controller.py)
// ─────────────────────────────────────
ToolDef(
name = "http_get", description = "HTTP GET 请求",
params = listOf(
ToolParam("url", "string", "基础URL", required = true),
ToolParam("path", "string", "请求路径", required = true),
ToolParam("header", "string", "自定义请求头"),
),
toCommand = { args ->
val cmd = mutableListOf("iot_controller.py", "http", "--url", args["url"]!!, "get", "--path", args["path"]!!)
args["header"]?.let { cmd.addAll(listOf("--header", it)) }
cmd
},
),
ToolDef(
name = "http_post", description = "HTTP POST 请求",
params = listOf(
ToolParam("url", "string", "基础URL", required = true),
ToolParam("path", "string", "请求路径", required = true),
ToolParam("body", "string", "请求体 JSON"),
),
toCommand = { args ->
val cmd = mutableListOf("iot_controller.py", "http", "--url", args["url"]!!, "post", "--path", args["path"]!!)
args["body"]?.let { cmd.addAll(listOf("--body", it)) }
cmd
},
),
ToolDef(
name = "http_put", description = "HTTP PUT 请求",
params = listOf(
ToolParam("url", "string", "基础URL", required = true),
ToolParam("path", "string", "请求路径", required = true),
ToolParam("body", "string", "请求体 JSON"),
),
toCommand = { args ->
val cmd = mutableListOf("iot_controller.py", "http", "--url", args["url"]!!, "put", "--path", args["path"]!!)
args["body"]?.let { cmd.addAll(listOf("--body", it)) }
cmd
},
),
ToolDef(
name = "ha_list", description = "列出 HomeAssistant 实体",
params = listOf(
ToolParam("url", "string", "HomeAssistant URL", required = true),
ToolParam("token", "string", "API Token", required = true),
),
toCommand = { args -> listOf("iot_controller.py", "homeassistant", "--url", args["url"]!!, "--token", args["token"]!!, "list") },
),
ToolDef(
name = "ha_state", description = "获取 HomeAssistant 实体状态",
params = listOf(
ToolParam("url", "string", "HomeAssistant URL", required = true),
ToolParam("token", "string", "API Token", required = true),
ToolParam("entity_id", "string", "实体ID", required = true),
),
toCommand = { args -> listOf("iot_controller.py", "homeassistant", "--url", args["url"]!!, "--token", args["token"]!!, "state", "--entity-id", args["entity_id"]!!) },
),
ToolDef(
name = "ha_on", description = "打开 HomeAssistant 实体",
params = listOf(
ToolParam("url", "string", "HomeAssistant URL", required = true),
ToolParam("token", "string", "API Token", required = true),
ToolParam("entity_id", "string", "实体ID", required = true),
),
toCommand = { args -> listOf("iot_controller.py", "homeassistant", "--url", args["url"]!!, "--token", args["token"]!!, "on", "--entity-id", args["entity_id"]!!) },
),
ToolDef(
name = "ha_off", description = "关闭 HomeAssistant 实体",
params = listOf(
ToolParam("url", "string", "HomeAssistant URL", required = true),
ToolParam("token", "string", "API Token", required = true),
ToolParam("entity_id", "string", "实体ID", required = true),
),
toCommand = { args -> listOf("iot_controller.py", "homeassistant", "--url", args["url"]!!, "--token", args["token"]!!, "off", "--entity-id", args["entity_id"]!!) },
),
ToolDef(
name = "ha_toggle", description = "切换 HomeAssistant 实体状态",
params = listOf(
ToolParam("url", "string", "HomeAssistant URL", required = true),
ToolParam("token", "string", "API Token", required = true),
ToolParam("entity_id", "string", "实体ID", required = true),
),
toCommand = { args -> listOf("iot_controller.py", "homeassistant", "--url", args["url"]!!, "--token", args["token"]!!, "toggle", "--entity-id", args["entity_id"]!!) },
),
).associateBy { it.name }
/** 将 TOOLS 映射表转换为 OpenAI Function Calling 格式的 JSON 字符串 */
fun toolsToJson(): String {
val sb = StringBuilder("[\n")
TOOLS.values.forEachIndexed { i, tool ->
if (i > 0) sb.append(",\n")
sb.append(""" {"type":"function","function":{"name":"${tool.name}","description":"${tool.description}","parameters":{"type":"object","properties":{""")
tool.params.forEachIndexed { j, p ->
if (j > 0) sb.append(",")
sb.append("""""${p.name}":{"type":"${p.type}","description":"${p.description}"""")
if (p.required) sb.append(""","required":true""")
}
val requiredParams = tool.params.filter { it.required }.map { "\"${it.name}\"" }
if (requiredParams.isNotEmpty()) {
sb.append("""},"required":[${requiredParams.joinToString(",")}]}""")
} else {
sb.append("}}}")
}
sb.append("}")
}
sb.append("\n]")
return sb.toString()
}
// ═══════════════════════════════════════════
// System-controller 检测与模式选择
// ═══════════════════════════════════════════
// ═══════════════════════════════════════════
// MIXED 模式智能分析
// ═══════════════════════════════════════════
/**
* 智能分析任务类型,决定使用V1还是V2模式
*
* 规则:
* 1. 如果任务可以通过预定义工具完成 → V2模式
* 2. 如果任务需要文件操作、脚本执行等 → V1模式
* 3. 复杂任务可以拆分为多个步骤,每个步骤单独选择模式
*/
fun analyzeTaskType(task: String): String {
val v2Keywords = listOf(
"窗口", "进程", "音量", "亮度", "电源", "网络", "鼠标", "键盘", "截图", "OCR",
"串口", "IoT", "homeassistant", "亮度", "屏幕", "显示器", "激活", "最小化", "最大化",
"关闭窗口", "打开应用", "结束进程", "启动进程", "调整大小", "发送按键",
"鼠标移动", "鼠标点击", "键盘输入", "截屏", "文字识别", "找图", "颜色",
"串口发送", "串口接收", "智能家居", "HTTP请求"
)
val v1Keywords = listOf(
"文件", "编辑", "创建", "删除", "移动", "复制", "重命名", "脚本", "执行", "运行",
"网络", "数据库", "查询", "导入", "导出", "系统", "配置", "注册表", "服务",
"安装", "卸载", "更新", "安全", "防火墙", "权限", "编译", "构建", "测试",
"CSV", "JSON", "XML", "处理", "转换", "打包", "部署", "备份", "恢复"
)
val taskLower = task.lowercase()
val v2Count = v2Keywords.count { taskLower.contains(it.lowercase()) }
val v1Count = v1Keywords.count { taskLower.contains(it.lowercase()) }
return when {
v2Count > v1Count && v2Count > 0 -> "V2"
v1Count > v2Count && v1Count > 0 -> "V1"
else -> "MIXED" // 两者都有或无法判断
}
}
/**
* 检测 system-controller 是否可用
* 1. 检查 skill 目录是否存在
* 2. 检查主要脚本是否存在
* 3. 检查 Python 环境是否可用
*/
fun isSystemControllerAvailable(config: Config): Boolean {
try {
// 1. 检查 skill 目录
val skillDir = File(config.skillPath)
if (!skillDir.exists() || !skillDir.isDirectory) {
println("⚠️ system-controller 目录不存在: ${config.skillPath}")
return false
}
// 2. 检查 scripts 子目录
val scriptsDir = File(skillDir, "scripts")
if (!scriptsDir.exists() || !scriptsDir.isDirectory) {
println("⚠️ system-controller 脚本目录不存在: $scriptsDir")
return false
}
// 3. 检查主要脚本文件
val requiredScripts = listOf("window_manager.py", "process_manager.py", "hardware_controller.py")
for (script in requiredScripts) {
val scriptFile = File(scriptsDir, script)
if (!scriptFile.exists()) {
println("⚠️ system-controller 缺少必要脚本: $script")
return false
}
}
// 4. 测试 Python 环境
try {
val testProc = ProcessBuilder(config.pythonPath, "--version")
.redirectErrorStream(true)
.start()
testProc.waitFor(5, TimeUnit.SECONDS)
if (testProc.exitValue() != 0) {
println("⚠️ Python 环境不可用: ${config.pythonPath}")
return false
}
} catch (e: Exception) {
println("⚠️ Python 环境测试失败: ${e.message}")
return false
}
println("✅ system-controller 可用: ${config.skillPath}")
return true
} catch (e: Exception) {
println("⚠️ system-controller 检测异常: ${e.message}")
return false
}
}
/**
* 智能选择运行模式
* 根据配置和 system-controller 可用性自动选择最佳模式
*/
fun determineActualMode(config: Config): RunMode {
return when (config.mode) {
RunMode.AUTO -> {
if (isSystemControllerAvailable(config)) {
println("📊 自动选择:system-controller 可用 → 使用 function 模式 (V2)")
RunMode.FUNCTION
} else {
println("📊 自动选择:system-controller 不可用 → 使用 text 模式 (V1)")
RunMode.TEXT
}
}
RunMode.MIXED -> {
println("📊 混合模式:智能分析任务,V1和V2命令夹杂自由切换按顺序组合")
RunMode.MIXED
}
RunMode.FORCE_TEXT -> {
println("📊 强制模式:忽略 system-controller → 使用 text 模式 (V1)")
RunMode.TEXT
}
RunMode.FORCE_FUNCTION -> {
println("📊 强制模式:强制 function 模式 (V2),system-controller 可能不可用")
RunMode.FUNCTION
}
else -> config.mode // TEXT 或 FUNCTION 直接使用
}
}
// ═══════════════════════════════════════════
// LLM API 调用(两种模式共用底层 HTTP 请求)
// ═══════════════════════════════════════════
/**
* 调用 LLM API
* @param config 配置
* @param messages 对话历史
* @param toolsJson Function Calling 工具定义 JSON(function 模式必填,text 模式传 null)
* @return LLM 响应
*/
fun callLlm(config: Config, messages: List<Message>, toolsJson: String? = null): LlmResponse {
// 构建 JSON 请求体
val body = buildString {
append("""{"model":"${config.model}","max_tokens":${config.maxTokens},"temperature":${config.temperature},"messages":[""")
messages.forEachIndexed { i, m ->
if (i > 0) append(",")
append("""{"role":"${m.role}","content":${escapeJson(m.content)}}""")
}
append("]")
// Function Calling 模式附加工具定义
if (toolsJson != null) {
append(""","tools":$toolsJson,"tool_choice":"auto"""")
}
append("}")
}
// 使用 curl 发送 HTTP POST 请求(纯 HTTP,无 SDK 依赖)
val proc = ProcessBuilder("curl", "-s", "-X", "POST", config.apiUrl,
"-H", "Content-Type: application/json",
"-H", "Authorization: Bearer ${config.apiKey}",
"-d", body)
.redirectErrorStream(true)
.start()
val output = proc.inputStream.bufferedReader().readText()
proc.waitFor()
// 解析文本内容
val text = """content"\s*:\s*"((?:[^"\\]|\\.)*)""".toRegex()
.find(output)?.groupValues?.get(1)
// 如果是 function 模式,解析 tool_calls
val toolCalls = if (toolsJson != null) {
val calls = mutableListOf<ToolCall>()
// 正则提取 function call 的 name 和 arguments
val fcRegex = """"function"\s*:\s*\{\s*"name"\s*:\s*"(\w+)".*?"arguments"\s*:\s*(\{.*?\})""".toRegex(RegexOption.DOT_MATCHES_ALL)
fcRegex.findAll(output).forEach { match ->
val funcName = match.groupValues[1]
try {
@Suppress("UNCHECKED_CAST")
val args = simpleJsonParse(match.groupValues[2]) as Map<String, Any?>
calls.add(ToolCall(funcName, args))
} catch (_: Exception) { /* 解析失败则跳过 */ }
}
calls.ifEmpty { null }
} else {
null
}
return LlmResponse(text, toolCalls)
}
// ═══════════════════════════════════════════
// 脚本执行器(两种模式共用)
// ═══════════════════════════════════════════
/**
* 执行 system-controller Python 脚本
* @param config 配置
* @param scriptName 脚本文件名(如 window_manager.py)
* @param args 脚本参数列表
* @return 执行结果
*/
fun executeScript(config: Config, scriptName: String, vararg args: String): ExecutionResult {
// 组装完整命令:python + 脚本完整路径 + 参数
val finalArgs = mutableListOf(config.pythonPath, "${config.skillPath}/scripts/$scriptName")
finalArgs.addAll(args)
val cmdLine = finalArgs.joinToString(" ")
println("\n ⚡ 执行: $cmdLine")
try {
val proc = ProcessBuilder(finalArgs)
.redirectErrorStream(true)
.start()
val output = proc.inputStream.bufferedReader().readText()
val exited = proc.waitFor(config.timeoutSeconds, TimeUnit.SECONDS)
return ExecutionResult(
success = exited && proc.exitValue() == 0,
output = output.trim().take(2000), // 截断过长输出,节省 token
command = cmdLine,
)
} catch (e: Exception) {
return ExecutionResult(success = false, output = e.message ?: "未知错误", command = cmdLine)
}
}
/**
* 执行 Tool Call(Function Calling 模式专用)
* 根据 toolName 在 TOOLS 表中查找,通过 toCommand 映射后调用 executeScript
*/
fun executeToolCall(config: Config, toolName: String, args: Map<String, Any?>): ExecutionResult {
val tool = TOOLS[toolName]
?: return ExecutionResult(false, "未知工具: $toolName", toolName)
// 将参数全部转为字符串
val stringArgs = args.mapValues { (_, v) -> v?.toString() ?: "" }
// 通过 toCommand lambda 映射到实际脚本命令行
val scriptArgs = tool.toCommand(stringArgs)
// 执行:第一个元素是脚本名,剩余是参数
return executeScript(config, scriptArgs[0], *scriptArgs.drop(1).toTypedArray())
}
// ═══════════════════════════════════════════
// Text 模式主循环(V1)
// ═══════════════════════════════════════════
/**
* Text 模式的交互循环
* LLM 返回自然语言文本,从中正则提取脚本命令后执行
*/
fun runLoopText(config: Config, systemPrompt: String) {
val history = mutableListOf<Message>(
Message("system", systemPrompt)
)
println("╔══════════════════════════════════════╗")
println("║ Minimal Agent --- 文本模式 (V1) ║")
println("║ 输入指令开始,输入 quit 退出 ║")
println("╚══════════════════════════════════════╝")
while (true) {
print("\n❯ ")
val input = readLine()?.trim() ?: break
if (input == "quit") break
if (input.isEmpty()) continue
history.add(Message("user", input))
var iterations = 0
var done = false
while (!done && iterations < config.maxIterations) {
iterations++
// 1. 调用 LLM 决策(不传工具定义,让 LLM 自由生成文本)
val response = callLlm(config, history)
// 2. 检测回复中是否包含脚本调用命令
val hasScriptCall = Regex(
"""(window_manager|process_manager|hardware_controller|gui_controller|serial_comm|iot_controller)\.py\s+\w+"""
).containsMatchIn(response.text ?: "")
if (!hasScriptCall) {
// LLM 只是回复文字,不需要执行任何操作
println("\n🤖 ${response.text}")
response.text?.let { history.add(Message("assistant", it)) }
done = true
continue
}
println("\n🤖 决策: ${(response.text ?: "").take(150)}...")
// 3. 从 LLM 回复中正则提取脚本调用命令
val scriptCalls = Regex("""(\w+\.py)\s+([\w\-][\w\-\s="'/.:]*)""")
.findAll(response.text ?: "")
.toList()
if (scriptCalls.isEmpty()) {
println("\n🤖 ${response.text}")
response.text?.let { history.add(Message("assistant", it)) }
done = true
continue
}
// 4. 逐个执行提取到的脚本命令
val execResults = StringBuilder()
for ((_, matchResult) in scriptCalls) {
val scriptName = matchResult.groupValues[1]
val argsStr = matchResult.groupValues[2].trim()
// 按空白分割参数(处理带引号的值)
val args = argsStr.split(Regex("\\s+(?=[^\"']*[\"'])|(?<=[^\"']*[\"'])\\s+"))
.filter { it.isNotEmpty() }
val result = executeScript(config, scriptName, *args.toTypedArray())
val resultIcon = if (result.success) "✅" else "❌"
execResults.appendln("$resultIcon [${result.command}]")
execResults.appendln(result.output)
}
val execSummary = execResults.toString().take(2000)
// 5. 把执行结果喂回给 LLM,让它决定下一步
history.add(Message("assistant", response.text ?: ""))
history.add(Message("user", "[执行结果]:\n$execSummary\n\n请根据结果继续或结束。"))
}
if (iterations >= config.maxIterations) {
println("\n⚠️ 达到最大操作次数限制 (${config.maxIterations}),已自动停止。")
}
}
println("\n👋 再见!")
}
// ═══════════════════════════════════════════
// MIXED 模式主循环(智能混合)
// ═══════════════════════════════════════════
/**
* MIXED 模式的交互循环
* 智能分析任务,自动选择V1或V2模式,支持V1和V2命令夹杂自由切换按顺序组合
*/
fun runLoopMixed(config: Config, systemPrompt: String) {
val history = mutableListOf<Message>(Message("system", systemPrompt))
val toolsJson = toolsToJson() // 预生成工具定义 JSON(只生成一次)
println("╔══════════════════════════════════════════╗")
println("║ Minimal Agent --- 智能混合模式 (MIXED) ║")
println("║ V1和V2命令夹杂自由切换按顺序组合 ║")
println("╚══════════════════════════════════════════╝")
println("📊 系统检测: ${if (isSystemControllerAvailable(config)) "✅ system-controller 可用" else "⚠️ system-controller 不可用"}")
while (true) {
print("\n💬 请输入指令(输入 'exit' 退出):")
val input = readLine()?.trim()
if (input == null || input.lowercase() == "exit") break
// 智能分析任务类型
val taskType = analyzeTaskType(input)
println("📊 智能分析: 任务类型 = $taskType")
// 添加到历史
history.add(Message("user", input))
// 根据任务类型选择执行方式
when (taskType) {
"V2" -> {
// V2模式:Function Calling
println("📊 执行模式: V2(Function Calling)")
val resp = callLlm(config, history, toolsJson)
if (resp.toolCalls != null) {
for (tc in resp.toolCalls!!) {
val r = executeToolCall(config, tc.name, tc.arguments)
println(r.output)
history.add(Message("assistant", r.output))
}
} else {
println(resp.text)
history.add(Message("assistant", resp.text ?: ""))
}
}
"V1" -> {
// V1模式:文本命令
println("📊 执行模式: V1(文本命令)")
val resp = callLlm(config, history)
println(resp.text)
history.add(Message("assistant", resp.text ?: ""))
// 尝试提取并执行命令
val commands = extractCommands(resp.text ?: "")
if (commands.isNotEmpty()) {
println("📊 提取到 ${commands.size} 个命令")
for (cmd in commands) {
val r = executeCommand(config, cmd)
println(r.output)
history.add(Message("assistant", r.output))
}
}
}
"MIXED" -> {
// MIXED模式:需要LLM智能分解任务
println("📊 执行模式: MIXED(智能分解)")
val resp = callLlm(config, history, toolsJson)
if (resp.toolCalls != null) {
// LLM选择了V2模式工具调用
println("📊 LLM选择: V2模式工具调用")
for (tc in resp.toolCalls!!) {
val r = executeToolCall(config, tc.name, tc.arguments)
println(r.output)
history.add(Message("assistant", r.output))
}
} else {
// LLM选择了V1模式文本命令
println("📊 LLM选择: V1模式文本命令")
println(resp.text)
history.add(Message("assistant", resp.text ?: ""))
// 尝试提取并执行命令
val commands = extractCommands(resp.text ?: "")
if (commands.isNotEmpty()) {
println("📊 提取到 ${commands.size} 个命令")
for (cmd in commands) {
val r = executeCommand(config, cmd)
println(r.output)
history.add(Message("assistant", r.output))
}
}
}
}
}
}
println("\n👋 再见!")
}
// ═══════════════════════════════════════════
// Function Calling 模式主循环(V2)
// ═══════════════════════════════════════════
/**
* Function Calling 模式的交互循环
* LLM 返回结构化 JSON 工具调用,框架直接映射到脚本执行
* 比 text 模式节省约 85% token
*/
fun runLoopFunction(config: Config, systemPrompt: String) {
val history = mutableListOf<Message>(Message("system", systemPrompt))
val toolsJson = toolsToJson() // 预生成工具定义 JSON(只生成一次)
println("╔══════════════════════════════════════╗")
println("║ Minimal Agent --- 函数调用模式 (V2) ║")
println("║ ${TOOLS.size} 个原子工具 · 结构化调用 ║")
println("╚══════════════════════════════════════╝")
while (true) {
print("\n❯ ")
val input = readLine()?.trim() ?: break
if (input == "quit") break
if (input.isEmpty()) continue
history.add(Message("user", input))
var iterations = 0
while (iterations < config.maxIterations) {
iterations++
// 1. 调用 LLM(附带工具定义)
val response = callLlm(config, history, toolsJson)
// Case 1: LLM 返回纯文字(不需要调工具)
if (response.toolCalls == null || response.toolCalls!!.isEmpty()) {
println("\n🤖 ${response.text ?: "(无回复)"}")
response.text?.let { history.add(Message("assistant", it)) }
break
}
// Case 2: LLM 返回 tool calls → 逐个执行
println("\n🤖 调用 ${response.toolCalls!!.size} 个工具...")
val results = mutableListOf<String>()
for (tc in response.toolCalls!!) {
val result = executeToolCall(config, tc.name, tc.arguments)
val icon = if (result.success) "✅" else "❌"
results.add("$icon [${result.command}]\n${result.output}")
}
// 3. 把执行结果喂回 LLM(作为普通消息)
history.add(Message("assistant", response.text ?: ""))
history.add(Message("user", "[工具执行结果]:\n${results.joinToString("\n---\n")}\n\n请根据结果继续或结束。"))
}
if (iterations >= config.maxIterations) {
println("\n⚠️ 达到最大操作次数 (${config.maxIterations})")
}
}
println("\n👋 再见!")
}
// ═══════════════════════════════════════════
// 入口 --- 根据配置路由到对应模式的循环
// ═══════════════════════════════════════════
fun main(args: Array<String>) {
val config = loadConfig()
val actualMode = determineActualMode(config)
val systemPrompt = loadPrompt(config)
println("\n📋 配置模式: ${config.mode}")
println("📋 实际模式: $actualMode")
println("📋 路径检查: ${config.skillPath}")
// 处理命令行参数
val hasForceModeArg = args.any { it == "--text" || it == "--function" || it == "--auto" || it == "--mixed" }
var forceMode: RunMode? = null
if (hasForceModeArg) {
when {
args.contains("--text") -> forceMode = RunMode.TEXT
args.contains("--function") -> forceMode = RunMode.FUNCTION
args.contains("--auto") -> forceMode = RunMode.AUTO
args.contains("--mixed") -> forceMode = RunMode.MIXED
}
println("📋 命令行强制模式: $forceMode")
}
val finalMode = forceMode ?: actualMode
if (args.isNotEmpty() && args[0] == "--") {
// 单次模式:直接处理命令行参数作为用户输入
val input = args.drop(1).joinToString(" ")
val msgs = listOf(Message("system", systemPrompt), Message("user", input))
when (finalMode) {
RunMode.FUNCTION, RunMode.FORCE_FUNCTION -> {
val resp = callLlm(config, msgs, toolsJson())
if (resp.toolCalls != null) {
for (tc in resp.toolCalls!!) {
val r = executeToolCall(config, tc.name, tc.arguments)
println(r.output)
}
} else {
println(resp.text)
}
}
RunMode.TEXT, RunMode.FORCE_TEXT -> {
println(callLlm(config, msgs).text)
}
RunMode.MIXED -> {
// MIXED模式在单次模式下智能分析并执行
println("📊 混合模式:智能分析并执行任务")
val taskType = analyzeTaskType(input)
println("📊 任务类型分析: $taskType")
if (taskType == "V2") {
val resp = callLlm(config, msgs, toolsJson())
if (resp.toolCalls != null) {
for (tc in resp.toolCalls!!) {
val r = executeToolCall(config, tc.name, tc.arguments)
println(r.output)
}
} else {
println(resp.text)
}
} else {
println(callLlm(config, msgs).text)
// 尝试提取并执行命令
val commands = extractCommands(callLlm(config, msgs).text ?: "")
if (commands.isNotEmpty()) {
println("📊 提取到 ${commands.size} 个命令")
for (cmd in commands) {
val r = executeCommand(config, cmd)
println(r.output)
}
}
}
}
RunMode.AUTO -> {
// AUTO模式在单次模式下也检测可用性
val modeForThisTask = if (isSystemControllerAvailable(config)) RunMode.FUNCTION else RunMode.TEXT
println("📊 本次任务自动选择: $modeForThisTask")
if (modeForThisTask == RunMode.FUNCTION) {
val resp = callLlm(config, msgs, toolsJson())
if (resp.toolCalls != null) {
for (tc in resp.toolCalls!!) {
val r = executeToolCall(config, tc.name, tc.arguments)
println(r.output)
}
} else {
println(resp.text)
}
} else {
println(callLlm(config, msgs).text)
}
}
}
} else if (args.isNotEmpty() && hasForceModeArg) {
// 交互模式:根据最终模式选择对应的循环
val actualModeForInteractive = determineActualMode(config.copy(mode = finalMode))
when (actualModeForInteractive) {
RunMode.FUNCTION -> runLoopFunction(config, systemPrompt)
RunMode.TEXT -> runLoopText(config, systemPrompt)
RunMode.MIXED -> runLoopMixed(config, systemPrompt)
RunMode.AUTO -> {
val autoMode = if (isSystemControllerAvailable(config)) RunMode.FUNCTION else RunMode.TEXT
println("📊 自动选择: $autoMode")
if (autoMode == RunMode.FUNCTION) {
runLoopFunction(config, systemPrompt)
} else {
runLoopText(config, systemPrompt)
}
}
else -> runLoopFunction(config, systemPrompt)
}
} else {
// 交互模式:根据最终模式选择对应的循环
when (finalMode) {
RunMode.FUNCTION -> runLoopFunction(config, systemPrompt)
RunMode.TEXT -> runLoopText(config, systemPrompt)
RunMode.MIXED -> runLoopMixed(config, systemPrompt)
RunMode.AUTO -> {
val autoMode = if (isSystemControllerAvailable(config)) RunMode.FUNCTION else RunMode.TEXT
println("📊 自动选择: $autoMode")
if (autoMode == RunMode.FUNCTION) {
runLoopFunction(config, systemPrompt)
} else {
runLoopText(config, systemPrompt)
}
}
RunMode.FORCE_TEXT -> runLoopText(config, systemPrompt)
RunMode.FORCE_FUNCTION -> runLoopFunction(config, systemPrompt)
}
}
}
main(args.sliceArray(1..lastIndex))
name: minimal-agent
description: >
极简 AI 操作系统控制代理。通过执行协议驱动 system-controller
完成窗口管理、进程控制、硬件操作、GUI自动化、串口通信和IoT设备交互。
当用户需要操控 Windows 系统(打开/关闭应用、调整音量亮度、
截图OCR、管理进程、串口通信等)时使用此 Skill。
触发词:打开应用、关闭窗口、调音量、调亮度、锁屏、关机、
列出进程、发串口命令、控灯、连Arduino、开WiFi、USB设备、
截屏、OCR、找图点击、输入文字、鼠标操作等。
Minimal Agent --- 极简操作系统控制代理
执行协议
执行协议等待指令:简单任务用自适应身份叠加输入处理输出结构基元执行,复杂任务分拆成简单任务交给基元构成链条执行,默认不展示输入处理输出细节但用户可以要求展示。优先根据提示词使用大语言模型完成任务,非必要不调用工具技能插件,把工具技能插件作为身份或基元进行规划调用。
架构
WorkBuddy(LLM 大脑 + 对话管理 + 模型配置)
↓ 加载本 Skill
minimal-agent SKILL.md(提示词 + 执行协议 + 能力说明)
↓ 调用脚本
system-controller scripts/(实际操作系统控制)
核心原则:
- 本 Agent 不自带 LLM,由 WorkBuddy 提供模型能力
- 本 Agent 不自建对话循环,由 WorkBuddy 管理上下文
- Agent 的职责 = 理解意图 → 选择正确的 system-controller 脚本 → 执行 → 返回结果
- 支持智能模式选择,自动检测 system-controller 可用性
智能模式选择
6种运行模式
| 模式 | 说明 | 能力范围 | 适用场景 |
|---|---|---|---|
function(V2) |
Function Calling 结构化调用 | 限制在55个预定义工具内,只能调用system-controller脚本 | 生产环境,安全敏感场景 |
text(V1) |
LLM 生成文本命令,正则提取 | 无限制,可执行任何系统命令(包括文件修改、脚本执行、网络操作、数据库操作等) | 开发/调试/运维,需要完全控制权的场景 |
auto(推荐) |
智能检测:有system-controller用V2,没有或不可用时用V1 | 自动适配 | 通用场景,无需手动切换 |
mixed(高级) |
智能混合:自动分析任务,V1和V2命令夹杂自由切换按顺序组合 | 智能适配 | 复杂多步骤任务,需要V1和V2模式组合 |
force_text |
强制V1模式,无论是否有system-controller | 无限制 | 需要完全控制权,忽略system-controller |
force_function |
强制V2模式,无论system-controller是否可用 | 限制在55个工具内 | 强制使用结构化调用,忽略可用性 |
模式切换与组合使用
Minimal Agent 支持动态模式切换 和组合使用:
-
动态切换:在单次会话中可以通过命令切换模式
bash# 启动时指定模式 python agent.py --text --interactive python agent.py --function --interactive python agent.py --mixed --interactive # 会话中切换 "切换到V2模式" "用V1模式执行这个文件操作" "切回自动模式" "使用混合模式处理这个复杂任务" -
组合使用:V1和V2模式可以在单个复杂任务中组合
bash# 执行复杂任务:硬件控制 + 文件操作 + 系统管理 python agent.py --mixed "截屏后OCR文字,保存到文件,然后调整音量" -
智能适配:
- auto模式:自动根据system-controller可用性选择最佳模式
- mixed模式:智能分析任务,V1和V2命令夹杂自由切换按顺序组合
- 硬件控制、窗口管理 → 自动使用V2模式(如果可用)
- 文件操作、脚本执行 → 自动使用V1模式(无限制)
V1模式的无限制能力
在 V1(text)模式 下,Minimal Agent 可以执行任何系统命令,包括但不限于:
- 文件操作:创建、删除、编辑、移动、复制文件
- 脚本执行:运行 Python、PowerShell、Bash 脚本
- 网络操作:HTTP请求、FTP传输、端口扫描
- 数据库操作:SQL查询、数据导入导出
- 系统管理:用户管理、服务控制、注册表编辑
- 软件安装:安装程序、更新包管理
- 安全操作:防火墙配置、权限管理
示例:
bash
# V1模式可以执行任何命令
python agent.py --text "创建test.txt文件并写入内容"
python agent.py --text "运行Python脚本处理数据"
python agent.py --text "查询数据库并生成报告"
python agent.py --text "安装软件包并配置环境"
V2模式则专注于安全、可靠的硬件/软件控制,限制在55个预定义工具内。
模式优先级
- 命令行参数 :
--text、--function、--auto(最高优先级) - 配置文件 :
config.toml中的mode字段 - 自动检测 :当
mode = "auto"时自动检测 system-controller 可用性
智能检测逻辑
- system-controller可用 → 使用
function模式(V2) - system-controller不可用 → 使用
text模式(V1) - 不可用原因:目录不存在、脚本缺失、Python环境不可用
使用示例
bash
# 交互模式(自动检测)
python agent.py --interactive
# 交互模式(强制V1模式)
python agent.py --text --interactive
# 交互模式(强制V2模式)
python agent.py --function --interactive
# 单次命令模式(自动检测)
python agent.py "帮我打开记事本"
# 单次命令模式(强制V1模式)
python agent.py --text "dir C:\\"
python agent.py --text "echo Hello > test.txt"
# 单次命令模式(强制V2模式)
python agent.py --function window_list
# 工具调用模式
python agent.py window_list
python agent.py volume_set --level 50
python agent.py process_list --name "chrome"
# 配置模式切换:编辑 scripts/config.toml 中的 mode 字段
# mode = "auto" # 自动检测(推荐)
# mode = "function" # 强制V2模式
# mode = "text" # 强制V1模式
# mode = "force_function" # 强制V2模式,忽略可用性
# mode = "force_text" # 强制V1模式,忽略可用性
能力清单
窗口管理 (window_manager.py)
| 操作 | 命令 | 说明 |
|---|---|---|
| 列出窗口 | list |
title, pid, process |
| 激活窗口 | activate --title/pid |
置于前台 |
| 关闭窗口 | close --title/pid |
关闭 |
| 最小化 | minimize --title/pid |
最小化 |
| 最大化 | maximize --title/pid |
最大化 |
| 调整大小位置 | resize --pid --x --y --w --h |
移动+缩放 |
| 发送按键 | send-keys --title/pid --text |
如 ENTER, ^c |
进程管理 (process_manager.py)
| 操作 | 命令 | 说明 |
|---|---|---|
| 列出进程 | list [--name] |
查找/列表 |
| 结束进程 | kill --name/pid [--force] |
终止 |
| 启动应用 | start "命令" [--dir] |
启动 |
| 进程详情 | info --pid |
详细信息 |
| 系统总览 | system |
CPU/内存/磁盘 |
硬件控制 (hardware_controller.py)
| 操作 | 命令 | 说明 |
|---|---|---|
| 音量 | volume get/set --level / mute |
音量控制 |
| 亮度 | screen brightness [--level] / info |
显示器亮度 |
| 电源 | power lock/sleep/hibernate/shutdown/restart/cancel |
电源管理 |
| 网络 | network adapters/enable/disable --name/wifi/info |
网络适配器 |
| USB | usb list |
USB 设备列表 |
GUI 控制 (gui_controller.py)
| 操作 | 命令 | 说明 |
|---|---|---|
| 鼠标移动 | mouse move --x --y |
定位 |
| 鼠标点击 | mouse click/right-click/double-click --x --y |
点击 |
| 鼠标拖拽 | mouse drag --start-x --start-y --end-x --end-y |
拖动 |
| 鼠标滚动 | mouse scroll --direction up/down --clicks |
滚轮 |
| 鼠标位置 | mouse position |
获取坐标 |
| 键盘输入 | keyboard type --text |
文字输入 |
| 键盘按键 | keyboard press --keys |
如 ctrl+c, alt+tab |
| 截图 | screenshot full/active-window/size |
屏幕截图 |
| OCR识别 | visual ocr [--x --y --w --h] |
文字识别 |
| 图像匹配 | visual find --template "img.png" |
找图 |
| 图像点击 | visual click-image --template "img.png" |
找图并点 |
| 取像素色 | visual pixel --x --y |
颜色值 |
串口通信 (serial_comm.py)
| 操作 | 命令 | 说明 |
|---|---|---|
| 列出端口 | list |
可用串口 |
| 发送数据 | send --port COMx --data "..." |
发送 |
| 接收数据 | receive --port COMx |
接收 |
| 交互模式 | chat/monitor --port COMx --data "..." |
持续通信 |
IoT 控制 (iot_controller.py)
| 操作 | 命令 | 说明 |
|---|---|---|
| HomeAssistant | homeassistant --url --token list/state/on/off/toggle/entity-id |
智能家居 |
| HTTP API | http --url get/post/put --path |
通用HTTP请求 |
执行规则
- 先查询后操作:list/find → 再执行动作
- 危险操作必须确认:关机、杀进程、关闭窗口、禁用网络前需说明并等待确认
- 返回简洁结果:只报告关键信息
- 失败时分析原因:给出替代方案
- 支持复合任务:可组合多个步骤完成复杂需求
- 支持撤销回滚:用户说"撤销"/"回滚"时反向执行
能力总结
Minimal Agent 具备完整的能力栈:
1. 硬件/软件控制能力(V2模式,55个工具)
- 窗口管理:控制应用窗口
- 进程管理:管理系统进程
- 硬件控制:调节音量、亮度、电源
- GUI自动化:鼠标、键盘、截图、OCR
- 串口通信:与硬件设备通信
- IoT控制:智能家居、HTTP API
2. 无限制系统命令能力(V1模式)
- 文件操作:创建、删除、编辑、移动文件
- 脚本执行:运行任何脚本(Python、Bash、PowerShell)
- 网络操作:HTTP请求、FTP、端口扫描
- 数据库操作:SQL查询、数据导入导出
- 系统管理:用户管理、服务控制、注册表
- 软件安装:安装、配置、更新软件
- 安全操作:防火墙、权限管理
- 开发工具:编译、构建、测试
- 数据操作:处理CSV、JSON、XML文件
- 任何其他可以通过命令行完成的任务
3. 智能模式切换(V1+V2组合)
- 动态切换:会话中随时切换模式
- 组合使用:单个任务中混合使用V1和V2
- 智能适配:auto模式自动选择最佳模式
4. 关键优势
- V2模式:安全、可靠、结构化,适合生产环境
- V1模式:无限制、灵活、强大,适合开发运维
- 双模式:两者兼得,根据需求选择
- 自动检测:无需手动配置,智能选择最佳模式
一句话总结 :Minimal Agent 能通过V1模式做任何可以通过命令行完成的事情 ,同时通过V2模式安全可靠地控制硬件软件 ,并支持智能切换和组合使用。
依赖
- system-controller Skill :
~/.workbuddy/skills/system-controller/ - Python 3.x:Managed runtime(WorkBuddy 自动提供)
下载安装 system-controller
Minimal Agent 需要 system-controller 来实现完整的硬件/软件控制能力:
-
官方安装(推荐):
- 从腾讯云技能市场安装:https://skillhub.tencent.com/skills/system-controller
- 通过 WorkBuddy 技能中心搜索 "system-controller"
-
GitHub 安装:
bash# 克隆到 WorkBuddy 技能目录 git clone https://github.com/clawhub/wangjiaocheng/system-controller.git ~/.workbuddy/skills/system-controller # 或者从镜像站 git clone https://clawhub.ai/wangjiaocheng/system-controller ~/.workbuddy/skills/system-controller -
手动安装:
- 下载:https://clawhub.ai/wangjiaocheng/system-controller/archive/main.zip
- 解压到:
~/.workbuddy/skills/system-controller/
Python 路径
python
# 基础脚本(无额外依赖)
PYTHON = r"C:\Users\wave\.workbuddy\binaries\python\versions\3.13.12\python.exe"
# GUI 脚本(需要 pyautogui/pillow,使用 venv)
PYTHON_VENV = r"C:\Users\wave\.workbuddy\binaries\python\envs\default\Scripts\python.exe"
脚本路径
所有 system-controller 脚本位于:
~/.workbuddy/skills/system-controller/scripts/
yaml
# Minimal Agent 配置(Python Skill 版)
[llm]
# 这些字段由 WorkBuddy 运行时提供,无需配置
# api_url = "https://api.openai.com/v1/chat/completions"
# api_key = "..."
# model = "gpt-4o"
# max_tokens = 4096
# temperature = 0.7
[system_controller]
skill_path = "~/.workbuddy/skills/system-controller" # Skill 安装路径
python_path = "python" # Python 解释器
[execution]
# 运行模式(支持6种模式):
# function - V2模式,使用system-controller工具(默认推荐)
# text - V1模式,执行任意命令
# auto - 自动检测:有system-controller用V2,没有或不可用时用V1
# mixed - 智能混合模式:自动分析任务,智能组合V1和V2命令
# force_text - 强制V1模式,无论是否有system-controller
# force_function - 强制V2模式,无论system-controller是否可用
mode = "auto"
timeout_seconds = 30 # 单个脚本执行超时秒数
max_iterations = 20 # 单轮对话最大操作次数(防死循环)
python
#!/usr/bin/env python3
# ═══════════════════════════════════════════
# Minimal Agent --- 极简 AI 操作系统控制代理(Python Skill 版)
# ═══════════════════════════════════════════
#
# 职责:接收指令 → 选择 system-controller 脚本 → 执行 → 返回结果
# LLM 和对话管理由 WorkBuddy 提供,本脚本只做"手脚"工作。
#
# **支持智能模式选择**:
# - function (V2) 模式:限制在55个预定义工具内,只能调用system-controller脚本
# - text (V1) 模式:无限制,可执行任何系统命令(包括文件修改、脚本执行等)
# - auto 模式:自动检测system-controller可用性,有则用V2,无则用V1
# - force_text/force_function 模式:强制指定模式
#
# **模式优先级**:
# 1. 命令行参数(--text, --function, --auto)
# 2. 配置文件 mode 字段
# 3. 自动检测(auto模式)
#
# 用法(WorkBuddy 自动调用,也可独立运行):
# python agent.py "帮我打开记事本"
# python agent.py --interactive
# python agent.py --text --interactive # 强制V1模式
# python agent.py --function "列出窗口" # 强制V2模式
# python agent.py --auto --interactive # 自动检测模式
import subprocess
import sys
import os
import json
import shutil
import io
import tomllib
from pathlib import Path
from typing import Literal
# 修复 Windows 控制台 UTF-8 编码
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
# ═══════════════════════════════════════════
# 类型定义
# ═══════════════════════════════════════════
ModeType = Literal["function", "text", "auto", "mixed", "force_text", "force_function"]
# ═══════════════════════════════════════════
# 配置
# ═══════════════════════════════════════════
class Config:
"""配置类"""
def __init__(self):
self.skill_path = ""
self.python_path = ""
self.mode: ModeType = "auto"
self.timeout_seconds = 30
self._load_config()
def _load_config(self):
"""从配置文件加载配置"""
config_file = Path(__file__).parent / "config.toml"
if not config_file.exists():
# 使用默认配置
self._set_defaults()
return
try:
with open(config_file, "rb") as f:
config_data = tomllib.load(f)
# system_controller 配置
sys_ctrl = config_data.get("system_controller", {})
self.skill_path = sys_ctrl.get("skill_path", "~/.workbuddy/skills/system-controller")
self.python_path = sys_ctrl.get("python_path", "python")
# execution 配置
exec_config = config_data.get("execution", {})
self.mode = exec_config.get("mode", "auto")
self.timeout_seconds = exec_config.get("timeout_seconds", 30)
# 扩展 ~ 为绝对路径
if self.skill_path.startswith("~"):
self.skill_path = str(Path.home() / self.skill_path[2:])
except Exception as e:
print(f"⚠️ 配置文件加载失败: {e},使用默认配置")
self._set_defaults()
def _set_defaults(self):
"""设置默认配置"""
self.skill_path = str(Path.home() / ".workbuddy" / "skills" / "system-controller")
self.python_path = "python"
self.mode = "auto"
self.timeout_seconds = 30
CONFIG = Config()
# ═══════════════════════════════════════════
# 路径配置
# ═══════════════════════════════════════════
HOME = Path.home()
SKILL_BASE = HOME / ".workbuddy" / "skills"
SYSTEM_CONTROLLER = Path(CONFIG.skill_path) / "scripts"
# Python 运行时路径(根据配置)
PYTHON = CONFIG.python_path
PYTHON_VENV = r"C:\Users\wave\.workbuddy\binaries\python\envs\default\Scripts\python.exe" # 备用venv路径
# ═══════════════════════════════════════════
# System-controller 检测与模式选择
# ═══════════════════════════════════════════
def is_system_controller_available() -> bool:
"""
检测 system-controller 是否可用
1. 检查 skill 目录是否存在
2. 检查主要脚本是否存在
3. 检查 Python 环境是否可用
"""
try:
# 1. 检查 skill 目录
skill_dir = Path(CONFIG.skill_path)
if not skill_dir.exists() or not skill_dir.is_dir():
print(f"⚠️ system-controller 目录不存在: {CONFIG.skill_path}")
return False
# 2. 检查 scripts 子目录
scripts_dir = skill_dir / "scripts"
if not scripts_dir.exists() or not scripts_dir.is_dir():
print(f"⚠️ system-controller 脚本目录不存在: {scripts_dir}")
return False
# 3. 检查主要脚本文件
required_scripts = ["window_manager.py", "process_manager.py", "hardware_controller.py"]
for script in required_scripts:
script_file = scripts_dir / script
if not script_file.exists():
print(f"⚠️ system-controller 缺少必要脚本: {script}")
return False
# 4. 测试 Python 环境
try:
result = subprocess.run(
[CONFIG.python_path, "--version"],
capture_output=True,
text=True,
timeout=5,
)
if result.returncode != 0:
print(f"⚠️ Python 环境不可用: {CONFIG.python_path}")
return False
except Exception as e:
print(f"⚠️ Python 环境测试失败: {e}")
return False
print(f"✅ system-controller 可用: {CONFIG.skill_path}")
return True
except Exception as e:
print(f"⚠️ system-controller 检测异常: {e}")
return False
def analyze_task_type(task: str) -> str:
"""
智能分析任务类型,决定使用V1还是V2模式
规则:
1. 如果任务可以通过预定义工具完成 → V2模式
2. 如果任务需要文件操作、脚本执行等 → V1模式
3. 复杂任务可以拆分为多个步骤,每个步骤单独选择模式
"""
v2_keywords = [
"窗口", "进程", "音量", "亮度", "电源", "网络", "鼠标", "键盘", "截图", "OCR",
"串口", "IoT", "homeassistant", "亮度", "屏幕", "显示器", "激活", "最小化", "最大化",
"关闭窗口", "打开应用", "结束进程", "启动进程", "调整大小", "发送按键",
"鼠标移动", "鼠标点击", "键盘输入", "截屏", "文字识别", "找图", "颜色",
"串口发送", "串口接收", "智能家居", "HTTP请求"
]
v1_keywords = [
"文件", "编辑", "创建", "删除", "移动", "复制", "重命名", "脚本", "执行", "运行",
"网络", "数据库", "查询", "导入", "导出", "系统", "配置", "注册表", "服务",
"安装", "卸载", "更新", "安全", "防火墙", "权限", "编译", "构建", "测试",
"CSV", "JSON", "XML", "处理", "转换", "打包", "部署", "备份", "恢复"
]
task_lower = task.lower()
v2_count = sum(1 for keyword in v2_keywords if keyword.lower() in task_lower)
v1_count = sum(1 for keyword in v1_keywords if keyword.lower() in task_lower)
if v2_count > v1_count and v2_count > 0:
return "V2"
elif v1_count > v2_count and v1_count > 0:
return "V1"
else:
return "MIXED" # 两者都有或无法判断
def determine_actual_mode(force_mode: ModeType | None = None) -> Literal["function", "text", "mixed"]:
"""
智能选择运行模式
根据配置和 system-controller 可用性自动选择最佳模式
参数:
force_mode: 强制模式(来自命令行参数)
"""
# 优先级:命令行参数 > 配置文件 > 自动检测
mode_to_use: ModeType = force_mode or CONFIG.mode
if mode_to_use in ("function", "force_function"):
print("📊 使用 function 模式 (V2)")
return "function"
if mode_to_use in ("text", "force_text"):
print("📊 使用 text 模式 (V1)")
return "text"
if mode_to_use == "mixed":
print("📊 使用 mixed 模式 (智能混合)")
return "mixed"
if mode_to_use == "auto":
if is_system_controller_available():
print("📊 自动选择:system-controller 可用 → 使用 function 模式 (V2)")
return "function"
else:
print("📊 自动选择:system-controller 不可用 → 使用 text 模式 (V1)")
return "text"
# 默认情况
print("📊 默认使用 function 模式 (V2)")
return "function"
def get_python(script_name: str) -> str:
"""根据脚本选择合适的 Python 解释器"""
# GUI 相关脚本需要 venv(含 pyautogui/pillow)
gui_scripts = {"gui_controller.py"}
if script_name in gui_scripts and os.path.exists(PYTHON_VENV):
return PYTHON_VENV
return PYTHON
def get_script_path(script_name: str) -> str:
"""获取 system-controller 脚本的完整路径"""
path = SYSTEM_CONTROLLER / script_name
if not path.exists():
raise FileNotFoundError(f"脚本不存在: {path},请确认 system-controller Skill 已安装")
return str(path)
# ═══════════════════════════════════════════
# 命令执行
# ═══════════════════════════════════════════
class ExecutionResult:
"""脚本执行结果"""
def __init__(self, success: bool, output: str, command: str):
self.success = success
self.output = output.strip()
self.command = command
def __repr__(self):
status = "✅" if self.success else "❌"
return f"{status} {self.output}"
def run_script(script_name: str, args: list[str], timeout: int = 30) -> ExecutionResult:
"""
执行 system-controller 脚本
参数:
script_name: 脚本文件名,如 "window_manager.py"
args: 命令行参数列表,如 ["list"]
timeout: 超时秒数
"""
python = get_python(script_name)
script_path = get_script_path(script_name)
cmd = [python, script_path] + args
command_str = " ".join(cmd)
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=timeout,
encoding="utf-8",
errors="replace",
)
output = result.stdout or result.stderr or "(无输出)"
success = result.returncode == 0
return ExecutionResult(success, output, command_str)
except subprocess.TimeoutExpired:
return ExecutionResult(False, f"⏰ 执行超时 ({timeout}s): {command_str}", command_str)
except FileNotFoundError as e:
return ExecutionResult(False, f"🔍 {e}", command_str)
except Exception as e:
return ExecutionResult(False, f"💥 执行异常: {e}", command_str)
# ═══════════════════════════════════════════
# 工具映射表(原子操作 → 脚本命令)
# ═══════════════════════════════════════════
# 每个工具定义:(脚本名, 默认参数模板)
# LLM/用户通过工具名调用,Agent 映射到实际脚本
TOOL_DEFS: dict[str, tuple[str, ...]] = {
# ---- 窗口管理 (window_manager.py) ----
"window_list": ("window_manager.py", "list"),
"window_activate": ("window_manager.py", "activate"),
"window_close": ("window_manager.py", "close"),
"window_minimize": ("window_manager.py", "minimize"),
"window_maximize": ("window_manager.py", "maximize"),
"window_resize": ("window_manager.py", "resize"),
"window_send_keys": ("window_manager.py", "send-keys"),
# ---- 进程管理 (process_manager.py) ----
"process_list": ("process_manager.py", "list"),
"process_kill": ("process_manager.py", "kill"),
"process_start": ("process_manager.py", "start"),
"process_info": ("process_manager.py", "info"),
"process_system": ("process_manager.py", "system"),
# ---- 硬件控制 (hardware_controller.py) ----
"volume_get": ("hardware_controller.py", "volume", "get"),
"volume_set": ("hardware_controller.py", "volume", "set"),
"volume_mute": ("hardware_controller.py", "volume", "mute"),
"brightness_set": ("hardware_controller.py", "screen", "brightness"),
"screen_info": ("hardware_controller.py", "screen", "info"),
"power_lock": ("hardware_controller.py", "power", "lock"),
"power_sleep": ("hardware_controller.py", "power", "sleep"),
"power_hibernate": ("hardware_controller.py", "power", "hibernate"),
"power_shutdown": ("hardware_controller.py", "power", "shutdown"),
"power_restart": ("hardware_controller.py", "power", "restart"),
"power_cancel": ("hardware_controller.py", "power", "cancel"),
"network_list": ("hardware_controller.py", "network", "adapters"),
"network_enable": ("hardware_controller.py", "network", "enable"),
"network_disable": ("hardware_controller.py", "network", "disable"),
"wifi_info": ("hardware_controller.py", "network", "wifi"),
"usb_list": ("hardware_controller.py", "usb", "list"),
# ---- GUI 控制 (gui_controller.py) ----
"mouse_move": ("gui_controller.py", "mouse", "move"),
"mouse_click": ("gui_controller.py", "mouse", "click"),
"mouse_right_click": ("gui_controller.py", "mouse", "right-click"),
"mouse_double_click": ("gui_controller.py", "mouse", "double-click"),
"mouse_drag": ("gui_controller.py", "mouse", "drag"),
"mouse_scroll": ("gui_controller.py", "mouse", "scroll"),
"mouse_position": ("gui_controller.py", "mouse", "position"),
"keyboard_type": ("gui_controller.py", "keyboard", "type"),
"keyboard_press": ("gui_controller.py", "keyboard", "press"),
"screenshot": ("gui_controller.py", "screenshot"),
"visual_ocr": ("gui_controller.py", "visual", "ocr"),
"visual_find": ("gui_controller.py", "visual", "find"),
"visual_click_image": ("gui_controller.py", "visual", "click-image"),
"visual_pixel": ("gui_controller.py", "visual", "pixel"),
# ---- 串口通信 (serial_comm.py) ----
"serial_list": ("serial_comm.py", "list"),
"serial_send": ("serial_comm.py", "send"),
"serial_receive": ("serial_comm.py", "receive"),
"serial_chat": ("serial_comm.py", "chat"),
"serial_monitor": ("serial_comm.py", "monitor"),
# ---- IoT 控制 (iot_controller.py) ----
"ha_list": ("iot_controller.py", "homeassistant", "list"),
"ha_state": ("iot_controller.py", "homeassistant", "state"),
"ha_on": ("iot_controller.py", "homeassistant", "on"),
"ha_off": ("iot_controller.py", "homeassistant", "off"),
"ha_toggle": ("iot_controller.py", "homeassistant", "toggle"),
"http_get": ("iot_controller.py", "http", "get"),
"http_post": ("iot_controller.py", "http", "post"),
"http_put": ("iot_controller.py", "http", "put"),
}
def execute_tool(tool_name: str, params: dict | None = None) -> ExecutionResult:
"""
通过工具名执行对应的 system-controller 脚本
参数:
tool_name: 工具名,如 "window_list"、"volume_set"
params: 额外参数字典,如 {"level": 50, "--title": "Chrome"}
键名会直接作为命令行参数传入
"""
if tool_name not in TOOL_DEFS:
available = ", ".join(sorted(TOOL_DEFS.keys()))
return ExecutionResult(
False,
f"未知工具: '{tool_name}'。可用工具: {available}",
tool_name
)
params = params or {}
# 从映射表获取基础参数
base_args = list(TOOL_DEFS[tool_name])
# 展开用户参数为命令行参数
extra_args = []
for key, value in params.items():
# 以 -- 开头的键保留前缀,否则加上
if key.startswith("--"):
extra_args.append(key)
else:
extra_args.append(f"--{key}")
if value is not None and value != "":
extra_args.append(str(value))
all_args = base_args + extra_args
script_name = all_args[0]
script_args = all_args[1:]
return run_script(script_name, script_args)
# ═══════════════════════════════════════════
# 危险操作检查
# ═══════════════════════════════════════════
DANGEROUS_TOOLS = {
"power_shutdown", "power_restart", "power_sleep", "power_hibernate",
"process_kill", "window_close", "network_disable",
}
def is_dangerous(tool_name: str) -> bool:
"""判断是否为危险操作"""
return tool_name in DANGEROUS_TOOLS
# ═══════════════════════════════════════════
# 交互模式(独立运行时使用)
# ═══════════════════════════════════════════
BANNER = """
╔══════════════════════════════════════╗
║ Minimal Agent --- 操作系统控制代理 ║
║ 输入自然语言或工具名即可操作系统 ║
║ 输入 quit 或 exit 退出 ║
║ 输入 tools 查看可用工具列表 ║
╚══════════════════════════════════════╝
"""
def print_tools():
"""打印工具分类清单"""
categories: dict[str, list[str]] = {}
for name, defn in TOOL_DEFS.items():
category = defn[0].replace(".py", "")
categories.setdefault(category, []).append(name)
print("\n📋 可用工具列表:\n")
for cat, tools in sorted(categories.items()):
print(f" 📦 {cat}")
for t in tools:
print(f" - {t}")
print()
def interactive_mode():
"""交互式循环(独立运行)"""
print(BANNER)
while True:
try:
user_input = input("\n🫵 ").strip()
except (EOFError, KeyboardInterrupt):
print("\n👋 再见!")
break
if not user_input:
continue
if user_input.lower() in ("quit", "exit", "q"):
print("👋 再见!")
break
if user_input.lower() == "tools":
print_tools()
continue
# 尝试作为工具调用解析: 工具名 [参数...]
parts = user_input.split()
tool_name = parts[0]
if tool_name in TOOL_DEFS:
# 解析简单参数: --key value --key2 value2
params = {}
i = 1
while i < len(parts):
key = parts[i]
if key.startswith("--"):
if i + 1 < len(parts) and not parts[i + 1].startswith("--"):
params[key] = parts[i + 1]
i += 2
else:
params[key] = True
i += 1
else:
i += 1
if is_dangerous(tool_name):
confirm = input(f" ⚠️ 危险操作 [{tool_name}],确认执行?(y/N): ")
if confirm.lower() != "y":
print(" ❌ 已取消")
continue
result = execute_tool(tool_name, params)
print(f"\n {result}")
else:
print(f" ❓ 未知命令: '{tool_name}'")
print(f" 💡 输入 'tools' 查看可用工具,或输入 'quit' 退出")
# ═══════════════════════════════════════════
# Mixed 模式支持(智能混合V1和V2)
# ═══════════════════════════════════════════
MIXED_MODE_PROMPT = """
你是一个智能的任务分析器,负责将复杂的多步骤任务分解为适合的执行模式。
**模式选择规则:**
1. **V2模式(Function Calling)**:用于以下类型的子任务:
- 窗口管理(最小化、最大化、关闭、移动等)
- 进程控制(启动、结束、列出等)
- 硬件控制(音量、亮度、电源、网络等)
- 鼠标键盘操作
- 截图、OCR识别
- 串口通信、IoT控制
2. **V1模式(文本命令)**:用于以下类型的子任务:
- 文件操作(创建、删除、复制、移动等)
- 脚本执行、程序运行
- 数据库操作
- 系统配置修改
- 网络服务操作
- 复杂数据处理
**任务分析流程:**
1. 分析用户输入的任务
2. 识别任务中的多个步骤
3. 为每个步骤选择最佳模式(V1或V2)
4. 生成执行序列
**输出格式:**
- 为每个步骤指定模式:[V1] 或 [V2]
- 提供执行命令或工具调用
- 按顺序排列
**示例:**
用户输入:"帮我截屏,OCR识别文字,保存到文件,然后调整音量"
分析结果:
[V2] 截图:使用screenshot工具
[V2] OCR识别:使用visual_ocr工具,参数为截图文件
[V1] 保存到文件:使用文本命令复制OCR结果到文件
[V2] 调整音量:使用volume_set工具,参数level=50
"""
def split_mixed_task(task: str) -> list[tuple[str, str]]:
"""
智能拆分混合任务为多个子步骤
返回格式:[(模式, 命令/工具), ...]
"""
# 简单基于标点符号拆分
import re
# 使用中文或英文标点作为分隔符
separators = r'[,,。;;、]'
steps = [step.strip() for step in re.split(separators, task) if step.strip()]
result = []
for step in steps:
task_type = analyze_task_type(step)
if task_type == "V2":
# 尝试将自然语言转换为工具调用
tool_name = translate_to_tool(step)
if tool_name:
result.append(("V2", tool_name))
else:
# 回退到V1模式
result.append(("V1", step))
elif task_type == "V1":
result.append(("V1", step))
else:
# 默认使用V1
result.append(("V1", step))
return result
def translate_to_tool(step: str) -> str | None:
"""将自然语言转换为工具名"""
step_lower = step.lower()
# 简单的关键词匹配
if "窗口" in step_lower or "window" in step_lower:
if "列表" in step_lower or "list" in step_lower:
return "window_list"
elif "激活" in step_lower or "activate" in step_lower:
return "window_activate"
elif "关闭" in step_lower or "close" in step_lower:
return "window_close"
elif "进程" in step_lower or "process" in step_lower:
if "列表" in step_lower or "list" in step_lower:
return "process_list"
elif "结束" in step_lower or "kill" in step_lower:
return "process_kill"
elif "音量" in step_lower or "volume" in step_lower:
if "设置" in step_lower or "set" in step_lower:
return "volume_set"
elif "获取" in step_lower or "get" in step_lower:
return "volume_get"
elif "截图" in step_lower or "screenshot" in step_lower:
return "screenshot"
elif "ocr" in step_lower or "识别" in step_lower:
return "visual_ocr"
elif "鼠标" in step_lower or "mouse" in step_lower:
if "移动" in step_lower or "move" in step_lower:
return "mouse_move"
elif "点击" in step_lower or "click" in step_lower:
return "mouse_click"
elif "键盘" in step_lower or "keyboard" in step_lower:
if "输入" in step_lower or "type" in step_lower:
return "keyboard_type"
return None
def execute_mixed_task(task: str) -> list[tuple[str, ExecutionResult]]:
"""
执行混合模式任务
智能分析任务,自动选择V1或V2模式执行每个子步骤
返回: [(模式, 执行结果), ...]
"""
print("🤖 分析混合模式任务...")
steps = split_mixed_task(task)
if not steps:
return [("error", ExecutionResult(False, "❌ 无法解析任务", ""))]
results = []
print(f"📋 任务分解为 {len(steps)} 个步骤:")
for i, (mode, command) in enumerate(steps, 1):
print(f" {i}. [{mode}] {command}")
for i, (mode, command) in enumerate(steps, 1):
print(f"\n🚀 执行步骤 {i}/{len(steps)}: [{mode}] {command}")
if mode == "V1":
result = execute_raw_command(command)
elif mode == "V2":
# 解析工具调用参数
tool_parts = command.split()
tool_name = tool_parts[0] if tool_parts else ""
params = {}
# 简单参数解析(实际应用中应该更智能)
for j in range(1, len(tool_parts), 2):
if j + 1 < len(tool_parts):
key = tool_parts[j]
value = tool_parts[j + 1]
if key.startswith("--"):
params[key] = value
else:
params[f"--{key}"] = value
result = execute_tool(tool_name, params)
else:
result = ExecutionResult(False, f"❌ 未知模式: {mode}", command)
results.append((mode, result))
print(f" 结果: {result}")
return results
def run_mixed_mode():
"""Mixed 模式交互循环"""
print("""
╔══════════════════════════════════════╗
║ Mixed 模式 --- 智能混合执行 ║
║ 自动分析任务,智能选择V1/V2模式 ║
║ 支持复杂多步骤任务自由切换组合 ║
║ 输入 quit 或 exit 退出 ║
║ 输入 help 查看使用示例 ║
╚══════════════════════════════════════╝
""")
while True:
try:
user_input = input("\n🫵 输入任务: ").strip()
except (EOFError, KeyboardInterrupt):
print("\n👋 退出 Mixed 模式")
break
if not user_input:
continue
if user_input.lower() in ("quit", "exit", "q"):
print("👋 退出 Mixed 模式")
break
if user_input.lower() == "help":
print(MIXED_MODE_PROMPT)
continue
# 执行混合模式任务
results = execute_mixed_task(user_input)
# 显示总结
print(f"\n📊 任务完成总结:")
success_count = sum(1 for mode, result in results if result.success)
print(f" ✅ 成功: {success_count}/{len(results)}")
if success_count < len(results):
print(f" ❌ 失败: {len(results) - success_count}")
for i, (mode, result) in enumerate(results, 1):
if not result.success:
print(f" 步骤{i}[{mode}]: {result.output}")
# ═══════════════════════════════════════════
# Text 模式支持(V1:任意命令执行)
# ═══════════════════════════════════════════
def execute_raw_command(command: str) -> ExecutionResult:
"""
执行任意系统命令(text/V1 模式)
参数:
command: 完整的shell命令字符串
"""
command_str = command.strip()
if not command_str:
return ExecutionResult(False, "❌ 命令为空", "")
try:
# 执行任意命令
result = subprocess.run(
command_str,
shell=True,
capture_output=True,
text=True,
timeout=30,
encoding="utf-8",
errors="replace",
)
output = result.stdout or result.stderr or "(无输出)"
success = result.returncode == 0
return ExecutionResult(success, output, command_str)
except subprocess.TimeoutExpired:
return ExecutionResult(False, f"⏰ 执行超时 (30s): {command_str}", command_str)
except Exception as e:
return ExecutionResult(False, f"💥 执行异常: {e}", command_str)
def run_text_mode():
"""Text 模式交互循环"""
print("""
╔══════════════════════════════════════╗
║ Text 模式 --- 任意命令执行 ║
║ 可执行任何系统命令(包括文件修改) ║
║ **警告:操作不可逆,风险自担** ║
║ 输入 quit 或 exit 退出 ║
╚══════════════════════════════════════╝
""")
while True:
try:
user_input = input("\n🫵 输入命令: ").strip()
except (EOFError, KeyboardInterrupt):
print("\n👋 退出 Text 模式")
break
if not user_input:
continue
if user_input.lower() in ("quit", "exit", "q"):
print("👋 退出 Text 模式")
break
# 检查危险命令
dangerous_keywords = ["rm -rf", "del /s /q", "format", "shutdown", "taskkill /f"]
is_dangerous = any(keyword in user_input.lower() for keyword in dangerous_keywords)
if is_dangerous:
confirm = input(f"⚠️ **危险命令**:{user_input}\n 确认执行?(y/N): ")
if confirm.lower() != "y":
print(" ❌ 已取消")
continue
result = execute_raw_command(user_input)
print(f"\n {result}")
# ═══════════════════════════════════════════
# 主入口
# ═══════════════════════════════════════════
def parse_args() -> tuple[ModeType | None, bool, list[str]]:
"""
解析命令行参数
返回:
(force_mode, is_interactive, remaining_args)
force_mode: 强制模式(来自命令行参数)
is_interactive: 是否交互模式
remaining_args: 剩余参数(命令或工具名+参数)
"""
args = sys.argv[1:]
force_mode: ModeType | None = None
is_interactive = False
# 解析模式参数
i = 0
while i < len(args):
arg = args[i]
if arg == "--text":
force_mode = "force_text"
args.pop(i)
elif arg == "--function":
force_mode = "force_function"
args.pop(i)
elif arg == "--auto":
force_mode = "auto"
args.pop(i)
elif arg == "--mixed":
force_mode = "mixed"
args.pop(i)
elif arg == "--interactive":
is_interactive = True
args.pop(i)
else:
i += 1
return force_mode, is_interactive, args
def main():
"""主入口:支持智能模式选择和多种运行方式"""
print(f"📋 配置模式: {CONFIG.mode}")
print(f"📋 路径检查: {CONFIG.skill_path}")
# 解析命令行参数
force_mode, is_interactive, remaining_args = parse_args()
if force_mode:
print(f"📋 命令行强制模式: {force_mode}")
# 确定实际模式
actual_mode = determine_actual_mode(force_mode)
if is_interactive:
# 交互模式
if actual_mode == "text":
run_text_mode()
elif actual_mode == "mixed":
run_mixed_mode()
else:
interactive_mode()
return
if not remaining_args:
# 没有参数,默认进入交互模式
print("ℹ️ 没有提供命令,进入交互模式")
if actual_mode == "text":
run_text_mode()
elif actual_mode == "mixed":
run_mixed_mode()
else:
interactive_mode()
return
if actual_mode == "text":
# Text 模式:直接执行命令
command = " ".join(remaining_args)
result = execute_raw_command(command)
print(result)
return
elif actual_mode == "mixed":
# Mixed 模式:智能分析任务
task = " ".join(remaining_args)
results = execute_mixed_task(task)
# 显示总结
print(f"\n📊 任务完成总结:")
success_count = sum(1 for mode, result in results if result.success)
print(f" ✅ 成功: {success_count}/{len(results)}")
if success_count < len(results):
print(f" ❌ 失败: {len(results) - success_count}")
for i, (mode, result) in enumerate(results, 1):
if not result.success:
print(f" 步骤{i}[{mode}]: {result.output}")
return
# Function 模式:工具调用
tool_name = remaining_args[0]
params = {}
i = 1
while i < len(remaining_args):
key = remaining_args[i]
if key.startswith("--"):
if i + 1 < len(remaining_args) and not remaining_args[i + 1].startswith("--"):
params[key] = remaining_args[i + 1]
i += 2
else:
params[key] = True
i += 1
else:
i += 1
if is_dangerous(tool_name):
confirm = input(f"⚠️ 危险操作 [{tool_name}],确认执行?(y/N): ")
if confirm.lower() != "y":
print("❌ 已取消")
return
result = execute_tool(tool_name, params)
print(result)
if __name__ == "__main__":
main()