Agent configuration
AI Assistant agents expose a configuration that can drive the agent behavior at runtime. This guide explains how to use the key fields — systemPromptTemplate, seedMessages, userId, toolApproval, and skills — and when to reach for each one.
Before configuring the agent, select an agent that matches your use case. The agent determines the base capabilities, and the configuration options in this guide let you customize behavior further.
Agent configuration overview
The agent configuration is passed as part of the initial options when configuring AI Assistant in the SDK. The following example shows this as part of Nutrient Web SDK:
PSPDFKit.load({ aiAssistant: { ..., agentConfiguration: { systemPromptTemplate: <systemPromptTemplate>, seedMessages: <seedMessages>, userId: <userId>, toolApproval: <toolApproval>, skills: <skills>, } }, ...});systemPromptTemplate— High-level system prompt used to set overall voice and operating context.seedMessages— Messages injected on every model invocation (after the system prompt, before history) to prime early responses; not shown directly to users and not persisted.userId— Optional identifier for rate limiting and authorization.toolApproval— Controls which tools are available and whether they require user approval before execution.skills— Optional, on-demand instructional snippets the agent can load to enrich context without inflating every request.
All of these fields are optional; sensible defaults apply when you omit them.
System prompt template
Use systemPromptTemplate to encode durable, high-level context about an agent — for example, the industry you operate in, compliance tone, and the common operations the agent should prioritize. Keep this template concise and stable so it acts as the north star for the agent’s behavior. Avoid putting very specific tasks here; move task- or document-specific guidance into skills so you don’t bloat the base prompt or slow down every call.
Helpful patterns when shaping this template:
- Define the domain, expertise, and what “good” looks like (terminology, preferred outputs, citation rules).
- Set execution posture (autonomous vs. confirm-first) and how to handle ambiguity.
- State safety and presentation rules (no internal IDs, include units in specific formats, structured sections).
- Include explicit date/time context directly in the prompt when freshness matters.
new Date().toLocaleString()uses the client’s local clock by default. Note:toISOString()returns UTC, not local time; prefertoLocaleString()(or format with an explicit offset) when the agent needs the client’s local clock.
Example:
const currentTime = new Date().toLocaleString(); // client-local time
const systemPromptTemplate = `You are a construction drawing analysis assistant specialized in architectural floor plans.
Your expertise includes:- Interpreting plans, sections, and schedules- Extracting room dimensions, areas, and spatial relationships- Identifying doors/windows and construction details
Operating mode:- Act autonomously; do not ask to proceed when information is sufficient- When ambiguous, make a professional assumption, note it in the result, and continue- Ask at most one clarifying question only if multiple outcomes would be drastically different
Presentation:- Never expose internal IDs; use sheet titles or human-readable labels- Always include units (meters/m²) and state measurement methodology when relevant- Structure responses with clear sections (measurements, findings, assumptions)
Current date and time: ${currentTime}`;Seed messages
seedMessages are injected on every model invocation (after the system prompt, before conversation history) to shape the first assistant replies. They are not persisted to history and are not shown to the user — only the model’s generated replies are visible.
Use seedMessages to ask the agent write things like:
- A welcome message from the assistant.
- A default task prompt (for example, “Suggest three useful questions I can ask about this document”).
- A short scripted back and forth that sets model expectations before the user types anything.
Each seed message has a type and content:
type SeedMessage = { type: "human" | "ai"; content: string;};type: "human"— Represents a user message. Use this to instruct the agent on what to do or say.type: "ai"— Represents an assistant response. Use this to demonstrate the expected response format.
In the simplest case, you can treat content as just a string:
const welcomeMessage: SeedMessage = { type: "human", content: "Greet the user, briefly introduce yourself as a document review assistant, and ask whether they want a summary or a check for key risks.",};Keep seed messages concise and user-facing in tone — greetings, suggested questions, or a short next step are all good patterns. When omitted, a generic friendly greeting is used.
Examples:
type: "human"— “Introduce yourself as a contract assistant and propose two starting points: (1) quick summary, (2) check termination and liability clauses.”type: "ai"— “Hello! I’m your document assistant. I can summarize this file or check it for key risks. Which do you prefer?”
User ID
userId is an optional identifier used for rate limiting and authorization. If set, rate limiting claims in the JSON Web Token (JWT) are checked per user so usage limits are enforced; see the JWT claims guide.
PSPDFKit.load({ aiAssistant: { agentConfiguration: { userId: currentUser.id, ...otherAgentConfig, }, },});Tool approval
toolApproval controls both which tools are available to the agent and whether they require user approval before execution. This provides fine-grained control over agent autonomy and capabilities.
Configuration structure
toolApproval: { defaults: { default: "allow" | "ask" | "deny", read?: "allow" | "ask" | "deny", write?: "allow" | "ask" | "deny" }, rules?: [ { pattern: "<glob>", approval: "allow" | "ask" | "deny" }, ... // evaluated top-to-bottom, first match wins ]}Approval values
| Value | Tool available? | Requires approval? |
|---|---|---|
"allow" | Yes | No |
"ask" | Yes | Yes |
"deny" | No | N/A |
Defaults and fallbacks
defaults.defaultis required and is used when nothing else matches.- If
defaults.readordefaults.writeare omitted, the assistant falls back todefaults.defaultfor those categories. rulesis optional; when omitted, only the defaults are used.
Resolution order
For any tool call, approval is resolved in this order:
rules— First matching glob pattern winsdefaults.read/defaults.write— Based on the tool’sreadOnlyHintmetadatadefaults.default— Final fallback
Examples
Allow reads automatically, ask for writes:
toolApproval: { defaults: { default: "allow", read: "allow", write: "ask" }}Approve all actions (maximum oversight):
toolApproval: { defaults: { default: "ask" }}Read-only mode (no document modifications):
toolApproval: { defaults: { default: "allow", write: "deny" }}Trust built-in Nutrient tools but ask for everything else:
toolApproval: { defaults: { default: "ask" }, rules: [ { pattern: "nutrient_*", approval: "allow" } ]}Deny destructive MCP-style tools by pattern:
toolApproval: { defaults: { default: "allow", write: "ask" }, rules: [ { pattern: "mcp__*__delete_*", approval: "deny" } ]}Some tools require specific license components. If you configure a tool that your license doesn’t cover, the configuration will fail at startup.
Skills
Think of skills as your company’s playbooks or operational manuals written in natural language. Instead of teaching the agent complex business processes from scratch every time, you provide skills that encode your company’s specific workflows, rules, and knowledge. When relevant, the agent automatically reaches for the right “manual” and performs just like a trained employee who knows your processes.
When to use skills:
- Embed business logic — Encode company-specific rules, policies, and workflows that should apply consistently (for example, “Always redact date of birth before finalizing documents” or “Standard steel pricing is $10/m² for projects under 1000m²”)
- Inject dynamic context — Provide customer-specific, document-type-specific, or project-specific guidance without rebuilding the system prompt
- Maintain flexibility — Swap or compose skills across deployments to adapt agent behavior for different teams, regions, or use cases
- Keep requests fast — Load task-specific instructions only when needed rather than bloating every request with all possible guidance
Real-world examples:
const skills = [ { name: 'Document finalization checklist', description: 'Required steps before sending documents to clients', content: `Before finalizing any document for client delivery:- Redact all dates of birth (DOB) completely- Verify that all monetary amounts include currency symbols- Ensure client name spelling matches CRM exactly (check against customer database)- Add standard footer with document ID and generation timestamp- Flag any missing required signatures or initials` }, { name: 'Construction material pricing', description: 'Standard material costs and pricing rules', content: `Apply these standard material costs unless project specifies otherwise:- Steel sheets: $10/m² for orders under 1000m²; $8.50/m² for bulk orders- Concrete: $150/m³ delivered within 50km radius- Labor rates: $45/hour standard, $67.50/hour overtime (>40hrs/week)Always show line-item breakdown and flag any costs exceeding 15% of estimate.` }, { name: 'Contract risk assessment', description: 'Key clauses to flag during legal review', content: `When reviewing contracts, always identify and summarize:- Termination clauses (notice period, conditions, penalties)- Liability caps (flag if uncapped or >$1M)- Indemnification scope (flag broad indemnity language)- Payment terms (net-30 is standard; flag anything >45 days)- Non-compete restrictionsHighlight missing or unusual terms that deviate from our standard MSA.` }];
export const configuration = { systemPromptTemplate, seedMessages: [ { type: 'human', content: 'Greet the user, explain that you can help them review this document, and ask whether they prefer a quick summary or a check for key risks.', }, ], toolApproval: { defaults: { default: 'allow', read: 'allow', write: 'ask', }, }, skills,};Schema and limits:
name— Required unique identifier; must be at least one characterdescription— Optional; up to 500 characters (helps agent choose when to load this skill)content— Required instructional text in natural language; up to 50,000 characters
Best practices:
- One skill per process — Keep skills modular (one business process per skill) rather than creating one massive manual
- Natural language — Write instructions as you would for a new employee, not code
- Be specific — Include exact rules, thresholds, and examples (“$10/m²” not “competitive pricing”)
- Keep it concise — Very long skills (>10K characters) can slow requests and crowd out document context; split large manuals into composable skills