# cloud-sandbox

来源：https://model.dflop.top/docs/guides/cloud-sandbox

`xiaozhanAI` 可以在云端 Linux 沙箱（OpenAI 官方托管容器，带 Python 3、Node.js、LibreOffice、python-docx / python-pptx / openpyxl 和中文字体）里写代码、跑命令，把 Word、PPT、Excel、PDF 这类文件做出来交给你。用法与 OpenAI 官方的 Responses API 完全一致：

1. 调 `POST /v1/responses`，带上 `code_interpreter` 或 `shell` 工具；
2. 回答里的 `container_file_citation` 告诉你文件在哪个容器（`container_id`）、文件 id 是什么（`file_id`）；
3. 用 `GET /v1/containers/{container_id}/files/{file_id}/content` 把文件下载下来。

要不要做文件由模型根据你的提示词自己判断：让它「做一份 PPT」它就会做，只是问问题它就直接用文字回答。

> 只有 `xiaozhanAI` 支持云沙箱，并且只在 `/v1/responses` 上（`/v1/chat/completions` 不支持这两个工具，和 OpenAI 官方一致）。

## 快速开始

### 1. 让模型做文件

```bash
curl https://api.dflop.top/v1/responses \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "xiaozhanAI",
    "tools": [{ "type": "code_interpreter", "container": { "type": "auto" } }],
    "input": "用 python-pptx 做一份 5 页的「第三季度复盘」PPT，中文字体用 Noto Sans CJK SC，保存到 /mnt/data/第三季度复盘.pptx，最后给出文件链接。"
  }'
```

回答的 `output` 里，消息内容带着文件引用（节选）：

```json
{
  "type": "output_text",
  "text": "已完成：[下载 第三季度复盘.pptx](sandbox:/mnt/data/第三季度复盘.pptx)",
  "annotations": [
    {
      "type": "container_file_citation",
      "container_id": "cntr_6acf7f72fff48198b31b607db0a7a003020f215d49c216d5",
      "file_id": "cfile_6acf7f757e848191962c004f076016de",
      "filename": "第三季度复盘.pptx",
      "start_index": 4,
      "end_index": 48
    }
  ]
}
```

正文里的 `sandbox:/mnt/data/…` 是容器内路径，不能直接打开，请用下一步的接口下载。

### 2. 下载文件

```bash
curl https://api.dflop.top/v1/containers/$CONTAINER_ID/files/$FILE_ID/content \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" \
  -o 第三季度复盘.pptx
```

### Python（OpenAI SDK）

```python
from openai import OpenAI

client = OpenAI(base_url="https://api.dflop.top/v1", api_key="sk-gpushare-...")

resp = client.responses.create(
    model="xiaozhanAI",
    tools=[{"type": "code_interpreter", "container": {"type": "auto"}}],
    input="用 python-docx 写一份一页的《活动方案》Word 文档，保存到 /mnt/data/活动方案.docx，最后给出文件链接。",
)

for item in resp.output:
    if item.type != "message":
        continue
    for part in item.content:
        for ann in getattr(part, "annotations", None) or []:
            if ann.type == "container_file_citation":
                data = client.containers.files.content.retrieve(ann.file_id, container_id=ann.container_id)
                data.write_to_file(ann.filename)
                print("saved", ann.filename)
```

做文件通常要几十秒到几分钟，建议加 `stream=True`（`"stream": true`）边做边收，避免长时间没有数据导致客户端或代理超时。

## 用 `shell` 工具

`code_interpreter` 只跑 Python；`shell` 给模型一个完整的 Linux shell（可以用 LibreOffice 转 PDF、用 Node.js 的 PptxGenJS 做 PPT、渲染预览图等），写法同 OpenAI：

```json
{
  "model": "xiaozhanAI",
  "stream": true,
  "tools": [{ "type": "shell", "environment": { "type": "container_auto" } }],
  "input": "做一份「新品发布会」PPT，8 页，配色简洁，保存到 /mnt/data/新品发布会.pptx 并给出链接。"
}
```

文件引用和下载方式与上面相同。

## 把你自己的素材交给模型

先建一个容器、把文件传进去，再让模型在这个容器里工作：

```bash
# ① 建容器
curl https://api.dflop.top/v1/containers \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "q3-report"}'
# → {"id": "cntr_…", "object": "container", "status": "running", "memory_limit": "4g", …}

# ② 上传素材（multipart，字段名 file）
curl https://api.dflop.top/v1/containers/$CONTAINER_ID/files \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" \
  -F "file=@sales.xlsx"
# → {"id": "cfile_…", "path": "/mnt/data/…sales.xlsx", "source": "user", …}

# ③ 让模型使用这个容器
curl https://api.dflop.top/v1/responses \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "model": "xiaozhanAI",
    "tools": [{ "type": "shell", "environment": { "type": "container_reference", "container_id": "'$CONTAINER_ID'" } }],
    "input": "读 /mnt/data 里的 sales.xlsx，做一份带图表的销售分析 Word 报告，保存到 /mnt/data/销售分析.docx。"
  }'
```

`code_interpreter` 引用已有容器的写法是 `"container": "cntr_…"`。上传后文件在容器里的实际路径以返回的 `path` 为准（会带一段随机前缀），可以在提示词里让模型先 `ls /mnt/data`。

Python 里对应 `client.containers.create(name=…)` 与 `client.containers.files.create(container_id, file=open("sales.xlsx", "rb"))`。

## 接口一览

| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/v1/containers` | 建容器（当场计一个容器窗口，见下文计费） |
| GET | `/v1/containers` | 列出你的容器（`limit` ≤ 50、`order`、`after`） |
| GET | `/v1/containers/{container_id}` | 查看容器状态 |
| DELETE | `/v1/containers/{container_id}` | 删除容器 |
| GET | `/v1/containers/{container_id}/files` | 列出容器里的文件（`limit` / `order` / `after`） |
| POST | `/v1/containers/{container_id}/files` | 上传文件（multipart，字段 `file`，≤ 50 MB） |
| GET | `/v1/containers/{container_id}/files/{file_id}` | 文件信息 |
| GET | `/v1/containers/{container_id}/files/{file_id}/content` | 下载文件（总是以附件形式返回） |
| DELETE | `/v1/containers/{container_id}/files/{file_id}` | 删除文件 |

请求和响应的字段与 OpenAI 官方 Containers API 相同。余额用完时这些接口仍然可以查看和下载（建容器除外）。

## 计费

- **token、联网、出图**：按 `xiaozhanAI` 的价格照常计（见[模型与定价](../reference/models.md#xiaozhanai-1)）。
- **容器费**：每个容器每 **20 分钟**计一次 **194.112 积分**。一个容器第一次被用到（或用 `POST /v1/containers` 建出来）时开始计第一个 20 分钟；这 20 分钟里再用它不另收；过了 20 分钟还在用，再计下一个 20 分钟。
- 容器费在用量日志里单独一行：`unit_type` 为 `container`，`unit_count` 是这次计了几个 20 分钟。

想省容器费：同一批文件尽量在同一个容器里连续做（用 `container_reference` / `"container": "cntr_…"` 复用），而不是每次都让它新建。

## 限制

- 容器固定 **4 GB** 内存（请求里写别的 `memory_limit` 也按 4 GB）。
- 容器**不能访问外网**：`network_policy` 只接受 `{"type": "disabled"}`。
- 不支持 OpenAI Files 的 `file_ids`（建容器、自动容器、上传接口都不收），素材请用上传接口传进容器。
- 不支持 `background: true`，长任务请用 `stream: true`。
- 容器闲置 **20 分钟**后会被销毁，里面的文件随之消失 —— 做完请尽快下载。
- 容器和其中的文件**只有建出它的那把 API Key** 能访问；别的 Key（包括同一账号下的其他 Key）访问会得到 404。

## 常见错误

| 状态 | 场景 |
|---|---|
| 404 `Container with id '…' not found.` | 容器不存在、已删除，或不是这把 Key 建的 |
| 400 `Container is expired.` | 容器闲置超过 20 分钟已过期，换一个新容器（`container_auto` / `type: auto`） |
| 400 `… file_ids is not supported …` / `… network_policy …` | 用了上面「限制」里不支持的参数 |
| 400 `tool_not_supported`（`/v1/chat/completions` 带 `shell`） | 云沙箱只在 `/v1/responses` 上 |
| 402 | 余额不足（建容器、调用模型时） |

更多错误码见[错误码](../reference/errors.md)。
