Authoring for the agent
The Appilot assistant works best when you give it a small amount of the right information, not a large amount of everything. This page is the mental model you should hold while configuring your app.
If you take only one thing from this page: the assistant already reads the page. Your job is not to describe the page; your job is to teach the assistant how your app works and what your team expects.
If you are configuring a brand-new domain, start with Getting started first. This page is the mental model; that page is the checklist.
How the assistant uses what's on the page
The assistant has a small set of read-only tools that look at the live DOM during every turn:
- Find buttons, inputs, links by their visible label or accessible name.
- Read what tab or section is currently active.
- Read what is in a form right now (current values, which fields are disabled, which are showing validation errors).
- Watch for a status banner to appear after an action.
- Read any rendered text on the page.
You don't need to teach the assistant any of these. It can already see them. Authoring exists to give the assistant the things the page does NOT say:
- The why of a workflow (tab ordering depends on a linked Leistung; you must save the draft before the Publish dialog can open).
- The meaning of a term in your context (Vertrauensniveau is not a confidence score).
- The organisation's expectations (the four mandatory parameters; QS-Review before publish).
- The names the assistant should use for actions so plans survive UI relabels and your KB stays coherent.
The remaining sections of this page tell you when to author each kind of artifact.
What belongs where
Good authoring keeps four responsibilities separate:
| Layer | What it should contain | What it should not contain |
|---|---|---|
| System prompt | Stable assistant identity, safety boundaries, answer language, and the minimal loop protocol. | App-specific workflows, tool-routing rules, selectors, URLs, or long manuals. |
| Tool metadata | When to use a capability, when not to use it, required parameters, and what the returned resource means. | Business-process explanations that belong in Knowledge, or hidden runtime details such as HTTP paths. |
| Knowledge | Workflows, business rules, terminology, decision criteria, and why a capability matters in your domain. | Raw selectors, API paths, or generic descriptions of what is already visible on the page. |
| Runtime | Resolved selectors, clickable labels, citations, resources, zones, and executable plan targets. | Model-authored selectors or URLs. |
For authors, the practical rule is simple: write Knowledge like a domain expert explaining the work, and write Tool descriptions like a capability contract. A Tool description should say "use when", "does not", and "if no fit, answer honestly". A Knowledge entry should explain how that capability fits a real workflow.
Three layers, with precedence
When a user asks the assistant a question, the assistant draws from three layers of information that compose, not compete. They have explicit precedence when more than one describes the same thing.
1. Knowledge - the manual, primary source when you author it
Markdown documents you author. Knowledge has three legitimate flavours, and the highest-leverage choice is which mix to invest in:
- Business / conceptual content: glossary, business rules, conformance requirements, the meaning of fields, the why behind workflows. No buttons or forms mentioned. Almost always
domain_globalscope. Author when the information matters to the user and is NOT directly readable from the page (terminology, organisation policy, multi-page rules). - App UI patterns: explains the selector conventions of your application: "all action buttons in this app follow the CSS pattern X", "form fields are wrapped in element Y with the visible label as
aria-label", "the sidebar menu uses a CSS-hover mechanic on the wrapper". Usually one entry per app,domain_globalscope. This is the highest-leverage entry a customer can write: one document covers hundreds of buttons without registering each one as a Control. The assistant derives selectors from your patterns at runtime and verifies them against the live DOM before acting. - Workflows: procedural prose: "to create an X, navigate to the list, click Create, fill the General tab, ...". May embed
{{form:X}}tokens for multi-field forms (where the structured Registry adds value) and{{control:Y}}tokens only for affordances whose prerequisites are irreducible (hover-to-reveal flyouts, modal-only-after-click). The assistant turns the current-view chunk into narrative steps that the Action Plan UI can render and execute. Cross-view workflows should be described as a sequence of view-sized chunks, not one giant plan.
Knowledge is the primary source when you author it. Most well-configured apps run beautifully with one UI-patterns entry plus a handful of workflow + business entries.
When a task is a stable, repeatable click-path you want the assistant to run the same way every time (e.g. "create a folder", "reset a password"), author it as a curated Action Plan (Backoffice → Action Plans), not as a workflow Knowledge entry. The Action Plan is the maintained, deterministic path: the assistant sees it in context and adopts it in one step.
Do not then also write a Knowledge article that lists the same steps. A step-by-step KB doc for a task that already has an Action Plan competes with it: it teaches the assistant to re-author the steps from prose (often worse, and it can drop the form field) instead of adopting the maintained plan. Keep Knowledge for the why and the surrounding rules; let the Action Plan own the steps.
2. Controls, Forms, and Tools - named handles, precision override layer
These are not pre-cooked DOM selectors. They are named, addressable handles the agent can refer to when prose-Knowledge and the live page aren't enough on their own:
- A Control names a single UI element (a button, a field, a link) with a stable identifier, optional prerequisites (the prior steps that reveal it), and a description.
- A Form names an ordered group of fields that fill together, possibly across tabs, with visibility rules and defaults. This is the highest-leverage Registry entity: Forms carry multi-field structure that prose cannot compress.
- A Tool names a backend capability (an HTTP lookup, a server-side calculation) the agent can invoke autonomously.
You author them only when prose-Knowledge patterns and the live DOM aren't enough: when the affordance is hidden until a non-obvious prior step reveals it, when a multi-field workflow needs structure, or when a Form's entry_control_id / submit_control_id requires a stable handle by id. The four-bullet decision rules per entity are below.
3. The page itself - the universal substrate, always available
The assistant queries the DOM directly with read-only tools (see the list at the top of this page). You don't author this; it's already there.
The DOM is not a fallback; it's the substrate every plan ultimately resolves to. The assistant is designed to function with zero authored content by reading the page intelligently. When you do author Knowledge or Registry entries, you make the agent more precise and more confident; you don't make it work that wasn't working.
Precedence: registry > pattern-KB > DOM inference
When more than one layer describes the same thing, the assistant follows: registered Control / Form > prose-Knowledge pattern > DOM inference from ARIA. Each lower layer is more general and less customer-verified, so when you have invested in a precise layer, that investment wins. This means you can safely author a UI-patterns entry that covers most buttons AND register a specific Control for one tricky case; the Control will take precedence for that specific case and the patterns entry will cover the rest.
Sizing: how much Knowledge is enough
Knowledge entries scoped to the user's current view (and entries scoped domain_global) are pre-loaded on every turn. They share a finite token budget with the rest of the agent's context. Authoring too much pre-loaded content degrades response quality silently.
The discipline:
| Question | Rule of thumb |
|---|---|
| Tokens per entry | 200–500 tokens (~150–400 words). If you exceed 700, split by sub-topic. |
Always-loaded domain_global entries | 4–8 entries across the whole domain. These are read on every question; treat them as gold. |
page-scoped entries | As many as you need, scoped tightly. Only the entries linked to the current view load. |
| Long-form material (manuals, regulations, glossaries) | Ingest as RAG files (PDF / Markdown upload). The agent retrieves only the relevant chunks per turn. See RAG files. |
| Multi-locale tenants | The pre-loaded set is read per locale. If you author the same entry in DE + ES + EN, the budget triples. Use locale overrides only for terminology that genuinely differs. |
Symptoms of over-authoring: the assistant drops citations on simple questions, ignores your tokens, repeats itself, or seems to forget context mid-conversation. Pull the per-locale domain_global set under 4,000 tokens total and quality returns.
Symptoms of under-authoring: 🔴 responses ("I don't have guidance for this") on questions a colleague could answer in one sentence. Open the AI Reports page and check the Missing Knowledge inbox; add the missing entries.
Decision trees
Each entity has a four-bullet "author when" rule. If none of the bullets apply, do not author. The agent's accuracy degrades faster from over-authoring than from under-authoring.
Should I author a Knowledge entry for this?
Author when any of:
- The information cannot be inferred from the page alone (workflow rationale, ordering rules, conformance constraints, business meaning of a field).
- The information must apply across multiple pages / views (terminology, organisation-wide policy).
- You want to change the assistant's response style for a workflow (escalation policy, regulated wording, brand voice).
- The entry references a Form / Control / Tool via
{{form:…}}/{{control:…}}/{{tool:…}}to make the action plan executable.
Do not author when the entry would just re-state what is plainly on the page ("the Save button saves the form"). The assistant can read the page.
Should I author a Control for this button or field?
A Control is not a DOM selector; it's a named handle with metadata neither the DOM nor a UI-patterns Knowledge entry can express.
Author when any of:
- Prerequisites. The button exists only after a prior step reveals it (open the drawer, hover a menu, open a modal). The agent can't infer that prior step from the visible DOM; the Control declares it as a prerequisite. This is the #1 reason to register a Control.
- Form linkage. The button is a Form's
entry_control_id(the action that opens the form) orsubmit_control_id(the action that commits it). Forms reference Controls by id, so these must be registered. - Disambiguation that prose patterns can't resolve. Multiple visually similar elements share a label or role, AND a UI-patterns entry would not unambiguously pick the right one. The Control provides override precision.
- A KB workflow MUST refer to this specific action by token name (rare; prefer prose with a UI-patterns entry whenever the button is findable from its label).
Do not author when the button has a unique visible label on a stable page AND your UI-patterns entry teaches the agent how to find it AND no Form references it as entry_control_id / submit_control_id. The assistant resolves selectors from patterns + find_elements verification; registering one more Control just adds maintenance burden. The TSA refactor of 2026-05-19 dropped 4 such Controls (a primary-action button, two read-only tab transitions, an add-language button) and kept the entry / submit Controls because Forms reference them by id; the IDv5 UI-patterns entry covers the rest with zero loss of precision.
Should I author a Form for these fields?
A Form is not a pre-loaded blueprint of every field; it's a semantic template materialised on demand when the KB references it.
Author when any of:
- Multi-field intent that a user would describe as "fill in the X form" rather than "click each field".
- Multi-tab transitions where field ordering crosses tab boundaries the agent must walk.
- Visibility rules (a field appears only when another has a specific value) that the DOM does not declare upfront.
- Field defaults that drive "fill with test data" or autocomplete-style flows.
Do not author when a single field with a unique label suffices (use a Control), or when the workflow is non-deterministic enough that prose Knowledge is more honest than a fixed Form.
Should I author a View for this region?
A View is a detectable UI state the agent can route from, not just a section.
Author when any of:
- The region has its own URL OR a stable DOM detection rule.
- The agent's answers should differ when the user is here vs elsewhere.
- You want
page-scoped Knowledge / Controls / Forms / Tools to attach only here. - You want analytics broken out by this surface.
Do not author when the "region" is a modal (that's state, not a view), a focused field, or a transient overlay.
Should I author a Zone for this region?
A Zone is descriptive only: the agent grounds references like "the sidebar" or "the notifications area" against it; it never clicks on a Zone.
Author when any of:
- The agent should be able to point at a region by name in its prose answers.
- The region lacks a standard ARIA landmark or
<nav>/<aside>/<header>/<footer>so auto-detection fails. - The region maps to a future WebMCP Resource you want to declare today.
Do not author when the region is already covered by an auto-detected landmark you have not renamed, or when it would duplicate a View.
Should I author a Tool for this capability?
A Tool is a backend capability the agent invokes autonomously, equivalent in shape to a WebMCP registerTool({ name, description, parameters }). It is not a click-script.
Author when any of:
- Server-side computation or lookup the user benefits from (translate a code, validate a key, compute a summary, generate a draft).
- Integration with an existing API (
/services/leistung-search?q=…) the agent should be able to call without you scripting the request. - Stateful operation (create a draft on the server, schedule a job) where calling once is preferred over guiding a multi-click form.
- A capability the host app will eventually expose via WebMCP (today's Tool is the migration shim that becomes obsolete once the app publishes its own).
Do not author when the data is static (use Knowledge), the action is purely DOM-side (use Control or Form), or the capability requires an auth token the agent does not hold.
When you author a Tool, make the description do routing work:
- Use when: the user intent that should trigger this capability.
- Does not: the nearby requests it must not answer.
- No fit: what the assistant should say when none of the declared tools match.
Put the domain explanation in Knowledge. For example, the Tool description can say "searches the user directory, not personal contacts"; the Knowledge entry can explain when your team treats a request as a colleague search.
See Managing Tools for the configuration walkthrough.
Examples
Good Knowledge entry (prose-driven workflow, tokens only where they earn their keep)
Title: Creating an Onlinedienst, end-to-end Scope:
page, onzf/dashboard,zf/online-services,zf/online-services/formFrom the Dashboard, navigate to the Onlinedienste list via
{{control:drawer-goto-online-services}}(the sidebar icon requires a brief hover to reveal the submenu; the assistant walks you through that hover step). On the list, click the Neu button in the toolbar (toolbar actionCREATE, found via the IDv5 UI patterns entry).The form has 7 tabs. The order matters: Zuständigkeiten must be filled before Zahlungen and Informationen, because those tabs depend on the linked Leistung.
Allgemeine Angaben fields can be filled with
{{form:online-service-form}}. Configuration parameters use{{form:online-service-parameter}}. The Landesredaktion requires four mandatory parameters (see the Pflichtparameter entry).Save the draft via the Speichern button in the toolbar (toolbar action
SAVE). Publishing is a separate dialog, not covered by this assistant.
This entry:
- Teaches the workflow rationale (tab order matters because of dependencies).
- Uses tokens only where they earn their keep: forms (multi-field structure) and the hidden drawer control (its irreducible hover prerequisite). Toolbar buttons are described in prose; the assistant finds them via the app's UI-patterns entry.
- Keeps the workflow in view-sized chunks: navigation, a tab fill, a parameter, or feedback reading.
- Points at related Knowledge for deeper topics (mandatory parameters).
- Does not re-describe what every field is for. The assistant can read the form.
Size: ~280 tokens. Below the 500-token soft cap.
Good UI-patterns entry (one document, hundreds of buttons covered)
Title: IDv5 UI selector conventions Scope:
domain_globalToolbar action buttons follow
vaadin-menu-bar-button.shared-action-button.{NAME}, where{NAME}is the English action in uppercase (Neu →CREATE, Speichern →SAVE, Bearbeiten →EDIT, Löschen →DELETE, ...). Primary action buttons (Testlink generieren, Hinzufügen, ...) followvaadin-button.shared-primary-button.{NAME}. Form fields are wrapped indiv.FieldRoot[aria-label="{visible label}"]with the input element inside (vaadin-text-field, -area, -combo-box, -select, -multi-select-combo-box, -checkbox). Form tabs usevaadin-menu-bar-button.shared-tab-bar-tab:has(vaadin-menu-bar-item[aria-label="{tab name}"]). Sidebar drawer items are hidden until hovered: the wrapperdiv.shared-drawer-button:has(...)reveals the click target via CSS:hover; use the registered drawer-wrapper-* and drawer-goto-* Controls for the supported sections.
This entry lets the assistant find any standard button or field across the app without you registering it. Investment: 30 minutes once per app. Payoff: dozens of unauthored controls become discoverable. The TSA reference seed uses exactly this pattern and runs the demo with ~23 registered Controls instead of the ~80 a button-by-button approach would require.
Bad Knowledge entry (parrots the screen)
Title: The Save button
The Save button saves the form. Click it to save your changes.
The assistant already knows this from the page. Delete.
Good Control authoring (precise, hard to find)
semantic_id:
drawer-goto-online-serviceslabel: Onlinedienste description: (USER-FACING) Opens the list of online services. llm_hint: (AGENT-FACING) Hidden untildrawer-wrapper-online-servicesis hovered. When the user is on the Dashboard, emit a single click on this control; the assistant inlines the hover step automatically from its prerequisites. locator:vaadin-button.shared-drawer-button[aria-label="Onlinedienste"]prerequisites: a singlehoverstep ondrawer-wrapper-online-servicesthat reveals this control.
This Control:
- Has a stable, semantic name (
drawer-goto-online-services) that survives UI relabels. - Encodes the non-trivial hover step via
prerequisitesso the assistant inlines it and guides the user through it. - Has a
descriptionin the user's voice: prose the assistant can show users in the on-page coach-mark. - Has an
llm_hintin your voice as an author talking to the agent: routing notes, semantic_ids, and architectural hints the user must never see. See § Writing for users vs the agent.
Bad Control authoring (every button on the page)
Authoring
control-button-1,control-button-2, ...control-button-47for every button on the toolbar.
Don't. The assistant can find buttons by their visible label or aria-label. Author Controls when the assistant would otherwise pick the wrong one, can't reach the affordance, or needs a stable name your Knowledge depends on.
Good Tool authoring (backend capability)
Tool name:
lookup_leistungtitle: Search the Leistungskatalog description: Use this tool when the user needs to find a service by name or LeiKa key while creating an Onlinedienst. Returns id, official name, applicable regions, and current version. Does not explain service policy or mandatory parameters. If no service lookup is needed, answer from Knowledge or the page instead. parameters:{ query: string (required), region_filter: string (optional, ARS prefix) }
This Tool:
- Solves something the DOM and KB cannot (a server search).
- Has a description that tells the agent when to call it, when not to call it, and what to do when it does not fit.
- Has typed parameters the agent can populate from the user's question.
Bad Tool authoring (static data masquerading as a Tool)
semantic_id:
list-mandatory-parametersdescription: Returns the list of four mandatory parameters required by the Landesredaktion.
This isn't a Tool: it's a fact. Put it in a Knowledge entry titled "Pflichtparameter" and let the agent read it directly.
Writing for users vs the agent
Every Control and every Form has two text fields that look similar but serve different audiences. Knowing which is which prevents accidental leaks.
The two fields
description: written for the end user. The assistant shows it verbatim in the on-page coach-mark when the tour runs in "Teach me" pace, and in the Action Plan row's content column. It also appears in the Backoffice listings for your team.llm_hint: written for the agent. Returned to the LLM when it callsget_control/list_controls/get_form/list_forms. Optional. The system strips it out before the payload reaches the browser extension, so users never see it. Use it for notes the agent needs but the user shouldn't read.
When to use each
| If your sentence reads like... | It belongs in |
|---|---|
| "Click here to add a parameter." | description (user instruction) |
| "Opens the responsibility wizard." | description (factual, user-readable) |
| "Required when payment_status === 'choice'." | llm_hint (agent routing logic) |
| "Hover-trigger only; do not emit as click." | llm_hint (agent-only directive) |
| "Wrapper element. CSS :hover reveals the flyout." | llm_hint (internal architecture) |
| "(drawer-wrapper-online-services)" | llm_hint (NEVER in description: those parentheses are an internal id) |
Quick test: read your sentence out loud. If it sounds like documentation written for a developer or technical writer (mentions selectors, semantic_ids, architectural terms, code), it belongs in llm_hint. If it sounds like instructions you'd put on a sticky note for a colleague using the app, it belongs in description.
description is the exception: it is agent-facingA Control's / Form's description is user-facing (above). An Action Plan's description is the opposite: it is read by the assistant, not shown on the plan card. Write it as a trigger so the assistant knows when to adopt the plan: "Use when the user wants to create a new folder." The plan's name is the user-facing label on the card; the description is how the assistant matches a request to the plan. Keep selectors and internal ids out of it as usual.
A bad example (from a real regression)
This was a real authored description that leaked to a user during a demo:
❌ description: "Navigates to the Onlinedienste list. Requires hovering the wrapper (drawer-wrapper-online-services) so the flyout reveals the entry."
What's wrong:
- "the wrapper" is internal terminology.
- "(drawer-wrapper-online-services)" is a
semantic_id, an internal identifier the user has no context for. - "the flyout reveals the entry" describes the DOM mechanism, not the user's action.
Fixed by splitting:
✅ description: "Opens the list of online services." ✅ llm_hint: "Hidden until drawer-wrapper-online-services is hovered. Emit a single click on this control; the assistant inlines the hover step automatically from its prerequisites."
Both fields say what's true. They just say it to the right reader.
When in doubt, write the description first
Most Controls only need description. The user-voice prose works fine as agent context too: the agent reads description along with everything else.
Only add llm_hint when you have something to say that the user shouldn't see. Three common cases:
- The control has a non-obvious technical constraint (HTTPS only, max 200 chars, hidden when X is false).
- The control is a runtime-only artifact (a hover trigger, a wrapper) and the agent needs to know not to emit it as its own action.
- You're using the same control across multiple workflows and want to tell the agent which workflow each is intended for.
If none of those apply, leave llm_hint blank.
The "do less" principle
A common pitfall is the urge to describe everything. Resist it.
Every Control you author is a small maintenance burden: if the page changes, the selector might drift, and the assistant ends up worse off than it would be by reading the page fresh. Every Knowledge entry that just re-states the screen is noise the assistant has to wade through. Every Tool that duplicates a fact is a worse alternative to a single KB sentence.
A good rule of thumb: start with Knowledge only. Watch the assistant for a few days. Add a Control, Form, or Tool only when you see the assistant struggle in a specific spot.
The Backoffice will help you spot the right places to invest: the AI Reports page shows which questions ended in 🔴 ("I don't have guidance for this") and which Controls the assistant tried to use but couldn't find.
Web MCP: why the vocabulary matters
Appilot's entities are not invented in isolation. They are deliberately shaped to mirror Web MCP (the emerging Model Context Protocol for web apps) and VOIX (a sister proposal):
| Appilot entity | Web MCP equivalent |
|---|---|
| Control | tool/affordance with a stable id |
| Form | structured tool with a parameters schema |
| Tool | navigator.modelContext.registerTool({ name, description, parameters }) |
| Zone | MCP resource (described region) |
| View | MCP resource (page state) |
When the host app eventually adopts Web MCP directly, your authored entities re-export to the standard with minimal rewriting. Today, your configuration is the bridge that lets the agent work on apps that haven't yet adopted the standard. The vocabulary you learn here is the vocabulary you will use when the web catches up.
You don't have to think about this while authoring, but knowing it explains why the entity shapes look the way they do.
When the assistant gets it wrong
When a user reports a bad response (👎 in the conversation panel):
- Open the AI Reports page in the Backoffice for that domain.
- Find the conversation. The deep inspector shows what the assistant saw at each step: which Knowledge entries it read, which Controls / Forms / Tools it tried to fetch, what the page told it, what it ended up saying.
- Diagnose the gap:
- Was the right Knowledge entry not loaded? → Adjust its view links or scope.
- Did the assistant invent a
control_id? → Make sure the entity it tried to reach actually exists. - Did the assistant emit 🔴 honestly? → Author the missing Knowledge, or add a Tool if a backend capability would unblock it.
Bias toward adding Knowledge, not Controls. Knowledge entries are cheap to author and easy to maintain. Controls are heavier; Tools heavier still.
Writing the steps of a curated Action Plan
When you curate an Action Plan for a known task, the Backoffice editor guides each step so you do not have to remember the marker syntax:
- One step, one sentence, one action. Write the step in plain language, for example "Click Save to confirm". Type
/in the step box (or use Insert action) to pick the control, form, or zone for that view; the editor inserts the action for you. - The action chip. Below each step the editor shows the parsed action as
Verb -> Target. For a control step you can change the verb (Click, Hover, Set value, Select) straight from the chip without rewriting anything. Hover hands the step off to the user, because a page cannot trigger a real hover on its own. - The value never lives in the step. When a step fills a form, its default values appear in their own fields under the step, not inside the sentence.
- Typos are caught early. If a step points at a control or form id that does not exist, the editor flags it and suggests the closest real id with a one-click fix.
A plan is grouped by view. Add a new section when the user has to move to another view first, and describe how they get there (a control to click, or a short hint).
Related pages
- Getting started: Day 1 checklist for a fresh domain.
- Knowledge management: Backoffice mechanics for KB entries.
- Managing Controls: Backoffice mechanics for Controls.
- Managing Forms: Backoffice mechanics for Forms.
- Managing Views: Backoffice mechanics for Views.
- Managing Zones: Backoffice mechanics for Zones.
- Managing Tools: Backoffice mechanics for Tools.
- Domain configuration: registering domains and URL patterns.
- RAG files: ingest long-form documentation.