sshvarzman-salesforce/claude-skills

building-voice-asa-agent

End-to-end playbook for building a voice Agentforce Service Agent (ASA) in a demo org — the full sequence from agent-user perm set, data seeding, system-context data flows, prompt template, agent bundle authoring, live-preview checkpoint, publish/activate,…

Voir la source

Mode d’emploi

  1. Copiez la commande et examinez le paquet avant de l’installer dans votre environnement agent.
  2. Consultez le document original pour les outils, identifiants et environnements requis.
  3. Décrivez les entrées et le livrable souhaité ; comparez la sortie aux sources.
npx skills add https://github.com/sshvarzman-salesforce/claude-skills --skill building-voice-asa-agent

Avant de commencer

  • L’installation n’inclut ni abonnement au modèle, ni quota API, ni accès aux fichiers.
  • Étoiles et installations reflètent l’activité, pas un test indépendant de qualité ou de sécurité.
Document Skill original

Rendu depuis le dépôt source en conservant titres, exemples, code, tableaux, liens et images.

Building a Voice ASA Agent — End-to-End Playbook

What this skill is for

This is the spine of a voice Agentforce Service Agent build. It orders the pieces and points to the deep-dive skills for each. Use it when the ask is "build a voice service agent that looks up a customer's records, opens/creates something, answers FAQs, and escalates to a human."

Proven pattern (BWAMASASean → ClaimSecure_ASA): hub-and-spoke router, hardcoded `DemoContactId`, verification gate, prompt-template-over-PromptFlow for reads, autolaunched flow for writes, `@utils.escalate`, per-channel escalation flows, inbound omni-flow that hands the call to the bot.

Companion skills (read these for the details)

  • `developing-agentforce` — the authoritative agent lifecycle (generate bundle → write .agent → validate → preview → publish → activate). This playbook does NOT replace its Required Steps; it sequences the surrounding infra.
  • `building-system-context-agent-data-flows` — the read/write/verify flows (all SystemModeWithoutSharing), and the PromptFlow empty-results bug + fix.
  • `scoping-agent-user-permissions` — the agent-user perm set (object Read-only, VA/MA=0, full FLS).
  • `connecting-agent-data-library` — the GeneralFAQ subagent + shared flex template for knowledge/citations.
  • `connecting-channels-to-asa` — the full voice + messaging story: inbound omni-flow → ASA and escalation ASA → queue/skill/direct-to-rep, and exactly what binds in metadata vs. Setup.
  • `replicating-omnichannel-routing-flows` — the escalation RoutingFlows (voice + messaging) and channel binding.
  • `building-contact-center-kpis-agentwork` — AgentWork-driven KPIs (Accepted-by-Human with genuine-human gating, Abandoned, Speed-to-Answer) + the VoiceCall/MessagingSession custom fields.
  • `calling-prompt-templates-in-flows` — template↔flow wiring.
  • `handling-locked-standard-objects` — if the demo object is a locked industry object (e.g. Claim).

Standing user requirements (apply on every build here)

  • Prefix everything with the demo name (agent, flows, template, perm set).
  • All agent-action / prompt-template flows run system context (SystemModeWithoutSharing).
  • Agent user: no ViewAll/ModifyAll on any object; object Read-only + full field FLS.
  • Every new field on all page layouts with FLS read+write for all profiles.
  • Related-record access via the junction first (e.g. Contact→Claim always through ClaimParticipant).
  • Always --json; always --target-org <the demo org> (the global default is usually a different org).

Two hard rules for actions (learned the hard way — do NOT violate)

RULE 1 — Prompt templates are for READING data ONLY. Never for writes. A generatePromptResponse:// action (prompt template + PromptFlow) may only read/summarize data. It must never create, update, or delete a record. The template runs in a generative/data-provider context, not a transactional one — a "write" expressed through a template either silently does nothing or behaves nondeterministically.

  • Reads (pension summary, claim status, "what are my AVCs") → prompt template over a system-context PromptFlow. ✅
  • Writes (update AVC amount, purchase a buyback, verify a member, create a case) → a real autolaunched Flow (flow://…, SystemModeWithoutSharing, Active) that returns the result var. ✅
  • If you find yourself wanting a prompt template to "confirm and save" — stop. That's a Flow. (Real OMERS bug: the member-verify step was a prompt template; it had to be rebuilt as an autolaunched Flow returning VERIFIED, with the agent setting isVerified on the flow's output.)

RULE 2 — Every agent action needs at least ONE input parameter. Zero-input actions are invalid. An ASA action that declares no inputs is rejected by the platform — the Reset/Simulator reports "The action `<Name>` is missing the input parameters…" even though nothing is technically "missing." The fix is to give every action ≥1 declared, genuinely-passed input.

  • For a data action that conceptually needs no runtime input (it hardcodes the demo persona), add a required `Input:ContactID` wired end-to-end and bound to a hardcoded ASA variable:
  • .agent variables block: DemoContactId: mutable string = "<hardcoded Contact Id>" (visibility Internal).
  • .agent action inputs: — "Input:ContactID": string (is_required: True, complex_data_type_name: "lightning__textType").
  • .agent reasoning binding — with "Input:ContactID" = @variables.DemoContactId.
  • Flex template <inputs> — apiName=ContactID, referenceName=Input:ContactID, definition=primitive://String, required=true; plus a <templateDataProviders><parameters> map contactId = {!$Input:ContactID}.
  • Backing PromptFlow — an isInput String var contactId. (The subflow can still hardcode the persona; the input exists to satisfy the ≥1-input contract and is really declared/passed, so demo behavior is unchanged.)
  • Adding an input to a Published flex template must be `required=false` on the template parameter side + bump `versionIdentifier`/`activeVersionIdentifier` — see the flex-template-input-add-required-false memory.
  • Flow actions use bare input names (no Input: prefix) matching the flow's isInput var names, and output the flow's output var (not promptResponse).

Loading / progress messages on actions (what the caller hears while an action runs)

An action can show a short "please hold…" message while it runs, so the caller isn't left in dead air — essential for voice (a flow/prompt-template call can take a few seconds). The OOB knowledge action already does this ("Getting answers"); add the same to every custom action you build.

In the `.agent`, two attributes at the ACTION-DEFINITION level — siblings of label/description/target/inputs/outputs, placed at the END of the action's definition block (after outputs:):

    actions:
        Get_Program_Account:
            label: "Get Program Account"
            description: "…"
            target: "generatePromptResponse://Synchrony_Get_Program_Account"
            inputs:
                "Input:ContactID": string
                    …
            outputs:
                promptResponse: string
                    …
                    filter_from_agent: False
            include_in_progress_indicator: True
            progress_indicator_message: "Please hold while I check on your program account"
  • include_in_progress_indicator: True turns the indicator on for this action; progress_indicator_message is the exact text the agent surfaces while the action runs. Both are action-level — do NOT put them inside inputs:/outputs: or on a subagent.
  • Works for both flow:// (write/verify) and generatePromptResponse:// (read) actions. Verified on SynchronyASA v4: `GetProgramAccount` → *"Please hold while I check on Program Account"*, `RequestCreditLimitIncrease` → "Please hold while I request Credit limit increase".
  • Write the message in the agent's first-person voice ("Please hold while I …", "One moment while I …") — it is spoken/shown to the customer, not an internal label. Keep it short.
  • The OOB AnswerQuestionsWithKnowledge block is the reference shape (it carries require_user_confirmation: False, include_in_progress_indicator: True, progress_indicator_message: "Getting answers", then source:/target:).

Decomposed-bundle equivalent (if you hand-edit the `GenAiPlannerBundle` XML instead of the `.agent`): the per-topic/per-action <localActions> block carries <isIncludeInProgressIndicator>true</isIncludeInProgressIndicator> and <progressIndicatorMessage>…</progressIndicatorMessage> (that's what the .agent attributes compile to).

Preserve-on-publish gotcha. Each sf agent publish cuts a NEW numbered planner bundle (<Agent>_v1, _v2, …); the live content is always the highest-numbered one. If the user added loading messages (or any edit) in the Agent Builder UI, those live ONLY in the org's current bundle — re-publishing from your older local `.agent` will silently overwrite them. Before any re-publish, retrieve the live top-numbered GenAiPlannerBundle:<Agent>_vN, base64-decode its agentScript/*.agent, and reconcile your local source first. (Retrieving the pinned _v1 name gives you the stale original, not the live version.)

Build sequence

0. Design + Agent Spec (HARD GATE). Draft the Agent Spec (identity, variables incl. hardcoded DemoContactId + linked VoiceCallId @VoiceCall.Id, subagent graph, each action's target/inputs/outputs, verification factors, modality/connections). Save it as a file and STOP for explicit user approval. No .agent/flow code before approval.

1. Agent-user perm set. Build + deploy + assign per scoping-agent-user-permissions. Do this early so previews have access.

2. Seed demo data. Anonymous Apex creating the records the agent will read (and their junction rows — e.g. Claim + ClaimParticipant with Roles). Capture Ids. If the object is a locked standard object, pack structured detail into a long-text field (handling-locked-standard-objects).

3. Read action. Author + deploy the system-context subflow (privileged SOQL) + the thin PromptFlow wrapper + the flex prompt template, per building-system-context-agent-data-flows. Test the template resolves against the seeded persona. This is where the empty-results bug shows — verify real rows come back.

4. Write + verify actions. Author + deploy the autolaunched create/update flow and the verify-identity flow (both SystemModeWithoutSharing, Active). Smoke-test create returns a real record number; verify returns true for the correct factors, false otherwise.

5. (Optional) FAQ / data library. Add the GeneralFAQ subagent reusing a published flex template per connecting-agent-data-library.

6. Author the agent bundle.

bash
sf agent generate authoring-bundle --json --no-spec --name "<Label>" --api-name <Api_Name> --target-org <org>

Write the .agent from the approved spec: start_agent router (model sfdc_ai__DefaultEinsteinHyperClassifier), verification subagent gating on isVerified with currentTopic round-trip, the records subagent owning read+write actions, GeneralFAQ, escalation (@utils.escalate), offtopic + ambiguousquestion guardrail subagents (copy verbatim from the reference agent), modality voice, and the two connection blocks (see step 9).

7. Validate → preview (CHECKPOINT).

bash
sf agent validate authoring-bundle --json --api-name <Api_Name> --target-org <org>   # zero errors
sf agent preview start --json --use-live-actions --authoring-bundle <Api_Name> --target-org <org>
sf agent preview send --json --authoring-bundle <Api_Name> --session-id <ID> -u "<utterance>" --target-org <org>

Exercise every capability: verify (correct + wrong factors), list records, detail on one, create a new one, FAQ, escalate. Read the traces to confirm routing + action I/O. STOP for user approval before publishing.

8. Publish + activate.

bash
sf agent publish authoring-bundle --json --api-name <Api_Name> --target-org <org>
sf agent activate --json --api-name <Api_Name> --target-org <org>
sf agent preview start --json --api-name <Api_Name> --target-org <org>   # verify the published (not bundle) version

9. Inbound + escalation routing flows.

  • Inbound omni-flow (RoutingFlow): Get_VoiceCall(recordId) → assign FromPhoneNumber → findMatchingIndividuals(searchFields=Phone, searchObject=Contact) → Get_Contact(Id In contactIds) → Update_VC (Contact__c + RelatedRecordId = Contact.Id) → routeWork routingType=Copilot, copilotId setupReference=<Agent>/setupReferenceType=BotDefinition, queueId <voice queue>, serviceChannelDevName sfdc_phone. Deploy Active.
  • Escalation flows are PER-CHANNEL (see replicating-omnichannel-routing-flows):
  • connection telephony.outbound_route_name = "flow://<Escalation_Voice>" → updates VoiceCall, routes sfdc_phone → voice queue.
  • connection messaging.outbound_route_name = "flow://<Escalation_Messaging>" → updates MessagingSession, routes sfdc_livemessage → messaging queue.
  • Do NOT point messaging at the voice flow — messaging gets a MessagingSession recordId, not a VoiceCall Id; it must use the messaging flow or it fails at runtime. (This was a real, user-caught bug.)

10. Setup handoff (voice binding is Setup-only). Voice-channel inbound-flow + escalation-flow bindings (SessionHandlerId/FallbackQueueId on the voice channel) are NOT API/metadata-settable — hand the user exact Setup steps: Setup → the Service Cloud Voice channel → set inbound flow → <Inbound_Omniflow> and escalation flow → <Escalation_Voice>. Messaging escalation, by contrast, binds in the agent metadata (step 9) and takes effect on republish.

Voice: making the agent SPEAK identifiers correctly (hard-won)

A voice agent reads any numeric string as a quantity — certificate number 33556 comes out "thirty-three thousand five hundred fifty-six," never "3-3-5-5-6." Formatting-only instructions ("read digit by digit") are unreliable; the model still quantifies. Two fixes, applied together:

1. Emit a pre-spaced digit string from the backing flow, and read it verbatim. Have the verify/read flow return an extra String output that is the identifier with a space after every digit, built with nested SUBSTITUTE inside a TRIM:

TRIM(SUBSTITUTE(SUBSTITUTE( ... SUBSTITUTE(TRIM({!certificateNumber}),
"0","0 "),"1","1 ") ... "9","9 "))

(one SUBSTITUTE per digit 0–9). Add a corresponding isOutput=true String var (e.g. certificateSpoken), assign the formula to it in every success branch, and declare it as an action output in the .agent. Instruction to the agent: "Read the `certificateSpoken` string back to the customer exactly as given — do not reformat or interpret it as a number." The agent voices "3 3 5 5 6" as digits. This works for any digit-only identifier (cert #, claim #, member ID).

2. Verify-then-speak ordering — NEVER recap a captured number before the action runs. The subtler bug: the agent recites the raw transcript number before verification, because the pre-verification recap ("Let me confirm — your certificate is 33556, verifying now…") voices the number before the flow's spaced output exists. Fix the verification subagent so that, on capturing DOB + identifier, it calls the verify action immediately with no recap and no "I'm about to verify you", and speaks only after the action returns — leading with the success line (e.g. "I've verified you successfully, {contact name}", name from the flow output) and reading the spaced string if it must read the number at all. Same principle for a submit/confirm step: one confirmation, then call create immediately — don't gate twice.

Verification checklist

  • Perm set assigned to the Einstein Agent User; object Read-only + full FLS; VA/MA=0.
  • All action flows carry <runInMode>SystemModeWithoutSharing</runInMode> (the PromptFlow wrapper is the intentional exception).
  • Read action returns real seeded rows in live preview (empty-results bug ruled out).
  • Verify returns true/false correctly; create returns a real record number.
  • Agent validates clean, previews correctly for every capability, publishes, activates; published-version preview works.
  • Both RoutingFlows Active (FlowDefinitionView ProcessType=RoutingFlow, IsActive=true, standard API — not --use-tooling-api); inbound copilotId resolves to the agent; escalation queueId resolves to the right queue.
  • Per-channel escalation confirmed: telephony→voice flow, messaging→messaging flow.

Gotchas (the ones that actually cost time)

  • Prompt template used for a write = silent no-op / nondeterministic. Writes are always autolaunched Flows. (See Rule 1 above.)
  • `"The action <Name> is missing the input parameters…"` in the Simulator = that action declares zero inputs. Add a required Input:ContactID bound to the hardcoded DemoContactId var, wired through template + PromptFlow. (See Rule 2 above.)
  • Empty read results = SOQL is in the PromptFlow (user context), not the subflow. Move it. (building-system-context-agent-data-flows.)
  • Messaging escalation pointed at the voice flow = runtime failure. Per-channel, always.
  • Voice binding isn't metadata — it's a Setup handoff. Don't promise it via deploy.
  • `FlowDefinitionView` is standard API, columns ApiName/ProcessType/IsActive. Querying it --use-tooling-api throws INVALID_TYPE.
  • Base a new agent version on the ACTIVE version — retrieve the activated bundle before editing (see memory: always start from the active version).
  • Editing a Published prompt template's content requires bumping versionIdentifier + activeVersionIdentifier, or the change won't take.
  • Voice agent voices identifiers as quantities — fix in the flow (spaced-digit string output) + verbatim-readback instruction, not in prompt formatting alone. See the "Voice: making the agent SPEAK identifiers correctly" section.
  • Agent recites a number before verifying = the verification subagent has a pre-verification recap. Remove it: capture → call verify immediately → speak only after the action returns.
  • `duplicate value found: <unknown>` on a Capability PromptFlow deploy = two channel-resolution record lookups (VoiceCall + MessagingSession) each with storeOutputAutomatically, not queriedFields/EndsWith. Collapse to a single lookup (hardcode the contact, drop channel resolution) and it clears. (memory: promptflow-queriedfields-duplicate.)

Tâches connexes

du même dépôt

Autres Skills

Tous les Skills
sshvarzman-salesforce
Communauté

activating-datacloud

Salesforce Data Cloud Act phase. Use this skill when the user manages activations, activation targets, data actions, or downstream delivery of Data Cloud audiences and data. TRIGGER when: user manages activations, activation targets, data actions, or downstream delivery of Data Cloud audiences and data. DO NOT TRIGGER when: the task is segment creation (use segmenting-datacloud), data retrieval/search work (use retrieving-datacloud), or STDM/session tracing (use observing-agentforce).

installations
1
GitHub Stars
0
Mis à jour
15 oct.
sshvarzman-salesforce
Communauté

agentforce-skills-research

Research Agentforce configuration patterns — topics, actions, instructions, CLTs, knowledge, planner behavior. Searches Salesforce docs, Trailhead, internal repos (git.soma), Slack channels, and Google Docs to find working examples, known issues, and best practices.

installations
1
GitHub Stars
0
Mis à jour
15 oct.
sshvarzman-salesforce
Communauté

harmonizing-datacloud

Salesforce Data Cloud Harmonize phase. Use this skill when the user works with DMOs, mappings, relationships, identity resolution, unified profiles, data graphs, or universal IDs. TRIGGER when: user works with DMOs, mappings, relationships, identity resolution, unified profiles, data graphs, or universal IDs. DO NOT TRIGGER when: the task is only about streams/DLOs (use preparing-datacloud), segments/insights (use segmenting-datacloud), retrieval/search (use retrieving-datacloud), or STDM/sessio

installations
1
GitHub Stars
0
Mis à jour
15 oct.
sshvarzman-salesforce
Communauté

preparing-datacloud

Salesforce Data Cloud Prepare phase. Use this skill when the user creates or manages Data Cloud data streams, DLOs, transforms, or Document AI configurations. TRIGGER when: user creates or manages Data Cloud data streams, DLOs, transforms, or Document AI configurations, or asks about ingestion into Data Cloud. DO NOT TRIGGER when: the task is connection setup only (use connecting-datacloud), DMOs and identity resolution (use harmonizing-datacloud), or query/search work (use retrieving-datacloud)

installations
1
GitHub Stars
0
Mis à jour
15 oct.