--- title: 'A workflow that delegates a turn to an agent' description: 'A lead triage workflow that gates the input in a deterministic step, then asks an agent to score the lead and parses a typed verdict out of the reply.' position: 1 --- # A workflow that delegates a turn to an agent > A triage workflow validates the incoming lead in a deterministic step, then > hands the judgment to a `lead-qualifier` agent and parses a typed verdict out > of its reply. `callDeepAgent` always resolves with plain text, because an > agent turn has no typed output of its own. Ask for JSON in the message and > validate the reply to get a typed verdict. The whole flow is a single `workflows/triage-lead.workflow.ts` file. The workflow stays auditable while the agent does the open-ended scoring. ```ts // workflows/triage-lead.workflow.ts import { z } from '@stackbone/sdk'; import { callDeepAgent } from '@stackbone/sdk/workflow'; export const inputSchema = z.object({ email: z.email(), company: z.string(), note: z.string(), }); export const outputSchema = z.object({ email: z.string(), score: z.number(), rationale: z.string(), }); const verdictSchema = z.object({ score: z.number().min(0).max(100), rationale: z.string(), }); export async function triageLeadWorkflow(input: z.infer) { 'use workflow'; const clean = await validate(input); // deterministic gate const verdict = await scoreWithAgent(clean.company, clean.note); // agent judgment return { email: clean.email, score: verdict.score, rationale: verdict.rationale }; } async function validate(input: z.infer) { 'use step'; if (!input.company.trim()) throw new Error('Missing company'); return input; } async function scoreWithAgent(company: string, note: string) { 'use step'; const { text } = await callDeepAgent( 'lead-qualifier', `Company: ${company}\nNote: ${note}\n` + 'Score this lead from 0 to 100 and explain why. ' + 'Reply with JSON only: {"score": number, "rationale": string}.', ); // A malformed reply throws here, which fails the step and applies the // run's retry policy: a retry re-runs the whole turn. return verdictSchema.parse(JSON.parse(text)); } ``` The `lead-qualifier` agent's own prompt, the one you publish for it in Studio, should tell it to always answer in that exact JSON shape, so the step can trust the parse. ## What's next - **[An agent that starts a workflow](/docs/examples/workflow-agents/agent-starts-workflow)**: the same two pieces, with the agent in charge. - **[Draft with an agent, then gate the send on a human](/docs/examples/workflow-agents/draft-then-approve)**: an agent draft behind a human approval. - **[What is a workflow agent](/docs/sdk/workflow-agents/overview)**: the model behind this flow. - **[Agent examples](/docs/examples/agents/plain-greeter)** and **[Workflow examples](/docs/examples/workflows/onboarding-pipeline)**: the building blocks on their own.