Agentforce is Salesforce's platform for building and deploying autonomous AI agents that can reason, plan, and take actions inside your org — without a human in the loop for every step. Unlike a chatbot that only answers questions, an Agentforce agent can retrieve data, update records, call external APIs, and trigger flows, all driven by natural language instructions.
What Is Agentforce?
Agentforce sits on top of the Einstein 1 Platform and uses a large language model (the Einstein Reasoning Engine) as its brain. You define the agent's role, the topics it can handle, and the actions it can take. At runtime, the LLM decides which topic and action to invoke based on the user's message, executes it, and returns a grounded response.
Key mental model: You don't code the agent's decision-making — you configure what the agent can do (Topics + Actions) and the LLM decides when and how to use those tools based on the conversation context.
Core Concepts
Agentforce has four building blocks. Understanding how they compose is the foundation of everything else.
Agent
The top-level entity. Defines the agent's name, persona, role description, and which topics it has access to. An agent can serve one channel (e.g., a service portal) or multiple.
Topic
A domain of responsibility. Each topic has a natural language description that the LLM uses to classify incoming requests. A topic contains one or more actions. Example: "Order Management".
Action
A specific capability the agent can invoke — an Apex method, a Flow, a prompt template, or an external API call. Each action has inputs/outputs the LLM maps to and from the conversation.
Reasoning Engine
The LLM layer that reads the conversation, selects the right topic and action, maps user intent to action inputs, executes it, and generates a natural language response from the output.
Configuring Topics
Topics are configured in Setup → Agents → [Your Agent] → Topics. The topic description is critical — it's the text the LLM reads to decide whether an incoming user message belongs to this topic. Write it like a job description for the topic.
Write better topic descriptions: Be specific about what the topic handles AND what it doesn't. Overlapping descriptions cause the LLM to pick the wrong topic. Include example triggers like "Use this topic when the user asks about order status, cancellations, or shipment tracking."
Building Apex Actions
Apex actions are the most powerful and flexible way to extend Agentforce. An invocable Apex method becomes an action the agent can call with inputs drawn from the conversation and CRM context.
Requirements: The Apex class must be global, the method must be @InvocableMethod, and all parameters must use an inner @InvocableVariable wrapper class. The method must be bulkified (accept and return List).
global class GetOrderStatusAction {
// Input wrapper — each @InvocableVariable is a field the LLM can populate
global class ActionInput {
@InvocableVariable(
label='Order Number'
description='The customer-facing order number, e.g. ORD-12345'
required=true
)
global String orderNumber;
@InvocableVariable(
label='Customer Account ID'
description='Salesforce Account ID of the requesting customer'
required=false
)
global String accountId;
}
// Output wrapper — the LLM reads these to formulate its response
global class ActionOutput {
@InvocableVariable(label='Order Status')
global String status;
@InvocableVariable(label='Estimated Delivery Date')
global String estimatedDelivery;
@InvocableVariable(label='Tracking URL')
global String trackingUrl;
@InvocableVariable(label='Error Message')
global String errorMessage;
}
@InvocableMethod(
label='Get Order Status'
description='Retrieves the current status and delivery information for a given order number. Use when the customer asks where their order is or when it will arrive.'
category='Order Management'
)
global static List<ActionOutput> execute(List<ActionInput> inputs) {
List<ActionOutput> outputs = new List<ActionOutput>();
for (ActionInput inp : inputs) {
ActionOutput out = new ActionOutput();
try {
List<Order__c> orders = [
SELECT Id, Status__c, Estimated_Delivery__c, Tracking_URL__c
FROM Order__c
WHERE Order_Number__c = :inp.orderNumber
LIMIT 1
];
if (orders.isEmpty()) {
out.errorMessage = 'No order found with number ' + inp.orderNumber;
} else {
Order__c order = orders[0];
out.status = order.Status__c;
out.estimatedDelivery = order.Estimated_Delivery__c != null
? order.Estimated_Delivery__c.format()
: 'Not yet available';
out.trackingUrl = order.Tracking_URL__c;
}
} catch (Exception e) {
out.errorMessage = 'Unable to retrieve order: ' + e.getMessage();
System.debug(LoggingLevel.ERROR, 'GetOrderStatus error: ' + e.getMessage());
}
outputs.add(out);
}
return outputs;
}
}
Write great descriptions: The description on @InvocableMethod and each @InvocableVariable is read by the LLM to understand when and how to call your action. Treat them like API documentation — be precise about what each field means and when the action should be used.
Flow Actions
Auto-launched Flows can also be surfaced as Agentforce actions — no Apex required. This is ideal for admins or when the logic already exists in Flow. Mark the Flow's input/output variables as Available for Input and Available for Output to expose them to the agent.
// In Flow Builder, for each variable you want the agent to use:
// INPUT variables (agent provides values TO the flow):
// ✅ Data Type: Text, Number, Boolean, Record, etc.
// ✅ "Available for Input" = true
// ✅ Description filled in (the LLM reads this)
// OUTPUT variables (flow returns values TO the agent):
// ✅ "Available for Output" = true
// ✅ Description filled in
// Flow-level settings:
// ✅ Flow Type: Auto-launched Flow (No Trigger)
// ✅ API Name is meaningful (shown in Agent Builder)
// ✅ Description explains when to use this flow
// In Agent Builder:
// Setup → Agents → [Agent] → Topics → [Topic] → Actions → Add Action
// Filter by "Flow" → select your Auto-launched Flow
// The LLM will map conversation context to flow variables automatically
Prompt Templates
Prompt Templates let you define reusable, data-merged prompts that the agent (or your own Apex/Flow) can invoke. They support merge fields from any Salesforce object and can be versioned and shared across agents.
import ConnectApi.EinsteinLLM;
import ConnectApi.EinsteinPromptTemplateGenerationsInput;
import ConnectApi.EinsteinPromptTemplateGenerationsRepresentation;
public class PromptTemplateService {
public static String generateCaseEmail(Id caseId) {
ConnectApi.EinsteinPromptTemplateGenerationsInput input =
new ConnectApi.EinsteinPromptTemplateGenerationsInput();
// Reference the template by its API name
input.promptTemplateName = 'Case_Resolution_Email';
// Provide the record context for merge fields
input.inputParams = new Map<String, ConnectApi.WrappedValue>{
'Input:CaseId' => ConnectApi.EinsteinLLM.wrap(caseId)
};
input.isPreview = false;
ConnectApi.EinsteinPromptTemplateGenerationsRepresentation result =
ConnectApi.EinsteinLLM.generateMessagesForPromptTemplate(input);
return result.generations[0].text;
}
}
Agent Builder: Step-by-Step
Agentforce agents are created and configured in Setup → Agents. Here's the end-to-end process for standing up a new agent.
Setup → Agents → New Agent. Choose a type (Service, Sales, Custom), give it a Name and a Role Description. The role description sets the agent's personality and overall scope — e.g., "You are a helpful service agent for Acme Corp. You help customers with orders, returns, and account questions."
Inside the agent, go to Topics tab → New Topic. Write a descriptive Scope field — this is the most important configuration you'll do. Add actions to each topic. Start with 3–5 focused topics rather than one massive topic.
Actions → Add Action → select Apex, Flow, or API. Fill in the Action Description (used by LLM), map input/output variables, and set confirmation behavior — some actions should ask the user before executing (e.g., canceling an order).
Use the built-in conversation simulator in Agent Builder. Type test messages that match each topic's expected triggers. Check which topic and action the agent selected — use the Reasoning Trace to debug unexpected behavior.
Activate the agent and assign it to a channel — Service Cloud Messaging, a website bot widget, Slack, or a custom channel via API. Set guardrails (out-of-scope message handling) before going live.
Testing & Debugging Agents
Agent behavior depends on LLM reasoning, which means deterministic unit tests alone aren't sufficient. Use a layered testing strategy.
// Unit test the Apex action independently of the agent runtime
@isTest
private class GetOrderStatusActionTest {
@TestSetup
static void makeData() {
Order__c order = new Order__c(
Order_Number__c = 'ORD-99999',
Status__c = 'Shipped',
Estimated_Delivery__c = Date.today().addDays(3),
Tracking_URL__c = 'https://track.example.com/ORD-99999'
);
insert order;
}
@isTest
static void validOrder_returnsStatusAndTracking() {
GetOrderStatusAction.ActionInput inp = new GetOrderStatusAction.ActionInput();
inp.orderNumber = 'ORD-99999';
Test.startTest();
List<GetOrderStatusAction.ActionOutput> results =
GetOrderStatusAction.execute(new List<GetOrderStatusAction.ActionInput>{ inp });
Test.stopTest();
System.assertEquals(1, results.size(), 'Should return one result');
System.assertEquals('Shipped', results[0].status, 'Status should be Shipped');
System.assertNotEquals(null, results[0].trackingUrl, 'Tracking URL should be present');
System.assertEquals(null, results[0].errorMessage, 'No error expected');
}
@isTest
static void invalidOrder_returnsErrorMessage() {
GetOrderStatusAction.ActionInput inp = new GetOrderStatusAction.ActionInput();
inp.orderNumber = 'ORD-DOESNOTEXIST';
Test.startTest();
List<GetOrderStatusAction.ActionOutput> results =
GetOrderStatusAction.execute(new List<GetOrderStatusAction.ActionInput>{ inp });
Test.stopTest();
System.assertNotEquals(null, results[0].errorMessage, 'Should return an error message');
System.assertEquals(null, results[0].status, 'Status should be null on error');
}
}
Debugging with Reasoning Trace
Every agent conversation in the simulator exposes a Reasoning Trace — a step-by-step log of what the LLM decided. Use it to diagnose:
description field on your @InvocableMethod. Be explicit about the trigger condition.description on your @InvocableVariable fields. Include format hints like "Order number in format ORD-XXXXX".Agentforce vs Einstein Bots
If you've used Einstein Bots before, here's how Agentforce differs — and when to use each.
Best Practices
Design checklist for production-ready Agentforce agents:
✅ Keep topics narrow and non-overlapping — one clear job per topic
✅ Write action descriptions like API docs — who uses it, when, and what each field means
✅ Always return a graceful errorMessage from Apex actions — never let unhandled exceptions bubble to the LLM
✅ Use confirmation prompts for destructive actions (cancel, delete, update)
✅ Unit test every Apex action independently before wiring to the agent
✅ Add an "Out of Scope" system message telling the agent how to handle irrelevant requests
✅ Use Named Credentials for any external API calls within actions
✅ Monitor conversations with Einstein Conversation Insights after launch