Most agent demos answer questions. The interesting moment — the one where I finally felt I understood Agentforce — was when my agent stopped answering and started doing.
This post walks through a project I actually built and deployed: a service agent for HanseWatt, a fictional energy retailer in the DACH region. The company is invented and the external systems (the smart-meter MDM, SAP IS-U) are simulated — but the agent, its actions, and its grounding are real, running in a real org, under the Einstein Trust Layer.
The agent handles one complete customer conversation: identify the customer, pull their latest bill, explain why their consumption jumped, and log a support Case if they want one. Four steps, four Apex actions, and every answer traceable to data. Let me show you how it fits together.
What “grounded” really means
I’ve written before about grounding as a concept, but building this project sharpened my definition to one sentence:
A grounded answer is one the agent retrieved, not one it composed. The data is the answer; the model only phrases it.
When a HanseWatt customer asks “why is my bill so high?”, the worst thing the agent could do is reason its way to a plausible-sounding explanation. Plausible is not true. Instead, the agent calls an action that reads a Calculated Insight in Data 360 — a metric that says this meter’s latest reading is +64.6% above its trailing average. The agent’s job is to say that clearly, in a human sentence. The number came from data. The model never got a vote on what the number was.
That’s the design rule for the whole flow: the model decides which action to run and how to phrase the result. It never decides what the facts are.
Designing the four-step multi-turn flow
The planner — a ReAct-style reasoner — chooses actions turn by turn. My job was to give it four tools that chain naturally:
- HWIdentifyCustomer — the customer gives an email; the action resolves it to an Account. Nothing else works until this succeeds.
- HWGetLatestBill — with a verified Account, fetch the most recent bill (amount, period, consumption).
- HWExplainConsumption — the heart of the flow: read the anomaly insight from Data 360 and return the grounded facts (+64.6% vs the trailing average) for the model to phrase.
- HWCreateCase — if the customer wants follow-up, create a support Case linked to the Account, with the anomaly context in the description.
Notice what makes this multi-turn rather than one big action: each step produces something the next step needs. The planner asks for the email if it doesn’t have one, carries the Account forward, and only offers a Case after it has actually explained something. You don’t script that sequence — you describe each action clearly enough that the planner assembles it. (If that idea is new, start with Topics and Instructions.)
One design decision I’d defend anywhere: identification comes first and gates everything. An energy bill is personal data. Under GDPR, an agent that reads billing data to whoever asks is not a feature, it’s an incident. Step 1 isn’t just conversationally polite — it’s the access control the rest of the flow stands on, with the Einstein Trust Layer handling masking and audit on top.
Anatomy of a good @InvocableMethod action
All four actions follow the same shape, and the shape matters more than any single line. Here’s the pattern, using the identify action:
public with sharing class HWIdentifyCustomer {
public class Request {
@InvocableVariable(required=true label='Customer email')
public String email;
}
public class Response {
@InvocableVariable public Boolean found;
@InvocableVariable public Id accountId;
@InvocableVariable public String customerName;
}
@InvocableMethod(label='Identify HanseWatt customer by email')
public static List<Response> identify(List<Request> requests) {
// Delegate immediately — the invocable is just a doorway
return HWIdentifyCustomerService.identify(requests);
}
}
The habits baked into that shape:
- Request/response wrapper classes. The planner passes plain values; the wrappers give them names and labels the planner can reason about.
label='Customer email'is documentation for the model, the same way the action description is. - The invocable is thin; a service does the work.
HWIdentifyCustomerServiceholds the SOQL and the logic. That’s what makes the logic testable without an agent in the room — which becomes the whole cost story in a minute. - Bulk-safe by construction. The method takes a
List<Request>and returns aList<Response>, and the service processes the whole list with no queries inside loops. Agentforce may call it with one request today; the platform contract is a list, and I honor the contract. with sharingandWITH USER_MODE. The class respects sharing rules, and every query in the service runsWITH USER_MODE, so field- and object-level security apply too:
List<Account> accts = [
SELECT Id, Name
FROM Account
WHERE PersonEmail = :emailsToCheck
WITH USER_MODE
];
An agent action runs on behalf of a conversation. The defensive posture — assume the caller should see only what the running user may see — is not optional in a GDPR context. It’s the floor.
- One test class per action. Each of the four actions has a test that exercises the service with real records: the found case, the not-found case, and a bulk call with multiple requests. Nothing exotic — just proof.
Why I mock first (a word about Flex Credits)
Here’s the unglamorous truth about building agents: every real agent conversation costs Flex Credits. Debugging Apex logic by chatting with your agent is like proofreading an essay by publishing it and waiting for complaints.
So my discipline on this project was mock-first: prove every action’s logic in Apex tests before the agent ever calls it. The external systems were simulated anyway — the smart-meter MDM and SAP IS-U feeds are seeded data, not live integrations — so I could make the tests airtight cheaply. By the time I wired the actions into the agent, I wasn’t testing logic in conversations anymore. I was testing phrasing and planning — the only things a conversation can actually tell you.
The same thinking applied to the grounding side: I verified the Data 360 anomaly insight with plain SQL through the Query API before the agent touched it — zero UI, zero credits. That deserves its own post, and it gets one next week.
The economics are simple. Apex tests are effectively free and run in seconds. Agent turns cost credits and take minutes to evaluate. Spend the cheap currency until only the expensive questions remain.
When the agent went from answering to acting
The first three actions read. The fourth one writes — HWCreateCase inserts a real Case record. And I noticed my own posture change the moment I built it.
A wrong answer is embarrassing. A wrong action is a mess someone has to clean up. So the write action earned extra caution:
- The agent confirms before creating — it summarizes what the Case will say and asks the customer first. The instructions require it; the action doesn’t run on a maybe.
- The Case is created in user mode, linked to the verified Account from step 1 — never to an account the customer merely mentioned.
- The description carries the grounded context (“consumption +64.6% vs trailing average, latest bill period…”) so the human agent who picks it up inherits the evidence, not just a complaint.
That last point is my favorite detail of the whole build. Grounding isn’t only about the customer getting true answers — it’s about the Case being useful to the colleague who reads it tomorrow.
The mindset
Twenty years in classrooms taught me that trust is built in small, checkable steps — you show your work, every time. A grounded multi-turn agent is the same idea in software: identify before you reveal, retrieve before you explain, confirm before you act, and make every claim traceable to a record. None of the individual pieces here are advanced. The discipline of the sequence is the product.
Your next step
The grounding half of this project — proving the +64.6% anomaly with plain SQL through the Data 360 Query API, before spending a single credit — is next week’s post: The Data 360 Query API: Prove Your Grounding with Plain SQL. If you’re earlier in the journey, The Agentforce Planner explains the reasoner that chains these actions, and the Agentforce category has the full beginner path.