NativeHelpy

Documentation

Integrating a NativeHelpy assistant

A NativeHelpy assistant answers your users from your own content (a website, a help centre, documentation), in their language, with a link to where each answer came from. This page explains how we build one for you, what to prepare, and how to put it into your website or your app.

Overview

The assistant is your content plus NativeSoft's pre-trained model. Adding your content does not retrain the model: we read your pages, split them into sections that keep their links, and index them. A question is matched against that index and answered from the closest sections.

You receive one assistant, built and tested on your content, and you choose where it runs:

WhereWhat you receiveTypical use
Hosted by NativeSoftAn address for your assistant, plus a chat you add to your pages with one script tag (or the HTTP API for your own interface). Nothing to installWebsites and help centres that want nothing to run
On your serverThe assistant server, your sealed assistant file, its library and your licence file, plus the same chat and APIWebsites, help centres, web apps, internal tools that must run on your own machines
On the deviceThe engine as a Dart package and your assistant as a sealed model file, ready to ship inside the appMobile and desktop apps; works offline, needs no permissions

All three run on an ordinary CPU, with no GPU and no outside AI service. Questions and answers stay on the machine that runs the assistant. On your machines, the assistant is delivered sealed: it runs, but it cannot be copied out and used elsewhere.

How a project runs

  1. You send your content: the address of your help pages, or the documents themselves (see Preparing your content), and where the assistant should appear.
  2. We build the assistant: we read and index the content, set how it answers and what it may talk about, and run your test questions against it. You get the results and can try it before delivery.
  3. We deliver: we host it for you, or we send the server or the app package with your sealed assistant file and the licence.
  4. You integrate it as described below, in your own interface and look.
  5. When your content changes, the index is rebuilt from it. No retraining (see Keeping it current).

Preparing your content

The assistant knows only what is in the content you give it, and it answers with your own sentences. The better your content is organised, the better the answers.

What we can read

  • A part of a website: a start address and a path prefix (for example https://example.com/help/). We follow the links under that prefix and its sitemap.xml, respect robots.txt, and do not leave the address you give.
  • A sitemap: a sitemap.xml listing exactly the pages to use.
  • A folder of documents: HTML, Markdown or plain-text files. Give each file the public address where users can read it, so answers can link to it.

Pages behind a login, pages built only by scripts with no text in the HTML, and scanned images of text cannot be read. Send those as documents instead.

Content that answers well

  • One topic per section, under a clear heading. Each heading (<h2>, <h3>, or ## in Markdown) starts a section, and the answer links to it. "How to reset your password" works better than "Account, part 2".
  • Say the answer early. Put the answer in the first sentences of the section, then the details.
  • Use your users' words. If people say "refund", write "refund" next to your internal term.
  • Keep examples as code. Commands, queries and configuration in <pre> blocks or Markdown code fences can be shown in an answer, ready to copy.
  • Leave out what goes stale. Version numbers and release notes can be left out of the answers on request, so the assistant never quotes an old version.
  • Mark what it should not use. Tell us which pages or sections to leave out: legal boilerplate, internal pages, duplicates.

Languages

The model reads and answers in 23 languages: English, Romanian, German, Spanish, French, Italian, Portuguese, Dutch, Polish, Czech, Hungarian, Danish, Swedish, Greek, Turkish, Russian, Ukrainian, Arabic, Hebrew, Persian, Hindi, Japanese and Korean. Each page should declare its language (<html lang="...">). For documents, tell us the language. If your content exists in several languages, send all of them: the assistant answers in the language of the question, from the content in that language.

How it talks

Send us, for each language you use:

  • the assistant's name and your product or company name;
  • a greeting and the sentence it says when a question is outside your content;
  • which topics beyond your content it may talk about (jokes, recipes, weather, sport, general knowledge ...), or none. Greetings, thanks and who made it are always answered.

You also choose how it answers: only with sentences from your content (it never invents anything, and it is the fastest), or in its own words, checked against your content.

Actions in your app

The assistant can also understand what a user wants to do in your app, and say so to your code. The live bank demo works this way: "I need last month's statement" opens Statements for that month, and "Send Ana $250" fills in a transfer. Your app always does the work, with its own checks, and asks the user to confirm anything that changes data.

For each action, send:

FieldWhat it isExample
keyThe name your code receives, the same in every languagestatement
titleA short titleAccount statements
textWhat the assistant says, at most three sentencesI can prepare the statement of any month ...
examplesSeveral ways users ask for it, in each languageI need last month's statement; download a bank statement
{
  "statement": {
    "title": "Account statements",
    "text": "I can prepare the statement of any month in your history. I open Statements and select the period.",
    "examples": ["I need last month's statement", "download a bank statement", "monthly report"]
  }
}

Details inside a request (a name, an amount, a month) are read by your app from the question, with your own data. The reply tells you which action was meant; see The reply.

Test questions

Send real questions your users ask, with the answer you expect: the page, section or action that should answer it. We run them before every delivery and after every update, so you can see that the assistant answers what it should. One question per line:

{"q": "How do I reset my password?", "lang": "en", "expect": "https://example.com/help/account#reset-password"}
{"q": "I need last month's statement", "lang": "en", "expect": "statement"}
{"q": "What's the weather tomorrow?", "lang": "en", "expect": "refusal"}

A spreadsheet with the same three columns is fine too. Include questions it should refuse.

On a website

On a website, your pages reach the assistant under a path such as /assistant-api/. The assistant runs either on our servers or on yours; the chat and the API are the same.

Hosted by NativeSoft

We run your assistant on our servers and give you its address. You add the chat to your pages, or call the API from your own interface, and we keep it running and current. The model never leaves our machines. Pass it through your own web server, so your pages call their own domain:

location /assistant-api/ {
    proxy_pass https://<your assistant's address>/;
}

On your server

You receive the assistant server, your assistant as a sealed file (.ainx), the library that opens it (libainx.so, made for you only) and your licence file (license.txt), plus libainative_kernels.so (the 8-bit maths that make it fast). Keep them in one folder. It needs:

  • Linux on x86-64 (a CPU with AVX2). No GPU, no database, no outside service.
  • About 100 to 160 MB of memory for a worker with a search assistant like this site's; an answer usually takes a few milliseconds (see Size, memory and speed).
  • It listens on 127.0.0.1 only. Put your web server in front of it.
ainative-serve --bundle assistant.ainx --port 3024 --workers 2

The server opens the sealed file in memory, with the library and the licence next to it. Without a valid licence it does not start, and it says why: no licence, a changed one, one that has ended, or one made for another machine. A licence can name an end date and the machine it runs on; a renewed licence is a new file, the model stays the same.

OptionDefaultWhat it does
--port3008The port to listen on
--host127.0.0.1The address to listen on
--workers4Questions answered at the same time, each with its own copy of the model
--queue16Questions that may wait; beyond that the answer is 503
--cooldown-ms500The shortest time between two questions of the same conversation; sooner is 429
--max-chars300Questions are cut at this length
--licenselicense.txt next to the serverYour licence file (or the AINX_LICENSE environment variable)
--seal-liblibainx.so next to the serverThe library that opens your sealed file

Behind Caddy:

example.com {
    handle_path /assistant-api/* {
        reverse_proxy 127.0.0.1:3024
    }
    # ... your site
}

Behind nginx:

location /assistant-api/ {
    proxy_pass http://127.0.0.1:3024/;
}

Run it under your process manager (systemd, pm2 ...) so it starts with the machine. It is ready a few seconds after it starts: curl 127.0.0.1:3024/health.

The chat on your pages

The ready-made chat is one script and one stylesheet. It adds a button at the bottom of the page and a panel with the conversation. A link in an answer scrolls to the paragraph it came from and highlights it. The button appears only when the assistant answers /health, so your pages never wait for it and work without it.

<link rel="stylesheet" href="/assistant-chat.css">
<script src="/assistant-chat.js" defer
        data-name="Example Assistant"
        data-title="Ask about Example"
        data-greeting="Hi! Ask me anything about Example."
        data-placeholder="How do I start?"></script>
AttributeWhat it does
data-apiWhere the assistant is (default /assistant-api)
data-langThe page's language (default: <html lang>)
data-name, data-title, data-greeting, data-placeholder, data-buttonThe texts of the chat
data-opentrue: open the panel on the first visit
data-baseYour site's address, taken off the links so they stay on the current host (useful on staging)
data-unavailableWhat it says when the assistant cannot be reached
data-label-*The labels of the panel in another language: question, clear, restart, close, more, also, wait

The conversation is kept in the browser tab (sessionStorage), so follow-up questions ("and on Android?") work across pages. Nothing is tracked.

HTTP API

For your own interface, call the server directly. All requests and replies are JSON.

GET /health: whether the assistant is ready.

{"ok": true, "name": "Example Assistant", "langs": ["en", "ro"], "busy": 0, "queued": 0}

POST /ask: a question.

{
  "q": "How do I reset my password?",
  "session": "b7f3c2e1",          // one id per conversation, made by your page
  "page_lang": "en",              // the page's language: used when the question is too short to tell
  "links": "show",                // none | show | open
  "session_state": { ... }        // optional: from the previous reply, for follow-ups
}

The reply is described in The reply. Pass its session_state back with the next question of the same conversation. The server also remembers it for a while by session, but sending it back keeps follow-ups working across restarts and servers.

StatusMeaningWhat to do
200The answerShow it
400No question (empty after cleaning)Ask the user to type a question
429The same conversation asked again too soon (retry_ms says how long to wait)Wait, then send it again
503All workers busy and the queue fullWait a little, then send it again

Questions are plain text: control and invisible characters are removed, and anything over --max-chars is cut. With "stream": true, an assistant that writes its answers in its own words sends them as they are written, one JSON object per line ({"partial": "..."}), and the full reply last, with "final": true.

In a mobile or desktop app

In an app, the assistant runs on the user's device: the engine is a Dart package (ainative_core, no plugins), and your assistant is a sealed model file inside the app. It answers without a network and needs no permissions. The model is loaded once, in the background, when the app starts; see Size, memory and speed for what it takes. It is in production in Flutter apps on Android.

You receive, with the licence:

  • the ainative_core package;
  • your assistant as a sealed model file (.ainx);
  • libainx.so, the library that opens it, in memory only, built for your app's id: for Android (arm64-v8a, armeabi-v7a, x86_64) and for Linux desktop. It opens the model only inside your app: copied into another app, it refuses.

Put the libraries where the platform loads them, and add the package and the model to pubspec.yaml:

android/app/src/main/jniLibs/arm64-v8a/libainx.so
android/app/src/main/jniLibs/armeabi-v7a/libainx.so
android/app/src/main/jniLibs/x86_64/libainx.so
assets/assistant/your_assistant.ainx
dependencies:
  ainative_core:
    path: third_party/ainative_core   # as delivered

flutter:
  assets:
    - assets/assistant/

Load the model in its own isolate, so your screens never wait for it, and keep one session per conversation:

import 'dart:isolate';
import 'dart:typed_data';
import 'package:ainative_core/ainative_core.dart';

void assistantMain(List args) {
  final ready = args[0] as SendPort;
  // opened by libainx.so, in this isolate's memory only
  final ai = AinbBundle.parse(openSealedModel(args[1] as Uint8List)).assistant();
  final sessions = <String, AssistantSession>{};
  final inbox = ReceivePort();
  ready.send(inbox.sendPort);
  inbox.listen((m) {
    final [q, sid, out] = m as List;
    final r = ai.ask(cleanQuestion(q as String),
        session: sessions.putIfAbsent(sid as String, AssistantSession.new),
        host: const HostCapabilities({'open_url', 'open_menu'}),
        links: LinkMode.show);
    (out as SendPort).send({
      'text': r.text,
      'answered': r.answered,
      'lang': r.lang,
      if (r.link != null) 'link': {'title': r.link!.title, 'url': r.link!.url, 'route': r.link!.route},
    });
  });
}

If the model does not open, openSealedModel throws a SealedModelException with the reason. Start the isolate when the app starts, with the model's bytes from rootBundle.load('assets/assistant/your_assistant.ainx'), and send it each question with the conversation's id and a SendPort for the reply. HostCapabilities tells the assistant what your app can do: open_url (open a page) and open_menu (open a screen of the app, for actions).

Release builds

The licence requires that a release of your app:

  • ships the model only as the sealed file we deliver, with our libainx.so, never unsealed;
  • is built with obfuscation, for example flutter build appbundle --release --obfuscate --split-debug-info=<folder> (keep that folder: it reads crash reports);
  • shows the NativeSoft licence notice in its About, Info or Settings screen.

Obfuscation changes the names of classes, so code that compares runtimeType.toString() stops working. enum.name is safe. Try the assistant's flows on the release build.

Size, memory and speed

Measured on 30 October 2026 with the assistant of this site, the same model as in the Helpy Bank Demo app: a search assistant in 23 languages (it finds the answer in your content and does not write new text). An assistant that also writes its answers carries a text generator, which makes the model and the memory several times larger; we measure it for your project.

WhatWhere it was measuredResult
Model fileThe assistant of nativehelpy.com, plain (.ainb) and sealed (.ainx)13.1 MB
App packageHelpy Bank Demo 1.1.0 for Google Play, the whole app with the model, for three processor types (arm64-v8a, armeabi-v7a, x86_64)65 MB
Memory in the appHelpy Bank Demo on the Android emulator (Android 14, x86_64, 2 GB of RAM), the whole app with the assistant loaded (PSS)146 MB at start, 174 MB after three requests
Memory on a serverainative-serve with one worker, Linux, Intel Core i9-14900HX100 MB at start, 160 MB after 400 questions
Answer time on a serverThe same server, 400 questions of our test set in 23 languages, one after another6 ms median, 11 ms for 95% of them; the first one 1.6 s (the model is read)

On phones

Measured on 31 October 2026 with Helpy Bank Demo as installed from Google Play, on two phones. Its built-in test sends requests one after another: 206 in the first run after the app starts, 500 in the third. In the assistant is the time to understand the request and find the answer; on screen is the time until the answer is shown in the app. Everything runs on the phone, without a network.

WhatSamsung Galaxy S25 UltraSamsung Galaxy S25 FE
PhoneAndroid 16, Qualcomm SM8750, 8 cores, 11 GB of RAMAndroid 16, Samsung Exynos 2400 (s5e9945), 10 cores, 7 GB of RAM
Sealed model opened52 ms118 ms
Ready to answer, from the app's start0.9 s1.1 s
First run (206 requests), in the assistant11 ms median, 19 ms for 95% of them, 1.3 s the slowest14 ms median, 23 ms for 95% of them, 1.7 s the slowest
First run, on screen12 ms median, 20 ms for 95%14 ms median, 24 ms for 95%
Third run (500 requests), in the assistant12 ms median, 22 ms for 95% of them, 59 ms the slowest15 ms median, 29 ms for 95% of them, 77 ms the slowest
Third run, on screen12 ms median, 23 ms for 95%16 ms median, 30 ms for 95%
Memory of the whole app (PSS)349 MB after the first run, 332 MB after the third459 MB after the first run, 370 MB after the third

The usual answer takes the same time from the first question on. Only a few of the first answers after the app starts are slower, up to about a second and a half, while the app warms up; from then on even the slowest answer stays under a tenth of a second. Speed depends on the phone's processor: ask us for the figures on the devices you target, or try Helpy Bank Demo on yours.

The reply

The same fields come from the HTTP API and from ai.ask() in the app:

FieldWhat it is
textThe answer to show
answeredfalse when it declined: the question is outside your content
langThe language of the answer
sourceextractive (sentences from your content), generated (its own words, checked against your content), refusal, or system (greetings, thanks, small talk)
linkWhere the answer came from: title, url (a page, with the section's anchor) and/or route (a screen of your app, menu:<key> for an action)
more, more_textUp to two related sections, and the sentence to introduce them
unsuretrue when it is not sure it understood and shows what comes closest
exampleA code example from your content: code and language
talkThe kind of small talk, when it is one (greeting, thanks ...)
session_stateHTTP only: send it back with the next question

For an action, read the key from link.route: menu:statement means "open Statements". Your app then fills in the details and asks the user to confirm.

{
  "text": "I can prepare the statement of any month in your history ...",
  "answered": true,
  "lang": "en",
  "source": "extractive",
  "link": {"title": "Account statements", "url": null, "route": "menu:statement"}
}

How the model is protected

When the assistant runs on your machines or your users' devices, it is delivered sealed:

  • The assistant file is encrypted and authenticated: changing one byte, or the NativeSoft notice at its start, makes it refuse to open.
  • It is opened only in memory, by a library made for you alone. You never receive a key, and the library of another licence does not open your file.
  • In an app, the library opens it only inside the app it was made for. On a server, only with your signed licence file.

No protection can make a model that runs on someone's machine impossible to study. Sealing makes copying it slow and costly; the licence makes it unlawful. If you need the model to stay entirely with us, choose hosting by NativeSoft.

Keeping it current

  • Hosted by NativeSoft, your content is read again on a schedule (every night or every week) or when you ask, and the index is rebuilt. If no section changed, nothing is rebuilt. A scan that finds much less than before is not used: the previous version stays, and we look at it.
  • On your server, we send you a new sealed file when your content changes. Replace the old one and restart the server; the library and the licence stay.
  • In an app, a content update is a new sealed file in your next release.
  • A new version of the model means rebuilding the index from your content. We tell you before, and you test it before it replaces the old one.

Licence

A licence covers one assistant for one product: its website or its app, hosted by us or run by you. The model, the engine, the file format and the index are NativeSoft's. Your content stays yours.

On your server, the licence is also a file the server reads at start. It can name an end date and the machine; renewing it means a new file, not a new model.

The licence does not allow:

  • extracting, unsealing, decompiling or reverse engineering the model, the engine or the library that opens the model;
  • using the model, or its answers in bulk, to build or train another product or model;
  • removing the NativeSoft notice from the model file or from the app;
  • using the assistant for another product than the licensed one.

Terms, prices and what fits your product: write to us.