> ## Documentation Index
> Fetch the complete documentation index at: https://docs.superdoc.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Best practices

> Get better results from LLM document editing: prompting, tool call patterns, and workflow tips for both presets

These patterns help your LLM agent produce reliable, efficient document edits. The first group applies to every integration; the rest is split by preset — [core](/ai/agents/core-preset) (recommended) and [legacy](/ai/agents/legacy-preset) work differently enough that their playbooks are separate.

## Practices for every integration

### Use the bundled system prompt — from the same preset as your tools

Each preset ships the prompt it was designed and evaluated with. Load both through the toolkit so they can never mismatch:

```typescript theme={null}
import { createAgentToolkit } from '@superdoc-dev/sdk';

const { tools, systemPrompt, dispatch } = await createAgentToolkit({
  provider: 'openai',
  preset: 'core',
});
```

Extend it with task-specific rules rather than replacing it:

```typescript theme={null}
const fullPrompt = `${systemPrompt}\n\n## Additional rules\n- Use tracked changes for all edits.\n- End with a one-sentence summary of what changed.`;
```

### Feed errors back

Dispatch failures are written for the model to act on. Pass them back as tool results — most models self-correct on the next turn:

```typescript theme={null}
try {
  const result = await dispatch(doc, call.function.name, JSON.parse(call.function.arguments));
  messages.push({ role: 'tool', tool_call_id: call.id, content: JSON.stringify(result) });
} catch (err: any) {
  // Return the error as a tool result: the model will see it and adjust
  messages.push({ role: 'tool', tool_call_id: call.id, content: JSON.stringify({ error: err.message }) });
}
```

### Cache tools and prompts

Tools and the system prompt don't change between requests. Build the toolkit once at startup and reuse it across conversations. On Anthropic, also pass `cache: true` so the tool array carries prompt-caching markers (see [token budget](/ai/agents/llm-tools#token-budget)).

```typescript theme={null}
let kit: Awaited<ReturnType<typeof createAgentToolkit>> | null = null;

async function ensureToolkit() {
  kit ??= await createAgentToolkit({ provider: 'openai', preset: 'core' });
  return kit;
}
```

### Use tracked changes for review workflows

Set `changeMode: "tracked"` (or instruct it in your appended prompt rules): every AI edit lands as a tracked change users can accept or reject in SuperDoc or Microsoft Word. A "suggest vs. apply directly" toggle in your UI maps 1:1 to this flag.

### Add examples for repeatable workflows

If the same kind of edit runs across many documents, include a concrete tool call example in your system prompt. Models that see a working example of the exact invocation produce correct calls more reliably than models that only see the schema.

### Pin your model version

Use a specific model ID rather than an alias. Aliases can change behavior between releases and break working tool call patterns.

## Core preset (recommended)

The core surface is two tools — `superdoc_inspect` and `superdoc_perform_action` — with 40 named actions that resolve their own targets and return receipts. The playbook follows from that.

### Inspect narrowly, then act

A typical edit is 2–3 calls: one narrow inspect, one action, and (only when the receipt says so) a follow-up. Steer the model away from full-document reads:

* `{countsOnly: true}` for orientation; `includeDomains` to fetch only what the task needs.
* On large documents, window with `blockOffset`/`blockLimit` — ordinals are absolute, so windows line up.

### Trust receipts, not vibes

Every action returns real pre/post evidence. Teach your loop (and your users) to read it:

* `status: "ok"` with `verificationPassed: true` — done; don't re-inspect "to be sure."
* `status: "partial"` — some of the work landed; the receipt says which part. Re-inspect, then fix forward.
* `status: "failed"` — nothing changed unless the receipt explicitly says otherwise. `errors[].message` usually contains the exact recovery step, and `recovery`/`revertHint` are machine-usable.

### Batch with selectors, not with repeated calls

Actions absorb batching: `add_comments` takes `selectors[]` for many blocks in one call; `replace_text` takes `edits[]`; `format_text` takes `targetTexts[]`. One action call with a batch argument beats N calls every time — cheaper, atomic, one receipt.

### Narrow the surface for your product

If your product should never delete tables or fill placeholders, exclude those actions — [`excludeActions`](/ai/agents/core-preset#narrowing-the-surface-excludeactions) removes them from the enum, the prompt, and dispatch in one move. A smaller surface is also cheaper per turn.

### Prompt examples

Tested against the core action surface — use as inspiration or few-shot examples:

* "Find the termination clause and rewrite it to require 30-day written notice. Use tracked changes."
* "Replace all references to 'Contractor' with 'Service Provider' as tracked changes."
* "Add a comment to every paragraph that mentions personally identifiable information: 'Verify PII handling.'"
* "Number the unnumbered obligation at the end of section 2 like its siblings."
* "Accept all formatting revisions but leave text edits pending review."
* "Move the PREAMBLE section after SCHEDULE A."
* "Add a 2×3 table under the second heading with headers Owner and Stage, then style it."

## Legacy preset

The legacy surface is low-level: the model searches for handles, then edits by address. If you're on it, these patterns matter — and [migrating to core](/ai/agents/legacy-preset#migrating-to-core) removes most of them.

### Read first, search, then edit

A typical edit takes 3-5 tool calls:

1. `superdoc_get_content`: understand what's in the document
2. `superdoc_search`: find the exact location (returns stable handles/addresses)
3. Edit tool (`superdoc_edit`, `superdoc_format`, etc.): apply the change using targets from search

Handles from search results point to the exact right location. If the model guesses a block address instead of searching, edits land in the wrong place. **Search again after every mutation** — refs expire when the revision bumps.

### Prefer markdown insert for multi-block creation

When creating multiple headings and paragraphs, use `superdoc_edit` with `type: "markdown"` instead of one `superdoc_create` per block:

```json theme={null}
{
  "action": "insert",
  "type": "markdown",
  "value": "## Executive Summary\n\nThis agreement governs the terms of service.\n\n## Key Provisions\n\nThe following provisions apply to all parties."
}
```

After inserting, apply formatting in a single `superdoc_mutations` batch using `format.apply` steps — one step per block or range. This reduces a workflow that might otherwise take 40+ calls down to 4: read, search, insert, format.

### Use focused tools; `superdoc_mutations` is an escape hatch

For straightforward edits, use the focused intent tools — they validate arguments and give clear errors. Reach for `superdoc_mutations` only when you need preview/apply semantics, an atomic multi-step batch, or a workflow that would otherwise require refreshing targets between steps (it resolves all targets before any step executes).

### Choose formatting values from the document

Don't hardcode formatting values — read them from existing content and match:

* **Body text**: read `fontFamily`, `fontSize`, `color` from non-empty paragraphs; set `bold: false` for body.
* Many DOCX documents report `underline: true` on all blocks due to style inheritance — a DOCX artifact, not intentional formatting. Don't carry it forward.
* **Headings**: read from existing heading blocks; confirm bold/centering against the document rather than assuming.

### Prompt examples

* "Format the entire document in Times New Roman, 12-point."
* "Make all Heading 2 paragraphs bold and set them to 14-point font."
* "Replace every occurrence of 'FY2024' with 'FY2025' throughout the document."
* "Insert CONFIDENTIAL: DO NOT DISTRIBUTE at the very top, bold, red, 14pt."
* "Convert the list of references at the end into a numbered list and restart numbering at 1."

## Related

* [Core preset reference](/ai/agents/core-preset) · [Legacy preset reference](/ai/agents/legacy-preset)
* [How to use](/ai/agents/integrations): step-by-step integration guide
* [Debugging](/ai/agents/debugging): troubleshoot tool call failures
* [How it works](/ai/agents/architecture): SDK ↔ CLI ↔ LLM mechanics
