CrewAI Tutorial (TypeScript): running agents in parallel for advanced developers
This tutorial shows how to run multiple CrewAI agents in parallel from TypeScript, then merge their outputs into one result. You need this when one agent is waiting on another for no reason, and the work can be split into independent tracks like research, risk review, and summarization.
What You'll Need
- •Node.js 18+ and npm
- •A TypeScript project with
ts-nodeor a build step viatsc - •
crewaiinstalled in your project - •An OpenAI API key set as
OPENAI_API_KEY - •Basic familiarity with CrewAI concepts:
- •
Agent - •
Task - •
Crew - •
Process.sequential
- •
- •A use case where tasks do not depend on each other
Step-by-Step
- •Start with a clean TypeScript setup and install the packages you need. I’m using
dotenvso the API key stays out of source control.
npm init -y
npm install crewai dotenv
npm install -D typescript ts-node @types/node
npx tsc --init
- •Create a small entry file and load environment variables before you touch CrewAI. This keeps the runtime predictable and avoids hardcoding secrets.
import "dotenv/config";
import { Agent, Task, Crew, Process } from "crewai";
if (!process.env.OPENAI_API_KEY) {
throw new Error("OPENAI_API_KEY is required");
}
- •Define agents that can work independently. The key point here is that parallelism only makes sense when the tasks do not need each other’s output.
const researcher = new Agent({
role: "Research Analyst",
goal: "Find concise facts about a company",
backstory: "You extract relevant business details without filler.",
});
const riskReviewer = new Agent({
role: "Risk Reviewer",
goal: "Identify operational and compliance risks",
backstory: "You think like a bank or insurance reviewer.",
});
const summarizer = new Agent({
role: "Executive Summarizer",
goal: "Merge findings into a short decision brief",
backstory: "You write for senior stakeholders.",
});
- •Create independent tasks for the first phase, then run them in parallel by using separate crews at the same time. In TypeScript, the simplest production-friendly pattern is to start multiple crews concurrently with
Promise.all.
const researchTask = new Task({
description: "Summarize what Stripe does in 5 bullet points.",
expected_output: "A concise factual summary.",
agent: researcher,
});
const riskTask = new Task({
description: "List operational and compliance risks for using Stripe.",
expected_output: "A short risk list with practical concerns.",
agent: riskReviewer,
});
async function runParallelWork() {
const researchCrew = new Crew({
agents: [researcher],
tasks: [researchTask],
process: Process.sequential,
verbose: false,
});
const riskCrew = new Crew({
agents: [riskReviewer],
tasks: [riskTask],
process: Process.sequential,
verbose: false,
});
const [researchResult, riskResult] = await Promise.all([
researchCrew.kickoff(),
riskCrew.kickoff(),
]);
return { researchResult, riskResult };
}
- •Feed both outputs into a final synthesis task. This is where you keep parallel work isolated until the end, then let one agent combine everything into something usable.
async function main() {
const { researchResult, riskResult } = await runParallelWork();
const synthesisTask = new Task({
description: `
Create an executive brief using these inputs:
Research:
${researchResult}
Risk review:
${riskResult}
Write:
- a decision summary
- top risks
- recommended next action
`,
expected_output: "A structured executive brief.",
agent: summarizer,
});
const synthesisCrew = new Crew({
agents: [summarizer],
tasks: [synthesisTask],
process: Process.sequential,
verbose: true,
});
const finalReport = await synthesisCrew.kickoff();
console.log(finalReport);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
- •If you want more scale, repeat the same pattern with more independent crews. Keep dependency chains out of your parallel phase; otherwise you just create race conditions with extra steps.
const pricingTask = new Task({
description: "Estimate pricing model implications for Stripe usage.",
expected_output: "A short pricing analysis.",
agent: researcher,
});
const securityTask = new Task({
description: "Identify security review concerns for Stripe integration.",
expected_output: "A short security checklist.",
agent: riskReviewer,
});
// Run these alongside the earlier crews if they are independent.
const extraResults = await Promise.all([
new Crew({ agents: [researcher], tasks: [pricingTask], process: Process.sequential }).kickoff(),
new Crew({ agents: [riskReviewer], tasks: [securityTask], process: Process.sequential }).kickoff(),
]);
Testing It
Run the script with npx ts-node index.ts or compile first with npx tsc and execute the output with Node.js. You should see both independent crews finish before the final synthesis starts.
If one task clearly depends on another, move it out of the parallel block and confirm it runs after its upstream result is available. Turn on verbose for at least one crew while testing so you can see execution order.
A good sanity check is to add timestamps around each kickoff call. If parallel execution is working, total runtime should be closer to the slowest single crew than the sum of all crews.
Next Steps
- •Add retry logic around each crew kickoff for transient model failures.
- •Replace hardcoded task text with dynamically generated prompts from your app layer.
- •Learn how to route outputs into structured JSON so downstream services can consume them without parsing free text
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit