10. פרוטוקולי צוות
"חברי צוות צריכים כללי תקשורת משותפים"
חדש בנושא?
מה זה פרוטוקול בתקשורת סוכנים?
לחיצת יד מובנית — צד אחד שולח בקשה עם מזהה ייחודי, השני מגיב תוך הפניה למזהה הזה. פרוטוקולים מונעים אי הבנות: 'האם סיימת משימה X?' / 'כן, הנה תוצאה X.' במקום הודעות חופשיות.
מה זה פרוטוקול כיבוי (shutdown)?
לחיצת יד דו-שלבית שבה המוביל שולח shutdown_req (עם מזהה ייחודי), וחבר הצוות מאשר (מסיים עבודה נוכחית ויוצא) או דוחה (עדיין עסוק, נסה מאוחר יותר). זה מונע הריגת חבר צוות באמצע משימה ומותרת קבצים פגומים.
מה זה אישור תוכנית?
לפני שחבר צוות מתחיל משימה בסיכון גבוה, הוא שולח את תוכניתו למוביל לביקורת. המוביל יכול לאשר (להמשיך) או לדחות (לשנות את התוכנית). זה יוצר נקודת בדיקה של אדם בלולאה לפעולות מסוכנות.
הבעיה
בסשן צוותי הסוכנים, חברי הצוות עובדים ומתקשרים אבל חסר להם תיאום מובנה:
כיבוי: הריגת thread משאירה קבצים חצי-כתובים ו-config.json מיושן. צריך לחיצת יד: המוביל מבקש, חבר הצוות מאשר (מסיים ויוצא) או דוחה (ממשיך לעבוד).
אישור תוכנית: כשהמוביל אומר “ערוך את מודול האימות,” חבר הצוות מתחיל מיד. לשינויים בסיכון גבוה, המוביל צריך לבדוק את התוכנית קודם.
שניהם חולקים את אותו המבנה: צד אחד שולח בקשה עם מזהה ייחודי, השני מגיב תוך הפניה למזהה הזה.
הפתרון
Shutdown Protocol Plan Approval Protocol
================== ======================
Lead Teammate Teammate Lead
| | | |
|--shutdown_req-->| |--plan_req------>|
| {req_id:"abc"} | | {req_id:"xyz"} |
| | | |
|<--shutdown_resp-| |<--plan_resp-----|
| {req_id:"abc", | | {req_id:"xyz", |
| approved:true} | | approved:false,|
| | | feedback:"..."}|
איך זה עובד
- מזהי בקשות הם UUIDs קצרים. הבקשה והתגובה נושאות את אותו המזהה.
import uuid
def new_req_id() -> str:
return uuid.uuid4().hex[:8]
def send_shutdown_request(teammate_name: str) -> str:
req_id = new_req_id()
send_message(teammate_name, "lead", json.dumps({
"type": "shutdown_req",
"req_id": req_id,
}))
return req_id # caller stores this to match the response
def handle_shutdown_request(msg: dict, name: str) -> None:
req_id = msg["req_id"]
# Teammate decides: approve if idle, reject if mid-task
currently_working = get_status(name) == "WORKING"
send_message("lead", name, json.dumps({
"type": "shutdown_resp",
"req_id": req_id,
"approved": not currently_working,
"reason": "finishing current task" if currently_working else "ready to shutdown",
}))
if not currently_working:
update_status(name, "SHUTDOWN")
- אישור תוכנית משתמש באותה התבנית, מורחב עם תוכן תוכנית מובנה.
def request_plan_approval(plan: str, task_id: int) -> str:
req_id = new_req_id()
send_message("lead", "self", json.dumps({
"type": "plan_req",
"req_id": req_id,
"task_id": task_id,
"plan": plan,
}))
return req_id
def handle_plan_request(msg: dict) -> dict:
"""Lead reviews and approves/rejects the plan."""
# Lead's agent loop sees this in its inbox and decides
req_id = msg["req_id"]
teammate = msg.get("from")
# The LLM reviews the plan and calls approve_plan or reject_plan tool
return {"req_id": req_id, "teammate": teammate, "plan": msg["plan"]}
- הלולאה של חבר הצוות בודקת סוגי הודעות לפני פעולה.
def process_inbox(name: str) -> None:
messages = drain_inbox(name)
for msg_raw in messages:
try:
msg = json.loads(msg_raw["content"])
msg_type = msg.get("type", "plain")
except (json.JSONDecodeError, KeyError):
msg_type = "plain"
msg = msg_raw
if msg_type == "shutdown_req":
handle_shutdown_request(msg, name)
elif msg_type == "plan_resp":
handle_plan_response(msg, name)
else:
# Plain task assignment — start working
start_task(msg_raw["content"], name)
מה השתנה מ-צוותי סוכנים
| רכיב | לפני (צוותי סוכנים) | אחרי (פרוטוקולי צוות) |
|---|---|---|
| כיבוי | הרג thread | לחיצת יד req/resp מסודרת |
| סקירת תוכנית | אין | בקשת תוכנית + אישור מוביל |
| פורמט הודעות | טקסט חופשי | JSON מוקלד עם req_id |
| בטיחות | נמוכה | נקודות בדיקה מפורשות לסיכון |
מסקנה מרכזית
פרוטוקולים הופכים תקשורת חופשית למשא ומתן מובנה. תבנית req_id היא המפתח — היא מאפשרת לך להתאים תגובה לבקשה שגרמה לה, גם כשהודעות מגיעות שלא בסדר או עם עיכובים. אותה תבנית דו-שלבית (שלח בקשה, המתן לתגובה עם מזהה תואם) עובדת עבור כל צורך תיאום.
מדריך קוד אינטראקטיבי
1def new_req_id() -> str:2 return uuid.uuid4().hex[:8]3 4def send_shutdown_request(teammate_name: str) -> str:5 req_id = new_req_id()6 send_message(teammate_name, "lead", json.dumps({7 "type": "shutdown_req",8 "req_id": req_id,9 }))10 return req_id11 12def handle_shutdown_request(msg: dict, name: str) -> None:13 req_id = msg["req_id"]14 currently_working = get_status(name) == "WORKING"15 send_message("lead", name, json.dumps({16 "type": "shutdown_resp",17 "req_id": req_id,18 "approved": not currently_working,19 "reason": "finishing current task" if currently_working else "ready to shutdown",20 }))21 if not currently_working:22 update_status(name, "SHUTDOWN")23 24def process_inbox(name: str) -> None:25 messages = drain_inbox(name)26 for msg_raw in messages:27 try:28 msg = json.loads(msg_raw["content"])29 msg_type = msg.get("type", "plain")30 except (json.JSONDecodeError, KeyError):31 msg_type = "plain"32 if msg_type == "shutdown_req":33 handle_shutdown_request(msg, name)34 else:35 start_task(msg_raw["content"], name)36 new_req_id() מייצרת מחרוזת hex בת 8 תווים מ-UUID. קצרה מספיק לכלול בהודעות מבלי להפריח אותן, ייחודית מספיק כדי להימנע מהתנגשויות בין סוכנים מקבילים.ממשו זרימת אישור תוכנית: המוביל מציע תוכנית, חבר צוות בודק ומאשר או דוחה.
רמז
השתמשו בסוגי הודעות REQUEST/RESPONSE עם שדה "plan"