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
Direct answer
In HazelJS:
- Chatbot — a controller calls
@hazeljs/aifor 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. - Agent —
AgentRuntimemust 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:
| Shape | Who drives | HazelJS control | Typical stop |
|---|---|---|---|
| Chatbot | Human turns | Controller + @hazeljs/ai | User ends session |
| Workflow | Designer / eng | @hazeljs/flow graph | Terminal node |
| Agent | Runtime + model | AgentRuntime tool loop | Goal 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.
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/agentyou may also useAgentGraphwhen a DAG of agents is known up front. That is still a designed graph, not an open tool loop. Prefer@hazeljs/flowwhen 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.
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 idea | Better first shape | Why |
|---|---|---|
| “Rewrite this email nicer” | Chatbot / @hazeljs/ai | Human pastes; no tools required |
| “On signup: create tenant → send email → provision” | Workflow | Stages known; must be reliable |
| “Given a vague incident, dig through logs + deploys + runbooks” | Agent | Next query depends on prior findings |
| “Chat UI that proposes a refund; manager clicks Approve” | Hybrid | Chat 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:
| Criterion | Prefer chatbot | Prefer workflow | Prefer agent |
|---|---|---|---|
| Path known? | N/A (human drives) | Mostly yes | Mostly no |
| Side effects | User-clicked only | Explicit graph nodes | Needs gates + idempotency |
| Latency / cost variance | Per-turn budget | Predictable SLA | Soft / async OK |
| Auditability | Session logs | Flow audit timeline | Traces + durable runs + policies |
| Human availability | Always present | Optional waits | HITL via durableSuspend |
| Ops maturity | Basic | Retries, ownership | Budgets, 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
- Flow with an agentic node — deterministic shell;
runtime.executeonly for the ambiguous pocket. - Agent that calls workflows as tools — autonomy selects a playbook; playbooks stay tested.
- Chatbot proposes, flow/agent executes — conversation for UX; writes stay gated.
- Safe desk + full desk — read-only agent first; expand tools after evals pass.
Section 8 — Practical migration path
- Ship the thinnest slice as
@hazeljs/aior a small flow. - Measure where humans or brittle if/else explode.
- Introduce
AgentRuntimeonly for that pocket. - Wrap irreversible tools with approval (/patterns/human-approval-gate).
- 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
describeAgentbecause “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