---
title: When to Use a Workflow and When to Use an Agent
description: "How to decide between a workflow, an agent and a hybrid of the two, worked through on one bank's KYC checks and its morning news, with pseudocode and the cookbook recipes that run them."
canonical_url: https://superlinked.com/blog/agentic-vs-workflows-small-models
last_updated: 2026-11-11
---

Most of the work companies hand to models today is paperwork that repeats: reading invoices and claims, checking a new customer's documents, sorting support tickets, matching products to a catalog, triaging alerts. Agents are showing up in a different kind of work, where the next step depends on what you just found or on what someone asks next, like a support rep working through a customer's account, an engineer chasing an incident across logs and dashboards, a lawyer asking about a contract mid-negotiation, or a developer handing a coding agent a bug. Most teams have some of both, often inside the same job, and it isn't always obvious which one a given piece of work needs.

Models make more of this work possible to hand over, because most of it is reading (a scan, a bill, a handbook, a news story), and reading used to need a person. In this post I'll go through what a workflow and an agent each cost, then how I decide between a workflow, an agent and a hybrid of the two, and then walk through one bank's jobs (its daily KYC check on new accounts, two odd jobs that land on the KYC analyst's desk, and its morning news check on borrowers) with the KYC and counterparty news recipes from the [SIE cookbook](https://github.com/superlinked/cookbook), which each have a workflow and an agent. I'll finish with testing and how to run them yourself.

<BlogSieCta />

## Two Ways to Hand Work to Models

The usual line comes from Anthropic's [Building effective agents](https://www.anthropic.com/engineering/building-effective-agents). In a workflow, your code picks each step and calls a model where a step needs one; in an agent, a model picks its next step in a loop, calling tools as it goes.

If you're building with small open models, that choice decides how much of the work they get. A workflow can hand most of its steps to plain code or to a small model trained for one job (reading a scan, pointing at a field, scoring a story), so you know the cost per item before you start. An agent pays for whichever model runs its loop on every turn, because it rereads the whole conversation and every tool result each time (prompt caching makes those rereads cheaper, not free), and in most agents today that model is a large one.

Companies that have written about this mostly run a model as one step in a process they wrote down, like First American pulling fields out of property records, Sentry grouping crash reports that are the same bug, or Mirakl sending most of its marketplace catalog work to a fine-tuned 8B. Agents are the exception: Shopify's tool-calling agent that writes Flow automations (on a fine-tuned 32B, so not every agent runs on a frontier model) and Datadog's Scout, the model behind its AI SRE. The [hub post](/blog/small-open-models-across-industries) has the sources.

## How I Decide: Workflow, Agent or Hybrid

I start from the job's steps and ask how much they change from one case to the next.

- **The steps are known beforehand: a workflow.** If you can write the steps down before you see the case, and the job repeats, there's nothing for an agent to decide. Code runs the steps, small models read the scans and pages, and it runs on an event or a schedule with nobody watching.
- **The steps change completely based on what you're seeing: an agent.** The next step depends on the last answer, on what the last file showed, or on what the person at the screen asks next, so nobody can write them down in advance. A large model works out the steps, and code checks every part of the result it can.
- **The steps change only in some parts: a hybrid.** The workflow runs as usual and hands the parts it can't settle to an agent, code checks the agent's work, and the result goes to a person's queue.

![How to decide: if the job's steps are known beforehand, build a workflow of code and small models; if they change completely with what you see, build an agent and have code check its result; if they change only in some parts, build a hybrid where the workflow hands those parts to an agent; in all three a person decides](/blog/agentic-vs-workflows-small-models/pick.webp)

In all three, a person makes the decision at the end. And if you can't write the steps down and no code can check what the agent did, I'd leave that job with a person and let an agent help them look things up.

Cost is why I lean toward the workflow whenever the steps allow it. On the bank's KYC check, the workflow makes 4 SIE calls per case and no call to a large model. The two agent examples below make SIE calls too, and pay for Claude on top: the analyst's conversation about a customer who just moved took 4 to 6 SIE calls and a median of $0.12 of Claude, and the answer to a regulator's letter about last year's accounts 44 to 50 SIE calls and $0.17. That's nothing for a job that comes up a few times a week, and a lot for thousands of applications a night.

The cookbook's [Workflow or agent?](https://github.com/superlinked/cookbook#workflow-or-agent) section has a related test in a few lines (it names the person at the screen as its own case), and the agent recipes there show it on other jobs: a lawyer asking questions mid-negotiation in [legal](/blog/legal-work-on-small-open-models), a one-off look at one repair shop's claims in [insurance](/blog/insurance-claims-on-small-open-models), and the [agentic](https://github.com/superlinked/cookbook/tree/main/agentic) folder, where each recipe says which case it shows. If you've already decided on an agent and want to know which of its steps can go to small models, that's the [agent tools post](/blog/small-open-models-as-agent-tools).

## One Bank's KYC Work, Split Between a Workflow and an Agent

As an example, consider a [KYC analyst at a bank](/finance). She spends most of the day on one job, getting new accounts checked and opened. Every application arrives with a form, a scanned ID and a proof of address, and gets the same checks (is the form complete, are these the documents the policy accepts, does the ID belong to the person on the form, is the bill recent and at the right flat). By the end of the day the hundredth application gets a quicker look than the first. Then, a few times a week, something different lands. A customer has just moved and has no bill at the new address yet, and the analyst has to work out what the bank will take instead. Or the regulator writes asking for every account opened last year on a mobile phone bill, and what was done about each.

The [KYC recipe](https://github.com/superlinked/cookbook/tree/main/finance/check_kyc_documents_against_policy) has both: `recipe.py` is the daily check, and `agent.py` takes the two odd jobs, with the workflow as one of its tools. All of the data is synthetic.

### The Daily Check Is a Workflow

The checks come straight from the bank's policy, so I knew every one of them before I saw a single application. That's the whole case for a workflow.

![The KYC workflow: a new application or the nightly batch starts it, Python checks the form and the document types, LightOnOCR-3-4B reads each page, gliner2-large-v1 points at the fields, Python checks each policy rule, and the case comes out as checks_passed or review for the KYC analyst to decide](/blog/agentic-vs-workflows-small-models/workflow.webp)

The first two checks don't read any pages. The form arrives as structured data from the onboarding system, and so does the type the customer said each document is, so Python can see a blank occupation, or an ID card offered as its own proof of address, before any model runs. Then the scans, which have no text layer: [LightOnOCR-3-4B](/models/lightonai-lightonocr-3-4b) reads each page ([OCR](/ocr)), and [gliner2-large-v1](/models/fastino-gliner2-large-v1) [points at the name, date of birth, address and dates](/doc-field-extraction). Every decision after that is plain Python, and each one compares what the models read against something they didn't produce: the form, the check digits the issuer printed on the ID, the policy's list of accepted issuers, today's date. In pseudocode it's a straight line:

```python
def check_application(case):
    missing = check_form(case.form, policy)          # Python, no model
    docs = accepted_documents(case.uploads, policy)   # Python, no model
    for doc in docs:
        text = ocr(doc.pages)                         # small model on SIE
        doc.fields = extract_fields(text)             # small model on SIE
    results = [rule.check(case, docs) for rule in policy.rules]   # Python
    status = "checks_passed" if all(r.met for r in results) else "review"
    return status, results, asks_for_customer(results)
```

My favorite case has the form saying flat 2, 114 Harbour Road, and the bill saying flat 114, 2 Harbour Road. Same words, different flat, so a check that compares addresses as a bag of words passes it, and one that takes the address apart first doesn't. In production this runs when an application lands in the onboarding system, or as a nightly batch, and the results wait in the analyst's queue in the morning. Nobody waits up for a nightly batch, which is rather the point.

### The Customer Who Just Moved Is an Agent Job

The customer has just moved, the energy account at the new flat is still in the landlord's name, and the first bill is weeks away. The analyst asks the agent three things: what does our policy accept instead, she's uploaded a tenancy agreement so does it count, and then (once they've decided) draft the email listing what's still missing. The analyst is the one changing the steps here. She reads every answer before asking the next question, and what she asks next depends on what she just read, so nobody could have written the steps down before she sat down. That's the agent case from the test above, with the person at the screen as what changes the steps. A workflow can't hold up its end of that conversation.

The agent is Claude in a loop. Its tools look up a section of the bank's prose handbook, read the case, run the same two SIE models and the workflow's own checks on one scan, and run the whole workflow on the case. Every tool reads; none of them sends an email or changes a case. The loop looks like this:

```python
tools = [read_handbook, lookup_case, read_scan, run_kyc_check]   # lookups, small models on SIE, the workflow
messages = [analyst_question]
while True:
    reply = large_model(messages, tools)
    if reply.wants_tool:
        messages += [reply, run(reply.tool, reply.args)]   # every turn rereads all of this
        continue
    problems = check_answer(reply, messages)    # Python: quotes, ids, claims about the workflow
    if problems and not sent_back_yet:
        messages += [reply, problems]           # sent back once with what the check found
        continue
    show_to_analyst(reply.text)
    messages += [reply, wait_for_analyst()]     # the person asks the next question
```

![The KYC agent: the analyst's question goes to Claude, which picks among read_handbook (a handbook section by number), lookup_case, read_scan (OCR and field extraction on SIE) and run_kyc_check (the whole workflow); code checks the answer and sends it back once if a check fails, then the analyst reads it and asks the next question](/blog/agentic-vs-workflows-small-models/agent.webp)

The interesting part is the second question. The handbook accepts a signed tenancy agreement with the analyst's sign-off, but the workflow's policy file doesn't know about that exception, so `run_kyc_check` says the proof of address is missing. Both are right, and the agent mustn't settle it by itself. So code checks every answer before the analyst sees it: quotes have to appear word for word in their source, claims about the workflow have to match a real `run_kyc_check` result, and any answer that touches the proof of address has to show both the handbook's section and the workflow's result. The email has to ask for everything the KYC check asks for, except what the analyst signed off.

In 5 runs out of 5, every turn ended with an answer that passed those checks and showed both sides (one answer went back once, for quoting the single word "SPECIMEN"), and the analyst decided. The agent also pointed out, unprompted, that the agreement is stamped SPECIMEN, which is exactly what an analyst should notice (in fairness to the agent, every document in the recipe is). What code can't check, such as whether the agreement is really signed, stays with the analyst.

### The Regulator's Letter Is an Agent Job Too

The letter comes once or twice a year: every personal account opened in 2025 where the proof of address was a mobile phone bill, and what was done about each. Today it goes to whoever is free, and nobody has written the steps down because each account goes a different way. One opened on an exception a team lead approved in a note, one sent a water bill five weeks later, one was closed in August, and one was uploaded as a utility bill but comes from Larkspur Mobile, with nothing recorded about it. Code can list the accounts on its own (more on that below), but it can't work out what happened to each one. The work is following each one: the note decides whether you go looking for a later upload, the upload decides whether you run the KYC check on it, and a closure means you stop. What you do next on each account depends on what the last file showed, so it's an agent job, with tools that look up the bank's register of accounts, the uploads and the case notes, and that reach only the accounts opened in 2025.

The result can still be checked, and the workflow does a lot of the checking: when the agent says an account was fixed by a later document, the KYC check has to pass on that document, and every row's account, case and quotes have to exist and belong together.

The check I care most about is whether the agent found every account, because a missing account is the worst kind of wrong (nothing in the agent's own answer shows it's missing). Code answers that from sources the agent didn't write: it queries the register for every personal account opened in 2025 and reads every address scan of those cases with the workflow's own OCR and issuer check, whatever the customer called the document. An account it finds that the agent didn't goes back to the agent once to work out what was done. In 5 runs out of 5 the agent missed the Larkspur Mobile account, and in 5 out of 5 the check sent it back. The funny part is why it missed it. It didn't take the shortcut of filtering on the document type the customer declared; it started reading every scan in order, and ran out of its 10 tool calls four or five scans in. The final table had the right four accounts with the right outcomes in every run. In one run, though, the quote check refused the table anyway, because the agent quoted the register's status for a closed account as the single word "closed", which is too short to prove anything, so a person had to look at that one. Either way, the bank's money laundering reporting officer reads and signs the table before it goes out.

## A Hybrid: The Morning News Check Hands Off What It Can't Settle

At the same bank, a credit analyst starts every morning on the few news stories the bank's overnight news screen couldn't match to a borrower on its own. That screen is the [counterparty news recipe](https://github.com/superlinked/cookbook/tree/main/finance/alert_on_counterparty_news), the bank's other daily check. For every story in the morning's news, a workflow works out which borrower it's about and whether it reports a warning event, such as a missed payment or a lawsuit, using the counterparty list, an entity model and [GLiNER2.5-Decide](/models/fastino-gliner2-5-decide) on SIE. Most stories go one way or the other, an alert to the credit officer or filed. The workflow refuses to guess on a few: a name two borrowers share, or a ticker that belongs to a different company. Those go to review.

That's the hybrid case, because the steps change only for those few stories. `agent.py` runs the workflow unchanged and gives each review row to Claude overnight. For "Northwind has lost its largest customer, a national parcel carrier", the agent reads both Northwind credit files, sees that Northwind Logistics lists a parcel carrier as its largest customer, and suggests that borrower. For the story with the wrong ticker, the recipe's settings allow only one outcome, leaving the row for a person with a note, and the agent did that every time.

![The counterparty news hybrid: the news workflow works out the borrower and the event for each morning story; a warning alerts the credit officer, no warning is filed, and a shared name or wrong ticker goes to review, where an agent reads the credit files overnight, code checks its evidence and the analyst decides in the morning](/blog/agentic-vs-workflows-small-models/fallback.webp)

Code checks the agent's finding before it reaches the analyst's queue: the outcome has to be one the rules allow for that review reason, the suggested borrower has to be one the workflow listed, the tool log has to show every candidate's file read, and every quote has to appear word for word in the article or the file. So code always checks the agent's evidence, but it can only check the agent's pick of borrower when the candidates would score differently. On Northwind it can't. Code reruns the workflow's warning check once for each Northwind, and since both read the same article, both come back with the same score. So the finding goes to the analyst marked as not checked, and the analyst confirms the pick. Each agent job costs around 400 times what the workflow spends on an article, which is fine only because the review queue is a few rows a day.

## Testing Each One

A workflow runs the same steps thousands of times with nobody watching, so its tests are concrete cases of the inputs you expect plus the edge cases (the swapped flat and house number, a good bill from the wrong kind of issuer). An agent's route changes from run to run, so I'd test it with varied requests, odd ones included, and run each one more than once. The cookbook ran each agent example 5 times, and the missed Larkspur account came up in all 5, which tells you it's a pattern and not bad luck. Either way, test on your own documents before you trust it.

## Run It Yourself

The easiest way to try this is on [SIE Serverless](/serverless) with the cookbook's synthetic data. You need [uv](https://docs.astral.sh/uv/), an [SIE API key](https://console.superlinked.com/console/keys), and an `ANTHROPIC_API_KEY` for the agent:

```bash
git clone https://github.com/superlinked/cookbook && cd cookbook
cp .env.example .env    # set SIE_API_KEY and ANTHROPIC_API_KEY
cd finance/check_kyc_documents_against_policy
uv run recipe.py                     # the daily workflow
uv run --group agent agent.py        # the customer who moved, then the regulator's letter
```

`uv run --group agent agent.py --chat` lets you type the analyst's questions yourself, which is the quickest way to get a feel for where the code sends an answer back. The counterparty news folder runs the same way. If you'd rather keep the scans on your own GPUs, the cookbook's [self-hosting guide](https://github.com/superlinked/cookbook/blob/main/SELF_HOSTING.md) has the setup.

## Conclusion

Take a job you do every week and write down its steps. The steps you know beforehand go into a workflow, with code doing what it can and a small model for each step that reads a scan, a page or a story. If the steps change completely with what you see, build an agent and have code check every part of its answer it can. If they change only in some parts, keep the workflow and hand just those parts to an agent. Either way, the person still makes the decision.

If you're trying to place one of your own jobs on one side or the other, I'd love to hear what it is. Until next time.
