agent-Specialization/api_doc/examples.md

7.2 KiB
Raw Blame History

示例curl / Python / JS / Flutter

本文提供“最小可用”的端到端示例:创建对话 → 发送消息 → 轮询输出 →(可选)停止任务 → 文件上传/下载。

请先阅读 auth.md 并准备好 token。

0. 统一变量

  • BASE_URL:例如 http://localhost:8091
  • TOKEN:你的 Bearer Token明文

1) curl完整对话流程

1.1 创建对话

BASE_URL="http://localhost:8091"
TOKEN="<TOKEN>"

curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE_URL/api/v1/conversations"

假设返回:

{ "success": true, "conversation_id": "conv_20260123_234245_036" }

1.2 发送消息(创建任务)

curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "conv_20260123_234245_036",
    "message": "请用中文简要介绍《明日方舟:终末地》",
    "run_mode": "fast",
    "max_iterations": 100
  }' \
  "$BASE_URL/api/v1/messages"

假设返回:

{ "success": true, "task_id": "60322db3-...", "status": "running", "conversation_id": "conv_...", "created_at": 1769182965.30 }

1.3 轮询(建议脚本处理 next_offset

TASK_ID="60322db3-..."
OFFSET=0

curl -sS -H "Authorization: Bearer $TOKEN" \
  "$BASE_URL/api/v1/tasks/$TASK_ID?from=$OFFSET"

2) Python轮询并实时拼接文本

依赖:pip install requests

import time
import requests

BASE_URL = "http://localhost:8091"
TOKEN = "<TOKEN>"
H = {"Authorization": f"Bearer {TOKEN}"}

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

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

# 1) create conversation
conv = requests.post(BASE_URL + "/api/v1/conversations", headers=H, timeout=30).json()
assert conv["success"]
conversation_id = conv["conversation_id"]

# 2) send message
task = post_json("/api/v1/messages", {
    "conversation_id": conversation_id,
    "message": "请用中文简要介绍《明日方舟:终末地》",
    "run_mode": "fast",
    "max_iterations": 100
})
task_id = task["task_id"]

# 3) poll events
offset = 0
text_buf = []
think_buf = []

while True:
    data = get_json(f"/api/v1/tasks/{task_id}", params={"from": offset})
    if not data["success"]:
        raise RuntimeError(data.get("error"))
    info = data["data"]
    events = info["events"]
    offset = info["next_offset"]

    for ev in events:
        t = ev["type"]
        d = ev["data"] or {}
        if t == "text_chunk":
            text_buf.append(d.get("content",""))
            print(d.get("content",""), end="", flush=True)
        elif t == "text_end":
            print("\\n--- text_end ---\\n")
        elif t == "thinking_chunk":
            think_buf.append(d.get("content",""))
        elif t == "tool_start":
            print(f\"\\n[tool_start] {d.get('name')}\\n\")
        elif t == "update_action":
            # 工具状态更新
            st = d.get("status") or ""
            msg = d.get("message") or ""
            if st or msg:
                print(f\"\\n[update_action] {st} {msg}\\n\")
        elif t == "error":
            print(f\"\\n[error] {d.get('message')}\\n\")

    if info["status"] != "running":
        break

    time.sleep(1.0)

final_text = "".join(text_buf)
final_thinking = "".join(think_buf)
print("final status:", info["status"])
print("final text length:", len(final_text))
print("final thinking length:", len(final_thinking))

3) JavaScript浏览器/Node要点

浏览器端直接跨域请求时,请确保服务端允许 CORS当前服务端已启用 CORS。请求示例

const BASE_URL = "http://localhost:8091";
const TOKEN = "<TOKEN>";

async function api(path, options = {}) {
  const resp = await fetch(BASE_URL + path, {
    ...options,
    headers: {
      "Authorization": `Bearer ${TOKEN}`,
      ...(options.headers || {})
    }
  });
  const data = await resp.json();
  if (!resp.ok || !data.success) throw new Error(data.error || resp.statusText);
  return data;
}

const conv = await api("/api/v1/conversations", { method: "POST" });
const msg = await api("/api/v1/messages", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    conversation_id: conv.conversation_id,
    message: "请用中文简要介绍《明日方舟:终末地》",
    run_mode: "fast",
    max_iterations: 100
  })
});

let offset = 0;
let text = "";
while (true) {
  const poll = await api(`/api/v1/tasks/${msg.task_id}?from=${offset}`);
  const info = poll.data;
  for (const ev of info.events) {
    if (ev.type === "text_chunk") text += ev.data.content || "";
    if (ev.type === "tool_start") console.log("tool:", ev.data.name);
  }
  offset = info.next_offset;
  if (info.status !== "running") break;
  await new Promise(r => setTimeout(r, 1000));
}
console.log("done:", text);

4) FlutterDart轮询示例伪代码

依赖:http 包或 dio 包均可,这里用 http 表达逻辑。

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

const baseUrl = "http://localhost:8091";
const token = "<TOKEN>";

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

Future<String> createConversation() async {
  final resp = await http.post(Uri.parse("$baseUrl/api/v1/conversations"),
      headers: {"Authorization":"Bearer $token"});
  final data = jsonDecode(resp.body);
  if (resp.statusCode != 200 || data["success"] != true) throw Exception(data["error"]);
  return data["conversation_id"];
}

Future<String> sendMessage(String convId, String message) async {
  final resp = await http.post(Uri.parse("$baseUrl/api/v1/messages"),
      headers: headersJson(),
      body: jsonEncode({
        "conversation_id": convId,
        "message": message,
        "run_mode": "fast",
        "max_iterations": 100,
      }));
  final data = jsonDecode(resp.body);
  if (resp.statusCode != 202 || data["success"] != true) throw Exception(data["error"]);
  return data["task_id"];
}

Stream<String> pollText(String taskId) async* {
  int offset = 0;
  while (true) {
    final resp = await http.get(Uri.parse("$baseUrl/api/v1/tasks/$taskId?from=$offset"),
        headers: {"Authorization":"Bearer $token"});
    final data = jsonDecode(resp.body);
    if (resp.statusCode != 200 || data["success"] != true) throw Exception(data["error"]);
    final info = data["data"];
    final events = (info["events"] as List);
    for (final ev in events) {
      if (ev["type"] == "text_chunk") {
        yield (ev["data"]["content"] ?? "");
      }
    }
    offset = info["next_offset"];
    if (info["status"] != "running") break;
    await Future.delayed(Duration(milliseconds: 800));
  }
}

提示:

  • Flutter UI 展示建议:把 pollText() 的输出 append 到一个 StringBuffer,并用 setState()/状态管理更新。
  • 同时可以订阅 tool_* 事件在 UI 中显示“正在搜索/正在执行工具”等状态。