Category: Salesforce

  • Grounding Agentforce responses: retrieval patterns that stop an agent making things up

    Agentforce will answer a question whether it knows the answer or not. That’s the part teams miss when they go from a demo to production. In the demo, someone asks a question the agent happens to be grounded for, gets a clean answer, and everyone nods. In production, a customer asks something slightly off to the side of what the knowledge base covers, and the agent fills the gap with something plausible-sounding and wrong. Nobody flagged it because the answer read fine.

    Grounding is the fix, and it’s not one setting. It’s a set of retrieval patterns you have to choose deliberately, test against real questions, and keep auditing after go-live. Get it wrong and you’ve built a confident liar with a Salesforce logo on it.

    What grounding actually does

    An Agentforce topic doesn’t reason from nothing. Before the model generates a response, Salesforce retrieves content it thinks is relevant (Knowledge articles, Data Cloud records, data from a prompt template) and stuffs it into the context the model sees. The model’s job then becomes summarising and rephrasing that retrieved content, not inventing facts. Strip the retrieval step out, or retrieve the wrong thing, and the model falls back on its training data and whatever pattern-matching gets it to a fluent-sounding answer.

    That’s the whole mechanism. Retrieval-augmented generation isn’t exotic. It’s giving the model the right paragraph at the right moment so it has something true to paraphrase instead of something to guess.

    The retrieval patterns worth knowing

    Knowledge grounding

    The simplest pattern: point a topic at a Knowledge action, let Salesforce’s built-in relevance search pull matching articles, and let the model summarise them. This works well when your Knowledge base is actually current and well-tagged. It falls apart fast when articles are stale, contradictory, or written for internal agents rather than customers. I’ve seen a contact-centre team ship this straight from a Knowledge base that hadn’t had a content audit in two years. The bot was technically grounded. It was grounded in wrong information, confidently delivered.

    Before you turn this on, run a content audit. Not a vibe check, an actual pass through the top fifty articles your topic will retrieve from, checked against what’s actually true today.

    Data Cloud vector search

    For anything beyond FAQ-style content, you’re grounding against a vector database built from Data Cloud: unstructured documents chunked and embedded, searched by semantic similarity at query time. This is where most of the real engineering work lives. Chunk size matters more than people expect. Chunk too large and you retrieve a paragraph with the right answer buried in three unrelated ones, diluting the signal. Chunk too small and you lose the surrounding context that made the answer correct in the first place. There’s no universal number here. I’d start around 300 to 500 tokens per chunk with meaningful overlap, then tune against your own eval set rather than trusting a default.

    Metadata filtering is the other lever that gets ignored. If your vector index spans multiple products, regions, or policy versions, retrieval without a metadata filter will happily pull a correct-sounding answer from the wrong product line. Tag chunks at ingestion time and filter at query time. It’s unglamorous and it’s the difference between a grounded agent and one that’s grounded in the wrong document.

    Prompt template grounding

    The pattern that gets underused: grounding directly off record data through a prompt template, rather than off a document corpus at all. If the customer is asking “what’s my order status”, you don’t need semantic search over a knowledge article. You need the actual Order record merged into the prompt. This is deterministic, cheap, and nearly impossible to hallucinate against, because the field value either exists or it doesn’t. Reach for this first whenever the ground truth is a structured record rather than prose. Save vector search for the cases where the answer genuinely lives in unstructured text.

    Where teams get burned

    The most common failure isn’t a bad retrieval pattern. It’s confusing retrieval with validation. Grounding reduces the chance the model invents something, but it doesn’t guarantee the model only says what was retrieved. A model can still misread a retrieved chunk, blend two retrieved passages into a claim neither one makes, or answer a question the retrieved content doesn’t actually cover while sounding like it does. I’ve watched a demo agent cite a real, correctly retrieved article and still get the eligibility criteria backwards, because the article covered two scenarios and the model merged them.

    That’s why grounding and testing aren’t separable phases. You need an eval set: real questions, with the correct answer written down by a human who knows the domain, run against the agent regularly, not just before launch. Score for factual accuracy against the retrieved source, not just “did it answer in a reasonable tone.” A response can be fluent, on-brand, and wrong.

    A practical build order

    When I’m setting this up for a topic, the sequence is roughly: identify whether the ground truth is a record (use a prompt template, skip vector search entirely), a policy or process document (use Knowledge grounding, after a content audit), or genuinely unstructured and large (use Data Cloud vector search, with deliberate chunk size and metadata filters). Then write twenty to thirty real questions covering the edge cases your support team actually fields, not the easy ones. Run them before launch and after every content change, because grounding isn’t something you configure once. The source content keeps changing, and the retrieval behaviour needs to keep up with it.

    None of this requires exotic tooling. It requires treating “what does the agent actually know, and where did it come from” as a question you answer on purpose, every time, rather than something you assume because the demo went well.

  • Agentforce in a contact centre: where it helps the agent, and where it should stay out of the way

    A support agent picks up a call about a failed order. Before they’ve said a word, the case history, the customer’s last three interactions and a draft summary of the account are already sitting on screen. That’s Agentforce doing its job well: invisible, fast, and out of the customer’s way. Ten minutes later the same customer is angry about a refund policy exception, and the agent needs to read the room, bend a rule slightly, and say something that sounds human. That’s a different job entirely, and it’s not one Agentforce should be doing.

    I’ve spent years inside contact-centre Service Cloud orgs, and the pattern with Agentforce is becoming clear fast: the technology is genuinely good at a specific band of work, and genuinely bad at the work either side of that band. Knowing where that line sits matters more than any feature list Salesforce ships this year.

    Where Agentforce earns its place

    The strongest use cases I’ve seen all share one trait: they’re information-retrieval and drafting problems, not judgment problems.

    Summarising before the agent needs to ask

    Case summarisation is the easiest win in the whole platform. An agent opening a case with eleven prior interactions, three transfers and a product return doesn’t need to read all of that. They need the three facts that matter right now. Agentforce reading case history and surfacing a two-line summary saves real seconds on every single call, and seconds compound across thousands of calls a week.

    First-draft replies on routine requests

    Password resets, order status, standard policy questions: these have one correct answer and no emotional complexity. Letting Agentforce draft the reply and having the agent review and send it is faster than typing from scratch, and it’s lower risk than it sounds, because the agent is still the one hitting send.

    Routing and triage

    Classifying an inbound case by intent, urgency and the right queue is exactly the kind of pattern-matching large language models are built for. I’ve watched Omni-Channel routing improve meaningfully once case classification stopped depending on a customer picking the right dropdown option on a web form.

    Where it should stay out of the way

    The failure cases I’ve seen cluster around three things: emotional nuance, irreversible actions, and genuine edge cases the model has never seen framed quite that way.

    Anything with an angry or vulnerable customer

    An agent can hear tone shift in someone’s voice and adjust. A model reading transcript text is working from a much thinner signal. Handing de-escalation, retention conversations, or anything touching a customer in genuine distress to an autonomous agent is a bad trade even when the model gets the facts right, because the facts were never the hard part.

    Actions that can’t be undone

    Refunds above a threshold, account closures, data deletion requests: give Agentforce a narrow, well-tested action for these and it’ll happily execute it exactly as scoped. The risk isn’t the model being wrong occasionally. It’s that “occasionally” compounds fast when the action is irreversible and the volume is high. I treat any Apex action tied to money or data loss as something a human approves, full stop, no matter how good the surrounding prompt is.

    The exception that breaks the pattern

    Every contact centre has cases that don’t fit the normal shape: the customer who’s a VIP but isn’t flagged as one, the policy exception that was agreed on a call six months ago and never got written down, the integration that silently failed last Tuesday and left three systems disagreeing about the truth. Agentforce has no way to know what it doesn’t know. A human agent, at minimum, knows when something feels off.

    A rough framework for the line

    When I’m scoping where Agentforce should sit in a Service Cloud build, I ask three questions about the task:

    • Is there a single correct answer, or does the right answer depend on reading a person?
    • Can the action be reversed cheaply if the model gets it wrong?
    • Has this exact shape of request happened often enough that the model has real pattern density to draw on?

    Three yeses, and Agentforce can probably run with real autonomy. Two yeses, give it a supporting role with a human in the loop. One or zero, keep it out of the workflow entirely and spend the budget somewhere else.

    What this means for the build

    Practically, this changes how I scope Agentforce actions. I design the narrowest possible action for each task rather than one broad “handle the case” agent. A tight action that looks up order status is easy to test, easy to monitor, and easy to trust. A broad action that can also issue refunds, update addresses and escalate cases is none of those things, even if it demos beautifully.

    It also changes where I put the guardrails. Input validation on every Apex action an agent can call isn’t optional, because the agent will eventually call it with something you didn’t anticipate. And every action above a certain blast radius gets a human approval step before anything executes, not because I distrust the model specifically, but because that’s the same discipline I’d apply to any automation touching customer money or customer trust.

    None of this is an argument against Agentforce in a contact centre. It’s an argument for being precise about which parts of the job it’s actually solving. The agents who do this well in six months won’t be the ones who gave Agentforce the most access. They’ll be the ones who worked out, task by task, exactly where the model was reliable and designed everything else around a human doing what humans are still better at.

  • Flow orchestration for agents: handing a multi-step process to Agentforce safely

    An agent that calls one Apex action is easy to reason about. Give it a clear input, get a clear output, done. The trouble starts when you hand Agentforce something with five steps, two approval gates, and three ways it can fail halfway through. That’s not an action anymore. That’s a process, and Flow is where most Salesforce teams are reaching to let an agent run one safely.

    I’ve seen this go wrong two ways before it goes right. Either the Flow gets built the way it always has, screen-free but assuming a predictable caller, and the agent trips over edge cases nobody designed for. Or the Flow gets treated like a black box: wire it to an action, hope the agent passes sane inputs, and find out in production what “sane” actually meant.

    Why this isn’t just another autolaunched Flow

    A Flow built for a scheduled job or a button click has one caller, and that caller is predictable. A human clicking a button has already read the screen. A batch job runs against records that already passed a filter. Agentforce doesn’t give you either guarantee. It decides to invoke the Flow based on a conversation, so the inputs it hands over are only as good as the prompt, the topic instructions, and whatever grounding data it pulled that turn.

    That changes what the Flow needs to do before it does anything else: trust nothing coming in. Validate every input the way you’d validate a public API, not the way you’d validate a field that’s already constrained by a page layout and a validation rule.

    Three things to design before the happy path

    An input contract the agent can’t violate by accident

    Define exactly what the agent is allowed to pass, with types, required fields, and value ranges, as part of the action’s input parameters, not buried in Flow logic three screens deep. If a field should only ever be one of four picklist values, constrain it at the action level. An agent that free-types “urgent” into a priority field expecting “High” will do that eventually. Catch it at the boundary, not in a decision element nobody will debug at 9pm.

    A way to stop partway through without leaving a mess

    Multi-step Flows fail in the middle. A record updates, a callout times out, the fourth element in the chain throws. In a human-triggered Flow, a failure usually means the person tries again or logs a ticket. An agent-triggered Flow needs the same recovery path, except nobody’s watching it happen in real time. Build in checkpoints: a status field on the driving record that tracks which step last completed successfully, so a retry, whether triggered by the agent, a scheduled cleanup job, or a human reviewing a log, can pick up from where it actually broke instead of re-running steps that already succeeded.

    Idempotency, because the agent will call it twice

    Conversational agents retry. A timeout, a dropped connection, a user who asks the same thing two different ways in one session, and your Flow gets invoked again with the same or similar inputs. If step two creates a case and step four sends an email, a second run shouldn’t create a second case and send a second email. Check for an existing record before creating one. This is ordinary integration hygiene, and it matters more here than it did when the only caller was a trusted internal system.

    Where approval steps actually belong

    If the process needs a human sign-off before something happens, a refund over a threshold, a case escalation on a VIP account, anything touching financial data, don’t let the agent decide whether to pause for approval. Build the approval gate into the Flow itself, triggered by the data, not by the agent’s judgment about when something feels risky. The agent should know it can submit a request. It shouldn’t be the thing deciding whether that request needs a second pair of eyes. That decision belongs in business logic you control, versioned and testable, not in a prompt that someone can reword next week without telling you.

    Logging that helps you debug an agent, not just a Flow

    Standard Flow error emails and debug logs were built for admins debugging admin-triggered automation. When Agentforce is the caller, you need to know what the agent was asked, what it decided to pass in, and what the Flow did with it, all three, in one place you can actually query. I log the action name, the full input payload, the Flow interview GUID, and the final status to a custom object rather than relying on native fault emails. When something breaks three days later and the only report you get is “the agent said it couldn’t finish,” that log is the difference between a five-minute fix and a long afternoon reconstructing a conversation from debug logs that have already rolled off.

    Test it like an API, not like a form

    The usual Flow testing habit is to run it against a few representative records and eyeball the result. That’s not enough once an agent is the caller. Test it the way you’d test an integration endpoint: valid inputs, missing optional fields, wrong data types, boundary values, and the same call fired twice in a row. Where you can, write test invocations of the action itself, not just the Flow, that simulate what Agentforce would send on a bad day. A bad day is exactly when this will matter.

    The real shift

    None of this is new Flow capability. It’s the same orchestration tool doing what it always did, built with a different caller in mind. The teams getting this right aren’t running the fanciest Flow. They’re the ones who stopped assuming the caller is trustworthy by default and designed the contract first. Once that contract holds, handing a multi-step process to an agent stops being a risk you’re managing and starts being just another integration you’ve already solved before.

  • Designing Apex actions for Agentforce: input validation an agent can’t talk its way around

    An Apex action built for a Flow has one source of input: a developer who read the spec, or a user filling in a screen with picklists and masks already doing half the validation for them. An Apex action built for Agentforce has a different source: a language model deciding, turn by turn, what values belong in each parameter. It reads your method’s label and description, forms an intent, and fills in the blanks. Most of the time it gets close enough. The failures show up in the ten percent where “close enough” produces a record with the wrong status, a case reassigned to the wrong queue, or a refund approved against an account that doesn’t exist.

    This is the part of Agentforce work that doesn’t get much attention next to the demo-friendly stuff like topic design and grounding. But if you’re the one writing the Apex actions an agent calls, input validation is where most of your actual engineering time should go.

    The description is a suggestion, not a contract

    An @InvocableMethod annotation lets you set a label and description, and those strings do real work: they’re what the agent reads to decide whether this action fits the user’s request and how to populate its parameters. It’s tempting to treat a well-written description as validation. It isn’t. “Account Id of the customer, required” tells the model what you want. It doesn’t stop the model from passing a contact Id instead, a made-up string that looks like an Id, or an empty value when it couldn’t resolve one and decided to guess rather than ask.

    @InvocableVariable(required=true) is similarly softer than it looks. It affects what the agent is nudged to supply, not what Apex is guaranteed to receive. I’ve seen teams ship an action, watch it work fine in testing with a cooperative tester, then get a null reference exception three weeks later because a real conversation took a path nobody scripted. The agent didn’t do anything wrong by its own logic. It just didn’t have a value, and the action didn’t check.

    Validate inside the method, every time

    Treat the parameters arriving from an invocable Apex action exactly like you’d treat a public API payload: untrusted until proven otherwise. Concretely, that means checking for null and blank before anything else runs, confirming record Ids actually resolve to the object type you expect (an Id typed variable doesn’t guarantee the right sObject, just a valid Id format), and re-checking any business rule the description implied rather than assumed. If the action only makes sense for open cases, check the case status in Apex. Don’t rely on the agent having read that constraint correctly off a sentence in the description.

    A pattern that’s worked well for me: wrap the actual logic in a private method, and have the invocable method itself do nothing but validate and translate failures into a response the agent can act on. Something like this shape:

    • Check required fields are present and non-blank, collecting every violation rather than stopping at the first.
    • Resolve any Id to a real record with a targeted SOQL query, not a bare Id.valueOf() check.
    • Confirm the record is in a state the action is allowed to act on, inside Apex, not in the prompt.
    • Only then call the private method that does the actual work.

    That last point matters more than it sounds. An agent’s reasoning happens in natural language, which means it can be talked into contradicting its own earlier state, repeating a step it already completed, or applying an instruction from three messages ago that no longer applies. Apex doesn’t have that problem, as long as you don’t let the prompt do the gatekeeping on your behalf.

    Give the agent a response it can actually use

    When validation fails, resist the urge to throw a generic exception and let the Flow or Apex runtime error bubble up as a stack trace. The agent can’t do anything useful with that, and neither can the person on the other end of the conversation. Return a structured, readable failure: which field was the problem, and ideally what a valid value looks like. “Case Id did not match an open case” lets the agent re-ask the user or try a different lookup. A raw System.NullPointerException just ends the conversation badly, usually with the agent telling the customer something isn’t working rather than something wasn’t provided.

    Don’t let the action run with more power than the conversation deserves

    A separate failure mode: actions that run in system context doing things no standard user in that seat would be allowed to do. It’s a fair question to ask whether a given action should run without sharing at all. If an agent conversation shouldn’t be able to see or touch a record a human agent couldn’t, the Apex action shouldn’t either. I’d default to with sharing and only step outside it with a specific, documented reason, the same discipline you’d apply to any other integration user. An LLM making the call doesn’t change the sharing model; it just makes it easier to forget you’re still subject to one.

    Test it like someone’s trying to break it

    Standard Apex test classes covering happy-path invocations won’t catch what actually goes wrong in production, because the thing generating bad input isn’t a developer who skipped a null check. It’s a model improvising around a gap in context. Write test methods that call your invocable method directly with the inputs a confused or under-specified conversation would plausibly produce: blank strings instead of nulls, an Id from the wrong object, a valid Id for a record in the wrong status, duplicate calls for the same intent. If you can, run a handful of adversarial conversations against the actual agent in a sandbox before go-live and watch what it tries to pass. You’ll usually find at least one gap that no amount of prompt engineering would have caught, because the fix was never going to live in the prompt.

    None of this is exotic. It’s the same input-validation discipline you’d apply to any Apex method exposed to something you don’t fully control, whether that’s a REST callout, a Lightning component, or an integration user with a service account. Agentforce just removes the one thing that used to quietly cover for sloppy validation: a human reading the screen before they clicked submit.

  • Zero Copy and Data Cloud: when you actually need it, and when a sync job still wins

    Every Data Cloud conversation I’ve had in the last year eventually turns to Zero Copy. Someone read a blog post, saw a demo, and now wants to know why the team is still running nightly batch jobs to move data into Salesforce when Salesforce says you don’t have to copy anything at all. It’s a fair question. The answer is less exciting than the marketing: Zero Copy is the right tool for a narrow set of problems, and a genuinely bad fit for most of the integration work a typical org still needs.

    I’ll say the quiet part up front. Zero Copy doesn’t mean “no copying happens anywhere.” It means Data Cloud can query data sitting in an external system, typically a data warehouse like Snowflake, Databricks, Redshift or BigQuery, without physically ingesting it into Data Cloud’s own storage first. Federated access, not teleportation. The data still lives where it lived. Salesforce reaches out and reads it on demand, or through a virtualised connection, instead of pulling a copy in and storing it locally.

    What Zero Copy is actually solving

    Three real problems, and they’re the ones worth checking your org against before you touch a connector.

    Storage and duplication cost

    Every record you ingest into Data Cloud takes up space and needs to be kept in sync. If your warehouse already holds ten years of transaction history and you only need the last 90 days visible inside a Salesforce flow, ingesting the full history is wasteful twice over: you’re paying for storage you don’t need, and you’re building a pipeline to keep a copy fresh that nobody asked for.

    Freshness

    Batch sync jobs run on a schedule. Hourly, nightly, whatever the pipeline was built for. If a contact-centre agent needs to see a customer’s current loyalty tier and that tier updates in near real time in the warehouse, a four-hour-old copy in Data Cloud is wrong exactly when it matters. Zero Copy queries the source directly, so the answer reflects what the warehouse has right now, not what it had at the last sync.

    Governance and single source of truth

    Some organisations have a hard rule, often from a previous audit finding, that customer financial or health data cannot be duplicated outside its system of record. Compliance teams like fewer copies, not more. Zero Copy lets Salesforce reference that data for a specific use case without creating another place it has to be secured, retained and eventually deleted.

    Where a sync job still wins

    This is the part that gets skipped in the demos. A traditional ingestion or sync pattern beats Zero Copy in a lot of ordinary situations, and pretending otherwise causes real problems six months later.

    Query volume and latency

    Federated queries go over the network to the source system, every time. If you’re building something that needs to evaluate thousands of records per second, inside a real-time decisioning flow or an Agentforce topic that fires on every page load, round-tripping to an external warehouse for each lookup adds latency you can’t always absorb. Ingested data sitting in Data Cloud’s own storage is fast because it’s local. That’s not a small detail. I’ve seen a well-intentioned Zero Copy design get quietly reverted after performance testing showed response times that were fine in a demo with ten records and unacceptable with ten thousand.

    Transformation needs

    If the data needs real shaping before it’s useful, identity resolution, calculated fields, harmonisation across multiple source systems, a batch pipeline that transforms on the way in is usually simpler to build, test and debug than trying to push that logic into a live federated query. Zero Copy is a read path. It’s not where you want to be doing heavy lifting.

    Source system availability

    A sync job decouples Salesforce from the warehouse’s uptime. If the warehouse has maintenance windows, rate limits, or occasional outages, a Zero Copy dependency means your Salesforce experience goes down with it. A local copy keeps functioning even when the source is briefly unreachable. For anything customer-facing, that resilience is worth more than the storage you save.

    Cost at scale, the other direction

    Zero Copy isn’t free. Federated queries against a warehouse usually cost compute on the warehouse side, and that cost scales with how often Salesforce asks. A high-frequency use case can end up more expensive querying live than it would have been ingesting a well-scoped subset once a day. Nobody puts that in the slide deck.

    A decision framework that actually holds up

    Before picking a pattern, I ask four questions, roughly in this order.

    • How often does this data actually change, and does the use case genuinely need to see it the moment it changes, or is “accurate as of last night” good enough?
    • What’s the query pattern: occasional lookups during a case or interaction, or high-frequency reads across a large population?
    • Does the data need transformation, joining or harmonisation before it’s usable, or is it already clean and query-ready at the source?
    • Is there a governance constraint that actively prohibits a second copy, versus a preference for “less duplication is nicer”?

    If the honest answers are: rarely changes, low query volume, minimal transformation, and no hard governance block, Zero Copy might genuinely save you a pipeline. If any one of those tips the other way, especially the query volume question, a conventional sync is probably the safer build, and it’s the one your team already knows how to monitor and fix at 2am when something breaks.

    What I tell clients

    Zero Copy earns a place in an architecture when it removes a specific, named pain: a duplication rule from compliance, a freshness gap that’s actually been reported as a problem, or a dataset so large that ingesting it was never going to be practical. It’s not a default. I’ve started treating it the way I treat any other integration pattern choice: pick it because the requirements point there, not because it’s the pattern with the most momentum in the ecosystem this quarter. A sync job that’s boring, well-tested and runs every night without surprises is still, for most orgs, the right answer most of the time.

  • Agentforce and PII: what a Salesforce technical lead needs to get right before go-live

    Most PII conversations on a Salesforce project happen before anyone builds anything: field-level security, sharing rules, maybe a Data Mask run before a sandbox refresh. Agentforce breaks that habit. An agent doesn’t just display a field on a page layout where a profile either sees it or doesn’t. It reads fields into a prompt, reasons over them, and sometimes writes them straight into a conversation transcript that sits in your org indefinitely. The access model you already trust stops being the whole story.

    I’ve seen teams get genuinely stuck here, not because the Salesforce security model is unfamiliar, but because it answers a different question than the one Agentforce is asking.

    The access model you have isn’t the access model the agent uses

    Field-level security and permission sets govern what a user can see through the UI and through standard API access. Agentforce actions and topics run under whatever permission the agent’s running user holds, which is often broader than any single human agent needs, because it has to cover every scenario the topic might encounter. A case summarisation action that needs the case description field will usually also have query access to everything else on that object, including fields nobody scoped it to touch.

    That’s the first gap: scope creep in the permission set backing the agent, not a flaw in Agentforce itself.

    Retrieval pulls in more than the page layout ever showed

    Grounding makes this worse in a specific way. When an agent retrieves records to answer a question, it’s not rendering a page layout with field-level security quietly hiding what a profile shouldn’t see. A SOQL query inside an action or a retriever against Data Cloud returns whatever fields the query asks for, and it’s easy to write that query broadly “just in case” during development and never trim it back. I’ve reviewed actions that pulled ten fields off a Contact record to answer a question that needed three. Two of the surplus seven were PII with no business reason to be in that prompt at all.

    Where PII actually leaks once an agent is live

    Three places, in practice.

    Conversation transcripts. Agentforce logs what it reasoned over and what it said, and that log persists. If a customer’s date of birth or a national ID number passed through an action during the conversation, it’s sitting in the transcript object now, subject to whatever retention and access rules you set for that object, which on a lot of orgs is “whatever the default was.”

    Cross-object grounding. An agent answering a billing question might ground against Data Cloud profiles that unify data from several sources. Identity resolution can quietly merge a field that was masked on one system of record with an unmasked copy that arrived through a different ingestion path. Nobody intended that, and nobody will notice until someone asks why a customer’s unmasked phone number showed up in a response from an org where phone numbers are supposed to be protected.

    Third-party model calls. If any part of the topic routes through an external LLM provider rather than staying inside Salesforce’s Trust Layer boundary, you need to know exactly what left the org and under what data processing terms. Most Agentforce deployments stay inside the boundary. Confirm it rather than assume it, especially if a custom action calls out to another service.

    What to actually check before go-live

    This isn’t a theoretical list. It’s what I walk through on a release:

    • Pull the permission set or permission set group backing the agent’s running user and diff it against the fields each topic’s actions genuinely need. Strip the rest.
    • Read every SOQL query inside every action. If it selects more fields than the prompt or the response uses, narrow it.
    • Check the retention policy on conversation transcript objects and who has read access to them. Treat that list the same way you’d treat access to a case history with sensitive notes, because functionally it is one.
    • Test with masked data in a full sandbox refresh before go-live, not a partial copy. An agent behaves differently against realistic volume and realistic messy data than against twelve clean test records.
    • Confirm which calls, if any, leave the Trust Layer boundary, and get that in writing from whoever owns the integration, not just a verbal “yeah it’s fine.”
    • Walk through what happens when the agent gets something wrong with a real customer’s sensitive data mid-conversation. Is there a human escalation path that doesn’t require someone to dig through a transcript to understand what happened?

    Code review needs a new question

    When I review an Agentforce action or a custom Apex class behind one, I’ve added a question I didn’t used to ask: what does this expose that a human doing the same task manually wouldn’t have pulled up? A service rep looking at a case pulls up the case. They don’t typically also load the customer’s full contact record, their order history, and three related cases just to answer one question, because there’s a cost to clicking through all that. An agent pays no such cost. It’ll query everything it’s allowed to query if the action lets it, and that difference in behaviour is exactly where PII exposure creeps in without anyone deciding it should.

    Governance for this doesn’t need to be heavy. It needs an owner. Someone signs off on what each agent action is allowed to touch, and that sign-off gets revisited when the topic changes, not just at initial build. On most projects I’ve worked, that owner is the technical lead, because nobody else is reading both the permission model and the prompt logic closely enough to catch where they disagree.

    None of this is an argument against building with Agentforce. It’s an argument for treating the data model review as part of the build, the same way you’d treat a security review before any other release, rather than something you circle back to once an agent is already answering real customers.

  • Data Cloud ingestion patterns: getting external data into Salesforce without a mess

    Every Data Cloud project I’ve touched hits the same wall about six weeks in. The demo worked because someone fed it clean, pre-shaped data by hand. Then someone asks “can we pull in the real feed from the call centre platform?” and the confident answers get vague fast.

    Ingestion is where Data Cloud projects quietly succeed or fail. Not the Calculated Insights, not the segmentation, not the flashy Agentforce demo sitting on top. The pipe. Get the pipe wrong and everything downstream inherits the mess, except now it’s harder to see because it’s been through a few layers of transformation first.

    Three ways data actually gets in

    Data Cloud gives you three real paths, and most orgs end up using at least two of them whether they planned to or not.

    Batch, via connectors or the Bulk Ingestion API

    This covers the bulk of what lands in Data Cloud: CRM objects through the native Salesforce connector, Marketing Cloud data extensions, files sitting in S3 or Google Cloud Storage, data from a warehouse like Snowflake or Redshift. It’s scheduled, it’s batched, and it’s forgiving if a run is a few minutes late. Good fit for anything that doesn’t need to react to an event the moment it happens: historical case data, product catalogues, loyalty tiers, order history.

    Streaming, via the Ingestion API

    This is the one teams reach for too early. Streaming ingestion exists for data that needs to show up in near real time, things like a web click, an app event, a call just logged in the contact centre. It’s genuinely useful when a Calculated Insight or a segment needs to react within seconds. It’s overkill, and more expensive to run and maintain, when the actual business need is “update it sometime today.” I’ve seen teams build a streaming pipeline for data that gets looked at once a week.

    Native connectors for Salesforce-native sources

    If the data already lives in Sales Cloud, Service Cloud, or Marketing Cloud Engagement, the native connectors do the heavy lifting without custom code. They’re the easiest path and usually the first one worth setting up, because they prove the rest of the model works before you go anywhere near a third-party source.

    The mistake that shows up in almost every org: mapping without modelling

    Data Cloud makes it very easy to wire up a source, map some fields, and watch rows land in a Data Lake Object. That’s the trap. Mapping fields is not the same as deciding what those fields mean in your data model.

    I worked with a contact-centre team that ingested case data from two systems: Salesforce Service Cloud and a legacy ticketing tool that hadn’t been decommissioned yet. Both had a field called “status.” Both got mapped straight into Data Cloud. Nobody stopped to ask whether “Resolved” in one system meant the same thing as “Closed” in the other, or whether they should feed the same Calculated Insight at all. The segment built on top of that data looked fine in testing and was wrong in production, because it was quietly blending two different definitions of “resolved.”

    Before you map a single field, write down what the Data Model Object is actually for, who’s allowed to write to it, and what “done” looks like for the data flowing into it. That document takes an afternoon. Skipping it costs weeks later, usually right after go-live when someone notices the numbers don’t add up.

    Schema drift will happen. Plan for it before go-live, not after

    Source systems change. A field gets renamed, a picklist value gets added, an API version gets deprecated. On a standard integration, that breaks loudly and you fix it in an afternoon. In Data Cloud, a schema change upstream can quietly degrade a mapping, a Calculated Insight, or an identity resolution rule without throwing an obvious error. The pipeline keeps running. It’s just running on slightly wrong assumptions.

    Build monitoring around ingestion volume and null rates, not just success and failure flags. A batch job that “succeeds” but ingests zero rows, or ingests rows with a key field suddenly empty, needs the same attention as a failed job. Most teams only find out about a quiet schema break when someone downstream notices a number that looks off, and by then the bad data has already fed a few cycles of Calculated Insights.

    Identity resolution depends on what you ingested, not what you intended to ingest

    This one trips people up because it feels backwards. You don’t design identity resolution first and then feed it data. You design ingestion carefully, because identity resolution can only work with the identifiers you actually brought in. If the source system’s email field is inconsistently formatted, or a phone number comes through with inconsistent country codes, no amount of matching rule tuning in Data Cloud fixes that at the resolution stage. The fix belongs upstream, in how the data is cleaned on the way in.

    A practical habit: before connecting any new source, list the identifiers it carries (email, phone, loyalty ID, device ID, whatever applies) and check their format against what’s already in Data Cloud. A five-minute check here saves a painful unification exercise three months in, when unified profiles turn out to be quietly duplicating because two sources wrote the same phone number two different ways.

    A sequence that actually works

    Start with one source, ideally a native connector, and get it fully modelled: DMO defined, field meanings documented, identifiers validated. Add monitoring on that single pipeline before adding a second source. Only then bring in the next source, and check its identifiers against the first before mapping a single field.

    It’s slower than connecting everything in week one. It’s also the difference between a Data Cloud instance leadership trusts and one where every number needs a caveat attached. The second kind doesn’t get used for long, however good the dashboards look on day one.

  • Topics, actions, and reasoning: debugging an Agentforce agent that gives the wrong answer

    A client’s contact-centre team deployed an Agentforce service agent to handle “where’s my order” and “update my details” conversations. It worked, mostly. Then it started telling customers their refund had been processed when it hadn’t, and nobody could say why. The agent didn’t crash. It didn’t throw an error. It just answered confidently, and wrong.

    That’s the thing nobody warns you about when you move from Apex to Agentforce. A failed trigger gives you a stack trace. A wrong agent answer gives you nothing. The reasoning that produced it happened somewhere between a topic classifier, a set of instructions, and an action call, and none of those fail loudly. You have to go looking.

    Start with the transcript, not the model

    Before you touch instructions or actions, pull the actual conversation trace. In Agent Builder, the Testing and Activity tab shows you which topic got selected for a given utterance, which instructions were active, which action fired, and what came back from it. This is the single most useful screen in the whole build, and most teams don’t open it until something’s already gone wrong in production.

    Reproduce the exact utterance that caused the bad answer, not a paraphrase of it. Wording matters more than you’d expect. “Has my refund gone through” and “did you process my refund” can route to different topics if your topic descriptions and sample utterances aren’t tight, and a different topic means different instructions and different actions available. Before you assume the model reasoned badly, check whether it was even looking at the right topic.

    Topic misclassification is the usual suspect

    Most wrong answers trace back to the agent picking the wrong topic before it ever starts reasoning about the actual request. I’ve seen “Order Status” and “Return Request” topics both include sample utterances about “where is my order”, because both teams writing them figured it was relevant to their topic too. The classifier has to pick one, and when the boundary is fuzzy, it guesses. Once it’s in the wrong topic, every instruction and action downstream is wrong for the question actually being asked, and the agent has no way of knowing that.

    Fix the overlap directly. Write topic descriptions that describe what’s unique about that topic, not what’s generally true about customer service. Add sample utterances that cover the awkward phrasing customers actually use, not just the clean ones from your requirements doc. And test with utterances deliberately chosen to sit near the boundary between two topics. If you only test the easy cases, you’ll ship a classifier that’s never been asked a hard question.

    Watch for instructions stacking on top of each other

    Global instructions, topic-level instructions, and action-level guidance all apply at once, and they don’t fail loudly when they contradict each other. A global instruction telling the agent to “always confirm identity before discussing account details” combined with a topic instruction that says “answer order status questions directly” can produce an agent that sometimes skips verification, depending on which instruction the model weights more heavily for that phrasing. You won’t see an error. You’ll see inconsistent behaviour that looks random until you lay every active instruction for that topic side by side.

    Action inputs are where reasoning meets reality

    An action’s description isn’t documentation for your team. It’s the thing the model reads to decide whether to call that action and what to pass it. If the description of an input parameter is vague, generic, or copied from a different action, the agent will fill it with its best guess rather than asking the customer to clarify. That’s how you get an action called with the wrong Case Id, or a flow invoked with a date range that doesn’t match what the customer actually asked for.

    Go back through your action and input descriptions the same way you’d review a public API contract. Say exactly what the field is, what format it expects, and when it should be left blank versus when the agent should ask a clarifying question first. If an action can return an error state, make sure that error gets surfaced back to the agent in a way it can act on, not swallowed into a generic failure message that gets smoothed over into a confident-sounding non-answer.

    Grounding gaps don’t look like gaps

    When an agent is grounded on knowledge articles or Data Cloud records and the grounding doesn’t return anything useful, a well-tuned language model doesn’t say “I don’t know.” It produces the most plausible answer it can construct from general pattern, and that answer often sounds exactly as confident as a correctly grounded one. This is the failure mode that worries me most in production, because it’s invisible from the outside. The customer gets a fluent, specific-sounding answer that happens to be fabricated.

    Check what grounding actually returned for the conversation in question, not what you assume it should have returned. If the retrieval came back empty or low-relevance, that’s your answer right there, and the fix is either better source coverage or an explicit instruction telling the agent what to do when grounding comes up empty: ask a clarifying question, hand off to a human, anything other than guessing.

    Build the regression set before you ship the fix

    Once you’ve found the actual cause, whatever you change, rerunning the one failing utterance isn’t enough to call it fixed. Build a small set of utterances that cover the topic boundary you just tightened, the instruction conflict you just resolved, or the action input you just clarified, including a few phrasings you haven’t tried yet. Agentforce’s testing centre lets you run these as a set rather than one at a time, which matters because fixing one misclassification often shifts behaviour on utterances that were working fine before.

    This is the part teams skip under deadline pressure, and it’s exactly the part that catches the fix that solves today’s bug while quietly breaking three others. A service agent giving customers wrong information about their own refund isn’t a UX rough edge. It’s the kind of thing that erodes trust in the channel fast, and it’s worth the extra hour of testing before it goes back out.

  • Agent observability: what to monitor once Agentforce is making decisions in production

    Agentforce doesn’t just answer questions anymore. Give it the right actions and it updates records, raises cases, triggers flows, and replies to customers without a human in the loop. That’s a different risk profile than a chatbot that reads back knowledge articles, and most Salesforce teams are still monitoring it like one.

    I’ve sat through more than one go-live retro where the agent technically worked, every demo passed, every test case went green, and three weeks later someone in support asks why forty cases got auto-closed with the wrong resolution code. Nobody noticed because nobody was watching for it. The agent wasn’t broken. It was doing exactly what it was told, just slightly wrong, over and over, quietly.

    Why your existing monitoring doesn’t cover this

    Debug logs tell you a flow ran. They don’t tell you why the agent picked that flow over three other actions it had access to, or what it decided a customer meant before it acted on it. Standard Salesforce monitoring is built around deterministic systems: a trigger fires, a field changes, you can trace cause to effect in a straight line. An agent’s decision path isn’t straight. It reasons across a prompt, grounds against your data, picks a topic, picks an action, and the only artifact left behind is usually the outcome, not the reasoning.

    That gap is where things go wrong without tripping a single existing alert.

    The failure mode that actually bites teams

    It’s rarely a hard crash. Hard crashes get noticed fast. The failure mode that costs you is the agent that’s technically functioning but drifting: answering slightly outside its intended scope, grounding on stale data, routing to the wrong queue often enough to matter but not often enough to show up in a weekly spot check. Three months in, someone finally asks “has anyone actually been reviewing this?” and the honest answer is no.

    What to actually watch

    Four categories matter more than the rest, and most orgs only build dashboards for one of them.

    Decision accuracy, not just completion

    Did the agent pick the right topic and the right action for the request, not just whether an action fired at all. “Action executed successfully” and “action executed correctly” are two different metrics, and Salesforce’s own logs will happily tell you the first without touching the second. You need a sample review process, human-reviewed, on a regular cadence, checking a slice of transcripts against what the right call actually was.

    Escalation and deflection rates, tracked together

    A rising deflection rate looks great on a leadership slide. It looks a lot less great if escalations to a human are quietly climbing at the same time because the agent is resolving the easy 80% and dumping the hard 20% back on a queue with no context attached. Track both numbers next to each other, not in separate reports that nobody cross-references.

    Grounding and data freshness

    If the agent is pulling from Data Cloud or a knowledge base to answer, you need visibility into what it actually grounded on for a given response, not just that grounding happened. Stale records, duplicate profiles, an out-of-date knowledge article that should’ve been retired last quarter. All invisible until you go looking at the source the agent actually cited.

    Cost and latency per conversation

    Every agent interaction has a token cost and a response time, and both move as usage scales or as someone tweaks a prompt. I’ve seen a single prompt change meant to improve tone quietly double average response length and the per-conversation cost along with it. Nobody noticed for a billing cycle. Put this on the same dashboard as the business metrics, not buried in a separate admin console nobody opens.

    Building the actual observability layer

    You don’t need a new platform bolted on day one. Start with what Salesforce already gives you and get disciplined about using it.

    • Turn on and actually review Agentforce’s built-in conversation transcripts and action logs, not just when something breaks.
    • Build a lightweight Apex or Flow logging layer around custom actions that captures the inputs the agent passed in, not only the result, so a bad decision is traceable back to what triggered it.
    • Stand up a dashboard that puts decision accuracy, escalation rate, and cost per conversation on one screen, reviewed weekly by someone with the authority to change the prompt or the action design, not just report the number.
    • Define what “wrong” looks like before go-live, in writing, with examples. Teams that skip this end up arguing about what counts as a failure after the data’s already ugly.

    None of this needs to be sophisticated to be useful. A spreadsheet with fifty reviewed transcripts a week beats a beautiful dashboard nobody reads.

    What good observability actually buys you

    It’s not a compliance exercise. A team that reviews agent decisions weekly catches a bad prompt change in days instead of discovering it from a customer complaint three months later. It also gives you the evidence to go back to the business and say the agent is handling 40% of a queue correctly, not just handling 40% of a queue. Those are different claims, and only one of them survives scrutiny when leadership asks for proof.

    The teams that get burned by Agentforce in production aren’t usually the ones who built a bad agent. They’re the ones who built a decent agent and then stopped watching it the moment it shipped.

  • Prompt Builder vs Apex: where to draw the line when building an Agentforce action

    The question every Agentforce build runs into

    Every Agentforce action starts with the same decision, and most teams make it by instinct rather than by rule. Someone needs the agent to update a case, calculate a discount, or pull a customer’s order history, and the question lands on the table: do we build this in Prompt Builder, or do we write an Apex class and expose it as an invocable action? Get it wrong early and you end up rebuilding half your actions six months later, usually right after an audit or a production incident forces the question.

    I’ve sat through this conversation more than once on contact-centre builds, and the pattern repeats. Prompt Builder looks faster at first, because it is faster, for the first version. Apex looks like overkill, because for a simple lookup it often is. The real problem is that teams pick one tool and apply it everywhere, instead of treating each action as its own decision.

    What Prompt Builder is actually good at

    Prompt Builder earns its place when the task is genuinely about language: summarising a case history before a handoff, drafting a reply in the right tone, pulling structured fields out of a messy free-text description. These are jobs where a large language model is doing real work, not just passing data through unchanged.

    It also wins on iteration speed. An admin or business analyst can tune a prompt template, test it against sample records, and ship a change without touching a sandbox deployment pipeline. For teams still working out what the agent should actually say, or how it should phrase a recommendation, that speed matters more than architectural purity.

    Where it starts to strain is anywhere the action needs to enforce a rule rather than suggest an answer. Prompt Builder can call a flow or an Apex action to fetch data, but the orchestration logic sitting inside the prompt itself is soft. It’s an instruction the model usually follows, not a rule the platform guarantees. Usually isn’t a word you want anywhere near a discount calculation or an eligibility check.

    A short checklist for Prompt Builder

    • The task is about generating or interpreting language, not applying a business rule
    • Getting it right most of the time is acceptable, and a human reviews or approves the output
    • The logic changes often enough that redeploying Apex every time would slow the team down
    • There’s no regulatory or financial consequence if the output is occasionally wrong

    Where Apex still does the heavy lifting

    Apex actions earn their complexity the moment the task has one correct answer. Calculating a refund amount, checking whether a customer sits inside a cancellation window, validating that a requested change doesn’t breach a contract term: these tasks are deterministic, and deterministic logic belongs in deterministic code.

    Apex also gives you things Prompt Builder can’t match: proper unit tests, governor limit control, bulkification, and a debuggable stack trace when something goes wrong at 2am. When an agent takes the wrong action in production, “the prompt interpreted the instruction differently that day” is not an answer anyone wants to give a client. A failing test case is.

    There’s a security angle too, and it’s the one I push hardest on in design review. An Apex invocable action can validate every input before it touches a record. A prompt can describe constraints in natural language, but it can’t enforce them the way a validation rule or an explicit guard clause can. If an action writes data, changes a financial field, or triggers an external callout, I want code sitting between the agent’s intent and the system of record, not a well-worded instruction hoping the model follows it.

    A short checklist for Apex

    • The action writes data, triggers a callout, or changes system state
    • There’s a rule that has to hold every single time, not most of the time
    • You need test coverage you can point to in a release review
    • Performance matters: bulk processing, governor limits, or high call volume

    The pattern that actually works

    The teams getting this right aren’t choosing one tool over the other. They’re splitting the action into layers. Prompt Builder handles the parts that genuinely benefit from language understanding: pulling intent out of a customer’s message, drafting the response, deciding which of three templates fits the situation. Apex handles everything downstream of that: validating the request, applying the business rule, writing the record, and handing back a clean, structured result the prompt can reference.

    In practice this often looks like a Topic with a handful of actions attached. One or two are prompt-based, doing the language-heavy interpretation. The rest are Apex-backed invocable actions doing the actual work, each one scoped tightly enough that you could explain its whole job in a single sentence. That tight scoping matters more than it sounds. An Apex action that tries to do three things is harder to test, harder to reuse across topics, and harder to reason about once the agent starts calling it in sequences you didn’t design for.

    I’d flag a mistake I see a lot too: writing an Apex action that’s really just a thin wrapper doing nothing an admin couldn’t configure declaratively. If the logic is a handful of field updates and a lookup, Flow still has a place here, sitting between Prompt Builder and full Apex. Reach for Apex when you need tests, complex branching, or callouts. Reach for Flow when the logic is simple enough that the declarative tooling won’t fight you. Reach for Prompt Builder when the job is language, not logic.

    What this means for your next build

    Before building any Agentforce action, ask what happens if it’s wrong. If the cost of a mistake is a slightly off-tone response that a human reviews before sending, Prompt Builder is fine, maybe even ideal. If the cost of a mistake is a refund issued twice or an eligibility check that lets the wrong customer through, the logic belongs in Apex, even if a prompt still handles how the result gets communicated afterwards.

    Draw the line at the point where interpretation ends and consequence begins. Everything upstream of that line, where the agent is working out what the customer actually wants, can live comfortably in Prompt Builder. Everything downstream, where the system commits to an outcome, belongs in code you can test, review, and trust under load.