Skip to content
AI

Ask AI Chatbot

The Ask AI chatbot is an assistant that lives inside the Web client. Through the chat window, a user can work on a Dashboard in plain language while an agentic loop calls live 3forge Enterprise operations in response, e.g: querying data, building panels, editing callbacks, inspecting logs. The model's reasoning and results are then streamed back into the window.

It operates on the running Dashboard. It can see the current layout, run real queries, and make real changes.

Enabling and Configuring Providers

The chatbot is off by default. Enable it and declare at least one provider in the Web component's local.properties:

1
2
3
4
5
6
7
8
ami.web.ai.enabled=true

# Required list of providers. First entry is the default. Ids or class names.
ami.web.ai.providers=anthropic

# Per-provider credentials and model list (the <id> is the provider id):
ami.web.ai.anthropic.api_key=<your_anthropic_key>
ami.web.ai.anthropic.model=claude-opus-4-8,claude-sonnet-5,claude-haiku-4-5

Built-in provider ids are anthropic, openai, gemini, and xai.

Available Properties

ami.web.ai.providers

Default:
ami.web.ai.providers=

Description:
  • Comma-delimited list of provider IDs (for built-in) or fully qualified class names for custom providers.
  • The first entry is the default provider, the one used when a chat, a sub-agent, or session.promptAi does not name one explicitly.
  • Every listed provider is registered, so a user can switch providers mid-session from the chat's model picker.
  • Listing a provider is what activates it. A built-in provider still needs its ami.web.ai.<id>.api_key to make calls, but it is the presence in this list, not the key, that registers it.

Note

2 special tokens are also accepted: * registers every provider discovered on the classpath, and !id excludes one (only meaningful alongside * e.g: *, !xai).

If AI is enabled but ami.web.ai.providers is unset, each user session fails to initialize.

Per-Provider Properties

Each provider reads its own ami.web.ai.<id>.* keys, where <id> is the provider id:

Property Purpose
ami.web.ai.<id>.api_key Credential for a built-in provider
ami.web.ai.<id>.model Comma-delimited model list; the first entry is the default model, and the full list becomes the "Switch model" dropdown
ami.web.ai.<id>.timeout_ms Socket timeout for calls to this provider
ami.web.ai.<id>.context_window Override the assumed context-window size (drives history compaction)

Other Tunables

All of the following are under the ami.web.ai. prefix:

Property Default Purpose
max_tokens 4096 Max tokens per model response
tools.max_per_iteration 30 Cap on tool calls dispatched from one model turn (anti-runaway guard)
subagent.max_iterations 25 Iteration budget for a spawned sub-agent
tools.sql.permissions read read exposes run_amisql only; read_write / full also expose write_amisql for DDL/DML
conversations.dir data/users/ai_chats Where chat histories persist
conversations.max 10 Retained conversations per user
compaction.enabled true Auto-summarize long histories to stay within context
compaction.threshold 0.80 Fraction of context that triggers compaction
system_prompt.dir (bundled) Point at a directory to override the built-in agent definitions

Opening the Chat

When AI is enabled, the desktop/Windows menu gains an "Ask AI" entry. Selecting it opens a chat portlet.

A chat can also be scoped to a specific panel: right-click a panel and choose "Ask AI". The agent then sees what you are looking at, so questions like "why is this table empty?" have context.

Within a chat, the gear menu's Chat submenu holds the conversation actions: rename, delete, and start a new chat. Conversations persist per user (up to conversations.max).

How a Turn Runs

Each prompt you send drives an agentic loop:

  1. The model streams a response into the chat window.
  2. Any operations (tools) it decides to run are dispatched against the live deployment.
  3. Their results are fed back to the model, and the loop iterates.
  4. The loop ends when the model finishes, or hits a safety budget.

Practical characteristics:

  • Multiple operations in one model turn run together, capped by tools.max_per_iteration.
  • Mutating operations are guarded: the layout is autosaved before changes, and a DDL gate and result-size cap protect the deployment.
  • A turn ends with a stop reason such as END_TURN, MAX_ITERATIONS, or CANCELLED.
  • For focused sub-tasks, the agent can spawn specialized sub-agents.

Note

The agent does not have Enterprise knowledge baked in. It fetches authoritative, build-current documentation at runtime through documentation tools, so its guidance stays in sync with the version you are running.

Agents and Their Tool Allowlists

The chatbot ships a set of agents, one Markdown file per agent, enumerated by an _agents.txt manifest alongside them. Each file holds that agent's system prompt, and optional front matter declaring which operations it is allowed to use:

1
2
3
4
5
6
7
8
9
---
tools:
  - think
  - run_amisql
  - web_console(op=panels)
  - web_console(op=exportLayout)
  - web_verify(op=panelJson)
---
# system prompt body follows

Point ami.web.ai.system_prompt.dir at your own directory to replace the bundled definitions.

Entry Forms

Related operations are grouped behind a single tool with an op selector, so an allowlist entry can name either the whole tool or one operation of it:

Entry Grants
run_amisql That tool
web_console That tool and every operation it exposes
web_console(op=panels) Only the panels operation of web_console

Several op entries for the same tool accumulate, so an agent can be given 3 operations of a tool and no others. Omitting the tools: block entirely gives the agent every operation available in the deployment.

Note

If you are carrying forward an agent file written before operations were grouped, its flat entries (e.g: show_panels) are still accepted and resolved to the operation that absorbed them.

A few cannot be: names that every component exposes, e.g: the properties and plugin-registry listings, are claimed by the Web, Center and Relay tools alike, so they are refused rather than resolved to an arbitrary component, and the entry grants nothing. Write those in the explicit form, naming the component you mean: center_console(op=properties).

How It Is Enforced

The allowlist is a boundary. Every call is checked twice before it runs:

  1. the tool must be one the agent was given, and
  2. on a grouped tool, the op must be one its front matter names.

A rejected call never reaches the deployment. The agent is told which operations it may use, so it can pick a legal one or hand the task to an agent that has it, and a warning naming the agent, tool and operation goes to the component log so the allowlist can be widened deliberately.

Note

Enforcement matters because naming one operation does not narrow what the model is shown, it can still see the whole tool's schema. Dispatch check prevents the agent from executing illegal behavior.

Diagnostics

Each agent's list is resolved at startup against the operations the deployment actually exposes, and anything that does not line up is logged:

Condition Effect
An entry matches no available tool Logged as N of M allowlisted tools resolve to nothing; contributes no capability
tool(op=...) names an operation that tool does not have Logged with the valid operation list
An entry is malformed e.g: web_console(panels) Reported as unresolved, and grants nothing

Warning

A malformed entry deliberately grants nothing rather than falling back to the bare tool name. Otherwise a typo such as web_console(panels), missing its op=, would hand over every operation of web_console instead of the single one intended.

Switching Models and Providers

The chat's model picker lists every model from every registered provider's ami.web.ai.<id>.model list. Switching model (or provider) mid-conversation is supported; the conversation history carries over.

Observability

All chatbot activity is written to AmiAi.log, one JSON object per line, so a turn can be reconstructed after the fact:

1
2
3
4
{"ts":"2026-05-04T14:23:11.812Z","sessionId":"demo","chatId":"C-2","event":"prompt_received","payload":{"model":"claude-opus-4-8","providerId":"anthropic"}}
{"ts":"...","sessionId":"demo","chatId":"C-2","event":"tool_call","payload":{"tool":"write_amisql","iteration":0}}
{"ts":"...","sessionId":"demo","chatId":"C-2","event":"tool_result","payload":{"tool":"write_amisql","ok":false}}
{"ts":"...","sessionId":"demo","chatId":"C-2","event":"turn_complete","payload":{"stopReason":"END_TURN","iterations":3}}

Every line carries ts and, where applicable, sessionId, chatId, event, and a payload. To diagnose a conversation, filter by chatId, find the tool_result with "ok":false, and read the preceding tool_call to see what the agent attempted.