桌面 Agent 启动应用:先建索引再执行
作者让模型从已安装应用索引中选择名称,避免把模型输出直接拼成 Shell 命令,以降低命令注入及子进程拖住语音队列的风险。实现覆盖 Windows AppID 与快捷方式、macOS 应用目录和 Linux 桌面条目,并限制缓存权限与条目数量。
AI 深度解读
让桌面 AI 打开 Notepad,麻烦并不只在识别应用名称:模型生成的启动字符串可能变成代码,启动后的子进程还可能占住语音队列,让下一次对话卡死。Ankita 作者因此给 open_app 划了一条边界:模型只能从应用索引里选名字,不能自行提供路径。
这套方案已合入 main,计划随 2.7.5 发布。它值得看的地方,是把名称匹配、启动权限和进程生命周期拆开处理。但索引并非实时状态,Windows 启动分支也仍有需要信任的系统环节。
为什么模型只能选名字,不能决定启动路径
作者把 apps-index.json 当作唯一的可启动目标来源。模型负责表达用户想打开哪个应用,扫描器负责确认机器上有哪些应用,启动器执行经过验证的目标。
应用索引承担的是启动授权边界,不只是搜索缓存。
这个区别很实际。如果允许模型返回任意路径,或者把名称直接拼进 shell 字符串,模型输出就可能影响代码执行。即使没有恶意输入,裸命令也可能通过 PATH 找到另一个同名程序,偏离索引原本承诺的目标。
Ankita 在 Windows 上优先使用 PowerShell 的 Get-StartApps,获取显示名称和 AppID,也能覆盖可执行入口不是普通文件路径的 Store 应用。PowerShell 被阻止时,再扫描开始菜单的 .lnk 快捷方式。macOS 遍历 /Applications,Linux 读取 .desktop 文件。
缓存最多保留 600 条,权限设为 0o600,有效期最长 7 天,两次重新扫描至少间隔 30 秒。即使索引仍在有效期内,查不到应用也会自动补扫一次,以覆盖刚安装应用的情况。扫描失败按尽力发现处理,可能少收录应用;缓存加载时仍要校验,不能因为文件已经存在就直接信任。
这里也有一个没有展开的问题:600 条的上限如何决定保留顺序,原文没说清。应用较多时,这会影响哪些目标能够进入启动名单。
匹配可以逐步放宽,执行不能跟着猜
matchApps 按顺序尝试精确名称、唯一前缀、子串,再到多词匹配。最后一种允许处理「photo editor」这类词序不同的请求。
关键约束是:任一步出现多个匹配,就返回歧义,最多列出 5 个候选,让用户选择。它不会让模型替用户押一个答案。
索引本身也必须减少假歧义。同一应用可能同时以开始菜单 AppID、.lnk 快捷方式、Store AppID 出现。作者采用来源优先级,让先出现的来源胜出,以开始菜单 AppID 为标准入口,快捷方式作为后备;同时过滤纯分隔符、== Site == 横幅,以及伪装成快捷方式名称的 URL。
原文把重名去重作为问题来讨论,但没有给出完整判定规则。对于两个确实同名的应用,系统仍只能询问用户。
这里还要区分两种询问:多个已知应用匹配,需要用户消歧;应用根本不在索引中,需要额外批准。已知且唯一的应用直接启动,未知状态按风险处理。但批准以后如何把未知应用纳入可信流程,原文没有说明。
不用通用 shell,为什么 Windows 仍然出现 cmd
「不通过 shell 启动」需要分支理解。
普通文件路径直接创建进程,使用 shell: false。启动前检查路径必须是绝对路径,并且文件仍然存在。.desktop 中的参数只保留普通 token,含 shell 元字符的参数会被丢弃。
Windows AppID 则经过 cmd /c start,交给 Windows 解析。作者为这个例外加了严格格式检查,只接受 Store package ID,或逐字符验证的 {GUID}\program;规则排除 &、|、^、" 和 %VAR% 这类可能参与命令再解析的内容。校验逻辑放在扫描模块,让扫描器和启动器共用同一套 AppID 定义。
因此,更准确的理解是:普通路径不进入 cmd,AppID 经过受限入口调用系统解析器。后者仍然依赖 Windows 的解析行为。
另一半设计解决的是语音卡死。启动进程采用 detached 方式,忽略 stdio,并调用 unref(),同时不登记到助手的 job state。作者此前遇到的故障,正是后台子进程被当成仍在运行的任务,持续占住语音资源。
应用的运行寿命,不应该决定这一轮 AI 对话何时结束。
启动请求被接受后,工具立即返回。它并不等于已经确认应用窗口成功出现。审计日志也只记录应用名和来源,不记录包含操作系统用户名的绝对路径。
我的判断
我认可作者把执行权限收进确定性代码的方向。相比让模型拼接命令,再依赖提示词要求它小心,索引、路径检查和共享校验规则更容易审查,也更容易定位故障。
但原文提供的是设计说明和一次既有故障的修复解释,没有攻击测试结果、误匹配率或方案对照数据。「足够安全」仍是作者的工程判断,不能当作已经验证的结论。
索引也只是快照。应用在扫描后被卸载,会在存在性检查时返回「已不再安装」;7 天缓存仍然可能陈旧。纯文本匹配无法理解应用用途,拒绝猜测能减少误启动,也会增加询问次数。.desktop 参数过滤是否影响合法启动方式,文章没有给出兼容性结果。
这套取舍适合强调执行可控的桌面助手。若产品更看重模糊表达下的一步直达,就需要额外设计发现和确认流程,不能直接取消这些边界。
能拿来做什么
- 给 Node 桌面助手设计启动工具时,可以让模型只提交应用名称或受控标识,由代码从已验证索引中解析目标,避免把模型输出直接当成命令。
- 检查 AI 工具的任务状态管理,把「等待结果的任务」与「发起后长期运行的应用」分开,避免后台应用持续占用语音队列或对话资源。
- 为名称匹配明确规定歧义行为:唯一结果继续执行,多个结果展示候选,未收录目标进入确认流程,并保留各自独立的状态。
- 审查日志时,优先记录应用名称、来源和启动结果;需要排障再单独考虑路径信息,避免绝对路径随共享日志暴露用户名。