The Prompt Template Dilemma
As we integrate Agentforce into our enterprise workflows, one of the most common friction points developers face is retrieving unstructured data. When you build a Prompt Template in Salesforce, you naturally expect to select the main Input Object and pull in associated records. However, when you try to reference ContentDocument, ContentVersion, or the legacy Note and Attachment objects, you often find that the Prompt Builder resource picker does not expose these directly.
This happens because Prompt Builder is optimized for querying relational data through the standard Data Graph layer. Because Notes and Attachments (and the newer Files/ContentDocument structure) are stored via polymorphic relationships and system-level joins, they aren't automatically presented as "Related Lists" in the template editor. In this guide, we’ll explore how to overcome this limitation and feed that critical unstructured data into your LLM.
The Architecture Limitation: Why Prompt Builder Misses Files
Prompt Builder relies on the Einstein Trust Layer to fetch context. When you use the "Template" feature, the system generates a SOQL query behind the scenes to fetch the fields defined in your template. The ContentDocumentLink object, which connects an Account or Case to a file, is a junction object. Unlike a standard lookup field (e.g., Contact.AccountId), Prompt Builder’s UI does not support traversing these complex junction structures natively in the prompt preview tool.
If your goal is to summarize a Case using the latest PDF attached to it, you cannot simply click "Insert Resource" and expect to see "Related Notes." You must intervene at the data retrieval layer.
Strategy 1: Using Apex-Defined Resources
The most robust way to solve this is by creating an Apex-defined Prompt Resource. This allows us to write a custom class that handles the ContentDocumentLink query, extracts the text content, and passes it to the prompt.
First, we create an Invocable method to fetch the content:
public class PromptContentRetriever {
@InvocableMethod(label='Get Latest Attachment Text' description='Fetches text from latest file')
public static List<String> getAttachmentContent(List<Id> recordIds) {
List<String> results = new List<String>();
for (Id recordId : recordIds) {
// Fetch the most recent document link
ContentDocumentLink link = [SELECT ContentDocument.LatestPublishedVersion.VersionData
FROM ContentDocumentLink
WHERE LinkedEntityId = :recordId
ORDER BY SystemModStamp DESC LIMIT 1];
if (link != null) {
results.add(link.ContentDocument.LatestPublishedVersion.VersionData.toString());
} else {
results.add('No attachments found.');
}
}
return results;
}
}
Once deployed, go to Prompt Builder and select Add Resource. Choose Apex and select your PromptContentRetriever. You can now drag this resource into your template body. The LLM will now receive the raw string of the attachment content directly in its context window.
Strategy 2: Data Cloud and Unstructured Data
For enterprise-scale Agentforce implementations, relying on Apex for every file can create maintenance overhead. This is where Data Cloud shines. Instead of querying local Salesforce objects, we ingest the ContentVersion records into Data Cloud.
- Data Stream: Create a Data Stream for the
ContentVersionobject. - Data Model Mapping: Map the
VersionDatato a text-based attribute in the Data Model. - Search Index: Enable the Data Cloud Vector Database (if using RAG) or a standard lookup.
- Prompt Builder Integration: Use the Data Cloud resource type in your prompt template.
By indexing these files as unstructured text in Data Cloud, the Agentforce prompt can perform a vector search to find relevant information across thousands of files, rather than being restricted to the "latest" attachment.
Implementing a "Safe" Fallback
When dealing with AI, context is everything. You should never assume an attachment exists. When building your prompt template, always use a fallback condition to prevent the LLM from hallucinating when no attachment is found.
In your prompt template configuration, structure your instruction as follows:
"Review the following context provided from the Case record.
Case Details: {{Case.Subject}} / {{Case.Description}}
{IF: {!AttachmentResource} != 'No attachments found.'} Additional File Context: {!AttachmentResource} {ELSE} No file attachments were available for analysis. Proceed using only Case Details. {ENDIF}"
Key Takeaways
- Don't search for native support: Prompt Builder does not natively "see" Files or Notes via the UI because of the complex polymorphic nature of
ContentDocumentLink. - Use Apex-Defined Resources: For simple, direct-access needs, write an
@InvocableMethodto queryContentVersionand return the string content to your template. - Scale with Data Cloud: If your agents need to parse large volumes of historical documents, migrate your file ingestion to Data Cloud and use it as a vector-ready resource.
- Handle Nulls: Always implement logic in your template instructions to handle scenarios where the attachment retrieval returns empty data; this reduces AI hallucination significantly.
- Permissions Check: Ensure your Agentforce user profile has 'View All' or appropriate access to
ContentDocumentrecords, otherwise the background query will return an empty set.
Leave a Comment