Files
deerflow-code/frontend-web/docs/multi-agent-api.md
T
2026-05-22 20:40:36 +08:00

9.6 KiB
Raw Blame History

多智能体协调 API 接口文档

后端路由:offline-backend-20260512/backend/app/gateway/routers/multi_agent.py 路由前缀:/api/multi-agent

概览

提供一个"总控 + 多个子智能体"的协调编排接口。

  • 总控(leader) 智能体:通过 agent_orchestration 技能给子智能体派活
  • 子智能体(special):执行被派到的具体任务,完成后把交付内容广播给其他线程

调用方按下面顺序使用:

  1. 调 POST /api/multi-agent/init 一次性创建协调智能体 + 给每个子智能体建 thread,拿到 thread_ids 映射
  2. 客户端把 thread_ids 保管好,后续每次调 POST /api/multi-agent/run/stream 时回传
  3. 先调用 agent_type="leader" 拿派活清单,再针对清单里每个子智能体调用 agent_type="special"

1. 初始化多智能体集群

POST /api/multi-agent/init

创建协调智能体并为列表中的每个子智能体建立独立的 thread,同时把"职责说明"广播到所有子智能体线程作为初始上下文。

请求体

{
  "agent_names": ["taiwan-intelligence-gathering-agent", "us-intelligence-gathering-agent"],
  "main_agent_name": "main-controller",
  "model": "deepseek-chat"
}
字段 类型 必填 说明
agent_names string[] 是 已有子智能体的 id 列表(kebab-case,区分大小写不敏感,自动转小写)
main_agent_name string 否 要创建的协调智能体名,默认 main-agent。重名时会复用已存在的同名智能体
model string 否 协调智能体使用的模型名。不传则用后端默认模型

响应(200)

{
  "status": "success",
  "main_agent_name": "main-controller",
  "thread_ids": {
    "main-controller": "thread-uuid-xxx",
    "taiwan-intelligence-gathering-agent": "thread-uuid-yyy",
    "us-intelligence-gathering-agent": "thread-uuid-zzz"
  }
}

thread_ids 是一个 {agent_name: thread_id} 字典。前端必须把这个对象完整保管下来,后续调用 stream 接口需要原样回传。

错误

状态码 触发条件
400 agent_names 为空
401 未携带 Authorization 头
500 协调智能体创建失败且不是"已存在"错误

2. 流式调度

POST /api/multi-agent/run/stream

通过 SSE(Server-Sent Events)流式驱动一个 leader 或 special 智能体跑一轮对话。

请求体

{
  "agent_type": "leader",
  "d_agent_thread_id": {
    "main-controller": "thread-uuid-xxx",
    "taiwan-intelligence-gathering-agent": "thread-uuid-yyy"
  },
  "agent_name": "main-controller",
  "new_message": "请分析特朗普的支持率",
  "skill_stop_names": [],
  "model": "deepseek-chat"
}
字段 类型 必填 说明
agent_type "leader" | "special" 否 默认 leader。leader 调用协调智能体,special 调用子智能体
d_agent_thread_id object 是 由 /init 接口返回的 thread_ids 映射,原样回传
agent_name string 是 本轮要驱动的智能体名(必须存在于 d_agent_thread_id 中)
new_message string 是 这轮发给智能体的用户消息
skill_stop_names string[] 否 触发后中断执行的技能名列表(默认空)
model string 否 本轮 LLM 模型名,默认 deepseek-chat

响应

Content-Type: text/event-stream 的 SSE 长连接,按到达顺序产出多种数据:

a. 透传上游事件

原样转发底层 POST /api/threads/{thread_id}/runs/stream 的事件帧(LangGraph 协议),包含 event:、data: 等行。前端按 LangGraph SDK 既有方式渲染即可。

b. 最后一帧:调度状态

流结束前会额外产出一帧 data: 行,结构因 agent_type 而异:

agent_type == "leader":

data: {"status": [["us-intelligence-gathering-agent", "收集美国民众对特朗普的看法"], ["taiwan-intelligence-gathering-agent", "..."]]}

status 是一个 [agent_name, task_text] 二元组数组,代表协调智能体本轮派出去的任务清单。前端拿到后应当对每个元素再调用一次本接口(agent_type="special")来执行子智能体。如果数组为空,说明 leader 觉得已有信息足够,不再派活,可以直接展示 leader 的最终文本回答。

agent_type == "special":

data: {"status": "子智能体xxx完成yyy工作"}

代表子智能体完成本轮工作并已把交付内容广播给其他线程。

副作用(重要)

  • leader 调用:自动把"总控智能体收到用户消息: …"广播到所有非自己的线程;派活后再把"总控智能体给 X 派活, 内容为: …"广播到所有非派活双方的线程
  • special 调用:完成后把"子智能体 X 完成 Y 工作, 交付内容为: …"广播到所有非自己的线程

这意味着每个线程的对话历史里会逐步累积出全局上下文,无需前端再做合并。

错误

状态码 触发条件
400 agent_name 缺失或未在 d_agent_thread_id 中
401 未携带 Authorization 头
502 上游 runs/stream 返回非 200 / SSE 解析失败

3. 前端调用流程示例

// 1. 拿 token
const loginResp = await fetch("/api/v1/auth/login/username", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ username: "yourname" }),
});
const { access_token } = await loginResp.json();
const authHeaders = {
  Authorization: `Bearer ${access_token}`,
  "Content-Type": "application/json",
};

// 2. 初始化
const initResp = await fetch("/api/multi-agent/init", {
  method: "POST",
  headers: authHeaders,
  body: JSON.stringify({
    agent_names: [
      "taiwan-intelligence-gathering-agent",
      "us-intelligence-gathering-agent",
    ],
    main_agent_name: "main-controller",
    model: "deepseek-chat",
  }),
});
const { thread_ids } = await initResp.json();

// 3. 流式调用 leader
async function streamLeader(message: string, model = "deepseek-chat") {
  const resp = await fetch("/api/multi-agent/run/stream", {
    method: "POST",
    headers: authHeaders,
    body: JSON.stringify({
      agent_type: "leader",
      d_agent_thread_id: thread_ids,
      agent_name: "main-controller",
      new_message: message,
      skill_stop_names: [],
      model,
    }),
  });
  const reader = resp.body!.getReader();
  const decoder = new TextDecoder();
  let buf = "";
  let dispatched: [string, string][] = [];

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buf += decoder.decode(value, { stream: true });
    const lines = buf.split("\n");
    buf = lines.pop() ?? "";
    for (const line of lines) {
      if (!line.startsWith("data:")) continue;
      try {
        const payload = JSON.parse(line.slice(5).trim());
        if (Array.isArray(payload.status)) {
          dispatched = payload.status;
        }
        // 其余事件按 LangGraph SDK 协议自行渲染
      } catch {
        /* 非 JSON 帧,可忽略或交给现有渲染器 */
      }
    }
  }
  return dispatched;
}

// 4. 对派活清单逐个调用 special
async function streamSpecial(agentName: string, task: string, model = "deepseek-chat") {
  return fetch("/api/multi-agent/run/stream", {
    method: "POST",
    headers: authHeaders,
    body: JSON.stringify({
      agent_type: "special",
      d_agent_thread_id: thread_ids,
      agent_name: agentName,
      new_message: task,
      skill_stop_names: [],
      model,
    }),
  });
  // 同样按 SSE 解析
}

// 5. 编排:先 leader 派活 → 依次执行 special → 再让 leader 汇总
const dispatched = await streamLeader("请分析特朗普的支持率");
for (const [agentName, task] of dispatched) {
  await streamSpecial(agentName, task);
}
const next = await streamLeader("根据上面收集到的信息出最终结论");
// 当 leader 返回 status: [] 时,意味着汇报完毕,可结束循环

4. 注意事项

  • model 字段的取值:必须是后端 config.yaml 中 models[] 已配置的模型名(如 deepseek-chat、glm-4、gpt-4o 等)。传错会导致上游 502
  • thread_ids 必须每次回传:后端不存储这个映射,是无状态接口
  • 协调智能体必须装有 agent_orchestration 技能:/init 创建时会自动指定 skills=["agent_orchestration"]。后端 skills/ 目录里必须存在该技能定义,否则 leader 无法派活
  • 同名协调智能体冲突:main_agent_name 重名时后端不会报错("already exists" 走兼容分支),会复用既有智能体——所以同一个用户多次 init 用同一个名字是安全的,但 SOUL 不会被覆盖
  • SSE 断连:本接口底层 on_disconnect: "continue",断连后上游 run 会继续跑到结束(不会取消)
  • 并发广播:广播消息使用 asyncio.gather 并发更新所有线程 state,性能上不会随子智能体数线性变慢

5. 相关接口(底层依赖)

本接口内部转发以下 Gateway 原生接口,了解细节可查:

接口 路由 用途
创建 thread POST /api/threads 为每个智能体建立独立会话
读 thread state GET /api/threads/{id}/state 广播前取当前消息列表
写 thread state POST /api/threads/{id}/state 把广播消息追加到 messages
取 agent 详情 GET /api/agents/{id} init 时读取子智能体的 name/description
建 agent POST /api/agents init 时创建协调智能体
流式 run POST /api/threads/{id}/runs/stream leader/special 的实际执行