Skip to content

Workflow Orchestration (RAG + HITL)

This guide shows how to orchestrate a full workflow: an agent that retrieves data (RAG), flows through a deterministic pipeline, and can pause for human approval (HITL).

By the time you reach this page you’ve already seen the interaction stack; this is where you plug those ideas into long‑running workflows.

If you are new to interactions, start here first:

> **Demo path (4/4)** — Previous guides covered single‑turn interaction, sessions, and end‑to‑end UI.

This page closes the loop with full workflow orchestration: interactions → sessions → UI adapters → workflows.


Agent runtime quickstart

If you want the canonical agent loop without wiring recipes by hand, start with the agent runtime. It exposes a small run/stream surface while still using the same recipe system under the hood, and any internal packs remain an implementation detail behind that recipe surface.

js
import { createBuiltinModel, createBuiltinTools } from "@geekist/llm-core/adapters";
import { createAgentRuntime } from "@geekist/llm-core/interaction";

const runtime = createAgentRuntime({
  model: createBuiltinModel(),
  adapters: { tools: createBuiltinTools() },
});

const outcome = await runtime.run({
  text: "Draft a launch plan for our API.",
});
if (outcome.status !== "ok") {
  throw new Error("Agent run did not complete.");
}

console.log(outcome.artefact);
ts
import { createBuiltinModel, createBuiltinTools } from "@geekist/llm-core/adapters";
import { createAgentRuntime } from "@geekist/llm-core/interaction";

const runtime = createAgentRuntime({
  model: createBuiltinModel(),
  adapters: { tools: createBuiltinTools() },
});

const outcome = await runtime.run({
  text: "Draft a launch plan for our API.",
});
if (outcome.status !== "ok") {
  throw new Error("Agent run did not complete.");
}

console.log(outcome.artefact);

How workflows compose recipes

An agent workflow is assembled from recipes. In this example you attach the RAG recipe and the HITL recipe to the base agent recipe, and the workflow runtime executes them together as a single DAG:

flowchart LR
  R[Recipe] --> R1[RAG recipe]
  R --> R2[HITL recipe]
  R1 --> W[Workflow runtime]
  R2 --> W

Step 1: Define the workflow

js
import { createBuiltinModel, createBuiltinRetriever } from "@geekist/llm-core/adapters";
import { recipes } from "@geekist/llm-core/recipes";

const documents = [
  { id: "doc-1", text: "llm-core uses recipes, adapters, and workflows." },
  { id: "doc-2", text: "Interactions project streams into UI-ready state." },
];
ts
import { createBuiltinModel, createBuiltinRetriever } from "@geekist/llm-core/adapters";
import { recipes } from "@geekist/llm-core/recipes";

const documents = [
  { id: "doc-1", text: "llm-core uses recipes, adapters, and workflows." },
  { id: "doc-2", text: "Interactions project streams into UI-ready state." },
];
js
const workflow = recipes
  .agent()
  .use(recipes.rag())
  .use(recipes.hitl())
  .defaults({
    adapters: {
      model: createBuiltinModel(),
      retriever: createBuiltinRetriever(documents),
    },
  })
  .build();
ts
const workflow = recipes
  .agent()
  .use(recipes.rag())
  .use(recipes.hitl())
  .defaults({
    adapters: {
      model: createBuiltinModel(),
      retriever: createBuiltinRetriever(documents),
    },
  })
  .build();

In this setup:

  • the RAG recipe expects a retriever adapter and handles the query → documents → answer flow for you, and
  • the HITL recipe adds a gate that can return a paused outcome when the workflow needs human approval.

Step 2: Run and handle pause/resume

js
const result = await workflow.run({
  // HITL pack governs pausing; the prompt just provides intent.
  input: "Summarize the docs and request approval before finalizing.",
});

if (result.status === "paused") {
  console.log("Awaiting approval token:", result.token);
}

if (result.status === "ok") {
  console.log(result.artefact);
}
ts
const result = await workflow.run({
  // HITL pack governs pausing; the prompt just provides intent.
  input: "Summarize the docs and request approval before finalizing.",
});

if (result.status === "paused") {
  console.log("Awaiting approval token:", result.token);
}

if (result.status === "ok") {
  console.log(result.artefact);
}

When the workflow returns status: "paused", you get a token that captures the point‑in‑time state. Store the token, present the pending decision to a human, and resume the workflow once a decision has been made.


Next steps