Responses API กับ Chat Completions ต่างกันอย่างไร: คู่มือย้ายระบบ (2026)
Responses API กับ Chat Completions ต่างกันอย่างไร: คู่มือย้ายระบบ (2026)
OpenAI แนะนำให้โปรเจกต์ใหม่ใช้ Responses API พร้อมยืนยันว่า Chat Completions ยังคงรองรับต่อไป สอง endpoint จะอยู่ร่วมกันระยะยาว แต่โครงสร้างฟิลด์ การจัดการ state และระบบ tool ต่างกัน บทความนี้แยกความต่างเป็น 4 มิติ แสดงโค้ดที่ต้องแก้ตอนย้ายระบบ และจบด้วยตารางช่วยตัดสินใจ — ตัวอย่างทุกตัวรันได้จริงบน endpoint ของ EXAIV
นิยาม: Responses API คือ unified interface ที่ OpenAI เปิดตัวหลัง Chat Completions: รับ input เป็นอาร์เรย์ input, ส่ง output เป็นรายการ output ที่มี type ชัดเจน, เก็บ state ของบทสนทนาฝั่ง server โดยดีฟอลต์ (store) และมี hosted tools อย่าง web search, file search, code interpreter ในตัว ส่วน Chat Completions เป็นโปรโตคอลแบบ stateless: ทุก request ต้องแนบ messages ย้อนหลังครบ และ client เป็นผู้จัดการ context เอง
ตารางเดียวเห็นความต่างทั้งหมด
| มิติ | Chat Completions | Responses API |
|---|---|---|
| Endpoint | /v1/chat/completions | /v1/responses |
| ฟิลด์ input | อาร์เรย์ messages | input (string หรืออาร์เรย์ข้อความ) |
| โครงสร้าง output | choices[0].message.content | รายการ output แบบมี type, อ่าน output_text |
| การจัดการ state | stateless, ส่งประวัติซ้ำทุกครั้ง | store: true ดีฟอลต์, ใช้ previous_response_id เชื่อมรอบ |
| Hosted tools | ไม่มี | web search, file search, code interpreter, computer use, remote MCP |
| รูปแบบ function | โครงสร้าง tools รุ่นเก่า | โครงสร้างใหม่ คล้ายแต่ไม่เหมือน |
| ผลลัพธ์หลายตัว | รองรับ n > 1 | ถูกถอดออก เหลือผลเดียว |
จุดยืนทางการ
OpenAI ระบุว่า "Chat Completions ยังรองรับต่อไป แนะนำโปรเจกต์ใหม่ใช้ Responses" ไม่มีกำหนดบังคับย้าย แต่ความสามารถใหม่จะมีเฉพาะฝั่ง Responses ข้อเทียบเคียง: Assistants API ปิดตัวไปแล้วเมื่อ 26 สิงหาคม 2026 — ทิศทางจึงชัดเจน
เปรียบเทียบโค้ด: งานเดียวกันสองแบบ
Chat Completions (EXAIV endpoint /api/ai/v1/chat/completions):
curl https://exaiv.com/api/ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-6-sol",
"messages": [
{ "role": "user", "content": "อธิบายว่า AI gateway คืออะไรในหนึ่งประโยค" }
]
}'
Responses API (EXAIV endpoint /api/ai/v1/responses):
curl https://exaiv.com/api/ai/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-6-sol",
"input": "อธิบายว่า AI gateway คืออะไรในหนึ่งประโยค"
}'
ระดับ SDK: client.chat.completions.create(...) เปลี่ยนเป็น client.responses.create(...) และอ่านผลลัพธ์จาก completion.choices[0].message.content เปลี่ยนเป็น response.output_text — รายละเอียดพารามิเตอร์ดูได้ที่เอกสาร Chat Completions และเอกสาร Responses
ย้ายระบบ 4 ขั้น
messages→input: ข้อความล้วนส่งเป็น string ได้เลย กรณีมีประวัติให้เปลี่ยนชื่ออาร์เรย์messagesเป็นinputโครงสร้าง role/content คงเดิม- เปลี่ยนจุดอ่านผลลัพธ์:
choices[0].message.content→ วนoutputกรองtype === "message"หรือใช้ field สำเร็จรูปoutput_text - ย้าย system prompt ไป
instructions: ข้อความrole: "system"ใน Chat มีพารามิเตอร์ระดับบนสุดแยกต่างหากใน Responses - เขียน tool definition ใหม่: สองรุ่นคล้ายกันแต่ไม่เท่ากัน ตรวจทีละฟิลด์; hosted tools ของ Responses (web_search ฯลฯ) เปิดด้วยชื่อ ไม่ต้องเขียน orchestration เอง
Streaming event ต่างกัน
ทั้งสอง endpoint ใช้ SSE แต่ชนิด event ต่างกันทั้งหมด (Chat ส่ง delta ทั่วไป, Responses ส่ง event ระบุชื่ออย่าง response.output_text.delta) ตัวแยก SSE ต้องแก้แยกตอนย้าย
เลือกอย่างไร: ตามสถานการณ์
| สถานการณ์ | คำแนะนำ | เหตุผล |
|---|---|---|
| โค้ดเดิมใช้ได้ปกติ | คงใช้ Chat Completions | ไม่มีวันปิด service ย้ายทีละ flow ได้ |
| โปรเจกต์ใหม่, agent หลายขั้น, ต้องการ hosted tools | Responses | tools สำเร็จรูป + state ฝั่ง server ลดงานเขียนเอง |
| โค้ดต้องรันหลายค่าย model | Chat Completions | เป็นมาตรฐาน de facto ของวงการ ใช้ได้กว้างสุด |
| ต้องการหลายคำตอบต่อ request (n > 1) | Chat Completions | Responses ถอดพารามิเตอร์นี้ออกแล้ว |
EXAIV ให้บริการทั้งสอง endpoint ใช้ API key เดียวกัน จึงไม่ต้องเลือกข้างถาวร: ของเดิมคงใช้ Chat Completions ของใหม่ใช้ Responses และสลับโมเดลเบื้องหลังได้จากหน้าโมเดล
ทำไมจึงมีสอง interface
ที่มาเชิงวิวัฒนาการช่วยให้ตัดสินใจได้ Chat Completions เป็น interface ยุคแรกของ OpenAI ที่เพราะโครงสร้างเรียบง่ายและใช้ข้ามผู้ให้บริการได้ จึงกลายเป็นมาตรฐาน de facto ที่วงการเลียนแบบ — แทบทุกผู้ให้บริการโมเดลวันนี้มี endpoint สไตล์ messages Responses API คือการออกแบบใหม่ของ OpenAI สำหรับยุค agent: การที่ "โมเดลอาจต้องทำหลายอย่าง" (เรียก tool, ค้นข้อมูล, รันโค้ด) กลายเป็นพลเมืองชั้นหนึ่งในชั้น interface โดยย้ายการจัดการ state และการรัน tool กลับไปฝั่ง server สอง interface นี้เป้าหมายต่างกัน — นั่นคือเหตุผลที่พวกมันอยู่ร่วมกันได้
ตารางแมปพารามิเตอร์ฉบับลงมือ
ความเทียบเคียงที่ใช้บ่อยที่สุดตอนย้ายระบบ:
| Chat Completions | Responses API | หมายเหตุ |
|---|---|---|
messages | input | ข้อความล้วนส่ง string ได้ |
role: "system" | instructions | system prompt มีพารามิเตอร์แยก |
choices[0].message.content | output_text / วน output | จุดอ่านผลลัพธ์ต่างกัน |
max_tokens | max_output_tokens | ชื่อใหม่ ความหมายใกล้กัน |
response_format | text.format | structured output โครงสร้างเปลี่ยน |
tools + tool_choice | tools รูปแบบใหม่ | บทบาทคล้าย ตรวจทีละฟิลด์ |
n (หลายคำตอบ) | ถูกถอดออก | ถ้าต้องใช้ให้คงอยู่กับ Chat Completions |
| — | store / previous_response_id | ความสามารถ state เฉพาะของ Responses |
ฟิลด์ที่แม่นยำดูที่เอกสารอ้างอิง ย้ายระบบแบบตรวจทีละฟิลด์
เจาะลึกการจัดการ state: store กับ previous_response_id
นี่คือความต่างเชิงปรัชญาที่ลึกที่สุด Chat Completions ตั้งสมมติว่า "server ไม่จำอะไรเลย": ทุกรอบคุณต้องส่งประวัติเต็มซ้ำ การจัดการ context window และควบคุมต้นทุนอยู่ที่ฝั่งคุณ Responses API เก็บบทสนทนาฝั่ง server ตั้งแต่ต้น (store: true) รอบถัดไปอ้าง previous_response_id แค่เดียว server จะพาบทสนทนาและบริบท tool ครบถ้วนมาคิดต่อ
ผลต่อผู้พัฒนาจริง ๆ:
- โค้ดน้อยลง: โมดูลที่คุยกันว่าต้องรักษา messages array, ตัดประวัติ, บีบ context ลบทิ้งได้
- token น้อยลง: server ใช้บริบทเดิมซ้ำได้ ค่าส่งซ้ำหายไป
- ระวังเรื่องข้อมูล: บทสนทนาถูกเก็บฝั่ง server ทีมที่มีข้อกำหนด data residency ใช้
store: falseได้ แลกกับการกลับไปโหมดส่งเต็มทุกรอบ
กับดักย้ายระบบ 5 อันดับ
- ผลลัพธ์ว่าง: ยังอ่าน
choices[0].message.contentแต่เนื้อหาอยู่ในoutput - tool call เงียบ: รูปแบบ tools สองรุ่นไม่เหมือน ก๊อปฟิลด์ตรง ๆ จะพังเงียบ ๆ ต้องตรวจทีละฟิลด์
- ตัวแยก SSE พัง: ชนิด event ต่างกันสิ้นเชิง ตรรกะ
deltaไปใช้กับresponse.output_text.deltaไม่ได้ - พังเพราะหลายคำตอบ: พารามิเตอร์
nถูกถอด ตรรกะที่พึ่งพาต้องเขียนใหม่หรืออยู่กับ Chat - state รั่ว:
storeเปิดเป็นค่าเริ่มต้น ข้อมูลทดสอบจะถูกเก็บ — งานที่แคร์ข้อมูลปิดมันชัดเจน
FAQ
ย้าย streaming ทีละ event
ชื่อ event คือจุดที่กินเวลาย้ายระบบมากที่สุด แผนที่คร่าว ๆ:
| เหตุการณ์ | Event ฝั่ง Chat Completions | Event ฝั่ง Responses |
|---|---|---|
| ข้อความคำตอบมาเป็นชิ้น ๆ | chat.completion.chunk (delta) | response.output_text.delta |
| โมเดลต้องการเรียก tool | delta แบบ tool_calls ใน chunk | response.output_item.added (type: function_call) |
| tool คืนผลลัพธ์ | คุณ append message แล้ว request ใหม่ | item function_call_output แล้วไหลต่อใน call เดียว |
| stream จบ | data: [DONE] | response.completed |
สองความต่างเชิงโครงสร้างที่ต้องซึมซับ: delta ฝั่ง Chat เป็นเศษข้อความนิรนามที่คุณต่อเอง ขณะที่ event ของ Responses ระบุชื่อสิ่งที่มันแบกมา; และใน Responses หนึ่ง turn ที่มีหลาย tool เป็นวงจร request เดียว ไม่ใช่หลายรอบ request/response — ตัวแยก SSE, ตรรกะ retry และการจัดการ timeout ทั้งหมดต้องเขียนใหม่กับชื่อ event ใหม่ ส่วนนี้แหละที่ต้นทุนจริงมักมากกว่าประมาณการ
ใช้ Responses ผ่าน gateway
สามเรื่องที่ควรรู้เมื่อ endpoint ถูก proxy:
- hosted tools รันฝั่งต้นทาง: web search, code interpreter และเพื่อนรันฝั่ง OpenAI; gateway ส่งต่อ request และ stream ผลกลับมาโดยไม่แตะต้อง
- state ฝั่ง server ยังอยู่ต้นทาง:
previous_response_idอ้าง state ที่เก็บฝั่ง OpenAI; gateway ถ่ายทอดแบบโปร่งใส - key และการเรียกเก็บเงินเป็นของ gateway: key EXAIV เดียวใช้ได้ทั้งสอง endpoint คิดเงินตามโมเดลและ token ไม่ขึ้นกับ endpoint — ราคาสดที่หน้าราคา
เช็กลิสต์ก่อนย้าย
ห้าคำถามตามลำดับ — เจอคำตอบว่า "ยัง" ที่ข้อไหน ให้แก้ชั้นนั้นก่อน:
- ยิงข้อความล้วนบน endpoint ใหม่ผ่านไหม (ไม่มี tool ไม่มี streaming)?
- จุดอ่านผลลัพธ์คืนข้อความจริงไหม (ไม่ใช่
undefined)? - tool definition parse ผ่านโดยไม่มี schema error ไหม?
- รอบ tool เต็มหนึ่งรอบจบไหม (เรียก → รัน → ต่อ)?
- streaming เรนเดอร์ครบต้นจนจบด้วยชื่อ event ใหม่ไหม?