The ZaronAI runtime renders images inline in chat using a sentinel system. A sentinel is a structured token the AI emits in plain text. The runtime detects it, resolves it, and renders the appropriate HTML. The AI never emits raw HTML.
The image sentinel used in V1 is:
[[ZAI:IMAGE|url=https://yoursite.com/img/pool.jpg|width=320]]
To enable image display in a model, three things must be in place:
imageUrl and imageWidth fields on each product record.) is fragile — models occasionally mangle spacing or punctuation. Raw HTML in model output is a security risk. The [[ZAI:]] double-bracket prefix is rare in natural language, log output, and code, making parser collisions essentially impossible. The model emits intent; the runtime owns rendering.Each KB record that represents a visual product needs two new flat fields added at the top level alongside id, title, and content.
| Field | Type | Description |
|---|---|---|
imageUrl | string | Fully qualified https:// URL to the product image. Must be accessible from the browser serving the chat widget. |
imageWidth | string (digits) | Display width in pixels as a string, e.g. "320". Controls max-width in the rendered <img> tag. Omit or leave empty to use the runtime default of 320px. |
id and title. Do not nest them inside content. The AI reads the tool result as a JSON object and must be able to reference imageUrl and imageWidth directly by key name.Repeat for every KB record that has a product image. Records without imageUrl are unaffected — the Sales agent only emits the sentinel when imageUrl is present and non-empty.
Three agents in the pipeline need prompt additions. Two are simple one-paragraph preservation rules. Only the Sales agent requires a substantive instruction block.
| Agent | Change required | Reason |
|---|---|---|
| Sales | Add sentinel emission instruction block | Sales is the only agent that presents product data from the KB tool. It must be told when and how to emit the image sentinel. |
| Reviewer | Add sentinel preservation rule | The Reviewer rewrites messageToUser. Without an explicit instruction it may strip or corrupt the [[ZAI:]] token, treating it as unusual text. |
| Finalizer | Add sentinel preservation rule | The Finalizer polishes reply_to_user from Reviewer output. Same risk as the Reviewer — the token must be declared opaque. |
| Router | No change | Router never touches product content or output text. |
| Service | No change | Service handles repairs and diagnostics, not product presentation. It does not call the KB product tool. |
Where to insert: Immediately before the “Behavior and language:” section of the Sales agent system prompt.
Why the Sales agent only: Sales is the sole agent that calls search_knowledge_base and presents product records to the user. Other agents receive and forward the already-composed messageToUser — they do not generate product descriptions themselves.
Why include a worked example: Giving the model a concrete example of the exact sentinel syntax significantly improves emission reliability. The model anchors to the example pattern rather than inferring the format from the description alone.
Why the conditional on imageUrl: Some KB records may not have images. The conditional prevents the agent from emitting a broken sentinel with an empty URL.
Where to insert: Immediately before the “3. Output:” section of the Reviewer prompt.
Why this is necessary: The Reviewer inspects and potentially rewrites messageToUser. Without this instruction the model may paraphrase around the sentinel, relocate it, or silently drop it during a rewrite. The preservation rule makes the token explicitly opaque — the Reviewer must treat it as pass-through, not content.
Where to insert: Immediately before the “4. Consistency:” section of the Finalizer prompt.
Why this is necessary: The Finalizer produces reply_to_user by polishing the Reviewer output. It is the last agent before the response reaches the user — if it drops or corrupts the sentinel here there is no recovery path. The rule is identical in intent to the Reviewer's but scoped to reply_to_user.
[[ZAI:IMAGE|url=URL|width=WIDTH]] <-- preferred: model-controlled width
[[ZAI:IMAGE|url=URL]] <-- fallback: runtime defaults to 320px
[[ZAI: and end with ]].IMAGE) must be uppercase.key=value pairs.http:// or https:// URLs are accepted — other values are silently ignored.width must be digits only (pixels, no px suffix in the sentinel).|width= never accidentally matches the fallback.<!-- [[ZAI:IMAGE|url=URL|width=320]] renders as: -->
<img src="URL"
style="max-width:320px;width:100%;display:block;
margin:6px 0;border-radius:4px;" />
max-width:NNNpx — hard cap from imageWidth. Images wider than this are scaled down.width:100% — ensures the image shrinks on narrow screens or narrow chat widgets.imageWidth in the KB record. No code change required.imageWidth is absent: 320px.[[ZAI:POOL_CARD|id=KB-POOL-001]] <-- full product card
[[ZAI:POOL_COMPARE|name1=Laguna Breeze|name2=Monte Carlo]] <-- side-by-side
[[ZAI:POOL_LIST|names=Laguna Breeze~Monte Carlo]] <-- multi-card row
[[ZAI:KB_LINK|id=KB-POOL-001]] <-- expandable link
[[ZAI:]] tokens pass through RenderAiOutput() completely unchanged. The function checks for the [[ZAI: prefix before doing any work — normal responses take the fast path with zero regex overhead.imageUrl to each KB record that has a product image. Value must be a fully qualified https:// URL accessible from the browser.imageWidth to each KB record. Recommended starting value: "320". Adjust per product to control display size without touching code.cAiFormat.RenderAiOutput() contains the [[ZAI:IMAGE]] regex patterns). Earlier versions used the deprecated [img:url] syntax.imageUrl populated. Confirm the image renders inline at the expected size. Then test a product without imageUrl — confirm clean text with no broken sentinel visible.fillPastConversation path. Images in stored aiExchanges records should render correctly on replay since RenderAiOutput() is applied on that path as well.