「任务下发」是 a4agent 自 0.5.0 起的页签:把各端 CLI 的无头(headless)模式产品化——在界面里写一句任务描述、选一个引擎,a4agent 就在后台把它跑完并把产出留档,你可以继续干别的事。配置来源一律是你在各应用内自己配好的配置:a4agent 不为这些应用写入 API 配置(0.5.1 起只代管 Claude Code 与 Codex 两端,见配置方案与一键切换),改为在下发前做一次真实连通性验证。本文章介绍支持的引擎、下发前的三步预检、队列与产出、关窗常驻机制,以及各端的命令细节。

1. 功能概述

维度 说明
定位 七端无头任务的界面化下发、后台执行与产出留档
设计约束 下发与执行分离(接口不等结果)、不通就不入队(预检前置)
配置来源 各端应用内自己的配置;a4agent 不代写,只做真实连通验证
并发 线程池,默认 5(环境变量 A4AGENT_TASK_CONCURRENCY,钳制 1–8)
产出落盘 %APPDATA%\a4agent\task_outputs\<任务号>.jsonl\|md
引入版本 0.5.0(pi / dsh 双引擎),0.5.1 扩展至六端,0.6.1 增加 OpenCode 共七端
graph LR A[写任务描述
选引擎] --> B[下发前预检
三步] B -->|不通过| C[弹窗提示原因
不入队] B -->|通过| D[建任务行
提交线程池] D --> E[后台执行
无头 CLI] E --> F[产出落盘
状态机更新] F --> G[终态提醒
页面内或系统通知] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style C fill:#fadbd8,stroke:#c0392b,stroke-width:2px style D fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px style E fill:#ffecd6,stroke:#e67e22,stroke-width:2px style F fill:#fdebd0,stroke:#b7950b,stroke-width:2px style G fill:#d5f5e3,stroke:#27ae60,stroke-width:2px

2. 支持的引擎

引擎 无头命令 产出形态
Claude Code claude -p "<任务>" --output-format json JSON 单对象,产出在 result,含 token 用量与成本
Codex codex exec --json --ephemeral JSONL 事件流,取 agent_message 类型的产出
ZCode zcode -p "<任务>" --json JSON 单对象,产出在 text,含 toolCalls / usage / stopReason
Qoder qodercli -p "<任务>" -o json JSON 单对象
dsh dsh --profile headless "<任务>" stdout 直出(能解析为 JSON 时抽正文字段)
pi pi -p "<任务>" --mode json --no-session JSONL 事件流,按 stopReason 定成败、按事件取文本
OpenCode 不走 CLI,对运行中的服务建会话 会话消息流,取 assistant 文本;成本 / 用量 / 成败取自会话终态

引擎自动探测(安装状态 / 版本 / 可执行路径),未安装的在界面置灰并给出原因;0.5.2 起各引擎探测全部可见版本号,鼠标悬停可看不可用时的真实原因。

无头模式下没有人能点审批,因此每条命令都带权限预授权参数,否则会卡在审批或被直接拒绝:Claude Code 用 --permission-mode bypassPermissions、Codex 用 --sandbox danger-full-access、ZCode 用 --mode yolo、Qoder 与 pi 用 --yolo(dsh 不需要,其 headless profile 本身即无头入口)。

2.1 桌面端内置内核(0.5.2 起)

ZCode 与 Qoder 不必单独安装命令行工具——只要桌面端已安装,a4agent 会自动发现并复用其自带的命令行内核:

引擎 内核来源 探测优先级
ZCode 桌面端自带 resources/glm/zcode.cjs(随 IDE 自动更新) 官方 CLI(PATH)→ 桌面端内置内核
Qoder 桌面端自带 agent-sdk worker(与官方 qodercli 同源) 官方 CLI(PATH)→ 桌面端内置内核

内置内核随 IDE 更新,登录态与桌面端一致。ZCode 的无头执行还补上了 --cwd 工作目录参数,下发的「工作目录」对它真正生效。

2.2 命令细节里的两个坑

  • 命令名先解析成绝对路径:这些 CLI 在 Windows 上都是 npm 的 .cmd 壳,subprocess 不经 shell 直接给命令名会 FileNotFoundError;统一用 shutil.which() 解析,解析不到即「未安装」。Qoder 官方 CLI 名写 qodercli、博客写 qoder,两个名字都探测。
  • 探测不能只看退出码:坏壳(路径里的 \b 被转义成退格)会以退出码 0 打印 node 崩溃栈,因此探测会识别 Cannot find module / Error: / node: 等崩溃行并优先取有信息量的那条,直接显示在界面提示里。

3. 下发前自动预检(不通就不入队)

点「下发任务」会先同步做三步检查,任一步失败就不启动引擎,并用人话告诉你为什么:

graph LR A[点击下发任务] --> B[第一步
引擎可用] B -->|否| C[说明未安装
或坏壳原因] B -->|是| D[第二步
配置就绪] D -->|读不到| E[如实说明
以实测为准] D -->|已配好| F[第三步
真实连通] E --> F F -->|拿到产出| G[通过
建任务行并入队] F -->|失败| H[弹窗展示
真实报错原因] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style C fill:#fadbd8,stroke:#c0392b,stroke-width:2px style D fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style E fill:#fdebd0,stroke:#b7950b,stroke-width:2px style F fill:#ffecd6,stroke:#e67e22,stroke-width:2px style G fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style H fill:#fadbd8,stroke:#c0392b,stroke-width:2px
步骤 检查什么 说明
引擎可用 命令是否存在、能否执行、版本号 结果缓存 5 分钟
配置就绪 读你自己在应用内配好的配置,判断是否已配好服务商与模型 Qoder 因模型目录经专有加密读不到,如实说明「无法校验,请以实测结果为准」,不假装通过
真实连通 用最小提示词真实跑一次无头调用,拿到产出才算通过 密钥有效没、模型名拼写对不对、网络通不通,一次调用全都会暴露

第 2、3 步不再比对「配置字段是否等于我们写的值」——那会把「配置得和我不一样」误判成不可用。配置还没配好时不会跑实测(跑必然失败,白白等待)。

0.5.2 起,预检不通过时直接弹窗展示一句话可执行的原因(如「实测未通过:引擎退出码 1:ProviderBusinessError: User not found.」),不再向队列写入一条注定无法执行的失败记录;失败原因会带上 stderr 里的真实说明(此前 Qoder 只显示「退出码 127」,现在能看到「Qoder CLI is not installed. Install: https://qoder.com」)。

下发对话框还会显示「使用模型」一行,随引擎选择实时显示该工具当前配置的默认模型——让「实际会用哪个模型」在下发前就可见,避免 IDE 里切了模型、无头任务却走了另一份配置的「分叉」不知情。

4. 队列与产出

  • 下发即返回:接口只建任务行并交给后台线程池,不等结果;界面 2 秒轮询,全部空闲自动停表。
  • 状态机:排队中 → 执行中 → 已完成 / 失败 / 超时 / 已取消 / 预检未通过,每次迁移落库。
  • 查看产出:弹窗显示引擎产出全文(长产出只回传末尾 200KB,文件里始终完整)与预检各步耗时。
  • 取消与超时:取消用 taskkill /T /F 杀整棵进程树(Node 壳会派生子进程,只杀外壳杀不干净);超时默认 10 分钟(可选 5–60 分钟),到点强杀并标记超时。
  • 删除任务:同步清理产出文件。
graph LR A[排队中] --> B[执行中] B -->|正常结束| C[已完成] B -->|非零退出或解析失败| D[失败] B -->|到超时点| E[超时] B -->|用户取消或应用退出| F[已取消] A --> G[预检未通过] style A fill:#fdebd0,stroke:#b7950b,stroke-width:2px style B fill:#ffecd6,stroke:#e67e22,stroke-width:2px style C fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style D fill:#fadbd8,stroke:#c0392b,stroke-width:2px style E fill:#fadbd8,stroke:#c0392b,stroke-width:2px style F fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style G fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px

5. 关窗不杀任务

  • 关闭主窗口 = 转入后台常驻,任务继续执行;再次启动 a4agent 会自动唤回原实例的窗口而不是新起一个进程(端口写在数据目录 desktop.json)。
  • 真正的退出入口是顶栏「退出」,会先告知还有几个任务在跑并二次确认;退出时如实把运行中任务标记为「应用退出」并终止其进程树。
  • 任务到终态时:窗口开着就在页面内提示;窗口已隐藏则弹原生系统通知并闪烁任务栏图标,详见通知提醒文档。

6. OpenCode 为什么不一样

其它六端都是「每次新起一个 CLI 进程」,OpenCode 则是对接你已经开着的那台服务(本机默认 127.0.0.1:49374,密码自动发现,也可填远程地址):

能力 其它六端 OpenCode
免审批 全局开关(--yolo / bypassPermissions) 会话级权限规则,不碰全局配置,任务结束即失效
成败判定 从退出码反推 服务端直接给 outcome(成功 / 失败 / 被中断)+ 成本 + 用量
取消 杀整棵进程树 对服务端会话发 interrupt

因此 OpenCode 也不受「必须先有一个生效的配置方案」的限制——它的模型与凭据在 OpenCode 里,不走 a4agent 的方案体系。另外实测有一类隐蔽故障:服务端模型清单里标记为可用的模型可能已被上游废弃,真跑才报错,所以预检第三步与正式执行都会在遇到「模型被拒」时换一个可用模型重试。

7. 各端的两条特殊处理

  • dsh 需要屏蔽外部插件:~/.dsh/profiles/headless/cordis.patch.yml 里若被其他工具(如 a4phone)插入了依赖 host 侧服务的外部插件,dsh 会在启动阶段直接失败(退出码 1)。a4agent 下发时生成一个 --patch 覆盖层,只禁用那批相对路径插入的外部插件,不改 profile 本身;覆盖层落在 <数据目录>/task_runtime/dsh-headless-suppress.yml,屏蔽动作写一条 WARNING 便于追溯。dsh 自带插件按包名解析,不在屏蔽范围内。
  • pi 的成败按最后一条 assistant 消息判定:pi 的 --mode json 下退出码可能仍是 0,且它自带重试环(实测 3 次、退避 2s→4s),重试环里每条 assistant 消息都带 errorMessage 且成功后不清空。因此解析以最后一条 assistant 消息的 stopReason 为唯一依据,文本拼接各轮 assistant 文本,用量按 usage 累加;执行器不再叠第二层重试。
  • 子进程的默认工作目录是用户主目录:留空会继承 a4agent 自己的进程目录(开发时是仓库、打包后是安装目录),引擎会在那儿读到不该读的项目配置。pi 的子进程还会显式传 PI_CODING_AGENT_DIR,避免它拿旧配置跑。

8. 常见问题

问:下发时提示「实测未通过」,但我在 IDE 里用得好好的?

答:预检会用最小提示词真实跑一次无头调用。常见差异有三类:无头内核与 IDE 的登录态不共享(Qoder 首次使用需 qodercli login 一次)、无头内核读的是 CLI 配置而不是 IDE 界面里的套餐、模型被上游废弃但清单里仍标为可用(此时会自动换候选模型重试)。按弹窗里的真实报错处理即可,通常比「退出码 1」好定位得多。

问:为什么关了窗口任务还在跑?

答:这是刻意设计——关闭主窗口转入后台常驻,任务照常执行。要真正结束请用顶栏「退出」,它会先告知还有几个任务在跑并二次确认。

问:任务产出会跟着问题反馈一起发走吗?

答:不会。产出是引擎原始输出,可能包含代码与路径等敏感内容;应用内「问题反馈」不会自动附带任务产出,删除任务会同步删除其产出文件。产出落盘位置见数据目录与日志说明。

问:可以把并发调大让任务跑得更快吗?

答:可以设置环境变量 A4AGENT_TASK_CONCURRENCY(默认 5,钳制 1–8)。但瓶颈通常在引擎侧与上游限流,调大并发更容易触发上游 429,建议先按默认跑。

问:ZCode / Qoder 提示未安装,但我明明装了桌面端?

答:0.5.2 起会自动探测桌面端自带的命令行内核。若仍提示未安装,检查桌面端是否安装在标准位置;也可以直接装官方 CLI(zcode / qodercli),探测优先级是「官方 CLI → 桌面端内置内核」。