external-workflow-api.md 9.3 KB

外部工作流 API 参考

当需要从其他应用或新的集成层调用 agent-management 工作流时,按本参考实现服务调用。

契约概要

  • 基础路径:http(s)://<host>:<port>/api/v1
  • 默认后端端口:2438
  • 鉴权:每个 /api/v1/** 请求需携带 X-API-Key: <key>
  • JSON 成功响应包:

    { "code": "OK", "message": "", "data": {} }
    
  • 错误响应包:

    { "code": "VALIDATION_FAILED", "message": "可读错误信息", "data": null }
    
  • 主要错误码:UNAUTHORIZEDVALIDATION_FAILEDWORKFLOW_NOT_FOUNDRUN_NOT_FOUNDGRAPH_INVALIDRUN_ALREADY_STARTEDINTERNAL_ERROR

工作流标识

外部 API 的所有 {workflowName} 路径段同时接受两种值,优先使用 name

  • name(推荐):kebab-case 字符串,正则 ^[a-z0-9]+(-[a-z0-9]+)*$,最大 64 字符,全局唯一。例:military-analyze-agentreport-pipeline
  • id(兼容兜底):纯数字字符串。当传入值不是 name 且为纯数字时,按内部主键 id 查找。仅用于兼容历史调用方,新集成不要依赖。

解析顺序:先 findByName(nameOrId),未命中且为纯数字时回退 findById(Long.valueOf(nameOrId))

响应中的标识字段约定:

字段 类型 含义
workflowId number 内部数字主键,仅作为元数据返回,不用于 URL 路径
workflowName string kebab-case 唯一标识,所有 links、downloadUrl、logsUrl 都用它拼接
displayName string 中文展示名,仅用于 UI 展示

工作流列表响应还会返回 descriptioninputsoutputs(从 userInput / output 节点自动提取的字段定义)。

端点

方法 路径 用途
GET /api/v1/workflows 列出当前 API key 可访问的工作流。响应项含 idnamedisplayNamedescriptioninputsoutputs;当前实现返回所有可访问工作流,分页/搜索查询参数仅作前向兼容保留。
POST /api/v1/workflows/{workflowName}/runs 创建一次运行,可携带变量与可选文件。{workflowName} 接受 kebab-case name 或纯数字 id
POST /api/v1/workflows/{workflowName}/runs/{runId}/start 启动一个已创建的异步运行
GET /api/v1/workflows/{workflowName}/runs/{runId}/stream 订阅 SSE 节点/运行事件
GET /api/v1/workflows/{workflowName}/runs/{runId} 查询运行状态、输出、节点和工作空间信息
GET /api/v1/workflows/{workflowName}/runs/{runId}/nodes/{nodeId}/logs 查询某节点的持久化日志
GET /api/v1/workflows/{workflowName}/runs/{runId}/workspace 下载工作空间 zip

推荐的异步流程

  1. POST /workflows/{workflowName}/runs,带 async: true{workflowName} 推荐使用 kebab-case name
  2. 读取 data.runIddata.links。响应中的 workflowId(数字主键)和 workflowName(kebab-case)可分别作为元数据保存。
  3. POST /workflows/{workflowName}/runs/{runId}/start
  4. 打开 GET /workflows/{workflowName}/runs/{runId}/stream,请求头 Accept: text/event-stream
  5. 收到 workflow_completeworkflow_error 后,关闭流或标记为终态。
  6. 查询 GET /workflows/{workflowName}/runs/{runId} 获取持久化输出与节点摘要。
  7. GET /workflows/{workflowName}/runs/{runId}/workspace 作为二进制下载入口提供给用户。

仅当期望"单次阻塞请求直接返回 SSE"时才使用 async: false

创建运行

纯 JSON 请求:

POST /api/v1/workflows/military-analyze-agent/runs
X-API-Key: demo-key
Content-Type: application/json

{
  "variables": {
    "topic": "大语言模型在企业中的应用",
    "maxTokens": 2048,
    "options": { "lang": "zh", "verbose": false }
  },
  "async": true,
  "failStrategy": "abort",
  "ttlHours": 720
}

Multipart 请求:

POST /api/v1/workflows/military-analyze-agent/runs
X-API-Key: demo-key
Content-Type: multipart/form-data

payload: {"variables":{"topic":"汇总报告"},"async":true}
files: report.md
files: inputs/2024/data.csv

字段说明:

  • variables:自定义工作流输入变量;对象;可选。
  • async:默认 true。为 true 时仅创建;为 false 时立即启动并以 SSE 形式返回。
  • failStrategy:取值 abortskip;可选且前向兼容。当前 agent-management 实现中该字段虽被 DTO 接收,但执行仍依赖 graphData 中节点级别的 failStrategy;除非目标版本显式支持请求级覆盖,否则不要依赖此字段。
  • ttlHours:工作空间保留小时数;默认 720
  • files:可重复的 multipart 二进制 part;可选。
  • X-Relative-Path:multipart part 的可选 header,用于保留目录路径。若客户端技术栈不支持,则退化为仅使用文件名并明确记录该限制。

async: true 的响应:

{
  "code": "OK",
  "data": {
    "runId": "a3f9b2c1",
    "workflowId": 12,
    "workflowName": "military-analyze-agent",
    "displayName": "军事分析助手",
    "status": "CREATED",
    "createdAt": "2026-07-01T08:30:00Z",
    "links": {
      "start": "/api/v1/workflows/military-analyze-agent/runs/a3f9b2c1/start",
      "stream": "/api/v1/workflows/military-analyze-agent/runs/a3f9b2c1/stream",
      "result": "/api/v1/workflows/military-analyze-agent/runs/a3f9b2c1",
      "workspace": "/api/v1/workflows/military-analyze-agent/runs/a3f9b2c1/workspace"
    }
  }
}

SSE 流

打开连接(路径段使用 workflowName,即 kebab-case name):

GET /api/v1/workflows/{workflowName}/runs/{runId}/stream
X-API-Key: <key>
Accept: text/event-stream

断线重连时携带:

Last-Event-ID: 42

每条事件遵循标准 SSE 格式:

event: node_stream
id: 43
data: {"runId":"a3f9b2c1","type":"node_stream"}

事件类型:

  • run_started:引擎已启动。
  • node_status:节点状态变更。用于创建/更新节点卡片。
  • node_stream:节点增量流。kind 取值为 thinkingtool_calltool_result
  • workflow_complete:终态成功。包含最终输出和工作空间/结果 URL。
  • workflow_error:终态失败。包含错误信息,通常带有失败节点 ID。

关键负载字段示例:

{
  "runId": "a3f9b2c1",
  "nodeId": "node-2",
  "nodeType": "agent",
  "label": "研究助手",
  "status": "RUNNING",
  "kind": "thinking",
  "content": "首先我需要搜索相关资料...",
  "toolName": null,
  "timestamp": "2026-07-01T08:30:07Z"
}

客户端行为约定:

  • nodeId 为主键维护每个节点的状态。
  • node_stream.kind === "thinking" 的内容追加到该节点的思考缓冲区。
  • tool_calltool_result 作为日志存储,保留工具名和内容。
  • workflow_completeworkflow_error 视为终态。
  • 如需支持重连,持久化最近一条 SSE 的 id

查询结果

GET /api/v1/workflows/military-analyze-agent/runs/a3f9b2c1
X-API-Key: demo-key

预期的 data 形态:

{
  "runId": "a3f9b2c1",
  "workflowId": 12,
  "workflowName": "military-analyze-agent",
  "displayName": "军事分析助手",
  "status": "SUCCESS",
  "startedAt": "2026-07-01T08:30:05Z",
  "completedAt": "2026-07-01T08:31:12Z",
  "error": null,
  "outputs": {
    "summary": "本报告探讨了...",
    "_workingDirFiles": ["report.md"],
    "_runId": "a3f9b2c1"
  },
  "nodes": [
    {
      "nodeId": "node-2",
      "nodeType": "agent",
      "label": "研究助手",
      "status": "SUCCESS",
      "output": { "research": "..." },
      "error": null,
      "logsCount": 124,
      "logsUrl": "/api/v1/workflows/military-analyze-agent/runs/a3f9b2c1/nodes/node-2/logs"
    }
  ],
  "workspace": {
    "fileCount": 12,
    "totalBytes": 456789,
    "downloadUrl": "/api/v1/workflows/military-analyze-agent/runs/a3f9b2c1/workspace"
  }
}

状态取值:CREATEDRUNNINGSUCCESSFAILED

日志与工作空间

节点日志:

GET /api/v1/workflows/{workflowName}/runs/{runId}/nodes/{nodeId}/logs

日志类型通常为 THINKINGTOOL_CALLTOOL_RESULTINFOERROR。把思考过程抽到独立展示区,非思考日志放在可折叠的日志列表中。

工作空间下载:

GET /api/v1/workflows/{workflowName}/runs/{runId}/workspace

响应是二进制 zip 内容,不要走 JSON 响应拦截器。

安全与集成注意事项

  • 默认不要在前端代码中暴露 X-API-Key。优先使用后端代理,由服务端注入密钥。
  • base URL 与 API key 应存放在环境/配置中,不要硬编码到源码。
  • API Key 的访问范围(白名单)在 agent-management 后端 ExternalApiProperties 中配置,同时支持 workflowNames(推荐,按 kebab-case name 精确匹配)和 workflowIds(兼容,按数字主键匹配);两者皆空表示不限制。新集成建议使用 workflowNames 与 URL 路径保持一致。
  • 如实现代理上传,需校验路径,防止 ../ 路径穿越。
  • 代理 SSE 时关闭缓冲,保留事件名、id 和 data 负载。
  • 未经用户同意,不要悄悄把 SSE 降级为轮询。
  • 使用 axios/fetch 等封装时,对 text/event-stream 和工作空间 zip 端点绕过 JSON 响应包解析。