CrewAI Tutorial (TypeScript): debugging agent loops for advanced developers
By Cyprian AaronsUpdated 2026-04-21
crewaidebugging-agent-loops-for-advanced-developerstypescript
This tutorial shows you how to diagnose and stop agent loops in a CrewAI TypeScript project by instrumenting tasks, constraining retries, and adding hard stop conditions. You need this when an agent keeps re-running the same reasoning path, burning tokens, or producing repeated outputs that look “busy” but never converge.
What You'll Need
- •Node.js 18+ and npm
- •A TypeScript project with
crewaiinstalled - •An OpenAI API key set as
OPENAI_API_KEY - •A working
tsconfig.json - •Basic familiarity with CrewAI agents, tasks, and crews
- •A terminal where you can run
npx tsxornpm run build
Step-by-Step
- •Start by creating a minimal crew that is likely to expose looping behavior. The point is not to build a good workflow yet; it’s to reproduce the problem in a controlled way.
import { Agent, Task, Crew } from "crewai";
const analyst = new Agent({
role: "Risk Analyst",
goal: "Analyze the input and produce a concise risk summary",
backstory: "You work on operational risk reviews for financial services.",
verbose: true,
});
const task = new Task({
description:
"Review the following customer complaint and summarize the risk in one paragraph: repeated card declines for a premium customer.",
expectedOutput: "One concise risk summary.",
agent: analyst,
});
const crew = new Crew({
agents: [analyst],
tasks: [task],
});
async function main() {
const result = await crew.kickoff();
console.log(result);
}
main().catch(console.error);
- •Add explicit loop detection around the output stream. If your agent repeats phrases, restates the same conclusion, or exceeds a sane number of internal turns, fail fast and inspect the trace.
import { Agent, Task, Crew } from "crewai";
function detectRepetition(text: string): boolean {
const normalized = text.toLowerCase().replace(/\s+/g, " ").trim();
const phrases = [
"risk summary",
"customer complaint",
"repeated card declines",
"premium customer",
];
return phrases.filter((p) => normalized.includes(p)).length >= 3;
}
const analyst = new Agent({
role: "Risk Analyst",
goal: "Analyze the input and produce a concise risk summary",
backstory: "You work on operational risk reviews for financial services.",
verbose: true,
});
const task = new Task({
description:
"Review the following customer complaint and summarize the risk in one paragraph: repeated card declines for a premium customer.",
expectedOutput: "One concise risk summary.",
agent: analyst,
});
const crew = new Crew({ agents: [analyst], tasks: [task] });
async function main() {
const result = await crew.kickoff();
const text = typeof result === "string" ? result : JSON.stringify(result);
if (detectRepetition(text)) {
throw new Error("Loop detected: output is repeating key concepts.");
}
console.log(text);
}
main().catch(console.error);
- •Tighten the task contract so the model has less room to wander. Looping often happens when the prompt is underspecified or when success criteria are vague enough that the agent keeps “thinking” instead of finishing.
import { Agent, Task, Crew } from "crewai";
const analyst = new Agent({
role: "Risk Analyst",
goal: "Produce exactly one paragraph with no bullet points",
backstory: "You write concise operational risk summaries for banking teams.",
});
const task = new Task({
description:
[
"Summarize this complaint:",
"- Customer had repeated card declines",
"- Customer is on a premium account",
"",
"Rules:",
"- Output exactly one paragraph",
"- No lists",
"- No preamble",
"- No mention of your process",
].join("\n"),
expectedOutput:
"A single paragraph describing business risk and likely support impact.",
agent: analyst,
});
const crew = new Crew({
agents: [analyst],
tasks: [task],
});
async function main() {
const result = await crew.kickoff();
console.log(result);
}
main().catch(console.error);
- •If you still see loops, introduce an external guardrail at the application layer. In production systems I prefer this over trusting model self-control because it gives you deterministic failure behavior.
import { Agent, Task, Crew } from "crewai";
class LoopGuard {
private lastOutput = "";
private sameCount = 0;
check(output: string) {
const normalized = output.toLowerCase().replace(/\s+/g, " ").trim();
if (normalized === this.lastOutput) {
this.sameCount += 1;
} else {
this.sameCount = 0;
this.lastOutput = normalized;
}
if (this.sameCount >= 1) {
throw new Error("LoopGuard triggered: identical output repeated.");
}
}
}
const guard = new LoopGuard();
const analyst = new Agent({
role: "Risk Analyst",
goal: "Produce a concise final answer and stop",
});
const task = new Task({
description:
"Explain whether repeated card declines indicate support friction or fraud concern in two sentences max.",
expectedOutput: "Two sentences maximum.",
agent: analyst,
});
const crew = new Crew({ agents: [analyst], tasks: [task] });
async function main() {
const result1 = String(await crew.kickoff());
guard.check(result1);
}
main().catch(console.error);
- •Finally, add a debug harness that records each run so you can compare behavior after prompt changes. This makes loop regressions obvious when someone edits task wording or changes an agent’s goal.
import { Agent, Task, Crew } from "crewai";
import { writeFileSync } from "node:fs";
const analyst = new Agent({
role: "Risk Analyst",
};
const task = new Task({
});
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