Skip to main content
This guide builds a two-step workflow that researches a topic and then writes a report once the research output exists. It uses the AI SDK with Anthropic and validates task outputs with Zod.

Step 1: Create the Workflow

Create workflow.tsx:
/** @jsxImportSource smithers-orchestrator */
import { createSmithers, Sequence, Task } from "smithers-orchestrator";
import { ToolLoopAgent as Agent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";

const { Workflow, smithers, outputs } = createSmithers({
  research: z.object({
    summary: z.string(),
    keyPoints: z.array(z.string()),
  }),
  output: z.object({
    title: z.string(),
    body: z.string(),
  }),
});

const researcher = new Agent({
  model: anthropic("claude-sonnet-4-20250514"),
  instructions: "You are an expert research assistant.",
});

const writer = new Agent({
  model: anthropic("claude-sonnet-4-20250514"),
  instructions: "You are a concise technical writer.",
});

export default smithers((ctx) => {
  const research = ctx.outputMaybe(outputs.research, { nodeId: "research" });

  return (
    <Workflow name="research-report">
      <Sequence>
        <Task id="research" output={outputs.research} agent={researcher}>
          {`Research the following topic and return a summary with key points.\n\nTopic: ${ctx.input.topic}`}
        </Task>

        {research ? (
          <Task id="report" output={outputs.output} agent={writer}>
            {`Write a concise report.\n\nSummary: ${research.summary}\nKey points: ${research.keyPoints.join(", ")}`}
          </Task>
        ) : null}
      </Sequence>
    </Workflow>
  );
});
Two JSX-specific details matter here:
  • ctx.outputMaybe(outputs.research, { nodeId: "research" }) is how the second render discovers that research has finished; see Workflow State for the persisted lookup model.
  • the report <Task> only mounts once the research output exists.

Step 2: Run It

Create main.ts:
import { runWorkflow } from "smithers-orchestrator";
import workflow from "./workflow";

const result = await runWorkflow(workflow, {
  input: { topic: "The history of the Zig programming language" },
});

console.log(result.status);
if (result.status === "finished") {
  console.log(JSON.stringify(result.output, null, 2));
}
Run it:
bun run main.ts
Or run the workflow directly with the CLI:
bunx smithers-orchestrator up workflow.tsx --input '{"topic":"The history of the Zig programming language"}'

What Happened

  1. Smithers rendered the JSX tree. Only research was mounted; see Render Frame for the render step in detail.
  2. The research task ran, validated its output against the Zod schema, and persisted it.
  3. Smithers rendered again with that stored output available through ctx.outputMaybe(...); see Reactivity for how new tasks mount on later renders.
  4. The report task mounted on the second render and used the research output in its prompt.

Next Steps

  • JSX Overview — See how JSX rendering, branching, and composition work.
  • Workflow — Learn the root workflow component.
  • Task — See agent, compute, and static task modes.
  • runWorkflow — Learn the programmatic runtime entry point used in main.ts.
  • Tutorial: Build a Workflow — Build a larger production-style JSX workflow.