MIA Realtime vs Pipeline: Compare Two Voice Agent Modes in a Live Meeting
Compare MIA realtime vs pipeline agents in the same live meeting to evaluate response speed, transcription, listening controls, tools, and workflow flexibility.
Learn how to launch two MeetStream Infrastructure Agents into the same Zoom, Google Meet, or Microsoft Teams call and compare realtime mode against pipeline mode side by side.
Contents
- What you'll build
- What are realtime and pipeline MIA agents?
- Why compare both modes in the same meeting?
- How the comparison quickstart works
- How to build it: step by step
- Where this fits in real products
- Common issues
- Best practices for comparing agent modes
- FAQ
- Next step
Voice agents are not one-size-fits-all. Some meeting agents need the lowest possible response latency. Others need explicit transcription settings, wake-word controls, tools, functions, or a more inspectable orchestration path.
That is the difference between MIA realtime mode and MIA pipeline mode.
Realtime mode is optimized for direct, low-latency voice interaction. Pipeline mode routes the meeting audio through transcription, model orchestration, text-to-speech, listening controls, and optional tool-connected workflows. Both can power useful meeting agents, but they are built for different product tradeoffs.
This guide walks through a MeetStream Labs quickstart that launches both modes into the same meeting so you can compare them with the same prompt, same platform, and same callback server.
What you'll build
You will create a Node.js application that:
- Starts a local webhook listener for MeetStream lifecycle events.
- Creates a public callback URL with ngrok or uses a production callback URL.
- Sends a realtime MIA agent into a live meeting.
- Sends a pipeline MIA agent into the same live meeting.
- Tags events with
custom_attributes.modeso logs stay separated. - Tracks lifecycle events and basic join timing for each bot.
- Removes both bots cleanly when the process exits.
The actual agent behavior lives in MeetStream Dashboard. The code only needs two saved Agent IDs: one configured for realtime mode and one configured for pipeline mode.
What are realtime and pipeline MIA agents?
MIA is MeetStream's hosted agent runtime for live meetings. A MIA agent can join Zoom, Google Meet, or Microsoft Teams through a MeetStream bot and respond in the meeting based on the saved Agent configuration.
There are two useful runtime shapes to compare.
Realtime mode is the low-latency voice path. It is designed for direct voice interaction where the agent should feel conversational and respond quickly when addressed. If the product experience breaks when the agent waits too long, realtime mode is usually the first mode to test.

Pipeline mode is the orchestrated path. Audio moves through transcription, model logic, text-to-speech, wake-word or listening controls, and optional tool or function integrations. That adds latency, but it also gives the application more control over how the agent listens, reasons, and acts.

In short:
- Use realtime mode when speed and conversational feel are the priority.
- Use pipeline mode when control, tools, transcription settings, and workflow flexibility matter more.
Why compare both modes in the same meeting?
It is hard to compare voice agents from memory. A realtime test in one meeting and a pipeline test in another meeting can be distorted by platform latency, network conditions, meeting size, prompt phrasing, provider settings, and simple timing differences.
The comparison quickstart keeps the environment as close as possible:
One meeting link
-> realtime MIA bot
-> pipeline MIA bot
-> one callback server
-> mode-specific logs
You address both agents with the same request and observe the difference. The result is not a lab-perfect benchmark, but it is a practical developer test. You can hear the response behavior, watch the lifecycle events, and decide which mode fits the product you are building.
How the comparison quickstart works
The quickstart is in MeetStream Labs:
git clone https://github.com/meetstream-ai/labs
cd labs/mia-realtime-vs-pipeline
npm install
cp .env.example .env
It uses two saved MIA Agent IDs:
REALTIME_AGENT_CONFIG_ID=your_realtime_agent_id
PIPELINE_AGENT_CONFIG_ID=your_pipeline_agent_id
When you run the app, it creates two MeetStream bots with the same meeting_link. Each bot uses a different agent_config_id, and each one is tagged with mode-specific custom attributes:
const botSpecs = [
{
mode: "realtime",
label: "Realtime",
agentConfigId: config.realtimeAgentConfigId,
botName: config.realtimeBotName,
},
{
mode: "pipeline",
label: "Pipeline",
agentConfigId: config.pipelineAgentConfigId,
botName: config.pipelineBotName,
},
];
For each saved Agent, the app creates a bot:
const bot = await createBot({
apiKey: config.apiKey,
meetingLink: config.meetingLink,
agentConfigId: spec.agentConfigId,
botName: spec.botName,
callbackUrl,
customAttributes: {
example: "mia-realtime-vs-pipeline",
mode: spec.mode,
},
});
That mode attribute is what keeps the webhook logs readable. When MeetStream sends lifecycle events back to your callback URL, the local app can group those events under realtime or pipeline.
How to build it: step by step
Project setup
You need:
- Node.js 18+
- A MeetStream API key
- A Zoom, Google Meet, or Microsoft Teams meeting link
- Two saved MIA agents in MeetStream Dashboard
- A public callback URL or an ngrok auth token
- Provider integrations required by your saved Agents
Your .env file should look like this:
MEETSTREAM_API_KEY=your_meetstream_api_key
MEETING_LINK=https://meet.google.com/abc-defg-hij
REALTIME_AGENT_CONFIG_ID=your_realtime_agent_id
PIPELINE_AGENT_CONFIG_ID=your_pipeline_agent_id
CALLBACK_URL=
NGROK_AUTHTOKEN=your_ngrok_token
PORT=3001
REALTIME_BOT_NAME=MIA Realtime Agent
PIPELINE_BOT_NAME=MIA Pipeline Agent
Use either CALLBACK_URL or NGROK_AUTHTOKEN. During local development, ngrok is the easiest way to expose the webhook listener. In production, use your hosted application URL.
1. Validate both agent configs
The quickstart requires both saved Agent IDs:
const config = {
apiKey: readRequired("MEETSTREAM_API_KEY"),
meetingLink: readRequired("MEETING_LINK"),
realtimeAgentConfigId: readRequired("REALTIME_AGENT_CONFIG_ID"),
pipelineAgentConfigId: readRequired("PIPELINE_AGENT_CONFIG_ID"),
callbackUrl: readOptional("CALLBACK_URL"),
ngrokAuthtoken: readOptional("NGROK_AUTHTOKEN"),
port: Number.parseInt(process.env.PORT || "3001", 10),
};
It also checks that the two Agent IDs are different:
if (config.realtimeAgentConfigId === config.pipelineAgentConfigId) {
fail("REALTIME_AGENT_CONFIG_ID and PIPELINE_AGENT_CONFIG_ID must be different saved agents.");
}
That guard matters. The goal is to compare two runtime modes, not launch the same Agent twice with different names.
2. Start the webhook server
The local server exposes:
GET /health
POST /webhooks/meetstream
Each webhook event is acknowledged quickly, logged, and passed into the comparison tracker:
if (request.method === "POST" && request.url === "/webhooks/meetstream") {
const payload = await readJson(request);
sendJson(response, 200, { received: true });
logEvent(payload);
onEvent?.(payload);
return;
}
The log function reads the mode from custom attributes:
const mode =
payload.custom_attributes?.mode ||
payload.customAttributes?.mode ||
"unknown";
That gives you logs like:
[2026-08-12T22:20:00.000Z] [realtime] [bot_...] bot.inmeeting
[2026-08-12T22:20:04.000Z] [pipeline] [bot_...] bot.inmeeting
3. Create a public callback URL
MeetStream needs a public URL for lifecycle events. If CALLBACK_URL is set, the app uses it. Otherwise, it starts an ngrok tunnel:
tunnel = config.callbackUrl ? undefined : await startTunnel(config.port);
const publicUrl = config.callbackUrl ?? tunnel.url;
const callbackUrl = `${publicUrl.replace(/\/$/, "")}/webhooks/meetstream`;
Both bots use the same callback URL. The mode tag is what separates their events once the webhooks arrive.
4. Launch both MIA agents
For each mode, the app sends a standard MeetStream bot creation request:
const payload = {
meeting_link: meetingLink,
bot_name: botName,
video_required: false,
agent_config_id: agentConfigId,
callback_url: callbackUrl,
custom_attributes: customAttributes,
};
The important field is still agent_config_id. That saved Agent controls whether the bot runs as a realtime agent or a pipeline agent. The local script does not define the model, voice, wake phrase, or tool configuration. Those belong in MeetStream Dashboard.
Once both bots are created, the console prints:
Realtime vs pipeline run started.
callback : https://.../webhooks/meetstream
realtime : bot_...
pipeline : bot_...
Prompt both agents with the same request in the meeting.
Press Ctrl+C to remove both bots and print the comparison summary.
5. Compare the live behavior
In the meeting, use the wake phrase or direct-address behavior configured for each Agent. Keep the prompt as similar as possible.
For example:
Hey realtime agent, introduce yourself briefly.
Then:
Hey pipeline agent, introduce yourself briefly.
Watch for:
- How quickly each agent starts speaking.
- Whether the response feels conversational.
- Whether the pipeline agent's listening controls match the expected wake behavior.
- Whether tool-connected or workflow behavior is needed.
- Whether the extra latency is acceptable for the user experience.
The script records lifecycle event timestamps and event counts. Exact speech-to-speech latency still depends on the meeting platform, provider settings, model settings, network conditions, and how you prompt the bots during the live run.
6. Remove both bots safely
When you press Ctrl+C, the app removes both active bots:
for (const [mode, botId] of activeBots.entries()) {
await removeBot({ apiKey, botId });
console.log(`Removed ${mode} bot ${botId}.`);
}
Then it prints a summary for each mode:
Comparison summary
==================
Realtime
bot_id : ...
events : ...
first event : ...
in meeting : ...
stopped : ...
Pipeline
bot_id : ...
events : ...
first event : ...
in meeting : ...
stopped : ...
That cleanup step matters in live meeting development. If you stop the process without removing the bots, participants may remain in the meeting until the platform or bot timeout removes them.
Where this fits in real products
The comparison quickstart is useful because realtime and pipeline modes map to different product needs.
Conversational meeting assistants
If a user expects to speak naturally and get a fast spoken answer, start with realtime mode. The agent should feel like it is present in the conversation, not processing a request after the moment has passed.
Tool-connected meeting workflows
If the agent needs explicit transcription behavior, wake controls, tools, HTTP functions, or a more inspectable sequence of steps, pipeline mode is often the better fit. The response may take longer, but the workflow can be more structured.
Sales, support, and onboarding calls
Some products need both modes across different features. A realtime agent can handle quick conversational questions. A pipeline agent can run deeper actions, call tools, or follow a more controlled response path.
The right choice is not "which mode is better." The right choice is "which mode matches the user's moment."
Common issues
Both bots join, but I cannot tell which one is speaking
Use distinct bot names:
REALTIME_BOT_NAME=MIA Realtime Agent
PIPELINE_BOT_NAME=MIA Pipeline Agent
Also configure the saved Agents with clear names or different voices in Dashboard so the live comparison is easier to hear.
The pipeline agent does not respond
Check the saved pipeline Agent configuration in Dashboard. Pipeline mode depends on the selected transcription provider, model, TTS provider, wake-word or listening settings, and any required integrations.
The realtime agent responds faster, but the pipeline agent is more useful
That is a normal result. Realtime mode optimizes for voice latency. Pipeline mode gives you more orchestration flexibility. Use the comparison to decide which tradeoff is acceptable for your product.
Webhook logs show unknown mode
Confirm the bot creation request includes:
custom_attributes: {
example: "mia-realtime-vs-pipeline",
mode: spec.mode,
}
Without that attribute, the callback server cannot group events by mode.
One bot stays in the meeting after the script exits
Use Ctrl+C so the shutdown handler can remove both bots. If the terminal is killed abruptly, remove the bot from the dashboard or wait for the platform timeout.
Best practices for comparing agent modes
Keep the test meeting small. One or two humans is enough to hear the difference clearly.
Use the same meeting platform for both bots. Zoom, Google Meet, and Teams can each introduce different platform behavior.
Use short, repeatable prompts. Long prompts make it harder to tell whether latency comes from the runtime mode or from the model response itself.
Give the agents distinct names and voices if possible. A side-by-side comparison is much easier when you can immediately tell which bot answered.
Test with the product behavior you actually need. A simple introduction prompt is useful for setup, but the real decision should come from the workflow your users will run.
FAQ
What is the difference between MIA realtime mode and pipeline mode?
Realtime mode is optimized for low-latency voice interaction. Pipeline mode routes audio through transcription, model orchestration, TTS, listening controls, and optional tools or functions.
Do I need two saved Agents?
Yes. The comparison quickstart expects one saved realtime Agent and one saved pipeline Agent. Each bot receives a different agent_config_id.
Can both agents join the same Zoom, Google Meet, or Teams call?
Yes. The quickstart creates two MeetStream bots with the same meeting link and different saved Agent IDs.
Does the quickstart measure exact speech-to-speech latency?
Not exactly. It records lifecycle event timing and groups events by mode. Exact speech-to-response timing depends on meeting platform behavior, provider settings, model settings, network conditions, and how you prompt the agents.
Which mode should I use in production?
Use realtime mode when conversational speed is the core experience. Use pipeline mode when you need more control over transcription, listening behavior, tools, functions, or workflow orchestration.
Next step
Ready to compare both modes? Clone MeetStream Labs, open mia-realtime-vs-pipeline, add your MeetStream API key, meeting link, realtime Agent ID, pipeline Agent ID, and callback settings, then run:
npm start
Full source is in MeetStream Labs under mia-realtime-vs-pipeline.
Ready to try it? Explore MeetStream's meeting bot API and read the full MIA documentation to deploy a live voice meeting agent for Zoom, Google Meet, or Microsoft Teams.
