Skip to main content

Compose two agents by topics

Intermediate ⏱ 15 min multi-agent · composition · topics

You will build: two independent apps chained into a system — a detector that flags suspicious orders, and a responder that reasons over what the detector flags. No shared code, no framework: one topic.

detector: orders → rules → detector.screen.out ══► responder: (reads it) → llm → responder.reason.out

Prerequisites

1 · Build the detector — app number one

pulse new detector --source webhook --stage rule-based:screen
stages:
- name: screen
engine: rule-based
rules:
- { condition: "amount >= 1000", action: flag }
cd detector && pulse secret set PULSE_WEBHOOK_SECRET dev-secret # the scaffolded webhook source requires it
pulse deploy . && cd ..

Its output lives on detector.screen.out — a durable topic, like every stage boundary.

2 · Build the responder — app number two, subscribed to app number one

pulse new responder --stage llm:reason
stages:
- name: reason
engine: llm
inputTopics: [ detector.screen.out ] # ← the composition, in one line
systemPrompt: |
You receive a flagged order. Assess the risk briefly
and recommend: approve, review, or reject.
cd responder && pulse deploy . && cd ..

3 · Feed the detector, watch the responder

pulse events publish --topic detector.in --value '{"orderId":"o-9","amount":4200}'
pulse events tail --topic responder.reason.out

The event flows through two separately deployed apps: detector flags it → the flag lands on detector.screen.out → the responder picks it up and reasons → the recommendation appears on the responder's own output.

4 · Scale the pattern

  • Chain more agents the same way — each new app points inputTopics at any existing topic.
  • Fan out — deploy a second responder reading the same detector.screen.out; both see every flag.
  • Correlate — a stream-stream join matches events across two agents' outputs.

What just happened

"Multi-agent" here is not a framework feature — it's an addressing scheme. Every stage boundary is a durable, subscribable topic, so systems compose the way Unix pipes compose: independently deployed, independently observable (pulse events tail works at every seam), independently replaceable.

Troubleshooting

The responder never receives anything

Both apps must be deployed on the same Pulse. Confirm the detector actually emits: pulse events tail --topic detector.screen.out. If that's live but the responder is silent, check the inputTopics spelling.

Next