文件 執行 執行記錄

執行記錄

執行記錄是你按下「執行」後產生的可稽核記錄:狀態、階段日誌、人工關卡、最終產物。每一次執行都可重播、可恢復、可分享。

心智模型。一個應用是食譜,一次執行記錄是你實際下廚的那一次。 食譜在週二晚餐和週三晚餐之間不會變;變的是執行記錄,表單上的食材不同、沙盒不同、輸出不同、 時間戳不同。平台會永久保留每一次執行,方便你比較、偵錯與重播。

執行的生命週期

每次執行都會走入五種終止狀態之一。狀態機如下:

pending  →  running  →  completed

                     running  →  failed

                     running  →  cancelled
狀態意義你可以做什麼
pending執行已排入佇列但尚未真正開始。通常小於 1 秒。等待。必要時取消。
running至少一個階段正在進行中。階段日誌正在即時輸出。即時觀看。取消。回應人工關卡。
completed所有階段都成功完成。所有產物都已儲存。下載產物。分享。用新的輸入重新執行。從後面某個階段恢復以嘗試變動。
failed某個階段遇到錯誤或逾時。後續階段沒有執行。閱讀錯誤訊息。點擊 "Debug in chat"。重試。或修正應用後重新執行。
cancelled你(或某人)明確地停止了執行。從頭重新執行,或在已有部分完成階段時從後面階段恢復。

一筆執行記錄的結構

平台會將每一次執行持久化到 app_execution,包含以下欄位:

{
  "id": "exec_8f4c2e1b",
  "app_id": "app_...",
  "user_id": "usr_...",
  "status": "completed",
  "input": { company_name: "Stripe", ... },
  "is_test": false,
  "shared_session": false,
  "start_from_stage_index": 0,
  "prior_execution_id": null,
  "duration_ms": 43210,
  "error": null,
  "created_at": "2026-05-31T09:14:22Z"
}

每個階段也會擁有自己的 app_stage_log 條目:

{
  "execution_id": "exec_8f4c2e1b",
  "stage_index": 0,
  "stage_type": "agent",
  "status": "completed",
  "goal_expanded": "Research Stripe and write a brief...",
  "session_id": "sess_...",     // 如果你點擊 "Debug in chat",這裡會關聯到對應的會話
  "duration_ms": 38421,
  "error": null
}

goal_expanded 欄位是輸入替換之後的目標,也就是 agent 實際看到的內容。 如果你的表單中 company_name: "Stripe",那麼原始目標中的 {{company_name}} 就會在這裡被解析出來。

即時觀看執行:SSE 串流

running 狀態下,應用頁面會訂閱一個以執行記錄 ID 為鍵的 Server-Sent Events 串流。這個串流同時也是對外公開的 API 介面,任何想把執行情況鏡像到其他系統的人都可以使用。 端點如下:

GET /api/app-executions/:execId/stream
Authorization: Bearer <token>

// 回應標頭
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

五種執行層級事件

每個事件以 event: <name>\n data: <JSON>\n\n 的形式封包。連線時,伺服器 會先重播當前狀態,讓晚到的訂閱者也能看到完整序列,當前執行狀態、迄今為止的每一筆 階段日誌、任何待處理的人工關卡、每一個已儲存的產物,然後再切換到即時廣播。

event負載結構觸發時機
status{ status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' }訂閱時(重播)以及每次執行層級狀態變化時。
stage_log一整列 AppStageLog 資料,參見上面的結構訂閱時(每筆現有階段日誌一次)以及每次階段狀態變化時。
human_gate一整列 AppHumanGate 資料。human 階段開啟一個關卡時,以及關卡被解決時再次觸發。
artifact一整列 AppArtifact 資料,若存在則包含 content每次階段的輸出被持久化時。
done{ status, duration_ms }在結尾觸發一次,然後伺服器關閉串流。

若你訂閱的是一個已經處於終止狀態的執行,伺服器會重播已儲存的階段日誌與產物,發送一個 done 事件後斷線,不會建立即時連線。如此一來,重播與即時監控使用的是同一個 端點與同一個解析器。

更細粒度的 runner 事件

上面五個執行層級事件是摘要串流。在一個 agent 階段內部,底層的 run-agent 二進位檔會輸出粒度細得多的 JSONL 軌跡,逐 token 文字、工具呼叫、 子任務、思考過程。這些事件位於會話層級串流而非執行層級串流,但它們正是右側推理軌跡面板背後 的資料來源。完整事件目錄請參見runner 協定

最小 SSE 消費者

const es = new EventSource('/api/app-executions/' + execId + '/stream', {
  withCredentials: true,
});

es.addEventListener('status', (e) => console.log('status', JSON.parse(e.data)));
es.addEventListener('stage_log', (e) => console.log('stage', JSON.parse(e.data)));
es.addEventListener('artifact', (e) => console.log('artifact', JSON.parse(e.data)));
es.addEventListener('human_gate', (e) => console.log('gate', JSON.parse(e.data)));
es.addEventListener('done', (e) => { console.log('done', JSON.parse(e.data)); es.close(); });

測試執行 vs 正式執行

在執行上設定 is_test: true,它就會被標記為臨時性的。測試執行:

  • 不會以正式執行同樣的方式計入你的用量配額。
  • 預設會被應用「執行」分頁過濾掉(切換「顯示測試執行」即可看到)。
  • 使用獨立的 shared_session 命名空間,這樣隊友的測試執行不會污染你的。
  • 除非被釘選,30 天後會自動清除。

當你點擊試一下時,應用編輯器使用的就是測試執行,這正是你在迭代提示詞時 想要的。

人工關卡:暫停以等待核准

human 階段不會執行程式碼。它會暫停執行,顯示目標文字以及先前的產物,等待你 (或另一位有權限的使用者)採取行動。這次暫停會被儲存在 app_human_gate 中:

{
  "id": "gate_...",
  "execution_id": "exec_...",
  "stage_index": 2,
  "message": "Review these 47 enriched leads. Remove rows you don't want to email.",
  "status": "pending", // pending | approved | rejected
  "response": null,
  "responded_at": null
}

關卡在 UI 中長什麼樣

┌─────────────────────────────── Run #exec_8f4c2 ─────────────────┐
Stage 3 of 4 · Review  paused
│ │
Review these 47 enriched leads. Remove rows you don't want
to email.
│ │
📎 enriched_leads.csv (47 rows) [ View ] │
│ │
[ Approve & continue ]  [ Reject ]  [ Edit input first ]
└──────────────────────────────────────────────────────────────────┘

三個按鈕

  • Approve & continue(核准並繼續),關卡變為 approved,下一個階段立即開始。
  • Reject(拒絕),關卡變為 rejected,執行轉入 failed
  • Edit input first(先編輯輸入),開啟產物以編輯(例如從 CSV 移除列),存檔之後,被編輯的版本就是第 n+1 階段讀取的內容。

找到需要你處理的關卡

從頂部導覽開啟待處理的關卡,或呼叫 GET /api/app-executions/pending-gates。當關卡進入 pending 狀態時, 可以設定透過電子郵件或 Slack 通知你,這對你可能不會盯著看的長任務流程線非常有用。

關卡 API

透過向關卡的解決端點 POST 來核准或拒絕它。請求主體攜帶判定結果,以及可選的自由文字 response,會儲存在關卡列上:

POST /api/app-executions/:execId/gates/:gateId
Content-Type: application/json
Authorization: Bearer <token>

{
  "action": "approved" // 或 "rejected",
  "response": "Removed 4 lookalikes; lists looks tighter now."
}

機制上:伺服器會標記 app_human_gate.status 並寫入 responded_at, 然後解決一個執行器一直在 await 的記憶體 Promise。等待中的階段會立刻從 waiting 切出,流程線要嘛進入第 N+1 階段(核准時),要嘛短路到 failed(拒絕時)。一個攜帶更新後資料列的 human_gate SSE 事件會 送發給所有訂閱者。

當關卡待處理期間,執行始終保持 running 只有階段日誌會進入 waiting。沙盒會透過 keepAlive 保持運作,這樣 在核准後恢復時不必付冷啟動成本。代價是:一個無限期待處理的關卡會一直霸佔一個熱沙盒,請給審核者一個合理的預期,或者在應用設計裡為關卡設定自動逾時。

取消執行

在執行中的執行上點擊取消,或送出:

POST /api/app-executions/:execId/cancel
Authorization: Bearer <your_token>

取消會:

  • 停止當前正在執行的階段。已完成的階段維持完成狀態,其產物會被保留。
  • 關閉沙盒。
  • 將執行轉入 cancelled
  • 將尚未花用的預算退還到你的餘額。

稍後你可以從下一個階段恢復:見下文。

從指定階段恢復

一個真正強大的能力:只重跑需要重跑的階段,重用那些已經跑得好的階段產物。在執行請求中傳入 start_from_stage_indexprior_execution_id

POST /api/apps/:appId/run
Content-Type: application/json

{
  "input": { ... same shape as the original run ... },
  "start_from_stage_index": 2,
  "prior_execution_id": "exec_8f4c2e1b"
}

執行器實際上做了什麼

  1. 建立一筆新的 app_execution 列,指向同一個應用版本。對於索引 0 .. start_from_stage_index - 1 的階段日誌,會立即以 status: 'skipped' 寫入,執行歷史會清楚標示哪些階段並未重新執行。
  2. 透過 dao.listArtifactsByStages(priorExecutionId, [stageIds]) 載入被略過階段 的產物。對於不攜帶內嵌 content 的產物,執行器會以 s3_key 從 S3 下載主體並以 UTF-8 解碼,這樣即使原始列已被卸載,下一階段 的系統提示詞也能看到先前的內容。
  3. 取得一個全新的沙盒。原執行沙盒上第 0-1 階段寫入的檔案都消失了,只有已被持久化的 產物會跨越邊界。如果後續階段依賴的是中間暫存檔案而非宣告的產物,恢復就無法重現它們。
  4. 建立第 start_from_stage_index 階段的系統提示詞,把載入到的先前產物以 ## Previous Stage Outputs 的形式注入,然後從那裡一路跑到流程線結尾。

恢復 + shared_session

當原始執行使用了 shared_session: true,且恢復執行也要求它時,執行器會更進一步: 它會在 prior_execution_id 上找出一筆已經有 session_id 的階段日誌, 重用那個會話,繼續同一段 Claude 對話。可見效果:Debug in chat 會顯示一條橫跨原始執行與恢復執行的連續討論串。不要不假思索就啟用,共用 Claude 會話意味著 模型的上下文仍然包含先前的輪次,有時這正是你要的,有時不是。

什麼時候會用到它

  • 最後一個階段的草稿不對。第 0-2 階段花了 4 分鐘跑得好好的;第 3 階段的 郵件草稿語氣不對。調整應用,然後從第 3 階段重跑,省下 4 分鐘。
  • 階段因為外部原因失敗(Connect 權杖在執行中過期)。修復 Connect,用相同的 輸入從失敗階段重跑。
  • 人工拒絕了一個關卡。調整前一個階段的提示詞,從那裡重跑。
恢復不會回復已產生的副作用。如果被恢復的階段已經往你的 CRM 寫入了列、 發送了 Slack 訊息、或推送過一次 git commit,這些工作已經實際發生了。平台不追蹤對外副作用; 恢復只重用已持久化的產物。為了具備冪等性,請讓會觸及 Connect 的階段先檢查 「我們是不是已經做過這件事了?」。

在 UI 中:開啟執行,點擊從階段重新執行……,挑選階段。

執行歷史

在應用頁面,執行分頁會以最新優先的順序列出每一次執行。每一列顯示:

欄位顯示內容
狀態五個狀態徽章之一(pending / running / completed / failed / cancelled)。
觸發者你、隊友、排程,或 API。
開始時間以你工作區時區顯示的時間戳。
耗時所有階段加總的牆鐘時間。
成本沙盒秒數 + LLM token 換算成美元。
輸入所使用的表單值。點擊以展開。
產物數量,並附帶每個的快速下載。

頂部的篩選條件可以讓你按狀態、日期區間、觸發者與標籤縮小範圍。該列表也可透過 GET /api/app-executions?app_id=... 取得。

偵錯失敗的執行

第 1 步:閱讀錯誤訊息

開啟失敗的執行。頁面最上方第一項就是錯誤訊息與發生錯誤的階段。常見形式:

錯誤幾乎總是表示
stage_timeout階段耗時超過了 timeout_ms。把它調大。
connect_unauthorized某個 Connect 被撤銷或過期了。重新授權。
required_input_missing某個必填表單欄位是空的。不要把欄位過於激進地標為必填。
artifact_format_mismatch階段產出的格式不對(例如宣告的是 CSV,卻輸出 Markdown)。
sandbox_oom沙盒內記憶體耗盡。資料量太大,拆分階段,或使用腳本階段。
agent_gave_upagent 判斷任務做不下去了。閱讀它的推理,通常是少了某個輸入。

第 2 步:點擊 "Debug in chat"

點擊 Debug in chat。會開啟一個攜帶失敗執行完整脈絡的對話,輸入、部分產物、 agent 推理軌跡、錯誤。直接和 agent 對話:

> Why did this fail? What should I change in the App?

agent 會診斷問題、建議修正方案,並(通常會)直接編輯應用以套用修正。然後你就可以從相同位置 重跑。

第 3 步:重試或重新執行

  • 重試:相同輸入、相同應用版本、全新沙盒。有時候不穩定的外部 API 只是 需要再試一次。
  • 從第 N 階段重跑:保留先前的產物。當只有後段階段壞掉時使用。
  • 編輯應用後再執行:用於系統性的 bug(模糊的提示詞、錯誤的格式)。會讓應用升一個新版本。

分享一次執行記錄

你可以把一次執行的唯讀檢視給隊友看,適用於「看看這個應用昨天產出了什麼」之類的情境,又不必 給對方應用本身的寫入權限。

  1. 開啟執行。
  2. 點擊分享
  3. 設定:要包含哪些產物、有效期、是否允許下載、是否設密碼關卡。
  4. 取得一個公開連結:https://app.aitroop.net/s/<token>

分享記錄會被持久化到 share 表中,附帶 token、解鎖政策與下載處理器。可以隨時 從設定 → 分享撤銷。

透過 API 觸發執行

當你想要從一個 webhook、CI 流程線或另一個 agent 觸發一個應用時很有用。最小呼叫:

curl -X POST https://app.aitroop.net/api/apps/<appId>/run \
  -H "Authorization: Bearer $AT_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "input": { "company_name": "Stripe" } }'

回應是新的 app_execution,狀態為 status: "pending"。要觀察它:

curl https://app.aitroop.net/api/app-executions/<execId>/stream \
  -H "Authorization: Bearer $AT_USER_TOKEN" \
  -N

SSE 串流會持續輸出直到執行進入終止狀態。或者輪詢 GET /api/app-executions/:execId

常見問題 & 疑難排解

我的執行卡在 pending 好幾分鐘了。怎麼回事?

可能原因:

  • 沙盒供應商冷啟動(罕見;通常小於 1 秒)。
  • 你觸到了方案的並行執行配額;執行在排其他執行的佇列。
  • agent worker 佇列塞住了(設定 → 工作區的頂部會有狀態橫幅)。

解法:如果超過 2 分鐘,取消並重跑。檢查狀態橫幅。如果一切都正常但仍然卡著, 表示執行在排隊,等待或取消。

為什麼我已完成的執行顯示「0 個產物」?

應用的階段沒有宣告任何 artifact_defs,或者 agent 產出的格式不符合宣告,平台把 它丟掉了。開啟執行,捲到對應的階段,找一找「format mismatch」警告。把目標寫得更明確說明 要產出什麼,或者把宣告的格式改成 file 作為退路。

我取消了一個執行,能「取消取消」嗎?

不行,但你可以從最後一個完成的階段重新執行。先前執行的產物已保留,所以 不會丟掉工作。開啟被取消的執行,點擊從階段重新執行……,挑選最後一個已完成 階段之後的那個階段。

一個人工關卡待處理 3 天了。執行是死了嗎?

沒有。關卡預設無限期等待。執行只是停在那裡,並沒有失敗。要清理它,要嘛回應該關卡,要嘛 取消執行。

如果你希望關卡自動逾期,在階段定義中加入一個逾時: "gate_timeout_ms": 86400000 代表 24 小時。逾時後,關卡會轉入 rejected,執行轉入 failed

我能看到 agent 當時在想什麼嗎?

可以。在執行的時間軸上點擊任一階段,即可展開它的推理軌跡。軌跡會顯示 agent 的計畫、每一次 工具呼叫(含參數與結果),以及它最後的評註。要看更深的細節,切換「顯示思考過程」,這會 暴露模型內部的推理區塊(在可用時)。

為什麼同一個應用這週比上週慢了一倍?

可能原因:agent 進行了更多次工具呼叫。可能是提示詞改了(看一下版本), 處理的資料變多了,或者某個外部 API 變慢了。把兩次執行並排開啟:執行日誌會顯示每次工具呼叫 的次數與耗時。對比一下就能找到回歸點。

我想 A/B 測試同一個應用的兩個版本。怎麼做?

Fork 這個應用(會建立一份私有副本 v1),編輯副本,然後用相同的輸入執行兩個應用。從各自應用 的「執行」分頁並排比較兩次執行。要做統計層面的深入分析,寫一個排程,每天用相同的輸入執行 兩個版本,並把結果送到一張試算表裡。

我能從 Zapier / n8n / CI 中的 curl 指令觸發執行嗎?

可以,用你的 bearer token 呼叫 POST /api/apps/:appId/run。參考上面的 curl。多數 使用者會把這個呼叫放進一個 webhook 處理器中,再以 GET /api/app-executions/:execId 等待完成。完成時的對外 webhook 也可透過排程的投遞目標來支援。