การส่งต่องานระหว่างเอเจนต์: ส่งผ่านสถานะ ไม่ใช่แค่บทสรุป
เอเจนต์วิจัยพบบัญชีลูกค้า ยืนยันแผน และดึงใบแจ้งหนี้สี่ฉบับล่าสุด จากนั้นส่งต่อให้เอเจนต์ฝ่ายเรียกเก็บเงินพร้อมข้อความสั้นๆ ว่า “ลูกค้าต้องการคืนเงิน” เอเจนต์ฝ่ายเรียกเก็บเงินซึ่งไม่รู้ข้อมูลบัญชี แผน หรือใบแจ้งหนี้ จึงเริ่มต้นด้วยการขอรหัสบัญชีอีกครั้ง นี่คือปัญหาการส่งต่องาน: ข้อมูลที่รวบรวมไว้ถูกทิ้งที่ขอบเขต ทำให้เสียทั้งค่า API ซ้ำและค่าใช้จ่ายจากข้อผิดพลาดของเอเจนต์ตัวถัดไป
คู่มือนี้อธิบายว่าอะไรควรคงอยู่ระหว่างการส่งต่องาน วิธีส่งผ่านสถานะสามรูปแบบ เหตุใดบทสรุปจึงสูญเสียข้อมูล และวิธีทดสอบขอบเขตระหว่างเอเจนต์ให้เชื่อถือได้ ปัญหานี้เป็นเวอร์ชันหลายเอเจนต์ของประเด็นสถานะหาย ซึ่งอธิบายไว้ในบทความ เหตุใดเอเจนต์จึงล้มเหลวในการทำงานจริง
แนวทางที่คุ้มค่าที่สุดมักไม่ใช่การส่งข้อมูลทั้งหมด แต่เป็นการส่งผ่านตัวระบุแทน วิธีนี้จะใช้ได้ก็ต่อเมื่อเอเจนต์ทุกตัวดึงระเบียนเดียวกันผ่าน API และสิทธิ์ที่เหมาะสมได้
สิ่งที่ต้องข้ามขอบเขต
การคัดลอกบทสนทนาทั้งหมดผิดพลาดพอๆ กับการไม่ส่งอะไรเลย เอเจนต์ผู้รับจะต้องอ่านบริบทจำนวนมากเพื่อหาข้อมูลสำคัญ และอาจตัดสินใจใหม่ในเรื่องที่เอเจนต์ก่อนหน้าตัดสินใจไปแล้ว
ควรแยกข้อมูลส่งต่อเป็น 4 หมวดหมู่:
- ตัวระบุ (Identifiers) — รหัสบัญชี รหัสคำสั่งซื้อ รหัสงาน และหมายเลขตั๋ว เป็นข้อมูลขนาดเล็ก เสถียร และช่วยให้ผู้รับดึงข้อมูลที่ต้องการได้ ตัวระบุคือสิ่งที่มีค่าที่สุด แต่ก็มักถูกละทิ้งมากที่สุด
- การตัดสินใจที่ทำไปแล้ว (Decisions already made) — เช่น “ลูกค้ามีสิทธิ์ได้รับการคืนเงินภายใต้นโยบาย 3” ผู้รับไม่ควรนำเรื่องนี้มาพิจารณาซ้ำ เพราะอาจเกิดการตัดสินใจขัดแย้งกัน
- ข้อจำกัด (Constraints) — งบประมาณสูงสุด การอนุมัติ และการดำเนินการที่ทำไปแล้ว การสูญเสียข้อมูลเหล่านี้อาจทำให้เกิดการเรียกเก็บเงินซ้ำหรือขออนุมัติซ้ำ เชื่อมโยงโดยตรงกับแนวคิด idempotency สำหรับเอเจนต์ AI
- คำถามที่ยังเปิดอยู่ (Open questions) — สิ่งที่เอเจนต์แรกยังแก้ไม่ได้ ควรระบุให้ชัดเจน เพื่อป้องกันไม่ให้ผู้รับเดาคำตอบเอง
สิ่งที่ไม่จำเป็นต้องส่งต่อ ได้แก่ การตอบกลับ API ดิบ บันทึกการให้เหตุผล และข้อมูลที่เอเจนต์ผู้รับดึงได้เองจากการเรียก API เพียงครั้งเดียว
วิธีส่งผ่านสถานะ 3 รูปแบบ
1. ส่งต่อบทสนทนาทั้งหมด
วิธีนี้เริ่มต้นง่ายและใช้ได้กับเอเจนต์สองตัวในงานสั้นๆ แต่จะล้มเหลวเมื่อประวัติยาวขึ้น ผู้รับต้องใช้ทรัพยากรส่วนใหญ่ไปกับการอ่านบริบท และข้อมูลสำคัญอาจถูกฝังอยู่ตรงกลาง
บทความเรื่อง การเก็บการตอบสนองของเครื่องมือไว้นอกหน้าต่างบริบท อธิบายว่าทำไมโมเดลจึงสูญเสียข้อมูลจากส่วนกลางของบริบทได้ง่าย
2. ส่งต่อบทสรุป
เอเจนต์แรกเขียนข้อความส่งต่องาน แล้วเอเจนต์ที่สองเริ่มต้นจากข้อความนั้น นี่เป็นค่าเริ่มต้นของหลายเฟรมเวิร์ก แต่โมเดลมักสรุปเป็นเรื่องราวและละเว้นตัวระบุ เช่น ได้ข้อความว่า:
ลูกค้าเป็นสมาชิกมาสองปีแล้วและรู้สึกหงุดหงิด
แทนที่จะเป็น:
บัญชี 8812, แผน Pro, ใบแจ้งหนี้ 4 ฉบับ และอนุมัติการคืนเงินสำหรับ
inv_44
บทสรุปจึงเหมาะสำหรับเจตนาและรายละเอียดปลีกย่อย แต่ไม่ควรเป็นที่เก็บข้อมูลสำคัญเพียงแห่งเดียว
3. ส่งต่ออ็อบเจกต์ที่มีโครงสร้าง
เอเจนต์แรกกรอกข้อมูลลงในสคีมา และเอเจนต์ที่สองอ่านฟิลด์แทนการตีความเรียงความ วิธีนี้ต้องตั้งค่ามากกว่า แต่ตรวจสอบได้และเชื่อถือได้กว่า:
{
"task_id": "task_2026_08_26_0031",
"from_agent": "research",
"to_agent": "billing",
"entities": {
"customer_id": "cus_8812",
"invoice_ids": ["inv_41", "inv_42", "inv_43", "inv_44"],
"subscription_id": "sub_119"
},
"decisions": [
{
"decision": "refund_eligible",
"value": true,
"basis": "policy 3.2, charged twice in one cycle"
}
],
"constraints": {
"max_refund_cents": 4900,
"human_approval_granted": false,
"actions_taken": ["read_invoices"]
},
"open_questions": [
"Customer has not confirmed which invoice to refund"
],
"summary": "Customer cus_8812 was double-charged in August. Refund of one invoice is approved under policy 3.2, up to 4900 cents. Awaiting the customer's choice of invoice."
}
ฟิลด์ summary ยังมีประโยชน์สำหรับรายละเอียดที่สคีมารองรับไม่ได้ แต่ควรอยู่เคียงข้างฟิลด์ที่มีโครงสร้าง ไม่ใช่ใช้แทนที่ทั้งหมด
ตรวจสอบอ็อบเจกต์ก่อนส่งต่อเสมอ หาก customer_id หายไป ให้แจ้งข้อผิดพลาดทันทีที่ขอบเขต แทนที่จะปล่อยให้เอเจนต์ตัวที่สองค้นพบหลังจากเรียก API ไปแล้วหลายครั้ง
ส่งผ่านข้อมูลอ้างอิง ไม่ใช่เพย์โหลด
รูปแบบที่แข็งแกร่งที่สุดแทบไม่ต้องส่งข้อมูลจริงเลย ให้ส่ง ID แล้วให้ผู้รับดึงข้อมูลที่ต้องการ:
- สถานะยังเป็นปัจจุบัน หากข้อมูลเปลี่ยนระหว่างเอเจนต์สองตัว
- เพย์โหลดมีขนาดเล็กกว่าการส่งโทเค็นจำนวนมาก
- ทุกการอ่านปรากฏเป็น API call จึงตรวจสอบเส้นทางได้ง่ายกว่าข้อความที่คัดลอกในพรอมต์
ข้อกำหนดสำคัญคือเอเจนต์ทุกตัวต้องเข้าถึง API เดียวกันด้วยสิทธิ์ที่ถูกต้อง เอเจนต์แต่ละตัวควรมีข้อมูลรับรองของตัวเองและถูกจำกัดตามหน้าที่ บทความเรื่อง คีย์ API ที่มีสิทธิ์น้อยที่สุดสำหรับเอเจนต์ อธิบายแนวทางนี้เพิ่มเติม
ตัวอย่างเช่น:
- เอเจนต์ฝ่ายเรียกเก็บเงินที่ถือโทเค็นวิจัยแบบอ่านอย่างเดียวไม่สามารถออกใบคืนเงิน
- เอเจนต์วิจัยที่ถือโทเค็นเรียกเก็บเงินจะเพิ่มรัศมีความเสียหายโดยไม่จำเป็น
หากการดึงข้อมูลซ้ำมีราคาแพงหรือใช้เวลานาน ให้แคชระเบียนไว้ใน orchestrator แล้วส่งผ่านตัวอ้างอิงไปยังรายการแคช ผู้รับยังคงอ่านข้อมูลอย่างชัดเจน แต่การอ่านครั้งที่สองมีต้นทุนต่ำ
จุดที่การส่งต่องานมักล้มเหลว
ตัวระบุหายไป
บทสรุปพูดถึง “ลูกค้า” โดยไม่มี ID ผู้รับจึงค้นหาตามชื่อ พบสองรายการ และเลือกผิดตัว
การป้องกัน: ทำให้ ID ของเอนทิตีที่จำเป็นเป็นฟิลด์บังคับ และตรวจสอบก่อนส่งต่องาน
การกระทำซ้ำซ้อน
เอเจนต์แรกส่งอีเมลไปแล้ว แต่ไม่ได้บันทึกไว้ในอ็อบเจกต์ส่งต่อ เอเจนต์ที่สองจึงส่งซ้ำ
การป้องกัน: บันทึก actions_taken ตรวจสอบก่อนการเขียนทุกครั้ง และใช้ idempotency key เพื่อให้การทำซ้ำไม่ก่อให้เกิดผลเสีย
การอนุมัติหายไป
มนุษย์อนุมัติการคืนเงินระหว่างที่เอเจนต์แรกทำงาน แต่เอเจนต์ที่สองไม่ทราบและขออนุมัติอีกครั้ง ผู้ใช้จะมองว่าระบบไม่รับฟัง
การป้องกัน: บันทึกการอนุมัติเป็นข้อจำกัดของงาน ไม่ใช่คุณสมบัติของเอเจนต์
การสร้างข้อมูลอย่างมั่นใจ
ผู้รับต้องการค่าที่ไม่ได้อยู่ในข้อมูลส่งต่อ แต่แทนที่จะสอบถามกลับ กลับสร้างค่าที่เข้ากับเรื่องราวขึ้นมาเอง นี่เป็นความล้มเหลวที่อันตรายที่สุด เพราะผลลัพธ์อาจดูเหมือนงานสำเร็จแล้ว
การป้องกัน: ใช้ open_questions และกำหนดกฎในพรอมต์อย่างชัดเจนว่า หาก ID ที่จำเป็นหายไป ให้หยุดและสอบถาม ห้ามเดา
การวนซ้ำระหว่างเอเจนต์ทำให้ปัญหาเหล่านี้รุนแรงขึ้น เมื่อ A ส่งต่อให้ B และ B ส่งกลับให้ A สถานะอาจเสื่อมลงทุกครั้งเหมือนการคัดลอกสำเนา กำหนดจำนวนการส่งต่อสูงสุด และส่งผ่านอ็อบเจกต์งานต้นฉบับตลอดกระบวนการ แทนการสร้างอ็อบเจกต์ใหม่ที่แต่ละขอบเขต
ทดสอบขอบเขต ไม่ใช่แค่เอเจนต์
การส่งต่องานคือจุดเชื่อมต่อ จึงควรทดสอบเหมือนการทดสอบอินเทอร์เฟซ
ตรวจสอบอ็อบเจกต์ส่งต่อ
รันเอเจนต์แรกด้วยสถานการณ์ที่กำหนด แล้วตรวจสอบอ็อบเจกต์ที่สร้างขึ้นแบบกำหนดตายตัว:
- มีตัวระบุที่จำเป็น
- มีการบันทึกการตัดสินใจและพื้นฐานรองรับ
- มีรายการการดำเนินการที่ทำไปแล้ว
- มีคำถามที่ยังเปิดอยู่
แม้เอเจนต์จะให้ผลลัพธ์แบบไม่กำหนดตายตัว แต่เพย์โหลดที่มีโครงสร้างสามารถตรวจสอบได้ แนวทางเพิ่มเติมอยู่ใน คู่มือทดสอบเอเจนต์ที่ไม่ใช่แบบกำหนดตายตัว
ทดสอบผู้รับแยกกัน
ส่งอ็อบเจกต์ที่ถูกต้องให้เอเจนต์ฝ่ายเรียกเก็บเงิน แล้วตรวจสอบการทำงาน จากนั้นส่งอ็อบเจกต์ที่จงใจเสียหาย เช่น ไม่มี customer_id และยืนยันว่าผู้รับสอบถามแทนการเดา การทดสอบกรณีเสียหายช่วยจับการสร้างข้อมูลที่ไม่ถูกต้อง
ใช้ม็อกแทนระบบจริง
การทดสอบที่ออกใบคืนเงินจริงไม่ควรเป็นสิ่งที่ทำเพียงครั้งเดียว ให้ชี้เอเจนต์ทั้งสองไปยัง endpoint จำลอง เพื่อให้ชุดทดสอบทำงานได้ทุกครั้งที่มีการเปลี่ยนแปลง
ทำตามแนวทาง การรันเอเจนต์กับม็อกแทนที่จะเป็นระบบการผลิต ได้ โดยใน Apidog ม็อกสามารถสร้างจากคำจำกัดความ API เดียวกับที่เอเจนต์ใช้งาน ทำให้ทั้งสองตัวทดสอบกับสัญญาเดียวกัน
บันทึกทุกการส่งต่อ
บันทึกอ็อบเจกต์ที่แต่ละขอบเขตได้รับและส่งออก พร้อม task_id เมื่อการรันหลายเอเจนต์ผิดพลาด ข้อมูลนี้จะบอกได้ทันทีว่าเอเจนต์ตัวใดมีข้อมูลและตัวใดทำหาย รายละเอียดเกี่ยวกับฟิลด์ที่ควรเก็บอยู่ในบทความ การติดตามการเรียกใช้เครื่องมือของเอเจนต์
เฟรมเวิร์กส่งผ่านอะไรให้คุณบ้าง
ก่อนพึ่งพา primitive สำหรับ handoff ของเฟรมเวิร์กใดๆ ให้ตรวจสอบก่อนว่ามันส่งอะไรข้ามขอบเขตจริงๆ
- OpenAI Agents SDK จำลอง handoff เป็นเครื่องมือที่เอเจนต์เรียกใช้ โมเดลจึงเป็นผู้ตัดสินใจว่าจะโอนการควบคุมเมื่อใด ควรใช้คู่กับการตรวจสอบเพย์โหลด
- แนวทางหลายเอเจนต์ของ LangGraph ใช้สถานะเป็นอ็อบเจกต์กราฟที่ทุกโหนดอ่านและเขียน ซึ่งสอดคล้องกับ handoff แบบมีโครงสร้าง แต่คุณยังต้องกำหนดฟิลด์ที่จำเป็นเอง
- บทความของ Anthropic เรื่อง การสร้างระบบวิจัยแบบหลายเอเจนต์ มีรายละเอียดด้านปฏิบัติการ โดยเฉพาะจำนวนคำสั่งที่เอเจนต์ย่อยต้องมีก่อนจึงจะทำงานได้อย่างมีประโยชน์
ทุกเฟรมเวิร์กส่งผ่านบางสิ่ง แต่ไม่มีเฟรมเวิร์กใดตัดสินใจแทนคุณว่าข้อเท็จจริงใดห้ามสูญหาย รายการนั้นต้องมาจากการออกแบบของคุณและควรมีการทดสอบรองรับ
เก็บอ็อบเจกต์งานไว้นอกบทสนทนา
รูปแบบที่ช่วยป้องกันข้อผิดพลาดได้หลายประเภทคือการจัดเก็บสถานะงานแบบคงทน โดยใช้ task_id เป็นคีย์ แล้วให้เอเจนต์ทุกตัวอ่านและเขียนอ็อบเจกต์เดียวกัน แทนการส่งสถานะผ่านข้อความ
บทสนทนาเป็นที่เก็บสถานะที่ไม่ดี เพราะอาจถูกบีบอัด ตัดทอน หรือเขียนใหม่ด้วยการสรุป และไม่มีขั้นตอนใดรู้ว่าฟิลด์ใดห้ามหาย ฐานข้อมูลไม่มีปัญหานี้
รูปแบบการทำงาน:
- เมื่อเริ่มรอบ เอเจนต์โหลดอ็อบเจกต์งาน
- หลังดำเนินการ เพิ่มข้อมูลลงใน
actions_takenแล้วบันทึก - เมื่อส่งต่องาน ส่งผ่านเพียง
task_id - เอเจนต์ผู้รับโหลดอ็อบเจกต์เดียวกัน
ไม่มีข้อมูลสำคัญเดินทางไปกับพรอมต์ จึงไม่มีข้อมูลสำคัญถูกสรุปทิ้งไป หากการทำงานหยุดลงในขั้นตอนที่สี่ การลองใหม่ก็เริ่มจากสถานะที่บันทึกไว้หลังขั้นตอนที่สาม ไม่ใช่เริ่มจากศูนย์
แพลตฟอร์มสำหรับเก็บสถานะ
หากเอเจนต์ทำงานเป็น CLI runtime บนเครื่องนักพัฒนา อ็อบเจกต์งานคงทนคือสิ่งที่คุณต้องสร้างขึ้นเอง แพลตฟอร์มการจัดการงานบางแห่งมีรูปแบบนี้อยู่แล้ว
Sharkly เป็นระบบจัดการงานสำหรับผู้คนและเอเจนต์ที่สร้างขึ้นจากแนวคิดนี้โดยตรง งานประกอบด้วยเป้าหมาย สถานะ ผู้รับผิดชอบ เอเจนต์หรือ Crew ความคิดเห็น รวมถึงสถานะและผลลัพธ์การทำงานของเอเจนต์ Crew จับคู่เอเจนต์ผู้นำกับเอเจนต์และผู้คนอื่นๆ ทำให้งานที่ต้องใช้ผู้เชี่ยวชาญหลายคนอยู่ในกลุ่มที่นำกลับมาใช้ซ้ำได้ แทนการส่งต่อกันผ่านพรอมต์
เมื่อสถานะอยู่บน Task การส่งต่องานระหว่างเอเจนต์จึงไม่ขึ้นอยู่กับคุณภาพการสรุปของเอเจนต์ตัวใดตัวหนึ่ง ดูแนวทางเพิ่มเติมได้จาก เอกสารประกอบของ Sharkly
Claude Code, Codex และ runtime อื่นๆ ยังคงเป็นตัวดำเนินการบนคอมพิวเตอร์ที่ลงทะเบียน ส่วนแพลตฟอร์มจะจัดการรายการงาน การมอบหมาย และวงจรตรวจสอบ หากคุณกำลังสร้าง durable task เอง เอกสารดังกล่าวเป็นแหล่งอ้างอิงที่ดีสำหรับฟิลด์สำคัญ
Checklist สำหรับการส่งต่องาน
- [ ] มีสคีมาสำหรับ handoff และตรวจสอบที่ขอบเขต
- [ ] ID ของเอนทิตีเป็นฟิลด์บังคับ
- [ ] การตัดสินใจมีพื้นฐานรองรับ
- [ ] การดำเนินการที่ทำไปแล้วถูกบันทึกและตรวจสอบก่อนการเขียน
- [ ] การอนุมัติและงบประมาณเดินทางไปพร้อมกับงาน ไม่ใช่ติดอยู่กับเอเจนต์
- [ ] คำถามที่ยังเปิดอยู่ชัดเจน และผู้รับสอบถามแทนการเดา
- [ ] ใช้ข้อมูลอ้างอิงเมื่อการดึงข้อมูลซ้ำมีต้นทุนต่ำ
- [ ] จำกัดจำนวนการส่งต่อ และคงอ็อบเจกต์งานต้นฉบับไว้
- [ ] บันทึกทุก handoff พร้อม
task_id - [ ] รันการทดสอบขอบเขตใน CI ด้วยม็อก รวมถึงกรณีเพย์โหลดไม่สมบูรณ์
ความล้มเหลวส่วนใหญ่ของระบบหลายเอเจนต์ไม่ใช่การให้เหตุผลที่ผิด แต่เป็นข้อเท็จจริงที่มีอยู่ในเอเจนต์ตัวหนึ่งและหายไปในตัวถัดไป ออกแบบขอบเขตให้เป็นอินเทอร์เฟซที่มีสคีมาและการทดสอบ แล้วเอเจนต์ตัวที่สองจะไม่ต้องถามคำถามที่เอเจนต์ตัวแรกตอบไปแล้ว
ดาวน์โหลด Apidog เพื่อจัดเก็บม็อกและการทดสอบขอบเขตไว้ข้าง API ที่เอเจนต์ทั้งสองใช้งาน
คำถามที่พบบ่อย
Handoff แบบมีโครงสร้างคุ้มค่าสำหรับเอเจนต์สองตัวหรือไม่?
สำหรับเอเจนต์สองตัวในงานสั้นๆ การส่งต่อบทสนทนาอาจเพียงพอ อ็อบเจกต์ที่มีโครงสร้างคุ้มค่าเมื่อมีเอเจนต์ตั้งแต่สามตัวขึ้นไป งานใช้เวลานาน หรือ handoff ข้ามกระบวนการและขอบเขตการทำงาน
โมเดลควรเขียนอ็อบเจกต์ handoff หรือโค้ดควรสร้าง?
ให้โค้ดสร้างข้อมูลที่ตรวจสอบได้ เช่น ตัวระบุ การดำเนินการ และการอนุมัติ โดยอ้างอิงจากเหตุการณ์จริง ไม่ใช่ความทรงจำของโมเดล ให้โมเดลรับผิดชอบเฉพาะ summary และ open_questions
จะหยุดบริบทเสื่อมในวงจรได้อย่างไร?
ส่งผ่านอ็อบเจกต์งานเดียวตลอดการทำงานและอัปเดตอ็อบเจกต์นั้น แทนการสร้างใหม่ในแต่ละขอบเขต พร้อมจำกัดจำนวน handoff หากงานต้องส่งต่อมากกว่าสองสามครั้ง อาจควรแบ่งโครงสร้างงานใหม่
แล้วเฟรมเวิร์กที่มี handoff ในตัวล่ะ?
ใช้ได้ แต่ต้องตรวจสอบว่ามันส่งผ่านอะไรจริง หลายเฟรมเวิร์กส่งเพียงประวัติข้อความ ซึ่งทำให้ตัวระบุคงอยู่ได้ก็ต่อเมื่อปรากฏในข้อความ เพิ่มเพย์โหลดที่มีโครงสร้างควบคู่กับสิ่งที่เฟรมเวิร์กส่งผ่าน
เอเจนต์ย่อยต้องมีข้อมูลรับรอง API แยกกันหรือไม่?
ต้องมี โดยกำหนดสิทธิ์ตามหน้าที่ การใช้คีย์ทรงพลังร่วมกันทำให้จำกัดความเสียหายและตรวจสอบว่าเอเจนต์ตัวใดเป็นผู้เรียก API ได้ยาก อ่านเพิ่มเติมได้จาก คีย์ API ที่มีสิทธิ์น้อยที่สุดสำหรับเอเจนต์
ฟิลด์ summary ควรยาวแค่ไหน?
ประมาณสองสามประโยค ครอบคลุมเจตนาและรายละเอียดที่ฟิลด์มีโครงสร้างรองรับไม่ได้ หากเริ่มใส่ ID หรือจำนวนเงิน ให้ย้ายข้อมูลเหล่านั้นไปยังฟิลด์ที่ตรวจสอบได้แทน
Top comments (0)