ข้าม​ไป​ยัง​เนื้อหา

Orchestrator-workers: โครงสร้าง​ที่​ต้องหา​เงิน​จ่าย​ค่า token ให้​ตัวเอง​ได้

บท​ที่​แล้ว (🔁 parallelization) เปิด multi-actor pattern ตัว​แรก​ของ​คอร์ส — fan out งาน​ย่อย​ที่​คุณ แบ่ง​ไว้​ล่วงหน้า​ได้ ออก​ไป​ทำ​พร้อม​กัน แล้ว join ผล กติกา​ข้อ​เดียว​ที่​ทำให้​มัน​มี​ที่​ยืน​คือ​คำ​ว่า “independent”: ถ้า​งาน​ย่อย​ไม่​พึ่งพา​กัน ยิง​ขนาน​ได้ ถ้า​พึ่งพา​กัน​เมื่อไร ยุบ​กลับ​เป็น loop เดียว

บท​นี้​เดิน​ต่อ​อีก​ก้าว — และ​เป็น​ก้าว​ที่ เสี่ยง​ที่สุด ที่​จะ​อ่าน​ผิด​เป็น “agent จริง​ต้อง​แตก​ตัว​เป็น​หลาย​ตัว” orchestrator-workersOrchestrator-Workersworkflow ตาม BEA: 'a central LLM dynamically breaks down tasks, delegates them to worker LLMs, and synthesizes their results' — ต่าง​จาก parallelization ตรง​ที่ subtask ไม่​ได้​กำหนด​ล่วงหน้า ('you can't predict the subtasks needed') ⚠️ เป็น​บท​ที่​เสี่ยง​อ่าน​เป็น pro-multi-agent ที่สุด: MARS วัด​ว่า multi-agent กิน token ~15 เท่า​ของ​แชท ต้อง​แปะ​ป้าย​ราคา​นี้​ทุก​ครั้ง​ที่ pattern ปรากฏ — structure earns its place ไม่ใช่​การ​อัปเกรด​ที่​เอื้อมหา ใช้​ต่อ​เมื่อ​คุณค่า​ของ​งาน​คุ้ม​ค่า token ที่​เพิ่ม​ขึ้น​เท่านั้นArchitecture ต่าง​จาก parallelization ตรง​จุด​เดียว​ที่​เปลี่ยน​ทุก​อย่าง: งาน​ย่อย​ไม่​ได้​ถูก​กำหนด​ไว้​ล่วงหน้า ใน parallelization คุณ เป็น​คน​เขียน code ว่า​จะ​แตก​เป็น​สาม​งาน (eligibility / SLA / loyalty) แต่​ใน orchestrator-workers model กลาง เป็น​คน​ตัดสิน​ตอน runtime ว่า​งาน​นี้​ควร​แตก​เป็น​งาน​ย่อย​อะไร​บ้าง Anthropic นิยามไว้ตรงๆ — “In the orchestrator-workers workflow, a central LLM dynamically breaks down tasks, delegates them to worker LLMs, and synthesizes their results.” และ​ระบุ​เคส​ที่​เหมาะ — “This workflow is well-suited for complex tasks where you can’t predict the subtasks needed.”

📦 code ตัวอย่าง

บท​นี้​ต่อยอด agent Order ตัว​เดิม​จาก​คอร์ส #15 (พร้อม context layer จาก #16) — repo kaen-food-ordering (กำลัง​จัด​ทำ) code orchestrator-workers ด้าน​ล่าง​เป็น กลไก​ที่​สอน​ให้​รู้จัก ไม่ใช่ code ที่​เรา​จะ​รับ​มา​ใช้​กับ agent Order ตอน​จบ​บท​คุณ​จะ​เห็น​ว่า​ทำไม — และ​นั่น​คือ​ประเด็น​ทั้งหมด​ของ​บท กติกา API ฝั่ง​แชต​ยัง​ยึด precedent ที่​บท1 ตั้ง​ไว้​ทุก​ข้อ (x-api-key ไม่ใช่ Bearer, ห้าม​ส่ง temperature, model id เปล่า claude-opus-4-8, อ่าน Contents[] ไม่ใช่ .Text ดิบ)

ทั้ง2 pattern ยิง​หลาย model call แล้ว​รวม​ผล — เผินๆ เหมือน​กัน แต่​เส้น​แบ่ง​อยู่​ที่ ใคร​เป็น​คน​ตัดสิน​ว่า​มี​งาน​ย่อย​อะไร​บ้าง

  • parallelization (บท4): คุณ ตัดสิน code ของ​คุณ​เขียน​ตายตัว​ว่า​มี​สาม​งาน แล้ว Task.WhenAll ยิง​ขนาน มัน​คือ workflow — predefined code path นับ​งาน​ย่อย​ได้​ตั้งแต่​ก่อน​รัน
  • orchestrator-workers (บท​นี้): model ตัดสิน​ตอน runtime — คุณ​ให้​เป้า​หมา​ยก​ว้างๆ เข้าไป model กลาง (orchestrator) คิด​เอง​ว่า​จะ​แตก​เป็น​กี่​งาน งาน​อะไร​บ้าง แล้ว​ค่อย delegate นับ​งาน​ย่อย​ล่วงหน้า​ไม่​ได้ เพราะ​มัน​ขึ้น​กับ input

เส้น​แบ่ง​นี้​คือ​เหตุผล​ที่ Anthropic เก็บ orchestrator-workers ไว้​ต่าง​ช่อง​กับ parallelization: “where you can’t predict the subtasks needed” — ถ้า เดา​งาน​ย่อย​ล่วงหน้า​ได้ คุณ​ยัง​ไม่​ต้องการ pattern นี้ ใช้ parallelization หรือ loop เดียว​ก็​จบ

โครง​มี​สาม​จังหวะ: orchestrator แตก​แผน → fan out ให้ worker → orchestrator สังเคราะห์​ผลกลับ​เป็น​คำ​ตอบ​เดียว ทั้ง orchestrator และ worker เป็น IChatClient ที่​เชื่อม​ผ่าน AsIChatClient("claude-opus-4-8") เหมือน agent เดิม (ใช้ model เดียวกัน​เพื่อ​รักษา cache ไว้ — กรอบ​คิด​คือ “orchestrator แตก​งาน​แล้ว fan out” เป็น​โครงสร้าง​ชั่วคราว​ต่อ request ไม่ใช่​ฝูง agent ที่​ยืน​รัน​ค้าง​ไว้):

// (1) orchestrator แตกงานย่อยเอง — GetResponseAsync<T> คือ overload แบบ structured output
var plan = await orchestrator.GetResponseAsync<Subtask[]>(
$"แตกเป้าหมายนี้เป็นงานย่อย: {goal}", cancellationToken: ct);
// (2) fan out: 1 worker call ต่อหนึ่งงานย่อย — ยิงขนานด้วย Task.WhenAll
ChatResponse[] results = await Task.WhenAll(
plan.Result.Select(t =>
worker.GetResponseAsync($"ทำงานย่อยนี้: {t.Description}", cancellationToken: ct)));
// (3) orchestrator สังเคราะห์ผลของ worker ทั้งหมดกลับเป็นคำตอบเดียว
var synthesis = await orchestrator.GetResponseAsync(
"สังเคราะห์ผลของ worker เหล่านี้เป็นคำตอบเดียว:\n"
+ string.Join("\n---\n", results.Select(r => r.Text)), // .Text = convenience concat (บท1)
cancellationToken: ct);

plan.Result คือ Subtask[] ที่ model สร้าง​ขึ้น​เอง ตอน runtime — นี่แหละ​ความ​ต่าง​จาก​บท4 ที่ docs ถูก​แบ่ง​ไว้​ก่อน​แล้ว ส่วน .Text บน worker แต่ละ​ตัว​คือ convenience concatenation ที่​บท1 เตือน​ไว้ ไม่ใช่​รูป​ดิบ — พอเพียง​สำหรับ​ป้อน​กลับ​เข้า synthesis ที่​เป็น free text

หยุด​ตรง​นี้​แล้ว​นับ​ต้นทุน: ตัวอย่าง​นี้​มี 1 (plan) + N (worker) + 1 (synthesis) model call ต่อ​หนึ่ง​คำถาม​ของ​ผู้​ใช้ ถ้า orchestrator แตก​เป็น 3 งาน คุณ​จ่าย​ค่า 5 model call ใน​ที่​ที่ loop เดียว​จ่าย 1 นี่​ไม่ใช่​รายละเอียด​ปลีกย่อย — มัน​คือ​หัวใจ​ของ​บท

ป้าย​ราคา​ที่​ต้อง​แปะ​ทุก​ครั้ง​ที่ pattern นี้​โผล่: ~15×

หัวข้อ​ที่​มีชื่อ​ว่า “ป้าย​ราคา​ที่​ต้อง​แปะ​ทุก​ครั้ง​ที่ pattern นี้​โผล่: ~15×”

ราคา​นี้ Anthropic วัด​มา​เอง — “In our data, agents typically use about 4× more tokens than chat interactions, and multi-agent systems use about 15× more tokens than chats.” และ​ชี้​ว่า​ตัว​ขับ​ต้นทุน​หลัก​คือ token ล้วนๆ — “Token usage by itself explains 80% of the variance, with the number of tool calls and the model choice as the two other explanatory factors.”

⚠️ ตัวเลข 15× / 4× และ 80% variance นี้​คือ​ข้อมูล เฉพาะ workload วิจัย​ของ Anthropic เอง ที่​ลง​วัน​ที่ ไม่ใช่​ค่า​คงที่​สากล — สัดส่วน​จริง​ของ​คุณ​ขึ้น​กับ​งาน อย่างไร​ก็ตาม ทิศทาง ชัดเจน​และ​ซ้ำ​ได้: multi-actor structure เผา token เป็น​ทวีคูณ​ของ loop เดียว​เสมอ เพราะ​ทุก worker call พก context ของ​ตัวเอง​เข้าไป​ใหม่ บวก plan บวก synthesis

token ที่​เผา​ไป​นี้​มีชื่อ​ใน​คอร์ส​นี้ — งบ​ต้นทุน (cost budget)Cost Budgetเพดาน token/เงิน​ที่​สะสม​ข้าม​เทิร์น (จาก UsageDetails) แล้ว​หยุด​เมื่อ runCost ถึง​เพดาน — มี​ไว้​เพราะ error สะสม​แบบ​ทวีคูณ (95%^N: 10 ขั้น ≈ 60%, 100 ขั้น ≈ 0.6% — Huyen, ตัวเลข​ประกอบ) และ multi-actor กิน token มาก (MARS 15×) จึง​ต้อง bound loop ให้​แน่น ⚠️ `ChatResponse.Usage` เป็น connector-populated/beta-mapped — งาน​ระดับ​บิล​ต้อง cross-check `RawRepresentation`Process ต่อ1 run ใน​คอร์ส #16 คุณ​เรียน​ว่า context window คือ งบ token ต่อ1 call ที่​ใช้​แล้ว​หมด cost budget คือ​ญาติ​ของ​มัน​อีก​ชั้น: เงิน​และ token ที่​ทั้ง run กิน​รวม​กัน​ข้าม​หลาย call orchestrator-workers ไม่​ได้​ทำให้ context window ของ call ไหน​โต​ขึ้น แต่​ทำให้ จำนวน call พุ่ง — และ​นั่น​คือ​มิติ​ที่ cost budget รักษา​ไว้ Chip Huyen พูด​เรื่อง​นี้ตรงๆ ว่า​มัน​คือ​ชื่อเสียง​ที่ agent ต้อง​แบก — “A common complaint is that agents are only good for burning through your API credits.” บท​ที่ 8 (🔁 capstone) จะ​ทำให้ cost budget วัด​ได้​จริง ด้วย cost meter ที่​สะสม usage ข้าม​ทุก​เทิร์น — เอา snippet orchestrator-workers ด้าน​บน​ไป​รัน​คู่​กับ meter นั้น แล้ว​คุณ​จะ เห็น 15× ใน​รัน​ของ​คุณ​เอง ไม่ใช่​แค่​เชื่อ​ตาม​ตัวเลข​ใน​บทความ

กฎ​เหล็ก​ของ​บท​นี้: ป้าย​ราคา​แปะ​ติด​กลไก​เสมอ

ทุก​ครั้ง​ที่ orchestrator-workers ปรากฏ — ใน code ใน​ไดอะแกรม ใน​หัว — ให้​ตัวเลข “multi-agent systems use about 15× more tokens than chats” ปรากฏ​ข้างๆ มัน​ด้วย pattern นี้ ไม่​ฟรี และ​เกณฑ์​ตัดสิน​ว่า​จะ​ใช้​มัน​หรือ​ไม่​ไม่​เคย​เป็น “มัน agentic กว่า​ไหม” แต่​เป็น “มัน​ทำให้​ผลลัพธ์​ดี​ขึ้น​จริง​ไหม” เท่านั้น

เรียน​กลไก​มา​แล้ว ตอน​นี้​ตอบ​คำถาม​จริง: ควร​เอา orchestrator-workers มา​ใช้​กับ agent Order ไหม? ไม่ — และ​เหตุผล​ไม่ใช่​รสนิยม แต่​เป็น​รูปทรง​ของ​งาน

core loop ของ agent Order คือ get order → judge eligibility → issue refund — มัน​คือ dependency chain ที่​ใช้ context ร่วม​กัน: ต้อง​รู้ record ออเดอร์​ก่อน​ถึง​จะ​ตัดสิน eligibility ได้ ต้อง​ตัดสิน eligibility ก่อน​ถึง​จะ refund ได้ งาน​ย่อย​พึ่งพา​กัน​เป็น​ทอดๆ และ share context เดียวกัน​ทั้ง​เส้น นี่​คือ​เคส​ที่ Anthropic บอกตรงๆ ว่า multi-agent ไม่​เหมาะ“Some domains that require all agents to share the same context or involve many dependencies between agents are not a good fit for multi-agent systems today. For instance, most coding tasks involve fewer truly parallelizable tasks than research.”

agent Order ของ​เรา​เป็น​รูปทรง​เดียว​กับ “most coding tasks” ใน​ประโยคนั้นเป๊ะ — dependency เยอะ, context ร่วม, งาน​ที่​แตก​ขนาน​ได้​จริง​มี​น้อย เรา​จึง​สอน orchestrator-workers เพื่อ รู้จัก​มัน แล้ว — อย่าง​ซื่อสัตย์ — ไม่​ใช้​มัน​ที่​นี่

นี่​ไม่ใช่​ข้อ​ยกเว้น​ของ​คอร์ส​นี้ แต่​คือ​กฎ​ที่​สอง​คอร์ส​ก่อนหน้า​วาง​ไว้​แล้ว พูด​ซ้ำ​อีก​หน:

  • คอร์ส #12 (🔁 agent as bounded context) ตั้ง default ว่า 1 agent 1 loop — single-agent คือ​ค่า​ตั้งต้น ไม่ใช่ multi-agent
  • คอร์ส #16 (🔁 context engineering) จัด​วาง sub-agent ไว้​เป็น ทาง​เลือก​สุดท้าย ไม่ใช่​รี​เฟล็​กซ์แรก
  • บท​นี้ ไม่​ได้​ผ่อน​กฎ​ทั้ง​สอง​ข้อ​นั้น​เลย มัน​แค่​เพิ่ม กลไก ที่​คุณ​ต้อง​มี​ใน​กล่อง​เครื่องมือ — เพื่อ​จะ​ได้​เลือก​ไม่​ใช้​มัน​อย่าง​มี​เหตุผล

แล้ว​เมื่อไร orchestrator-workers ถึง จ่าย​ค่าตัวเอง​ได้? เมื่อ​งาน​แตก​ขนาน​ได้​จริง (ไม่ใช่ chain) และ “the value of the task is high enough to pay for the increased performance.” — Anthropic เอง​ใช้ pattern นี้​ใน production กับ​งาน​วิจัย — “Our Research system uses a multi-agent architecture with an orchestrator-worker pattern, where a lead agent coordinates the process while delegating to specialized subagents that operate in parallel.” — งาน​วิจัย​แบบ​เปิด​กว้าง (สำรวจ​หลาย​แหล่ง​พร้อม​กัน งาน​ย่อย​เป็น​อิสระ​จริง เดา​ล่วงหน้า​ไม่​ได้) คุ้ม ที่​จะ​จ่าย 15× แต่​การ​คืน​เงิน​ออเดอร์​หนึ่ง​ใบ ไม่​คุ้ม

เกณฑ์​เดียว​ที่​ตัดสิน​เรื่อง​นี้​คือ​ประโยค​เดียว​ของ Anthropic — “You should consider adding complexity only when it demonstrably improves outcomes.” โครงสร้าง​ต้องหา​ที่​ยืน​ของ​ตัวเอง​ให้​ได้ ไม่มี​วัน​เป็น​อัปเกรด​ที่​เอื้อม​ไป​หยิบ​เพราะ​มัน​ดู “จริงจัง” กว่า

flowchart TD
  G["เป้าหมายจากผู้ใช้"] --> O["orchestrator<br/>แตกงานย่อยเอง ตอน runtime"]
  O -->|"model call"| W1["worker 1<br/>งานย่อย ก"]
  O -->|"model call"| W2["worker 2<br/>งานย่อย ข"]
  O -->|"model call"| W3["worker 3<br/>งานย่อย ค"]
  W1 --> S["orchestrator สังเคราะห์ผล<br/>อีก1 model call"]
  W2 --> S
  W3 --> S
  S --> A["คำตอบเดียว"]
  classDef cost fill:#b91c1c,stroke:#7f1d1d,color:#f8fafc;
  class O,W1,W2,W3,S cost;

คำ​บรรยาย​ภาพ: orchestrator แตก​งาน​ย่อย​เอง แล้ว fan out ให้ worker ยิง​ขนาน ก่อน​สังเคราะห์​ผลกลับ​เป็น​คำ​ตอบ​เดียว ห้า​ช่อง​สี​แดง​คือ5 model call ต่อ​หนึ่ง​คำถาม (1 plan + 3 worker + 1 synthesis) — เทียบ​กับ loop เดียว​ที่​จ่าย 1 นี่​คือ​ภาพ​ของ ~15× ที่ Anthropic วัด​ไว้ ก่อน​หยิบ​รูป​นี้​มา​ใช้ ต้อง​ตอบ​ให้​ได้​ก่อน​ว่า​งาน​ย่อย เป็น​อิสระ​จริง และ คุณค่า​ของ​งาน​สูง​พอ​จ่าย​ค่า token ที่​เพิ่ม

orchestrator-workers คือ multi-actor pattern ที่ model กลาง​แตก​งาน​ย่อย ที่​เดา​ล่วงหน้า​ไม่​ได้ แล้ว delegate ให้ worker ก่อน​สังเคราะห์​ผล — กลไก​ที่​ทรง​พลัง​ใน​งาน​ที่​แตก​ขนาน​ได้​จริง​และ​มี​คุณค่า​สูง​พอ​จ่าย​ค่า ~15× token ที่​มัน​เผา แต่ agent Order ของ​เรา​เป็น dependency chain ที่​ใช้ context ร่วม​กัน มัน​คือ​เคส​ที่ single-agent loop เดียว​ทำได้​ดี​กว่า​และ​ถูก​กว่า เรา​จึง​เรียน​กลไก​นี้​เพื่อ รู้จัก​มัน แล้ว​เลือก​ไม่​ใช้​อย่าง​มี​เหตุผล — สอดคล้อง​กับ default ของ #12 และ #16 ทุก​ประการ บท​หน้า​เรา​ถอย​กลับ​เข้า loop เดียว แต่​เติม​ความ​สามารถ​ใหม่​เข้าไป​ข้าง​ใน: ให้ model ตรวจ​งาน​ของ​ตัวเอง แล้ว​ขัดเกลา — evaluator-optimizer และ reflection พร้อม​คำถาม​ที่​ซื่อสัตย์​ว่า เมื่อไร​ไม่​ควร reflect


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้

บท​นี้​อิง​ต้นทาง​ที่​ลง​วัน​ที่​กำกับ อ่าน​ต่อ​ได้​โดยตรง:

  • Anthropic, “Building Effective Agents” (2024-12-19) — นิยาม orchestrator-workers (“a central LLM dynamically breaks down tasks…”), เคส​ที่​เหมาะ (“where you can’t predict the subtasks needed”) และ​กฎ complexity (“adding complexity only when it demonstrably improves outcomes”)
  • Anthropic, “How we built our multi-agent research system” (2025-06-13) — ป้าย​ราคา “multi-agent systems use about 15× more tokens than chats” + 80% variance, value gate (“the value of the task is high enough to pay for the increased performance”), disqualifier (“share the same context or involve many dependencies… not a good fit”), และ orchestrator-worker ใน production ของ Research system
  • Chip Huyen, “Agents” (2025-01-07) — ชื่อเสียง​เรื่อง​ต้นทุน​ของ agent (“only good for burning through your API credits”) ที่​ทำให้ cost budget เป็น​เรื่อง​ต้อง​รักษา
  • Microsoft Learn, “Use the IChatClient interface”GetResponseAsync<T> (structured-output overload), ChatResponse.Text (convenience concat), รูปทรง IChatClient ที่ orchestrator/worker ใช้​ร่วม​กัน

เช็กความเข้าใจ — บทที่ 5

ข้อ 1 / 3

อะไรคือเส้นแบ่งเดียวที่แยก orchestrator-workers ออกจาก parallelization (บท4)?