Effective Prompt Writing

Effective Prompt Writing

Overview

A prompt is natural language text that you craft to communicate with an AI. The response that’s generated depends on the quality of the prompt. Use prompt engineering to define the prompt and then refine it until you get a response that works for you. For E4D responses, prompt quality determines the quality of the Apex code that’s generated.

About Generated Code Quality

As with all LLM-based products, the technology behind Einstein for Developers is still new. The generated output often requires editing to be customized for your individual project. Some responses aren’t optimal. We’d love to hear feedback from you on the generated output through the History & Feedback Panel in the IDE. Your feedback is critical to the continuous training of our large language model (LLM) during the beta program.

We strongly encourage you to let us know when you see examples of less optimal code, so that we can continue to improve generated responses. We recommend tools such as Apex PMD (already in your Salesforce Extension Pack) and Salesforce Code Analyzer to ensure the quality of your generated code.

Project Context and Grounding

In addition to your instructions, the response generated by an AI depends on the context that your instruction is grounded with. The CodeGen2.5 model has been trained on Apex data and grounded with the context of Apex programming best practices.

To extract accurate and relevant information from the model, first run SFDX:Refresh SObjects Definitions to gather org metadata in the form of object definitions into your project before running your query. Then use the following tips to help ground your prompts further with org context awareness to generate a high-quality response.

Currently the CodeGen2.5 model can pick up on the following context from your project and prompt:

  • Apex code that’s present in your project in Apex class files (.cls).
  • Custom objects in the schema that are mentioned in the object__c format. Schema information takes precedence over Apex code in the current file.
  • Custom metadata when it is a part of a prompt and uses the __mdt suffix. The metadata added to a prompt in this way is automatically included into the context to ground generations.

Prompt Writing Tips

We’re listing some tips for prompt writing and also providing you with some examples that help you craft your prompts. An effective prompt consists of these four parts:

Action: The task you want the AI to accomplish. For example, “Create an Apex batch class named “OpenPositionMatcher””.

Context Details: Information about the context you want the AI to use. This includes important context such as org metadata and awareness about the presence of custom objects and fields in your org. Run SFDX:Refresh SObjects Definitions to retrieve org metadata into your project. You can then add custom fields to your existing query. For example, “Create an Apex batch class named “OpenPositionMatcher” that processes “Position__c” records created within the past 15 days. The class should match these open positions with suitable candidates based on their skills.”.

Best Practices: Details about best practices you want the AI to follow such as security and formatting best practices. For example, “Create an Apex batch class named “OpenPositionMatcher” that processes custom objects “Position__c” records created within the past 15 days. The class aims to match these open positions with suitable candidates based on their skills. Order the results by candidate names in descending order. Make sure the generated code follows security best practices. Separate operators with a new line.”.

Additional Context: Any additional information that you want to provide. For example, “Create an Apex batch class named “OpenPositionMatcher” that processes custom objects “Position__c” records created within the past 15 days. The class should match these open positions with suitable candidates based on their skills.” Order the results by candidate names in descending order. Make sure the generated code follows security best practices. Separate operators with a new line. The class is used in an LWC component.”.

The final prompt gives you a response that looks something like this:

public with sharing class OpenPositionMatcher {

    @AuraEnabled(cacheable=true)
    public static List<Candidate__c> matchSkills(String positionId) {
        try {
            Position__c position = [SELECT Id, Skills__c FROM Position__c WHERE Id = :positionId WITH SECURITY_ENFORCED];
            List<String> skills = position.Skills__c?.split(';');

            if (skills != null && !skills.isEmpty()) {
                List<Candidate__c> candidates = [
                    SELECT Id, Name, Skills__c
                    FROM Candidate__c
                    WHERE Skills__c IN :skills
                    WITH SECURITY_ENFORCED
                    ORDER BY Name DESC
                ];

                return candidates;
            }
        } catch (Exception e) {
            System.debug(e.getMessage());
            throw new AuraHandledException(e.getMessage());
        }

        return new List<Candidate__c>();
    }
}

Example Prompts and Context Descriptions

Prompt: Create a class named OpportunityController. Create a method called getNumberofOpportunities. The method takes AccountId as a parameter and returns the number of opportunities for that AccountId. I’m working on an LWC component. Make sure the SOQL uses security best practices like user mode data operations. This prompt contains the following context:

  1. Specific mention of an LWC component.
  2. Specific mention of security best practices.

Prompt: Write an Apex class called VehiclePartInventoryClass for the custom object Vehicle__c, which has a many-to-many relationship with the Parts__c custom object. The class includes a method called getVehiclesByParts that accepts a Set of Strings. This method retrieves all the vehicle records associated with each part and returns a Map<String, List<Vehicle__c>>, where the key is the part name, and the values are lists of vehicles that have the specified part installed. This prompt contains the following context:

  1. Specify custom object names including __c
  2. Specify method input and output types and any logic that you wish to see in the method implementation.

Prompt: Create a method that takes a settingName as input and returns the value of the first record in Setting__mdt where QualifiedApiName matches the settingName; return null if no records are found. This prompt contains the following context:

  • Specify any custom metadata.

Prompt: Write a method that takes an Id as an argument. It compares the input Id with the value returned by the getStagingOrgId() method, and returns a boolean based on whether they match. This prompt contains the following context:

  1. Specify the relevant method name in the current file.
  2. Specify method input and output types and any logic that you wish to see in the method implementation.

Prompt: Create a method that takes settingName as input and returns the value of the first record in Setting__mdt where QualifiedApiName matches settingName; return null if no records are found.

Example Prompts and Responses

Prompt: Write an Apex method that accepts a list of sobjects and a field name and returns a set of non null field values.

Response:

public Set<String> getFieldValues(List<sObject> records, String fieldName) {
        Set<String> fieldValues = new Set<String>();

        for (sObject record : records) {
            try {
                Object fieldValue = record.get(fieldName);
                if (fieldValue != null) {
                    fieldValues.add((String) fieldValue);
                }
            } catch (Exception e) {
                system.debug('Error: ' + e.getMessage());
            }
        }
        return fieldValues;
    }


Prompt: Create an @invocable method that serializes an input list of sObjects and returns JSON.

Response:

@InvocableMethod(label='Create Account' description='Creates a new account with the specified name, number and symbol.' category='Account')
    public static List<String> createAccounts(List<Account> accounts) {
        List<String> response = new List<String>();

        for (Account account : accounts) {
            String jsonAccount = JSON.serialize(account);
            response.add(jsonAccount);
        }

        return response;
    }

Prompt: Write an Apex method that accepts product name as input and retrieves orderitems filtered on product name.

Response:

public List<OrderItem> getOrderItemsByProductName(String productName) {
List<OrderItem> orderItems = [SELECT Description FROM OrderItem WHERE ProductCode = :productName LIMIT 50];
return orderItems;
}

Prompt: Write an Apex trigger to add the number of contacts associated with an account. In the SOQL query ensure it is secure by adding WITH USER_MODE.

trigger AccountTrigger on Account (after update) {
    List<Account> accountsWithContacts = [
        SELECT Id, (SELECT Id FROM Contacts)
        FROM Account
        WHERE Id IN :Trigger.newMap.keySet()
        WITH USER_MODE
    ];

    for (Account account : accountsWithContacts) {
        Integer numberOfContacts = account.Contacts.size();

        // Update the Account's "Number_of_Contacts__c" field
        account.Number_of_Contacts__c = numberOfContacts;
    }

    update accountsWithContacts;
}

Edit a Prompt and Regenerate Output

A well designed prompt is key to getting a good response from Einstein. We encourage you to modify your prompts until you get Apex code that helps you complete your task at hand. When you use the Einstein: Generate Code command from the command palette, use the Retry and Accept options. In the sidebar, edit the prompt and click Ask.

For example, the prompt, “Write code to provide the ability to update the FirstName for a list of users” generates this response.

public with sharing class AccountService {
  public Account createAccount( String accountName, String accountNumber, String tickerSymbol ) {
    Account newAcct = new Account(
      Name = accountName,
      AccountNumber = accountNumber,
      TickerSymbol = accountNumber
    );
    return newAcct;
  }
// Function to update the FirstName for a list of users
public static List<User> updateFirstName(List<User> users, String newFirstName) {
        for (User u : users) {
            if (String.isNotBlank(u.FirstName)) {
                u.FirstName = newFirstName;
            }
        }
        return users;
 }

This response is good, but you can edit the prompt to generate reusable code by creating a method.

Click Retry and edit the prompt for more specific output. Update the prompt to say, “Write code to provide the ability to update the FirstName for a list of users as a method”. This is the new response.

public with sharing class AccountService {
  public Account createAccount( String accountName, String accountNumber, String tickerSymbol ) {
    Account newAcct = new Account(
      Name = accountName,
      AccountNumber = accountNumber,
      TickerSymbol = accountNumber
    );
    return newAcct;
  }
}

 //This is a method that allows updating the FirstName for a list of users
    public static void updateFirstName(List<User> users) {
        for (User usr : users) {
            if (usr.FirstName != null) {
                usr.FirstName = 'New First Name';
            }
        }
    }

If you like this output, click Accept. To update your prompt and regenerate new output, click Retry.

Feedback or Bugs | Edit this Article