Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Assistant

Explanation

How the intent assistant works

How the built-in intent assistant tokenises a request, scores it against the English and Turkish intent corpora, filters by permission, and answers through the chat route.

This page explains the intent assistant of Cluster ERP. The engine in src/cluster_erp/ai/ (intent_engine.py, tokenizer.py, vectorizer.py, and the corpora intent_corpus_en.json and intent_corpus_tr.json) matches a typed request to an API intent. The chat route src/cluster_erp/server/routes/chat.py wraps the engine for the web app: it handles small talk, checks permissions, calls the matched endpoint, and returns replies. The screens are described on assistant and chat screens and desktop chat widget.

In short

  • The engine is a local text classifier: tokenisation with Turkish stemming, word and character features, TF-IDF vectors, and cosine similarity against example sentences (src/cluster_erp/ai/tokenizer.py:400-438, src/cluster_erp/ai/vectorizer.py:7-52).
  • An intent names a module, an HTTP method, an API path, and a description (src/cluster_erp/ai/intent_engine.py:14-24).
  • The route offers only intents whose endpoint the caller holds the permission for, within the modules the client declares installed (src/cluster_erp/server/routes/chat.py:1158-1177, src/cluster_erp/server/routes/chat.py:1253-1265).
  • Reads run the matched list endpoint in-process with the caller's own token; create intents return a task card; update and delete intents point to the record instead of acting (src/cluster_erp/server/routes/chat.py:1055-1066, src/cluster_erp/server/routes/chat.py:1436-1466).

How it works

Request path

flowchart TD
 A["POST /api/v1/chat/message: text, action_context, allowed_modules"] --> P["require_permission dashboard.read"]
 P --> AC{"action_context set"}
 AC -->|yes| HA["_handle_action follow-up"]
 AC -->|no| ST{"small talk"}
 ST -->|yes| SR["fixed greeting or help reply"]
 ST -->|no| SP["_try_split_intents"]
 SP --> RE["IntentEngine.recognize top 5"]
 RE --> F["_intent_allowed: module scope and permission code"]
 F -->|none left| FB["_scope_fallback"]
 F --> C{"confidence below 0.60"}
 C -->|yes| SUG["did you mean, top 3"]
 C -->|no| K{"intent kind"}
 K -->|list or get| G["_api_get in-process with caller token"]
 K -->|create| T["task card from metadata"]
 K -->|update or delete| N["navigate to the record"]

In prose:

  1. The route needs dashboard.read and loads the caller's permission codes (src/cluster_erp/server/routes/chat.py:1191-1200).
  2. The language is Turkish when the text has Turkish letters or common Turkish command words, otherwise English (src/cluster_erp/server/routes/chat.py:999-1040).
  3. A follow-up from an action button goes to _handle_action. Greetings and help requests get a fixed reply (src/cluster_erp/server/routes/chat.py:1209-1218, src/cluster_erp/server/routes/chat.py:537-647).
  4. A text with several requests is split and each part is processed in turn (src/cluster_erp/server/routes/chat.py:1223-1239, src/cluster_erp/server/routes/chat.py:664-717).
  5. IntentEngine.recognize returns up to five candidates. The engine is created and loaded once per process (src/cluster_erp/server/routes/chat.py:24-35, src/cluster_erp/server/routes/chat.py:1253).
  6. Candidates whose endpoint the caller may not reach are removed before anything is shown. With no candidate left, the reply is the same as for an unknown request (src/cluster_erp/server/routes/chat.py:1255-1265, src/cluster_erp/server/routes/chat.py:1179-1185).
  7. Below a confidence of 0.60 the route lists the top three as suggestions (src/cluster_erp/server/routes/chat.py:1270-1279).
  8. For a list or get intent the route builds filters, temporal order, and a search term from the text, calls the endpoint in-process through an ASGI transport with the caller's Authorization header, and formats the rows with the list columns from metadata (src/cluster_erp/server/routes/chat.py:1282-1433, src/cluster_erp/server/routes/chat.py:1055-1066).
  9. For a create intent it builds a task card with fields from the entity's form metadata; for update and delete intents it asks the user to open the record (src/cluster_erp/server/routes/chat.py:1436-1466, src/cluster_erp/server/routes/chat.py:1552).

Matching

IntentEngine.load reads each corpus, extracts features from every example, fits one TF-IDF vectorizer per language, and stores the example vectors, a description vector, and the normalised parts of the intent id (src/cluster_erp/ai/intent_engine.py:364-418).

Step What happens Source
Tokenise lower-case (Turkish-aware), fold accents to ASCII, strip punctuation, drop stop words and one-letter tokens; Turkish tokens are stemmed by suffix src/cluster_erp/ai/tokenizer.py:250-258, src/cluster_erp/ai/tokenizer.py:391-422
Features unigrams, bigrams, and character trigrams of tokens of four letters or more src/cluster_erp/ai/tokenizer.py:424-438
Vectors term frequency times smoothed inverse document frequency; unseen terms get the highest weight src/cluster_erp/ai/vectorizer.py:17-38
Similarity cosine similarity to the best example and to the description src/cluster_erp/ai/vectorizer.py:40-52, src/cluster_erp/ai/intent_engine.py:521-528
Id overlap action and entity words, also mapped from Turkish and corrected by edit distance, compared with the parts of the intent id src/cluster_erp/ai/intent_engine.py:84-105, src/cluster_erp/ai/intent_engine.py:605-765
Score a weighted sum whose weights depend on whether an entity and a strong action word were found, plus small bonuses for list and get hints src/cluster_erp/ai/intent_engine.py:530-560
Ambiguity when the top two candidates of different modules are within 0.04 and the top is below 0.72, the top confidence is capped at 0.59 src/cluster_erp/ai/intent_engine.py:584-601

The default threshold for a candidate is 0.05 and the default number of results is 5 (src/cluster_erp/ai/intent_engine.py:420-426).

Corpora

Each corpus is a JSON file with version, description, total_intents, and intents. An intent has id, module, method, path, description, and examples (src/cluster_erp/ai/intent_corpus_en.json, src/cluster_erp/ai/intent_corpus_tr.json). Counted with a JSON load of both files: 347 intents in each language, 1553 English examples, and 1168 Turkish examples. The intents cover 12 targets: auth, crm, currencies, design_engineering, documents, owned_organisations, parties, products_and_services, projects, quotations, sales_orders, and server.

Why it is built this way

  • The route comment states that the assistant only knows what the caller may reach, so that out-of-scope intents cannot leak descriptions, suggestions, or forms (src/cluster_erp/server/routes/chat.py:1255-1258).
  • ChatRequest.allowed_modules only narrows what the assistant offers; permissions are always enforced from the caller's token (src/cluster_erp/server/routes/chat.py:1069-1077).
  • The permission code of an intent is derived from metadata in the form {entity_key}.{action}, not from a hand list (src/cluster_erp/server/routes/chat.py:1120-1156).

Rules and boundaries

  • The text is at most 2000 characters; allowed_modules at most 200 entries (src/cluster_erp/server/routes/chat.py:1069-1077).
  • An intent whose endpoint cannot be resolved in metadata is denied (src/cluster_erp/server/routes/chat.py:1158-1177).
  • The assistant does not run update or delete actions (src/cluster_erp/server/routes/chat.py:1454-1466).
  • The desktop client builds its own IntentEngine in the chat widget (src/cluster_erp/desktop/widgets/chat/chat_widget.py:47, src/cluster_erp/desktop/widgets/chat/chat_widget.py:818).

Interaction with other parts

Part Purpose Source
Metadata of all modules navigation targets, list columns, entity labels, permission codes, task card fields src/cluster_erp/server/routes/chat.py:875-998, src/cluster_erp/server/routes/chat.py:1044-1053
UserService.get_user_permission_codes the caller's permission codes src/cluster_erp/server/routes/chat.py:1200
The module APIs the actual data, behind their own permission and app checks src/cluster_erp/server/routes/chat.py:1055-1066

The chat route is one of the server routes outside modules; see shared server routes.

Limits and open points

  • The corpora cover 12 targets; intents for modules such as fleet, intranet, inventory, or analytics were not found in either corpus.
  • Replies such as greetings, help text, suggestions, and fallbacks are built as Turkish or English strings in the route (src/cluster_erp/server/routes/chat.py:537-647, src/cluster_erp/server/routes/chat.py:1179-1185, src/cluster_erp/server/routes/chat.py:1270-1276).

Discrepancies

Terms introduced

Term Code identifier Definition Turkish term Source
intent IntentMatch A recognised request mapped to a module, method, and API path, with a confidence. Niyet src/cluster_erp/ai/intent_engine.py:14-24
intent corpus intent_corpus_en.json The example sentences per intent that the engine learns from at start. Niyet derlemi src/cluster_erp/ai/intent_engine.py:365-371
task card TaskCardOut A step-by-step form in the conversation, built from metadata for a create intent, that creates one record. görev kartı src/cluster_erp/server/routes/chat.py:1099-1104

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13