> ## Documentation Index
> Fetch the complete documentation index at: https://ai.aidalinfo.fr/llms.txt
> Use this file to discover all available pages before exploring further.

# Étapes humaines

> Suspendre un workflow et reprendre avec l’entrée d’un opérateur.

Les étapes humaines interrompent un workflow pour attendre une décision manuelle ou un complément d’information. Déclarez-les via `createHuman` et réutilisez-les dans plusieurs pipelines.

```ts theme={null}
import { createHuman, createStep, createWorkflow } from "@ai_kit/core";
import { z } from "zod";

const fetchDraftApplication = createStep<
  { applicantId: string },
  { applicantId: string; amount: number; history: string[] }
>({
  id: "fetch-draft-application",
  description: "Charge un dossier à valider",
  inputSchema: z.object({ applicantId: z.string().min(1) }),
  handler: async ({ input }) => ({
    applicantId: input.applicantId,
    amount: 4200,
    history: ["2019: ouverture", "2023: mise à jour"],
  }),
});

export const manualReview = createHuman<
  { applicantId: string; amount: number; history: string[] },
  { decision: string; commentaire: string }
>({
  id: "manual-review",
  output: ({ current }) => ({
    dossier: {
      id: current.applicantId,
      montant: current.amount,
      historique: current.history,
    },
  }),
  input: ({ ask }) =>
    ask.form({
      title: "Validation manuelle du dossier",
      fields: [
        ask.text({ id: "commentaire", label: "Commentaire" }),
        ask.select({
          id: "decision",
          label: "Décision",
          options: ["approve", "reject"],
        }),
      ],
    }),
});

const finalizeDecision = createStep<
  { decision: string; commentaire: string },
  { status: string }
>({
  id: "finalize-decision",
  handler: ({ input, context }) => ({
    status: `${context.initialInput.applicantId}:${input.decision}:${input.commentaire}`,
  }),
});

export const onboardingWorkflow = createWorkflow({ id: "onboarding" })
  .then(fetchDraftApplication)
  .human(manualReview)
  .then(finalizeDecision)
  .commit();
```

* La sortie de `fetchDraftApplication` est injectée dans `manualReview` (`output.current`) et renvoyée via `pendingHuman.output`.
* L’entrée humaine devient l’entrée de `finalizeDecision`, qui accède aussi aux données initiales via `context.initialInput`.

## Cycle d’exécution

```ts theme={null}
const run = onboardingWorkflow.createRun();
const { stream, final } = await run.stream({ inputData: { applicantId: "app-123" } });

for await (const event of stream) {
  if (event.type === "step:human:requested") {
    const { form, output } = event.data;
    ui.displayReviewForm(form, output.dossier);
  }
}

const pending = await final;

if (pending.status === "waiting_human") {
  await run.resumeWithHumanInput({
    stepId: "manual-review",
    data: { decision: "approve", commentaire: "RAS" },
  });
}
```

`run.resumeWithHumanInput` valide la réponse selon le schéma de l’étape humaine puis relance l’exécution jusqu’à complétion.

## Bonnes pratiques

* Utilisez des identifiants stables pour vos étapes humaines (ils servent lors de la reprise).
* Exposez des métadonnées utiles dans `output` pour simplifier l’affichage côté front (résumés, liens, stats).
* Journalisez les événements `step:human:*` afin de mesurer les temps de traitement.
