Skip to content
Wetel
Go back

How an AI employee reads a photo, and how you check what it read

Think of the clerk at a parcel counter. A customer slides a delivery slip across the desk. The clerk looks at it, copies the tracking number onto a form, types it into the system, and reads the result back: “That one is out for delivery today.” If the slip is smudged, the clerk says so instead of guessing. If the parcel is expensive, a supervisor signs before anything is refunded.

Reading a photo is the same job for an AI employee. The picture has to be fetched and read, the result has to land where the next step can use it, and somebody has to be able to check afterwards what was read. This post walks through each step as it works in Wetel today, for the operations lead and for the developer who builds it.

The short version: one step fetches, shrinks and reads the images

In Wetel, a single llm step in the workflow can fetch the photos itself, shrink them, send them to the model and save what it read into a named variable. You do not need a separate download step per image. The later steps of the workflow then use that variable like any other piece of text: put it in a reply, compare it, or pass it to a step that calls your own system.

flowchart LR
A["Customer sends a photo<br/>in the conversation"] --> B["llm step fetches,<br/>shrinks and reads it"]
B --> C["What it read is saved<br/>in a variable"]
C --> D["Reply to the customer,<br/>or pass it to the next step"]
B -. "records counts and reasons" .-> E["Run record<br/>(node trace)"]

A workspace owner decides what the workflow may do. The AI employee reads; your people decide what happens with the result.

How the photo gets to the AI employee

There are three documented ways; pick the one that matches how your channel delivers files.

  1. A marker in the message text. Your channel writes [image:<url>] into the message for each picture, anywhere in the text. This is the pattern in the photo-reading recipe.
  2. Structured attachments on the message. Since 2026-10-08 a message can carry attachments as fields (up to 10 per message), and the workflow sees the image URLs in a ready-made variable called attachmentImageUrls. The changelog says where this is live. See Sending files and context.
  3. A list of URLs in a variable. For a batch job or a form submission, an earlier step (or your own integration) puts the URLs in a variable, and the reading step is told its name.

An older pattern still works: an action step downloads each image first and the llm step attaches it with imageVars, one download step per image. The image understanding guide covers it.

The configuration, copied from the docs

Three options on the llm step control image reading: imageMarkerHistory, imageUrlsVar and imageUrlsFailPolicy. These are the real field names; there are no others to set.

For a customer who sends photos of a parcel in the conversation, the docs show exactly this shape. The message:

Here are the two photos of the parcel [image:https://cdn.example.com/u/123/front.jpg] [image:https://cdn.example.com/u/123/label.jpg]

And the step:

{
"id": "reply",
"type": "llm",
"config": {
"imageMarkerHistory": 5,
"promptTemplate": "The user said: \"{{userMessage}}\". Answer using what you can see in the attached photos.",
"outputVar": "replyText"
}
}

imageMarkerHistory: 5 attaches the images named by the last five markers in the customer’s messages (the AI employee’s own replies are never read for URLs). Since 2026-10-08 it also picks up images sent as structured attachments, in earlier messages as well as the current one, so a customer can send a picture in one message and ask about it in the next.

When the URLs come from somewhere else, replace imageMarkerHistory with "imageUrlsVar": "photoUrls", naming the variable that holds them. If the images arrive as structured attachments, set imageUrlsVar to attachmentImageUrls and no extra step is needed.

Whatever the step reads lands in its outputVar (here replyText), and every later step can use it. That is the “copy the number onto the form” part of the clerk’s job.

One instruction worth copying word for word

The model also sees the marker text, URL included, so it can “read” a word that is only in the file name. A photo called damaged-brake-pad.jpg invites an answer about brake pads whether or not the picture shows one, and this can happen even for an image that failed to load. The docs give two fixes: use opaque file names, and put this line in the prompt:

Describe only what is visible in the attached images. Never infer anything from file names or URLs. If an image could not be loaded, say so.

In the docs’ own testing, a plain prompt let the model read the URL text and this wording stopped it. It is cheap insurance.

What happens when an image cannot be loaded

By default the step carries on with the images it could load and tells the model how many failed. The model sees one extra line, for example:

[note: 2 of 7 images could not be loaded]

so it does not describe five pictures as if they were all the customer sent. In the recipe’s dead-link test, the answer began “Based on the images that loaded:” and described the two that did.

Three details matter for operations:

The limits, as documented

These are platform defaults, documented on the llm step reference; they may be tuned, so check with us if a number matters to your design.

Every customer message re-fetches the last N marker images, so keep imageMarkerHistory as small as the question needs. A parcel label needs one or two, not forty.

The step must run on a model that reads images. The platform’s default model has since 2026-10-01; on an agent pinned to one that cannot, an image turn fails the run rather than letting the model answer about a picture it never received.

How to check what it read

Every run leaves a record, and the reading step’s entry says exactly how many images were requested, attached and skipped, and why. Query workflowRuns for the session with a dashboard login (the API key and the session token cannot read it), and look at the llm entry in nodeTrace. The recipe’s real, shortened example:

{
"nodeId": "look",
"status": "COMPLETED",
"nodeType": "llm",
"outputVar": "imageDescription",
"imageFetch": {
"hosts": ["cdn.example.com"],
"skips": [],
"shrunk": 3,
"skipped": 0,
"attached": 3,
"requested": 3
}
}

The same entry carries outputValue, the text the step produced, so you can put what the AI employee read next to what the customer sent. When something was skipped, skips holds one { host, reason } per image, for example http-404.

The image bytes are never stored in the run’s context or record, and the record never holds a URL path or query string, because a signed link works like a password. Read back through the API, a marker’s query string shows as ?[redacted] (the changelog says where this is live).

The honest limits: it can misread, so confirm what matters

We do not publish an accuracy figure for photo reading, and you should not assume one. A model can misread a digit, a smudged label or a photo taken at an angle. The run record tells you what it read; it cannot tell you whether that was right. Test it on your own photos before you depend on it.

So treat what was read the way the clerk treats a hand-copied number: check it before anything happens that is hard to undo.

flowchart LR
R["llm step reads<br/>the receipt"] --> P{{"approval:<br/>owner checks the amount"}}
P -- approved --> W["carry on with<br/>the checked value"]
P -- rejected --> N["tell the customer<br/>it was declined"]
P -- expired --> X["tell the customer<br/>someone will follow up"]

Approvals today go to workspace owners, with in-app notification; email notice is not available yet. The human approval walkthrough builds this step by step, and you can test each outcome in a draft without opening a real request.

What is live and what is not

Live today: URL image reading in one step, skip-and-tell and the fail option, the run record, and an approval step you can add. Newer options, structured attachments and redacted signed links, are listed in the changelog, which says where each one is live; check it for your environment. Not built: a published accuracy figure, approval that happens without you adding the step, and email notice for approvals.

To build one end to end, follow the photo-reading recipe: it takes you from an empty workspace to a working agent and a run record you can read.


Share this post:

Next Post
What happens to a workflow when nobody approves in time?
Previous Post
Chatbot, assistant or AI employee: what is the difference?