Skip to content

Code Examples

Complete AL code examples for extending Smart Agents in Business Central

This page provides complete, working AL code examples for common Smart Agents extension scenarios. Each example includes the full codeunit or object definition ready to be added to your AL project.

Example 1: Custom Data Source for Project Tracking

This example registers a custom "Project" table as a data source and provides context data to agents.

Custom Table

table 50100 "My Project"
{
    Caption = 'Project';
    DataClassification = CustomerContent;

    fields
    {
        field(1; "No."; Code[20])
        {
            Caption = 'No.';
        }
        field(2; Description; Text[100])
        {
            Caption = 'Description';
        }
        field(3; "Project Manager"; Code[50])
        {
            Caption = 'Project Manager';
            TableRelation = "User Setup"."User ID";
        }
        field(4; Status; Enum "My Project Status")
        {
            Caption = 'Status';
        }
        field(5; "Budget Amount"; Decimal)
        {
            Caption = 'Budget Amount';
        }
        field(6; "Actual Amount"; Decimal)
        {
            Caption = 'Actual Amount';
        }
        field(7; "Start Date"; Date)
        {
            Caption = 'Start Date';
        }
        field(8; "End Date"; Date)
        {
            Caption = 'End Date';
        }
    }

    keys
    {
        key(PK; "No.")
        {
            Clustered = true;
        }
        key(Status; Status) { }
    }
}

enum 50100 "My Project Status"
{
    Extensible = true;

    value(0; Planning)
    {
        Caption = 'Planning';
    }
    value(1; Active)
    {
        Caption = 'Active';
    }
    value(2; "On Hold")
    {
        Caption = 'On Hold';
    }
    value(3; Completed)
    {
        Caption = 'Completed';
    }
}

Data Source Registration and Context Provider

codeunit 50100 "My Project Data Source"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"SA Data Scope Manager",
        'OnRegisterDataSources', '', false, false)]
    local procedure RegisterProjectTable(var DataSourceBuffer: Record "SA Data Source Buffer" temporary)
    begin
        DataSourceBuffer.Init();
        DataSourceBuffer."Table ID" := Database::"My Project";
        DataSourceBuffer."Table Name" := 'Project';
        DataSourceBuffer.Category := 'Project Management';
        DataSourceBuffer.Description := 'Project tracking with budget and actuals';
        DataSourceBuffer."Read Only" := true;
        DataSourceBuffer.Insert();
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"SA Agent Router",
        'OnCollectDataContext', '', false, false)]
    local procedure ProvideProjectContext(
        AgentId: Code[20];
        TableId: Integer;
        UserMessage: Text;
        var ContextJson: JsonObject;
        var Handled: Boolean)
    var
        Project: Record "My Project";
        DataArray: JsonArray;
        RowObj: JsonObject;
        SummaryObj: JsonObject;
        TotalBudget: Decimal;
        TotalActual: Decimal;
        ProjectCount: Integer;
    begin
        if TableId <> Database::"My Project" then
            exit;

        if Project.FindSet() then
            repeat
                Clear(RowObj);
                RowObj.Add('no', Project."No.");
                RowObj.Add('description', Project.Description);
                RowObj.Add('manager', Project."Project Manager");
                RowObj.Add('status', Format(Project.Status));
                RowObj.Add('budget', Project."Budget Amount");
                RowObj.Add('actual', Project."Actual Amount");
                RowObj.Add('variance', Project."Budget Amount" - Project."Actual Amount");
                RowObj.Add('startDate', Format(Project."Start Date", 0, '<Year4>-<Month,2>-<Day,2>'));
                RowObj.Add('endDate', Format(Project."End Date", 0, '<Year4>-<Month,2>-<Day,2>'));
                DataArray.Add(RowObj);

                TotalBudget += Project."Budget Amount";
                TotalActual += Project."Actual Amount";
                ProjectCount += 1;
            until Project.Next() = 0;

        SummaryObj.Add('totalProjects', ProjectCount);
        SummaryObj.Add('totalBudget', TotalBudget);
        SummaryObj.Add('totalActual', TotalActual);
        SummaryObj.Add('totalVariance', TotalBudget - TotalActual);

        ContextJson.Add('summary', SummaryObj);
        ContextJson.Add('projects', DataArray);
        Handled := true;
    end;
}

Example 2: Query Validation and Content Filtering

This example demonstrates how to validate user queries before they are processed, blocking queries that reference restricted data.

codeunit 50101 "My Query Validator"
{
    var
        RestrictedTerms: List of [Text];

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"SA Agent Router",
        'OnBeforeProcessMessage', '', false, false)]
    local procedure ValidateQuery(
        AgentId: Code[20];
        var UserMessage: Text;
        var ContextJson: JsonObject;
        var Cancel: Boolean;
        var CancelReason: Text)
    begin
        InitRestrictedTerms();

        if ContainsRestrictedContent(UserMessage) then begin
            Cancel := true;
            CancelReason := 'Your query references restricted data categories. ' +
                'Please contact your administrator for access to this information.';
            LogBlockedQuery(AgentId, UserMessage);
            exit;
        end;

        // Enforce query length limits
        if StrLen(UserMessage) > 2000 then begin
            Cancel := true;
            CancelReason := 'Query exceeds the maximum length of 2000 characters. ' +
                'Please shorten your question and try again.';
        end;
    end;

    local procedure InitRestrictedTerms()
    begin
        if RestrictedTerms.Count > 0 then
            exit;

        RestrictedTerms.Add('salary');
        RestrictedTerms.Add('payroll');
        RestrictedTerms.Add('social security');
        RestrictedTerms.Add('bank account number');
        RestrictedTerms.Add('password');
    end;

    local procedure ContainsRestrictedContent(Message: Text): Boolean
    var
        Term: Text;
        LowerMessage: Text;
    begin
        LowerMessage := LowerCase(Message);
        foreach Term in RestrictedTerms do
            if LowerMessage.Contains(Term) then
                exit(true);
        exit(false);
    end;

    local procedure LogBlockedQuery(AgentId: Code[20]; Message: Text)
    begin
        // Log the blocked query for security auditing
        // Implementation depends on your logging infrastructure
    end;
}

Example 3: Custom Credit Tracking with Dimension Posting

This example maps Smart Agents credit consumption to Business Central dimensions for cost allocation reporting.

codeunit 50102 "My Credit Cost Allocator"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"SA Credit Tracker",
        'OnCreditConsumed', '', false, false)]
    local procedure AllocateCreditCost(
        UserId: Code[50];
        AgentId: Code[20];
        CreditsConsumed: Integer;
        ModelUsed: Text)
    var
        UserSetup: Record "User Setup";
        GenJournalLine: Record "Gen. Journal Line";
        CreditCostPerUnit: Decimal;
        TotalCost: Decimal;
    begin
        // Look up the user's department for cost allocation
        if not UserSetup.Get(UserId) then
            exit;

        // Calculate the monetary cost of credits consumed
        CreditCostPerUnit := GetCreditUnitCost(ModelUsed);
        TotalCost := CreditsConsumed * CreditCostPerUnit;

        // Post to the AI services expense account with department dimension
        CreateExpenseEntry(
            UserSetup."Salespers./Purch. Code",
            GetDepartmentCode(UserId),
            TotalCost,
            StrSubstNo('Smart Agents - %1 (%2 credits)', AgentId, CreditsConsumed)
        );
    end;

    local procedure GetCreditUnitCost(ModelUsed: Text): Decimal
    var
        SASetup: Record "SA Setup";
    begin
        // Retrieve the per-credit unit cost from the Smart Agents setup.
        // Rates vary by model tier; see the Pricing page for current values.
        SASetup.Get();
        exit(SASetup.GetUnitCostForModel(ModelUsed));
    end;

    local procedure GetDepartmentCode(UserId: Code[50]): Code[20]
    var
        Employee: Record Employee;
    begin
        Employee.SetRange("User ID", UserId);
        if Employee.FindFirst() then
            exit(Employee."Global Dimension 2 Code");
        exit('');
    end;

    local procedure CreateExpenseEntry(
        SalespersonCode: Code[20];
        DepartmentCode: Code[20];
        Amount: Decimal;
        Description: Text)
    begin
        // Create a general journal line for the expense
        // This is a simplified example — your implementation should use
        // the appropriate journal template and batch for your organization
    end;
}

Example 4: Page Extension for Agent Context

This example extends the Customer Card to show a Smart Agents action that pre-fills the chat with customer context.

pageextension 50100 "My Customer Card Ext" extends "Customer Card"
{
    actions
    {
        addlast("&Customer")
        {
            action(AskSmartAgent)
            {
                ApplicationArea = All;
                Caption = 'Ask Smart Agent';
                ToolTip = 'Open Smart Agents chat with this customer as context.';
                Image = Sparkle;

                trigger OnAction()
                var
                    AgentChat: Page "SA Chat";
                    ContextJson: JsonObject;
                begin
                    ContextJson.Add('customerNo', Rec."No.");
                    ContextJson.Add('customerName', Rec.Name);
                    ContextJson.Add('balance', Rec."Balance (LCY)");
                    ContextJson.Add('creditLimit', Rec."Credit Limit (LCY)");
                    ContextJson.Add('paymentTerms', Rec."Payment Terms Code");

                    AgentChat.SetContext(ContextJson);
                    AgentChat.SetSuggestedPrompt(
                        StrSubstNo('Tell me about customer %1 (%2)', Rec."No.", Rec.Name));
                    AgentChat.Run();
                end;
            }
        }
    }
}

Example 5: Custom Agent Template Registration

This example creates a custom agent template that is automatically available in the template library.

codeunit 50103 "My Template Registration"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"SA Data Scope Manager",
        'OnRegisterTemplates', '', false, false)]
    local procedure RegisterCustomTemplates(var TemplateBuffer: Record "SA Template Buffer" temporary)
    begin
        TemplateBuffer.Init();
        TemplateBuffer.ID := 'tmpl_custom_project';
        TemplateBuffer.Name := 'Project Status Reporter';
        TemplateBuffer.Description := 'Provides project status summaries, budget vs. actual analysis, and timeline tracking.';
        TemplateBuffer.Category := 'Project Management';
        TemplateBuffer.Model := 'Smart';
        TemplateBuffer."System Prompt" :=
            'You are a project management assistant. ' +
            'Provide clear summaries of project status, budget utilization, and timelines. ' +
            'Always include budget variance (budget minus actual) in your reports. ' +
            'Flag projects where actual costs exceed budget by more than 10%.';
        TemplateBuffer."Publisher" := 'My Company';
        TemplateBuffer.Version := '1.0.0';
        TemplateBuffer.Insert();
    end;
}

Project Setup

To use any of these examples, your app.json should include the Smart Agents dependency and appropriate ID ranges:

{
  "id": "your-app-guid-here",
  "name": "My Smart Agents Extension",
  "publisher": "Your Company",
  "version": "1.0.0.0",
  "brief": "Custom extensions for Smart Agents",
  "description": "Adds custom data sources, query validation, and cost tracking to Smart Agents.",
  "privacyStatement": "",
  "EULA": "",
  "help": "",
  "url": "",
  "contextSensitiveHelpUrl": "",
  "platform": "25.0.0.0",
  "application": "25.0.0.0",
  "idRanges": [
    {
      "from": 50100,
      "to": 50199
    }
  ],
  "dependencies": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "Smart Agents",
      "publisher": "Qualia AI",
      "version": "1.0.0.0"
    }
  ],
  "runtime": "14.0",
  "target": "Cloud"
}