SmoothLingua

A .NET library for building conversational agents powered by intent recognition, story-based dialogue management, and rule-based responses.

SmoothLingua is designed for teams that want a lightweight, code-first alternative to heavy conversational AI frameworks — you define your agent entirely in C# and run it anywhere .NET runs, with no external services required.

build and test   NuGet


Installation

Install SmoothLingua via the .NET CLI:

dotnet add package SmoothLingua

Or via the NuGet Package Manager Console:

Install-Package SmoothLingua

Quick Start

Define a domain, train it, load the agent, and start handling messages:

using SmoothLingua;
using SmoothLingua.Abstractions;
using SmoothLingua.Abstractions.Stories;

// 1. Define and train the domain in memory
MemoryStream memoryStream = new();
Trainer trainer = new();

await trainer.Train(new Domain(
    [
        new("Greeting", ["Hello", "Hi"]),
        new("Good",     ["I am fine", "I am good, thank you"]),
        new("Bad",      ["I am feeling bad", "I am not good"]),
        new("Bye",      ["Good bye", "Bye"]),
        new("IAmFrom",  ["I am from USA", "I am from Bulgaria", "I am from Germany"])
    ],
    [
        new("Good",
        [
            new IntentStep("Greeting"),
            new ResponseStep("Hello! How are you?"),
            new IntentStep("Good"),
            new ResponseStep("Glad to hear that! Where are you from?"),
            new IntentStep("IAmFrom"),
            new ResponseStep("It is nice in {country}!")
        ]),
        new("Bad",
        [
            new IntentStep("Greeting"),
            new ResponseStep("Hello! How are you?"),
            new IntentStep("Bad"),
            new ResponseStep("I am sorry to hear that.")
        ])
    ],
    [new("Bye", "Bye", "Bye")],
    [new Slot("country", "IAmFrom", "country", "")],
    [new Entity("country", new HashSet<string> { "Bulgaria", "USA", "Germany" })]
), memoryStream, default);

// 2. Load the trained agent and start a conversation
var agent = await AgentLoader.Load(new MemoryStream(memoryStream.GetBuffer()));
var conversationId = Guid.NewGuid().ToString();

var r = agent.Handle(conversationId, "bye");
// r.IntentName == "Bye", r.Messages == ["Bye"]

r = agent.Handle(conversationId, "hello");
// r.IntentName == "Greeting", r.Messages == ["Hello! How are you?"]

r = agent.Handle(conversationId, "I am fine");
// r.IntentName == "Good", r.Messages == ["Glad to hear that! Where are you from?"]

r = agent.Handle(conversationId, "I am from Bulgaria");
// r.IntentName == "IAmFrom", r.Messages == ["It is nice in Bulgaria!"]

Load a model from file

Train once and persist to disk — no retraining needed on subsequent starts:

// Train and save
await trainer.Train(domain, "model.zip", default);

// Load on every restart
var agent = await AgentLoader.Load("model.zip");

Concepts

Intent

A named category of user utterances. You supply example phrases and the ML trainer learns to recognise them. An Intent named Greeting with examples ["Hello", "Hi"] will match any similar greeting at runtime.

Story

A multi-turn conversation flow defined as an ordered list of IntentStep and ResponseStep entries. Stories model sequences: "after the user greets, reply; if they say they're fine, reply differently than if they say they're sad."

Rule

An always-active, single-turn shortcut. A rule fires whenever its intent is predicted, regardless of conversation context. Use rules for commands like Bye that should always produce the same response.

Domain

The bundle that groups all intents, stories, rules, slots, and entities into one trainable unit. Pass a Domain to Trainer.Train, which serialises it (together with the trained ML model) into a zip archive you can store anywhere.

Agent

The runtime object that handles user input. Obtain one via AgentLoader.Load. It predicts the intent, advances the conversation state, and returns a Response containing the matched intent name and the bot's reply messages.


Behavioural insights

Find out where the agent is unsure — without retaining personal data.

SmoothLingua ships with an opt-in IAnalyticsRecorder that captures anonymous per-turn signals (predicted intent, confidence, fallback flag) and aggregates them on demand. Conversation identifiers and message text never leave the recorder.

using SmoothLingua.Analytics;

var recorder = new InMemoryAnalyticsRecorder();
var agent = await AgentLoader.Load("model.zip", analyticsRecorder: recorder);

agent.Handle("conv-1", "hi");
agent.Handle("conv-1", "what's the weather on Jupiter?");

var snapshot = recorder.GetSnapshot();
// snapshot.TotalMessages, snapshot.FallbackRate, snapshot.AverageConfidence, snapshot.Intents

Reading the signals

  • High fallback rate — many user messages land below the confidence threshold. Either lower the threshold or add more examples to the intents users are reaching for.
  • Popular intent with low average confidence — the model picks it, but only barely. Add more (and more varied) examples so the score firms up.
  • Short average conversation length combined with a high fallback rate — usually points at a missing intent.

The SmoothLingua.Server host registers an InMemoryAnalyticsRecorder automatically and exposes the snapshot at GET /insights. The interactive demo at chat.smooth-lingua.com renders the same data live.


When SmoothLingua, when not

Good fit

  • .NET applications that need embedded conversational logic with no external services
  • Prototypes and internal tools where you want full control over training data and model lifecycle
  • Chatbots with a finite, well-defined intent set (up to ~50 intents)
  • Offline or air-gapped environments

Not a good fit

  • Large-scale open-domain conversation — use a hosted LLM instead
  • Use cases that require deep semantic understanding beyond the ML.NET SDCA classifier
  • Teams that need a visual or no-code dialogue editor

Changelog

See what's new added, changed, fixed, improved or updated in the latest versions.

Version 2.2.0 (10 June, 2026)

  • Added IAnalyticsRecorder abstraction in the core package — captures anonymous per-turn signals (intent, confidence, fallback flag) and aggregates them on demand. InMemoryAnalyticsRecorder ships as the default; NullAnalyticsRecorder stays in place when analytics are not requested.
  • Added GET /insights on SmoothLingua.Server — returns aggregated counts (fallback rate, average confidence, per-intent stats) without exposing conversation IDs or message text.
  • Added POST /playground/predict — trains a tiny in-memory classifier from visitor-supplied intents and predicts an intent for a piece of text. Cached per intent set; intent and example counts are hard-capped.
  • Added CORS support on SmoothLingua.Server via SmoothLingua:Cors:AllowedOrigins; the chat subdomain and the docs site are allowed by default.
  • Added Interactive demo at chat.smooth-lingua.com — chat with the pre-trained agent, a Playground tab for your own intents, and an Insights tab that shows the live aggregates.
  • Updated AgentLoader.Load and the Agent constructor accept an optional IAnalyticsRecorder — fully backward compatible.

Version 2.1.0 (9 June, 2026)

  • Added IConversationStore abstraction in the core package — no ASP.NET dependency. Exposes Get, Save, and Reset; works identically in a console app, a desktop tool, or a web service.
  • Added InMemoryConversationStore — default implementation, existing behaviour preserved exactly.
  • Added FileConversationStore — durable implementation that serialises each conversation as a JSON file. State survives process restarts with no external dependencies.
  • Added SmoothLingua.Server — minimal ASP.NET Core Web API: POST /conversations/{id}/messages, POST /conversations/{id}/reset, GET /health, and an AddSmoothLingua DI extension.
  • Added Dockerfile for SmoothLingua.Server — multi-stage build, /app/data volume for model and store.
  • Updated AgentLoader.Load and ConversationManager accept an optional IConversationStore — fully backward compatible.

Version 2.0.0 (8 June, 2026)

  • Added Confidence score on every prediction (softmax-normalised, range 0–1)
  • Added Fallback intent when confidence falls below a configurable threshold (default 0.4)
  • Added Lookup-based entity extraction — matched values returned in Response.ExtractedEntities
  • Added Domain.ConfidenceThreshold and Domain.FallbackIntentName configuration fields
  • Breaking Response gains a required Confidence field — see migration notes in the GitHub release
  • Breaking IPredictor.Predict now returns (string IntentName, float Confidence)

Version 1.2.0 (2026)

  • Added Rewritten README with Concepts, Quick Start, and When/Not guidance
  • Added XML documentation on all public types — improves IntelliSense for consumers
  • Added File-based model loading example in QuickStart
  • Added Automated website deploy workflow
  • Updated Website content aligned with library documentation

Version 1.1.0 (6 June, 2026)

  • Added Automated release pipeline — push a v* tag to build, test, pack, and publish to NuGet automatically
  • Added GitHub Releases auto-generated on each tag push
  • Added CHANGELOG.md and CONTRIBUTING.md
  • Added GitHub issue and pull request templates
  • Added Code coverage via Coverlet
  • Updated CI split into build-and-test and release workflows

Version 1.0.6 (1 January, 2024)

Initial public release — intent recognition, story management, rule-based responses, Trainer, Agent, AgentLoader, NuGet package.