Curriculum/Multi-Agent Systems
Detect Ping-Pong
Same payload hash twice, or A→B→A on a DAG, is a stop. A manager persona with more tools is not a control. Put cannot: ping-pong on the job and handoff.
Duplicate-message detection: hash the payload. If A asks B to review the same brief again, stop with cannot: ping-pong. Cycle detection on the path: if the next hop is already in the path and the graph is supposed to be a DAG, stop with cannot: cycle.
Hop limits already capped the count. This lesson caps repetition. A job can burn four hops on four new steps. It must not burn four hops on one brief bouncing. Agents tool-thrash hashed (tool, args). Here you hash (to, brief) or the full frozen payload. Same family. Different object.
A manager persona with more tools is not a control. The manager will join the bounce, now with edit unlocked. The control is a stop reason on the job, then a human or a spec fix.
Two detectors
Payload hash. Canonical JSON (sorted keys) so extra spaces in a paragraph cannot farm a new hash — which is another reason interfaces are data, not chat. If you hash raw chat, models rephrase “please review” and escape the detector. Freeze the brief, then hash.
Path cycle. If nxt is already in path, you have A→B→A (or a longer cycle). On a DAG this is always illegal. On a graph that allowed one replan edge, you still want: same hash or cycle of length 2 with identical briefs. The toy treats any nxt in path as cycle even with a fresh seen set, so A,B, then A stops even if the brief string were new. That is the strict DAG reading. If you need one replan, do it as return-to-supervisor, which replaces the path’s worker tail, rather than as a peer cycle.
| Event | Detector | Stop string |
|---|---|---|
| Same envelope twice | hash in seen | cannot: ping-pong |
| Next role already visited | nxt in path | cannot: cycle |
| Legal new hop | else | append, continue |
Same brief twice, or A then B then A, stops. A manager hat is not a brake.
Ping-pong is a cycleStore seen on the job, not in a model prompt. The model cannot be trusted to remember it hashed something.
Walkthrough: billing twice, then A-B-A
Start path ["intake"]. First hop to billing with brief “refund 99”: ok, path grows, hash stored.
Second hop to billing with the same brief: cannot: ping-pong. Path unchanged. This is the identical-handoff case even if you did not yet form a cycle of names.
Separate demo: path already ["A", "B"], next A, new seen set (so hash would not fire): cannot: cycle. A→B→A is a cycle even with a new hash set.
Together they cover “same package again” and “walked back to a visited role.” You want both. Hash without cycle still allows A→B→C→A with new briefs until hop cap. Cycle without hash still allows A→B→A if you reset path (a bug). Defense in depth.
Run to execute this in your browser. Nothing is sent to a server.
What printed: first hop path intake, billing, err none. Repeat same billing brief: path unchanged, cannot: ping-pong. Cycle print: path still A, B (not appended), cannot: cycle. The second identical billing hop is ping-pong. A→B→A is a cycle even with a new hash set.
json.dumps with sort_keys=True is the canonical form. Do not use string interpolation to build the blob. Do not execute the blob. Hash it.
If the third hop used a new brief to billing while path already contained billing, the hash would differ and cycle would fire because billing is in path. That is strict and good for a DAG. Supervisor mode avoids this class: workers only return to sup, and sup may reassign without putting two workers on a peer path.
Rephrasing is the usual escape hatch. If the wire is still chat, B will send “could you take another look?” and the hash changes while the job does not. That is why this lesson sits after typed handoffs. Freeze the brief, drop extra keys, then hash. If a teammate wants “fuzzy duplicate detection” on paragraphs, they are asking to debug tone again. Refuse. Make the payload small and canonical.
Hop limits without hashing still allow a four-hop bounce of the same object if the cap is 8. Hashing without hop limits still allows A→B→C→D→E with fresh briefs until the card melts. You want both, plus illegal edges. The three brakes are not alternatives. On-call should see which one fired: max_hops, illegal_edge, cannot: ping-pong, cannot: cycle. Those strings are the product.
After the stop
Handoff to a human with the path and the last payload on the board. Do not finish with a guessed answer to “be helpful.” Guessing is a new failure mode (silent wrong). Do not raise max hops. Do not add Director. Do not let the last speaker “summarize the deadlock” into a user-facing answer: that summary is ungrounded and often picks the louder brief.
Put the stop reason on the job record. Evals should have a fixture that forces a bounce: two envelopes, same hash, expect the string cannot: ping-pong. A second fixture should force A→B→A with different briefs and expect cannot: cycle. A multi-agent harness that never tries to induce a ping-pong will not notice one. If the suite is all happy-path FAQs, you will learn about bounces from the bill.
How agents use this
This is Agents tool-thrash, lifted to roles. Put the stop reason on the job. The evals track should have a fixture that forces a bounce.
Log hashes (short prefixes, like the toy’s ten hex chars) not full briefs if briefs contain PII. Operators still need the ids.
If ping-pong rate spikes after a prompt change, revert the prompt. If it spikes after enabling peer handoff, turn handoff off and return to sequential or star. The pattern is telling you the graph is wrong, not that the models need more personality.
Combine with hop-limits: illegal edges, max hops, duplicate hashes. Three brakes. This lesson is brake three in detail.
Check your understanding