Skip to main content

Salesforce Agentforce Best Practices: Return Decisions, Not Raw Data to LLMs

๐Ÿ’ฌ In plain words: When building custom Apex actions for AI (like Agentforce or Einstein Copilot), never just hand the raw record to the Large Language Model (LLM) and ask it to figure out the answer. While the model will usually get it right, its occasional silent failures are impossible to catch. Instead, do the heavy lifting in your Apex code and return a clear, final decision (a verdict) alongside a plain-language explanation to the model.
๐Ÿ“Œ Example: Instead of passing a raw Service Contract and Case record to the AI to determine a refund, your Apex should process the logic and return specific outputs like:
isEligible = true
refundAmount = 1240
reason = "Supply was interrupted for 14 hours, which exceeds the 12-hour guarantee."
Now, there is absolutely nothing left for the LLM to guess or infer.

The Concept: Verdicts, Not Evidence

When you expose an invocable method to an AI agent, you must strictly control the narrative. If you return raw data, the model acts as the judge, jury, and executioner. This leads to drift and hallucinations. To prevent this, follow these core rules:

  • Return the conclusion: Do the math and logic in Apex. Give the AI the final state (e.g., eligible vs. ineligible).
  • Provide a reason string: This is a deliberate design choice. It gives the model the exact, correct words to use when explaining the decision to the user.
  • Stop AI justification: Without a provided reason string, the model will compose its own justification for a decision it didn't actually make. The AI's explanation can be wildly wrong even when the final verdict is technically right.
  • Respect bulkification: Invocable methods receive and return lists. They are bulkified by design, even if the AI typically calls them one at a time.
  • Enforce security: Always use with sharing and USER_MODE queries. This ensures the action perfectly mirrors the running user's field-level security and sharing rules.
// ❌ BAD: Returning RAW RECORDS
// The model must compare dates, read picklists, and reach a verdict.
public List<Case> getRefundEligibility(List<Id> caseIds) { ... }

// ✅ GOOD: Returning a DECISION
// The model receives exact instructions and strings to output.
public class RefundDecision {
    @InvocableVariable public Boolean isEligible;
    @InvocableVariable public String coverageType;
    @InvocableVariable public String reason;
}
  
๐Ÿง  Verdict, not evidence. Send the conclusion and the sentence. Never send the raw file and hope for the best.
๐Ÿงญ 360 Card — Return a Decision, Not Data
  • Rule: Every Apex action meant for an LLM should return a decided value and a plain-text reason string.
  • Gain: The verdict cannot drift, and the AI uses accurate, pre-approved wording instead of inventing its own.
  • Price: You have to design specific wrapper classes for your return variables, and business logic is locked in code (requiring a release to change).
  • Limits: Standard Apex governor limits still apply, and the AI's ReAct loop might call the action repeatedly if it gets confused.
  • Mirror — Returning the record: It feels more flexible, but it moves business-critical decision making into a probabilistic model where you cannot write unit tests for it.
  • At Volume: Bulkify properly. Invocables receive lists, and per-record calls will quickly exhaust limits if not handled correctly.
⚠ INTERVIEW TRAP: Never say, "We return the record and let the AI summarize it." Interviewers probe for this exact phrase because it is the root cause of hallucinated eligibility answers. AI is a reasoning engine, not a database calculator.

Core Q&A

Q1: Why does your Apex action specifically return a "reason" string?
๐ŸŽฏ Say this first: "So the model has exact, accurate words to reuse instead of inventing an explanation for a decision it did not make."

A: The verdict and the explanation carry two separate risks. Returning a boolean fixes the verdict, but it leaves the model completely free to compose its own justification. That explanation can be totally wrong even when the verdict is right—and customers will remember the explanation.

By returning the decided value, the supporting figures, and a plain-language reason sentence, you restrict the LLM's creativity. You can then add a prompt instruction telling the agent to "state the provided reason exactly."

Q2: Why is USER_MODE critical for AI-facing Apex?

A: LLMs are highly obedient—to a fault. They will happily read and surface any data you feed them. If your Apex action runs in system context (without with sharing or WITH USER_MODE in your SOQL), you bypass the entire Salesforce security model. The AI might read a hidden field (like a private margin calculation or internal HR note) and confidently display it to a user who has no rights to see it. Always let the platform's security handle visibility.

Q3: How do you handle bulkification when an AI agent calls the action?

A: AI agents (like Copilot) typically invoke actions for a single conversational turn, leading developers to assume they only need to process one record at a time. However, Salesforce requires @InvocableMethod to take a List<Request> and return a List<Response>.

You must loop through the request list just like you would in a standard Flow or trigger. If you just grab requests[0], your code will immediately break if the action is ever reused in a bulk Flow or a future batch-agent scenario. Write for lists, query outside the loop, and return a matching list of verdicts.

Q4: How can you test if your AI action is drifting?

A: Try a simple experiment: temporarily alter your code to return the raw record instead of the decision wrapper. Then, ask the AI the exact same eligibility question five times in five separate conversational sessions. You will quickly notice the phrasing, logic, and sometimes even the final verdict drifting. This is the ultimate proof that business rules belong in deterministic code, not probabilistic prompts.