This project contains a reusable Lightning Web Component (c-agentforce-chat) and Apex facade (AgentforceService) for embedding an Agentforce chat experience anywhere in Lightning Experience.
Welcome to prompt engineering. In Agentforce experiences, outcome quality depends on two levers more than anything else: strong reusable skills and a well-crafted prompt. Skills package domain know-how and business rules into repeatable building blocks that can be user-specific or shared across teams, while a good prompt gives the agent clear intent, constraints, and expected output format. Together, they reduce ambiguity, improve consistency, and minimize hallucinations in production workflows.
Example
Instead of prompting:
"Help me with this customer case."
Use a skill-aware, structured prompt:
@manage-customer-complaints For
recordId{CASE_ID}, act as a senior claims/customer-service specialist.
Classify the issue type and urgency, then produce:
- Root cause hypothesis (with confidence % and missing data needed to confirm),
- Top 3 next actions ranked by business impact and effort,
- A French customer response email (empathetic, compliant, no legal admission, clear next step + ETA),
- A short internal CRM note in English for the account owner,
- Risk flags (regulatory, SLA breach, churn risk) with mitigation actions.
Keep output concise with headings and bullets. If data is missing, state assumptions explicitly instead of inventing facts.
The second version performs better because it combines scoped expertise and business rules (@skill) with explicit instructions for context (recordId), deliverables, language, tone, compliance guardrails, and anti-hallucination behavior.
TL;DR (why this repo exists):
- This repo provides a practical Agentforce client pattern for Salesforce teams.
- It shows how to connect LWC UI, Apex orchestration, and metadata-driven skills into one reusable solution.
- The goal is to make high-quality, governed agent interactions easy to deploy, reuse, and evolve.
- Skills catalog: Skills Index
- Prompting guide: Skills Prompting README
You can also quickly deploy the unmanaged app from this repository to get a working baseline and start customizing immediately (see One-command deploy (recommended)).
The UI is decoupled from direct REST calls. The component calls Apex, and Apex invokes Agentforce through Invocable.Action.createCustomAction(...).
- Reusable chat UI with conversation history and loading state.
- Dynamic agent selection UI backed by configured
BotDefinitionmetadata. - Suggested prompts, retry, copy assistant message, and auto-scroll.
- Session persistence for uncontrolled mode (
sessionStorage). - Public API methods:
sendMessage,clearConversation,focusInput,setSession,addSystemMessage. - Events:
message,response,error,sessionchange,voiceerror. - Markdown rendering with renderer choice and richer formatting support.
- Dedicated
c-voice-inputcomponent for microphone capture with retry/error UX. - Apex response normalization and user-safe error mapping.
The current component implementation supports:
-
Agent invocation via Apex facade
- No direct LWC REST callouts to Agentforce.
- Invokes Agentforce using
Invocable.Action.createCustomAction(...). - Exposes
getConfiguredAgents()to drive the LWC agent picker fromBotDefinition. - Exposes
getConfiguredAgentApiNames()for lightweight validation/verification flows.
-
Agent selection UX
agentApiNamecan be preconfigured or selected at runtime from available agents.- Optional collapsible picker section with agent details (label, description, freshness by
LastModifiedDate). - Prevents message send until an agent is selected, with user-safe system guidance.
- Persists selected agent and conversation/session history using the resolved selected agent key.
-
Conversation UX
- Send message with keyboard shortcut (
Enter) and send button. - Loading/typing indicator while waiting for response.
- Assistant message copy action.
- Retry last failed message.
- Auto-scroll behavior for active conversation flow.
- Send message with keyboard shortcut (
-
History and session handling (browser-backed)
- Session persistence for uncontrolled mode.
- Multiple browser-stored conversations per
agentApiName. - New chat creation.
- Conversation switcher (reopen previous messages).
- Delete conversation history entry.
-
Prompt and context support
suggestionsarray input.samplePromptsstring input (newline/semicolon/pipe parsing).- Record page context support via
recordId. @skillmentions resolved from custom metadata (Coworker_Skill__mdt) viaCoworkerSkillsController.
-
Skills custom metadata integration
- Skills loaded from
Coworker_Skill__mdtusingCoworkerSkillsController.getSkills(). - Uses
MasterLabelas mention label andContent__cas resolved prompt content. - Supports multi-word skill labels and
@autocomplete in the composer.
- Skills loaded from
-
Rendering
- Plain text mode using
lightning-formatted-textwithlinkify. - Markdown mode with renderer strategy:
richText:lightning-formatted-rich-textcompatibility renderer.custom: dedicatedc-markdown-viewer.
- Custom markdown supports:
- headings, bold/italic, inline/fenced code
- ordered and unordered lists
- blockquotes
- horizontal rules (
---,***,___) - tables (GFM-style pipe tables)
- collapsible heading sections (enabled by default) with chevron indicators
- Salesforce record-link rendering:
- detects Lightning record URLs
- shows record chip-style links
- uses
NavigationMixin(standard__recordPage) to keep navigation in-app/console-friendly
- Citation list rendering when citations are present.
- Plain text mode using
-
Voice input
agentforceChatdelegates microphone functionality toc-voice-input.- Browser-native speech recognition with:
- secure-context and support checks
- actionable error messages (permission, network, language, device)
- automatic one-shot retry for transient network errors
- manual "Try Again" action
- Emits transcript updates to composer and forwards voice errors via
voiceerror.
-
Public API and events
- Methods:
sendMessage,clearConversation,focusInput,setSession,addSystemMessage. - Events:
message,response,error,sessionchange.
- Methods:
-
Coworker skills discovery UX
- Category filter options are generated dynamically from loaded skill metadata.
- Cards render in a responsive grid using container queries for better App Builder embedding.
- Category card icon rendering is data-driven (
skill.icon) and omitted when unavailable.
- LWC bundle:
force-app/main/default/lwc/agentforceChat - LWC bundle:
force-app/main/default/lwc/coworkerSkills - LWC bundle:
force-app/main/default/lwc/markdownViewer - LWC bundle:
force-app/main/default/lwc/voiceInput - Apex service:
force-app/main/default/classes/AgentforceService.cls - Skills controller:
force-app/main/default/classes/CoworkerSkillsController.cls - Skills metadata type:
Coworker_Skill__mdt
Common c-agentforce-chat properties used in recent updates:
agentApiName(optional preselection; can be chosen from configured agents at runtime)markdownEnabled(trueto enable markdown rendering)markdownRenderer(customorrichText)voiceInputEnabled(trueto show microphone capture)voiceLanguage(for exampleen-US,fr-FR)minHeight/maxHeight(CSS values likeauto,none,24rem,70vh)showHeader,showAvatar,maxHistory,showCitations
The chat supports skill mentions in user messages, for example:
@Analyse mon portefeuille client
When a message is sent:
- The component loads skills from Apex (
CoworkerSkillsController.getSkills). - It matches
@labelin the user message against skill labels (MasterLabel). - It replaces each matched mention with the skill body (
Content__c) before callingAgentforceService.
Skills are read from custom metadata records of type Coworker_Skill__mdt.
Minimum required fields per record:
MasterLabel(used as the@labelmention text)Content__c(resolved text inserted into the prompt sent to the agent)
Optional fields (not required by resolution logic, but may be useful operationally):
Category__cExpected_Result__cAgents__cDeveloperName
- Type
@in the composer to open skill autocomplete. - Multi-word labels are supported.
- For user messages with resolved skills:
- the
@mentionis visually emphasized in the chat bubble, - a bold
Resolvedstatus is shown in message meta, - hovering
Resolvedshows tooltip content, - clicking
Resolvedopens a modal with full resolved prompt text.
- the
If Apex skill loading is unavailable, the component can still parse optional skillsJson (if provided) using:
labelcontent__c
In AgentforceService, the invocable call uses:
Invocable.Action action = Invocable.Action.createCustomAction(
ACTION_TYPE,
null,
agentApiName,
'1.1.0'
);Current constants:
ACTION_TYPE = 'generateAiAgentResponse'ACTION_NAME = 'generateAiAgentResponse'(kept in class for compatibility/readability)
Agentforce output in this org can arrive as:
response(plain text), oragentResponse(JSON string), where text is atvalue.message.
AgentforceService handles both formats.
Install dependencies:
npm installLint LWC:
npx eslint "force-app/main/default/lwc/**/*.js"Run Jest:
npm run test:unitRun Apex test in org:
sf apex run test --target-org "<org-username-or-alias>" --tests "AgentforceServiceTest" --result-format json --code-coverage --wait 30Install the unmanaged package directly in your org:
After opening the link:
- Sign in with the org where you want to install the package.
- Review and accept the package access/security prompts.
- Choose install scope (recommended: Install for Admins Only first).
- Complete installation and wait for confirmation.
- Open Lightning App Builder in the target org.
- Add Agentforce Chat (
c-agentforce-chat) to the page. - Set
agentApiNameto your Agentforce agent API name. - Optionally configure prompts, markdown, voice input, and history settings.
Use this sequence for predictable deployments.
sf org login web --alias "<org-alias>"
sf org display --target-org "<org-alias>"sf project deploy start \
--dry-run \
--target-org "<org-alias>" \
--source-dir "force-app/main/default/classes/AgentforceService.cls" \
--source-dir "force-app/main/default/classes/AgentforceService.cls-meta.xml" \
--source-dir "force-app/main/default/classes/CoworkerSkillsController.cls" \
--source-dir "force-app/main/default/classes/CoworkerSkillsController.cls-meta.xml" \
--source-dir "force-app/main/default/objects/Coworker_Skill__mdt" \
--source-dir "force-app/main/default/layouts/Coworker_Skill__mdt-Coworker Skill Layout.layout-meta.xml" \
--source-dir "force-app/main/default/classes/AgentforceServiceTest.cls" \
--source-dir "force-app/main/default/classes/AgentforceServiceTest.cls-meta.xml" \
--source-dir "force-app/main/default/lwc/agentforceChat" \
--source-dir "force-app/main/default/lwc/markdownViewer" \
--source-dir "force-app/main/default/lwc/voiceInput" \
--wait 30 \
--jsonsf project deploy start \
--target-org "<org-alias>" \
--source-dir "force-app/main/default/classes/AgentforceService.cls" \
--source-dir "force-app/main/default/classes/AgentforceService.cls-meta.xml" \
--source-dir "force-app/main/default/classes/AgentforceServiceTest.cls" \
--source-dir "force-app/main/default/classes/AgentforceServiceTest.cls-meta.xml" \
--wait 30 \
--jsonsf project deploy start \
--target-org "<org-alias>" \
--source-dir "force-app/main/default/lwc/agentforceChat" \
--source-dir "force-app/main/default/lwc/markdownViewer" \
--source-dir "force-app/main/default/lwc/voiceInput" \
--wait 30 \
--jsonsf apex run test \
--target-org "<org-alias>" \
--tests "AgentforceServiceTest" \
--result-format json \
--code-coverage \
--wait 30- Open the org:
sf org open --target-org "<org-alias>" - Go to Lightning App Builder.
- Add Agentforce Chat (
c-agentforce-chat) to a page. - Configure at minimum:
agentApiName(for exampleCRM_Assistant)- optional
samplePrompts - optional
skillsJson(only as fallback / small local config) - optional
showHeader,showAvatar,maxHistory
This project includes a manifest that contains all required metadata for the chat component:
manifest/package-agentforce-chat.xml
Deploy everything with one command:
sf project deploy start \
--target-org "<org-alias>" \
--manifest "manifest/package-agentforce-chat.xml" \
--wait 30 \
--jsonOptional dry-run validation:
sf project deploy start \
--dry-run \
--target-org "<org-alias>" \
--manifest "manifest/package-agentforce-chat.xml" \
--wait 30 \
--jsonThese helper scripts are included under scripts/:
verifyAgentforceService.apexverifyAgentforceInvoker.apexverifyGetConfiguredAgentApiNames.apexinspectInvocableResult.apex
Example:
sf apex run --target-org "<org-username-or-alias>" --file "scripts/verifyAgentforceService.apex" --jsonThe repo includes a Lightning Out bridge to render the chat component in Salesforce Classic:
- Aura host component:
force-app/main/default/aura/AgentforceChatHost - Lightning Out app:
force-app/main/default/aura/AgentforceClassicOutApp - Visualforce page:
force-app/main/default/pages/AgentforceClassic.page
Open the page in Classic with a record id:
/apex/AgentforceClassic?id=<RECORD_ID>
Example:
/apex/AgentforceClassic?id=001XXXXXXXXXXXXXXX
Contributions are very welcome. If you want to improve the component, please open a pull request with:
- a short description of the problem and approach,
- tests for behavior changes (Jest and/or Apex as applicable),
- and deployment notes when metadata shape changes.
Suggested workflow:
- Create a feature branch.
- Implement changes in small, reviewable commits.
- Run lint and tests locally.
- Open a PR with screenshots or short notes for UI changes.
If you have ideas but not a full implementation, opening an issue with repro steps is also appreciated.
This project is licensed under the MIT License. See LICENSE for details.
