เนื้อหา
บทนำ
Claude Code (v2.1.47) มาพร้อมฟีเจอร์ทดลองชื่อ Agent Teams: เซสชัน Claude Code หลายเซสชันประสานงานร่วมกันผ่านโครงสร้างแบบหัวหน้าทีมและสมาชิกทีม ผมกำลังพัฒนา Hive ซึ่งเป็นระบบจัดการเอเจนต์เขียนโค้ดหลายตัวที่มีเป้าหมายคล้ายกัน แต่ใช้สถาปัตยกรรมที่ต่างกันมาก จึงอยากเข้าใจว่าแนวทางของ Anthropic ทำงานอย่างไรภายใน
บทความนี้บันทึกสิ่งที่ผมค้นพบจาก:
- การอ่านเอกสารอย่างเป็นทางการ
- การตรวจสอบไฟล์จริงที่เซสชันทีมก่อนหน้าทิ้งไว้บนดิสก์
- การให้ Claude วิเคราะห์ไบนารีของ Claude Code (v2.1.47) เพื่อดูรายละเอียดการทำงานภายใน (ฮ่า!)
สารบัญ
- 1. ภาพรวมสถาปัตยกรรม
- 2. รายการงานที่ใช้ร่วมกัน
- 3. การสื่อสารระหว่างเอเจนต์
- 4. การสร้างเอเจนต์และวงจรชีวิต
- 5. ด่านตรวจสอบคุณภาพและฮุก
- 6. ต้นทุนการใช้โทเคน
- 7. สรุปสถาปัตยกรรม
- แหล่งข้อมูล
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,teamNameparentSessionId,colorplanModeRequired
ฟังก์ชันภายในที่สำคัญ:
isTeammate()/isTeamLead(): การตรวจจับบทบาทwaitForTeammatesToBecomeIdle(): กลไกพื้นฐานในการซิงโครไนซ์สำหรับหัวหน้าทีมgetTeammateContext()/setDynamicTeamContext(): การจัดการบริบทขณะรัน
การตรวจจับสถานะว่าง
หลังจบทุกรอบการทำงานของ LLM สมาชิกทีมจะเข้าสู่สถานะว่างโดยอัตโนมัติและส่ง idle_notification ให้หัวหน้าทีม นี่คือสถานะพักตามปกติ ไม่ใช่ข้อผิดพลาดหรือภาวะค้าง การส่งข้อความถึงสมาชิกทีมที่อยู่ในสถานะว่างจะปลุกให้กลับมาทำงาน (รอบการตรวจสอบถัดไปจะอ่านข้อความจากกล่องขาเข้า)
โพรโทคอลการปิดการทำงาน
- หัวหน้าทีมส่ง
shutdown_requestให้สมาชิกทีม - สมาชิกทีมสามารถอนุมัติ (ออกจากการทำงานอย่างเรียบร้อย) หรือปฏิเสธ (ทำงานต่อพร้อมให้คำอธิบาย)
- การล้างข้อมูลทีมผ่าน
TeamDeleteจะลบ~/.claude/teams/{team-name}/และ~/.claude/tasks/{team-name}/ - การล้างข้อมูลจะล้มเหลวหากยังมีสมาชิกทีมทำงานอยู่ ต้องปิดการทำงานของสมาชิกเหล่านั้นก่อน
การสืบทอดสิทธิ์
สมาชิกทีมสืบทอดโหมดสิทธิ์ของหัวหน้าทีม ณ เวลาที่สร้าง หากหัวหน้าทีมทำงานด้วย --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: สถาปัตยกรรม
- โหมดโต้ตอบ — รายการงาน
- ทีมเอเจนต์ — มอบหมายและรับงาน
- ทีมเอเจนต์ — บริบทและการสื่อสาร
- ทีมเอเจนต์ — ปิดการทำงานของสมาชิกทีม
- ทีมเอเจนต์ — ล้างข้อมูลทีม
- ทีมเอเจนต์ — สิทธิ์
- ทีมเอเจนต์ — ข้อจำกัด
- ทีมเอเจนต์ — แนวทางปฏิบัติที่แนะนำ
- ฮุก
- ค่าใช้จ่าย — ค่าโทเคนของทีมเอเจนต์
- การตั้งค่า
- เอเจนต์ย่อย
สิ่งที่พบบนดิสก์ (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