|
@@ -0,0 +1,385 @@
|
|
|
|
|
+# 数字员工状态上报 · 字段规范(机器人侧必读)
|
|
|
|
|
+
|
|
|
|
|
+| 字段 | 值 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| 文档编号 | 规范-20261020-reporter-fields |
|
|
|
|
|
+| 版本 | v1.0 |
|
|
|
|
|
+| 创建日期 | 2026-10-20 |
|
|
|
|
|
+| 适用范围 | **所有容器(数字员工)内的状态上报脚本** |
|
|
|
|
|
+| 权威源 | 原版 `status-report.py`(v11)+ 云函数 `xlosi3m46q` v0.1.2 白名单 |
|
|
|
|
|
+| 成对维护声明 | 云函数源码注释:「白名单与容器侧 `USAGE_FIELDS` 成对维护」——改一边必须改另一边 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 零、一句话总纲
|
|
|
|
|
+
|
|
|
|
|
+> **机器人不"打包上报",而是"逐字段上报"**:每个字段一次请求,
|
|
|
|
|
+> 字段名与值的形状**必须严格等于本文档的契约**;云函数按白名单校验,白名单外直接 `400`。
|
|
|
|
|
+
|
|
|
|
|
+三类最常犯的错(实测于 `agent-node-lj-test`):
|
|
|
|
|
+
|
|
|
|
|
+| 错法 | 后果 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `channels` 发**数组** `["wecom"]` | App 判 `matrix` 为 `undefined` → 显示「未接入」 |
|
|
|
|
|
+| `host` 发**裸负载** `cpuLoad1`,无 `cpuPercent` | CPU 指标不显示 |
|
|
|
|
|
+| 上报走 `AlP56LCKFm`(OBS 上传) | 数据进存储桶、**没进 FmodeAgent 表** |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 一、上报协议
|
|
|
|
|
+
|
|
|
|
|
+### 1.1 传输(唯一正确姿势)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+POST https://server.fmode.cn/api/functions
|
|
|
|
|
+Content-Type: application/json
|
|
|
|
|
+
|
|
|
|
|
+{
|
|
|
|
|
+ "id": "xlosi3m46q", ← 状态上报云函数(写死)
|
|
|
|
|
+ "token": "<FMODE_SESSION_TOKEN>", ← 主人的会话令牌,用于鉴权归属
|
|
|
|
|
+ "params": {
|
|
|
|
|
+ "agentId": "<你的 agentId>",
|
|
|
|
|
+ "field": "<字段名>", ← 白名单内的字段名
|
|
|
|
|
+ "value": <字段值> ← 形状见 §三
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**关键**:
|
|
|
|
|
+- 🔴 `id` 必须是 `xlosi3m46q`。用 `AlP56LCKFm` 是**上传 OBS 日志**,完全不同的事。
|
|
|
|
|
+- 🔴 **一次请求只报一个字段**。不存在「整包提交」的接口。
|
|
|
|
|
+- `params.companyId` 可选(cron 注入账套场景);无公司容器不用传。
|
|
|
|
|
+
|
|
|
|
|
+### 1.2 值序列化规则
|
|
|
|
|
+
|
|
|
|
|
+| 值类型 | 序列化 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| 对象 / 数组型字段(`host` / `channels` / `balance`)| **紧凑 JSON 字符串**(`json.dumps(v, ensure_ascii=False, separators=(",",":"), sort_keys=True)`)|
|
|
|
|
|
+| `heartbeatAt` | ISO 时间戳,**必须带类型包装**:`{"__type":"Date","iso":"2026-10-20T11:05:34+08:00"}` |
|
|
|
|
|
+| 其余标量 | 原样(字符串 / 数字)|
|
|
|
|
|
+
|
|
|
|
|
+⚠️ `host`/`channels`/`balance` 上送的是**字符串**(服务端该列是 String 类型),
|
|
|
|
|
+不是 JSON 对象 —— 但字符串里的内容必须是 §三 的结构。
|
|
|
|
|
+
|
|
|
|
|
+### 1.3 上报节奏:**变化检测 + 心跳**
|
|
|
|
|
+
|
|
|
|
|
+原版逻辑(务必照抄,别改成"每次全量上报"):
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+每次采集:
|
|
|
|
|
+ ① 与上次快照逐字段比对
|
|
|
|
|
+ ② 只上报「值发生变化」的字段
|
|
|
|
|
+ ③ 若本次无任何变化,且距上次上报 ≥ HEARTBEAT_SECS(默认 30s)
|
|
|
|
|
+ → 只上报一次 heartbeatAt(证明"我还活着")
|
|
|
|
|
+ ④ 失败字段写入本地队列,下次先 flush 重试
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**为什么不能全量上报**:10 容器 × 每分钟 × 20 字段的冗余写会打垮云函数(配额与延迟)。
|
|
|
|
|
+
|
|
|
|
|
+⚠️ **变化检测的经典坑**(原版踩过,见 v10 注释):
|
|
|
|
|
+`currentTask` 任务结束后**没人清除 env** → 值未变 → 变化检测判定"无需上报"
|
|
|
|
|
+→ `FmodeAgent.currentTask` **永久残留** → 看板假亮「进行中任务」。
|
|
|
|
|
+→ **任务结束时必须显式上报 `currentTask=""`**。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 二、字段白名单(云函数 `FIELDS`)
|
|
|
|
|
+
|
|
|
|
|
+白名单外一律 `400 字段 X 不允许通过该接口更新`:
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+status currentTask currentAction progress name containerRef host heartbeatAt
|
|
|
|
|
+botName botAvatar gender ownerName ownerMobile balance channels extra sessionStatus
|
|
|
|
|
+usage usageSessions
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+> `usage` / `usageSessions` 为 v0.1.0 新增(只增不删,平台规则 17)。
|
|
|
|
|
+> 容器侧 `USAGE_FIELDS` 与云函数 `USAGE_FIELDS` **成对维护**,改一边必须改另一边。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 三、逐字段契约(🔴 本文档核心)
|
|
|
|
|
+
|
|
|
|
|
+### 3.1 `host` —— 主机指标
|
|
|
|
|
+
|
|
|
|
|
+**必须是 JSON 对象字符串**,键名固定:
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{"cpuPercent": 16.4, "memPercent": 51.0, "diskPercent": 33.3}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 键 | 类型 | 取值口径 | 允许缺省 |
|
|
|
|
|
+|---|---|---|---|
|
|
|
|
|
+| `cpuPercent` | number | **CPU 使用率百分比** `0~100` | ❌ 必报 |
|
|
|
|
|
+| `memPercent` | number | 内存使用率 `0~100` | ❌ 必报 |
|
|
|
|
|
+| `diskPercent` | number | 磁盘使用率 `0~100`(`df -P <工作目录>`)| ❌ 必报 |
|
|
|
|
|
+
|
|
|
|
|
+**🔴 `cpuPercent` 怎么算 —— 实测口径(原版 v11 源码 `collect_slice`)**:
|
|
|
|
|
+
|
|
|
|
|
+```python
|
|
|
|
|
+# 原版:读 top 的 us+sy 真实占用率,不加换算
|
|
|
|
|
+out = sh("top -bn1 | grep -iE '^%?Cpu' | head -1")
|
|
|
|
|
+parts = out.replace(",", " ").split()
|
|
|
|
|
+nums = [p for p in parts if p.replace(".", "").isdigit()]
|
|
|
|
|
+cpu = float(nums[0]) # ← 第一个数字就是 CPU%
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+内存:`/proc/meminfo` 的 `(MemTotal - MemAvailable) / MemTotal * 100`
|
|
|
|
|
+磁盘:`df -P <WORK> | tail -1 | awk '{print $5}'` 去掉 `%`
|
|
|
|
|
+
|
|
|
|
|
+**❌ 禁止的错法**:
|
|
|
|
|
+- 发 `cpuLoad1` / `cpuLoad5`(负载均值,**不是**占用率)
|
|
|
|
|
+- 用 `cpuLoad1 / 核数 * 100` 自己换算 —— 这是**近似**,与 `top` 实测值不同;
|
|
|
|
|
+ 只在拿不到 `top` 时作为**降级**,且必须**显式标注来源**
|
|
|
|
|
+- 自己心算一遍值就算"验证通过"(**没有落库就等于没做**)
|
|
|
|
|
+
|
|
|
|
|
+**❌ 禁止**:上报`hostname` / `platform` / `uptimeSeconds` 等**非契约键**当作主键 ——
|
|
|
|
|
+云函数不认。需要附加信息请放 `extra` 字段。
|
|
|
|
|
+
|
|
|
|
|
+**降级路径**(`top` 不可用时):
|
|
|
|
|
+```
|
|
|
|
|
+1. /proc/stat 两次采样差分(最准)
|
|
|
|
|
+2. os.getloadavg()[0] / os.cpu_count() * 100(近似,需标注)
|
|
|
|
|
+3. 完全拿不到 → host 里的 cpuPercent 缺省,**不要编造**
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3.2 `channels` —— 通道接入
|
|
|
|
|
+
|
|
|
|
|
+**必须是 JSON 对象字符串**(🔴 **不是数组**):
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "homeserver": "https://matrix.fmode.cn",
|
|
|
|
|
+ "matrix": true,
|
|
|
|
|
+ "wecom": true,
|
|
|
|
|
+ "wecomBotId": "aibF7SNq4qV2dNwHypwp-eGUNPelYqiJ3kV",
|
|
|
|
|
+ "matrixConfig": {
|
|
|
|
|
+ "homeserver": "https://matrix.fmode.cn",
|
|
|
|
|
+ "userId": "@agent-node-lj-test:fmode.cn",
|
|
|
|
|
+ "serverName": "fmode.cn",
|
|
|
|
|
+ "channelState": "connected",
|
|
|
|
|
+ "listenerOk": true,
|
|
|
|
|
+ "channelDetail": {"matrixState": "connected", "gatewayState": "running", "wecomState": "connected"}
|
|
|
|
|
+ },
|
|
|
|
|
+ "wecomConfig": {"botId": "aibF7...", "connected": true}
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 键 | 类型 | 语义 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `matrix` | boolean | **Matrix 通道是否点亮** ← App 靠它判「已接入」 |
|
|
|
|
|
+| `homeserver` | string | Matrix 服务器地址 |
|
|
|
|
|
+| `matrixConfig.userId` | string | 容器自己的 MXID(`@xxx:fmode.cn`)|
|
|
|
|
|
+| `matrixConfig.channelState` | string | `"connected"` = 通道在跑 |
|
|
|
|
|
+| `matrixConfig.listenerOk` | boolean | 旧字段名保留,语义 = 官方 channel connected |
|
|
|
|
|
+| `wecom` | boolean | 企微通道是否点亮(**需同时有 `wecomBotId`**)|
|
|
|
|
|
+| `wecomBotId` | string | 企微机器人 BotID |
|
|
|
|
|
+
|
|
|
|
|
+**🔴 点亮规则**(不能只看 env,要**真探测**):
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+matrix = (官方 node 的 platforms.matrix.state == "connected") AND (mxid 存在)
|
|
|
|
|
+wecom = (WECOM_ENABLED == "true") AND (WECOM_BOT_ID 非空)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**❌ 禁止**:`["wecom"]` 这种数组;未探测就凭 env 声明「已接入」。
|
|
|
|
|
+矩阵没真的连上却报 `matrix:true` → 用户点进会话**连不上**,比报 `false` 更糟。
|
|
|
|
|
+
|
|
|
|
|
+### 3.3 `balance` —— 余额
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{"remainCny": 17842.31, "usedCny": 1203.5, "totalCalls": 8842, "source": "session"}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| `source` | 含义 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `"session"` | ✅ 正常:用 `FMODE_SESSION_TOKEN` 调余额接口成功 |
|
|
|
|
|
+| `"apikey"` | 旧 API_KEY 通道兜底 |
|
|
|
|
|
+| `"none"` | ⚠️ 拿不到(token 失效 / 主人无记账)→ **显式声明拿不到,不要省略字段** |
|
|
|
|
|
+
|
|
|
|
|
+**🔴 实测坑**:容器 `sessionToken` 过期(一小时前签发、次月失效)→ 接口 `401
|
|
|
|
|
+{"code":401,"mess":"登录已失效,请重新登录"}` → 只能降级 `source:"none"`。
|
|
|
|
|
+**这需要重登换新 token,不是改脚本能解决的**。
|
|
|
|
|
+
|
|
|
|
|
+> ⚠️ 注意与 §3.2 的 token 区分:`channels` 的判定**不需要** token(本地探测);
|
|
|
|
|
+> `balance` **必须**有有效 token。所以 token 过期时 **channels 正常、balance 降级**,
|
|
|
|
|
+> 这是正常的,不要因为 balance 失败就整个上报退出。
|
|
|
|
|
+
|
|
|
|
|
+### 3.4 `usage` / `usageSessions` —— Token 用量
|
|
|
|
|
+
|
|
|
|
|
+从 Hermes 的 `state.db` **只读**聚合(`mode=ro`,避免抢写锁):
|
|
|
|
|
+
|
|
|
|
|
+```python
|
|
|
|
|
+sqlite3.connect(f"file:{HERMES_STATE_DB}?mode=ro", uri=True, timeout=5)
|
|
|
|
|
+SELECT id, title, model, message_count, tool_call_count,
|
|
|
|
|
+ input_tokens, output_tokens, cache_read_tokens, reasoning_tokens,
|
|
|
|
|
+ estimated_cost_usd, cost_status, started_at
|
|
|
|
|
+FROM sessions ORDER BY started_at DESC LIMIT <N>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 约束 | 说明 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| 打不开 / 无 sqlite3 | **跳过该字段,不阻断其它字段上报** |
|
|
|
|
|
+| 时间字段 | 原样上报 epoch 数字,服务端统一转 ISO |
|
|
|
|
|
+| 无变化 | 不上报(避免冗余)|
|
|
|
|
|
+
|
|
|
|
|
+**🔴 实测**:容器有 `sessionmodelusage` 表(2118 万 token,42 行),
|
|
|
|
|
+数据**取得到**,只是**没接进上报链路** —— 这是"缺代码"不是"缺数据"。
|
|
|
|
|
+
|
|
|
|
|
+### 3.5 `heartbeatAt` —— 心跳
|
|
|
|
|
+
|
|
|
|
|
+每次上报必须刷新。**必须带类型包装**:
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+"value": {"__type": "Date", "iso": "2026-10-20T11:05:34+08:00"}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**节奏**:有字段变化时随变化上报;**无变化时按 `HEARTBEAT_SECS`(默认 30s)单独上报一次**。
|
|
|
|
|
+
|
|
|
|
|
+⚠️ 只上报 `host.probedAt` 而**不报 `heartbeatAt`** 是错的 —— App 判「心跳超时」会
|
|
|
|
|
+把在线容器显示成离线(App 侧已加回落兼容,但**不应依赖兼容**)。
|
|
|
|
|
+
|
|
|
|
|
+### 3.6 其余字段
|
|
|
|
|
+
|
|
|
|
|
+| 字段 | 类型 | 说明 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `status` | string | `running` / `idle` / `error` / `offline` / `stopped` |
|
|
|
|
|
+| `currentTask` | string | 当前任务;**任务结束必须报空串清除**(见 §1.3 坑)|
|
|
|
|
|
+| `currentAction` | string | 当前动作 |
|
|
|
|
|
+| `progress` | number | `0~100` |
|
|
|
|
|
+| `name` | string | 容器名(`AGENT_NAME`,缺省 hostname)|
|
|
|
|
|
+| `containerRef` | string | 容器标识(`HOSTNAME`)|
|
|
|
|
|
+| `botName` / `botAvatar` / `gender` | string | 机器人身份 |
|
|
|
|
|
+| `ownerName` / `ownerMobile` | string | 主人身份 |
|
|
|
|
|
+| `sessionStatus` | string | Agent 自身 token 健康灯:`ok` / `expired` / `unknown` |
|
|
|
|
|
+| `extra` | string | 白名单外原始数据的 JSON 字符串 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 四、环境变量契约
|
|
|
|
|
+
|
|
|
|
|
+| env | 必填 | 说明 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `AGENT_ID` | ✅ | 容器 agentId。**缺省会退化成 hostname hash → 云函数判 `agent not found` → 数据不上报** |
|
|
|
|
|
+| `FMODE_SESSION_TOKEN` | ✅ | 上报鉴权 + 归属判定 + 余额采集 |
|
|
|
|
|
+| `AGENT_FUNCTION_ID` | ✅ | 填 `xlosi3m46q`(**填错就走到 OBS 上传**)|
|
|
|
|
|
+| `WECOM_BOT_ID` | 条件 | 企微 BotID,有企微通道时必填 |
|
|
|
|
|
+| `WECOM_ENABLED` | 条件 | `"true"` 时企微通道才可能点亮 |
|
|
|
|
|
+| `MATRIX_*` | 条件 | Matrix 身份信息(`MATRIX_USER_ID` 等)|
|
|
|
|
|
+| `AGENT_STATUS_HEARTBEAT` | 可选 | 心跳间隔秒,默认 `30` |
|
|
|
|
|
+| `AGENT_STATUS_ENABLED` | 可选 | `false` 则整体停用 |
|
|
|
|
|
+| `AGENT_STATUS_BACKEND` | 可选 | 默认 `https://server.fmode.cn/api/fmodeagent/status`(旧 REST,服务端转发到云函数)|
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 五、🔴 自检规范:**只许回读,不许自算**
|
|
|
|
|
+
|
|
|
|
|
+这是本轮实测暴露的**最严重方法论错误**:
|
|
|
|
|
+
|
|
|
|
|
+> 容器自述「cpuPercent 算法已验证 = 16.4」,
|
|
|
|
|
+> 但平台库里 `host` **连 `cpuPercent` 这个键都没有**。
|
|
|
|
|
+
|
|
|
|
|
+**原因**:它在调试里手算了一遍,那个值**从未进入上报体**。
|
|
|
|
|
+「我算过」≠「已落库」。
|
|
|
|
|
+
|
|
|
|
|
+### 正确的验收流程
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+# ① 上报一次
|
|
|
|
|
+python3 <真实路径>/status-report.py
|
|
|
|
|
+
|
|
|
|
|
+# ② 回读平台值
|
|
|
|
|
+curl -s 'https://server.fmode.cn/parse/classes/FmodeAgent?where={"agentId":"<你的agentId>"}' \
|
|
|
|
|
+ -H 'X-Parse-Application-Id: ncloudmaster' | jq '.results[0] | {host, channels, heartbeatAt, usage, balance, lastField}'
|
|
|
|
|
+
|
|
|
|
|
+# ③ 逐条断言(缺一不可)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| # | 断言 | 通过标准 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| 1 | `host` 含 `cpuPercent` | 是数字且 `0~100` |
|
|
|
|
|
+| 2 | `host` 含 `memPercent` / `diskPercent` | 同上 |
|
|
|
|
|
+| 3 | `channels` 是**对象** | 能被 JSON 解析为 `{...}`,不是 `[...]` |
|
|
|
|
|
+| 4 | `channels.matrix` | 与真实通道状态一致 |
|
|
|
|
|
+| 5 | `channels.wecomBotId` | 等于 `WECOM_BOT_ID` |
|
|
|
|
|
+| 6 | `heartbeatAt` | 最近 60 秒内 |
|
|
|
|
|
+| 7 | `balance.source` | `"session"`(若是 `"none"` 需说明原因)|
|
|
|
|
|
+| 8 | `usage` | 有会话记录 |
|
|
|
|
|
+| 9 | `lastField` | 持续变化(证明在写)|
|
|
|
|
|
+
|
|
|
|
|
+**任一条不满足 → 报告「字段未落库:<字段名>」,
|
|
|
|
|
+禁止报告"已完成/已验证"。**
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 六、排查规范:**查运行态,不查路径**
|
|
|
|
|
+
|
|
|
|
|
+**本轮教训**:容器按预设路径 `find status-report.py` 找不到 → 得出「没部署」结论。
|
|
|
|
|
+但平台库显示它在**持续上报**(44 秒前更新)。
|
|
|
|
|
+
|
|
|
|
|
+> **「找不到文件」≠「进程没在跑」。**
|
|
|
|
|
+
|
|
|
|
|
+正确的运行态排查:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+# ① 找进程(不是找文件)
|
|
|
|
|
+ps aux | grep -iE "status|report" | grep -v grep
|
|
|
|
|
+
|
|
|
|
|
+# ② 看进程实际跑的是哪个脚本
|
|
|
|
|
+ls -l /proc/<pid>/cmdline && cat /proc/<pid>/cmdline | tr '\0' ' '
|
|
|
|
|
+ls -l /proc/<pid>/cwd # 实际工作目录
|
|
|
|
|
+
|
|
|
|
|
+# ③ 定位到真实路径后再改
|
|
|
|
|
+grep -n "cpuPercent\|channels" <真实路径>
|
|
|
|
|
+
|
|
|
|
|
+# ④ 反查:平台侧读回值里带容器自证字段(hostname / uptime)
|
|
|
|
|
+# host.hostname == 本机 hostname → 确认"就是我在上报"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 七、App 侧兼容现状(供参考,**不应依赖**)
|
|
|
|
|
+
|
|
|
|
|
+App 已加兼容层(`core/api/parse-api.ts` `normalize*` 函数),能兜住:
|
|
|
|
|
+
|
|
|
|
|
+| 兼容项 | 处理 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `channels` 数组 | `["matrix","wecom"]` → 对象 |
|
|
|
|
|
+| `cpuLoad1` | 有 `cpuCores` 时派生 `cpuPercent`;**核数未知则不派生** |
|
|
|
|
|
+| `heartbeatAt` 缺失 | 回落 `host.probedAt` → `updatedAtClient.iso` |
|
|
|
|
|
+
|
|
|
|
|
+🔴 **但这是"止痛药"不是"解药"**:
|
|
|
|
|
+- 兼容层无法还原**没上报的数据**(如 `usage` 压根没发,App 也变不出来)
|
|
|
|
|
+- 数组形态丢失了 `matrixConfig` 的细节(`channelState` / `listenerOk`)
|
|
|
|
|
+- 派生 `cpuPercent` 与 `top` 实测值**有偏差**
|
|
|
|
|
+
|
|
|
|
|
+→ **容器侧改成标准格式才是根治**。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 八、验收清单(贴进部署提示词)
|
|
|
|
|
+
|
|
|
|
|
+```markdown
|
|
|
|
|
+□ 进程在跑:ps aux | grep status-report 有输出
|
|
|
|
|
+□ 手动跑一次 → 每个字段返回 {"code":200,"field":"<字段名>",...}
|
|
|
|
|
+□ 回读 host.cpuPercent 是 0~100 的数字(不是 cpuLoad1)
|
|
|
|
|
+□ 回读 channels 能被解析为对象(不是数组)
|
|
|
|
|
+□ 回读 channels.matrix 与真实通道一致
|
|
|
|
|
+□ 回读 heartbeatAt 在 60 秒内
|
|
|
|
|
+□ 回读 balance.source 为 "session" 或已说明为何 "none"
|
|
|
|
|
+□ 回读 usage 有会话记录
|
|
|
|
|
+□ 上报调用的是 xlosi3m46q,不是 AlP56LCKFm
|
|
|
|
|
+□ 任务结束会清 currentTask(不残留)
|
|
|
|
|
+□ token / 密钥 未明文落盘、未入日志
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 九、关联文档
|
|
|
|
|
+
|
|
|
|
|
+- `docs/03-interface/容器初始化流程优化建议-20261020.md`(初始化流程优化 + 回复容器提示词)
|
|
|
|
|
+- `docs/03-interface/reporter部署方案-20261020.md`(部署步骤)
|
|
|
|
|
+- `docs/02-design/architecture/账号与账套体系设计.md`(归属模型)
|
|
|
|
|
+- 云函数 `xlosi3m46q`(状态上报,`Function` 表 v0.1.2)
|
|
|
|
|
+- 原版脚本:`E:\work\fmode-app\docs\03-interface\reporter-v3\status-report.py`(v11,49410 字节)
|
|
|
|
|
+- 原版部署说明:同目录 `部署说明书.md` / `给容器Hermes的部署提示词.md`
|