Compose two agents by 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.
Prerequisites
- Your first LLM stage (the responder uses an LLM).
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
inputTopicsat 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.