OpenBI Docs Buy

Example: Customer Service

Customer Service answers e-mail with your data and an agent beside you. It is Norway Analytics’ reference plug-in: everything it does goes through the OpenBI SDK, so it shows how a real plug-in is built, from its agent tools to its privacy and its tests.

What it does

Customer Service works a mailbox inside OpenBI Studio: a shared one (support@) or your own, on Microsoft 365, Outlook.com, Gmail, iCloud, Yahoo or any IMAP server. An agent reads each message, looks the customer up in the open model, notes what it found and drafts the reply. You read the draft, change it, and press Send: the agent has no tool to send mail.

It is free, a zip of its own from www.openbi.no/sdk/, for OpenBI Studio on Windows: its tab is a WPF view, so on a Mac its agent tools work but its tab cannot show.

Its setup has four steps:

  1. What it is for: Customer service, Power BI administration, OpenBI administration or My own email. It decides the words it uses and what the agent looks for first.
  2. Your mailbox: Microsoft’s sign-in for Microsoft 365 and Outlook.com, an app password for Gmail, iCloud and Yahoo, or any IMAP and SMTP server. The password stays on this computer, encrypted for your Windows account.
  3. Who drafts the replies: OpenBI’s built-in agent, with an Anthropic API key of yours, or My own agent, the agent you already connect to the studio.
  4. Privacy: Personal - no anonymization, Company - anonymize personal data, or Company - with Microsoft Presidio.

How it is built

Two projects and a manifest:

OpenBI.CustomerService.Core (net10.0)
Its logic, free of WPF: the mailboxes (Microsoft Graph, and IMAP and SMTP with MailKit), the queue and the work in progress, the agent tools, the built-in agent, and privacy. It builds and tests on any system.
OpenBI.CustomerService (net10.0-windows, WPF, EnableWindowsTargeting)
Its views, the setup window and the entry class, on top of the core. It builds on any system and runs in the Windows studio. It references the SDK without copying it, and imports the SDK’s build targets, so its assembly carries the contract it was built against.
plugin.json
Its id openbi.customer-service, its assembly and entry class, minStudio 0.3.16, and a license block that requires no key. Customer Service 1.1.2, built with OpenBI Studio 0.3.74 against contract 1.2, says minStudio 0.3.73 in its zip’s plugin.json: the studio that contract came with (see minStudio).

The entry class is the whole contract on the plug-in’s side:

public sealed class CustomerServicePlugin : IStudioPlugin
{
    public const string Id = "openbi.customer-service";

    /// <summary>The studio (set when the plug-in is turned on).</summary>
    internal static IStudioHost Host { get; private set; } = null!;

    public void Initialize(IStudioHost host) => Host = host;

    public IReadOnlyList<PluginView> Views =>
    [
        new("customer-service", "Customer Service", "", "Answer email with your data and an agent beside you: a shared mailbox or your own",
            () => new CustomerServiceView(Host)),
    ];

    public IReadOnlyList<Type> AgentTools => [typeof(MailTools)];

    public string? AgentInstructions => MailTools.Instructions;

    public void Shutdown()
    {
        if (CsSession.Current is { } s) _ = s.DisposeAsync().AsTask();
    }
}

Its agent tools

One class, MailTools, seven tools. None sends mail: a test makes sure of it.

mail_queue Read only
The queue as the operator sees it, newest first: id, sender, subject, preview, whether answered, state.
mail_read Read only
A message with its whole conversation, the draft in the reply box, and the operator’s guidance for the reply.
mail_lookup Read only
An OAX query on the open model, for the facts: tokens in the query get their real values before it runs, and personal data in the answer comes back as tokens.
mail_note
A note beside the message, in the operator’s Context pane: what the agent looked up and found, or a fix it proposes, with the OAX it ran.
mail_draft_reply
The agent’s draft, in the operator’s reply box.
mail_mark
A message marked done, or open again, in the mailbox itself, so everyone working it sees the same.
mail_next_request Read only
Waits for the operator’s next request, sent with Send to the agent: the message and their instruction.

Two agents, one set of tools

  • Your own agent connects to the studio’s endpoint, where the plug-in’s tools join the studio’s. It waits in mail_next_request; each time you press Send to the agent, it gets the message and your instruction, works it, and puts its draft in your reply box.
  • OpenBI’s built-in agent is Claude, through Anthropic’s API with your key, which Anthropic bills. It is given the same tool classes, described from the same attributes, except mail_next_request: you ask it directly in the tab.

Building the built-in agent’s tool list from the same [McpServerTool] attributes keeps one set of tools for both. See Privacy for what the built-in agent sends.

Privacy in practice

With privacy on, Customer Service asks the studio for a pseudonymizer (IStudioHost.NewPseudonymizer()) and passes everything it gives an agent through it: the queue, the conversation, your guidance, the answers of mail_lookup. Names, e-mail addresses, phone numbers, Norwegian national identity numbers, account, IBAN and card numbers and IP addresses become tokens, the same value always the same token. What comes back, a draft or a query, gets the real values again before it is used.

  • The people in a mail’s headers (From, To, Cc, Reply-To) are learned before anything of the mail is shown, so a sender’s name is a token everywhere, in the model’s rows too.
  • The mailbox’s own address is shown as it is: it is the side the agent writes for, not a person written about.
  • For the data in the model itself, anonymize it as it loads with M’s Privacy.Anonymize, so it never enters the model (see Privacy).

Its tests

Customer Service’s logic is tested without the studio and without Windows: a FakeStudioHost answers for the studio, and a FakeMailbox in memory for the mail server. One of them:

[Fact]
public async Task With_privacy_on_the_agent_sees_tokens_and_its_draft_gets_the_real_values()
{
    var host = new FakeStudioHost();
    var session = new CsSession(host, host.Storage("cs"), new CsConfig { Agent = "own" });
    await session.UseAsync(new FakeMailbox(Message("m1", "Kari Nordmann", "kari@example.no", "Faktura",
        "Hei, mitt fødselsnummer er 15076500565 og fakturaen er feil.\n\nHilsen Kari Nordmann")));
    var tools = new MailTools(host);
    var queue = await tools.MailQueue();
    Assert.DoesNotContain("kari@example.no", queue);
    Assert.Contains("<EMAIL_1>", queue);
    var read = await tools.MailRead("m1");
    Assert.DoesNotContain("15076500565", read);
    Assert.DoesNotContain("Kari Nordmann", read);
    Assert.Contains("<NO_ID_1>", read);
    // ...
}

The same pattern, for your own plug-in: Testing.

Last updated · OpenBI Studio 0.3.74