การที่ระบบรับคำขอสร้างภาพแล้ว ไม่ได้หมายความว่าสร้างภาพเสร็จแล้ว แอปพลิเคชันต้องเก็บ ID งาน ตรวจสอบงานเดิม และจัดการคำขอที่ล้มเหลวหรือขาดช่วงโดยไม่เผลอเริ่มงานใหม่
บทสอนนี้จะพัฒนาไคลเอนต์ Node.js ขนาดเล็กที่ทำงานฝั่งเซิร์ฟเวอร์สำหรับขั้นตอนดังกล่าว โดยแยกคำสั่ง submit และ resume ออกจากกัน คำสั่งแรกใช้สร้างงาน ส่วนอีกคำสั่งใช้ตรวจสอบงานที่มีอยู่แล้วเท่านั้น เริ่มจาก ID โมเดลที่ API รองรับ ไม่ใช่ชื่อโมเดลที่คัดลอกมาจากแบนเนอร์หน้าแรก
บทสอนนี้ครอบคลุมการสร้างภาพจากข้อความ ไม่ได้อัปโหลดไฟล์อ้างอิง พัฒนาส่วนติดต่อบนเบราว์เซอร์ หรือวัดคุณภาพของโมเดล การตรวจสอบประกอบด้วยการทดสอบจำลองในเครื่อง 29 รายการ และงานสร้างภาพจริงในระบบใช้งานจริงหนึ่งงานภายใต้ขอบเขตที่ควบคุมไว้ เมื่อวันที่ 4 กันยายน ค.ศ. 2026 งานดังกล่าวส่งคืนภาพหนึ่งภาพและมีบันทึกค่าบริการ API 120 พอยต์ นี่คือการตรวจสอบการเชื่อมต่อเพียงครั้งเดียว ณ วันที่ระบุ ไม่ใช่การวัดความเร็ว ราคาที่ใช้ได้กับทุกกรณี หรือการรับประกันความพร้อมใช้งานสำหรับบัญชีอื่น
ภาพหน้าปกเป็นภาพหน้าจอแคตตาล็อกโมเดลที่บันทึกเมื่อวันที่ 4 กันยายน ค.ศ. 2026 โปรดใช้เป็นภาพประกอบตามวันที่ระบุ ไม่ใช่ราคาเสนอปัจจุบันหรือการรับประกันความพร้อมใช้งาน
สรุปประเด็นสำคัญ
- เก็บคีย์ API ไว้บนเซิร์ฟเวอร์ ไม่รวมไว้ในชุดไฟล์สำหรับเบราว์เซอร์หรือคลังซอร์สโค้ดสาธารณะ
- สำหรับงานสร้างภาพ ให้อ่านค่า
data.taskIdที่เป็นตัวเลขจากการตอบกลับที่ยืนยันว่ารับคำขอแล้ว และเก็บไว้ใช้สอบถามภายหลัง- ตรวจสอบงานที่บันทึกไว้เป็นระยะจนกว่าจะได้สถานะ
readyหรือfailedอ่าน URL ภาพจากdata.imagesหลังจากงานสำเร็จแล้วเท่านั้น- การหมดเวลาฝั่งไคลเอนต์ไม่ได้ยืนยันว่างานบนเซิร์ฟเวอร์ล้มเหลวหรือถูกยกเลิก หากมี ID งาน ให้กลับมาตรวจสอบต่อ อย่าส่งคำขอสร้างงานซ้ำโดยไม่ตรวจสอบก่อน
เนื้อหาในคู่มือนี้
- ก่อนเริ่มต้น
- เลือกโมเดลและตั้งค่าคีย์ API
- ส่งคำขอด้วย cURL
- ตรวจสอบสถานะและอ่าน URL ภาพ
- ตัวอย่าง Node.js ฉบับเต็ม
- จัดการข้อผิดพลาด
- ความแตกต่างของวิดีโอและแชต
- ตรวจสอบการเชื่อมต่อ
ก่อนเริ่มต้น
ใช้เทอร์มินัลส่วนตัวหรือสภาพแวดล้อมเซิร์ฟเวอร์ที่มี Node.js คีย์ API และบัญชีที่ตั้งค่าการเรียกเก็บค่าบริการ API แล้ว ตัวอย่างที่ให้มานี้ใช้ fetch ที่มีในตัวและโมดูล JavaScript โดยไม่ต้องติดตั้งแพ็กเกจจากภายนอก เอกสาร API แบบโกลบอลของ Node อธิบาย fetch และ AbortSignal.timeout ซึ่งไคลเอนต์ใช้จำกัดเวลาของคำขอแต่ละครั้ง สภาพแวดล้อมที่ใช้ตรวจสอบในเครื่องคือ Windows พร้อม Node.js v22.22.1 ข้อมูลนี้เป็นบันทึกสภาพแวดล้อมการทดสอบ ไม่ได้หมายความว่าทดสอบเวอร์ชันอื่นทั้งหมดแล้ว
คุณควรคุ้นเคยกับการแก้ไขไฟล์ การตั้งค่าตัวแปรสภาพแวดล้อม และการอ่าน JSON ตรวจสอบรันไทม์ที่ติดตั้งไว้ก่อนดำเนินการต่อ:
node --version
คำสั่งนี้ควรแสดงเวอร์ชันที่ติดตั้งอยู่ โดยไม่ติดต่อ API สร้างภาพ บันทึกไคลเอนต์ฉบับเต็มจากหัวข้อถัดไปเป็น examples/pixmind-image.mjs ภายในโฟลเดอร์ทำงาน แล้วเรียกใช้คำสั่งต่าง ๆ จากโฟลเดอร์นั้น
เลือกพรอมต์ทดสอบที่ไม่มีเนื้อหาอันตราย ข้อมูลลูกค้า หรือข้อมูลลับ บทสอนนี้ใช้ภาพถ้วยกาแฟเซรามิกบนฉากหลังในสตูดิโอ จึงไม่ต้องอัปโหลดภาพอ้างอิง ตรวจสอบราคา API ปัจจุบันของโมเดลที่เลือกก่อนส่งคำขอ อย่าคิดว่าเครดิต Studio การสมัครสมาชิก และการเรียกเก็บค่าบริการ API ใช้แทนกันได้ แคตตาล็อกโมเดลอธิบายบริการ API ที่เกี่ยวข้องไว้
หากต้องการเพียงสร้างภาพผ่านการโต้ตอบกับระบบ คู่มือ Image Agent อธิบายขั้นตอนนั้นไว้ ไคลเอนต์ในบทสอนนี้ใช้สำหรับแอปพลิเคชันที่ระบุ ID โมเดลอย่างชัดเจนและจัดการการตอบกลับด้วยตัวเอง
เลือกโมเดลและตั้งค่าคีย์ API
ใช้ ID โมเดลที่ API รองรับและพารามิเตอร์ที่โมเดลนั้นรองรับ ตัวอย่างนี้เลือก nano-banana-pro พร้อม aspectRatio: "1:1" และ resolution: "1K" ซึ่งตรงกับคำขอสร้างภาพใน คู่มือเริ่มต้น API การเรียก GET /models แบบยืนยันตัวตนและการทดสอบในระบบใช้งานจริงยืนยันชุดค่านี้สำหรับบัญชีทดสอบเมื่อวันที่ 4 กันยายน ค.ศ. 2026 ตรวจสอบความพร้อมใช้งานและพารามิเตอร์ที่บัญชีของคุณรองรับอีกครั้งก่อนเรียกใช้งานจริง การเปลี่ยนเฉพาะข้อความชื่อโมเดลไม่ได้รับประกันว่าส่วนที่เหลือของคำขอจะยังถูกต้อง
สร้างคีย์ในคอนโซล API แล้วส่งให้โปรเซสฝั่งเซิร์ฟเวอร์ผ่าน PIXMIND_API_KEY หากมีระบบจัดการข้อมูลลับแบบส่วนตัว ให้ใช้ระบบนั้น ตัวอย่างต่อไปนี้แสดงไวยากรณ์ของตัวแปรสภาพแวดล้อมโดยใช้ค่าตัวอย่างแทนข้อมูลจริง ไม่ใช่ข้อมูลรับรองที่ใช้งานได้
สำหรับ PowerShell:
$env:PIXMIND_API_KEY = "REPLACE_WITH_YOUR_PRIVATE_API_KEY"
$env:PIXMIND_MODEL = "nano-banana-pro"
$env:PIXMIND_PROMPT = "A ceramic coffee cup on a plain studio background, soft side lighting, no text"
สำหรับ Bash:
export PIXMIND_API_KEY="REPLACE_WITH_YOUR_PRIVATE_API_KEY"
export PIXMIND_MODEL="nano-banana-pro"
export PIXMIND_PROMPT="A ceramic coffee cup on a plain studio background, soft side lighting, no text"
การกำหนดค่าเหล่านี้มีผลต่อเชลล์ปัจจุบันและโปรเซสที่เปิดจากเชลล์นั้น แต่ไม่ได้สร้างงาน เมื่อกรอกข้อมูลลับจริง ให้ปกป้องประวัติคำสั่งและสิ่งบันทึกต่าง ๆ ของเชลล์ และห้ามบันทึกข้อมูลลับนั้นลงในระบบควบคุมเวอร์ชัน ปฏิบัติตาม คำแนะนำการยืนยันตัวตน เพื่อแยกคีย์สำหรับการพัฒนาและระบบใช้งานจริง รวมถึงเปลี่ยนคีย์หลังจากเกิดการเปิดเผย
URL หลักสำหรับบทสอนนี้คือ:
https://aihub-admin.aimix.pro/api-platform/v1
ต่อท้าย URL หลักด้วย /generations หรือ /tasks/{taskId} อย่าเพิ่ม /v1 ซ้ำอีกครั้ง สามารถค้นหารายการโมเดลผ่าน GET /models ได้ แต่แคตตาล็อกเป็นแหล่งอ่านความสามารถเฉพาะของแต่ละโมเดลที่สะดวกกว่าก่อนเลือกพารามิเตอร์
ส่งคำขอสร้างภาพแรกด้วย cURL
คำขอ cURL เป็นทางเลือกแทนคำสั่ง Node.js submit ไม่ใช่ขั้นตอนตั้งค่าที่ต้องเรียกใช้ก่อน ทั้งสองวิธีส่งคำขอสร้างภาพเหมือนกัน การเรียกใช้ทั้งคู่จึงอาจสร้างงานที่มีค่าใช้จ่ายสองงาน หากส่งคำขอด้วย cURL ให้ใช้คำสั่ง Node.js resume ต่อจากนั้นพร้อม ID งานที่ได้รับกลับมา
ตัวอย่างนี้ใช้ไวยากรณ์ Bash หากใช้ PowerShell ให้ใช้ไคลเอนต์ Node.js ด้านล่างแทนการวางคำสั่งต่อบรรทัดแบบ Bash ลงในเทอร์มินัล
curl --connect-timeout 10 --max-time 30 \
--request POST \
'https://aihub-admin.aimix.pro/api-platform/v1/generations' \
--header "Authorization: Bearer $PIXMIND_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"model": "nano-banana-pro",
"type": "image",
"prompt": "A ceramic coffee cup on a plain studio background, soft side lighting, no text",
"aspectRatio": "1:1",
"resolution": "1K"
}'
พรอมต์และพารามิเตอร์ในเนื้อหาคำขอ cURL นี้เป็นค่าที่ระบุไว้โดยตรง การเปลี่ยน PIXMIND_PROMPT จะไม่เปลี่ยน JSON นี้ เพราะตัวแปรสภาพแวดล้อมดังกล่าวถูกอ่านโดยไคลเอนต์ Node.js การแยกแยะจุดนี้ช่วยให้เห็นชัดว่าคำขอที่ตั้งใจส่งกับการตั้งค่าเชลล์ที่แวดล้อมคำขอนั้นเป็นคนละส่วนกัน
ด้านล่างคือตัวอย่างแบบย่อของการตอบกลับที่ยืนยันว่ารับคำขอแล้ว ID ในตัวอย่างเป็นค่าที่ใช้แทนข้อมูลจริง ไม่ใช่ ID จากการทดสอบในระบบใช้งานจริง และ 12345 ไม่ใช่งานที่ผู้อ่านควรสอบถาม
{
"code": 1000,
"message": "success",
"data": {
"id": "img_12345",
"taskId": 12345
}
}
การตอบกลับสำหรับงานสื่อที่ทดสอบในระบบใช้งานจริงห่อข้อมูลที่สำเร็จไว้ในออบเจ็กต์ที่มี code, message, data และ timestamp ตัวอย่างนี้ละเวลาและฟิลด์อื่น ๆ ไว้ สำหรับขั้นตอนนี้ ความสำเร็จต้องประกอบด้วยทั้งการตอบกลับ HTTP ที่สำเร็จและการตอบกลับระดับแอปพลิเคชันที่ถูกต้อง ให้อ่าน data.taskId ไม่ใช่ data.id เพราะค่าหลังอาจเป็นสตริงที่มีคำนำหน้า เช่น img_12345 ในขณะที่เส้นทางสอบถามงานใช้ ID ตัวเลข
บันทึก ID ตัวเลขนั้นทันที หากคำขอหมดเวลาก่อนที่คุณจะได้รับ ID ให้หยุดและตรวจสอบผลการส่งคำขอกับประวัติงานในบัญชีหรือฝ่ายสนับสนุน การส่ง POST ซ้ำไม่ใช่วิธีที่ปลอดภัยสำหรับใช้แทนการตรวจสอบว่าเกิดอะไรขึ้น
ตรวจสอบสถานะงานเป็นระยะและอ่าน URL ภาพ
สอบถามงานที่มีอยู่แล้วจนกว่าจะเข้าสู่สถานะสิ้นสุด เอกสารงานแบบอะซิงโครนัส อธิบายสถานะ pending, processing, ready และ failed ไว้ มีเพียงสองสถานะหลังเท่านั้นที่ทำให้ขั้นตอนตรวจสอบสถานะเป็นระยะนี้สิ้นสุดลง
Submit once
|
Save numeric taskId
|
GET /tasks/{taskId} <--- wait, then query again
| ^
+--- pending / processing ---+
|
+--- ready ---> read images, stop
|
+--- failed ---> report failure, stop
หากต้องการสอบถามด้วยตนเองใน Bash ให้แทนที่ ID ตัวอย่างด้วย ID ของคุณ:
TASK_ID="REPLACE_WITH_YOUR_NUMERIC_TASK_ID"
curl --connect-timeout 10 --max-time 30 \
"https://aihub-admin.aimix.pro/api-platform/v1/tasks/$TASK_ID" \
--header "Authorization: Bearer $PIXMIND_API_KEY"
ในรูปแบบการตอบกลับสำหรับงานสื่อปัจจุบัน ฟิลด์ที่ต้องใช้มีหน้าที่ต่างกันดังนี้:
| ฟิลด์ | ความหมายสำหรับไคลเอนต์นี้ |
|---|---|
data.taskId |
ตัวระบุแบบตัวเลขที่ใช้สอบถามงานเดิม |
data.status |
บอกว่าควรรอต่อ อ่านผลลัพธ์ หรือหยุดเมื่องานล้มเหลว |
data.images |
อาร์เรย์ URL ของภาพผลลัพธ์ ใช้หลังจากได้สถานะ ready |
data.videoUrl |
ฟิลด์ผลลัพธ์วิดีโอ ไม่ใช่อาร์เรย์ผลลัพธ์ภาพ |
การตอบกลับสถานะ ready ที่ไม่มี URL ภาพซึ่งใช้งานได้ ไม่ถือเป็นผลลัพธ์การสร้างภาพที่สำเร็จสำหรับแอปพลิเคชันของคุณ ควรแจ้งความไม่สอดคล้องนี้พร้อม ID งานเพื่อให้ตรวจสอบได้ อย่าใช้ URL ตัวอย่างแทนหรือรายงานว่าดาวน์โหลดภาพแล้ว
ไคลเอนต์แสดง URL ภาพที่ได้รับกลับมา แต่ไม่ได้ดึงหรือจัดเก็บไฟล์ภาพ หากแอปพลิเคชันต้องเก็บไฟล์ในระยะยาว ให้ออกแบบเป็นขั้นตอนแยกต่างหากและตรวจสอบเงื่อนไขการเก็บรักษาและการใช้งานไฟล์ที่เกี่ยวข้อง การมี URL ไม่ได้หมายความว่ามีการรับประกันพื้นที่จัดเก็บถาวร
เรียกใช้ตัวอย่าง Node.js ฉบับเต็ม
ใช้ submit สำหรับงานใหม่หนึ่งงาน หรือ resume สำหรับงานที่มีอยู่แล้ว หากเรียกสคริปต์โดยไม่มีอาร์กิวเมนต์ สคริปต์จะแสดงวิธีใช้และไม่เรียก API ค่าเริ่มต้นนี้ช่วยป้องกันไม่ให้การเริ่มโปรแกรมใหม่ตามปกติหรือการเปิดดูวิธีใช้ CLI อย่างรวดเร็วสร้างงานใหม่ขึ้นมา
บันทึกซอร์สโค้ดฉบับเต็มด้านล่างเป็น examples/pixmind-image.mjs:
ซอร์สโค้ด Node.js ฉบับเต็ม: pixmind-image.mjs
import { pathToFileURL } from 'node:url';
const BASE_URL = 'https://aihub-admin.aimix.pro/api-platform/v1';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
export class ApiError extends Error {
constructor(message, { status = 0, transient = false, retryAfterMs = 0 } = {}) {
super(message);
Object.assign(this, { status, transient, retryAfterMs });
}
}
export function parseTaskId(value) {
if (!/^\d+$/.test(String(value))) throw new Error('Use a numeric taskId, not img_...');
const id = Number(value);
if (!Number.isSafeInteger(id) || id <= 0) throw new Error('Invalid taskId');
return id;
}
export function retryAfter(value, now) {
if (!value) return 0;
if (/^\d+(\.\d+)?$/.test(value)) return Number(value) * 1000;
const date = Date.parse(value);
return Number.isFinite(date) ? Math.max(0, date - now) : 0;
}
export function createClient({
apiKey,
fetchImpl = fetch,
now = Date.now,
wait = sleep,
random = Math.random,
requestTimeoutMs = 30_000,
pollTimeoutMs = 300_000,
maxConsecutiveErrors = 5,
} = {}) {
if (typeof apiKey !== 'string' || !apiKey.trim()) throw new Error('Set PIXMIND_API_KEY');
async function request(path, method, payload, timeoutMs = requestTimeoutMs) {
let response;
let body;
try {
response = await fetchImpl(`${BASE_URL}${path}`, {
method,
redirect: 'error',
headers: {
Authorization: `Bearer ${apiKey}`,
...(payload ? { 'Content-Type': 'application/json' } : {}),
},
...(payload ? { body: JSON.stringify(payload) } : {}),
signal: AbortSignal.timeout(Math.max(1, Math.ceil(timeoutMs))),
});
// Read the body inside the timeout/network error boundary as well.
body = await response.text();
} catch {
throw new ApiError('Network error or request timeout', { transient: true });
}
let envelope;
try { envelope = JSON.parse(body); } catch { /* handle below */ }
const transient = response.status === 429 || response.status >= 500;
if (!response.ok) {
// Do not echo arbitrary server bodies, prompts, credentials, or output URLs.
throw new ApiError(`HTTP ${response.status}; inspect the account and request`, {
status: response.status,
transient,
retryAfterMs: retryAfter(response.headers.get('retry-after'), now()),
});
}
if (!envelope || typeof envelope !== 'object') {
throw new ApiError('Expected a JSON API response');
}
if (envelope.code !== 1000 || !envelope.data) {
throw new ApiError('API returned an unsuccessful or incomplete envelope');
}
return envelope.data;
}
async function submit({ model = 'nano-banana-pro', prompt } = {}) {
if (typeof model !== 'string' || !model.trim()) throw new Error('A model is required');
if (typeof prompt !== 'string' || !prompt.trim()) throw new Error('A prompt is required');
try {
const data = await request('/generations', 'POST', {
model, type: 'image', prompt, aspectRatio: '1:1', resolution: '1K',
});
// The public media contract returns a number, not the prefixed display ID.
if (typeof data.taskId !== 'number') throw new Error('Missing numeric taskId');
return parseTaskId(data.taskId);
} catch (error) {
// A timeout or malformed response does not prove that creation failed.
throw new Error(`Submission not confirmed: ${error.message}. No automatic retry was made. Check task records before submitting again.`);
}
}
async function poll(taskId) {
const id = parseTaskId(taskId);
const deadline = now() + pollTimeoutMs;
let attempts = 0;
let errors = 0;
const timedOut = () => new Error(`Stopped waiting for task ${id}; it may still be running. Resume this ID later.`);
while (now() < deadline) {
let retryFloor = 0;
let task;
try {
task = await request(`/tasks/${id}`, 'GET', undefined,
Math.min(requestTimeoutMs, deadline - now()));
errors = 0;
} catch (error) {
if (now() >= deadline) throw timedOut();
if (!(error instanceof ApiError) || !error.transient) throw error;
errors += 1;
if (errors >= maxConsecutiveErrors) {
throw new Error(`Stopped after ${errors} consecutive query errors for task ${id}; resume this ID later.`);
}
retryFloor = error.retryAfterMs;
}
if (now() >= deadline) throw timedOut();
if (task) {
if (task.taskId !== id) throw new Error('Task response ID does not match the requested task');
if (task.status === 'failed') throw new Error(`Task ${id} failed; inspect its record before creating another task.`);
if (task.status === 'ready') {
if (!Array.isArray(task.images) || task.images.length === 0 ||
!task.images.every(url => {
try { return ['https:', 'http:'].includes(new URL(url).protocol); }
catch { return false; }
})) throw new Error(`Task ${id} is ready but has no valid image URLs`);
return task.images;
}
if (!['pending', 'processing'].includes(task.status)) {
throw new Error(`Task ${id} returned an unrecognized status; inspect its record.`);
}
}
// Client policy, not a PixMind latency guarantee or server-side retry feature.
const ceiling = Math.min(10_000, 1_000 * 2 ** Math.min(attempts++, 4));
const delay = Math.max(retryFloor, ceiling * (0.5 + 0.5 * random()));
const remaining = deadline - now();
if (delay >= remaining) {
// Never poll earlier than Retry-After just to fit the local deadline.
await wait(Math.max(0, remaining));
throw timedOut();
}
await wait(delay);
}
throw timedOut();
}
return { submit, poll };
}
export async function main(args = process.argv.slice(2), env = process.env, deps = {}) {
const log = deps.log ?? console.log;
const [mode, rawId] = args;
if (!mode) {
log('Usage: node examples/pixmind-image.mjs submit | resume TASK_ID');
return;
}
if (!((mode === 'submit' && args.length === 1) || (mode === 'resume' && args.length === 2))) {
throw new Error('Use submit, or resume followed by a numeric taskId');
}
const resumeId = mode === 'resume' ? parseTaskId(rawId) : undefined;
const client = createClient({ ...deps, apiKey: env.PIXMIND_API_KEY });
const id = resumeId ?? await client.submit({
model: env.PIXMIND_MODEL || 'nano-banana-pro',
prompt: env.PIXMIND_PROMPT || 'A studio photograph of an unbranded ceramic coffee cup on a plain background',
});
// Save this line in your application record before relying on the polling process.
log(`TASK_ID=${id}`);
log(`Resume without a new generation: node examples/pixmind-image.mjs resume ${id}`);
const images = await client.poll(id);
log(JSON.stringify({ taskId: id, images }, null, 2));
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
main().catch(error => { console.error(error.message); process.exitCode = 1; });
}
เริ่มจากตรวจสอบคำสั่งที่รองรับโดยยังไม่ส่งคำขอใด ๆ:
node examples/pixmind-image.mjs
หลังจากตรวจสอบคีย์ พารามิเตอร์โมเดล และค่าบริการที่คาดไว้แล้ว ให้สร้างงานหนึ่งงาน:
node examples/pixmind-image.mjs submit
เมื่อสร้างงานสำเร็จ ไคลเอนต์จะแสดง TASK_ID ทันที ก่อนเริ่มรอภาพ คัดลอกค่าจริงไปเก็บไว้ในที่ส่วนตัว ตัวอย่างนี้ไม่ได้บันทึกค่าลงไฟล์หรือฐานข้อมูล ดังนั้น หากปิดเทอร์มินัลโดยไม่ได้เก็บผลลัพธ์ไว้ คุณอาจสูญเสียข้อมูลอ้างอิงสำหรับกลับมาตรวจสอบงานต่อ
หากต้องการสอบถามงานต่อ ให้แทนที่ 12345 ด้วยค่าที่บันทึกไว้:
node examples/pixmind-image.mjs resume 12345
คำสั่งนี้ส่งเฉพาะคำขอ GET เพื่อสอบถามงานเท่านั้น ไม่ได้อ่านพรอมต์ใหม่แล้วสร้างภาพอีกภาพ เมื่อจบด้วยสถานะสำเร็จ ควรได้ URL ผลลัพธ์ของงานของคุณเอง บทสอนนี้ไม่ได้ใส่ผลลัพธ์ปลอมจากระบบใช้งานจริงเพื่อใช้เป็นเกณฑ์เปรียบเทียบ
ตัวอย่างนี้จำกัดเวลาของคำขอ HTTP แต่ละครั้งไว้ที่ 30 วินาที และกำหนดเวลาสิ้นสุดการตรวจสอบสถานะเป็นระยะไว้ที่ 5 นาที การลอง GET ใหม่จะเพิ่มช่วงเวลารอแบบทวีคูณพร้อมค่าหน่วงสุ่ม กำหนดเพดานช่วงเวลารอฝั่งไคลเอนต์ไว้ที่ 10 วินาที และใช้เฮดเดอร์การตอบกลับ Retry-After ที่ถูกต้องเมื่อมี หากเซิร์ฟเวอร์ระบุให้รอนานกว่านั้น จะยึดเวลาที่เซิร์ฟเวอร์กำหนดเป็นหลัก หากช่วงเวลานั้นเกินเวลาที่เหลือก่อนถึงกำหนดสิ้นสุดฝั่งไคลเอนต์ ไคลเอนต์จะหยุดโดยไม่สอบถามก่อนเวลา ข้อผิดพลาดชั่วคราวของ GET ครั้งที่ห้าติดต่อกันจะทำให้ความพยายามครั้งนั้นสิ้นสุดลง ซึ่งหมายความว่าสามารถลองใหม่ได้สี่ครั้งในชุดข้อผิดพลาดต่อเนื่องนั้น ค่าเหล่านี้เป็นการตั้งค่าฝั่งไคลเอนต์ ไม่ใช่ข้อตกลงระดับการให้บริการหรือคำรับรองว่าทุกภาพจะเสร็จภายในห้านาที
เลือกนโยบายระยะเวลารอรวมให้เหมาะกับแอปพลิเคชันของคุณ งานที่ใช้เวลานานอาจดำเนินต่อหลังจากเซสชันเทอร์มินัลหรือคำขอเว็บสิ้นสุดลงแล้ว ในบริการที่นำขึ้นใช้งานจริง ให้เก็บ ID งานไว้กับระเบียนงานของคุณเอง และเปิดเอนด์พอยต์สถานะแยกให้เบราว์เซอร์เรียก แทนการให้ผู้ใช้คงการเชื่อมต่อเดียวไว้ตลอด
จัดการข้อผิดพลาดโดยไม่สร้างงานที่มีค่าใช้จ่ายซ้ำ
แยกกรณีที่ไม่แน่ใจว่าสร้างงานสำเร็จหรือไม่ออกจากกรณีสอบถามสถานะไม่สำเร็จ คำขอ GET เพื่อสอบถามงานสามารถส่งซ้ำได้โดยไม่สร้างงานใหม่ แต่ POST สำหรับสร้างงานที่สูญเสียการตอบกลับอาจเริ่มงานไปแล้ว ตัวอย่างนี้จึงไม่ส่งคำขอสร้างงานซ้ำอัตโนมัติ
| สิ่งที่พบ | ขั้นตอนถัดไป |
|---|---|
HTTP 400 |
ตรวจสอบรูปแบบคำขอ ID โมเดล และพารามิเตอร์ที่รองรับ ก่อนตัดสินใจลองใหม่อีกครั้ง |
HTTP 401 หรือ 403 |
ตรวจสอบการยืนยันตัวตนและสิทธิ์เข้าถึง อย่าใส่คีย์ลงในบันทึกขณะแก้ข้อผิดพลาด |
HTTP 404 เมื่อสอบถามงาน |
ตรวจสอบ ID ตัวเลขที่บันทึกไว้และบัญชีที่เป็นเจ้าของงาน อย่าใช้ ID งานของผู้อื่นแทน |
HTTP 429 เมื่อส่ง GET สอบถามงาน |
รอตามเฮดเดอร์ Retry-After ที่ถูกต้องและนโยบายลองใหม่แบบมีขีดจำกัดของไคลเอนต์ |
| ข้อผิดพลาดเครือข่ายหรือข้อผิดพลาดเซิร์ฟเวอร์ชั่วคราวเมื่อส่ง GET สอบถามงาน | ลองสอบถามงานเดิมใหม่โดยเพิ่มช่วงเวลารอ และหยุดเมื่อถึงขีดจำกัดที่กำหนด |
สถานะงาน failed |
หยุดสอบถามและรายงานว่างานล้มเหลว การสร้างใหม่ต้องเป็นการตัดสินใจแยกต่างหาก |
การสร้างงานหมดเวลา JSON ไม่ถูกต้อง หรือไม่มี taskId |
ถือว่ายังไม่ทราบผลการสร้างงาน ตรวจสอบประวัติงานก่อนส่งคำขออีกครั้ง |
อย่าคิดว่าข้อผิดพลาดทั้งหมดมีรูปแบบการตอบกลับเหมือนกัน ในการตรวจสอบระบบใช้งานจริงเมื่อวันที่ 4 กันยายน การเรียก GET /models โดยไม่ยืนยันตัวตนส่งกลับ HTTP 401 พร้อมเพียง code และ message ส่วนการตอบกลับสำหรับงานสื่อที่สำเร็จและยืนยันตัวตนแล้วมี data และ timestamp ด้วย ดังนั้น บทสอนนี้จึงไม่รับรองว่าทุกการตอบกลับจะมี requestId แฟล็ก retryable หรือฟิลด์ราคา กรณีข้อผิดพลาดอื่น ๆ ข้างต้นเป็นคำอธิบายวิธีจัดการฝั่งไคลเอนต์ ไม่ใช่การอ้างว่าได้จำลองทุกกรณีกับระบบใช้งานจริงแล้ว เก็บสถานะ HTTP และข้อความจากแอปพลิเคชันที่ลบข้อมูลอ่อนไหวแล้วเมื่อมี และจัดการเนื้อหาการตอบกลับที่ผิดรูปแบบเป็นข้อผิดพลาด แทนที่จะปล่อยให้โปรแกรมหยุดทำงานภายใน JSON.parse
การตรวจสอบสถานะหมดเวลาหมายความว่าไคลเอนต์หยุดรอ ไม่ได้หมายความว่างานบนเซิร์ฟเวอร์ถูกยกเลิก เก็บ ID ไว้แล้วกลับมาตรวจสอบต่อภายหลัง สถานะที่ไม่รู้จักก็ไม่ใช่หลักฐานว่างานสำเร็จเช่นกัน ไคลเอนต์นี้จะหยุดและรายงานสถานะดังกล่าวเพื่อให้ตรวจสอบ อย่าปล่อยให้โปรแกรมสรุปว่า “เสร็จสมบูรณ์” เพียงเพราะไม่มีค่าหนึ่งอยู่ในคำสั่ง switch
สำหรับการแก้ปัญหา ให้เก็บ ID งาน ID โมเดล การดำเนินการ สถานะ HTTP และเวลาโดยประมาณ หลีกเลี่ยงการบันทึกเฮดเดอร์ยืนยันสิทธิ์หรือเนื้อหาคำขอทั้งหมดที่มีพรอมต์ส่วนตัว ก่อนแชร์ข้อมูล ให้ตรวจสอบว่าเนื้อหาการตอบกลับสำหรับวินิจฉัยหรือ URL ผลลัพธ์มีข้อมูลอ่อนไหวหรือไม่
ปรับรูปแบบนี้สำหรับวิดีโอและแชต
วิดีโอสามารถใช้แนวทางส่งคำขอแล้วตรวจสอบสถานะเป็นระยะแบบเดียวกันได้ แต่ต้องมีการตรวจสอบคำขอและตัวอ่านผลลัพธ์ของตัวเอง ตรวจสอบสื่ออินพุตที่จำเป็น ระยะเวลา ความละเอียด และตัวเลือกเสียงของโมเดลวิดีโอที่เลือก แทนที่จะสมมติว่าสามารถนำฟิลด์จากเนื้อหาคำขอภาพมาใช้ซ้ำได้ วิดีโอที่เสร็จแล้วใช้ data.videoUrl ไม่ใช่ data.images
คู่มือ Video Agent อธิบายขั้นตอนสร้างสรรค์แบบโต้ตอบ ซึ่งช่วยกำหนดภาพช็อตที่ต้องการก่อนพัฒนาระบบสร้างวิดีโออัตโนมัติได้ แต่การมีฟีเจอร์ใน Agent ไม่ใช่หลักฐานว่ามีพารามิเตอร์ API ชื่อเดียวกัน
แชตเป็นเส้นทางการเชื่อมต่อแยกต่างหาก เส้นทาง /chat/completions ที่ระบุในเอกสารรองรับรูปแบบที่เข้ากันได้กับ OpenAI และส่งกลับคำตอบแชต หรือสตรีมเมื่อมีการขอแบบสตรีม อย่าส่งการตอบกลับนั้นเข้าไปในตัวแยกวิเคราะห์ data.taskId ของไคลเอนต์ภาพ การใช้ URL หลักเดียวกันไม่ได้หมายความว่าทุกเอนด์พอยต์มีโครงสร้างการตอบกลับเหมือนกัน
จำกัดขอบเขตไคลเอนต์ภาพให้ชัดเจนจนกว่าจะตรวจสอบเส้นทางนี้เรียบร้อย เพิ่มตัวปรับการเชื่อมต่อวิดีโอหรือไคลเอนต์แชตแยกต่างหาก พร้อมการทดสอบสำหรับรูปแบบการตอบกลับของแต่ละส่วน การแยกเช่นนี้ทำให้เข้าใจการทำงานได้ง่ายกว่าการมีฟังก์ชันเดียวที่คาดเดาว่าออบเจ็กต์ที่ได้รับกลับมาเป็นงานภาพ งานวิดีโอ หรือข้อความแชต
ตรวจสอบการเชื่อมต่อและเลือกขั้นตอนถัดไป
การทดสอบจำลองในเครื่องตรวจสอบพฤติกรรมของไคลเอนต์โดยไม่ใช้ยอดคงเหลือ API ชุดทดสอบที่ให้มาผ่านการทดสอบ 29 รายการในสภาพแวดล้อม Windows และ Node.js ที่ระบุ ครอบคลุมเส้นทางส่งคำขอด้วย POST เพียงครั้งเดียว การตรวจสอบต่อด้วย GET เท่านั้น การตอบกลับที่ผิดรูปแบบ สถานะสิ้นสุดแบบล้มเหลว กำหนดเวลาสิ้นสุด และขีดจำกัดการลองใหม่ บันทึก ไฟล์ทดสอบที่ให้มา ไว้ข้าง ซอร์สโค้ดไคลเอนต์ แล้วเรียกใช้ชุดทดสอบจากโฟลเดอร์บทสอน:
node --test examples/pixmind-image.test.mjs
การทดสอบจำลองไม่ได้ยืนยันความพร้อมใช้งานหรือการเรียกเก็บค่าบริการในระบบใช้งานจริง การตรวจสอบอีกครั้งที่แยกออกมาและควบคุมขอบเขตไว้เมื่อวันที่ 4 กันยายน ค.ศ. 2026 ใช้ไคลเอนต์เดียวกัน โดยมีขอบเขตที่บันทึกไว้ดังนี้:
| รายการทดสอบ | ผลที่สังเกตได้ |
|---|---|
| คำขอ | POST /generations หนึ่งครั้ง โดยใช้ nano-banana-pro, type: "image", aspectRatio: "1:1" และ resolution: "1K" |
| พรอมต์ | A studio photograph of an unbranded ceramic coffee cup on a plain background |
| ข้อมูลอ้างอิงสำหรับกลับมาตรวจสอบต่อ | ID งานตัวเลข 64114 ซึ่งเก็บไว้ก่อนตรวจสอบสถานะ เป็นข้อมูลอ้างอิงหลักฐาน ไม่ใช่ ID สำหรับให้ผู้อ่านสอบถาม |
| การเสร็จสิ้น | สอบถามงานเดิมด้วย GET แปดครั้ง สถานะสุดท้ายเป็น ready พร้อม URL หนึ่งรายการใน data.images |
| การตรวจสอบผลลัพธ์ | ภาพที่ได้รับกลับมาโหลดได้ที่ขนาด 1024 × 1024 พิกเซล และมองเห็นเป็นถ้วยเซรามิกบนฉากหลังเรียบ |
| การเรียกเก็บค่าบริการ | การตอบกลับราคา API ระบุ 120 พอยต์สำหรับการตั้งค่านี้ และบัญชีรายการ API ที่เชื่อมโยงกับงานบันทึกการหัก 120 พอยต์ |
การตรวจสอบนี้ใช้คีย์ API ของโครงการที่มีอยู่แล้วหนึ่งคีย์ พรอมต์ที่ไม่มีข้อมูลอ่อนไหว ไม่มีการอัปโหลดไฟล์อ้างอิง และไม่มีการลองสร้างงานใหม่อัตโนมัติ รายการเรียกเก็บค่าบริการถูกจับคู่กับ ID งาน ไม่ได้อนุมานจากยอดคงเหลือที่แสดงใน Studio มีเพียงการตั้งค่านี้และเส้นทางสร้างภาพที่สำเร็จเท่านั้นที่ได้รับการทดสอบจริง วิดีโอ แชต การเรียกเก็บค่าบริการเมื่องานล้มเหลว การคืนเงิน และความน่าเชื่อถือเมื่อเรียกซ้ำไม่ได้รับการทดสอบ ตรวจสอบราคาอีกครั้งก่อนส่งคำขอของคุณเอง อย่าถือว่าค่าบริการในอดีตนี้เป็นราคาเสนอที่ยังใช้ได้ตลอด
ก่อนเชื่อมต่อตัวอย่างเข้ากับขั้นตอนที่ผู้ใช้จริงจะใช้งาน ให้ตรวจสอบว่า:
- คีย์อยู่บนเซิร์ฟเวอร์ และไม่ปรากฏในชุดไฟล์ฝั่งไคลเอนต์ ภาพหน้าจอ หรือบันทึกสาธารณะ
- โมเดลที่เลือกและพารามิเตอร์ทุกตัวรองรับโดยบริการ API ปัจจุบัน
- เมื่อระบบรับคำขอแล้ว คุณมี ID งานตัวเลขที่สามารถนำกลับมาใช้ตรวจสอบต่อได้
resumeไม่ส่ง POST เพื่อสร้างงาน แม้จะเกิดข้อผิดพลาดชั่วคราวระหว่างสอบถาม- งานล้มเหลว ผลลัพธ์ว่าง และการหมดเวลาฝั่งไคลเอนต์ให้ผลวินิจฉัยที่แยกกันได้
- การสูญเสียการตอบกลับจากคำขอสร้างงานไม่ทำให้เกิดการส่งคำขอครั้งที่สองอัตโนมัติ
สำหรับการตรวจสอบจริงภายใต้ขอบเขตที่ควบคุมไว้ของคุณเอง ให้บันทึกบัญชี โมเดล ขอบเขตค่าใช้จ่ายที่อนุมัติ คำขอที่ลบข้อมูลอ่อนไหวแล้ว ID งาน การตอบกลับในสถานะสิ้นสุด และรายการเรียกเก็บค่าบริการจริง ตรวจดูผลลัพธ์แยกจากการตรวจว่ามี URL อยู่ อย่าเรียก “การทดสอบเบื้องต้น” ซ้ำ ๆ โดยไม่คำนึงว่าการส่งคำขอใหม่แต่ละครั้งอาจสร้างงานที่มีค่าใช้จ่าย
เมื่อพร้อมแล้ว ให้ สร้างคีย์ API และเลือกวิธีส่งคำขอเพียงวิธีเดียว เริ่มพัฒนาจากขั้นตอนที่บันทึกงานไว้ก่อนเพิ่มคิว การอัปโหลด หรือการประมวลผลเป็นชุด หากขั้นตอนถัดไปคือการนำไฟล์ที่สร้างขึ้นไปใช้ในกระบวนการตรวจทานงานสร้างสรรค์ ขั้นตอนสร้างโฆษณาสินค้าด้วย Canvas เป็นตัวอย่างแยกต่างหากที่มีมนุษย์เป็นผู้ดำเนินการ ไม่ใช่คำรับรองว่าสามารถเรียกใช้งานโครงการ Canvas ผ่าน API นี้ได้
ผู้รับผิดชอบด้านบรรณาธิการ: PixMind Editorial Team เป็นชื่อผู้เขียนในนามองค์กรของบทสอนนี้ หลักฐานที่ใช้ประกอบด้วยเอกสารทางการ การพัฒนาส่วนควบคุมงานสื่อของโครงการ การทดสอบจำลองในเครื่อง 29 รายการ และการตรวจสอบระบบใช้งานจริงเพียงครั้งเดียวตามที่อธิบายข้างต้น ทั้งหมดได้รับการทบทวนเมื่อวันที่ 4 กันยายน ค.ศ. 2026 บันทึกงานและการเรียกเก็บค่าบริการที่ลบข้อมูลอ่อนไหวแล้วได้รับการเก็บรักษาไว้เพื่อการตรวจสอบด้านบรรณาธิการ บทสอนนี้ไม่ได้อ้างคุณวุฒิของวิศวกรรายบุคคล ผลเปรียบเทียบคุณภาพโมเดล หรือผลวัดประสิทธิภาพ



