giip
SES 商机登记
6分钟阅读

代理注册与队列API规范

本文档提供将AI代理(或任意主机)注册为GIIP逻辑机器、轮询其自身CQE任务队列并报告执行结果的技术规范 — 与生产环境中giipAgentWin/giipAgentLinux实际使用的机制相同。

🔌 前往giip-agent技能包 →

📋 概述

此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)— 而非参数
tokenSK,作为普通表单字段发送。不是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-codeLowy-DP01-codexLowy-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的首次轮询触发了隐式重新注册路径 — 应视为"尚无任务",稍后再次轮询
0404已检查,当前没有排队任务 — 正常,非错误
其他值真正的错误

推荐轮询间隔: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,任何非200RstVal都应视为严重失败 — 不要将其当作心跳重试,也不要悄悄丢弃该结果。

没有注销(unregister/deregister)端点

没有用于删除逻辑机器的调用。停止发送心跳的机器只会逐渐"过期"(LSLastHeartbeat不再更新)— 在API层面没有"告别"机制。


🔍 响应标准

无论调用三个命令中的哪一个,giipApiSk2调用始终将结果包装在data数组中:

{ "data": [ { "RstVal": 200, ... } ] }
  • 成功RstVal200(在AgentAutoRegister/CQEQueueGet的边缘情况下偶尔为201)。RstVal根据命令不同可能是数字或数字字符串——本规范的客户端脚本将其作为字符串比较以同时处理这两种情况。
  • poll的"无任务"RstVal0404 — 成功,不是失败。
  • 失败:其他任何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字符串。

故障排查

症状原因解决方法
AgentAutoRegisterdata[0].RstVal401SK无效或已重新签发/svclist重新核实SK
poll始终返回RstVal 404/has_work: false要么确实没有排队任务,要么lssn属于与此SK不同的csn重新运行register,比较返回的lssn
report返回RstVal 411lssn不属于此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-CN.md

v1.0(2026-09-06,giip #2081):首次发布。基于对giipdb/SP/pApiAgentAutoRegisterBySK.sqlpApiCQEQueueGetbySK.sqlpApiKVSPutbySk.sqlgiipfaw/giipApiSk2/run.ps1的源代码审查,以及giipAgentLinuxscripts/giip-auto-discover.shlib/cqe.shlib/kvs.shcqe/giipCQE.sh)的生产客户端行为,记录了giipApiSk2调度端点上的AgentAutoRegisterCQEQueueGetKVSPut(kFactor=cqeresult)。实际验证:一次register(新建lssn 71290)、一次重复register(心跳,相同lssn)、一次poll(无任务)。


相关文档