「任务下发」是 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 共七端 |
选引擎] --> 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. 下发前自动预检(不通就不入队)
点「下发任务」会先同步做三步检查,任一步失败就不启动引擎,并用人话告诉你为什么:
引擎可用] 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 分钟),到点强杀并标记超时。 - 删除任务:同步清理产出文件。
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 → 桌面端内置内核」。
举手提问