代理註冊與佇列API規範
本文件提供將AI代理(或任意主機)註冊為GIIP邏輯機器、輪詢其自身CQE任務佇列並回報執行結果的技術規範 — 與生產環境中giipAgentWin/giipAgentLinux實際使用的機制相同。
📋 概述
此API使主機 — 執行giipAgentWin/giipAgentLinux的實體/虛擬機器,或沒有自身持久硬體身分的AI程式設計代理工作階段 — 能夠將自己註冊為tLSvr中的邏輯機器(lssn),然後輪詢該lssn自身的CQE(Centralized Queue Engine,集中式佇列引擎)任務佇列並回報結果。這三種操作都透過同一個端點完成。
🔐 認證 — 與REST風格不同的單一調度端點
這與問題管理API的API形態不同。 那個API是使用
x-api-key請求標頭和真實HTTP狀態碼的一般REST端點(/api/giipIssues)。此API是giipApiSk2— 一個表單編碼的通用調度端點,始終回傳HTTP 200。
基礎URL
https://giipfaw.azurewebsites.net/api/giipApiSk2
請求格式(始終是相同的三個欄位)
| 欄位 | 內容 |
|---|---|
text | 預存程序「命令」名稱加其參數名稱,以空格分隔(例如AgentAutoRegister hostname jsondata)— 而非參數值 |
token | SK,作為一般表單欄位傳送。不是HTTP請求標頭。 |
jsondata | 保存實際參數值的JSON物件 |
curl -s -X POST "https://giipfaw.azurewebsites.net/api/giipApiSk2" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "text=AgentAutoRegister hostname jsondata" \
--data-urlencode "token=${GIIP_API_KEY}" \
--data-urlencode 'jsondata={"hostname":"Lowy-DP01-claude-code","os":"Windows 10"}'
HTTP狀態碼幾乎無法說明任何問題
此端點能夠解析的所有請求都回傳200 OK。 無效SK沒有401,不存在的lssn沒有404,權限問題沒有403。真實結果始終位於JSON主體的data[0].RstVal中(已透過審查giipfaw/giipApiSk2/run.ps1原始碼及實際測試確認,2026-09-06):
{"data":[{"Proc_MSG":"401|Unauthorized - Invalid SK","RstVal":401,"RstMsg":"Unauthorized - Invalid Secret Key"}]}
上述回應仍然是HTTP 200。判斷giipApiSk2呼叫是否真正成功,務必始終查看data[0].RstVal,而非HTTP狀態碼。
SK — 與Issue API相同的金鑰類型,不同的傳輸方式
- 與Issue API使用的相同、在
/svclist按csn簽發的專案SK — 但此處以POST主體中的token=<sk>傳送,而非x-api-key請求標頭。 - csn由SK在伺服端推導得出(
dbo.lwGetCSNbysk(@sk))— 您不會在請求中直接傳送csn,AgentAutoRegister/CQEQueueGet根本不存在可被靜默鉗制的客戶端指定csn欄位(已透過審查giipdb/SP/pApiAgentAutoRegisterBySK.sql確認,2026-09-06)。KVSPut有相關但不同的所有權檢查 — 見下文。
端點
AgentAutoRegister — 註冊邏輯機器,或對現有機器傳送心跳
請求:
text=AgentAutoRegister hostname jsondata
token=<sk>
jsondata={
"hostname": "Lowy-DP01-claude-code",
"os": "Windows 10",
"cpu": "...", "cpu_cores": 8, "memory_gb": 32, "disk_gb": 512,
"agent_version": "giip-agent-skill/1.0.0",
"ipv4_global": "...", "ipv4_local": "...",
"network": [{"name":"eth0","ipv4":"...","ipv6":"...","mac":"..."}],
"software": [{"name":"...","version":"...","vendor":"...","type":"..."}],
"services": [{"name":"...","status":"...","start_type":"...","port":80}]
}
只有hostname是必需的,其餘欄位均為盡力而為、可以省略。
身分鍵是(hostname, csn)。 預存程序(pApiAgentAutoRegisterBySK)以LSHostname = hostname AND CSn = <從sk推導>條件查找tLSvr:
| 存在現有列? | 動作 | 回應 |
|---|---|---|
| 否 | 插入新tLSvr列,lssn = SCOPE_IDENTITY() | {"data":[{"lssn":<new>,"action":"new","RstVal":200,...}]} |
| 是 | 更新現有列(規格、網路/軟體/服務清單、LSLastHeartbeat) | {"data":[{"lssn":<same>,"action":"update","RstVal":200,...}]} |
已實際驗證(2026-09-06,csn 47):
$ python scripts/giip_agent.py register --tool-slug claude-code
{"success": true, "hostname": "Lowy-DP01-claude-code", "lssn": 71290, "action": "new"}
$ python scripts/giip_agent.py register --tool-slug claude-code # 重複呼叫
{"success": true, "hostname": "Lowy-DP01-claude-code", "lssn": 71290, "action": "update"}
強制性主機名稱規則: <實體主機名稱>-<tool-slug>。同一主機上不同的AI工具必須各自使用自己的主機名稱註冊(因此取得各自的lssn)— 在同一台電腦上執行的Claude Code、Codex和Antigravity是三台不同的邏輯機器(例如Lowy-DP01-claude-code、Lowy-DP01-codex、Lowy-DP01-antigravity)。這是文件/客戶端層面的約定,而非伺服器強制的限制 — 伺服器會照單全收你傳送的任何主機名稱,因此這種自律必須由呼叫方來保證。
無效的SK不會回傳401,而是在data[0]中表現為RstVal: 401({"Proc_MSG":"401|Unauthorized - Invalid SK",...})。
CQEQueueGet — 輪詢此lssn的佇列一次
請求:
text=CQEQueueGet lssn hostname os op
token=<sk>
jsondata={"lssn":71290,"hostname":"Lowy-DP01-claude-code","os":"Windows 10","op":"op"}
回應,有任務:
{"data":[{"RstVal":"200","ms_body":"<script or payload text>","mslsn":8032,"script_type":"sh","mssn":123}]}
回應,佇列為空(實際驗證,2026-09-06)— 這是成功,不是錯誤:
{"data":[{"RstVal":"404","ProcName":"[pCQEQueueGetbySK02] no queue 2"}]}
$ python scripts/giip_agent.py poll --lssn 71290 --tool-slug claude-code
{"success": true, "lssn": 71290, "has_work": false}
RstVal意義(已透過審查giipdb/SP/pApiCQEQueueGetbySK.sql確認,2026-09-06):
RstVal | 意義 |
|---|---|
200 | 佇列中有任務;ms_body/mslsn/mssn/script_type均已填入 |
201 | 對全新lssn的首次輪詢觸發了隱式重新註冊路徑 — 應視為「尚無任務」,稍後再次輪詢 |
0或404 | 已檢查,目前沒有排隊任務 — 正常,非錯誤 |
| 其他值 | 真正的錯誤 |
建議輪詢間隔:60秒。 與實際giipAgentLinux安裝透過cron使用的間隔(* * * * *)一致。此API本身不對輪詢進行速率限制,但沒有理由輪詢得更快 — 佇列是按GIIP自身的排程填充的。
錯誤的lssn與「無任務」表現相同。 如果輪詢的lssn不屬於該SK的csn,尋找待處理任務的連接查詢(tMgmtScriptList ms inner join tLSvr ls on ... where ls.csn = @csn and ms.lssn = @lssn)根本找不到任何列,回傳的RstVal: 404與空佇列相同。這裡沒有單獨的「權限拒絕」或「lssn不存在」回應 — 如果懷疑輪詢了錯誤的lssn,請重新執行register並比對。
KVSPut(kFactor=cqeresult)— 回報結果
沒有專門的「完成/確認」命令。結果是透過KVSPut寫入的,這是用於GIIP所有代理遙測資料的同一通用鍵值機制。
請求:
text=KVSPut kType kKey kFactor
token=<sk>
jsondata={
"kType": "lssn", "kKey": "71290", "kFactor": "cqeresult",
"kValue": {"mslsn": 8032, "mssn": 123, "lssn": 71290,
"status": "success", "exit_code": 0,
"stdout": "...", "stderr": ""}
}
kType必須是字串字面量"lssn",kKey必須是結果所屬的lssn(字串形式)。
與register/poll不同,此呼叫會檢查所有權並乾淨俐落地失敗(已透過審查giipdb/SP/pApiKVSPutbySk.sql確認,2026-09-06):SP在寫入前會檢查EXISTS(SELECT 1 FROM tLSvr WHERE LSsn = @kKey AND CGSn = <從sk推導>)。如果lssn不屬於該SK的金鑰群組:
{"data":[{"RstVal":411,"RstMsg":"..."}]}
對於report,任何非200的RstVal都應視為嚴重失敗 — 不要將其當作心跳重試,也不要悄悄丟棄該結果。
沒有取消註冊(unregister/deregister)端點
沒有用於刪除邏輯機器的呼叫。停止傳送心跳的機器只會逐漸「過期」(LSLastHeartbeat不再更新)— 在API層面沒有「告別」機制。
🔍 回應標準
無論呼叫三個命令中的哪一個,giipApiSk2呼叫始終將結果包裝在data陣列中:
{ "data": [ { "RstVal": 200, ... } ] }
- 成功:
RstVal為200(在AgentAutoRegister/CQEQueueGet的邊緣情況下偶爾為201)。RstVal根據命令不同可能是數字或數字字串——本規範的客戶端腳本將其作為字串比較以同時處理這兩種情況。 - poll的「無任務」:
RstVal為0或404— 成功,不是失敗。 - 失敗:其他任何
RstVal,並附帶說明原因的RstMsg/Proc_MSG/ProcName。 - 傳輸層失敗(網路錯誤、逾時,或非200的HTTP狀態):意味著請求根本沒有到達預存程序 — 這與
RstVal失敗是完全不同的失敗模式。
🤖 AI代理的完整流程(註冊 → 輪詢 → 回報)
export GIIP_API_KEY="<the SK>"
# 1. 註冊(冪等 — 首次呼叫建立lssn,之後使用相同hostname的
# 呼叫都是對該lssn的心跳)
python scripts/giip_agent.py register --tool-slug claude-code
# {"success": true, "hostname": "Lowy-DP01-claude-code", "lssn": 71290, "action": "new"}
# 2. 輪詢一次。按自己的排程(約60秒)重複 -- 不要在內部迴圈。
python scripts/giip_agent.py poll --lssn 71290 --tool-slug claude-code
# {"success": true, "lssn": 71290, "has_work": false}
# 3. 若has_work為true,處理ms_body後回報結果。
python scripts/giip_agent.py report --lssn 71290 --mslsn 8032 --mssn 123 \
--status success --exit-code 0
⚠️ 重要說明
- 這是與問題管理API不同的API系列。 請勿混淆兩者:
giip-issue的客戶端將SK作為x-api-key請求標頭傳送給具有真實HTTP狀態碼的REST端點;giip-agent的客戶端將SK作為token表單欄位傳送給始終回傳HTTP 200的單一調度端點。 - 主機名稱規則是客戶端層面的約束,而非伺服器約束。 沒有任何機制阻止您為兩個不同的工具都註冊
foo兩次 — 但這樣做會產生兩個相互混淆佇列歷史的lssn。請嚴格遵循<host>-<tool-slug>規則。 - 即使系統資訊負載發生變化,重複的
register仍然是心跳 — 不是新機器。決定身分的只有hostname字串。
疑難排解
| 症狀 | 原因 | 解決方法 |
|---|---|---|
AgentAutoRegister的data[0].RstVal為401 | SK無效或已重新簽發 | 在/svclist重新核實SK |
poll始終回傳RstVal 404/has_work: false | 要嘛確實沒有排隊任務,要嘛lssn屬於與此SK不同的csn | 重新執行register,比較回傳的lssn |
report回傳RstVal 411 | lssn不屬於此SK的金鑰群組(cgsn) | 僅針對自己register呼叫回傳的lssn進行回報 |
| HTTP狀態本身不是200 | 傳輸層失敗(網路問題,或路由器本身拒絕了格式錯誤的請求) | 與RstVal失敗不同 — 在確認連線後重試是合理的(RstVal失敗則不應重試) |
原以為是一台機器,卻出現兩個不同的lssn | 主機名稱建構不一致(使用了不同的--tool-slug,或使用了裸主機名稱覆寫) | 主機名稱應始終按<host>-<tool-slug>建構,並在各次呼叫間保持穩定 |
版本:1.0
最後更新:2026-09-06(在giipfaw生產環境實際驗證,csn 47,結果lssn 71290)
來源檔案:giipv3/public/help/giip-agent-api.zh-TW.md
v1.0(2026-09-06,giip #2081):首次發布。基於對
giipdb/SP/pApiAgentAutoRegisterBySK.sql、pApiCQEQueueGetbySK.sql、pApiKVSPutbySk.sql、giipfaw/giipApiSk2/run.ps1的原始碼審查,以及giipAgentLinux(scripts/giip-auto-discover.sh、lib/cqe.sh、lib/kvs.sh、cqe/giipCQE.sh)的生產客戶端行為,記錄了giipApiSk2調度端點上的AgentAutoRegister、CQEQueueGet和KVSPut(kFactor=cqeresult)。實際驗證:一次register(新建lssn 71290)、一次重複register(心跳,相同lssn)、一次poll(無任務)。
相關文件: