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.”
บทนี้ต่อยอด 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 ดิบ)
เส้นแบ่งเดียวที่แยก orchestrator-workers ออกจาก parallelization
หัวข้อที่มีชื่อว่า “เส้นแบ่งเดียวที่แยก orchestrator-workers ออกจาก parallelization”ทั้ง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 เดียวก็จบ
กลไกใน C# — decompose → fan out → synthesize
หัวข้อที่มีชื่อว่า “กลไกใน C# — decompose → fan out → synthesize”โครงมีสามจังหวะ: 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 outputvar plan = await orchestrator.GetResponseAsync<Subtask[]>( $"แตกเป้าหมายนี้เป็นงานย่อย: {goal}", cancellationToken: ct);
// (2) fan out: 1 worker call ต่อหนึ่งงานย่อย — ยิงขนานด้วย Task.WhenAllChatResponse[] 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 กว่าไหม” แต่เป็น “มันทำให้ผลลัพธ์ดีขึ้นจริงไหม” เท่านั้น
แล้วทำไม agent Order ของเราถึง ไม่ใช่ เคสนี้
หัวข้อที่มีชื่อว่า “แล้วทำไม agent Order ของเราถึง ไม่ใช่ เคสนี้”เรียนกลไกมาแล้ว ตอนนี้ตอบคำถามจริง: ควรเอา 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)?