很多 Agent 工具默认以交互会话的方式运行——使用者需要等它一步步提问、确认、输出,像实时对话。但真实场景里,往往只想发一条命令,让它自己后台跑完、把结果落到文件里,甚至配合定时任务每天自动跑一套工作流。本教程讲解如何在 不进入交互会话 的前提下,让 DeepSeek Harness(DSH)、Codex、Claude Code 后台自主执行任务,核心机制是 no-tty(无终端)与 headless(无头)模式。全文配套可运行的示例项目 agent-dispatcher,把三种方式串成一条完整的下发与编排链路。

前置教程

如想快速开始学习本教程,你可能需要先完成以下前置教程:

资源下载

1. 为什么需要"后台自主执行"

交互式会话适合学习和调试,但它有两个天然限制:

  • 必须有人守着:每一步都需要人工确认,无法无人值守。
  • 无法编排:难以塞进定时任务、CI/CD 或自动化流水线。

而很多真实需求恰恰相反——只想"甩一条指令,让它自己干完"。例如:

  • 每天定时让 Agent 审查项目代码并提交修复
  • 后台批量处理一批文件或生成报告
  • 在服务器、容器、脚本里触发一次性的编码任务

这类需求的共同点:只管下发任务,不关心也不想要交互过程。实现它们的关键,是让 Agent 工具进入 no-tty(无终端输入)或 headless(无界面)模式——一旦检测到没有交互式终端,它们会自动从"对话模式"切换到"任务模式":跑完即止、结果存留、可被后台调度。

1.1 延伸概念:CI/CD 与自动化流水线

前面限制里提到的 CI/CD 是软件工程里的一对标准概念,这里在首次出现处把它解释清楚。

CI(Continuous Integration,持续集成):开发者频繁把代码合并到主干,每次合并自动触发一套检查流程——自动构建、自动跑测试,尽早发现冲突和 bug。CI 只负责到"测试通过"为止,不会自动上线。

graph LR A[开发者提交代码] --> B[CI 自动触发] B --> C[自动构建] C --> D[自动跑测试] D --> E{测试通过?} E -->|是| F[合并成功] E -->|否| G[报错, 通知作者修复] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ffecd6,stroke:#e67e22,stroke-width:2px style C fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style D fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style E fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style F fill:#fdebd0,stroke:#b7950b,stroke-width:2px style G fill:#fadbd8,stroke:#c0392b,stroke-width:2px

CD(Continuous Deployment / Continuous Delivery,持续部署 / 持续交付):把"发布上线"也自动化。两者名字相近,区别在最后一步是否需要人工确认:

类型 是否自动上线 人的角色
持续交付(Delivery) 不自动,准备好发布包,人工点确认 人工把关最后一步
持续部署(Deployment) 全自动,测试通过就自动发 人只管维护规则
graph LR A[CI 测试通过] --> B[自动打包] B --> C[部署到测试环境] C --> D{发布方式?} D -->|持续交付| E[人工确认后上线] D -->|持续部署| F[全自动直接上线] style A fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style B fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style C fill:#fdebd0,stroke:#b7950b,stroke-width:2px style D fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style E fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style F fill:#ffecd6,stroke:#e67e22,stroke-width:2px

与本教程的关系:后文介绍的 no-tty 命令正是 CI/CD 流水线的执行单元——CI 在每次代码提交后触发一次 codex -p 审查,或在发布前自动跑一遍检查;而 7.2 节讲的"无人值守与人工确认的平衡",对应的就是持续交付(自动化到最后一步、人工放行发布)与持续部署(全自动)的分界。

2. 核心概念:no-tty 与 headless

no-ttyheadless 是两回事,但目标一致——都为了让任务脱离交互。

概念 含义 触发条件 典型场景
no-tty(无终端) 程序检测不到交互式终端输入 被重定向、管道、后台进程调用时 脚本、cron、CI 流水线
headless(无头) 程序在无人值守、无图形界面的模式下运行 显式参数开启或环境无界面时 服务器、容器、自动化测试

no-tty 强调的是"没有交互输入设备",headless 强调的是"没有图形界面"。对编码 Agent 而言,前者决定它"进不进对话循环",后者决定它"看不看得见页面"。

2.1 任务模式与交互模式的区别

Agent 工具通常区分两种运行状态:

graph LR A[一条命令下发] --> B{是否有交互终端?} B -->|是| C[交互会话模式: 逐步提问确认] B -->|否| D[任务模式: 跑完即止, 结果存留] D --> E[后台/定时任务可调度] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style C fill:#ffecd6,stroke:#e67e22,stroke-width:2px style D fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style E fill:#fdebd0,stroke:#b7950b,stroke-width:2px

当命令被放入后台、重定向到文件、或没有终端时,工具检测到"无交互终端",就会走下面的任务模式——这是无人值守调度的前提。

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 发令、底层引擎后台跑"的组合。

graph LR A[DSH 调度层] --> B[后台代理: 内部编排] A --> C[Codex: --print 模式] A --> D[Claude Code: headless 模式] B --> E[结果存留 / 通知] C --> E D --> E style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ffecd6,stroke:#e67e22,stroke-width:2px style C fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style D fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style E fill:#fdebd0,stroke:#b7950b,stroke-width:2px

关键理解:Codex 和 Claude Code 是"引擎",负责真正执行任务;DSH 是"调度层",负责编排和承接。"后台默认执行"这一需求,在 DSH 里由它的后台代理能力 + 底层引擎的 no-tty / headless 模式协同实现。

6. 实战示例:agent-dispatcher 任务下发与工作流编排

前面讲了三种工具各自的后台执行方式,本节把它们串成一个可运行的示例项目 agent-dispatcher:前端页面提交任务后,后端立即返回任务编号并在后台线程自主执行;执行过程按 Workflow 方式编排为四个节点(解析任务、引擎执行、质量校验、生成报告);引擎层以 no-tty 方式调用 Codex / Claude Code CLI,未安装时回退到模拟引擎。整体链路如下:

graph LR A[前端页面 提交任务] --> B[POST 接口 立即返回] B --> C[后台线程 自主执行] C --> D[工作流四节点 顺序编排] D --> E[引擎适配层 codex -p 与 claude] D --> F[结果写入 任务表] F --> G[前端轮询 展示进度与产出] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#fdebd0,stroke:#b7950b,stroke-width:2px style C fill:#ffecd6,stroke:#e67e22,stroke-width:2px style D fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style E fill:#ffecd6,stroke:#e67e22,stroke-width:2px style F fill:#fadbd8,stroke:#c0392b,stroke-width:2px style G fill:#fdebd0,stroke:#b7950b,stroke-width:2px

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
graph LR A[定时任务触发] --> B[后台执行 Agent 命令] B --> C[结果存留到日志/文件] C --> D{需要人工确认?} D -->|否| E[自动完成循环] D -->|是| F[通知人工,人工确认后继续] F --> E style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ffecd6,stroke:#e67e22,stroke-width:2px style C fill:#fdebd0,stroke:#b7950b,stroke-width:2px style D fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style E fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style F fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px

7.2 无人值守与人工确认的平衡

完全无人值守适合低风险任务(审查、整理、生成草稿);涉及写入或发布的高风险操作,建议停在确认页,人工过一遍再放行。

任务类型 推荐模式 说明
代码审查、日志分析 全自动后台跑 只读操作,结果存留即可
生成文档、报告草稿 后台生成 + 通知 产出文件,人工审阅
提交代码、发布内容 后台执行 + 人工确认 停在确认页,人工放行

8. 总结

8.1 核心内容回顾

  • no-tty 与 headless:是让 Agent 脱离交互、后台执行的两个关键机制。no-tty 决定"进不进对话循环",headless 决定"看不看得见界面"。
  • 任务模式 vs 交互模式:Agent 工具检测到无交互终端时,会自动从"逐步确认"切到"跑完即止"的任务模式,这是无人值守调度的前提。
  • 三者对比:DSH 是调度层,通过后台代理 + 调度底层引擎实现后台执行;Codex 的 --print 模式最纯粹,适合单条命令直出结果;Claude Code 支持 headless + 结构化 JSON 输出。
  • 定时循环:配合 crontab / 任务计划程序,一行命令即可实现每日自动工作的无人值守流程。
  • 实战示例:agent-dispatcher 项目演示了任务下发、后台线程自主执行、四节点工作流编排的完整闭环,前端可视化查看各节点进度。

8.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 模式承接。