Skip to Content
操作流程任務輸出交給 Command

任務輸出交給自訂表格 Command

任務版本可以用執行一個自訂表格 Command 來收尾。版本宣告一個 kind 為 custom_table_command 的 output_policy;每次 run 都會拿到一把單次 run 憑證,這把憑證只能在某一個聊天室裡呼叫這一個 Command,work script 產出 Command 的輸入後就把它送出去。平台再用與 Commands API 相同的執行器去執行這個 Command。

本頁只寫 Sandbox 契約(v5.21.0 發布)。Command 的輸入、效果、核准與結果的意義,仍留在自訂表格的文件。較早的那種,表格 writeback(custom_table_writeback),契約不變,只多了一項:它現在會拒絕 write_policy 為 commands_only 的表;本頁會標出兩者不同的地方。

1. 在草稿版本宣告

POST /tasks/{task_id}/versions 與 PATCH .../versions/{vid} 上的 output_policy 是以 kind 區分的聯集型別。Command 這一種長這樣:

{ "output_policy": { "kind": "custom_table_command", "command_id": "<Command id>", "chatroom_id": "<room id>", "input_schema": [ { "name": "order_id", "type": "string", "required": true } ] } }
欄位說明
kind字面 custom_table_command。
command_id這次 run 可以呼叫的 Command。≤36 字元,字元集 [A-Za-z0-9._-]。
chatroom_idCommand 執行所在的聊天室(同樣的格式)。此版本的每一次 run 都必須從這個聊天室提交。
input_schemaCommand 的輸入宣告,≤200 項(每項是自訂表格的 CommandInput:至少 name、type、required)。必須等於該 Command 自己的 inputs(正規化之後比對):順序相同,每個輸入的每個欄位值都相同(包括 description 與 nullable)。

未知欄位是 422。這個政策會被雜湊進版本的 canonical_digest,只出現在 owner 視圖(borrower 看到 null)。和表格 writeback 一樣,它屬於部門擁有的任務:公司擁有的任務在發布時回 409 output_policy_owner_scope_unsupported,而且只有部門擁有的任務的 run 能通過 §4。草稿與發布時都不會檢查 chatroom_id 是否為擁有部門的聊天室;若不是,這個版本能發布,但每一次 run 都會因為 borrowed 而被拒。

2. 什麼樣的 Command 合格

草稿建立、草稿 PATCH、發布,以及每一次提交 run 時都會檢查 Command:

檢查拒絕方式
Command 存在、沒被刪除,且屬於你的公司422 output_policy_command_not_found
input_schema 等於 Command 宣告的 inputs,且 Command 是寫入型(mode 為 write)422 output_policy_schema_mismatch
Command 以 definition authority 執行、execute policy 為 restricted,且宣告了 effect_identity403 output_policy_author_denied

在發布時,這三項檢查的任何一項失敗(包括第三項)都會以 409 output_policy_unsatisfiable 回報——和表格 writeback 的表格不見了是同一種回報方式(訊息文字是通用的、提到的是 table;以 code 為準)。只有下一節對發布者與已記錄撰寫者做的身分、grant 與範圍檢查,仍是 403 output_policy_author_denied。在提交時,表中的 code 會原樣回傳,所以 Command 若在發布後被刪除或重新宣告,這次提交就會被拒(什麼都不會排隊),直到 Command 與版本的 policy 又一致為止。

還有一件這些檢查抓不到的事:需要凍結提案的 Command(有 proposal_policy)能通過這三項檢查,但 executor 對 run 發出的每一次呼叫都會拒絕(ct.governed_context_required),所以它不能當作 run 的輸出。

3. 誰能撰寫、發布與提交

撰寫者與提交者必須各自通過同一套 Command 檢查,而且每一步都會重做:

  • **身分:**公司內有效(已驗證、未停用、未過期)、非 service account 的使用者,且是 chatroom_id 的成員(該聊天室必須還在)。
  • **Grant:**該 Command 有效的 execute grant(以 chatroom_id 為行動中的聊天室來判斷)。光是管理該 Command 的表、或持有 execute 委派 lease 都不夠,雖然 Commands API 本身對 restricted Command 兩者都會放行。
  • **範圍:**部門範圍的 Command,chatroom_id 必須是該部門的聊天室;聊天室範圍的 Command,chatroom_id 必須就是那個聊天室。

失敗是 403 output_policy_author_denied。撰寫者是最後寫入草稿的人:草稿建立、每次草稿 PATCH 與每次 POST .../script-file 上傳都檢查呼叫者並讓呼叫者成為撰寫者,發布則同時檢查發布者(平台 root operator 除外)與已記錄的撰寫者。

4. 提交 run

每一條會建立 run 的路徑(手動 POST /chatrooms/{id}/runs、重試、Agent 工具、自訂表格 trigger)都會在同一個交易裡為這次 run 鑄出一把密封憑證。表格 writeback 遇到 borrowed 的 run、或提交者不是以 JWT 驗證的使用者(API key、受限 Sandbox 金鑰或社群媒體 client)時會跳過鑄憑證,run 照樣排隊。Command 輸出的版本則是拒絕這次提交(什麼都不會排隊),回 403 output_policy_author_denied,除非下列條件全部成立(Command 已被刪除或不再相符,則是 §2 的 422):

  • 任務是部門擁有,且這次 run 不是 borrowed(房間屬於擁有部門;公司擁有的任務或其他部門的房間都算 borrowed——見任務受眾);
  • run 的聊天室就是政策的 chatroom_id;
  • 提交者是以 JWT 驗證的使用者主體——已登入的使用者、trigger 觸發的 run 中 trigger 的撰寫者,或內部聊天中 Agent 代表的使用者;API key、受限 Sandbox 金鑰、社群媒體 client 都不算;
  • 提交者與版本的撰寫者都通過 Command 檢查。

若平台的 writeback 簽章金鑰缺少或太短,提交會回 503 sandbox_writeback_key_unavailable,與表格 writeback 相同。

5. run 裡的憑證

認領時,憑證透過密封 secret 通道送進 work script,用的是與表格 writeback 相同的兩個變數名稱。它們是 runner 保留的 TEAMSYNC_ 名稱,不能設在 ordinary_env:

變數Command 輸出 run 的值
TEAMSYNC_CT_WRITEBACK_URL{API origin}/public/module/custom_tables/callback/command-output/{credential id}
TEAMSYNC_CT_WRITEBACK_TOKEN這個 URL 的 Bearer secret
  • 每次 run 一把憑證;只儲存 secret 的 SHA-256。
  • 它會在下列三個限制中最早到的那個失效:run 離開 queued/starting/running;認領時間 + timeout_seconds + 120 秒收尾包絡(finalization envelope);以及提交時設下的保底期限,也就是提交時間 + 48 小時與 timeout_seconds + 120 秒兩者中較大的那個(排隊等待也算在內,超過之後才被認領的 run 會在沒有這兩個變數的情況下啟動)。每次進入終態都會撤銷。
  • **請檢查這兩個變數是否存在。**若平台在認領時無法揭露憑證——run 排隊期間撰寫者或提交者的 grant 被收回、Command 或版本的 policy 變了、任務或版本被封存、上述保底期限已過、簽章金鑰或公開的 API origin 不可用,或多出來的變數會讓環境超過大小上限——run 仍會啟動,只是沒有這兩個變數。
  • work script 必須連得到 API origin:託管雲端需要執行期對外網路(限制);地端部署另外需要主機的 SANDBOX_ONPREM_EGRESS=internet 與 operator 對 API origin 的開放(雲端與地端部署)。

6. 送出輸出

POST {TEAMSYNC_CT_WRITEBACK_URL} # /public/module/custom_tables/callback/command-output/{credential id} Authorization: Bearer {TEAMSYNC_CT_WRITEBACK_TOKEN} Content-Type: application/json { "inputs": { "order_id": "A-1001" } }
import json, os, urllib.request request = urllib.request.Request( os.environ["TEAMSYNC_CT_WRITEBACK_URL"], data=json.dumps({"inputs": {"order_id": "A-1001"}}).encode(), headers={ "Authorization": "Bearer " + os.environ["TEAMSYNC_CT_WRITEBACK_TOKEN"], "Content-Type": "application/json", }, method="POST", ) print(urllib.request.urlopen(request).read().decode())

這是公開路由(沒有 /private 前綴、不用租戶登入):Bearer secret 本身就是授權。Body 必須恰好是 { "inputs": { … } }——任何其他頂層欄位都是 422——inputs 會對照 Command 宣告的輸入與它的 JSON 上限驗證。成功時的 body 是 Commands API 的執行回應(CommandExecutionResponse,省略 null 欄位);欄位與狀態請看自訂表格文件。

  • Command 以 run 的提交者身分執行(trigger 觸發的 run:trigger 的撰寫者),以 chatroom_id 為行動中的聊天室,冪等鍵是 sandbox-output:{run_id},所以每次 run 的 Command 最多生效一次。
  • **第一份有效輸出勝出。**第一份通過宣告輸入檢查的 inputs 會在 Command 執行之前以 digest 釘住(對驗證後的 inputs 取 digest),即使 Command 隨後拒絕它們或執行失敗,這個釘選也不會解除:重試必須送相同的 inputs,改過的 inputs 在這次 run 剩下的時間裡都會得到 409 output_command_conflict。再送相同的 inputs 會走 Command 自己的冪等機制:已經成功(或已暫存等待核准)的執行會被重放、不會執行兩次,失敗的執行則可以重試(若呼叫與一個仍在進行中的執行競爭,可能收到 Commands API 自己的 409 idempotency_in_progress)。
  • 每次呼叫都會重新檢查授權,所以 run 進行中 grant 被收回,呼叫就會變成 403 output_policy_author_denied。
  • **核准需要 run 還活著。**若 Command 沒有直接完成、而是暫存了一個核准,放行時會重新解析 run 的憑證;run 一結束憑證就被撤銷,所以在 run 結束之後才放行的核准無法再成功。
狀態detail.code何時發生
404output_run_unavailable憑證 id 不存在,或 Bearer secret 缺少、超過 256 字元或不對(統一一種回答,不洩漏任何資訊)
409output_run_unavailable憑證已撤銷或過期,或 run 已不在 queued/starting/running,或它的任務或版本不再是 active 且已發布
409output_command_stalerun 提交之後,版本的政策、Command 的定義或它宣告的輸入變了,或該 Command 已被刪除、或已不再是你公司的寫入型 Command
409output_command_conflict這次 run 已經接受過另一份不同的有效 inputs
422output_schema_violationinputs 不符合 Command 宣告的輸入
403output_policy_author_denied提交者或撰寫者的 Command 授權被收回,或該 Command 不再是帶 effect identity 的 restricted definition-authority Command
422(validation 陣列)body 不是 { "inputs": { … } }、超過 JSON 上限,或路徑裡的憑證 id 不是小寫 UUID
其他Command 執行錯誤同一個呼叫在 Commands API 會得到的任何回應,沿用它自己的錯誤形狀(detail.error 物件,或由 {type, msg} 項目組成的 detail[] 清單)

7. Trigger、Agent 與前端

  • 自訂表格 trigger。submit_sandbox_job 可以對準 Command 輸出的版本(表格 writeback 的版本仍然被拒)。run 由 trigger 的撰寫者提交,輸入檔就是渲染後的 input,而 §4 的拒絕在觸發時是 trigger-run receipt 上一個失敗的 action(不會有 Sandbox run)。見自訂表格 trigger。
  • Agent。requires_confirmation=false 時 Agent 直接提交,不經提案回合;表格 writeback 的版本仍需要兩回合確認。§4 的 403 拒絕,對模型來說是工具錯誤 unauthorized;422(Command 不存在或不符)是 validation_error;503 是 provider_unavailable。見 Agent toolkit。
  • **前端。**owner 在版本上看得到 output_policy,borrower 看到 null。處理上面的撰寫與提交拒絕。用一般的執行 API觀察 run;Sandbox 的 run 紀錄不帶 Command 的結果。

相關

Last updated on