Thursday, 18 April 2024

Chaining Einstein Copilot Custom Actions


Image created by DALL-E 3 based in a prompt by Bob Buzzard

Introduction

I've been testing out Salesforce's Einstein Copilot assistant for a few weeks now, but typically I've been tacking a single custom action onto the end of one or more standard actions, and usually a Prompt Template where I can easily ground it with some data from a specific record. 

Something that hasn't been overly clear to me is how I can link a couple of custom actions and pass information other than record ids between them. The kind of information that would be shown to the user if it was a single action, or the kind of information that the user had to provide in a request.

Scenario

The other aspect I was interested in was some kind of DML. I've seen examples where a record gets created or fields get changed based on a request, but the beta standard actions don't have this capability, so it was clear I'd need to build that myself. So the scenario I came up with was : Given an opportunity, it's existing related activities, and a few rules (like if it's about to close someone should be in contact every day), Copilot suggests a follow up task and inserts it into the Salesforce database. 

This can't easily (or sensibly, really) be done in a single custom action. I guess I could create a custom Apex action that uses a Prompt Template to get a task recommendation from the LLM and then inserts it, but it seems a bit clunky and isn't overly reusable. What I want here are two custom actions:

  1. A Prompt Template custom action that gets the suggestion for the task from the LLM
  2. An Apex custom action that takes the details of the suggestion and uses it to create a Salesforce task.
I also don't want to have to expend a lot of effort processing the recommendation response to pick out the important details - if I'm writing that much code I might as well figure out the task that I need as well.

Implementation


The key aspect of the Prompt Template is explaining how I want the response to be formatted, so that it can be easily processed by Apex code. I was half expecting to have to use few shot prompting to get this to work, but was pleasantly surprised to find that I could just describe it in natural language with a few rules about labelling:
Generate a recommendation for a follow up task in JSON format, including the following details:
- The subject of the task with the element label 'subject'
- A brief description with the element label 'description' - this should include the names of anyone other than the user responsible for the task who should be involved
- The date the task should be completed by, in DD/MM/YYYY format with the element label' due_date'
- The record id of the user who is responsible for the task with the element label 'user_id'
- {!$Input:Candidate_Opportunity.Id} with the element label 'what_id'

Do not include any supporting text, output JSON only.
Note that I did have to remind it not to add any text over and above the JSON output - this is something LLMs tend to suffer from a lot in my experience, always wanting to a add a "here you go" or "as requested". Nice that they try to be polite, but not helpful when you need structured data!

Trying this out in Copilot Builder showed that I was getting the output that I wanted:


Note that while there is the additional text of 'Great, I have generated ...', that's Copilot rather than the LLM, so if I can chain this to another custom action I'll just get the JSON format data.

My Apex Custom Action code is surprisingly svelte:

public with sharing class CopilotOppFollowUpTask 
{
    @InvocableMethod(label='Create Task' description='Creates a task')
    public static List<String> createTask(List<String> tasksJSON) {

        JSONParser parser=JSON.createParser(tasksJSON[0]);
        Map<String, String> params=new Map<String, String>();

        while (null!=parser.nextToken()) 
        {
            if (JSONToken.FIELD_NAME==parser.getCurrentToken())
            {
                String name=parser.getText();
                parser.nextToken();
                String value=parser.getText();
                System.debug('Name = ' + name + ', Value = ' + value);
                params.put(name, value);
            }
        }

        String dateStr=params.get('due_date');

        Date dueDate=date.newInstance(Integer.valueOf(dateStr.substring(6,10)),
                                      Integer.valueOf(dateStr.substring(3, 5)),
                                      Integer.valueOf(dateStr.substring(0, 2)));

        Task task=new Task(Subject=params.get('subject'),
                           Description=params.get('description'),
                           ActivityDate=dueDate,
                           OwnerId=params.get('user_id'),
                           WhatId=params.get('what_id'));

        insert task;
        
        return new List<String>{'Task created with id ' + task.Id};
    }
}

All that's needed to make it available for a Custom Action is the invocable aspect :

    @InvocableMethod(label='Create Task' description='Creates a task')

When I define the custom action, it's all about the instruction:


which hopefully is enough for the Copilot reasoning engine to figure out it can use the JSON format output from the task recommendation action. Of course I still need to give Copilot the correct instruction so it understands it needs to chain the actions:


And here's the task it came up with:


Related Posts



Sunday, 7 April 2024

Einstein Copliot Custom Actions

Image created by DALL-E 3 based on a prompt by Bob Buzzard

Introduction

One of the key features of Einstein Copilot from Salesforce is its extensibility - while there are a bunch of standard actions, and many more coming, there will always be something more you want to give your users. Custom actions allow you to create an AI assistant that targets your specific business challenges, adding capabilities to your copilot that generate real value for your users.

Scenario

In my last post, I introduced the Sales Coach - a custom Lightning Web Component that you add to an Opportunity record page. When the Sales Coach component renders, it executes Apex code to hydrate a prompt template with details of the Opportunity and request some guidance from an LLM. 

While this works well for users that are viewing an Opportunity record, I also want to provide a way tfor users receive guidance on deals while they are in another part of the Salesforce application - their Home Page, for example.

Creating the Custom Copilot Action

Custom Copilot Actions can be created using Apex, Flow or Prompt Templates. I've gone for a Prompt Template, as I already have this in place for the Sales Coach component. Once I choose the Reference Action Type of Prompt Template, I get to select from a list of existing prompts in the Reference Action selector. 


Once I've chosen my action, I provide details of how the Copilot can use it :



The Copilot Action Instructions is pre-populated from my Prompt Template description. As I want to use it in exactly the same way, I can leave that alone. I then provide the instructions around the Candidate Opportunity input, and check the box in the Output section to Show in conversation - without this, the user won't be able to receive the coaching they so richly deserve.

Note that while the Prompt Template pull several fields from the Opportunity record - Name, Amount, Close Date, Stage Name - I don't need to specify any of this information here. I don't even need to specify that it's an Opportunity record - that is picked up automatically from the Prompt Template input. Pretty simple really.

Once my custom action is created, I need to add it to the Copilot. I do this via the Copilot Builder, selecting my new action from the list of available actions and then clicking a button to add it to the Active Copilot :



As an aside, this Copilot isn't Active yet, so the text in the button is slightly misleading, but not a big deal as I can only have one Copilot so I'm hardly likely to get confused.

Putting it Together

Remember earlier when I created the Copilot action I gave it details of how to use it? The Copilot Action Instructions field does what it says on the tin - it instructs the Copilot as to the purpose of the action and when it should be used:


I've defined this as providing advice to progress a specific opportunity, so in order to include the action in the Copilot plan, I need to ask the it to help me do that. As we're in AI world, I don't need to be as obvious as asking for advice to progress an opportunity, instead I used my own words and asked for "help to win a deal".

Interestingly, when I ran this to capture the screenshots, Copilot told me that it was following the instructions that I'd provided for the action, which I hadn't seen before:




Something to remember when using Prompt Template actions is they will lead to a slower response time, as each one involves a round trip to an LLM. There will always be one round trip, to create the plan, but if you manage to generate a request that involves multiple Prompt Template actions, there will be separate requests made for each of those, and you'll be consuming additional tokens. 

Monday, 1 April 2024

Deploy Code at the Speed of AI with Ship Happens

Image from DALL-E 3 based on a prompt by Bob Buzzard


Introduction


It's been quite the year for Salesforce Einstein - Copilot, Prompt Builder, lots of new Generative AI features, and it's only the beginning of April!

Einstein for Developers has been out in beta for a while now, built on CodeGen - Salesforce's in-house open-source LLM. To date this as been focused on generating code from natural language prompts, auto-completions. and unit tests, but now it's time for artificial intelligence to dip a toe into the Dev Ops space with Ship Happens.

Ship Happens


In keeping with most Generative AI launches, Einstein Dev Ops goes live with a single feature and the expectation that more will come in a few months. It's quite the feature though - with Ship Happens, your code will barely have a chance to touch down in a scratch org before it's flung to production and into the hands of users.

You've Got to be Shipping Me


Research shows that by far the biggest blocker to getting code into production is developers accepting that they have finished. While this sounds like a simple decision, developers can't help polishing - always looking to squeeze another feature in, or a percentage point of unit test coverage.  Ship Happens unblocks your development team by handing the decision off to Generative AI.

As development progresses, the code is constantly analysed by a Large Language Model trained on years of Salesforce deployments, both successful and unsuccessful, and the quality of code for your specific instance. Once the LLM decides the code is as good as it's ever going to be, it's committed to version control and on it's way to production before the developer knows what has hit them. To ensure full traceability, Ship Happens reuses the same commit message when it takes the decision to ship a feature - "You've Got to be Shipping Me".

Welcome to Ship Creek


Crunching the numbers after the pilot program showed that users are delighted when Ship Happens. An updated experience every time they login, reduced waiting time for new features, and the oh so familiar bugs they have come to expect with every release. Once they accept their role under our new AI overlords, your developers will enjoy the freedom to focus purely on writing code - with Ship Happens in charge, everyone knows that features will be released exactly when they should be, not a moment earlier and not a moment later. Before long, everyone will be happily floating along Ship Creek and forget they were ever in a different place.