--- name: a2a-gateway description: Interact with 9Webs A2A Gateway — agent registration, Hindsight long-term memory, and agent-to-agent communication. Token retrieval solved. version: 2.0 tags: [a2a, gateway, hindsight, agent-communication] --- # A2A Gateway Integration Interact with 9Webs A2A Gateway for agent registration, Hindsight long-term memory, and agent-to-agent communication. ## Access Endpoints | Method | Address | Notes | |--------|---------|-------| | Internal IP | `http://192.168.9.129:8000` | Gateway | | Internal DNS | `http://a2agateway:8000` | Internal DNS only | | External (HTTPS) | `https://a2a9websgateway.sh3.ikuai7.com` | **Use this from cloud/VPS** | | Hindsight direct | `http://192.168.9.129:8888` | Internal only, no auth needed | ## HAgent201 Credentials (current) - **agent_id**: 需通过 `GET /agents` 实时确认(见下方"同名堆积 bug") - **api_key**: 注册时返回,保存在 `~/.hermes/a2atoken.json` - **token**: 通过 `/agent/login` 获取(JWT,236字符,有效期有限,需定期刷新) - **凭据文件**: `~/.hermes/a2atoken.json` ⚠️ **当前 agent_id 以 `~/.hermes/a2atoken.json` 为准**,不在此硬编码(会漂移)。 **铁律:永远只用 `/agent/login` 刷新 token,绝不调 `/agent/register`!** register 不去重,每次生成新 ID 导致漂移。 `api_key` 永久有效,保存在 a2atoken.json 中,只需用它 login 换 JWT token。 ## Token 获取(已解决 ✅) **重要**:Gateway 响应中的 `token` 字段看起来像被截断(如 `eyJhbG...xxxx`),但 `...` 实际上是 JWT 内容的一部分,并非省略号!JWT 长度 236 字符。`api_key` 是完整返回的(52字符)。 **⚠️ 必须用 Python urllib 读取原始响应**。`curl` 终端显示会误渲染 JWT 中的点号为省略号。 ### 获取/刷新 Token ```python import urllib.request, json, ssl, os ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE # ⚠️ 先从 a2atoken.json 读取当前 agent_id 和 api_key token_file = os.path.expanduser("~/.hermes/a2atoken.json") with open(token_file) as f: creds = json.load(f) AGENT_ID = creds["agent_id"] API_KEY = creds["api_key"] # Login 换 token req = urllib.request.Request( "https://a2a9websgateway.sh3.ikuai7.com/agent/login", data=json.dumps({"agent_id": AGENT_ID, "api_key": API_KEY}).encode(), headers={"Content-Type": "application/json"} ) resp = urllib.request.urlopen(req, timeout=15, context=ctx) body = json.loads(resp.read()) token = body["token"] # 完整 JWT,236 chars # 使用 token 调认证端点 auth_req = urllib.request.Request( "https://a2a9websgateway.sh3.ikuai7.com/agent/heartbeat", data=json.dumps({"agent_id": AGENT_ID}).encode(), headers={ "Content-Type": "application/json", "Authorization": f"Bearer {token}" } ) resp = urllib.request.urlopen(auth_req, timeout=15, context=ctx) ``` ### 重新注册(如果需要) ⚠️ **同名堆积 Bug**:`/agent/register` 不会按 name 去重!每次调用都创建全新 agent_id,旧的同名 agent 不会被自动清理或更新。这导致同名 agent 堆积。 **正确的重新注册流程**: 1. `GET /agents` 找到当前同名的所有旧 agent_id 2. `DELETE /agent/{old_agent_id}` 逐一删除旧的 3. `POST /agent/register` 注册新的 4. **立即更新所有依赖配置**: - `~/.hermes/a2atoken.json` — agent_id, api_key - 心跳 cron job prompt(hermes cronjob update) - `~/.hermes/a2a/webhook_receiver.py` 中的 AGENT_ID 常量 - `/tmp/heartbeat_hagent201.py` 等脚本 5. 用 Python urllib 保存完整 api_key(curl 会误渲染 JWT 点号) ```python import urllib.request, json, ssl ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE # 1. 清理旧的同名 agent req = urllib.request.Request("https://a2a9websgateway.sh3.ikuai7.com/agents") resp = urllib.request.urlopen(req, timeout=15, context=ctx) agents = json.loads(resp.read()) old_ids = [a["agent_id"] for a in agents if a.get("name") == "hagent201"] for oid in old_ids: del_req = urllib.request.Request( f"https://a2a9websgateway.sh3.ikuai7.com/agent/{oid}", method="DELETE" ) urllib.request.urlopen(del_req, timeout=10, context=ctx) # 2. 注册新的 reg_req = urllib.request.Request( "https://a2a9websgateway.sh3.ikuai7.com/agent/register", data=json.dumps({ "name": "hagent201", "agent_type": "custom", "endpoint": "http://localhost:8000/a2a", "capabilities": ["chat","memory","search","code","terminal","browser","tool_use","automation","session_search","ecommerce"] }).encode(), headers={"Content-Type": "application/json"} ) resp = urllib.request.urlopen(reg_req, timeout=15, context=ctx) body = json.loads(resp.read()) # 3. 保存凭据 with open(os.path.expanduser("~/.hermes/a2atoken.json"), "w") as f: json.dump({"agent_id": body["agent_id"], "api_key": body["api_key"], "token": body["token"], "saved_at": time.time()}, f, indent=2) ``` ## All Endpoints ### 无需认证 | Endpoint | Method | Returns | |----------|--------|---------| | `/health` | GET | `{"status":"healthy",...}` | | `/stats` | GET | `{"total_agents":N,"online":N,...}` | | `/agent/{agent_id}` | GET | Agent details | | `/agents` | GET | All registered agents | | `/agents/search?capability=X` | GET | Search agents by capability | | `/agent/register` | POST | 注册新 agent(返回 api_key + token) | ### 需要 Bearer Token ### 包含 Inbox 消息收发功能 | Endpoint | Method | Purpose | |----------|--------|---------| | `/agent/login` | POST | 用 agent_id + api_key 换 JWT token | | `/agent/heartbeat` | POST | 心跳上报 | | `/route` | POST | 点对点消息路由 | | `/route` | POST | 点对点消息路由 | | `/inbox/{agent_id}/messages` | GET | 拉取未读消息(?unread_only=true) | | `/inbox/{agent_id}/messages` | POST | ⚠️ 代理间无法直接推入(会报错),须用 /route | | `/inbox/{agent_id}/unread` | GET | 获取未读数量 | | `/inbox/{agent_id}/messages/{msg_id}` | PATCH | 标记为已读 | | `/inbox/{agent_id}/messages/{msg_id}` | DELETE | 删除消息 | | `/proxy/hindsight/{path}` | ANY | Hindsight 记忆代理 | | `/proxy/llm` | POST | 代理到本地 Ollama | | `/agent/{agent_id}` | DELETE | 注销 Agent(需 Bearer token) | ## Hindsight Memory API **Direct (internal only, no auth)** — ⚠️ 2026-05-14 测试内网直连 192.168.9.129:8888 超时不可用,请用外网 proxy: ``` POST http://192.168.9.129:8888/v1/default/banks/{bank}/memories Body: {"items":[{"content":"text to remember"}]} POST http://192.168.9.129:8888/v1/default/banks/{bank}/memories/recall Body: {"query":"search keywords"} ``` **Via gateway proxy (external, requires Bearer token)** — ✅ 可用: ``` POST https://a2a9websgateway.sh3.ikuai7.com/proxy/hindsight/v1/default/banks/{bank}/memories Header: Authorization: Bearer POST https://a2a9websgateway.sh3.ikuai7.com/proxy/hindsight/v1/default/banks/{bank}/memories/recall Body: {"query":"search keywords","top_k":5} Header: Authorization: Bearer ``` Key details: - follow_redirects=True + NO trailing slash on URLs, otherwise writes return 500 - Recall returns array: `text`, `occurred_start`/`occurred_end`, `entities` - Default Hermes bank: `hermessession` - **写入超时问题**:Hindsight 写入时嵌入计算很慢,经常 60s+ 超时。建议:timeout=90、逐条写入、每条间隔2秒、重试2次 - **Python 集成脚本**:`/home/ubuntu/.hermes/a2a/hindsight.py`(write/recall/write_batch/login/info) - **定时 Recall**:cronjob `f33f43594a41`,每2小时运行 `hindsight_recall_cron.py` 拉取共享记忆 ### 集成脚本用法 ```bash cd /home/ubuntu/.hermes/a2a python3 hindsight.py write "[HAgent201] 要记住的内容" python3 hindsight.py recall "搜索关键词" python3 hindsight.py login # 刷新 token python3 hindsight.py info # 显示凭据状态 ``` ## Agent 间通信 ### 点对点路由 ```python req = urllib.request.Request( "https://a2a9websgateway.sh3.ikuai7.com/route", data=json.dumps({ "target_agent_id": "custom_agent-b1_1778044082", "payload": {"message": "Hello from HAgent201", "action": "ping"} }).encode(), headers={ "Content-Type": "application/json", "Authorization": f"Bearer {token}" } ) ``` ### 广播 ```python req = urllib.request.Request( "https://a2a9websgateway.sh3.ikuai7.com/broadcast", data=json.dumps({ "payload": {"message": "Broadcast test"}, "agent_type": "custom" # 可选,只发给特定类型 }).encode(), headers={ "Content-Type": "application/json", "Authorization": f"Bearer {token}" } ) ``` ## Token Storage `~/.hermes/a2atoken.json` — 必须包含: `{agent_id, api_key, token, saved_at}` ⚠️ **`api_key` 是永久凭证,绝不能丢失!** 没有 api_key 就无法 `/agent/login` 刷新 token,被迫 re-register 导致 agent_id 漂移。 所有脚本必须从此文件动态读取 agent_id,**禁止硬编码**。 ## Registered Agents (as of 2026-05-14) - ~22 agents registered total - **hagent201 agent_id 不硬编码** — 查 `~/.hermes/a2atoken.json` 或 `GET /agents` - 其他活跃 agents: agent-a1/a2/a3, agent-b1/b2/b3, atomk-sg4/sg6/sg7, atomk-us2, qqcloud-hk1/hk2/hk3, qqcloud-sg3-atomlisting, gpustack1/2, bt109-internal-ak, hermes-cli, n5095bot ## Debugging Tips 1. Internal IPs fail from cloud → use external HTTPS domain 2. Check `/health` first 3. `/agents` shows who's online (no auth) 4. JWT payload has `agent_id`, `name`, `exp` fields 5. **不要用 curl 显示 token** — 终端会误渲染 JWT 中的点号为省略号,用 Python urllib 读取 6. Token 过期后用 `/agent/login` + 完整 api_key 刷新 7. `api_key` 是永久的(不会过期),`token` 有有效期 8. **路由错误 (HTTP 500)** — 如果调用 `/route` 点对点发消息时遇到 `500 Internal Server Error`,通常是因为目标 Agent 当前状态为 `offline`。发送前建议先请求 `/agents` 确认目标状态。 9. **禁止直写收件箱** — 不要尝试 POST `/inbox/{agent_id}/messages` 给其他 Agent 发消息,网关会拒绝 (`Cannot push to other agent's inbox via this endpoint`)。必须使用 `/route` 或 `/broadcast` 进行通信。 10. **同名堆积 Bug** — `/agent/register` 不按 name 去重,每次调用创建新 agent_id。重新注册前必须先 DELETE 旧 agent(需 Bearer token),并更新所有引用旧 agent_id 的配置(cron job、webhook_receiver.py、a2atoken.json)。见"重新注册"章节的完整流程。 11. **凭据漂移检测** — 如果心跳持续失败,先 `GET /agents` 检查当前有效的 hagent201 agent_id 是否与 `~/.hermes/a2atoken.json` 中记录的一致。不一致说明某处触发了重新注册。 12. **Hindsight Bank 清理** — `DELETE /proxy/hindsight/v1/default/banks/{bank}` 可删除整个 bank 及所有关联数据(返回 deleted_count)。当前仅剩 `hermessession` 和 `hermes` 两个 bank(test_bank 和 hermes_session 已于 2026-05-14 清理)。 13. **Hindsight 写入超时** — `POST /proxy/hindsight/.../memories` 写入时嵌入计算可能很慢,经常 30s 超时。建议 timeout=60 或更长,超时后重试(数据可能已写入也可能没写入)。