Passer au contenu principal
Les workflows AI Kit s’appuient sur des étapes typées (createStep, createMapStep, etc.) enchaînées via createWorkflow. Chaque run est observable, annulable et compatible avec la télémétrie OpenTelemetry.

1. Installer les dépendances

pnpm add @ai_kit/core zod
# ou
npm install @ai_kit/core zod
zod est facultatif mais recommandé pour typer vos entrées/sorties.

2. Déclarer une étape

import { createStep } from "@ai_kit/core";
import { z } from "zod";

type WeatherInput = { city: string };
type WeatherOutput = { forecast: string };

export const fetchWeather = createStep<WeatherInput, WeatherOutput>({
  id: "fetch-weather",
  description: "Récupère la météo courante",
  inputSchema: z.object({ city: z.string().min(1) }),
  handler: async ({ input, signal }) => {
    if (signal.aborted) {
      throw new Error("Requête annulée");
    }

    // TODO: remplacez par un appel API réel
    return { forecast: `Il fait beau à ${input.city}` };
  },
});
  • Le schéma est optionnel : toute valeur exposant parse ou safeParse convient.
  • Dupliquez une étape via cloneStep(step, overrides) si vous souhaitez garder le même handler.

3. Assembler un workflow

import { createWorkflow } from "@ai_kit/core";
import { z } from "zod";
import { fetchWeather } from "./steps/fetchWeather";

export const weatherWorkflow = createWorkflow({
  id: "weather-line",
  description: "Workflow météo simple",
  inputSchema: z.object({ city: z.string() }),
  outputSchema: z.object({ forecast: z.string() }),
})
  .then(fetchWeather)
  .commit();
commit() retourne une instance immuable de Workflow. Le schéma de sortie est appliqué sur la valeur renvoyée par la dernière étape (ou finalize si vous l’avez défini).

4. Exécuter et inspecter

const result = await weatherWorkflow.run({
  inputData: { city: "Paris" },
});

if (result.status === "success") {
  console.log(result.result.forecast);
} else {
  console.error("Échec", result.error);
}

Contrôler l’exécution

  • workflow.createRun() retourne un WorkflowRun réutilisable.
  • run.watch(listener) reçoit un événement à chaque étape (workflow:start, step:success, step:event, etc.).
  • run.stream() expose un itérateur asynchrone pour consommer les événements en direct tout en attendant la résolution.
  • run.cancel() annule proprement l’exécution via un AbortSignal.
const run = weatherWorkflow.createRun();

const unwatch = run.watch(event => {
  console.log(`[${event.type}]`, event);
});

const { stream, final } = await run.stream({ inputData: { city: "Lyon" } });

for await (const evt of stream) {
  // UI temps réel ou logs applicatifs
}

const outcome = await final;
unwatch();

Métadonnées partagées

Initialisez un objet partagé via metadata lors du run.start() ou run.stream(). Accédez-y dans une étape avec context.getMetadata() et mettez-le à jour avec context.updateMetadata(). context.store expose une Map partagée pour stocker des références temporaires.
const notifyTeam = createStep({
  id: "notify-team",
  handler: async ({ context }) => {
    context.emit({ type: "notification", data: { channel: "slack" } });
    return { status: "sent" };
  },
});

Contexte d’exécution (ctx)

Transportez un contexte typé entre les étapes :
type OrderCtx = {
  orgId: string;
  total: number;
  userId?: string;
};

const orderWorkflow = createWorkflow<
  { amount: number; userId: string },
  string,
  Record<string, never>,
  OrderCtx
>({
  id: "order-processing",
  ctx: { orgId: "default-org", total: 0 },
})
  .then(
    createStep({
      id: "apply-amount",
      handler: ({ input, ctx, stepRuntime }) => {
        stepRuntime.updateCtx(current => ({
          ...current,
          total: current.total + input.amount,
          userId: input.userId,
        }));
        return `Enregistré pour ${ctx.orgId}`;
      },
    }),
  )
  .then(
    createStep({
      id: "format-summary",
      handler: ({ ctx }) => `Organisation ${ctx.orgId} — total ${ctx.total}`,
    }),
  )
  .commit();

const run = await orderWorkflow.run({
  inputData: { amount: 120, userId: "user_42" },
  ctx: { orgId: "acme-co" },
});

console.log(run.ctx);
// { orgId: "acme-co", total: 120, userId: "user_42" }

5. Exemple complet (agent + workflow)

import { Agent, createStep, createWorkflow, scaleway } from "@ai_kit/core";
import { z } from "zod";

type WeatherInput = { city: string };
type WeatherSnapshot = {
  location: string;
  temperature: number;
  feelsLike: number;
  humidity: number;
  windSpeed: number;
  windGust: number;
  conditions: string;
};
type AdviceOutput = { text: string };
type WorkflowMeta = Record<string, unknown>;

const weatherCodeLabels: Record<number, string> = {
  0: "Ciel dégagé",
  1: "Principalement dégagé",
  2: "Nuages épars",
  3: "Couvert",
  45: "Brouillard",
  48: "Brouillard givrants",
  51: "Bruine légère",
  53: "Bruine modérée",
  55: "Bruine dense",
  56: "Bruine verglaçante légère",
  57: "Bruine verglaçante intense",
  61: "Pluie légère",
  63: "Pluie modérée",
  65: "Pluie forte",
  66: "Pluie verglaçante légère",
  67: "Pluie verglaçante forte",
  71: "Neige légère",
  73: "Neige modérée",
  75: "Neige forte",
  77: "Grain de neige",
  80: "Averses de pluie légères",
  81: "Averses de pluie modérées",
  82: "Averses de pluie violentes",
  85: "Averses de neige légères",
  86: "Averses de neige fortes",
  95: "Orage léger",
  96: "Orage avec grêle légère",
  99: "Orage avec grêle forte",
};

const fetchWeather = createStep<WeatherInput, { forecast: WeatherSnapshot }>({
  id: "fetch-weather",
  description: "Récupère la météo courante via un service externe",
  inputSchema: z.object({ city: z.string().min(1) }),
  outputSchema: z.object({
    location: z.string(),
    temperature: z.number(),
    feelsLike: z.number(),
    humidity: z.number(),
    windSpeed: z.number(),
    windGust: z.number(),
    conditions: z.string(),
  }),
  handler: async ({ input, signal }) => {
    const response = await fetch(
      `https://api.open-meteo.com/v1/forecast?current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code&timezone=Europe/Paris&hourly=temperature_2m&daily=weather_code&latitude=48.8566&longitude=2.3522`,
      { signal },
    );

    if (!response.ok) {
      throw new Error(`Erreur sur l'API météo (${response.status})`);
    }

    const body = await response.json();
    const current = body.current;

    const snapshot: WeatherSnapshot = {
      location: input.city,
      temperature: current.temperature_2m,
      feelsLike: current.apparent_temperature,
      humidity: current.relative_humidity_2m,
      windSpeed: current.wind_speed_10m,
      windGust: current.wind_gusts_10m,
      conditions:
        weatherCodeLabels[current.weather_code as keyof typeof weatherCodeLabels] ??
        "Conditions inconnues",
    };

    return { forecast: snapshot };
  },
});

const generateAdvice = createStep<
  { forecast: WeatherSnapshot },
  AdviceOutput,
  WorkflowMeta
>({
  id: "generate-advice",
  description: "Produit un message conseils grâce à un agent",
  handler: async ({ input, context }) => {
    context.emit({
      type: "forecast",
      data: input.forecast,
    });

    const agent = new Agent({
      name: "weather-advisor",
      instructions:
        "Tu es un assistant météo qui donne des conseils pratiques et un ton chaleureux.",
      model: scaleway("gpt-oss-120b"),
    });

    const advice = await agent.generate({
      prompt: `Ville: ${input.forecast.location}
Température ressentie: ${input.forecast.feelsLike}°C
Conditions: ${input.forecast.conditions}
Humidité: ${input.forecast.humidity}%
Vent: ${input.forecast.windSpeed} km/h (rafales ${input.forecast.windGust} km/h)

Propose un court message en français qui résume la météo et donne un conseil concret.`,
    });

    return { text: advice.text };
  },
});

export const weatherAdvisorWorkflow = createWorkflow<
  WeatherInput,
  AdviceOutput,
  WorkflowMeta
>({
  id: "weather-advisor",
  description: "Workflow de conseils météo piloté par un agent",
})
  .then(fetchWeather)
  .then(generateAdvice)
  .commit();
Ce workflow combine une étape “automatique” (appel API) et une étape IA (génération via Agent). Les événements context.emit peuvent alimenter une interface temps réel lors du run.