很多 Agent 工具默认以交互会话的方式运行——使用者需要等它一步步提问、确认、输出,像实时对话。但真实场景里,往往只想发一条命令,让它自己后台跑完、把结果落到文件里,甚至配合定时任务每天自动跑一套工作流。本教程讲解如何在 不进入交互会话 的前提下,让 DeepSeek Harness(DSH)、Codex、Claude Code 后台自主执行任务,核心机制是 no-tty(无终端)与 headless(无头)模式,并以 ZCode 为延伸案例剖析 harness 内建的后台执行体系。全文配套可运行的示例项目 agent-dispatcher,把三种方式串成一条完整的下发与编排链路。
前置教程
如想快速开始学习本教程,你可能需要先完成以下前置教程:
- DeepSeek Harness 安装与使用教程,了解 DSH 作为 Agent 宿主的调度机制,本教程的后台任务以其 background job 能力为基础。
- Python+FastAPI在Windows环境下创建一个基础后端服务教程,本教程示例项目基于 FastAPI 构建后台服务与接口。
- Codex 安装与 DeepSeek v4 Flash 接入教程,了解 Codex CLI 的调用方式,本教程的 no-tty 命令以其为基础。
- Claude Code 安装及配置国内大模型完整教程,了解 Claude Code 的命令行入口,用于对比 headless 模式。
- Workflow 概念详解,理解 Workflow 的多步骤编排机制,后台定时循环工作流正是 Workflow 的典型应用;Skill 概念详解 说明 Skill 定义行为规范、Workflow 定义执行路径,二者互补。
资源下载
- agent-dispatcher 示例项目源码下载 — 包含 FastAPI 后端、工作流引擎、引擎适配层与前端页面的完整源码,解压后进入项目目录即可运行。
1. 为什么需要"后台自主执行"
交互式会话适合学习和调试,但它有两个天然限制:
- 必须有人守着:每一步都需要人工确认,无法无人值守。
- 无法编排:难以塞进定时任务、CI/CD 或自动化流水线。
而很多真实需求恰恰相反——只想"甩一条指令,让它自己干完"。例如:
- 每天定时让 Agent 审查项目代码并提交修复
- 后台批量处理一批文件或生成报告
- 在服务器、容器、脚本里触发一次性的编码任务
这类需求的共同点:只管下发任务,不关心也不想要交互过程。实现它们的关键,是让 Agent 工具进入 no-tty(无终端输入)或 headless(无界面)模式——一旦检测到没有交互式终端,它们会自动从"对话模式"切换到"任务模式":跑完即止、结果存留、可被后台调度。
1.1 延伸概念:CI/CD 与自动化流水线
前面限制里提到的 CI/CD 是软件工程里的一对标准概念,这里在首次出现处把它解释清楚。
CI(Continuous Integration,持续集成):开发者频繁把代码合并到主干,每次合并自动触发一套检查流程——自动构建、自动跑测试,尽早发现冲突和 bug。CI 只负责到"测试通过"为止,不会自动上线。
CD(Continuous Deployment / Continuous Delivery,持续部署 / 持续交付):把"发布上线"也自动化。两者名字相近,区别在最后一步是否需要人工确认:
| 类型 | 是否自动上线 | 人的角色 |
|---|---|---|
| 持续交付(Delivery) | 不自动,准备好发布包,人工点确认 | 人工把关最后一步 |
| 持续部署(Deployment) | 全自动,测试通过就自动发 | 人只管维护规则 |
与本教程的关系:后文介绍的 no-tty 命令正是 CI/CD 流水线的执行单元——CI 在每次代码提交后触发一次 codex -p 审查,或在发布前自动跑一遍检查;而 7.2 节讲的"无人值守与人工确认的平衡",对应的就是持续交付(自动化到最后一步、人工放行发布)与持续部署(全自动)的分界。
2. 核心概念:no-tty 与 headless
no-tty 和 headless 是两回事,但目标一致——都为了让任务脱离交互。
| 概念 | 含义 | 触发条件 | 典型场景 |
|---|---|---|---|
| no-tty(无终端) | 程序检测不到交互式终端输入 | 被重定向、管道、后台进程调用时 | 脚本、cron、CI 流水线 |
| headless(无头) | 程序在无人值守、无图形界面的模式下运行 | 显式参数开启或环境无界面时 | 服务器、容器、自动化测试 |
no-tty 强调的是"没有交互输入设备",headless 强调的是"没有图形界面"。对编码 Agent 而言,前者决定它"进不进对话循环",后者决定它"看不看得见页面"。
2.1 任务模式与交互模式的区别
Agent 工具通常区分两种运行状态:
当命令被放入后台、重定向到文件、或没有终端时,工具检测到"无交互终端",就会走下面的任务模式——这是无人值守调度的前提。
3. Codex:最纯粹的"命令即任务"
Codex 的 CLI 从设计上就支持脚本化、非交互运行,是三者中最适合"一条命令后台执行"的。
3.1 --print 模式:不进会话直接出结果
-p(或 --print)是 Codex 的招牌非交互模式:给一条指令,直接打印结果,完全不进入交互会话,非常适合管道和脚本。
# 直接输出结果,不进交互界面
codex -p "用 try/except 包裹 src/main.py 里所有文件读取调用"
# 配合重定向,把结果存留
codex -p "给本项目加一份 README,说明安装步骤" > README.md 2>&1
3.2 后台执行:命令立刻返回
用操作系统的后台能力,让 Codex 在后台跑,命令本身立刻返回:
# Windows PowerShell
Start-Process codex -ArgumentList "-p","重构 src/ 的异常处理" -WindowStyle Hidden
# Linux / macOS(bash)
codex -p "重构 src/ 的异常处理" > result.md 2>&1 &
3.3 无 TTY 自动适配
没有交互式终端时,Codex 自动走非交互逻辑:
# 强制无 stdin,确保非交互
codex "给 config.py 补全注释" < /dev/null > out.md 2>&1
4. Claude Code:headless + 结构化输出
Claude Code 的 CLI 同样区分交互会话与一次性任务。当它检测到没有交互式 TTY 时,会自动走任务模式——跑完即止。
4.1 任务模式:跑完即止
在脚本或非交互环境下直接给指令:
# 没有交互式 TTY 时,跑完任务即退出
claude "审查 src/ 的代码质量并给出修复建议"
4.2 headless 模式:无人值守
配合后台重定向,让 Claude Code 在无界面下运行并把日志存留:
# 后台运行,日志存留,命令立即返回
claude "批量重构 utils/ 目录" > log.txt 2>&1 &
4.3 结构化输出:便于脚本解析
用 --output-format 指定输出为 JSON,方便后续脚本处理:
# 以 JSON 输出结果
claude "检查 src/ 是否有未捕获的异常" --output-format json > report.json
5. DeepSeek Harness:调度层 + 后台代理
DSH 的机制与前面两个工具不同:它是 Agent 宿主 / 调度层,本身不直接跑代码任务,而是通过调用底层引擎(Codex、Claude Code 等)来完成。因此"后台执行"在 DSH 里分两层理解。
5.1 DSH 的后台代理(background job)
DSH 可以直接创建后台子代理执行任务,无需在会话中守候,这是调度 node main.js 等脚本类任务时的常见用法。后台任务完成后,DSH 会主动通知结果。
# 在 DSH 会话中触发一个后台任务
(由 DSH 的 subagent / background job 机制承接,跑完即止)
5.2 DSH 调度底层引擎
DSH 作为调度层,可以把"一条命令后台执行"的活儿交给底层引擎。结合第 3、4 节的 --print / headless 模式,就能实现"DSH 发令、底层引擎后台跑"的组合。
关键理解:Codex 和 Claude Code 是"引擎",负责真正执行任务;DSH 是"调度层",负责编排和承接。"后台默认执行"这一需求,在 DSH 里由它的后台代理能力 + 底层引擎的 no-tty / headless 模式协同实现。
6. 实战示例:agent-dispatcher 任务下发与工作流编排
前面讲了三种工具各自的后台执行方式,本节把它们串成一个可运行的示例项目 agent-dispatcher:前端页面提交任务后,后端立即返回任务编号并在后台线程自主执行;执行过程按 Workflow 方式编排为四个节点(解析任务、引擎执行、质量校验、生成报告);引擎层以 no-tty 方式调用 Codex / Claude Code CLI,未安装时回退到模拟引擎。整体链路如下:
6.1 项目结构
示例项目 agent-dispatcher 直接放在本教程目录下,通过资源下载区获取后解压即可使用:
agent-dispatcher/
├── main.py # FastAPI 入口:提交任务、查询状态、托管页面
├── workflow.py # 工作流引擎:四节点线性流水线
├── engines.py # 引擎适配层:no-tty 调用 codex -p 与 claude
├── config.py # 配置:引擎顺序、超时、端口
├── requirements.txt # 依赖清单
└── index.html # 前端界面
6.2 工作流引擎:四节点线性流水线
workflow.py 定义了四个节点的执行顺序——这是整个编排的"剧本":
# 工作流定义:节点按列表顺序执行,构成一条线性流水线
WORKFLOW_STEPS = [
{"id": "parse", "name": "解析任务", "desc": "确认引擎与输入,生成执行计划"},
{"id": "execute", "name": "引擎执行", "desc": "以 no-tty 方式调用底层 CLI 处理任务"},
{"id": "validate", "name": "质量校验", "desc": "检查产出是否有效"},
{"id": "report", "name": "生成报告", "desc": "汇总各节点结果与耗时"},
]
每个节点执行时记录状态(running / success / failed)与耗时。流水线的推进逻辑包含一个关键控制流——失败即截断,任一节点失败后剩余节点标记为 skipped:
pipeline = [parse, execute, validate, report]
for index, fn in enumerate(pipeline):
self._run_step(index, fn)
if self.status == "failed":
# 失败截断:剩余节点全部标记 skipped
for rest in self.steps[index + 1:]:
rest["status"] = "skipped"
return
self.status = "success"
self.result = self.steps[-1]["output"]
这正是 Workflow 的典型形态:步骤固定、顺序明确、有分支控制流。完整代码请查看 agent-dispatcher/workflow.py。
6.3 引擎适配层:以 no-tty 方式调用底层 CLI
engines.py 负责把任务真正交给底层引擎,核心是 subprocess.run 的单次调用——被调进程没有交互终端,CLI 自动进入任务模式,stdout 即产出:
def run_engine(engine: str, prompt: str) -> str:
argv = ENGINE_COMMANDS[engine] + [prompt] # codex -> ["codex", "-p", prompt]
result = subprocess.run(
argv,
capture_output=True,
text=True,
timeout=ENGINE_TIMEOUT,
)
if result.returncode != 0 or not result.stdout.strip():
raise RuntimeError(f"{engine} 调用失败或无输出")
return result.stdout.strip()
ENGINE_COMMANDS 来自 config.py,对应第 3、4 节的非交互命令前缀:codex 为 codex -p,claude 为 claude。本机两者都未安装时自动回退到 mock 引擎,保证示例在任何环境都能跑通完整工作流。完整代码请查看 agent-dispatcher/engines.py。
6.4 后端接口:下发即返回,后台自主执行
main.py 提供提交、列表、详情三个接口,并托管前端页面。关键在 create_task:任务对象创建后交给后台线程,接口立即返回 task_id,不阻塞等待结果——这就是"下发"与"执行"的分离:
@app.post("/api/tasks")
def create_task(req: TaskRequest):
wf = TaskWorkflow(req.prompt, req.engine)
TASKS[wf.task_id] = wf
# 下发到后台线程后立即返回,接口不阻塞(自主执行的关键)
threading.Thread(target=wf.run, daemon=True).start()
return {"task_id": wf.task_id, "engine": wf.engine}
前端随后轮询详情接口,即可看到四个节点从 pending 到 success 的实时推进。完整代码请查看 agent-dispatcher/main.py。
6.5 前端界面
index.html 是单文件页面,功能分两块:顶部表单提交任务(任务描述 + 目标引擎选择),下方任务队列每 2 秒轮询刷新,展示每个任务的总体状态徽标、四节点进度条(含各节点耗时)以及最终产出或错误信息。
样式遵循 frontend-style 规范:主题色 #16baaa 及衍生色全部收敛为 CSS 变量;组件采用 BEM 命名(.card__title、.badge--success、.step--running 等);间距遵循 8px 网格系统;按钮与输入框均带 hover / focus / active 交互态和过渡动画;卡片标题使用主题色装饰线;表单校验色采用成功绿、警告黄、错误红三态;并针对移动端做了响应式断点。完整代码请查看 agent-dispatcher/index.html。
6.6 运行与验证
conda create -n agent-dispatcher python=3.10
conda activate agent-dispatcher
uv pip install -r requirements.txt
uvicorn main:app --host 127.0.0.1 --port 8888
浏览器打开 http://127.0.0.1:8888,输入任意任务描述并下发:
- 选择「模拟引擎」:无需安装任何 CLI,几秒内可见四节点依次点亮,产出为模拟输出;
- 本机已安装 Codex 或 Claude Code:选择对应引擎(或保持自动探测),「引擎执行」节点会真实调用 CLI 的非交互模式完成处理。
观察两个要点:其一,提交后接口立刻返回,页面不卡顿(下发不阻塞);其二,「解析任务」节点先行点亮、其余节点依次推进直至报告产出——这正是"后台自主执行 + 工作流编排"的直观呈现。
7. 进阶:定时循环工作流
把上面的"一条命令后台执行"放进定时任务,就能实现无人值守的每日循环工作流——这也正是"个人 AI 工作流"的核心形态。
7.1 定时触发:让 Agent 每天自动干活
Linux / macOS 用 crontab,Windows 用"任务计划程序",都能做到定时触发一个后台 Agent 任务:
# crontab 示例:每天凌晨 2 点让 Codex 审查代码
0 2 * * * codex -p "审查 src/ 本周改动,报告潜在问题" >> /path/to/log.txt 2>&1
7.2 无人值守与人工确认的平衡
完全无人值守适合低风险任务(审查、整理、生成草稿);涉及写入或发布的高风险操作,建议停在确认页,人工过一遍再放行。
| 任务类型 | 推荐模式 | 说明 |
|---|---|---|
| 代码审查、日志分析 | 全自动后台跑 | 只读操作,结果存留即可 |
| 生成文档、报告草稿 | 后台生成 + 通知 | 产出文件,人工审阅 |
| 提交代码、发布内容 | 后台执行 + 人工确认 | 停在确认页,人工放行 |
8. 延伸:ZCode 如何在 harness 内实现后台自主执行
前面几节分别讲了引擎(Codex、Claude Code)与调度层(DSH)的做法。本节以 ZCode 为例做一次机制剖析,它的特别之处在于:教程中分散在三处的机制——引擎的 no-tty 入口、调度层的后台代理、操作系统层的定时任务——在 ZCode 中被全部收进自身的 harness(宿主框架)内部,并做了一个关键下沉:后台化的不是 Agent 进程本身,而是会话内的"子任务",由事件通知驱动模型继续工作。整体链路如下:
8.1 入口层:--prompt 一次性调用与 no-tty 判定
与 codex -p、Claude Code headless 是同一设计思路:ZCode CLI(随桌面端内置的 zcode.cjs)在检测到无交互终端或收到 --prompt 参数时进入任务模式——跑完即止、结构化输出。
# 一次性调用:不进交互界面,直接输出 JSON 结果
zcode --prompt "审查 src/ 的代码质量" --json --no-color
# 无界面续接已有会话,并指定工作目录
zcode --prompt "继续修复剩余告警" --resume <会话ID> --cwd <项目目录>
--prompt对应 3.1 节 Codex 的-p:一条指令直接出结果,不进会话--json对应 4.3 节 Claude Code 的--output-format json:脚本从 JSON 的response字段取结果--resume是它多出的能力:会话历史持久化在本地,无界面下也能续接既有上下文
a4phone 开源项目的"远程续聊"功能就是这条链路的生产级用法——手机端发消息,服务器后台以 headless 方式续聊指定会话,再把回复推回手机。
8.2 授权层:--mode yolo 预授权
headless 模式下没有人可以点确认按钮,权限必须事前解决,这是"自主"的前提:
# 预授权模式下无人值守执行
zcode --prompt "整理 logs/ 并生成日报" --mode yolo --json
--mode yolo 表示工具调用跳过逐步人工审批直接执行;桌面端交互会话中,对应的机制是用户选定的权限模式与自动维护的预授权记录。7.2 节"无人值守与人工确认的平衡"在 ZCode 中的落地方式就是权限模式的选择,而不是执行中途弹窗。
8.3 会话内后台:子任务 detach 与事件唤醒
这是 ZCode 与 3.2 节 & 方案差异最大的一层。3.2 节的后台是把整个 Agent 进程扔给操作系统,输出要手动重定向,跑完没有任何通知;ZCode 把"后台"做进了 harness:
- 后台运行选项:执行命令或派生子代理时可标记为后台任务,进程被 detach 成独立进程,立即返回输出文件路径与任务 ID,当前对话不被阻塞
- 跨回合存活:后台任务不随当前回合结束而终止,模型可以先结束回合等待结果
- 完成时反向唤醒:任务退出时 harness 生成一条通知注入对话流,把模型重新唤起继续处理结果——push 推送模型,无需任何人轮询
- 控制通道:等待接口可阻塞查询结果(有超时上限),也可按任务 ID 随时取消
| 对比项 | 3.2 节 & 方案 |
ZCode harness 后台 |
|---|---|---|
| 后台化对象 | 整个 Agent 进程 | 会话内的子任务 |
| 输出存留 | 手动重定向到文件 | harness 自动托管到输出文件 |
| 完成感知 | 无通知,需人工查看 | 事件通知自动唤醒模型 |
| 过程控制 | kill 进程 | 任务 ID 级查询与取消 |
关键理解:ZCode 的"后台自主执行"本质是模型会话成为常驻的调度中枢——子任务在 harness 里跑,完成事件把模型再叫醒。
8.4 子代理并行:事件驱动替代轮询
与后台命令同理,子代理也可以后台并行:每个子代理拥有独立上下文,多个同时下发,完成时逐个通知父代理。对照第 6 节的 agent-dispatcher:示例项目用后台线程实现"下发即返回",前端每 2 秒轮询任务表,是 pull 拉取模型;ZCode 用"后台子代理 + 完成通知"把轮询省掉,是 push 推送模型。工作流编排也一样——agent-dispatcher 靠写死的四节点流水线推进,ZCode 则由模型按任务清单自主多回合推进。
8.5 调度层:harness 内建定时器
7.1 节用 crontab 定时触发,ZCode 把这一层也收进了 harness:内建持久化定时任务,支持 cron 表达式与"N 分钟后"两种表达,任务定义存放在工作区、跨应用重启存活,到点自动拉起新会话执行一段自然语言任务描述。与 crontab 的本质差别:crontab 调度的是一条 shell 命令,ZCode 调度的是一个带完整上下文的 Agent 任务。
8.6 机制对照表
| 教程机制 | 工具 | ZCode 对应实现 |
|---|---|---|
codex -p 一次性出结果 |
Codex | --prompt + --json |
| 无 TTY 自动切任务模式 | Codex / Claude Code | CLI 启动时 TTY 判定 |
| headless 结构化输出 | Claude Code | --json 输出 |
| 续接会话 | 教程未涉及 | --resume + 本地会话记录 |
| 后台代理 | DSH | 子任务后台 detach + 完成事件唤醒 |
& 扔后台 + 手动重定向 |
操作系统 | harness 托管输出与通知 |
| 前端轮询任务表 | agent-dispatcher | 事件通知 push,免轮询 |
| crontab 定时触发 | 操作系统 | 内建持久化定时器拉起新会话 |
| 无人值守的权限问题 | 教程未展开 | --mode yolo / 权限模式预授权 |
一句话总结:教程里的"后台"指 Agent 进程脱离终端;ZCode 把"后台"下沉为 harness 的事件系统——入口靠 no-tty 判定进入任务模式,执行靠子任务 detach 与完成事件唤醒,调度靠内建定时器拉起新会话,自主性靠权限模式事前授权,四者闭环。
9. 总结
9.1 核心内容回顾
- no-tty 与 headless:是让 Agent 脱离交互、后台执行的两个关键机制。
no-tty决定"进不进对话循环",headless决定"看不看得见界面"。 - 任务模式 vs 交互模式:Agent 工具检测到无交互终端时,会自动从"逐步确认"切到"跑完即止"的任务模式,这是无人值守调度的前提。
- 四者对比:DSH 是调度层,通过后台代理 + 调度底层引擎实现后台执行;Codex 的
--print模式最纯粹,适合单条命令直出结果;Claude Code 支持 headless + 结构化 JSON 输出;ZCode 把三层机制收进 harness 内部,后台子任务完成时由事件通知自动唤醒模型。 - 定时循环:配合 crontab / 任务计划程序,一行命令即可实现每日自动工作的无人值守流程。
- 实战示例:agent-dispatcher 项目演示了任务下发、后台线程自主执行、四节点工作流编排的完整闭环,前端可视化查看各节点进度。
9.2 常见问题与解答
问:后台执行时任务出错了怎么办?
答:把标准错误重定向到日志文件(2>&1 >> log.txt),方便事后排查;高风险任务建议停在确认页而非全自动。
问:no-tty 和 headless 必须同时用吗?
答:不必。编码 Agent 主要看 no-tty(决定是否进对话循环);只有涉及图形界面时才需要 headless。对 Codex、Claude Code 这类命令行工具,no-tty 通常就够了。
问:DSH 本身能像 Codex 那样用 --print 吗?
答:DSH 是调度层,不是直接执行引擎。它通过后台代理和调用底层引擎(Codex、Claude Code)来实现后台执行,因此"一条命令后台跑"最终由底层引擎的 no-tty / headless 模式承接。
问:ZCode 的后台执行与 DSH 的后台代理有何区别?
答:机制同构——都是"调度中枢派活、后台跑完通知"。差别在发起方式与粒度:DSH 的后台代理由调度层面向整个任务发起;ZCode 把后台化做成了对话内的执行选项,模型运行中随时可把一条命令或一个子代理放入后台,完成事件自动注入对话唤醒模型继续处理,无需人工查看输出。
举手提问