Skip to main content
// Props
import { Optimizer } from "smithers-orchestrator";

type OptimizerProps = {
  id?: string; // default "optimizer"; task ids {id}-generate, {id}-evaluate
  generator: AgentLike;
  evaluator: AgentLike | ((candidate: unknown) => unknown | Promise<unknown>); // function = compute task
  generateOutput: OutputTarget;
  evaluateOutput: OutputTarget; // must include `score: number`
  targetScore?: number; // omit to run all iterations
  maxIterations?: number; // default 10
  onMaxReached?: "return-last" | "fail"; // default "return-last"
  skipIf?: boolean;
  children: string | ReactNode; // initial generation prompt
};
export default smithers(() => (
  <Workflow name="prompt-optimizer">
    <Optimizer
      generator={promptEngineer}
      evaluator={evaluator}
      generateOutput={outputs.prompt}
      evaluateOutput={outputs.evaluation}
      targetScore={90}
      maxIterations={5}
    >
      Generate a prompt for summarizing legal documents.
    </Optimizer>
  </Workflow>
));

Notes

  • score drives convergence against targetScore.
  • A function evaluator renders as a compute task rather than an agent task.

Source

The <Optimizer> implementation and the files it imports, straight from the package source. This section is generated; edit the source, not this block.
// @smithers-type-exports-begin
/** @typedef {import("./OptimizerProps.ts").OptimizerProps} OptimizerProps */
// @smithers-type-exports-end

import React from "react";
import { Loop } from "./Ralph.js";
import { Sequence } from "./Sequence.js";
import { Task } from "./Task.js";
/**
 * Generate -> evaluate -> improve loop with score convergence.
 *
 * Composes Loop, Sequence, and Task to create an iterative
 * optimization pattern. Each iteration receives the previous
 * score and feedback to guide improvement.
 * @param {OptimizerProps} props
 */
export function Optimizer(props) {
    if (props.skipIf)
        return null;
    const { id, generator, evaluator, generateOutput, evaluateOutput, maxIterations = 10, onMaxReached = "return-last", children, } = props;
    const prefix = id ?? "optimizer";
    const generateId = `${prefix}-generate`;
    const evaluateId = `${prefix}-evaluate`;
    // `until` is false — the runtime re-renders and checks the evaluate
    // output's `score` field against `targetScore` each frame.
    // When no targetScore is set, the loop always runs all iterations.
    const isAgentEvaluator = typeof evaluator !== "function";
    return React.createElement(Loop, {
        id: prefix,
        until: false,
        maxIterations,
        onMaxReached,
    }, React.createElement(Sequence, null, React.createElement(Task, {
        id: generateId,
        output: generateOutput,
        agent: generator,
        children,
    }), isAgentEvaluator
        ? React.createElement(Task, {
            id: evaluateId,
            output: evaluateOutput,
            agent: evaluator,
            needs: { candidate: generateId },
            children: `Evaluate the generated candidate and provide a score.`,
        })
        : React.createElement(Task, {
            id: evaluateId,
            output: evaluateOutput,
            needs: { candidate: generateId },
            children: evaluator,
        })));
}