POST /api/v1/forward/batches
Forward 在请求内读取输入文件、解析 JSONL 结构并持久化初始任务计数,然后返回 validating。文件资源等外部依赖在后台预校验;只有预校验通过的任务才会进入等待调度状态。Batch 默认只在闲时窗口内具备调度资格;可通过 ignore_idle_window=true 让它不受该时间窗口限制。完成后生成 output.jsonl,存在失败行时同时生成 error.jsonl。
请求头
| Header | 是否必填 | 说明 |
|---|
| Authorization | 是 | Bearer <PAT 或 SAT> |
| Content-Type | 是 | application/json |
| Idempotency-Key | 否 | 有副作用请求可选的幂等键。 |
前置准备:上传输入文件
创建 Batch 需要先准备 JSONL 格式的输入文件,然后通过 CAS Files API 上传获得 file_id。
JSONL 格式:每行一个 JSON 对象,代表一个独立任务:
{"custom_id":"task-001","template_id":"tmpl_example001","identity_id":"idn_example001","body":{"input":"分析报告","resources":[{"type":"file","file_id":"opaque-report-id","mount_path":"/data/input/report.pdf"}]}}
{"custom_id":"task-002","template_id":"tmpl_example001","identity_id":"idn_example001","body":{"input":"总结会议记录","resources":[{"type":"file","file_id":"opaque-notes-id"}]}}
| 字段 | 类型 | 必填 | 说明 |
|---|
custom_id | string | 是 | 调用方自定义标识,单 Batch 内唯一,用于结果文件行对应。 |
template_id | string | 是 | Forward Template ID,指定执行模板。 |
identity_id | string | 是 | Forward Identity ID,指定执行身份。 |
body | object | 是 | 传给 Session 的请求体。当前包含必填的 input,以及可选的 resources。 |
body.input | string | 是 | 该任务发送给 Agent 的输入。 |
body.resources | array | 否 | 当前任务追加的文件资源。省略或传空数组表示不追加行级文件。 |
body.resources[].type | string | 是 | 资源类型,当前只能为 file。缺失或传入其他值会使该行成为 invalid_line。 |
body.resources[].file_id | string | 是 | Files API 返回的文件 ID。按不透明字符串处理,不要求固定前缀;trim 后不能为空。 |
body.resources[].mount_path | string | 否 | Agent 容器内的挂载路径。必须是规范的绝对路径;省略时根据文件名生成 /data/workspace/<filename>。 |
每个 resources 元素必须是 JSON object,并且只能包含 type、file_id、mount_path。字段类型错误、未知字段、相对路径、包含 .. 的非规范路径或控制字符都会使整行成为 invalid_line,不会静默忽略该资源。
行级文件会追加到 Template 和 Identity 的有效资源中,不会替换已有资源。重复 file_id、重复挂载路径,以及与 Template、Identity 或仓库资源发生路径重叠,都会使对应任务以 config_error 失败。
文件必须由当前 Batch 调用身份可访问,并处于可挂载的 ready 状态。Batch 不会复制、锁定文件或延长文件生命周期;文件在预校验后被删除、失效或变更时,Session 创建阶段仍会再次校验并可能使该任务失败。
上传文件:
curl -X POST 'https://api.qoder.com.cn/api/v1/cloud/files' \
-H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
-F "file=@batch_input.jsonl" \
-F "purpose=session_resource"
purpose=session_resource 是 Batch 输入文件的强制约束。省略该字段时 Files API 会按 user_upload 保存,CAS 禁止服务端下载此类文件,创建 Batch 将返回 400 invalid_input_file。
返回的 id 即为 创建 Batch 请求中的 input_file_id。
请求体参数
| 参数 | 类型 | 是否必填 | 说明 |
|---|
| input_file_id | string | 是 | 通过 Files API 上传的 JSONL 文件 ID。 |
| completion_window | string | 是 | 完成窗口:24h、48h、72h。超时后 Batch 自动进入 expired 状态。 |
| metadata | object | 否 | 调用方业务元数据,最多 16 个 key;value 可为任意 JSON 类型;整体序列化后 ≤ 2KB,key ≤ 64 字符,且不得包含 NUL(U+0000)。 |
| ignore_idle_window | boolean | 否 | 是否无视闲时窗口;省略默认为 false。只接受 JSON boolean true 或 false,不会将字符串或数字隐式转换。 |
ignore_idle_window 为严格布尔字段。null、"true"、1、[] 或 {} 均会返回 HTTP 400 invalid_request_error / invalid_request,错误消息为 ignore_idle_window must be a boolean。
示例请求
普通 Batch(默认遵循闲时窗口)
省略 ignore_idle_window 等价于显式传入 false:
curl -s -X POST 'https://api.qoder.com.cn/api/v1/forward/batches' \
-H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input_file_id": "file_input001",
"completion_window": "24h"
}'
无视闲时窗口的 Batch
curl -s -X POST 'https://api.qoder.com.cn/api/v1/forward/batches' \
-H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_batch_001" \
-d '{
"input_file_id": "file_input001",
"completion_window": "24h",
"metadata": {
"source": "data_pipeline",
"job_id": "12345"
},
"ignore_idle_window": true
}'
示例响应
HTTP 200 OK
{
"id": "batch_example001",
"object": "batch",
"status": "validating",
"input_file_id": "file_input001",
"completion_window": "24h",
"ignore_idle_window": true,
"created_at": "2026-07-07T07:25:01Z",
"expires_at": "2026-07-08T07:25:01Z",
"request_counts": {
"total": 2,
"pending": 2,
"running": 0,
"completed": 0,
"failed": 0,
"cancelled": 0,
"expired": 0
},
"usage": null
}
创建 Batch 在同步完成 JSONL 结构解析、既有 Template/Identity 无人值守策略检查和任务持久化后返回 validating 快照,因此 request_counts 是真实初始计数,不是全 0 占位值。逐文件查询和合并资源冲突检查在后台进行,通过后 Batch 才能转为 queued;output_file_id / error_file_id 在任务终态后才出现,创建响应中省略。
若 100 行中有 3 行在同步结构解析阶段失败,创建响应计数应为 total=100、pending=97、failed=3。之后后台资源预校验还可能把部分 pending 任务转为 failed,但计数恒等式始终成立。
全局容量已满时
全局 processing Batch 已达到容量上限时,Create 仍返回 HTTP 200 OK 和 validating,当前等待原因通过动态快照表达:
{
"id": "batch_capacity_waiting001",
"object": "batch",
"status": "validating",
"input_file_id": "file_input001",
"completion_window": "24h",
"ignore_idle_window": true,
"queue_reason": "global_capacity",
"created_at": "2026-07-07T07:25:01Z",
"expires_at": "2026-07-08T07:25:01Z",
"request_counts": {
"total": 2,
"pending": 2,
"running": 0,
"completed": 0,
"failed": 0,
"cancelled": 0,
"expired": 0
},
"usage": null
}
响应字段
| 字段 | 类型 | 说明 |
|---|
| id | string | Batch ID,前缀 batch_。 |
| object | string | 固定为 batch。 |
| status | string | Batch 状态,见状态说明。 |
| input_file_id | string | 输入 JSONL 文件 ID。 |
| completion_window | string | 完成窗口:24h、48h、72h。 |
| ignore_idle_window | boolean | 始终返回。是否无视闲时窗口;历史数据和创建时省略该字段的 Batch 均为 false。 |
| queue_reason | string | 可选的动态排队原因快照;只在 validating 或 queued 时可能返回。 |
| created_at | string | 创建时间,RFC 3339。 |
| expires_at | string | 过期时间,created_at + completion_window。 |
| request_counts | object | 任务计数聚合。 |
| usage | object/null | 创建响应为 null;后续 Batch 详情、列表和取消响应中,至少一个子任务已有合法 CAS Session 用量时返回 Credit 汇总。 |
| usage.total_credits | number | 已持久化子任务 total_credits 之和;单位为 CAS Credit,不代表 token 数或货币金额,显式零值保留。 |
| metadata | object | 调用方业务元数据。 |
Batch 状态
| 状态 | 说明 | 类型 |
|---|
validating | JSONL 行及同步配置检查结果已持久化,正在后台预校验有效配置和文件资源;此时任务尚未进入执行队列。 | 非终态 |
queued | 资源预校验已完成,存在可执行任务,等待 Scheduler 按闲时窗口、容量和 owner 互斥规则调度激活。 | 非终态 |
processing | 正在执行任务。 | 非终态 |
cancelling | 取消中,等待 running task 结束。 | 非终态 |
expiring | 过期中,等待 running task 结束。 | 非终态 |
finalizing | 正在生成输出文件。 | 非终态 |
completed | 所有任务执行完毕,输出文件已生成。 | 终态 |
failed | 输入文件无法读取、持久化失败或资源校验流程无法恢复等 Batch 级错误。 | 终态 |
cancelled | 用户取消完成。 | 终态 |
expired | completion_window 到期。 | 终态 |
调度与排队原因
owner 表示鉴权凭据对应的业务归属范围:PAT 按当前用户判定,管理员 SAT 按其 organization + workspace 判定。
ignore_idle_window=true 只改变 Batch 的时间窗口资格,不代表立即执行,也不提高优先级。以下规则仍然生效:
- 同一 owner 同时最多一个
processing Batch。
- 全局 Batch 容量和全局 Task 容量不变。
- 只有完成资源校验并从
validating 进入 queued 的 Batch 才可能被激活。
- 闲时窗口内,普通 Batch 与无视窗口的 Batch 按
created_at / ID 共享 FIFO 顺序。
completion_window 仍从 created_at 起算;校验、排队和执行时间都计入,不会因无视闲时窗口而延长或重置。
queue_reason 是读取时的动态快照,可能在响应后立即变化,且快照加载失败时会省略,不影响主接口成功。取值按以下顺序判定,命中第一个即返回:
| 优先级 | 值 | 含义 |
|---|
| 1 | idle_window | 当前不在闲时窗口,且该 Batch 没有设置无视窗口。 |
| 2 | owner_processing | 同一 owner 已有其他 processing Batch。 |
| 3 | global_capacity | 全局 processing Batch 数已达容量上限。 |
| 4 | scheduler_pending | Batch 已是 queued,前三类都未阻塞,正等待 Scheduler 激活。 |
queue_reason 只适用于 validating 和 queued。validating 只返回前三类已确定的外部阻塞,没有外部阻塞时省略,不返回 scheduler_pending;processing、finalizing、cancelling、expiring 和所有终态均省略。
全局 Batch 容量已满时,创建 Batch 仍可成功返回 validating;Batch 完成校验后继续排队,并通过 global_capacity 表达当前等待原因。
Request Counts
| 字段 | 类型 | 说明 |
|---|
| total | integer | 总行数(含校验失败行)。 |
| pending | integer | 尚未开始执行的行数。Batch 为 validating 时表示已接受、仍在等待资源预校验;此时尚未进入执行队列。 |
| running | integer | 正在执行的行数。 |
| completed | integer | 执行成功的行数。 |
| failed | integer | 永久失败的行数(含校验失败)。 |
| cancelled | integer | 因取消而终止的行数。 |
| expired | integer | 因过期而终止的行数。 |
total = pending + running + completed + failed + cancelled + expired 始终成立。
校验与入队门禁
创建 Batch 请求内同步完成以下工作:
- 读取输入文件并解析每一行 JSON;
- 校验必填字段、
custom_id 唯一性和 body.resources 结构;
- 解析 Template/Identity,并执行既有的无人值守工具权限策略检查;
- 将同步校验通过的行保存为
pending,将失败行按 invalid_line、config_error 或 permission_denied 保存为 failed;
- 持久化真实的
total、pending、failed 初始计数。
响应返回后,Forward 在后台以创建 Batch 时的调用身份解析 Template/Identity 的有效资源,并执行文件查询、默认路径生成及合并冲突检查。预校验期间 Batch 保持 validating,不得进入执行队列或创建 Session。预校验完成后:
- 仍有合法任务:
validating → queued → processing;
- 所有任务均失败:
validating → finalizing → completed,并生成结果文件;
- 取消或过期先发生:资源校验不会再激活或入队该 Batch。
单行校验失败不会阻塞其他行。失败行仍保留在 output.jsonl,并进入 error.jsonl;若 Session 尚未创建,该行省略 session_id,且 body.resources 保留原始请求内容。
任务错误分类
| Code | 含义 | 是否重试资源查询 |
|---|
invalid_line | JSONL 结构、资源字段、type、file_id 或显式挂载路径非法。 | 否 |
config_error | 文件返回 404/410、非 ready、不可挂载,或合并后的资源冲突。 | 否 |
permission_denied | 上游明确返回 401/403。 | 否 |
transient_error | 文件查询超时、429 或 5xx,最多 3 次退避重试后仍失败。 | 最多 3 次 |
出于防枚举考虑,上游可能同时用 404 表示“文件不存在”或“当前调用方不可见”。Forward 不通过 404 推断文件所有权或是否真实存在,统一返回不泄露资源元数据的 config_error;只有明确的 401/403 才分类为 permission_denied。
| HTTP | Type | Code | 触发条件 |
|---|
| 400 | invalid_request_error | invalid_request | input_file_id 缺失、completion_window 非法、metadata 超限,或 ignore_idle_window 不是 JSON boolean。 |
| 400 | invalid_request_error | invalid_input_file | 输入文件不可供 Batch 下载;上传时必须指定 purpose=session_resource。 |
| 409 | conflict_error | batch_already_processing | ignore_idle_window=true 且同一 owner 已有状态精确为 processing 的 Batch。 |
| 429 | rate_limit_error | rate_limit_exceeded | 用户未完成 Batch 数量达到上限。 |
| 401 | authentication_error | authentication_required | PAT 或 SAT 无效或已过期。 |
同 owner 正在处理的冲突
仅当请求为 ignore_idle_window=true 且同一 owner 已有精确的 processing Batch 时,创建 Batch 返回 HTTP 409:
{
"type": "error",
"request_id": "req_example001",
"error": {
"type": "conflict_error",
"code": "batch_already_processing",
"message": "Another Batch is already processing for this owner.",
"batch_id": "batch_existing001"
}
}
error.batch_id 是同一 owner 当前正在处理的 Batch ID。该冲突检查早于未完成 Batch 配额检查以及所有文件和持久化副作用:被拒绝的请求不会创建 Batch 或 Task,不会读取、解析或校验输入文件,也不会发送过期消息。validating、queued、finalizing、cancelling、expiring 和终态不触发此 409。
- 单批次最大 10,000 行 JSONL。
- 全局并发上限 50 个 task。
- 默认调度在闲时窗口(默认 22:00~08:00,后台可配)内执行;
ignore_idle_window=true 只放宽该时间资格。
- 客户端创建 Batch 后,应通过
GET /api/v1/forward/batches/{batch_id} 轮询到终态。