AI Agents with Trigger.dev
Trigger.dev is purpose-built for AI agent workflows — long-running, multi-step processes that use LLMs to make decisions and take actions.
Why Trigger.dev for AI Agents?
- No timeouts — agents can run for minutes, hours, or days
- Automatic retries — recover from LLM failures and API errors
- Wait & resume — pause agents while waiting for human approval or external events
- Real-time streaming — stream agent progress to your frontend
- Parallel execution — run multiple agent workers simultaneously
- Full observability — trace every step in the dashboard
Agent Patterns
Based on Anthropic's guide to building effective agents:
Prompt Chaining
Chain LLM calls where output from one becomes input to the next:
ts
export const generateAndTranslateTask = task({
id: "generate-and-translate",
run: async (payload: { topic: string; targetLanguage: string }) => {
// Step 1: Generate content
const content = await generateContent(payload.topic);
// Step 2: Translate content
const translated = await translateContent(content, payload.targetLanguage);
return { content, translated };
},
});Routing
Direct requests to different models or handlers based on analysis:
ts
export const routeQuestionTask = task({
id: "route-question",
run: async ({ question }: { question: string }) => {
const complexity = await analyzeComplexity(question);
if (complexity === "simple") {
return await gpt4oMini.generate(question);
} else {
return await claude.generate(question);
}
},
});Parallelization
Run multiple checks or processes simultaneously using batchTriggerAndWait:
ts
export const moderateContent = task({
id: "moderate-content",
run: async ({ message }: { message: string }) => {
// Simultaneously check for violence, hate speech, and spam
const [violenceCheck, hateCheck, spamCheck] = await Promise.all([
checkViolence.triggerAndWait({ message }),
checkHateSpeech.triggerAndWait({ message }),
checkSpam.triggerAndWait({ message }),
]);
return { safe: !violenceCheck.flagged && !hateCheck.flagged && !spamCheck.flagged };
},
});Orchestrator Pattern
A parent task coordinates multiple specialized worker tasks:
ts
export const orchestratorTask = task({
id: "fact-check-article",
run: async ({ article }: { article: string }) => {
const claims = await extractClaims(article);
const verificationResults = await claimVerifier.batchTriggerAndWait(
claims.map((claim) => ({ payload: { claim } }))
);
return { verified: verificationResults.filter((r) => r.output.verified) };
},
});Evaluator-Optimizer
Generate output, evaluate it, then refine through feedback:
ts
export const refineTranslationTask = task({
id: "refine-translation",
run: async ({ text, targetLanguage }: { text: string; targetLanguage: string }) => {
let translation = await translateText(text, targetLanguage);
let score = 0;
let attempts = 0;
while (score < 0.9 && attempts < 3) {
const evaluation = await evaluateTranslation({ original: text, translation, targetLanguage });
score = evaluation.score;
if (score < 0.9) {
translation = await refineTranslation({ translation, feedback: evaluation.feedback });
}
attempts++;
}
return { translation, score, attempts };
},
});Human-in-the-Loop
Pause an agent for human review using wait tokens:
ts
export const humanApprovalTask = task({
id: "human-approval",
run: async (payload) => {
const content = await generateContent(payload.prompt);
// Create a token and send for review
const token = await wait.createToken({ timeout: "24h" });
await sendForReview(content, token.publicAccessToken);
// Wait for human to approve/reject
const result = await wait.forToken<{ approved: boolean; feedback?: string }>(token);
if (result.ok && result.output.approved) {
return { content, approved: true };
} else {
// Revise with feedback
return await reviseContent(content, result.output?.feedback);
}
},
});