feat(agent): align HM to AM's coding_a2a_agent contract
Adapt HM's template-agent integration to AM's actual CODING_A2A API (per their
doc), keeping it isolated in agent_template_runtime.go:
- start payload -> AM's POST /agents { name, template:"coding_a2a_agent",
framework:"A2A", config:{user_id,...}, env } with the template .md folded into
env.AGENT_INSTRUCTION_TEXT, template_key -> AGENT_ROLE_NAME, model gateway via
OPENAI_BASE_URL + MODEL_NAME (OPENAI_API_KEY left to the client per A2A request).
- response parse -> access_info.domain/external_ip -> subdomain, namespace/name
-> runtime_id; AM issues no access_token (client uses A2A api_key).
- env names aligned to AM: GIT_DEFAULT_BRANCH, POSTGRES_* (was PG_*),
AZURE_BLOB_ACCOUNT_NAME/CONTAINER/ACCOUNT_KEY (was BLOB_*); source keys aligned
to the resource-binding form (db_name/username/database_password/access_key).
Only AM-supported types (git/mysql/postgres/azure-blob); vm/redis/mongo/bucket
now rejected as unsupported until AM adds them.
- frontend: resources page splits DB into MySQL/PostgreSQL (correct provider),
drops vm; deploy page hides unsupported resource types.
- docs: AM contract + client doc updated to the real env names, payload, and the
A2A direct-connect (message/send · message/stream) + api_key auth.
- tests updated for the new env names + AM payload/response shape. All green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,41 +16,40 @@
|
||||
|
||||
HM 用 `Authorization: Bearer <service_token>` 调 AM。统一响应信封建议 `{ "success": true, "data": {...} }`(HM 会从 `data` 取值;失败给 `{ "success": false, "message": "..." }`)。
|
||||
|
||||
### 1.1 启动 agent(核心)
|
||||
`POST {AM}/api/agent/agents/start`
|
||||
### 1.1 启动 agent(核心,已对齐你们的 `POST /agents`)
|
||||
`POST {AM}/agents`
|
||||
|
||||
**请求体(HM 发):**
|
||||
**请求体(HM 实际发送的,已按你们 CODING_A2A §2 对齐):**
|
||||
```json
|
||||
{
|
||||
"manager_deployment_id": "dep_b5fab27e9255", // HM 侧记录 id,回调/排查用
|
||||
"template_key": "architect", // 模板标识
|
||||
"agent_definition": "---\nname: architect\nmodel: opus\n...\n---\n<Agent_Prompt>...</Agent_Prompt>",
|
||||
"model": "opus", // 从模板 frontmatter 解析(可空)
|
||||
"env": { // 资源配置,注入 agent 的 .env(见 §2)
|
||||
"GIT_REPO_URL": "https://github.com/owner/repo",
|
||||
"GIT_TOKEN": "ghp_…",
|
||||
"VM_HOST": "1.2.3.4", "VM_USER": "deploy", "VM_PASSWORD": "…"
|
||||
"name": "dep-b5fab27e9255", // 由 HM 部署 id 派生(DNS 安全)
|
||||
"template": "coding_a2a_agent",
|
||||
"framework": "A2A",
|
||||
"config": {
|
||||
"user_id": "22",
|
||||
"manager_deployment_id": "dep_b5fab27e9255",
|
||||
"callback_url": "https://code.xinghanlab.com/api/agent/callbacks/runtime-events"
|
||||
},
|
||||
"callback_url": "https://code.xinghanlab.com/api/agent/callbacks/runtime-events"
|
||||
"env": {
|
||||
"AGENT_ROLE_NAME": "architect",
|
||||
"AGENT_INSTRUCTION_TEXT": "---\nname: architect\n---\n<Agent_Prompt>...</Agent_Prompt>",
|
||||
"OPENAI_BASE_URL": "https://code.xinghanlab.com/v1",
|
||||
"MODEL_NAME": "gpt-5.4",
|
||||
"GIT_PROVIDER": "github", "GIT_REPO_URL": "...", "GIT_TOKEN": "…",
|
||||
"MYSQL_HOST": "...", "MYSQL_PASSWORD": "…"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应 data(AM 返回):**
|
||||
```json
|
||||
{ "runtime_id": "rt_xxx", "subdomain": "https://abc123.agents.example.com", "access_token": "…", "status": "running" }
|
||||
```
|
||||
- `runtime_id`:AM 侧实例 id(HM 存,用于后续 status/stop/delete)。HM 也接受 `agent_id`/`id`/`deployment_id` 作为别名。
|
||||
- `subdomain`:agent 对外**唯一可直连地址**(完整 URL 最佳)。HM 也接受 `address`/`url` 别名。
|
||||
- `access_token`:客户端连 agent 时出示的令牌(见 §3)。HM 也接受 `token` 别名。
|
||||
- `status`:`running`/`starting`/… HM 也接受 `runtime_status` 别名。
|
||||
**响应(HM 解析你们返回的字段):**
|
||||
- `runtime_id` ← `runtime_id`/`agent_id`/`id`/`name`/`namespace`(取到的实例标识,用于 status/stop/delete)。
|
||||
- `subdomain` ← `subdomain` 或 `access_info.domain` / `access_info.external_ip`(agent 直连地址)。
|
||||
- `access_token` ← `access_token`/`token`(你们当前不返回 → HM 留空,客户端用 A2A `api_key`,见 §3)。
|
||||
- `status` ← `status`/`runtime_status`(取不到默认 `running`)。
|
||||
|
||||
**AM 启动时必须做的:**
|
||||
1. 用 `agent_definition`(.md:frontmatter + 系统提示)作为该 agent 的**角色/行为定义**(等价 Claude Code subagent)。
|
||||
2. 把 `env` 注入 agent 的 `.env`/进程环境,**agent 只读 env 即知道有哪些资源**。
|
||||
3. 分配唯一子域名 + 生成 access_token,**agent 端自行校验该 token**(§3)。
|
||||
4. 该 agent 用模型时**调 HM 的 `/v1/*`**(带用户身份,HM 计费)——不要直连上游模型。
|
||||
**AM 启动时做的(你们已实现):**用 `env.AGENT_INSTRUCTION_TEXT`+`AGENT_ROLE_NAME` 设角色;注入 env;agent 用模型走 `OPENAI_BASE_URL`(=HM `/v1`)。
|
||||
|
||||
> ⚠️ **provisioning 是同步的**:HM 启动调用超时默认 60s(`AGENT_RUNTIME_START_TIMEOUT_SECONDS`),请在该时间内完成起容器+分配地址并返回。
|
||||
> ⚠️ provisioning 同步:HM 超时默认 60s(`AGENT_RUNTIME_START_TIMEOUT_SECONDS`)。
|
||||
|
||||
### 1.2 取 agent 状态
|
||||
`GET {AM}/api/agent/agents/{runtime_id}`
|
||||
@@ -68,19 +67,20 @@ HM 用 `Authorization: Bearer <service_token>` 调 AM。统一响应信封建议
|
||||
|
||||
HM 把用户绑定的资源按**资源类型 + provider**解成固定 env 名(代码 `controller/agent_template_env.go`)。**AM 的 agent 模板按这些名字读 env**;用户在 HM 只填业务字段、看不到 env 名。
|
||||
|
||||
> **2026-06-04 已对齐 AM 的 `coding_a2a_agent`**:env 名按你们文档实现。当前**只支持 AM 已支持的 4 类**(git / mysql / postgres / azure blob);vm / redis / mongo / 对象桶等 HM 暂不下发,待 AM 支持再加。
|
||||
|
||||
| 资源类型 | provider | 注入的 env(非密 + 密钥) |
|
||||
|---|---|---|
|
||||
| git | github / gitea / gitlab | `GIT_PROVIDER` `GIT_REPO_URL` `GIT_BRANCH` `GIT_API_BASE` · `GIT_TOKEN`(密) |
|
||||
| vm | (ssh) | `VM_HOST` `VM_PORT` `VM_USER` · `VM_PASSWORD` / `VM_SSH_KEY`(密) |
|
||||
| git | github / gitea / gitlab | `GIT_PROVIDER` `GIT_REPO_URL` `GIT_DEFAULT_BRANCH` · `GIT_TOKEN`(密) |
|
||||
| database | mysql | `MYSQL_HOST` `MYSQL_PORT` `MYSQL_DATABASE` `MYSQL_USER` · `MYSQL_PASSWORD`(密) |
|
||||
| database | pg(postgres) | `PG_HOST` `PG_PORT` `PG_DATABASE` `PG_USER` · `PG_PASSWORD`(密) |
|
||||
| database | redis | `REDIS_HOST` `REDIS_PORT` `REDIS_DB` · `REDIS_PASSWORD`(密) |
|
||||
| database | mongo | `MONGO_HOST` `MONGO_PORT` `MONGO_DATABASE` `MONGO_USER` · `MONGO_PASSWORD`(密) |
|
||||
| storage(blob) | azure blob | `BLOB_ACCOUNT` `BLOB_CONTAINER` · `BLOB_KEY`(密) |
|
||||
| storage(bucket) | s3/oss/minio | `BUCKET_ENDPOINT` `BUCKET_REGION` `BUCKET_NAME` · `BUCKET_ACCESS_KEY_ID` `BUCKET_SECRET_ACCESS_KEY`(密) |
|
||||
| database | postgres | `POSTGRES_HOST` `POSTGRES_PORT` `POSTGRES_DATABASE` `POSTGRES_USER` · `POSTGRES_PASSWORD`(密) |
|
||||
| storage | azure blob | `AZURE_BLOB_ACCOUNT_NAME` `AZURE_BLOB_CONTAINER` · `AZURE_BLOB_ACCOUNT_KEY`(密) |
|
||||
|
||||
另外随启动注入(模型网关):`OPENAI_BASE_URL`(= HM `/v1`)、`MODEL_NAME`。**`OPENAI_API_KEY` 不在启动时注入** —— 由客户端在 A2A 请求里带 `api_key`(你们文档 §7);后续若走 V2 解密路径再调整。
|
||||
|
||||
- 缺失的可选字段不会出现在 env 里(agent 自行容错)。
|
||||
- 同类型资源 HM 限制只挂一个(避免 env 名冲突)。
|
||||
- 角色/指令注入:HM 把模板 .md 放进 `env.AGENT_INSTRUCTION_TEXT`、模板 key 放进 `env.AGENT_ROLE_NAME`(对齐你们 §4)。
|
||||
|
||||
---
|
||||
|
||||
@@ -114,7 +114,7 @@ HM 这些路径都可用环境变量覆盖(默认值见 §1),AM 若用别的路
|
||||
|---|---|---|
|
||||
| `AGENT_RUNTIME_BASE_URL` | — | AM 基址(必须配,HTTPS/私网) |
|
||||
| `AGENT_RUNTIME_SERVICE_TOKEN` | — | HM 调 AM 的 Bearer |
|
||||
| `AGENT_RUNTIME_AGENT_START_PATH` | `/api/agent/agents/start` | 启动 |
|
||||
| `AGENT_RUNTIME_AGENT_START_PATH` | `/agents` | 启动(你们的 create) |
|
||||
| `AGENT_RUNTIME_AGENT_PATH` | `/api/agent/agents/{agent_id}` | 取状态(GET)/删除(DELETE) |
|
||||
| `AGENT_RUNTIME_AGENT_STOP_PATH` | `/api/agent/agents/{agent_id}/stop` | 停止 |
|
||||
| `AGENT_RUNTIME_START_TIMEOUT_SECONDS` | `60` | 启动超时 |
|
||||
|
||||
@@ -170,17 +170,31 @@ signature = base64( ed25519_sign( device_priv, sha256(canonical) ) )
|
||||
|
||||
---
|
||||
|
||||
## 6. 直连 Agent(客户端 ↔ agent,SSE)🔴
|
||||
## 6. 直连 Agent(客户端 ↔ agent,A2A 协议)
|
||||
|
||||
> 这一段是 **客户端直接连 agent**,不经过 HM。HM 只负责把 `subdomain + access_token` 给你。
|
||||
> 客户端**直接连 agent 子域名**(§5 的 `subdomain`),不经过 HM。agent 是 AM 的 `coding_a2a_agent`,走 **A2A 协议**。
|
||||
|
||||
- 从 §5 列表拿到某个 `status=running` agent 的 `subdomain` 和 `access_token`。
|
||||
- 客户端**直连该子域名,SSE 双向通信**,请求携带 `access_token`(具体头部/握手以 AM 的 agent 端契约为准)。
|
||||
- **agent 自己校验 token**;token 错/缺则拒绝(防公网裸奔)。
|
||||
- agent 用 `.env` 里注入的资源(git/vm/db/blob 配置)自行干活;**用模型仍打 HM `/v1/*`**。
|
||||
- agent 不会把 `.env`/密钥回显到对话或日志(由 AM 保证)。
|
||||
- **同步**:`POST {subdomain}/message/send`
|
||||
- **流式**:`POST {subdomain}/message/stream`(返回 `text/event-stream`)
|
||||
- **发现/健康**:`GET {subdomain}/.well-known/agent.json`、`GET {subdomain}/health`
|
||||
|
||||
> 该直连契约(SSE 握手、token 头名、消息格式)由 AM 的 agent 端定义,定稿后补入本节。
|
||||
请求体(JSON-RPC,A2A):
|
||||
```json
|
||||
{ "jsonrpc":"2.0", "id":"task-1", "method":"message/send",
|
||||
"params": {
|
||||
"api_key": "sk-…", // ★ 模型 key,agent 用它调 HM /v1 计费(客户端自带)
|
||||
"model": "gpt-5.4",
|
||||
"message": { "role":"user", "parts":[ {"kind":"text","text":"…"} ] },
|
||||
"configuration": { "workspace": { "root_dir":"/workspace", "allowed_paths":["a.py"] } }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **鉴权 = `params.api_key`**(模型 sk-,客户端自带的网关 key);agent 凭它调 HM `/v1/*` 计费。HM 当前列表里的 `access_token` 字段对该 agent**为空**(AM 不发专属 token);连 agent 用上面的 `api_key`。
|
||||
- agent 用 `.env` 里注入的资源(git/mysql/postgres/azure-blob)自行干活;工具:`read_file/write_file/edit_file/run_command/git_*/run_database_query/list_blob_objects` 等。缺配置的资源工具调用会返回 `resource not configured`,不阻塞。
|
||||
- 可在 `params.configuration.resources` 里按需覆盖资源(请求级覆盖启动级)。
|
||||
|
||||
> ⚠️ 安全:当前 agent 端鉴权仅为"持有效 api_key",非 per-agent 专属令牌。HM 侧规划**用 V2 解密接口**做按用户隔离(客户端加密请求→agent 解密拿 key),或由 AM 自行加门。上线公网前需确认该端点不被他人用任意 key 驱动(见 AM 契约 §3/§4)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user