For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
主要導覽

本機 Shell

讓智慧體在本機 Shell 中執行指令。

本機 Shell 工具已過時。新的使用案例請改用 shell 工具,並搭配 GPT-5.1。瞭解 更多

本機 Shell 工具可讓智慧體在你或使用者提供的機器上執行 Shell 指令,專為搭配 Codex CLIcodex-mini-latest 而設計。指令會在你自己的執行環境中執行,因此 實際執行哪些指令完全由你掌控。API 只會傳回指令,不會在 OpenAI 基礎架構上執行這些指令。

本機 Shell 可透過 Responses API 搭配 codex-mini-latest 使用,不支援其他模型,也無法透過 Chat Completions API 使用。

執行任意 Shell 指令可能有危險。將指令轉交系統 Shell 前,務必使用沙盒隔離執行環境, 或加入嚴格的允許清單或拒絕清單。


如需參考實作,請參閱 Codex CLI

運作方式

本機 Shell 工具可讓智慧體持續以迴圈方式運作,並存取終端。

模型會傳送 Shell 指令,由你的程式碼在本機上執行,再將輸出傳回模型。透過這個迴圈,模型無需使用者額外介入,就能完成建置、測試與執行的循環。

你的程式碼必須實作一個迴圈,監聽 local_shell_call 輸出項目,並執行其中的指令。我們強烈建議在沙盒中執行,以防止非預期的指令執行。

整合本機 Shell 工具

以下是在應用程式中整合本機 Shell 工具所需遵循的主要步驟:

  1. 向模型傳送請求: 將 local_shell 工具加入可用工具中。

  2. 接收模型的回應: 檢查回應是否包含任何 local_shell_call 項目。 這類工具呼叫包含 exec 等動作,以及要執行的指令。

  3. 執行請求的動作: 在你掌控的本機環境中執行指令。

  4. 傳回動作輸出: 執行動作後,將指令輸出傳回模型。

  5. 重複執行: 將更新後的狀態以 local_shell_call_output 形式放入新的請求並傳送,重複此迴圈,直到模型不再請求執行動作,或你決定停止為止。

工作流程範例

以下是示範請求與回應迴圈的最小範例。選擇程式語言, 即可查看其 SDK 的對應工作流程。為求簡潔,範例省略了正式環境所需的 沙盒隔離與安全性檢查。若未採取額外防護措施, 請勿在正式環境中執行不受信任的指令

import { spawn } from "node:child_process";
import process from "node:process";
import OpenAI from "openai";

const client = new OpenAI();
const MAX_TIMEOUT_MS = 10_000;

function runCommand(command, options) {
  return new Promise((resolve) => {
    let stdout = "";
    let stderr = "";
    let settled = false;
    let groupPoll;
    const child = spawn(command[0], command.slice(1), {
      ...options,
      detached: process.platform !== "win32",
      stdio: ["ignore", "pipe", "pipe"],
    });
    const finish = (suffix = "") => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      clearTimeout(groupPoll);
      resolve(stdout + stderr + suffix);
    };
    const processGroupIsRunning = () => {
      if (process.platform === "win32" || !child.pid) return false;
      try {
        process.kill(-child.pid, 0);
        return true;
      } catch {
        return false;
      }
    };
    const finishAfterProcessGroup = (suffix) => {
      if (settled) return;
      if (processGroupIsRunning()) {
        groupPoll = setTimeout(() => finishAfterProcessGroup(suffix), 10);
      } else {
        finish(suffix);
      }
    };
    const killProcessTree = () => {
      try {
        if (process.platform !== "win32" && child.pid) {
          process.kill(-child.pid, "SIGKILL");
        } else {
          child.kill("SIGKILL");
        }
      } catch {
        child.kill("SIGKILL");
      }
      child.stdout?.destroy();
      child.stderr?.destroy();
    };
    const timer = setTimeout(() => {
      killProcessTree();
      finish("Command timed out.\n");
    }, options.timeout);

    child.stdout?.on("data", (chunk) => {
      stdout += chunk;
    });
    child.stderr?.on("data", (chunk) => {
      stderr += chunk;
    });
    child.on("error", (error) => {
      finish(`Command failed: ${error.message}.\n`);
    });
    child.on("close", (code, signal) => {
      if (signal) {
        finishAfterProcessGroup(`Command failed with signal ${signal}.\n`);
      } else if (code !== 0) {
        finishAfterProcessGroup(`Command failed with exit code ${code}.\n`);
      } else {
        finishAfterProcessGroup("");
      }
    });
  });
}

let response = await client.responses.create({
  model: "codex-mini-latest",
  tools: [{ type: "local_shell" }],
  parallel_tool_calls: false,
  input: "List files in the current directory.",
});

while (true) {
  const shellCall = response.output.find(
    (item) => item.type === "local_shell_call"
  );
  if (!shellCall) break;

  const { command, env, timeout_ms, user, working_directory } =
    shellCall.action;
  let output;
  if (user) {
    output = `Unsupported execution user: ${user}.\n`;
  } else if (command.length === 0) {
    output = "Command is empty.\n";
  } else {
    const timeout =
      timeout_ms && timeout_ms > 0
        ? Math.min(timeout_ms, MAX_TIMEOUT_MS)
        : MAX_TIMEOUT_MS;
    try {
      output = await runCommand(command, {
        cwd: working_directory ?? process.cwd(),
        env: { PATH: process.env.PATH ?? "", ...env },
        timeout,
      });
    } catch (error) {
      output = `Command failed: ${error instanceof Error ? error.message : String(error)}.\n`;
    }
  }

  response = await client.responses.create({
    model: "codex-mini-latest",
    tools: [{ type: "local_shell" }],
    parallel_tool_calls: false,
    previous_response_id: response.id,
    input: [
      {
        type: "local_shell_call_output",
        id: shellCall.call_id,
        output,
      },
    ],
  });
}

console.log(response.output_text);

最佳實務

  • 在沙盒或容器中 執行。可考慮使用 Docker,或 受隔離限制的使用者帳戶。
  • 強制實施資源限制 (時間、記憶體、網路)。模型提供的 timeout_ms 僅供參考,你應強制實施自己設定的限制。
  • 過濾或仔細檢查 高風險指令(例如 rmcurl,以及 網路公用程式)。
  • 記錄每個指令及其輸出 ,以便稽核與偵錯。

錯誤處理

如果指令在你這端執行失敗,例如結束代碼非零或執行逾時,你仍可傳送 local_shell_call_output,並在 output 欄位中包含錯誤訊息。

模型可以選擇從錯誤中復原,或嘗試執行其他指令。如果你傳送格式錯誤的資料(例如缺少 id),API 會傳回標準的 400 驗證錯誤。