วิศวกรรมย้อนกลับ Claude Code Agent Teams: สถาปัตยกรรมและโพรโทคอล

คำแปลโดยเครื่อง บทความต้นฉบับ.

Banner for วิศวกรรมย้อนกลับ Claude Code Agent Teams: สถาปัตยกรรมและโพรโทคอล

เนื้อหา

บทนำ

Claude Code (v2.1.47) มาพร้อมฟีเจอร์ทดลองชื่อ Agent Teams: เซสชัน Claude Code หลายเซสชันประสานงานร่วมกันผ่านโครงสร้างแบบหัวหน้าทีมและสมาชิกทีม ผมกำลังพัฒนา Hive ซึ่งเป็นระบบจัดการเอเจนต์เขียนโค้ดหลายตัวที่มีเป้าหมายคล้ายกัน แต่ใช้สถาปัตยกรรมที่ต่างกันมาก จึงอยากเข้าใจว่าแนวทางของ Anthropic ทำงานอย่างไรภายใน

บทความนี้บันทึกสิ่งที่ผมค้นพบจาก:

  1. การอ่านเอกสารอย่างเป็นทางการ
  2. การตรวจสอบไฟล์จริงที่เซสชันทีมก่อนหน้าทิ้งไว้บนดิสก์
  3. การให้ Claude วิเคราะห์ไบนารีของ Claude Code (v2.1.47) เพื่อดูรายละเอียดการทำงานภายใน (ฮ่า!)

สารบัญ

1. ภาพรวมสถาปัตยกรรม

ทีมเอเจนต์ประกอบด้วยองค์ประกอบสี่ส่วน:

องค์ประกอบ บทบาท
หัวหน้าทีม เซสชัน Claude Code หลักที่สร้างทีมและสร้างสมาชิกทีม
สมาชิกทีม อินสแตนซ์ Claude Code ที่แยกจากกัน แต่ละตัวมีหน้าต่างบริบทของตัวเอง
รายการงาน งานที่ใช้ร่วมกัน ซึ่งจัดเก็บเป็นไฟล์ JSON แยกแต่ละงานบนดิสก์
กล่องจดหมาย ไฟล์กล่องขาเข้าของเอเจนต์แต่ละตัวสำหรับส่งข้อความ

ชั้นการประสานงานทั้งหมด อาศัยไฟล์ ระบบไฟล์ที่ ~/.claude/ เป็นสื่อกลางเพียงอย่างเดียวในการประสานงาน:

~/.claude/
├── teams/{team-name}/
│   ├── config.json                  # team membership registry
│   └── inboxes/{agent-name}.json    # per-agent mailbox
└── tasks/{team-name}/
    ├── .lock                        # flock() for concurrent task claiming
    ├── .highwatermark               # auto-increment counter
    ├── 1.json                       # individual task files
    ├── 2.json
    └── ...

โดยพื้นฐานแล้ว การออกแบบนี้เป็นแบบ กระจายศูนย์ หัวหน้าทีมก็เป็นเพียงเซสชัน Claude อีกเซสชันหนึ่งที่มีเครื่องมือเพิ่มเติม (TeamCreate, TeamDelete, SendMessage) ไม่มีโพรเซสเบื้องหลัง การประสานงานเกิดขึ้นจากการเข้าถึงไฟล์ร่วมกัน

ในเซสชันที่กำลังทำงานอยู่ หากคุณขอให้ Claude สร้างทีมขึ้นมาทำงานบางอย่าง แล้วรันคำสั่งต่อไปนี้ในอีกหน้าต่างหนึ่ง คุณจะเห็นการเปลี่ยนแปลงในระบบไฟล์แบบเรียลไทม์

watch -n 0.5 'tree ~/.claude/teams/ 2>/dev/null; echo "---"; tree ~/.claude/tasks/ 2>/dev/null'

ตัวอย่างเช่น เมื่อใช้พรอมป์ต์ต่อไปนี้:

can you spawn an agent team to examine this codebase?
  - have one look for bugs
  - have one look for complexity
  - have one look for good things to call out and play devil's advocate against the other two agents

ผมสังเกตเห็นสิ่งนี้:

teams
└── code-review
    ├── config.json
    └── inboxes
        ├── bug-hunter.json
        ├── complexity-analyst.json
        ├── devils-advocate.json
        └── team-lead.json

การตั้งค่าทีม

ไฟล์การตั้งค่าทีมที่ ~/.claude/teams/{team-name}/config.json มีอาร์เรย์ members ที่สมาชิกทีมอ่านเพื่อค้นหาสมาชิกคนอื่น:

{
  "members": [
    { "name": "team-lead", "agentId": "abc-123", "agentType": "leader" },
    {
      "name": "researcher",
      "agentId": "def-456",
      "agentType": "general-purpose"
    }
  ]
}

ชื่อเป็นวิธีหลักในการระบุปลายทาง (มี UUID แต่ไม่ได้ใช้กำหนดเส้นทาง) การส่งข้อความและการมอบหมายงานทั้งหมดใช้ฟิลด์ name

2. รายการงานที่ใช้ร่วมกัน

รูปแบบไฟล์

แต่ละงานจัดเก็บเป็นไฟล์ JSON แยกกันใน ~/.claude/tasks/{team-name}/ นี่คือตัวอย่างจริงจากเซสชันก่อนหน้า:

{
  "id": "1",
  "subject": "Hunt for bugs across the codebase",
  "description": "...",
  "activeForm": "Hunting for bugs",
  "owner": "bug-hunter",
  "status": "completed",
  "blocks": [],
  "blockedBy": []
}

สคีมาของงาน:

ฟิลด์ ชนิด คำอธิบาย
id string ID ตัวเลขที่เพิ่มค่าอัตโนมัติผ่าน .highwatermark
subject string ชื่องานในรูปคำสั่ง (เช่น "รันการทดสอบ")
description string ข้อกำหนดโดยละเอียดและเกณฑ์การยอมรับ
activeForm string รูปประโยคที่บอกว่ากำลังทำอยู่ สำหรับแสดงคู่กับสัญลักษณ์หมุน ("กำลังรันการทดสอบ")
status string pending → in_progress → completed (หรือ deleted)
blocks string[] ID ของงานที่ต้องรองานนี้เสร็จก่อนจึงจะเริ่มได้
blockedBy string[] ID ของงานที่ต้องเสร็จก่อนจึงจะเริ่มงานนี้ได้

การควบคุมการทำงานพร้อมกัน

ไฟล์พิเศษสองไฟล์ทำหน้าที่ประสานงาน:

  • .lock: ไฟล์ขนาด 0 ไบต์ที่ใช้ป้องกันการเข้าถึงพร้อมกันในระดับระบบไฟล์ (flock()) พบในไดเรกทอรีงานทั้ง 42 แห่งที่สังเกตบนเครื่องของผม
  • .highwatermark: มีจำนวนเต็มเพียงค่าเดียว (เช่น "3", "13") เป็น ID งานถัดไปที่พร้อมให้ใช้ในการเพิ่มค่าอัตโนมัติ

การรับงาน

การรับงานใช้การล็อกไฟล์เพื่อป้องกันภาวะแข่งขัน สมาชิกทีมให้ความสำคัญกับการรับงานที่มี ID ต่ำที่สุดก่อน งานที่มีอาร์เรย์ blockedBy ไม่ว่างจะยังรับไม่ได้ จนกว่างานทั้งหมดที่ขวางอยู่จะเข้าสู่สถานะสิ้นสุด

ข้อสังเกต: ไดเรกทอรีงานส่วนใหญ่ว่างเปล่า

จากไดเรกทอรีงาน 42 แห่งบนเครื่องของผม มีเพียง 5 แห่งที่มีไฟล์ JSON ของงานจริง ส่วนอีก 37 แห่งมีเพียง .lock และ .highwatermark นี่น่าจะหมายความว่างานถูกล้างออกหลังจากทำเสร็จ หรือเป็นเซสชันที่ Claude ใช้รายการงานภายใน (มีให้ใช้มาตั้งแต่เปิดตัวฟีเจอร์รายการงาน) โดยไม่ได้แยกงานออกเป็นไฟล์งานย่อย

3. การสื่อสารระหว่างเอเจนต์

รูปแบบกล่องจดหมาย

เอเจนต์แต่ละตัวมีไฟล์อาร์เรย์ JSON ที่ ~/.claude/teams/{team-name}/inboxes/{agent-name}.json นี่คือตัวอย่างกล่องขาเข้าจริงจากเซสชันก่อนหน้า ซึ่งหัวหน้าทีมส่งงานให้กับ controlplane-agent:

[
  {
    "from": "team-lead",
    "text": "{\"type\":\"task_assignment\",\"taskId\":\"1\",\"subject\":\"Phase 2: Control-plane - remove participants/presence\",\"description\":\"Remove multiplayer code from the control-plane package...\",\"assignedBy\":\"team-lead\",\"timestamp\":\"2026-02-18T02:37:16.890Z\"}",
    "timestamp": "2026-02-18T02:37:16.890Z",
    "read": false
  }
]

สังเกตการเข้ารหัสแบบ JSON ซ้อนใน JSON: ฟิลด์ text เป็นสตริง JSON ที่มีออบเจ็กต์ข้อความซึ่งแปลงให้อยู่ในรูปแบบสำหรับจัดเก็บหรือส่งต่อแล้ว ซองข้อความชั้นนอกมีฟิลด์ from, text, timestamp และ read

ประเภทข้อความ

ฟิลด์ type ภายในเพย์โหลด text รองรับ:

ประเภท ทิศทาง วัตถุประสงค์
task_assignment หัวหน้าทีม → สมาชิกทีม มอบหมายงานพร้อมรายละเอียดครบถ้วน
message ใครก็ได้ → ใครก็ได้ ส่งข้อความโดยตรงถึงผู้รับหนึ่งคน
broadcast หัวหน้าทีม → ทุกคน ส่งข้อความเดียวกันถึงสมาชิกทีมทุกคน
shutdown_request หัวหน้าทีม → สมาชิกทีม ขอให้ปิดการทำงานอย่างเรียบร้อย
shutdown_response สมาชิกทีม → หัวหน้าทีม อนุมัติหรือปฏิเสธการปิดการทำงาน
plan_approval_request สมาชิกทีม → หัวหน้าทีม ส่งแผนให้ตรวจสอบ
plan_approval_response หัวหน้าทีม → สมาชิกทีม อนุมัติหรือปฏิเสธพร้อมข้อเสนอแนะ
idle_notification สมาชิกทีม → หัวหน้าทีม ส่งอัตโนมัติเมื่อรอบการทำงานของสมาชิกทีมสิ้นสุด

กลไกการส่งข้อความ

เส้นทางการเขียน: ผู้ส่งเพิ่มรายการใหม่ต่อท้ายไฟล์อาร์เรย์ JSON ของกล่องขาเข้าผู้รับ

เส้นทางการอ่าน: ผู้รับตรวจสอบไฟล์กล่องขาเข้าของตัวเองเป็นระยะ ข้อความใหม่จะถูกแทรกเป็นรอบการสนทนาจำลอง (ดูเหมือนเป็นข้อความที่ผู้ใช้ส่งมา)

การส่งแบบกระจาย: เขียนข้อความเดียวกันลงในไฟล์กล่องขาเข้าของสมาชิกทีมทุกคนจริง ๆ ค่าใช้จ่ายด้านโทเคนเพิ่มขึ้นเป็นสัดส่วนโดยตรงกับขนาดทีม

การสื่อสารมีเพียงการเขียนต่อท้ายไฟล์ + การอ่านไฟล์ ความหน่วงระหว่างการส่งกับการรับขึ้นอยู่กับช่วงเวลาระหว่างการตรวจสอบของผู้รับ

การมองเห็น DM ระหว่างสมาชิกทีม

เมื่อสมาชิกทีมส่ง DM ถึงสมาชิกทีมอีกคน จะมีสรุปสั้น ๆ รวมอยู่ในการแจ้งเตือนสถานะว่างที่ส่งถึงหัวหน้าทีม วิธีนี้ทำให้หัวหน้าทีมมองเห็นการทำงานร่วมกันระหว่างสมาชิก โดยไม่ต้องเห็นเนื้อหาข้อความทั้งหมด

4. การสร้างเอเจนต์และวงจรชีวิต

วิธีสร้างสมาชิกทีม

สมาชิกทีมแต่ละคนเป็น โพรเซส CLI ของ claude ที่แยกต่างหาก หัวหน้าทีมสร้างสมาชิกทีมผ่านเครื่องมือ Task โดยใช้พารามิเตอร์ team_name และ name โพรเซสที่สร้างขึ้นจะได้รับการตั้งค่าตัวแปรสภาพแวดล้อมดังนี้:

  • CLAUDE_CODE_TEAM_NAME: ตั้งค่าให้อัตโนมัติสำหรับสมาชิกทีมที่สร้างขึ้น
  • CLAUDE_CODE_PLAN_MODE_REQUIRED: ตั้งค่าเป็น true หากต้องขออนุมัติแผน

การเริ่มต้นบริบท

สมาชิกทีมโหลดบริบทของโครงการเหมือนกับเซสชันใหม่ทั่วไป:

  • ไฟล์ CLAUDE.md จากไดเรกทอรีทำงาน
  • เซิร์ฟเวอร์ MCP
  • Skills
  • พรอมป์ต์จากหัวหน้าทีมที่ใช้สร้างสมาชิกทีม

ประวัติการสนทนาของหัวหน้าทีมจะไม่ถูกส่งต่อมาด้วย สมาชิกทีมแต่ละคนเริ่มต้นใหม่โดยมีเพียงพรอมป์ต์ที่ใช้สร้างเป็นบริบท

รายละเอียดการทำงานภายใน

จากการวิเคราะห์ไบนารีของ Claude Code v2.1.47 บริบทของสมาชิกทีมได้รับการจัดการผ่าน AsyncLocalStorage โดยมีฟิลด์ดังนี้:

  • agentId, agentName, teamName
  • parentSessionId, color
  • planModeRequired

ฟังก์ชันภายในที่สำคัญ:

  • isTeammate() / isTeamLead(): การตรวจจับบทบาท
  • waitForTeammatesToBecomeIdle(): กลไกพื้นฐานในการซิงโครไนซ์สำหรับหัวหน้าทีม
  • getTeammateContext() / setDynamicTeamContext(): การจัดการบริบทขณะรัน

การตรวจจับสถานะว่าง

หลังจบทุกรอบการทำงานของ LLM สมาชิกทีมจะเข้าสู่สถานะว่างโดยอัตโนมัติและส่ง idle_notification ให้หัวหน้าทีม นี่คือสถานะพักตามปกติ ไม่ใช่ข้อผิดพลาดหรือภาวะค้าง การส่งข้อความถึงสมาชิกทีมที่อยู่ในสถานะว่างจะปลุกให้กลับมาทำงาน (รอบการตรวจสอบถัดไปจะอ่านข้อความจากกล่องขาเข้า)

โพรโทคอลการปิดการทำงาน

  1. หัวหน้าทีมส่ง shutdown_request ให้สมาชิกทีม
  2. สมาชิกทีมสามารถอนุมัติ (ออกจากการทำงานอย่างเรียบร้อย) หรือปฏิเสธ (ทำงานต่อพร้อมให้คำอธิบาย)
  3. การล้างข้อมูลทีมผ่าน TeamDelete จะลบ ~/.claude/teams/{team-name}/ และ ~/.claude/tasks/{team-name}/
  4. การล้างข้อมูลจะล้มเหลวหากยังมีสมาชิกทีมทำงานอยู่ ต้องปิดการทำงานของสมาชิกเหล่านั้นก่อน

การสืบทอดสิทธิ์

สมาชิกทีมสืบทอดโหมดสิทธิ์ของหัวหน้าทีม ณ เวลาที่สร้าง หากหัวหน้าทีมทำงานด้วย --dangerously-skip-permissions สมาชิกทีมทุกคนก็จะทำงานแบบเดียวกัน เปลี่ยนโหมดของแต่ละคนได้หลังจากสร้างแล้ว แต่ไม่สามารถตั้งค่าแยกให้สมาชิกแต่ละคนขณะสร้างได้

5. ด่านตรวจสอบคุณภาพและฮุก

Agent Teams ทำงานร่วมกับระบบฮุกของ Claude Code เพื่อบังคับใช้เกณฑ์คุณภาพ:

ฮุก TeammateIdle

ทำงานเมื่อสมาชิกทีมกำลังจะเข้าสู่สถานะว่าง รหัสจบการทำงาน 2 จะส่ง stderr กลับไปเป็นข้อเสนอแนะ และป้องกันไม่ให้เข้าสู่สถานะว่าง ทำให้สมาชิกทีมทำงานต่อ

{
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "my-project"
}

ฮุก TaskCompleted

ทำงานเมื่อกำลังทำเครื่องหมายว่างานเสร็จสมบูรณ์ รหัสจบการทำงาน 2 จะป้องกันไม่ให้ระบุว่างานเสร็จสมบูรณ์ และส่ง stderr กลับไปเป็นข้อเสนอแนะ

{
  "hook_event_name": "TaskCompleted",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "my-project"
}

ฮุกนี้ทำงานในสองกรณี: (1) เมื่อเอเจนต์ใดก็ตามระบุอย่างชัดเจนว่างานเสร็จสมบูรณ์ผ่าน TaskUpdate หรือ (2) เมื่อสมาชิกในทีมเอเจนต์จบรอบการทำงานขณะที่ยังมีงานอยู่ระหว่างดำเนินการ

ประเภทตัวจัดการฮุก

ประเภท คำอธิบาย
command สคริปต์เชลล์ รับ JSON ผ่าน stdin และใช้รหัสจบการทำงานในการตัดสินใจ
prompt การประเมินด้วย LLM ในรอบเดียว คืนค่า {ok, reason}
agent เอเจนต์ย่อยที่ทำงานหลายรอบและมีเครื่องมืออ่าน สูงสุด 50 รอบ

6. ต้นทุนการใช้โทเคน

ทีมเอเจนต์ใช้ โทเคนมากกว่าประมาณ 7 เท่า เมื่อเทียบกับเซสชันมาตรฐาน หากสมาชิกทีมทำงานในโหมดวางแผน สมาชิกทีมแต่ละคนมีหน้าต่างบริบทเต็มของตัวเองในฐานะอินสแตนซ์ Claude ที่แยกจากกัน

ค่าอ้างอิงพื้นฐาน

  • การใช้งาน Claude Code โดยเฉลี่ย: ~$6/นักพัฒนา/วัน
  • ทีมเอเจนต์: เพิ่มจากค่าพื้นฐานโดยประมาณตามสัดส่วนของขนาดทีม

7. สรุปสถาปัตยกรรม

ด้าน Claude Code Agent Teams
สื่อกลางในการประสานงาน ไฟล์ธรรมดา (~/.claude/tasks/, ~/.claude/teams/)
รูปแบบงาน ไฟล์ JSON หนึ่งไฟล์ต่อหนึ่งงาน + .lock สำหรับรับงาน
การส่งข้อความ ไฟล์กล่องขาเข้า JSON (เขียนต่อท้าย + ตรวจสอบเป็นระยะ)
วงจรชีวิตเอเจนต์ โพรเซส CLI ที่จัดการตัวเอง
การแยกพื้นที่ทำงาน ไดเรกทอรีทำงานที่ใช้ร่วมกัน
กลยุทธ์การผสานงาน ไม่มี (เอเจนต์แก้ไขไฟล์โดยตรง)
การลองใหม่/การยกระดับปัญหา ด้วยตนเอง (หัวหน้าทีมตัดสินใจ หรือผู้ใช้เข้ามาแทรกแซง)
โครงสร้างการเชื่อมต่อ หัวหน้าทีม + สมาชิกระดับเดียวกัน พร้อมการส่งข้อความระหว่างกัน
การจัดสรรงาน รับงานเอง (สมาชิกทีมรับงานถัดไป)
ความคงทนของสถานะ เฉพาะไฟล์ ไม่มีการกลับมาทำงานต่อของสมาชิกทีมภายในโพรเซส
ด่านตรวจสอบคุณภาพ ฮุกเชลล์ (TeammateIdle, TaskCompleted)
การติดตามโทเคน เฉพาะแต่ละเซสชัน ไม่มีการรวมยอดข้ามเอเจนต์
การตรวจจับการหยุดค้าง ด้วยตนเอง (ผู้ใช้สังเกตว่าสมาชิกทีมหยุดทำงาน)
การควบคุมการทำงานพร้อมกัน โดยนัย (ขนาดทีม = จำนวนสมาชิกทีม)
แบบจำลองการพึ่งพา blocks/blockedBy ในไฟล์งาน

แหล่งข้อมูล

เอกสารอย่างเป็นทางการ

สิ่งที่พบบนดิสก์ (Claude Code v2.1.47)

สังเกตพบที่ /Users/tau/.claude/:

  • ไดเรกทอรีทีมที่มีไฟล์ config.json และ inboxes/{agent-name}.json
  • ไดเรกทอรีงานที่มี .lock, .highwatermark และไฟล์ JSON แยกแต่ละงาน
  • ตัวอย่างข้อความมอบหมายงานจาก team-lead ถึง cp-agent พร้อมประทับเวลา 2026-02-18T02:37:16.890Z

การวิเคราะห์ไบนารี

ไบนารีของ Claude Code v2.1.47 ฟังก์ชันภายในที่ระบุได้จากการวิเคราะห์สตริง: getTeamName, getAgentName, getAgentId, isTeammate, isTeamLead, waitForTeammatesToBecomeIdle, getTeammateContext, setDynamicTeamContext, createTeammateContext ฟิลด์บริบทของ AsyncLocalStorage: agentId, agentName, teamName, parentSessionId, color, planModeRequired

ฐานโค้ด Hive