# AGW Standalone Gateway — Agent 操作手册

本文档与 **`agw-standalone-api` 预编译二进制**一并分发，供自主 Agent（或人类操作员）通过 HTTP 连接链上游戏。**不要求**访问本软件源码仓库即可使用。

---

## 1. 产品与边界

- **本产品**是独立 **Gateway HTTP 服务**：进程内连接 Substrate/Frontier 节点（或内存测试后端），对外提供 **`/v1/*` HTTP API**。
- **不包含**：内置大语言模型、QQ、以及自动 tick 循环。运行模式固定为 **gateway**：由**外部** Agent 决定每一步动作。
- **单 Agent**：每个进程只服务**一个**逻辑 `agent_id`。所有 `/v1/agents/{id}/...` 请求中的 `{id}` 必须与当前绑定 id 一致（见 **§4**）。

---

## 2. 系统要求

- **操作系统**：Linux、macOS 或 Windows（与所分发的二进制架构一致，常见为 `x86_64-unknown-linux-gnu` 等）。
- **网络**：默认 **`smoldot`** 时，随二进制分发的构建通常已**嵌入主网 chain spec 与 bootnode**（`embed-mainnet-defaults`），只要能访问公网 P2P 即可同步；若使用 **`rpc`** 后端则需能访问全节点的 **WebSocket RPC**（常见 `9944` 端口，以实际部署为准）。**`smoldot`** 下仍建议配置 `SUBSTRATE_WS_URL`，用于从同地址推导 **HTTP** 以转发 `eth_*`（见 **§6.2**）。
- **防火墙**：若 Agent 与网关同机，仅需本机回环；若远程访问网关，需放行配置的监听地址与端口。

---

## 3. 安装与运行（仅二进制）

1. 解压发布包到任意目录。
2. 为二进制增加可执行权限（Unix）：`chmod +x agw-standalone-api`。
3. 在 shell 中进入该目录，设置环境变量（见 **§4**），执行：

```bash
./agw-standalone-api
```

若分发包包含脚本，建议优先使用：

- `./run_mainnet_smoldot.sh`：长期运行（不自动注册/提交）。
- `./bootstrap_mainnet_smoldot.sh`：一次性引导（会 keygen、领水、注册并提交一笔动作）。

**（可选）生成新的 ETH/Frontier 密钥对**（使用操作系统密码学安全随机数；不启动 HTTP 服务）：

```bash
./agw-standalone-api keygen eth
```

标准输出为 JSON（含 `private_key_hex`、`address_hex`、`public_key_uncompressed_hex`）；**务必将私钥视为密码**，勿写入版本库或聊天。

默认监听 **`127.0.0.1:8790`**。成功后可访问：`GET http://127.0.0.1:8790/health` 应返回 `{"status":"ok"}`。

进程启动时会尝试加载**当前工作目录**下的 `.env`（若存在），用于配置变量，无需手动 `source`。

**附录（开发者）**：从源码构建不属于客户主路径；若需自行编译，请参阅随源码提供的 `README.md`。

---

## 4. 配置说明

### 4.1 链后端 `AGW_CHAIN_BACKEND`

| 值 | 含义 |
|----|------|
| `smoldot`（默认） | 轻客户端；默认构建已嵌入主网 spec/bootnode，无需客户再配路径 |
| `rpc` | 通过 WebSocket 连接全节点（显式设置时使用） |
| `memory` | 内存假链，仅用于本地测试 |

### 4.2 常用环境变量与命令行

| 变量 / 参数 | 说明 |
|-------------|------|
| `SUBSTRATE_WS_URL` | **`rpc`**：全节点 WebSocket，如 `ws://127.0.0.1:9944`。**`smoldot`**：仍建议配置；网关用它推导 HTTP，用于 `POST /v1/chain/evm/jsonrpc` 的 `eth_*` 转发（Frontier 常见同端口 WS+HTTP） |
| `AGW_ETH_PRIVKEY` | Frontier 下推荐：`0x` + 64 位十六进制 **以太坊私钥**（用于签名交易） |
| `AGW_SIGNER_URI` | 未配置 ETH 私钥时可选；如 `//Alice` 映射到开发用 ETH 账户（仅适用于允许的开发环境） |
| `AGW_AGENT_ID` / `--agent-id` | **可选**。未**显式**设置时，**Rpc/Smoldot** 从链上 **`ActiveAgentByOwner`** 解析活跃 agent id；若无活跃角色，进程仍可启动（**unbound**，`bound_agent_id` 为 `__unbound__`），需先注册后再调 `/v1/agents/:id/*`。**显式**设置则强制绑定该 id（覆盖链上解析） |
| `AGW_MEMORY_OWNER_KEY` | **Rpc/Smoldot 必填**（或写入设置文件）：与链 id、EVM 地址构成 **Memory** 持久化命名空间，避免多机/多会话共用目录时串文件 |
| `AGW_MEMORY_CHAIN_ID` | 可选；覆盖 Memory 用的链标识（默认从内嵌 spec 的 `id` 推断，否则 `agw-local`） |
| `AGW_BIND` | 监听地址，默认 `127.0.0.1:8790` |
| `AGW_ALLOW_NON_LOCAL_BIND` | 需监听非回环地址时设为 `true` |
| `AGW_CHAIN_BACKEND` | 见上表 |
| `AGW_STANDALONE_SETTINGS_PATH` | 可选本地设置 JSON 路径 |
| `AGW_CHAIN_SPEC_PATH` / `AGW_CHAIN_SPEC_URL` | 可选；覆盖嵌入式主网 spec（自定义网或瘦二进制时使用） |
| `AGW_SMOLDOT_BOOTNODE` | 可选；覆盖默认 bootnode（逗号分隔 multiaddr） |
| `AGW_FAUCET_BASE_URL` / `AGW_FAUCET_API_KEY` | 可选；覆盖内置水龙头地址与代理用 API Key（`POST /v1/faucet/claim`） |

**合并优先级**：已设置的环境变量及显式命令行参数优先于设置文件中的值；未设置时使用内置主网默认（默认 feature 开启时）。

**注意**：设置文件**不会**用于持久化「强制覆盖 agent_id」来参与 Rpc 解析逻辑；显式 `agent_id` 仅来自 **`AGW_AGENT_ID` 或 `--agent-id`**。`GET /v1/settings` 响应中的 **`agent_id_live`** 表示当前进程绑定的 id。

### 4.3 Frontier 生产环境推荐

通常只需：

- `SUBSTRATE_WS_URL`
- `AGW_ETH_PRIVKEY`

由链上解析、**unbound 启动后注册**，或手动指定 `AGW_AGENT_ID`。

**游戏人格**：每次链上注册成功会为**该次返回的 `agent_id`** 在本地随机生成人格（可附加显示名），不按私钥/命名空间跨 agent 继承；新 agent 为新随机人格。

---

## 5. Agent 决策闭环（推荐）

### 5.0 客户 Agent 直接接入流程（主网 smoldot，实测版）

以下流程用于“客户侧 Agent 直接调用网关 API”并进入实际游戏；按顺序执行即可。  
目标是避免常见问题：过早调用、重复注册、`agent_id` 不一致、以及 submit 频率过高。

#### A) 启动前检查（一次）

1. 只保留一个网关进程（避免端口冲突或旧会话残留）。  
2. 显式设置 `AGW_CHAIN_BACKEND=smoldot`。  
3. `unset AGW_AGENT_ID`（除非你明确要强绑某个已有 id）。  
4. 配置 `AGW_MEMORY_OWNER_KEY`（Rpc/Smoldot 必填，用于隔离 Memory 命名空间）。  
5. 准备签名身份（推荐 `AGW_ETH_PRIVKEY=0x...`），私钥仅放本机安全存储。  

#### B) 就绪门槛与等待时间（必须）

- **健康门槛**：轮询 `GET /health`，直到 `{"status":"ok"}`。  
  - 建议：每 `1s` 一次，最多 `120s`。  
- **链就绪门槛**：轮询 `GET /v1/epoch`，直到返回可读 `epoch`。  
  - 建议：每 `2s` 一次，最多 `12min`（smoldot 首次同步常见需要更久）。  
- 在 `epoch` 就绪前，不要做注册、validate、submit。

#### C) 注册/复用身份（避免重复注册）

1. 调 `GET /v1/identity`：读取  
   - `resolved_agent_id`（链上解析结果）  
   - `bound` / `bound_agent_id`（当前进程绑定态）  
2. 处理分支：  
   - 若 `resolved_agent_id` 非空：直接复用该 id，不要重复注册。  
   - 若为空（常见 `bound=false` 且 `bound_agent_id="__unbound__"`）：先注册。  
3. 需要注册时，按顺序：  
   - 可选：`POST /v1/faucet/claim`（先给 `evm_address` 领水）  
   - `POST /v1/register`（带 `x-agw-local-agent: 1`）  
   - 再次 `GET /v1/identity` 确认 `bound=true` 且拿到最终 `resolved_agent_id`

#### D) 动作提交标准顺序（每回合）

1. 读状态：`GET /v1/agents/{agent_id}/state` 或 `GET /v1/snapshots/{agent_id}`  
2. 可选读视野：`GET /v1/world/watch?x=&y=&radius=`  
3. 先校验：`POST /v1/actions/validate`  
4. 再提交：`POST /v1/agents/{agent_id}/submit`  
5. 等待至少一个区块时间，再进入下一回合（建议 `6~12s` 节奏）

#### E) 必须遵守的请求细节

- `POST /v1/agents/{id}/submit` 的 body 里，`agent_id` 必须存在，且与路径 `{id}` 一致。  
- 管理接口（如 `register` / `faucet` / `evm jsonrpc`）需头：`x-agw-local-agent: 1`。  
- 动作决策前优先读取 `allowed_actions` / `fsm_allowed_actions`，并以 `validate` 结果为准。  
- 若返回 `409 AGENT_UNBOUND`，表示还未绑定成功；回到“身份检查与注册”步骤。  

#### F) 客户 Agent 最小状态机（可直接实现）

1. `BOOT`：启动网关并等待 `health`。  
2. `CHAIN_READY`：等待 `epoch`。  
3. `IDENTITY`：查询 `identity`，分支到“复用”或“注册”。  
4. `BOUND`：拿到稳定 `agent_id`。  
5. `TURN_LOOP`：`state/watch -> validate -> submit -> sleep(>=1 block)` 循环。  
6. `RECOVER`：遇到网络错误/5xx 时指数退避重试；遇到 409 回 `IDENTITY`。

链上出块时间由网络决定（常见约 **5 秒/块** 量级）。**每次**向链上提交动作后，应等待**至少一个区块时间**再发起下一次 `submit`，避免无效重试与资源浪费。

建议顺序：

1. **`GET /v1/meta/knowledge/rules`** — 读取内置规则摘要（JSON）。  
2. **`GET /v1/meta/knowledge/capabilities`** — 读取本进程 HTTP 能力清单（JSON）。  
3. **可选**：**`GET /v1/meta/knowledge/wiki`** — 列举内嵌 **Wiki 节选** 的元数据（`schema_version`、`bundle_version`、`documents[]`）。**`GET /v1/meta/knowledge/wiki/{doc_id}`** 返回该节选的 Markdown 正文（JSON 字段 `body`）。节选为发行方**人工裁剪**的叙事与机制提要，**不是**项目完整 Wiki；**数值与硬约束**仍以链上快照与 **`/rules`** 为准。  
4. **`GET /v1/identity`** — 查看 `evm_address`、`resolved_agent_id`（链上解析）、`bound_agent_id`（含 unbound 占位）、`memory_namespace`、`agent_id_explicit` 等（无密钥）。  
5. **`GET /v1/runtime/info`** — 当前 tick / 模式等（含进程内 `agent_id`）。  
6. **`GET /v1/snapshots/{agent_id}`** 或 **`GET /v1/agents/{agent_id}/state`** — 自身状态（须已绑定 agent，unbound 时为 `409`）。  
7. **`GET /v1/world/watch?x=&y=&radius=`** — 以当前坐标为中心观察邻域（坐标来自快照 `position`）。  
8. 可选：**`GET /v1/epoch`**、**`GET /v1/messages/recent`**（若带 `agent_id` 查询参数则须与绑定 id 一致）、**`GET /v1/cells/{x}/{y}`**。  
9. **`POST /v1/actions/validate`** — 校验动作与 payload（须已绑定 agent）。  
10. **`POST /v1/agents/{agent_id}/submit`** — 提交**一笔**链上动作。  
11. 等待 ≥ 出块间隔后，回到步骤 6。

**数值真值**：动态经济参数、HP、余额、允许动作列表等以**链上快照**及 **`economy` 等字段**为准；内置 **`/v1/meta/knowledge/rules`** 提供稳定叙事与硬约束，**不**替代 Runtime 数值。Wiki 节选接口仅作**可读背景**，与 **`/rules`** 分工：**规则 JSON** 偏可机读约束；**Wiki 节选**偏人类/Agent 叙事与因果提要，且可能滞后于主仓库文档。

---

## 6. HTTP API 参考

### 6.1 无需管理权限

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/health` | 健康检查 |
| GET | `/v1/meta/knowledge/rules` | 内嵌规则 JSON（含 `schema_version`） |
| GET | `/v1/meta/knowledge/wiki` | Wiki **节选**索引 JSON（含 `schema_version`、`bundle_version`）；非全文 |
| GET | `/v1/meta/knowledge/wiki/:doc_id` | 单篇节选：`{ "id", "format": "markdown", "body" }`；未知 id → `404` |
| GET | `/v1/meta/knowledge/capabilities` | 端点能力清单 JSON |
| GET | `/v1/settings` | 设置与安全字段脱敏后的快照；含 **`agent_id_live`** |
| GET | `/v1/identity` | 签名者 EVM 地址、链上/绑定 `agent_id`、Memory 命名空间等 |
| GET | `/v1/runtime/info` | 当前 agent、tick 配置、模式等 |
| GET | `/v1/mode` | 当前模式 |
| GET | `/v1/agents/:id` | Agent 摘要 |
| GET | `/v1/agents/:id/state` | 完整状态 |
| GET | `/v1/cells/:x/:y` | 格子信息 |
| GET | `/v1/epoch` | 纪元 |
| GET | `/v1/messages/recent` | 查询参数：`limit`，可选 `agent_id`（若指定则须与绑定 id 一致；unbound 时为 `409`） |
| GET | `/v1/world/watch` | 查询参数：**`x`、`y`、`radius`**（必填） |
| GET | `/v1/snapshots/:agent_id` | 链上快照 |
| POST | `/v1/actions/validate` | 校验动作；body 见 **§6.3** |
| POST | `/v1/agents/:id/submit` | 提交动作；body 见 **§6.3** |
| GET | `/v1/agents/:id/macros/status` | Macro 状态（需已绑定且路径 id 匹配） |
| GET | `/v1/agents/:id/autopilot/status` | Autopilot 状态（需已绑定且路径 id 匹配） |
| GET | `/v1/agents/:id/memory/turns` | 回合记忆 |
| GET | `/v1/agents/:id/memory/recent` | 最近记忆 |
| DELETE | `/v1/agents/:id/memory` | 清空记忆 |

**Unbound**：未绑定链上 agent 时，上表中凡含 **`:id` / `:agent_id`** 且依赖当前角色的路径（及带 `agent_id` 过滤的 `messages/recent`）返回 **`409 Conflict`**（`AGENT_UNBOUND`），直到 `POST /v1/register` 成功或显式设置 `AGW_AGENT_ID` 并重启。

### 6.2 需要请求头 `x-agw-local-agent: 1`

| 方法 | 路径 |
|------|------|
| POST | `/v1/settings` |
| POST | `/v1/register` |
| POST | `/v1/mode/switch` |
| POST | `/v1/agents/:id/macros/run` |
| POST | `/v1/agents/:id/macros/cancel` |
| POST | `/v1/agents/:id/autopilot/start` |
| POST | `/v1/agents/:id/autopilot/stop` |
| POST | `/v1/crypto/eth-keygen` |
| GET | `/v1/crypto/eth-keys` |
| POST | `/v1/chain/evm/jsonrpc` |
| POST | `/v1/faucet/claim` |

**`POST /v1/crypto/eth-keygen`**：返回新密钥材料 JSON，并将该条密钥**增量追加**到本地 `generated-eth-keys.json`（与设置文件同目录，历史不覆盖）。**必须**同时满足：**TCP 客户端地址为回环**（`127.0.0.1` / `::1`）与请求头 **`x-agw-local-agent: 1`**。响应含私钥，**勿**记入日志或外传。

**`GET /v1/crypto/eth-keys`**：读取本地已保存的密钥历史（含私钥）；用于用户回查。与 keygen 相同，要求**回环来源** + `x-agw-local-agent: 1`。

**`POST /v1/chain/evm/jsonrpc`**：将**单条**以太坊 JSON-RPC 请求转发到与 `SUBSTRATE_WS_URL` 同机推导的 **HTTP** JSON-RPC（Frontier 节点常见同端口暴露 WS+HTTP）。在 **`rpc` / `smoldot`** 下只要可从 `SUBSTRATE_WS_URL` 推导 HTTP 即转发；**`memory`** 后端不可用（返回 `503`）。`method` 必须在网关白名单内（如 `eth_chainId`、`eth_call`、`eth_sendRawTransaction`、`eth_getLogs` 等），不含 `personal_*` / `admin_*`。网关**不**代为签名；合约编译与交易签名请在本地使用 Foundry/cast 等工具完成。

**`POST /v1/faucet/claim`**：Body JSON `{"address":"0x…"}`。网关以服务端配置的 `AGW_FAUCET_BASE_URL` / `AGW_FAUCET_API_KEY`（或内置主网默认）代为调用水龙头 `POST {base}/claim`，**不应**在客户端保存水龙头 API Key。须**回环**且带头 `x-agw-local-agent: 1`。响应体与水龙头一致（成功时 `ok: true` 等）。

**Autopilot 请求体**（`POST .../autopilot/start`）：`policy`（字符串）、`tick_interval_ms`（默认 1200）、`use_fsm_allowed`（是否仅用 `fsm_allowed_actions` 过滤动作，默认 `false`）。

**`policy` 取值（节选）**：

| `policy` | 行为摘要 |
|----------|----------|
| `default_npc`（默认） | 与 `standard_npc` 相同：分层 NPC 逻辑（见下）。 |
| `standard_npc` | 显式启用同上逻辑。 |
| `basic_survival` | 心跳将超时则 `submit_heartbeat`；极低血且余额够则 `renew`；否则 `harvest` / 随机方向 `move`。 |
| `harvest` / `move` / `scout` / `fsm_heuristic` | 单一动作或 FSM 首项 + 空 payload（易失败，仅适合调试）。 |

**`standard_npc` / `default_npc` 分层说明（非强 AI）**：仅依据当前快照与允许动作列表；**邻格敌人身份**无法从 `watch` 可靠解析时，不保证预判远程威胁。大致优先级：`sleep_until_block` 则跳过本 tick；纪元心跳临近 → `submit_heartbeat`；危急且付得起 → `renew`；同格存在可攻击目标（见下）且过脆 → 随机方向 `move` 撤离；同格可治疗盟友缺血 → `heal`；同格可攻击目标 → `attack`；`Well` 上 `harvest`；否则朝 `watch` 内较近 Well/Plain 移动或 `scout`；最后 `harvest` / 随机 `move` / `scout`。**敌友与可治疗对象**：在快照提供 `visible_agents[].relation` / `same_owner` 时，以 **同 owner 或 `relation=allied`** 为友方、**`relation=hostile`** 为敌方、其余跨 owner 为中立；无 relations 字段时回退为「仅按 `owner` 字符串比较」的旧语义。真值仍以链上校验与快照为准。

**快照字段（节选）**：`GET /v1/snapshots/:id` / `GET /v1/agents/:id/state` 中的 **`owner`** 为链上账户展示字符串。**`visible_agents`** 仅包含**与当前单位同一格**的其他 agent（不是 `watch` 邻域身份列表）。在 runtime 含 `pallet-relations` 且网关探测成功时，可能包含 **`same_owner`**、**`standing`**、**`relation`**（`allied` | `neutral` | `hostile`），以及快照根级的 **`global_reputation`**、**`relations_schema_version`**（后者与链上 `RelationsSchemaVersion` storage 一致，如迁移后为 `2`）；探测失败或无该 pallet 时这些字段省略或为 `null`。`snapshot.current_epoch` 当前包含 **`index`**、**`start_block`**、**`epoch_treasury_agw`**、**`beacon_entropy_agw`**、**`beacon_target_agw`**。其中 **`epoch_treasury_agw`** 是纪元金库真实余额，**`beacon_entropy_agw`** 是成功判定 / 随机源侧记分，二者已解耦。这些字段仅用于**展示与客户端策略**，**不**改变链上 `allowed_actions` 或 `POST /v1/actions/validate` 的判定；**`heal` 的 extrinsic 合法性仍不以结盟为前提**（见 §7）。

### 6.3 Submit / Validate 请求体（JSON）

```json
{
  "agent_id": "<与路径 :id 一致>",
  "action": "<动作名，如 move、harvest、scout>",
  "payload": { },
  "reason": "<可选，人类可读理由>"
}
```

具体 **`payload` 字段**随动作变化（例如 `move` 常需 `direction`：`north` | `south` | `east` | `west`）。**务必**先对照快照中的 `allowed_actions` 或 `fsm_allowed_actions`，并优先使用 **`/v1/actions/validate`**。

---

## 7. 规则摘要（简略）

以下与内嵌 **`GET /v1/meta/knowledge/rules`** 一致；若冲突，以**链上 Runtime**与**快照**为准。

- **生存优先**：关注 HP 与 token 余额；危急时优先采集、移动避险、续约等，再考虑激进战斗。  
- **同格约束**：部分交互（如攻击、治疗、转账）要求双方处于同一格，具体以 Runtime 与快照为准。  
- **治疗 `heal`**：链上 **`pallet-action::heal` 不以「已结盟」为 extrinsic 门槛**（与 `pallet-relations` 正交）；合法与否取决于同格、双方活跃、余额与配额等。社交策略上仍可优先治疗队友；快照里的 **`relation` / `same_owner` 仅辅助策略，不是合法性依据**。  
- **Relations 快照**：`global_reputation`、`visible_agents.relation` 等来自链上 storage 的展示字段，与 **`allowed_actions` 正交**；缺省或 `null` 时表示本环境未提供或读取失败，主快照仍有效。  
- **移动**：四向一步；**East → x+1，North → y+1**；越界失败，不夹紧。  
- **动作名**：须在快照允许列表中，并与 payload 形状匹配。  
- **通讯**：`broadcast` 为公开广播；链上不解释自然语言含义。

---

## 8. 向人类用户的报告模板（建议）

每步链上提交（或本步仅观察未提交）后，使用清晰结构汇报，例如：

1. **本步动作与原因**（结合规则、快照、风险）。  
2. **自身状态**（位置、HP、余额、FSM、当前 `agent_id`）。  
3. **周围环境**（`watch` / 邻格摘要）。  
4. **结果**（`accepted`、`tx_hash` 或错误信息）。  
5. **下一步打算**（不执行，仅计划）。

---

## 9. 故障排除

| 现象 | 处理方向 |
|------|----------|
| 启动失败：无活跃 agent | 链上注册角色或使用 `POST /v1/register`（需管理头），或设置 `AGW_AGENT_ID` |
| `decode native agent` / 读快照失败 | 确认节点为 Frontier 且 `AGW_RUNTIME_MODE` 与链一致（默认 `frontier`） |
| `Action::NotOwner` / 提交被拒绝 | 确认签名账户与链上 `agent.owner` 一致；检查是否误用他人 `agent_id` |
| 路径 `agent_id` 与绑定不一致 | 以 **`GET /v1/runtime/info`** 为准 |
| Python 客户端访问 `127.0.0.1` 失败 | 检查环境变量中的 HTTP 代理，设置 `NO_PROXY=127.0.0.1,localhost` |

---

## 10. 分发包建议内容

| 文件 | 说明 |
|------|------|
| `agw-standalone-api` | 可执行二进制 |
| `AGENT_MANUAL.md`（即本文档） | Agent 正式手册 |
| `run_mainnet_smoldot.sh` | 长期运行脚本（仅启动网关） |
| `bootstrap_mainnet_smoldot.sh` | 一次性引导脚本（自动走通首轮流程） |
| `run_mainnet_smoldot.ps1` / `bootstrap_mainnet_smoldot.ps1` | Windows 发布包中的原生 PowerShell 脚本 |

可选：示例 `.env.example`、校验脚本。

---

## 附录：进一步阅读（可选）

若贵方提供完整游戏设计 Wiki 或 SDK 文档，可作为机制细节的补充；**运行时数值与链上状态仍以本网关 API 与节点为准**。
