ACAgentic Craft

Foundation~30 minHazelJS

Agent vs Workflow vs Chatbot

Choose among HazelJS chatbots (@hazeljs/ai), deterministic flows (@hazeljs/flow), and AgentRuntime tool loops—with beginner examples and a decision checklist.

Authors
editorial-team
Published
Last reviewed
Progress is stored locally in this browser.

Direct answer

In HazelJS:

  • Chatbot — a controller calls @hazeljs/ai for completions; the human stays the actor for side effects.
  • Workflow — the path is mostly known; model it with @hazeljs/flow (durable waits, saga compensation, audit timeline). LLM nodes are optional; the graph is source of truth.
  • AgentAgentRuntime must choose a sequence of tool actions under uncertainty, with observation between steps.

Most real products are hybrids: a chat UI over a flow that sometimes calls runtime.execute for the messy middle.

Compare: /compare/prompt-vs-workflow-vs-graph-vs-agent-loop.

Section 1 — Same model, different driver

The LLM provider can be identical. What changes is who drives:

ShapeWho drivesHazelJS controlTypical stop
ChatbotHuman turnsController + @hazeljs/aiUser ends session
WorkflowDesigner / eng@hazeljs/flow graphTerminal node
AgentRuntime + modelAgentRuntime tool loopGoal met, budget, policy, or HITL

If you only change the prompt and keep a single completion handler, you still have a chatbot or LLM feature — not an agent.

Section 2 — Chatbots with @hazeljs/ai (beginner view)

Good at: drafting replies, explaining concepts, exploring options while a human clicks the real “Submit refund” button.

Mental model: one request → one (or a few) completions → text out.

typescript
import { OpenAIProvider } from '@hazeljs/ai';

const llm = new OpenAIProvider(process.env.OPENAI_API_KEY!);
const draft = await llm.chat({
  messages: [
    { role: 'system', content: 'Draft replies only. Never claim you refunded.' },
    { role: 'user', content: userMessage },
  ],
});

Failure modes beginners hit:

  • Users think the bot “did the refund” when it only described it
  • Ad-hoc tool calls without validation
  • Unbounded conversation cost

Prefer when: wrong actions are costly and a human is already in the loop. Adjacent pattern: /patterns/augmented-llm.

Section 3 — Workflows with @hazeljs/flow

Good at: reliable multi-step processes with known stages:

ingest → validate → enrich → decide → notify

Mental model: you draw the graph; the engine walks it; LLM calls are optional nodes.

Why beginners should love workflows:

  • Predictable stages for testing
  • Durable WAIT/resume when humans or external systems delay
  • Clear audit timeline for compliance

Failure modes:

  • One giant “LLM node” that secretly branches forever (a hidden agent)
  • Business rules only in free-form prompts — edges cannot be tested

Prefer when: compliance-heavy, high volume, or you need deterministic replay. Glossary: /glossary/workflow.

Note: inside @hazeljs/agent you may also use AgentGraph when a DAG of agents is known up front. That is still a designed graph, not an open tool loop. Prefer @hazeljs/flow when the primary artifact is a business process.

Section 4 — Agents with AgentRuntime

Good at: tasks where the next tool depends on prior output in ways you cannot fully enumerate — investigation, multi-system triage, research with tools.

Mental model: give a goal + tool box + budgets; the runtime loops until stop.

typescript
import { Agent, Tool, AgentRuntime } from '@hazeljs/agent';

@Agent({
  name: 'triage',
  description: 'Investigates incidents with read tools',
  systemPrompt: 'Use tools; cite tool results; stop when done.',
  maxSteps: 8,
})
class TriageAgent {
  @Tool({
    name: 'getIncident',
    description: 'Fetch incident by id',
    parameters: [{ name: 'id', type: 'string', required: true }],
  })
  async getIncident({ id }: { id: string }) {
    return { id, severity: 'high' };
  }
}

const runtime = new AgentRuntime({ llmProvider /* from @hazeljs/ai */ });
runtime.registerAgent(TriageAgent);
runtime.registerAgentInstance('triage', new TriageAgent());
await runtime.execute('triage', 'Summarize INC-42', {
  loop: { maxIterations: 4, stages: ['plan', 'execute', 'critique', 'validate'] },
});

Failure modes: infinite loops, duplicate side effects, injection via tool outputs, “worked in the demo” without golden tests.

Prefer when: branching under uncertainty and you will pay for governance (see Agent OS). Pattern: /patterns/tool-using-agent.

Section 5 — Worked examples (classify these)

Product ideaBetter first shapeWhy
“Rewrite this email nicer”Chatbot / @hazeljs/aiHuman pastes; no tools required
“On signup: create tenant → send email → provision”WorkflowStages known; must be reliable
“Given a vague incident, dig through logs + deploys + runbooks”AgentNext query depends on prior findings
“Chat UI that proposes a refund; manager clicks Approve”HybridChat for UX; approval gate for write
“Always: fetch order → if shipped, post tracking”Workflow (or tiny agent with 1–2 tools)If order is fixed A→B, prefer flow

Section 6 — Decision checklist

Score honestly before writing @Agent:

CriterionPrefer chatbotPrefer workflowPrefer agent
Path known?N/A (human drives)Mostly yesMostly no
Side effectsUser-clicked onlyExplicit graph nodesNeeds gates + idempotency
Latency / cost variancePer-turn budgetPredictable SLASoft / async OK
AuditabilitySession logsFlow audit timelineTraces + durable runs + policies
Human availabilityAlways presentOptional waitsHITL via durableSuspend
Ops maturityBasicRetries, ownershipBudgets, evals, kill switch

If you cannot yet do metrics and on-call, do not start with open-ended write-capable agents.

Section 7 — Hybrids that win on HazelJS

  1. Flow with an agentic node — deterministic shell; runtime.execute only for the ambiguous pocket.
  2. Agent that calls workflows as tools — autonomy selects a playbook; playbooks stay tested.
  3. Chatbot proposes, flow/agent executes — conversation for UX; writes stay gated.
  4. Safe desk + full desk — read-only agent first; expand tools after evals pass.

Section 8 — Practical migration path

  1. Ship the thinnest slice as @hazeljs/ai or a small flow.
  2. Measure where humans or brittle if/else explode.
  3. Introduce AgentRuntime only for that pocket.
  4. Wrap irreversible tools with approval (/patterns/human-approval-gate).
  5. Add durable runs before scaling concurrency (/patterns/durable-agent-run).

Anti-patterns

  • Renaming a chatbot “Agent X” without AgentRuntime
  • Encoding a 20-step business process only in a system prompt
  • Giving an agent production write credentials on day one
  • Skipping describeAgent because “it’s non-deterministic”

Checkpoint

  • You classified your sticky-note feature as chatbot / workflow / agent / hybrid
  • You can explain “same model, different driver” to a teammate
  • You know which HazelJS package matches each shape

What to do next

Learn when not to use an agent: /learn/agent-foundations/when-not-to-use-an-agent

Artifact: Filled decision checklist for one backlog feature

Sources

Related