# 示例（curl / Python / JS / Flutter）

默认服务端：`https://agent.cyjai.com`

通用 Header：

- `Authorization: Bearer <TOKEN>`

下文示例使用：

- `TOKEN=<TOKEN>`
- `WS=ws1`

---

## 1) curl：完整流程（工作区 + 对话 + 发送 + 轮询）

```bash
BASE_URL="https://agent.cyjai.com"
TOKEN="<TOKEN>"
WS="ws1"

# 1) 创建/确保工作区存在
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"workspace_id\":\"$WS\"}" \
  "$BASE_URL/api/v1/workspaces"

# 2) 创建对话
CONV=$(curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE_URL/api/v1/workspaces/$WS/conversations" \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["conversation_id"])')
echo "CONV=$CONV"

# 3) 发送消息 -> task_id
TASK=$(curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"conversation_id\":\"$CONV\",\"message\":\"介绍明日方舟：终末地\",\"run_mode\":\"fast\"}" \
  "$BASE_URL/api/v1/workspaces/$WS/messages" \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["task_id"])')
echo "TASK=$TASK"

# 4) 轮询增量事件
FROM=0
while true; do
  RESP=$(curl -sS -H "Authorization: Bearer $TOKEN" "$BASE_URL/api/v1/tasks/$TASK?from=$FROM")
  STATUS=$(echo "$RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["status"])')
  NEXT=$(echo "$RESP"   | python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["next_offset"])')

  # 把本轮新增 text_chunk 直接输出（按 token 拼接）
  echo "$RESP" | python3 - <<'PY'
import sys,json
obj=json.load(sys.stdin)
for ev in obj.get("data",{}).get("events",[]):
    if ev.get("type")=="text_chunk":
        print(ev.get("data",{}).get("content",""), end="")
PY

  if [ "$STATUS" != "running" ]; then
    echo
    echo "status=$STATUS"
    break
  fi
  FROM=$NEXT
  sleep 0.5
done
```

---

## 2) Python：requests（推荐写法）

```python
import time
import requests

BASE_URL = "https://agent.cyjai.com"
TOKEN = "<TOKEN>"
WS = "ws1"

H = {"Authorization": f"Bearer {TOKEN}"}

def post_json(path, payload):
    r = requests.post(BASE_URL + path, headers={**H, "Content-Type": "application/json"}, json=payload, timeout=30)
    r.raise_for_status()
    return r.json()

def get_json(path):
    r = requests.get(BASE_URL + path, headers=H, timeout=30)
    r.raise_for_status()
    return r.json()

# workspace
post_json("/api/v1/workspaces", {"workspace_id": WS})

# conversation
conv = requests.post(BASE_URL + f"/api/v1/workspaces/{WS}/conversations", headers=H, timeout=30).json()
conv_id = conv["conversation_id"]

# message -> task
task = post_json(f"/api/v1/workspaces/{WS}/messages", {
    "conversation_id": conv_id,
    "message": "介绍明日方舟：终末地",
    "run_mode": "fast",
    "max_iterations": 100,
})
task_id = task["task_id"]

# poll
offset = 0
while True:
    poll = get_json(f"/api/v1/tasks/{task_id}?from={offset}")["data"]
    offset = poll["next_offset"]
    for ev in poll["events"]:
        if ev["type"] == "text_chunk":
            print(ev["data"]["content"], end="", flush=True)
    if poll["status"] != "running":
        print()
        print("status:", poll["status"])
        break
    time.sleep(0.5)
```

---

## 3) JS：fetch（浏览器/Node）

```js
const BASE_URL = "https://agent.cyjai.com";
const TOKEN = "<TOKEN>";
const WS = "ws1";

async function api(path, { method = "GET", body } = {}) {
  const headers = { Authorization: `Bearer ${TOKEN}` };
  if (body !== undefined) headers["Content-Type"] = "application/json";
  const res = await fetch(BASE_URL + path, { method, headers, body: body ? JSON.stringify(body) : undefined });
  if (!res.ok) throw new Error(await res.text());
  return await res.json();
}

await api("/api/v1/workspaces", { method: "POST", body: { workspace_id: WS } });
const conv = await api(`/api/v1/workspaces/${WS}/conversations`, { method: "POST" });
const msg = await api(`/api/v1/workspaces/${WS}/messages`, {
  method: "POST",
  body: { conversation_id: conv.conversation_id, message: "你好", run_mode: "fast" },
});

let from = 0;
for (;;) {
  const poll = await api(`/api/v1/tasks/${msg.task_id}?from=${from}`);
  const data = poll.data;
  from = data.next_offset;
  for (const ev of data.events) {
    if (ev.type === "text_chunk") process.stdout?.write?.(ev.data.content) ?? console.log(ev.data.content);
  }
  if (data.status !== "running") break;
  await new Promise((r) => setTimeout(r, 500));
}
```

---

## 4) Flutter：http（示意）

```dart
import 'dart:convert';
import 'package:http/http.dart' as http;

const baseUrl = "https://agent.cyjai.com";
const token = "<TOKEN>";
const ws = "ws1";

Map<String, String> headersJson() => {
  "Authorization": "Bearer $token",
  "Content-Type": "application/json",
};

Future<void> main() async {
  await http.post(Uri.parse("$baseUrl/api/v1/workspaces"),
      headers: headersJson(), body: jsonEncode({"workspace_id": ws}));

  final convResp = await http.post(Uri.parse("$baseUrl/api/v1/workspaces/$ws/conversations"),
      headers: {"Authorization": "Bearer $token"});
  final conv = jsonDecode(convResp.body);
  final convId = conv["conversation_id"];

  final msgResp = await http.post(Uri.parse("$baseUrl/api/v1/workspaces/$ws/messages"),
      headers: headersJson(),
      body: jsonEncode({"conversation_id": convId, "message": "你好", "run_mode": "fast"}));
  final msg = jsonDecode(msgResp.body);
  final taskId = msg["task_id"];

  var from = 0;
  while (true) {
    final pollResp = await http.get(
      Uri.parse("$baseUrl/api/v1/tasks/$taskId?from=$from"),
      headers: {"Authorization": "Bearer $token"},
    );
    final poll = jsonDecode(pollResp.body)["data"];
    from = poll["next_offset"];
    for (final ev in poll["events"]) {
      if (ev["type"] == "text_chunk") {
        // append to UI
        print(ev["data"]["content"]);
      }
    }
    if (poll["status"] != "running") break;
    await Future.delayed(const Duration(milliseconds: 500));
  }
}
```

