Batch API
Batch API 讓你能夠非同步地向 AI Agent 發送多個訊息。這非常適合高效處理大量請求,而無需維持持久連線。
概述
Batch API 讓你能夠:
- 在單一 API 呼叫中提交多個請求
- 在背景非同步處理請求
- 追蹤批次進度並取得結果
- 在批次完成時接收 Webhook 通知
- 取消進行中的批次
建立批次
提交一批請求進行非同步處理。
端點: POST /api/v1/batches
請求內容
| 欄位 | 類型 | 必填 | 說明 |
|---|---|---|---|
agent_id |
UUID | 是 | 所有請求的預設 Agent ID(必須有已發布版本) |
requests |
array | 是 | 要處理的請求列表(1-1000 項) |
webhook |
object | 否 | 通知的 Webhook 設定 |
請求項目結構
requests 陣列中的每個項目:
| 欄位 | 類型 | 必填 | 說明 |
|---|---|---|---|
custom_id |
string | 是 | 你的關聯鍵(最多 256 字元,在批次中必須唯一) |
message |
string | 是 | 要發送的訊息 |
agent_id |
UUID | 否 | 覆蓋此特定請求的 Agent |
external_user_id |
string | 否 | 外部用戶識別碼 |
name |
string | 否 | 建立的聊天名稱(最多 256 字元) |
attached_file_uuids |
array | 否 | 檔案附件的 UUID 列表 |
history_id |
integer | 否 | 現有的聊天歷史 ID,用於繼續對話 |
Webhook 設定
| 欄位 | 類型 | 必填 | 說明 |
|---|---|---|---|
url |
string | 是 | Webhook URL(必須是 HTTPS) |
events |
array | 否 | 要通知的事件:completed、failed、cancelled(預設:["completed", "failed"]) |
secret |
string | 否 | 用於簽署 Webhook 負載的密鑰(最多 255 字元) |
範例
curl -X POST "https://api.codeer.ai/api/v1/batches" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"requests": [
{
"custom_id": "req-001",
"message": "你們的營業時間是什麼?",
"external_user_id": "user_123"
},
{
"custom_id": "req-002",
"message": "如何退貨?",
"external_user_id": "user_456"
}
],
"webhook": {
"url": "https://your-server.com/webhooks/codeer",
"events": ["completed", "failed"],
"secret": "your-webhook-secret"
}
}'
回應
狀態碼: 202 Accepted
{
"data": {
"id": "batch-uuid-here",
"status": "in_progress",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"total_requests": 2,
"completed_requests": 0,
"failed_requests": 0,
"created_at": "2024-01-15T10:30:00Z",
"completed_at": null,
"cancelled_at": null
},
"message": null,
"error_code": 0
}
Agent 必須已發布
預設的 agent_id 和任何個別請求的 agent_id 覆蓋都必須參照已發布的 Agent。使用未發布 Agent 的請求將被拒絕。
列出批次
取得批次列表,支援分頁和狀態篩選。
端點: GET /api/v1/batches
查詢參數
| 參數 | 類型 | 預設值 | 說明 |
|---|---|---|---|
limit |
integer | 50 | 每頁結果數(最多 1000) |
offset |
integer | 0 | 要跳過的結果數 |
status |
string | - | 依狀態篩選:in_progress、completed、failed、cancelling、cancelled |
範例
curl -X GET "https://api.codeer.ai/api/v1/batches?limit=10&status=completed" \
-H "x-api-key: YOUR_API_KEY"
回應
{
"data": [
{
"id": "batch-uuid-here",
"status": "completed",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"total_requests": 100,
"completed_requests": 98,
"failed_requests": 2,
"created_at": "2024-01-15T10:30:00Z",
"completed_at": "2024-01-15T10:45:00Z",
"cancelled_at": null
}
],
"pagination": {
"limit": 10,
"offset": 0,
"total_records": 50,
"total_pages": 5,
"current_page": 1,
"next_page": "https://api.codeer.ai/api/v1/batches?offset=10&limit=10",
"prev_page": null
},
"message": null,
"error_code": 0
}
取得批次
取得特定批次的目前狀態。
端點: GET /api/v1/batches/{batch_id}
路徑參數
| 參數 | 類型 | 說明 |
|---|---|---|
batch_id |
UUID | 批次 ID |
範例
curl -X GET "https://api.codeer.ai/api/v1/batches/batch-uuid-here" \
-H "x-api-key: YOUR_API_KEY"
回應
{
"data": {
"id": "batch-uuid-here",
"status": "in_progress",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"total_requests": 100,
"completed_requests": 45,
"failed_requests": 2,
"created_at": "2024-01-15T10:30:00Z",
"completed_at": null,
"cancelled_at": null
},
"message": null,
"error_code": 0
}
批次狀態值
| 狀態 | 說明 |
|---|---|
in_progress |
批次正在處理中 |
completed |
所有請求已處理完成 |
failed |
批次處理失敗 |
cancelling |
正在取消中 |
cancelled |
批次已取消 |
取得批次結果
取得批次中個別請求的結果。
端點: GET /api/v1/batches/{batch_id}/results
路徑參數
| 參數 | 類型 | 說明 |
|---|---|---|
batch_id |
UUID | 批次 ID |
查詢參數
| 參數 | 類型 | 預設值 | 說明 |
|---|---|---|---|
limit |
integer | 50 | 每頁結果數(最多 1000) |
offset |
integer | 0 | 要跳過的結果數 |
status |
string | - | 依狀態篩選:pending、processing、success、failed |
範例
curl -X GET "https://api.codeer.ai/api/v1/batches/batch-uuid-here/results?status=success" \
-H "x-api-key: YOUR_API_KEY"
回應
{
"data": [
{
"custom_id": "req-001",
"status": "success",
"response_content": "我們的營業時間是週一至週五,上午 9 點到下午 6 點。",
"response_usage": {
"tool_calls": [
{
"model": "gemini-2.5-flash",
"call_type": "tool_call",
"total_tokens": 195,
"prompt_tokens": 150,
"completion_tokens": 45
}
],
"total_calls": 1,
"total_tokens": 195,
"main_response": null,
"total_prompt_tokens": 150,
"total_completion_tokens": 45
},
"error_code": null,
"error_message": null,
"history_id": 12345,
"conversation_id": 6789,
"processed_at": "2024-01-15T10:31:00Z"
},
{
"custom_id": "req-002",
"status": "failed",
"response_content": null,
"response_usage": null,
"error_code": 500,
"error_message": "處理過程中發生內部伺服器錯誤",
"history_id": null,
"conversation_id": null,
"processed_at": "2024-01-15T10:31:05Z"
}
],
"pagination": { ... },
"message": null,
"error_code": 0
}
請求狀態值
| 狀態 | 說明 |
|---|---|
pending |
請求已排隊等待處理 |
processing |
請求正在處理中 |
success |
請求已成功完成 |
failed |
請求失敗 |
結果中的錯誤代碼
結果中的錯誤代碼遵循 HTTP 語義:
| 錯誤代碼 | 說明 |
|---|---|
400 |
驗證錯誤(請求資料無效) |
408 |
批次處理逾時,請求在完成前已中止 |
499 |
批次已被取消 |
500 |
處理過程中發生內部伺服器錯誤 |
取消批次
取消進行中的批次。待處理的請求將被取消,而已在處理中的請求將會完成。
端點: POST /api/v1/batches/{batch_id}/cancel
路徑參數
| 參數 | 類型 | 說明 |
|---|---|---|
batch_id |
UUID | 批次 ID |
範例
curl -X POST "https://api.codeer.ai/api/v1/batches/batch-uuid-here/cancel" \
-H "x-api-key: YOUR_API_KEY"
回應
狀態碼: 202 Accepted
{
"data": {
"id": "batch-uuid-here",
"status": "cancelling",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"total_requests": 100,
"completed_requests": 45,
"failed_requests": 2,
"created_at": "2024-01-15T10:30:00Z",
"completed_at": null,
"cancelled_at": null
},
"message": null,
"error_code": 0
}
取消行為
- 只有狀態為
in_progress的批次可以被取消 - 已在
processing狀態的請求將正常完成 - 待處理的請求將被標記為失敗,錯誤代碼為
499 - 批次狀態將從
cancelling轉變為cancelled,當所有進行中的請求完成時
Webhooks
設定後,Webhook 將以 HTTP POST 請求發送到你指定的 URL。
Webhook 負載
{
"event": "batch.completed",
"timestamp": "2024-01-15T10:45:00Z",
"data": {
"id": "batch-uuid-here",
"status": "completed",
"total_requests": 100,
"completed_requests": 98,
"failed_requests": 2
}
}
Webhook 簽名
如果你在 Webhook 設定中提供了 secret,負載將使用 HMAC-SHA256 簽名。
請讀取 X-Codeer-Signature 與 X-Codeer-Timestamp 標頭,並用 {timestamp}.{raw_payload} 驗證簽名:
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
signed_payload = f"{timestamp}.".encode("utf-8") + payload
expected = hmac.new(
secret.encode("utf-8"),
signed_payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Webhook 事件
| 事件 | 說明 |
|---|---|
batch.completed |
批次中的所有請求已處理完成 |
batch.failed |
批次處理失敗 |
batch.cancelled |
批次已被取消 |
程式碼範例
Python
import requests
import time
API_KEY = "your-api-key"
BASE_URL = "https://api.codeer.ai/api/v1"
headers = {
"x-api-key": API_KEY,
"Content-Type": "application/json"
}
# 建立批次
batch_response = requests.post(
f"{BASE_URL}/batches",
headers=headers,
json={
"agent_id": "your-agent-id",
"requests": [
{"custom_id": f"req-{i}", "message": f"問題 {i}"}
for i in range(10)
]
}
)
batch = batch_response.json()["data"]
batch_id = batch["id"]
print(f"已建立批次:{batch_id}")
# 輪詢完成狀態
while True:
status_response = requests.get(
f"{BASE_URL}/batches/{batch_id}",
headers=headers
)
status = status_response.json()["data"]["status"]
print(f"批次狀態:{status}")
if status in ["completed", "failed", "cancelled"]:
break
time.sleep(5)
# 取得結果
results_response = requests.get(
f"{BASE_URL}/batches/{batch_id}/results",
headers=headers
)
results = results_response.json()["data"]
for result in results:
print(f"{result['custom_id']}: {result['status']}")
if result["response_content"]:
print(f" 回應:{result['response_content'][:100]}...")
JavaScript
const API_KEY = 'your-api-key';
const BASE_URL = 'https://api.codeer.ai/api/v1';
const headers = {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
};
// 建立批次
const batchResponse = await fetch(`${BASE_URL}/batches`, {
method: 'POST',
headers,
body: JSON.stringify({
agent_id: 'your-agent-id',
requests: Array.from({ length: 10 }, (_, i) => ({
custom_id: `req-${i}`,
message: `問題 ${i}`
}))
})
});
const { data: batch } = await batchResponse.json();
console.log(`已建立批次:${batch.id}`);
// 輪詢完成狀態
const pollBatch = async (batchId) => {
while (true) {
const statusResponse = await fetch(`${BASE_URL}/batches/${batchId}`, { headers });
const { data } = await statusResponse.json();
console.log(`批次狀態:${data.status}`);
if (['completed', 'failed', 'cancelled'].includes(data.status)) {
return data;
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
};
await pollBatch(batch.id);
// 取得結果
const resultsResponse = await fetch(`${BASE_URL}/batches/${batch.id}/results`, { headers });
const { data: results } = await resultsResponse.json();
results.forEach(result => {
console.log(`${result.custom_id}: ${result.status}`);
if (result.response_content) {
console.log(` 回應:${result.response_content.slice(0, 100)}...`);
}
});