docs(heicode): add sub-agile end-to-end demo walkthrough for client cross-check

Step-by-step walkthrough of a real client-simulated run (task dep_39e53ee4c692)
with actual request/response at each stage: create -> poll workflow -> artifacts
-> manifest -> files -> archive. Confirms agent_management delivers real code
files + nested folders (backend/main.py, backend/models.py) surfaced as a
project_folder. Includes a client self-check list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 16:47:53 +08:00
co-authored by Claude Opus 4.8
parent b4c3104dda
commit 78048858b9
@@ -0,0 +1,232 @@
# Heicode 桌面客户端 ↔ Manager Sub Agile 端到端联调演练(真实数据)
> 更新时间:2026-06-02
> 环境:Heicode Manager(生产同构 VM)→ agent_management(Sub Agile Runtime)
> 用途:给桌面客户端团队**逐步对照**接入实现。本文每一步都是**一次真实跑出来的请求 + 真实响应**(任务 `dep_39e53ee4c692`,模型 `gpt-5.4`)。
> 配套:接口细节见同目录 [`heicode-desktop-unified-api.md`](./heicode-desktop-unified-api.md)。
本演练验证的核心结论:**Sub Agile 任务跑完后,agent_management 产出的是真实代码文件 + 目录结构**,Manager 解析成 `project_folder`,客户端可拿到「文件树 / 单文件正文 / 整包 zip」,zip 解压即真实文件夹。
---
## 0. 角色与链路
```text
桌面客户端 ──(只调 Manager)──> Heicode Manager ──(归口)──> agent_management(Sub Agile Runtime)
```
- 客户端**只**调 `https://code.xinghanlab.com/api/heicode/sub-agile/*`,不直连 Runtime。
- Manager 是唯一状态裁判:客户端只看 `display_status`。
- 认证:与模型调用同一套(写请求 V2 加密 body + 设备签名;GET 无 body 设备签名)。详见统一文档 §1。本演练为聚焦业务流程,示例用已登录态表示,不展开加密细节。
---
## 1. 创建任务(提交需求包)
客户端提交**需求包**(不是 Runtime 原始参数),Manager 翻译成编排计划并创建。
**请求**
```http
POST /api/heicode/sub-agile/tasks
Content-Type: application/json
```
```json
{
"mode": "sub_agile",
"requirement": {
"objective": "做一个前后端分离的 Todo 应用。后端放在 backend/ 目录:backend/main.py (FastAPI, 提供 /todos 增删查) 和 backend/models.py (Todo 数据模型)。前端放在 frontend/ 目录:frontend/src/App.jsx 和 frontend/package.json。根目录加 README.md。每个文件用对应语言代码块, 文件名写在代码块第一行注释里。"
},
"model_selection": { "type": "single", "default_model": "gpt-5.4" },
"roles": ["backend"]
}
```
**响应(真实)**
```json
{ "success": true, "data": {
"deployment_id": "dep_39e53ee4c692",
"sub_mode": "agile",
"mode": "sub_agile",
"status": "accepted",
"display_status": "accepted",
"runtime_state": "runtime_syncing",
"agent_instances": [ { "instance_id": "agi_...", "role": "backend", "phase": "pending" } ]
}}
```
要点:
- `deployment_id` 就是后续所有接口的 `{task_id}`(`task_id ≡ deployment_id`)。
- 客户端本地保存:`deployment_id` / `mode` / `display_status`,以便重启后恢复。
- `roles` 决定起几个 agent / 出哪些目录:本例只起了 `backend`,所以只产出 `backend/` 下文件。要前后端并列目录,传 `["backend","frontend"]`。
---
## 2. 轮询工作流,直到终态
**请求**
```http
GET /api/heicode/sub-agile/tasks/dep_39e53ee4c692/workflow
```
运行中每 3–5 秒轮询一次,终态后停止。本次真实轨迹:
```text
poll 1: display_status = accepted
poll 2: display_status = running
poll 3: display_status = completed <- 终态
```
**workflow 响应(真实,节选)**
```json
{ "success": true, "data": {
"task_id": "dep_39e53ee4c692",
"mode": "sub_agile",
"sub_mode": "agile",
"display_status": "completed",
"cloud_deployment_status": "completed",
"runtime_execution_status": "completed",
"phase": "development",
"phases": [ { "phase_id": "development", "name": "development", "status": "completed", "agents": ["backend"] } ],
"agent_count": 1,
"agents": [ { "agent_id": "agi_...", "role": "backend", "status": "completed",
"tokens": 0, "tools": 0, "elapsed_seconds": 0, "artifact_ids": ["art_swm_826fe9814483_backend_1"] } ],
"metrics": { "tokens_used": 618, "tools": 0, "elapsed_seconds": 33, "total_messages": 2 },
"artifact_ids": ["art_swm_826fe9814483_backend_1"]
}}
```
客户端判定规则:
- **只看 `display_status`**。`completed` = 完成且有有效交付物;`needs_codegen` / `completed_without_deliverable` = 没有效产物,别当成功。
- per-agent 的 `tokens`/`tools` 当前为 `0`(agent_management 暂未上报 per-agent 指标,顶层 `metrics` 是真实值)。
---
## 3. 取产物列表(确认是项目而非文本)
**请求**
```http
GET /api/heicode/sub-agile/tasks/dep_39e53ee4c692/artifacts
```
**响应(真实,节选)**
```json
{ "success": true, "data": { "items": [
{
"artifact_id": "art_swm_826fe9814483_backend_1",
"artifact_type": "code_patch",
"is_project": true,
"display_artifact_type": "project_folder",
"manifest_path": ".../artifacts/art_swm_826fe9814483_backend_1/manifest",
"files_path": ".../artifacts/art_swm_826fe9814483_backend_1/files",
"archive_path": ".../artifacts/art_swm_826fe9814483_backend_1/archive"
}
] }}
```
要点:**主产物类型看 `display_artifact_type`**。即使 Runtime 原始 `artifact_type=code_patch`,Manager 归一化为 `project_folder` 且 `is_project=true`,并直接给出 manifest/files/archive 子路径,客户端不用自己猜。
---
## 4. 取文件树(manifest,含目录层级)
**请求**
```http
GET /api/heicode/sub-agile/tasks/dep_39e53ee4c692/artifacts/art_swm_826fe9814483_backend_1/manifest
```
**响应(真实)**
```json
{ "success": true, "data": {
"artifact_id": "art_swm_826fe9814483_backend_1",
"artifact_type": "project_folder",
"root_dir": "project",
"file_count": 2,
"entries": [
{ "path": "backend/models.py", "type": "file", "mime_type": "text/x-python", "size_bytes": 228,
"content_path": ".../files?path=backend%2Fmodels.py" },
{ "path": "backend/main.py", "type": "file", "mime_type": "text/x-python", "size_bytes": 927,
"content_path": ".../files?path=backend%2Fmain.py" }
],
"archive": { "format": "zip", "download_path": ".../archive" }
}}
```
要点:`entries[].path` 带**真实目录层级**(`backend/main.py`)。客户端用 `content_path`(已是 `?path=` URL 编码)直接取单文件,无需自己拼路径。
---
## 5. 取单文件正文(确认是真代码)
**请求**
```http
GET /api/heicode/sub-agile/tasks/dep_39e53ee4c692/artifacts/art_swm_826fe9814483_backend_1/files?path=backend%2Fmain.py
```
**响应(真实,节选)**
```python
# backend/main.py
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from .models import Todo, TodoCreate
app = FastAPI(title="Todo API")
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True,
allow_methods=["*"], allow_headers=["*"])
todos: list[Todo] = []
next_id = 1
@app.get("/todos", response_model=list[Todo])
def list_todos():
...
```
---
## 6. 下载整包 zip(确认解压是真实文件夹)
**请求**
```http
GET /api/heicode/sub-agile/tasks/dep_39e53ee4c692/artifacts/art_swm_826fe9814483_backend_1/archive
```
**响应头(真实)**
```text
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="art_swm_826fe9814483_backend_1.zip"
Content-Length: 896
```
**解压后真实落盘结构**
```text
project/
└── backend/
├── main.py
└── models.py
```
客户端下载契约(务必遵守):
- 读 `Content-Disposition` 文件名、按 `Content-Length` 校验后再落盘,**保存成功才提示「已下载」**。
- 产物未就绪时返回 `{"success":false,"error":{"code":"ARTIFACT_ARCHIVE_NOT_READY","retryable":true}}`,此时不要提示已下载。
---
## 7. 客户端核对清单(照此自检即三端对齐)
- [ ] 创建任务用**需求包**(`requirement.objective` + `model_selection` + `roles`),从响应拿 `deployment_id`(=`task_id`)。
- [ ] 运行中每 3–5 秒 `GET .../workflow`,**只读 `display_status`** 判断完成;不订阅 `/events/stream`(未提供)。
- [ ] 列表/详情/workflow 都读顶层 `mode`(`sub_agile`|`swarm`)做模式标签与路由,重启后用它恢复。
- [ ] 产物以 `display_artifact_type=project_folder` / `is_project=true` 为准,走 `manifest → files?path= → archive`。
- [ ] `files?path=` 用 manifest 给的 `content_path`(已 URL 编码),支持 `backend/main.py` 这种嵌套路径。
- [ ] `archive` 按响应头校验落盘;`ARTIFACT_ARCHIVE_NOT_READY` 时重试不报「已下载」。
- [ ] 终态成功判据:`display_status=completed` 且 artifacts 非空(`is_project=true`)。
---
## 附:已知由 Runtime(agent_management)决定、非 Manager 缺陷的点
- **目录范围由 `roles` 决定**:只传 `["backend"]` 就只出 `backend/`;要 `frontend/` 等并列目录,传对应角色。
- **per-agent `tokens`/`tools`/`elapsed_seconds`**:待 agent_management 在状态里上报 per-agent 指标,未上报时为 `0`(顶层 `metrics` 为真实值)。
- **artifact 归属 agent**:依赖 Runtime 在 artifact metadata 标 `source_agent_role`,未标时 per-agent `artifact_ids` 可能为空。
- **阶段细分 `phases[]`**:Runtime 现仅上报单个 `phase`,故默认是「当前阶段」一条;待上报阶段历史后展开。