"Teammates need agreements" — the request-response pattern drives negotiation.
The Problem
In the previous article we introduced team Agents, which let the Agent's teammates get work done, but unfortunately coordination is loose: the Lead sends messages, teammates reply, and there is no structured protocol. Two scenarios expose the problem:
Shutdown: the Lead wants Alice to shut down. If the thread is killed directly, the file Alice was halfway through writing is left on disk. Obviously we don't want that; instead, the Lead should send a request and Alice should confirm she has wrapped up before shutting down.
Plan approval: Bob wants to refactor the authentication module, which is a high-risk operation. Bob's plan should first be shown to the Lead, and work should start only after approval.
These two scenarios have exactly the same structure: one side sends a request, the other side gives a reply, and the request and reply are linked by the same ID.
The Solution
We won't dwell on the Agent loop above; let's focus on this newly added communication protocol. Simply put, it standardizes the structure of the content communicated between Agents. It's like in an OA system where there are no rules for how we talk to each other — we say whatever comes to mind. That does allow communication, but it isn't efficient, so we define a template and have everyone speak and reply according to this standard.
How It Works
Using an id to identify requests
AI is different from people. People can automatically recognize the logical correspondence between several questions (though sometimes the reply function is also needed). How does AI know, among all the messages sent, which question and which answer they correspond to? The answer is that for a single question, the same request_id is used throughout the entire process, as can be seen in the sample code below.
@dataclass
class ProtocolState:
request_id: str # 唯一 ID,如 "req_004281"
type: str # "shutdown" | "plan_approval"
sender: str # 发起方
target: str # 接收方
status: str # pending | approved | rejected
payload: str # 计划文本或关机原因
created_at: float # 时间戳
pending_requests: dict[str, ProtocolState] = {}Four-step protocol flow
Taking shutdown as an example, the complete chain:
① Lead 发请求
req_id = new_request_id() # "req_004281"
pending_requests[req_id] = ProtocolState(type="shutdown", status="pending", ...)
BUS.send("lead", "alice", "shutdown_request", metadata={"request_id": req_id})
② 队友收到 → dispatch
inbox = BUS.read_inbox("alice")
msg_type = msg["type"] # "shutdown_request"
→ 路由到 handle_shutdown_request()
③ 队友回复
BUS.send("alice", "lead", "shutdown_response",
metadata={"request_id": req_id, "approve": True})
④ Lead 收响应 → match
match_response("shutdown_response", req_id, approve=True)
pending_requests[req_id].status = "approved"request_id is the correlation key that runs through the whole chain: the request carries it out, and the reply carries it back.
90 likes
You can like once today