
Codex Agent Harness 是我今年在搞 AI 產品落地時踩過最深的一個坑也是目前覺得最值得講清楚的一套東西。很多人看到 Codex 第一反應是“這不就是個寫代碼的 CLI 工具嘛”但實際上它背后是一整套 Agent 運行時——把模型推理、工具調用、上下文管理、操作審批這些繁瑣的循環全部串起來了。而你要做的不是重新發明這套循環而是在它上面“套殼”把它包裝成自己的 AI 產品換模型接入、注冊自己的工具、按業務流程去編排任務。這篇文章是我從零開始實踐后的完整復盤適合想快速搭建 Agent 產品原型的開發者、后端工程師和技術負責人參考。我在做企業內部 AI 工具選型的時候對比過 LangChain、自研 agent loop、還有直接調 OpenAI API 硬寫最后選定了基于 Codex Agent Harness 來做二次開發。原因很簡單Agent 產品最難的不是“調一次模型”而是把“模型→工具→結果→再交給模型”這個循環跑得穩、跑得可觀測、跑得安全。Harness 把這塊已經做成了工程化的東西我再往上包一層業務邏輯就能在幾周內出一個內部能用的產品而不是花幾個月填框架的坑。1. 為什么選擇“套殼”而不是從零實現1.1 套殼不是貶義詞是工程上的理性選擇在很多技術討論里“套殼”經常被拿來嘲諷別人沒核心技術。但放到工程實踐里站在成熟運行時上做產品恰恰是最理性的路徑。自己做 Agent 運行時至少要走通模型接口調用、流式輸出、工具協議定義、權限確認、上下文裁剪、對話歷史持久化、錯誤恢復……這一套體量比你想象的大得多。Codex 本身經過了大規模真實用戶的使用很多邊界情況已經被處理過了與其從零開始去撞這些坑不如直接站在它的肩膀上。我這里的“套殼”不是指做一個轉發接口的皮包公司式套殼而是指保留 Harness 的 Agent 閉環能力替換和擴展模型層、工具層、編排層、權限層讓它變成一個具備自己產品語義的系統。換句話說上游負責“怎么把 Agent 跑起來”我負責“讓這個 Agent 為我的業務做什么”。用個生活化的類比Harness 就像一輛已經調校好的車底盤發動機、變速箱、轉向系統都齊了。你要做的不是重新造底盤而是給這臺車設計車廂、定外觀、接上自己車隊的調度系統。自己造底盤當然也行但除非你是做底盤生意的否則沒必要在這個層面消耗資源。1.2 從零實現 vs 基于 Harness 的取舍我團隊里曾經有一個方案是自己在 Node.js 里寫一個 agent loop代碼量倒是不大核心循環也就五十行左右但真正跑起來后問題不斷沒有好用的工具沙箱機制Shell 命令一執行就卡死沒有上下文壓縮對話稍微長一點就報 context 超限沒有權限模型模型看一眼文件就能直接執行高風險命令安全上完全不敢放開。這些問題如果自己修沒有兩三個月是穩不下來的。對比下來Codex Harness 自帶的能力包括內置 Shell、文件讀寫等工具支持多種審批模式每次確認、按工具確認、全自動有上下文壓縮與整理機制支持流式輸出提供 TS 類型定義和可編程 API。它可以直接作為 npm 依賴嵌入到自己的 Node.js 服務里也就意味著我可以把它當運行時來用而不是只能當命令行工具來用。選擇基于 Harness 之后省下來的時間主要花在業務工具開發和編排層設計上這才是產品差異化的地方。所以核心結論是凡是通用 Agent 跑起來要用的東西都盡量復用 Harness凡是跟業務強相關的東西比如工具、審批規則、任務流程都由自己來實現。2. Codex Agent Harness 的核心機制拆解2.1 Agent 運行時的工作閉環理解 Codex Agent Harness 之前先搞清楚什么是 Agent 運行時。所謂運行時就是一套驅動 Agent 不斷循環的框架。它做的事情可以拆成一個很樸素的循環接收用戶消息或系統任務把當前對話狀態歷史消息、上下文、可用工具列表發給模型模型返回文本回復或者返回一個工具調用請求如果是工具調用運行時負責調用對應工具、拿到結果把工具結果作為新消息追加進對話再次發給模型重復這個過程直到模型給出最終回復或達到終止條件。Codex Agent Harness 就是這套循環的工程實現。它不是一個簡單的“請求-響應”接口而是一個有狀態的任務執行引擎。因為每輪工具調用都會改變環境比如生成了文件、修改了代碼、查到了數據所以運行時必須維護好會話狀態并且實時把變化反饋給模型。我剛開始接觸的時候最大的誤區是把它當成一個普通的 API 封裝庫跑通一次對話就以為完事了。實際上真正好用的是它提供的開發者接口可以創建任務、監聽事件、注入工具、控制終止條件。通過這些接口我能精確控制 Agent 的執行過程而不是傻等一個結果。2.2 工具調用Harness 發起工具調用而不是自己就是工具“Agent harness 可以發起工具調用而不是自己就是工具”這句話是我在實踐里體會最深的一點。很多人在設計智能體時容易把“Claude Code”“Codex CLI”本身當成一個工具去集成比如在別的系統里通過命令行調用它。這當然是一種用法但比較初級。更好的做法是把 Harness 嵌入到自己的系統里讓 Harness 作為 Agent 的運行時去主動調用我注冊好的各類業務工具。比如我在內部系統里給 Agent 注冊了一個“查詢發布單狀態”的工具Agent 在執行任務時會自己決定什么時候調用這個工具、傳什么參數、怎么解讀返回結果。這個能力是通過 tool use protocol 實現的。Codex Harness 參照了類似 OpenAI Function Calling 的工具協議每個工具聲明有名稱、描述、參數 JSON Schema。模型根據當前任務決定調用哪個工具Harness 負責安全地執行工具代碼并把結果返回給模型。這里有個關鍵點工具調用過程中Harness 扮演的是“執行者”和“中間人”而不是“工具本身”。它像一個調度中樞所有的工具都掛在它下面。這也是套殼最大的發揮空間工具集決定了 Agent 的能力邊界。你注冊 CRM 工具它就能查客戶信息注冊代碼掃描工具它就能做靜態分析注冊構建部署工具它就能執行流水線。產品差異化的核心之一就是你的工具生態和別人的不一樣。2.3 會話、上下文壓縮與狀態管理Agent 跑久了最大的敵人是上下文窗口。模型一次能處理的 token 是有限的而 Agent 執行過程中工具調用結果、日志、中間思考都會塞進對話歷史。如果不做管理幾輪交互之后就會發現模型開始“忘事”甚至直接報 context 超限錯誤。Codex Harness 對此有一套機制超過閾值就觸發 compact也就是把之前的對話歷史壓縮成摘要釋放上下文空間。但這個機制并不是沒有代價的我在實踐里經常遇到“context ran out of room”的錯誤后面第五部分我會專門講排查方法。在套殼開發時我通常會額外維護一層“會話快照”機制每個任務開始記錄初始上下文任務結束之后保存完整對話記錄到數據庫。這樣即使代碼運行中間崩了也能從快照恢復執行而不是完全從頭開始。Harness 提供了事件回調我訂閱 session 相關事件把關鍵節點寫入自己的存儲這樣能實現比較精細的斷點續跑。3. 套殼落地搭建自己的 Agent 運行時3.1 環境準備Codex CLI 的安裝與基礎配置雖然我們要做的是把 Harness 嵌入自己的應用但第一步還是建議把 Codex CLI 裝好跑通。原因有兩個第一CLI 是最方便測試模型配置和工具行為的工具第二很多環境變量、模型參數可以通過 CLI 先驗證驗證通過后再搬到自己的代碼里。安裝很簡單Node.js 環境準備好之后npm install -g openai/codex裝完之后跑一下codex --version能輸出版本號就說明基礎環境OK。Windows 桌面版用戶可以直接去官網下載安裝包安裝完成后在桌面端登錄調試也可以。我自己的主力開發機是 Windows所以我同時裝了桌面版和 npm 版前者用來日常快速驗證后者用來集成到代碼里。登錄部分有兩種方式ChatGPT 賬號登錄和 API Key 登錄。如果你想在自定義產品里跑自己的模型強烈建議用 API Key 方式配置環境變量即可export OPENAI_API_KEY你的密鑰然后跑codex試試默認配置如果能在控制臺正常對話說明基礎鏈路是通的。這里有一個我當時踩過的坑用 ChatGPT 賬號登錄時模型選擇會受到限制某些模型名在 CLI 里直接不被支持所以如果你打算做產品盡早切換到 API Key 方式省得后面反復折騰。3.2 將 Codex Harness 集成進自己的 Node.js 應用CLI 跑通之后進入關鍵一步在代碼里使用 Harness。項目里安裝npm install openai/codex這個包提供的是 TypeScript 接口可以直接在 Node.js 應用里創建 Agent、配置模型、執行任務。下面是我在項目里實際用過的最小示例API 可能隨版本變化但整體思路不變import { Agent } from openai/codex; import { Shell } from openai/codex/tools/shell; const agent new Agent({ model: gpt-5.6-codex, tools: [new Shell({ sandbox: true })], cwd: /path/to/project, approvalPolicy: on-request, }); for await (const event of agent.run({ prompt: 看一下當前目錄結構 })) { console.log(event); }這里有幾個值得展開的細節。approvalPolicy我建議一開始用on-request也就是工具調用前需要人工確認跑通之后再根據業務場景調整成全自動或者半自動。sandbox: true是給 Shell 工具開沙箱防止 Agent 執行命令時對本地環境造成不可控的破壞。產品上線階段這個配置是保命的。如果你只想先看效果不打算寫代碼也可以通過命令行直接起一個回調式任務codex exec 幫我生成一份 readme但做產品的話肯定要往代碼集成方向走因為只有這樣才能把 Harness 輸出的流式事件、工具調用記錄、token 消耗這些數據接進自己的監控系統。3.3 讓 Agent 接入自己需要的模型Codex Harness 默認使用 OpenAI 自家模型但它也支持通過兼容接口接入第三方模型。因為現在很多模型服務商都提供 OpenAI 兼容的 API 格式所以接入成本很低。我做的第一個內部版本為了控制成本和驗證思路接的是一個國產模型服務配置方法大致是這樣的在 Codex 的配置文件里指定模型提供商和接口地址。你可以在~/.codex/config.toml里增加模型相關的配置類似于這樣model your-model-name model_provider custom同時在環境變量里設置這個自定義 provider 的 API 地址和密鑰。配置完成后先用 CLI 跑一句最簡單的對話確認模型返回正常再回到代碼里跑 Agent。這里要注意不同模型對工具調用協議的支持程度不一樣有的模型雖然在文本對話上表現不錯但 Function Calling 能力不穩定會導致 Agent 在工具調用環節反復失敗。我測試過幾個模型最終結論是工具調用能力是 Agent 產品模型選型最不可妥協的指標寧可犧牲一點文本生成質量也要保證工具調用準確率和格式穩定性。3.4 注冊自定義工具接入內部系統套殼產品最有價值的部分在這一節體現得最明顯。Harness 允許開發者注冊自己的工具然后 Agent 就能像使用內置 Shell 一樣去調用你的內部服務。工具本質就是一個函數輸入是模型生成的參數輸出是字符串結果。我舉個例子。我們內部有一個工單系統我在 Harness 里注冊了一個“查詢工單狀態”的工具const tools [ new Shell({ sandbox: true }), { name: query_ticket, description: 根據工單號查詢工單狀態, parameters: { type: object, properties: { ticketId: { type: string, description: 工單號 }, }, required: [ticketId], }, handler: async ({ ticketId }) { const data await internalApi.getTicket(ticketId); return JSON.stringify(data); }, }, ];Agent 在跟用戶對話時如果用戶問“幫我查一下工單 T20240501 的狀態”它會自動決定調用query_ticket工具把ticketId參數傳進去然后拿到返回結果再組織回復。這里的關鍵是你不用寫任何 if-else 去判斷用戶意圖模型自己會做路由。注冊工具時有一個很重要的設計原則工具描述要寫清楚“這個工具是干什么的、什么情況下該用、參數有什么限制”。模型是靠描述來理解工具的描述不清晰它就不會正確調用。我見過很多工具注冊完沒人調用的案例十有八九是 description 寫得太籠統。描述越具體調用準確率越高。4. 任務編排實踐從單次對話到業務流程4.1 無腦串行不行設計一個任務編排最小框架有了單任務的 Agent 執行能力之后下一步就是把多個 Agent 執行串聯成業務流程。我一開始的做法很樸素把任務列表排成一個數組for 循環里挨個調用 Agent前一個結束之后再把結果拼到下一個任務的 prompt 里。這種做法在小規模 demo 里沒問題但一旦任務之間有關聯、有失敗重試、有超時控制代碼就會變得很亂。后來我重新設計了編排框架核心抽象是 Task。一個 Task 包含任務標識、傳給 Agent 的 prompt、執行策略比如最大輪數、是否允許工具調用、超時時間、失敗重試次數。編排器拿到一個 Task 數組之后按順序執行并記錄每個 Task 的輸入輸出。框架很小但解決了很多問題interface Task { id: string; prompt: string; maxTurns?: number; timeoutMs?: number; retries?: number; } class Orchestrator { private tasks: Task[] []; addTask(task: Task) { this.tasks.push(task); } async run() { const results []; for (const task of this.tasks) { results.push(await this.executeWithRetry(task)); } return results; } private async executeWithRetry(task: Task) { const maxRetries task.retries ?? 2; for (let i 0; i maxRetries; i) { try { return await this.runAgent(task); } catch (err) { if (i maxRetries - 1) throw err; await sleep(1000 * (i 1)); } } } }這個框架雖然簡單但它讓我把注意力從“代碼怎么寫”轉移到了“業務流程怎么定義”。后面接消息隊列、加并發控制都是在這個最小框架上擴展。4.2 多階段任務計劃、執行、驗證任務編排里我踩過最有價值的坑是意識到“一步到位”的 Agent 任務容易失控。比如讓 Agent 直接“幫我修復這個 bug”它可能直接在代碼里亂改一通最后你根本不知道它改了哪里、為什么改。更好的方式是把它拆成多個階段先分析、再計劃、再執行、最后驗證。我在一個代碼倉庫巡檢產品里是這樣設計流程的第一階段讓 Agent 讀取 git diff輸出問題清單第二階段讓 Agent 根據問題清單逐項給出修復方案第三階段讓 Agent 調用內部的代碼掃描工具做驗證最后把結果匯總成報告。每個階段是一個獨立 Task階段之間通過參數傳遞上下文。這樣做的好處非常明顯每個階段的結果都可以人工審核發現問題可以卡在對應階段不用等到最后才面對一個完全不可控的改動。而且因為每個階段的對話上下文是干凈的模型不容易被之前的無關信息干擾任務完成質量更高。如果你正在把 Agent 做成業務流程而不是一次性問答強烈建議采用這種多階段拆分思路。4.3 編排中的重試、超時與并發控制Agent 任務不像普通的 HTTP 請求一個任務可能跑幾十秒甚至幾分鐘中間還有多次工具調用。這種情況下超時和重試策略必須單獨設計不能套用普通接口的套路。我在實踐里的經驗是給每個 Task 設置獨立的超時時間比如分析類任務 60 秒執行類任務 300 秒。超時之后不要立刻重試整個任務而是先做一次“診斷重試”——再跑一次同樣的 prompt但要求模型簡化步驟、減少工具調用。因為很多時候超時是因為模型在一個問題上反復卡住重新給一個更聚焦的指令往往就能解決問題。并發控制也很關鍵。Harness 本身不限制并發但如果你同時開太多 Agent 任務模型 API 的限流、目標系統的壓力都會成為瓶頸。我給編排器加了一個簡單的并發信號量默認限制同時最多跑 3 個任務跑完一個再補一個。這個策略看起來保守但在生產環境里非常穩。5. 常見問題與踩坑實錄5.1 模型不支持gpt-5.6-sol 這類報錯怎么破開發過程中最容易碰到的報錯之一就是類似the gpt-5.6-sol model is not supported when using codex with a chatgpt account這樣的提示。這個報錯出現的典型場景是用 ChatGPT 賬號登錄 Codex然后在配置里指定了一個模型但這個模型在當前賬號體系下不被 Codex 支持。解決辦法分兩步。第一步確認你用的是 API Key 而不是 ChatGPT 賬號登錄因為 API Key 方式對模型選擇的限制少很多。第二步在配置文件里查清楚 Codex 當前支持的模型列表選擇一個明確支持的模型名。如果配置的是自定義模型要確認模型服務商的 API 地址確實兼容 OpenAI 接口并且支持工具調用。我的經驗是遇到這個報錯先不要慌先檢查登錄方式和模型名。這兩個問題解決掉90% 的情況都能恢復。項目里如果你要支持多個模型建議做一個模型配置管理把模型名、接口地址、支持的上下文長度都放到配置文件里方便隨時切換。5.2 上下文爆掉remote compact task 跑不動Agent 跑長任務時上下文窗口很快會被撐滿。Codex 的應對機制是把對話歷史壓縮后再繼續但壓縮動作本身也需要空間。我在跑代碼修復任務時經常遇到類似error running remote compact task: codex ran out of room in the models context的報錯第一次碰到特別困惑因為任務明明沒寫多少字。后來我總結出來這類問題的根源是對話歷史里積累了太多工具輸出。比如 Agent 調用了一個讀文件工具把整個文件內容都塞進對話再調用幾次上下文就爆了。解決思路有幾個一是給工具輸出設置“截斷”邏輯超過一定長度的結果只保留摘要不要全量塞給模型二是適當調大 context window 的配置前提是模型本身支持那么長的上下文三是把大任務拆小盡量在上下文可控的范圍內執行避免把所有事情都交給同一個 Agent 會話。最笨但最有效的方案是在關鍵節點手動清理對話歷史比如每個階段結束之后把之前的內容做一次摘要然后用摘要繼續下一個階段。這也是我推薦多階段編排的原因之一。5.3 連接失敗 / 接口地址不通的排查思路還有一種高頻報錯是connection failed: error sending request這類問題通常跟網絡和配置有關。排查思路我整理成三步先確認 Codex 配置里填寫的接口地址能不能在瀏覽器或 curl 里正常訪問再確認本機是否有服務在監聽對應端口最后檢查接口地址是否填寫正確有沒有多余空格或拼寫錯誤。如果用了 CC Switch 這類配置管理工具來切換不同的 Codex 配置也要注意切換之后是否正確生效。我遇到過切換 provider 后請求仍然被發送到舊地址的情況重啟相關進程之后才恢復。這類本地配置管理工具切換配置之后記得重新發起一次最簡單的請求做驗證不要直接跑復雜任務。還有一個容易被忽視的點如果你在配置文件里填寫了自定義接口地址但是這個地址對應的服務只支持文本對話、不支持工具調用協議那么 Agent 在調用工具時會出現詭異的行為比如返回空結果、卡住不動或者反復重試。排查時如果所有配置看起來都正常建議用最簡單的工具調用任務去驗證接口兼容性。5.4 用 CC Switch 管理多套 Codex 配置時的注意點我在同時測試多個模型服務時用 CC Switch 這類工具來管理多份配置文件原理就是通過它切換 Codex 指向不同的模型 provider。整體上很好用但有幾個注意點值得提醒。第一個注意點切換配置后Codex 進程如果還開著不一定能自動重新讀取配置。我遇到過切換之后Codex 仍然使用舊模型處理請求的情況。解決方法是切換配置后重啟 Codex 相關進程然后再驗證。第二個注意點不同 provider 的模型能力差異很大同一段 prompt 在 A 模型上能正常走完工具調用在 B 模型上可能就完全亂套。不要因為“接口地址兼容”就認為行為也完全兼容每個模型都要單獨跑一輪工具調用集成測試。第三個注意點CC Switch 本身只做配置切換不做問題診斷。出現“本地轉發配置失敗”這類的提示時還是要到 Codex 的配置文件里去核對實際的接口地址、密鑰、模型名不要只看管理工具的界面顯示。管理工具有時候顯示的是緩存下來的舊信息最終生效的還是底層配置文件。5.5 Windows 桌面版安裝與 Node 環境坑最后說一下 Windows 桌面版和 Node 環境的問題。Codex 桌面版可以直接從官網下載安裝安裝過程本身沒什么坑但后面的使用環境經常出問題。我遇到比較多的是兩個一個是 Node.js 版本過舊導致 npm 包安裝失敗或運行時崩潰另一個是環境變量沒有正確設置導致命令行找不到 codex 命令。如果你打算在 Windows 上用代碼集成的方式開發建議把 Node.js 升到官方長期支持的版本不要用太老的版本。另外安裝完 npm 包之后如果命令行執行codex報錯找不到命令檢查一下 npm 全局安裝目錄是否在系統 PATH 里。通常重新打開終端或者手動刷新環境變量之后就能解決。6. 最后一點經驗套殼產品的邊界感我在這個項目里最大的體會是套殼能不能成功取決于你清不清楚邊界在哪里。Harness 負責把 Agent 跑起來但它不負責你的業務應該怎么定義、你的工具應該暴露哪些能力、你的任務流程該怎么設計。這些都是產品層要解決的問題。再分享一個小技巧在開發早期就把 Harness 的事件流全部記錄下來。Codex 在運行時會產生大量事件比如工具調用開始、工具調用結束、上下文壓縮、模型響應等等。這些日志在開發調試時可能覺得啰嗦但到了生產環境它們就是最能還原現場的材料。我后來排查線上問題幾乎全靠這些事件日志。這套方案后續還可以繼續擴展的方向很多比如把 Harness 接到消息隊列上做成異步任務處理平臺或者把自定義工具從本地函數改成可以熱插拔的遠程插件再或者把多階段編排做成可視化流程配置界面。我自己實踐中最先嘗到甜頭的是“工具生態”這條線——每多接入一個內部系統工具產品的實用價值就往上跳一大截。如果你也在考慮基于成熟框架做自己的 AI 產品Codex Agent Harness 值得一試關鍵是別把它當成終點而要當成一個可以隨意改造的運行時起點。