跳到主要内容

OpenAI Codex 三大开源组件:exec、SDK、app-server 用法全解

· 阅读需 12 分钟

很多人以为"强大的 AI Agent = 好模型 + 好 Prompt"。但实际上,模型之上还叠着一整套底层执行系统(Harness):理解任务、维护对话记忆、调用工具、实时展示进度、处理失败、请求人类审批。这部分决定了 Agent 能不能真正"干活",也决定了把 Agent 嵌进产品时的体验上限。

OpenAI 在开源仓库 openai/codex 里把这套 harness 的三个组件开放了出来,分层恰好对应三种不同的接入方式:

组件形态一句话适合场景
codex execCLI 子命令非交互式跑完一个任务脚本、CI、一次性后台任务
Codex SDKTypeScript / Python 库在应用代码里启动/恢复/流式监听任务CI/CD、内部工具、应用集成
Codex app-server本地 JSON-RPC 服务把 Agent 变成你产品的"一等公民"持久对话、实时事件、打断、自定义工具、审批

下面逐个过用法,代码片段可以直接跑。

说明:Codex 演进很快,本文所有命令均以 openai/codex 当前源码(rust-v0.132+)为准;旧文章里的参数可能已经变了,拿不准时先跑 codex exec --help

一、codex exec:非交互式自动化

安装

npm install -g @openai/codex

装完先登录一次(codex login,或复用已有的 Codex 会话)。

基本用法

# 跑一个一次性任务,在当前目录下
codex exec "Diagnose the CI failure and propose a fix"

# 指定工作目录
codex exec -C /path/to/project "Fix the failing tests"

# 从 stdin 读提示词(管道场景很方便)
echo "Add unit tests for the auth module" | codex exec -

codex exec 还有几个子命令:codex exec resume <SESSION_ID>(恢复上次会话)、codex exec fork <SESSION_ID>(从某次会话分叉)、codex exec review(对当前仓库跑一次 code review)。

关键参数(以当前版本为准)

参数说明
-s, --sandbox <MODE>沙箱策略:read-only / workspace-write / danger-full-access
--approve-for-me(别名 --not-so-yolo把审批交给自动审查(auto-review),同时强制 workspace-write
--dangerously-bypass-approvals-and-sandbox--yolo危险:跳过所有确认与沙箱,仅在外部已有沙箱的环境用
-m, --model <MODEL>指定模型
-C, --cd <DIR>工作根目录
--add-dir <DIR>额外可写目录(配合 workspace-write)
--json把事件以 JSONL 打到 stdout,机器可读
-o, --output-last-message <FILE>把 Agent 的最后一条回复写到文件
--output-schema <FILE>传一个 JSON Schema 文件,约束 Agent 结构化输出
--ephemeral不把会话持久化到磁盘
--skip-git-repo-check允许在非 Git 仓库里运行
-i, --image <FILE>附加图片输入

⚠️ 版本提示:网上不少教程提到的 --approval-policy--max-turns--timeout 在当前源码里已经不存在了——审批和沙箱改由 --sandbox / --approve-for-me 以及 config.toml 里的 approval_policysandbox_mode 控制。老版本迁移过来时注意。

结构化输出:把结果喂给下游程序

# schema.json 例如 {"type":"object","properties":{"summary":{"type":"string"},"status":{"type":"string"}},"required":["summary","status"]}
codex exec "Summarize repository status" --output-schema ./schema.json --json | jq 'select(.type=="turn/completed")'

--json 时,每一行是一个带 "type" 标签的事件(thread/startedturn/starteditem/starteditem/completedturn/completed…),item 类型包括 agentMessagereasoningcommandExecutionfileChangemcpToolCall 等。下游脚本按事件类型过滤即可拿到最终结果与执行痕迹。

典型 CI 场景

  • GitHub Actions:PR 时对 diff 跑一次 codex exec reviewcodex exec "Review the diff",把结论贴成评论;
  • GitLab CI:自动生成文档 / CHANGELOG,再自动提交;
  • Jenkins / CircleCI:用一段可移植的 shell 脚本包一层 codex exec,把结构化结果透传给通知系统。

CI 里建议固定版本(npm install -g @openai/codex@<version>),避免上游行为变化导致流水线不稳定。

二、Codex SDK:把 Codex 嵌进应用

SDK 的目标是"编程式启动、恢复、流式监听 Codex 任务"。TypeScript 版包住 codex CLI(子进程 + stdio 上交换 JSONL);Python 版走本地 app-server(JSON-RPC),并且自带一份配套的 Codex CLI 运行时,不需要单独装 CLI。

2.1 TypeScript(@openai/codex-sdk

npm install @openai/codex-sdk # 需要 Node.js 18+

快速开始:

import { Codex } from "@openai/codex-sdk";

const codex = new Codex();
const thread = codex.startThread();
const turn = await thread.run("Diagnose the test failure and propose a fix");

console.log(turn.finalResponse);
console.log(turn.items);

在同一线程上继续对话(复用 Thread 实例即可):

const nextTurn = await thread.run("Implement the fix");

流式监听(做 UI 进度条、实时渲染工具调用时用):

const { events } = await thread.runStreamed("Diagnose the test failure and propose a fix");

for await (const event of events) {
switch (event.type) {
case "item.completed":
console.log("item", event.item);
break;
case "turn.completed":
console.log("usage", event.usage);
break;
}
}

结构化输出(传 JSON Schema,或从 Zod 生成):

const schema = {
type: "object",
properties: {
summary: { type: "string" },
status: { type: "string", enum: ["ok", "action_required"] },
},
required: ["summary", "status"],
additionalProperties: false,
} as const;

const turn = await thread.run("Summarize repository status", { outputSchema: schema });
console.log(turn.finalResponse);

附加图片(文本 + 本地图片混排):

const turn = await thread.run([
{ type: "text", text: "Describe these screenshots" },
{ type: "local_image", path: "./ui.png" },
]);

恢复历史会话(线程持久化在 ~/.codex/sessions):

const savedThreadId = process.env.CODEX_THREAD_ID!;
const thread = codex.resumeThread(savedThreadId);
await thread.run("Implement the fix");

指定工作目录 / 跳过 Git 检查 / 控制子进程环境:

const thread = codex.startThread({
workingDirectory: "/path/to/project",
skipGitRepoCheck: true,
});

// 完全接管传给 CLI 的环境变量(适合 Electron 等沙箱宿主)
const codex = new Codex({
env: { PATH: "/usr/local/bin" },
});

2.2 Python(openai-codex

pip install openai-codex # 需要 Python >= 3.10,自带 Codex CLI 运行时

快速开始:

from openai_codex import Codex

with Codex() as codex:
thread = codex.thread_start()
result = thread.run("Explain this repository in three bullets.")
print(result.final_response)

thread.run(...) 返回 TurnResult,含最终回复、收集的 items 和 token 用量。

沙箱预设(对线程或单次 turn 指定文件系统访问):

from openai_codex import Codex, Sandbox

with Codex() as codex:
thread = codex.thread_start(sandbox=Sandbox.workspace_write)
thread.run("Make the requested changes.")
review = thread.run("Review the diff only.", sandbox=Sandbox.read_only) # turn 级覆盖

三个预设:Sandbox.read_only(只读)、Sandbox.workspace_write(工作区可写,日常默认)、Sandbox.full_access(不限制)。

继续 / 恢复线程:

with Codex() as codex:
thread = codex.thread_start()
thread.run("Summarize Rust ownership in two bullets.")
result = thread.run("Now explain it to a Python developer.")

# 之后任何时刻按 id 恢复
with Codex() as codex:
thread = codex.thread_resume("thr_123")
print(thread.run("Continue where we left off.").final_response)

异步客户端:

import asyncio

from openai_codex import AsyncCodex, Sandbox

async def main() -> None:
async with AsyncCodex() as codex:
thread = await codex.thread_start(sandbox=Sandbox.workspace_write)
result = await thread.run("Continue where we left off.")
print(result.final_response)

asyncio.run(main())

登录(默认复用已有 Codex 会话,必要时显式登录):

with Codex() as codex:
codex.login_api_key("sk-...") # API key
# 或 ChatGPT 浏览器登录 / 设备码登录:
# codex.login_chatgpt()
# codex.login_chatgpt_device_code()

Python SDK 还内置了完整帮助:help(openai_codex)help(Codex),或 python -m pydoc openai_codex

三、Codex app-server:让 Agent 成为你产品的一部分

前面两个是"调用 Codex 干一次活",app-server 是反过来——你连上一个常驻的 Codex 进程,把 Agent 直接嵌进产品 UI。它本质是一个有状态的本地 JSON-RPC 2.0 服务,负责持久对话、实时事件流、中途打断、把自研工具暴露给 Agent、以及请求人类审批。官方的 VS Code 扩展就是它的一个客户端。

启动与传输层

# 默认 stdio 传输:JSONL over stdin/stdout,适合父进程直接拉起
codex app-server

# WebSocket 传输(实验性,带健康探针 /readyz、/healthz)
codex app-server --listen ws://127.0.0.1:4242

# Unix socket(供本地控制面客户端用)
codex app-server --listen unix://

# 不暴露任何本地传输
codex app-server --listen off

协议和 MCP 类似:JSON-RPC 2.0(线上省略 "jsonrpc":"2.0" 头)。服务端过载时会回 -32001 + "Server overloaded; retry later.",客户端应按指数退避重试。

核心概念:Thread / Turn / Item

  • Thread:用户与 Agent 的一段对话,包含多个 turn;
  • Turn:一轮对话,通常从一条用户消息开始、到一条 Agent 消息结束,包含多个 item;
  • Item:turn 里的输入/输出单元——用户消息、Agent 推理、Agent 消息、shell 命令、文件编辑等,会持久化作为后续上下文。

一次完整对话的生命周期

1. initialize → 连接建立后先发 initialize(带 clientInfo)
2. initialized → 客户端回一个 initialized 通知,完成握手
3. thread/start → 开新会话(或 thread/resume 恢复、thread/fork 分叉)
4. turn/start → 提交用户输入,立即返回 turn 对象
5. (事件流) → 持续收到 item/started、item/completed、item/agentMessage/delta …
6. turn/completed → 收到最终 turn 状态与 token 用量(或用 turn/interrupt 打断)

握手示例(参考官方 VS Code 扩展):

{ "method": "initialize", "id": 0, "params": {
"clientInfo": { "name": "codex_vscode", "title": "Codex VS Code Extension", "version": "0.1.0" }
} }

开一个新会话:

{ "method": "thread/start", "id": 10, "params": {
"model": "gpt-5.1-codex",
"cwd": "/Users/me/project",
"approvalPolicy": "never",
"sandbox": "workspaceWrite"
} }

提交一轮对话(支持 text / image / audio 输入,以及 outputSchema 约束结构化输出):

{ "method": "turn/start", "id": 30, "params": {
"threadId": "thr_123",
"input": [ { "type": "text", "text": "Run tests" } ],
"sandboxPolicy": { "type": "workspaceWrite", "writableRoots": ["/Users/me/project"] }
} }

打断进行中的 turn:

{ "method": "turn/interrupt", "id": 31, "params": {
"threadId": "thr_123",
"turnId": "turn_456"
} }

服务端会为每个 item 发一条 item/started(完整 item)→ 若干 delta → item/completed(最终状态)。UI 就按这个节奏逐条渲染,进度是实时可见的。

Human-in-the-loop:审批流

这是 app-server 最"产品化"的能力。当 Agent 要执行 shell 命令或改文件、且按用户配置需要审批时,服务端会主动向客户端发一个 JSON-RPC 请求,而不是等轮询:

{ "method": "item/commandExecution/requestApproval", "id": 50, "params": {
"itemId": "item_123",
"threadId": "thr_123",
"turnId": "turn_456",
"command": "rm -rf /tmp/cache",
"cwd": "/Users/me/project",
"reason": "Clear build cache"
} }

客户端在 UI 里展示这个命令/文件 diff,由用户决定后回一条:

{ "id": 50, "result": { "decision": "accept" } }

可选的 decision 还有 acceptForSession(本次会话内放行)、acceptWithExecpolicyAmendment(放行并写入规则)、declinecancel。文件变更审批走 item/fileChange/requestApproval,流程一样。这就是把"人在环上"做进产品的方式:命令是否执行、diff 是否落地,都由你 UI 里的用户拍板。

暴露自研工具:dynamicTools(实验性)

把业务工具注册给 Agent,Agent 干活时由客户端执行并回结果——工具逻辑不出你的进程。

thread/start 里声明工具:

{ "method": "thread/start", "id": 10, "params": {
"dynamicTools": [{
"type": "namespace",
"name": "tickets",
"description": "Ticket management tools",
"tools": [{
"type": "function",
"name": "lookup_ticket",
"description": "Fetch a ticket by id",
"inputSchema": {
"type": "object",
"properties": { "id": { "type": "string" } },
"required": ["id"]
}
}]
}]
} }

Agent 调用时,服务端发 item/tool/call 请求,你执行后回 contentItemssuccess

{ "method": "item/tool/call", "id": 60, "params": {
"threadId": "thr_123",
"turnId": "turn_123",
"callId": "call_123",
"namespace": "tickets",
"tool": "lookup_ticket",
"arguments": { "id": "ABC-123" }
} }

{ "id": 60, "result": {
"contentItems": [ { "type": "inputText", "text": "Ticket ABC-123 is open." } ],
"success": true
} }

实验特性需要握手时声明 "capabilities": { "experimentalApi": true }。同理还有 thread/goal/*(给线程挂长期目标)、review/start(自动化代码评审)、command/exec(不建线程直接跑命令)等一批接口。

生成协议 Schema

协议按版本演进,不要手写类型。直接用配套命令从当前二进制导出,保证和版本一致:

codex app-server generate-ts --out DIR # TypeScript 类型
codex app-server generate-json-schema --out DIR # JSON Schema bundle

给接入方的一个提醒

initialize 里的 clientInfo.name 会用于 OpenAI 的合规日志平台。如果你做的是面向企业的集成,需要联系 OpenAI 把你加进已知客户端列表,否则合规日志里就是陌生的 client 名。

四、选型建议:什么时候用哪个

需求选哪个
跑一次脚本/CI 任务,拿到结构化结果codex exec
应用代码里按需启动、恢复、流式监听 CodexCodex SDK
Agent 就是产品的一部分:持久对话、实时进度、可打断、要审批、要接自研工具codex app-server
只是想把 Codex 作为多 Agent 编排里的一个"专家"建议用 Codex CLI 作为 MCP server,交给 Agents SDK 编排,而不是直接操作 app-server

官方文档的一句话总结:exec 面向脚本/CI,SDK 面向应用代码,app-server 面向把 Agent 做进产品。

参考


本文命令与参数均核对自 openai/codex 当前源码;若与你在用版本的 --help 不一致,以你本机版本为准。