1
0
Fork 0
docsearch/packages/website/versioned_docs/version-v4/v4/askai-markdown-indexing.mdx
Paul Jankowski ecd905d440
feat: promote DocSearch v5 to main (#2968)
* feat(askai): add compatibility with algolia mcp search tool [DASH-2294] (#2862)

## Summary
Fixes DASH-2294
Add compatibility with the Algolia MCP search tool (`algolia_search_index_${string}`) in AskAI.

## Changes
- Add `AlgoliaMCPSearchTool` type to handle the Algolia MCP server search tool
- Refactor how number of hits are retrieved in `ToolCall` into a `getNumberOfHits` helper

## Test plan
- Added unit tests for modified code 

* chore: Update to use tsdown for build system (#2824)

* chore: Update to use tsdown for build system

* fix: docsearch-react build

* fix: lint

* fix: glob resolved to incorrect version

* chore: migrate to from yarn, lerna and shipjs to bun & changesets (#2827)

* chore: add tool-versions file for node and bun versions (#2866)

* chore: watch in parallel (#2867)

* feat: agent studio feedback integration (#2868)

* feat(askai): Split Ask AI modal into own component (#2884)

* feat(askai): Split Ask AI modal into own component

* refactor(react): share modal utilities

* refactor(react): share search box form

* refactor(react): extract start screen sections

* refactor(react): extract shared modal hooks

* fix: lint adapter

* refactor(react): reorganize modal files

* fix: type error in examples

* fix: remove ai modal from adapter for now, fix import paths of react package

* feat(askai): Agent Studio core tools (#2886)

* feat(askai): Implement dynamic tool calls

* move ToolCall to components dir

* Converge Agent Studio search tools to same definition, fix client side tools breaking UI state

* add examples for custom tools

* fix: lint & types

* feat(askai): add Agent Studio memory support (#2888)

* feat(askai): remove Ask AI transport layer (#2889)

* feat(askai): add Agent Studio memory support

* refactor(askai): remove Ask AI transport abstraction

* feat(askai): Feedback notes and tags (#2890)

* feat(askai): add Agent Studio memory support

* refactor(askai): remove Ask AI transport abstraction

* feat(askai): Feedback notes and tags

* fix: bump css bundle size limit

* move feedback actions to components

* chore: fix deploys for v5 branch

* feat(askai): Aggregate MCP search tool calls (#2891)

* feat(askai): add Agent Studio memory support

* refactor(askai): remove Ask AI transport abstraction

* feat(askai): Feedback notes and tags

* fix: bump css bundle size limit

* move feedback actions to components

* feat(askai): Aggregate MCP search tool calls

* feat(askai): Allow dynamic indices for Agent Studio (#2893)

* feat(v5): UI updates (#2896)

* feat(v5): UI updates

* fix: css file size

* fix: e2e tests

* fix: e2e tests

* fix: e2e tests

* chore: add theme toggle to react demo example

* Update sources panel display, update dark theme

* fix: lint

* fix(askai): address ui review feedback

* fix: pin icon positioning

* fix(askai): improve a11y and dark-mode shimmer for thinking and error states

- add role=alert/status and aria-hidden on error/thinking UI
- support dark-mode shimmer gradients via CSS variables
- respect prefers-reduced-motion for shimmer
- handle null date in useRelativeFormattedDate with fallback translation

* feat(v5): Add hit breadcrumbs (#2897)

* feat(v5): UI updates

* fix: css file size

* fix: e2e tests

* fix: e2e tests

* fix: e2e tests

* chore: add theme toggle to react demo example

* Update sources panel display, update dark theme

* fix: lint

* fix(askai): address ui review feedback

* fix: pin icon positioning

* fix(askai): improve a11y and dark-mode shimmer for thinking and error states

- add role=alert/status and aria-hidden on error/thinking UI
- support dark-mode shimmer gradients via CSS variables
- respect prefers-reduced-motion for shimmer
- handle null date in useRelativeFormattedDate with fallback translation

* feat(v5): Add hit breadcrumbs

* fix: bump css bundle size limit

* Fix after conflicts

* chore: move CSS building to lightning css (#2898)

* feat: Facet filters for search (#2899)

* feat(v5): Initial facet filters work

* Perf updates, dark theme, facet chips, a11y improvements

* fix: bump css bundle size limit

* Dedupe facet filters, refetch facets on searchParameters changes

* Add chevron flourish

* fix(askai): Fix new conversation causing thread depth errors (#2900)

* feat(v5): Add hit result badge (#2901)

* feat(v5): Add hit result badge

* Add background to hit result badge

* feat(v5): Add follow up prompt suggestions (#2902)

* feat(v5): Add follow up prompt suggestions

* fix: bump css bundle size limit

* docs(agents): document Cursor Cloud dev environment setup for v5 (Bun) (#2903)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* feat(mcp): setup mcp plugins (#2895)

* feat(v5): Add prompt suggestions to keyword search (#2912)

* feat(v5): Add prompt suggestions to keyword search

* cleanup: Move consistent object to reusable constant

* chore(v5): Split Ask AI related CSS into own bundle (#2913)

* chore(v5): Split Ask AI related CSS into own bundle

* move style.css to include modal and askai

* fix: Ensure stage level and watch level scripts use bun runtime (#2915)

* fix: Ensure stage level and watch level scripts use bun runtime

* chore: move to node@24 update imports

* fix: lint

* feat(js): Document JS based hybrid mode, fix JS packages (#2916)

* feat(js): Document JS based hybrid mode, fix JS packages

* update: add model onOpen to docs

* feat(cli): add @docsearch/cli for MCP setup and search (#2911)

* chore(tsdown): Bump to latest tsdown version (#2918)

* chore(tsdown): Bump to latest tsdown version

* fix: bump nvmrc node version

* fix: cli tsconfig

* fix: website build

* fix: example build

* fix: circleci install bun

* fix: lint

* fix: circleci install bun

* fix: circleci install bun

* fix: circleci install bun

* refactor(docusaurus-adapter): rework theme config for v5 and modularize SearchPage (#2904)

Co-authored-by: Paul Jankowski <8BitTitan@gmail.com>

* feat(askai): Move askai related props under root askai (#2919)

* feat(askai): Move askai related props under root askai

* fix: playwright test case

* fix(docusaurus): validate Ask AI options

* feat(js): Split JS bundles for search only (#2920)

* chore: Move to oxlint and oxfmt (#2923)

* chore: Get NPM OIDC token before publishing (#2924)

* chore: Enter v5 beta (#2925)

* chore: Enter pre release mode for v5

* chore: update release summary

* chore: version bump

* fix: Remove NPM_ID_TOKEN for release

* fix: Try setting blank NPM_TOKEN

* fix: Try blank NPM_AUTH_TOKEN

* chore: bump node and npm for release job

* docs(mcp): add service disclaimer (#2921)

* fix: Agent Studio MCP search tool (#2927)

* fix: Agent Studio MCP search tool

* Add changeset

* chore: Update stylelint (#2926)

* chore: stylelint update

* bun.lock

* Add changeset

* fix(website): use bare @import for tailwindcss (#2933)

Tailwind's build-time `@import` cannot be written with `url()` notation,
so `@import url('tailwindcss')` was passed through as a plain CSS import
instead of being processed by Tailwind.

Also syncs bun.lock with the 5.0.0-beta.0 versions already committed to
package.json.

* chore: version v5.0.0-beta.1 (#2932)

* feat(react): remove deprecated index props (#2936)

* feat(mcp): add ChatGPT and Codex DocSearch plugin package (#2938)

* fix: cleanup claude

* feat: website redesign (#2930)

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: lint

* fix: crash on the demo (#2940)

* feat(docs): Document v5 beta (#2935)

* chore(docs): v5 documentation

* Writing style clean up

* fix: website after conflicts

* fix: reported issues on mobile (#2944)

* chore: Introduce a11y smoke tests (#2943)

* chore: Add Lorris as codeowner (#2946)

* fix(askai): Ask AI fixes for v5 (#2945)

* fix: General v5 fixes (#2947)

- Fix `ref` console error for a `FacetMenu`
- Whitespace only search/conversation input does not trigger requests
- Fix flash of no results page on search

* feat: v5 general improvements (#2948)

* feat(v5): General fixes and improvements

* add changeset

* fix: bundlesize

* feat(v5): UI and DX improvements (#2949)

* feat: Rename assistantId to agentId

* feat: Allow reading default facet values from index searchParameters

* feat: Remove indexName prop from Sidepanel, cleanup documentation pages

* feat: Move appId and apiKey up into @docsearch/core

* feat: Add back nested grouping of search results

* add changeset

* revert changes to example demo

* fix: e2e tests

* chore: push git tags on version release (#2951)

* chore: release v5.0.0-beta.2 (#2950)

* chore: Fix pushing git tags (#2953)

* fix(askai): sanitize markdown HTML in v5 (#2954)

Backport of #2929.\n\nOriginal commit: 681cbfec03

Co-authored-by: Vasco Bettencourt <32492444+vascobettencourt@users.noreply.github.com>

* fix(v5): stop truncating mobile snippets (#2958)

* fix(v5): stop truncating mobile snippets

Backport of #2907.\n\nOriginal commit: 9ad6d169fe

* fix(v5): allow mobile hit text to wrap

Completes the v5 adaptation of #2907 by overriding later v5 child-level truncation rules.\n\nOriginal commit: 9ad6d169fe

* chore(v5): account for mobile wrapping CSS

Updates the CSS size budget for the v5 adaptation of #2907.\n\nOriginal commit: 9ad6d169fe

---------

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>

* feat(v5): Add new footerAction prop (#2952)

* feat(v5): Add new footerAction prop

* Resolve PR comments

* fix(v5): recognize conversation depth errors (#2957)

Backport of #2881.\n\nOriginal commit: f68e52251c

Co-authored-by: Felipe Bermudez <felipeberm@gmail.com>

* fix(v5): expose Sidepanel search parameter types (#2956)

* fix(v5): expose Sidepanel search parameter types

Backport of #2906.\n\nOriginal commit: 4710d0ca77

* Delete sidepanel.test.ts

Had a pointless test case in it.

---------

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>

* fix(v5): ignore slash shortcut on focused buttons (#2955)

Backport of #2871.\n\nOriginal commit: 0e41a78c44

Co-authored-by: Sigmabro <122412346+Sigmabrogz@users.noreply.github.com>

* fix(agentStudio): agents dynamic mode enabled (#2959)

* fix(agentStudio): agents dynamic mode enabled

* fix(askai): use string[] for dynamic agentStudio indices

* feat(docs): add Ask AI to Agent Studio migration guide (#2931)

* feat(docs): add Ask AI to Agent Studio migration guide

* feat(docs): agentStudio migrating from askAI

* feat(docs): renaming agentId

* feat(agentStudio): dynamic mode indices updated

* fix: Docusaurus adapter styling, DocSearch website fixes (#2960)

* chore: release v5.0.0-beta.3 (#2961)

* feat(website): Launch updates (#2964)

* fix(website): Fix font loading (#2966)

* feat(v5): Back port cost control errors (#2965)

* chore: release v5.0.0-beta.4 (#2967)

---------

Co-authored-by: Vincent Lemeunier <vincentlemeunier+git@gmail.com>
Co-authored-by: Dylan Tientcheu <dylan.tientcheu@algolia.com>
Co-authored-by: Lorris Saint-Genez <lorrissaintgenez@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Dylan Tientcheu <dylantientcheu@gmail.com>
Co-authored-by: Vasco Bettencourt <32492444+vascobettencourt@users.noreply.github.com>
Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>
Co-authored-by: Felipe Bermudez <felipeberm@gmail.com>
Co-authored-by: Sigmabro <122412346+Sigmabrogz@users.noreply.github.com>
2026-08-06 15:31:45 -04:00

654 lines
21 KiB
Text

---
title: Improving Answer Quality with Markdown Indexing
---
To deliver more accurate, context-rich answers at scale, Ask AI benefits from cleanly structured content. One of the most effective ways to achieve this is by using a Markdown-based indexing helper in your Algolia Crawler configuration. This setup ensures Ask AI can access well-formed, content-focused records—especially important for larger documentation sites where metadata, navigation elements, or layout artifacts might otherwise dilute the quality of generative responses.
:::info
Setting up markdown indexing can be automated through the Crawler UI for most use cases. For advanced customization or understanding the underlying configuration, manual setup options are also available.
**Note:** For more integration examples (Docusaurus, VitePress, Astro/Starlight, and generic setups), see the section at the bottom of this page.
:::
## Overview
To maximize the quality of Ask AI responses, configure your Crawler to create a dedicated index for Markdown content. This approach enables Ask AI to work with structured, chunked records sourced from your documentation, support content, or any Markdown-based material—resulting in significantly more relevant and precise answers.
You can set up markdown indexing in two ways:
1. **Automated Setup (Recommended)**: Use the Crawler UI to automatically create and configure your markdown index
2. **Manual Configuration**: Manually configure your Crawler for advanced customization needs
---
## Automated Markdown Indexing Setup (Recommended)
The easiest way to set up markdown indexing is through the Crawler UI, which automatically creates and configures an optimized markdown index for Ask AI.
### Step 1: Access Markdown Indexing in Crawler Configuration
1. Navigate to your Crawler dashboard
2. Go to **Configuration** → **Markdown for LLMs** tab
3. You'll see the Markdown Indexing section where you can create a dedicated markdown index
![Crawler Configuration - Markdown for LLMs](/img/assets/askai-ready.png)
### Step 2: Add a New Markdown Index
1. Click **"+ Add Index"** to create a new markdown index
2. Fill in the required fields:
- **Index Name**: Enter a descriptive name (e.g., `my-docs-markdown`)
- **Content Tag**: Specify the HTML content selector (typically `main`)
- **Template**: Choose the template that matches your documentation framework:
- **Docusaurus** - For Docusaurus sites
- **VitePress** - For VitePress sites
- **Astro/Starlight** - For Astro/Starlight sites
- **Non-DocSearch (Generic)** - For custom sites or other frameworks
![Add Markdown Index Dialog](/img/assets/askai-md.png)
3. Click **"Add Index"** to create the index
The Crawler will automatically configure the optimal settings for your chosen template, including:
- Proper record extraction and chunking
- Framework-specific metadata extraction (language, version, tags)
- Optimized index settings for Ask AI
### Step 3: Run the Crawler
Once your markdown index is configured:
1. Click **"Start Crawling"** to begin indexing your content
2. Monitor the crawl progress in the dashboard
3. Your new markdown index will be populated with clean, structured records optimized for Ask AI
![Crawler Status](/img/assets/askai-crawling.png)
### Step 4: Integrate with Ask AI
After crawling completes, configure DocSearch to use your new markdown index for Ask AI responses. See the [Integration section](#integrate-your-new-index-with-ask-ai) below for detailed setup instructions.
---
## Manual Configuration (Advanced)
For users who need advanced customization or want to understand the underlying configuration, you can manually set up markdown indexing by modifying your Crawler configuration directly.
### Step 1: Update your existing DocSearch Crawler configuration
- In your Crawler config, add the following to your `actions: [ ... ]` array:
```js
// actions: [ ...,
{
indexName: "my-markdown-index",
pathsToMatch: ["https://example.com/docs/**"],
recordExtractor: ({ $, url, helpers }) => {
// Target only the main content, excluding navigation
const text = helpers.markdown(
"main > *:not(nav):not(header):not(.breadcrumb)",
);
if (text === "") return [];
const language = $("html").attr("lang") || "en";
const title = $("head > title").text();
// Get the main heading for better searchability
const h1 = $("main h1").first().text();
return helpers.splitTextIntoRecords({
text,
baseRecord: {
url,
objectID: url,
title: title || h1,
heading: h1, // Add main heading as separate field
lang: language,
},
maxRecordBytes: 100000, // Higher = fewer, larger records. Lower = more, smaller records.
// Note: Increasing this value may increase the token count for LLMs, which can affect context size and cost.
orderingAttributeName: "part",
});
},
},
// ...],
```
- Then, add the following to your `initialIndexSettings: { ... }` object:
```js
// initialIndexSettings: { ...,
"my-markdown-index": {
attributesForFaceting: ["lang"],
ignorePlurals: true,
minProximity: 1,
removeStopWords: false,
searchableAttributes: ["title", "heading", "unordered(text)"],
removeWordsIfNoResults: "lastWords",
attributesToHighlight: ["title", "text"],
typoTolerance: false,
advancedSyntax: false,
},
// ...},
```
---
### Step 2: Run the DocSearch crawler to create a new Ask AI optimized index
After updating your Crawler configuration:
1. **Publish your configuration** in the Algolia Crawler dashboard to save and activate it.
2. **Run the Crawler** to index your markdown content and create the new index.
The Crawler will process your content using the markdown extraction helper and populate your new index with clean, structured records optimized for Ask AI.
> **Tip:** Monitor the crawl progress in your dashboard to ensure all pages are processed correctly. You can view the indexed records in your Algolia index to verify the structure and content.
---
## Integrate your new index with Ask AI
Once your Crawler has created your optimized index, you can integrate it with Ask AI in two ways: using DocSearch (recommended for most users) or building a custom integration using the Ask AI API.
<Tabs groupId="integration-type" defaultValue="docsearch" values={[{ label: 'DocSearch Integration', value: 'docsearch' }, { label: 'Custom API Integration', value: 'custom' }]}>
<TabItem value="docsearch">
### Using DocSearch
Configure DocSearch to use both your main keyword index and your markdown index for Ask AI:
<Tabs groupId="language" defaultValue="js" values={[{ label: 'JavaScript', value: 'js' }, { label: 'React', value: 'react' }]}>
<TabItem value="js">
```js
docsearch({
indices: ['YOUR_INDEX_NAME'], // Main DocSearch keyword index
apiKey: 'YOUR_SEARCH_API_KEY',
appId: 'YOUR_APP_ID',
askAi: {
indexName: 'YOUR_INDEX_NAME-markdown', // Markdown index for Ask AI
apiKey: 'YOUR_SEARCH_API_KEY', // (or a different key if needed)
appId: 'YOUR_APP_ID',
assistantId: 'YOUR_ALGOLIA_ASSISTANT_ID',
searchParameters: {
facetFilters: ['language:en'], // Optional: filter to specific language/version
},
},
});
```
</TabItem>
<TabItem value="react">
```jsx
<DocSearch
indices={['YOUR_INDEX_NAME']} // Main DocSearch keyword index
apiKey="YOUR_SEARCH_API_KEY"
appId="YOUR_APP_ID"
askAi={{
indexName: 'YOUR_INDEX_NAME-markdown', // Markdown index for Ask AI
apiKey: 'YOUR_SEARCH_API_KEY',
appId: 'YOUR_APP_ID',
assistantId: 'YOUR_ALGOLIA_ASSISTANT_ID',
searchParameters: {
facetFilters: ['language:en'], // Optional: filter to specific language/version
},
}}
/>
```
</TabItem>
</Tabs>
- `indices`: Your main DocSearch index or indices for keyword search.
- `askAi.indexName`: The markdown index you created for Ask AI context.
- `assistantId`: The ID of your configured Ask AI assistant.
- `searchParameters.facetFilters`: Optional filters to limit Ask AI context (useful for multi-language sites).
</TabItem>
<TabItem value="custom">
### Custom API Integration
:::info
We highly recommend using the DocSearch package for most use cases. Custom implementations using the Ask AI API directly are not fully supported to the same extent as the DocSearch package, and may require additional development effort for features like error handling, authentication, and UI components.
:::
Build your own chat interface using the Ask AI API. This gives you full control over the user experience and allows for advanced customizations.
```js
class CustomAskAI {
constructor({ appId, apiKey, indexName, assistantId }) {
this.appId = appId;
this.apiKey = apiKey;
this.indexName = indexName; // Your markdown index
this.assistantId = assistantId;
this.baseUrl = 'https://askai.algolia.com';
}
async getToken() {
const response = await fetch(`${this.baseUrl}/chat/token`, {
method: 'POST',
headers: {
'X-Algolia-Assistant-Id': this.assistantId,
},
});
const data = await response.json();
return data.token;
}
async sendMessage(conversationId, messages, searchParameters = {}) {
const token = await this.getToken();
const response = await fetch(`${this.baseUrl}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Algolia-Application-Id': this.appId,
'X-Algolia-API-Key': this.apiKey,
'X-Algolia-Index-Name': this.indexName, // Use your markdown index
'X-Algolia-Assistant-Id': this.assistantId,
'Authorization': token,
},
body: JSON.stringify({
id: conversationId,
messages,
...(Object.keys(searchParameters).length > 0 && { searchParameters }),
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Handle streaming response
const reader = response.body.getReader();
const decoder = new TextDecoder();
return {
async *[Symbol.asyncIterator]() {
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
if (chunk.trim()) {
yield chunk;
}
}
} finally {
reader.releaseLock();
}
}
};
}
}
// Usage
const askAI = new CustomAskAI({
appId: 'YOUR_APP_ID',
apiKey: 'YOUR_API_KEY',
indexName: 'YOUR_INDEX_NAME-markdown', // Your markdown index
assistantId: 'YOUR_ASSISTANT_ID',
});
// Send a message with facet filters for your markdown index
const stream = await askAI.sendMessage('conversation-1', [
{
role: 'user',
content: 'How do I configure my API?',
id: 'msg-1',
},
], {
facetFilters: ['language:en', 'type:content'] // Filter to relevant content
});
// Handle streaming response
for await (const chunk of stream) {
console.log(chunk); // Handle each chunk of the response
}
```
**Benefits of custom integration:**
- Full control over UI/UX
- Custom authentication and session management
- Advanced filtering and search parameters for your markdown index
- Integration with existing chat systems
- Custom analytics and monitoring
> **📚 Learn More:** For complete API documentation, authentication details, advanced examples, and more integration patterns, see the [Ask AI API Reference](/docs/v4/v4/askai-api).
**Using Facet Filters with Your Markdown Index:**
Since your markdown index includes attributes like `lang`, `version`, and `docusaurus_tag`, you can filter Ask AI's context precisely:
```js
// Example: Filter to English documentation only
const searchParameters = {
facetFilters: ['lang:en']
};
// Example: Filter to specific version and content type
const searchParameters = {
facetFilters: ['lang:en', 'version:latest', 'type:content']
};
// Example: Use OR logic for multiple tags (from your integration examples)
const searchParameters = {
facetFilters: [
'lang:en',
[
'docusaurus_tag:default',
'docusaurus_tag:docs-default-current'
]
]
};
```
</TabItem>
</Tabs>
> **Tip:** Keep both indexes updated as your documentation evolves to ensure the best search and AI answer quality.
---
## Best Practices & Tips
- **Use clear, consistent titles in your markdown files** for better searchability.
- **Test your index** with Ask AI to ensure relevant answers are returned.
- **Adjust `maxRecordBytes`** if you notice answers are too broad or too fragmented.
- **Note:** Increasing `maxRecordBytes` may increase the token count for LLMs, which can affect the size of the context window and the cost of each Ask AI response.
- **Keep your markdown well-structured** (use headings, lists, etc.) for optimal chunking.
- **Add attributes** like `lang`, `version`, or `tags` to your records and `attributesForFaceting` if you want to filter or facet in your search UI or Ask AI.
---
## FAQ
**Q: Why use a separate markdown index?**
A: It allows Ask AI to access content in a format optimized for LLMs, improving answer quality.
**Q: Can I use this with other content types?**
A: Yes, but markdown is especially well-suited for chunking and context extraction.
**Q: What if I have very large markdown files?**
A: Lower the `maxRecordBytes` value to split content into smaller, more focused records.
---
For more details, see the [Ask AI documentation](./askai.mdx) or contact support if you need help configuring your Crawler.
---
## Crawler Configuration Examples by Integration
Below are example configurations for setting up your markdown index with different documentation platforms. Each shows how to extract facets (like language, version, tags) and configure the Crawler for your specific integration:
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs groupId="integration">
<TabItem value="generic" label="Non-DocSearch (Generic)">
**Generic Example:**
```js
// In your Crawler config:
// actions: [ ...,
{
indexName: "my-markdown-index",
pathsToMatch: ["https://example.com/**"],
recordExtractor: ({ $, url, helpers }) => {
// Target only the main content, excluding navigation
const text = helpers.markdown(
"main > *:not(nav):not(header):not(.breadcrumb)",
);
if (text === "") return [];
const language = $("html").attr("lang") || "en";
const title = $("head > title").text();
// Get the main heading for better searchability
const h1 = $("main h1").first().text();
return helpers.splitTextIntoRecords({
text,
baseRecord: {
url,
objectID: url,
title: title || h1,
heading: h1, // Add main heading as separate field
lang: language,
},
maxRecordBytes: 100000, // Higher = fewer, larger records. Lower = more, smaller records.
// Note: Increasing this value may increase the token count for LLMs, which can affect context size and cost.
orderingAttributeName: "part",
});
},
},
// ...],
// initialIndexSettings: { ...,
"my-markdown-index": {
attributesForFaceting: ["lang"], // Recommended if you add more attributes outside of objectID
ignorePlurals: true,
minProximity: 1,
removeStopWords: false,
searchableAttributes: ["title", "heading", "unordered(text)"],
removeWordsIfNoResults: "lastWords",
attributesToHighlight: ["title", "text"],
typoTolerance: false,
advancedSyntax: false,
},
// ...},
```
</TabItem>
<TabItem value="docusaurus" label="Docusaurus">
**Docusaurus Example:**
```js
// In your Crawler config:
// actions: [ ...,
{
indexName: "my-markdown-index",
pathsToMatch: ["https://example.com/docs/**"],
recordExtractor: ({ $, url, helpers }) => {
// Target only the main content, excluding navigation
const text = helpers.markdown(
"main > *:not(nav):not(header):not(.breadcrumb)",
);
if (text === "") return [];
// Extract meta tag values. These are required for Docusaurus
const language =
$('meta[name="docsearch:language"]').attr("content") || "en";
const version =
$('meta[name="docsearch:version"]').attr("content") || "latest";
const docusaurus_tag =
$('meta[name="docsearch:docusaurus_tag"]').attr("content") || "";
const title = $("head > title").text();
// Get the main heading for better searchability
const h1 = $("main h1").first().text();
return helpers.splitTextIntoRecords({
text,
baseRecord: {
url,
objectID: url,
title: title || h1,
heading: h1, // Add main heading as separate field
lang: language, // Required for Docusaurus
language, // Required for Docusaurus
version: version.split(","), // in case there are multiple versions. Required for Docusaurus
docusaurus_tag: docusaurus_tag // Required for Docusaurus
.split(",")
.map((tag) => tag.trim())
.filter(Boolean),
},
maxRecordBytes: 100000, // Higher = fewer, larger records. Lower = more, smaller records.
// Note: Increasing this value may increase the token count for LLMs, which can affect context size and cost.
orderingAttributeName: "part",
});
},
},
// ...],
// initialIndexSettings: { ...,
"my-markdown-index": {
attributesForFaceting: ["lang", "language", "version", "docusaurus_tag"], // Required for Docusaurus
ignorePlurals: true,
minProximity: 1,
removeStopWords: false,
searchableAttributes: ["title", "heading", "unordered(text)"],
removeWordsIfNoResults: "lastWords",
attributesToHighlight: ["title", "text"],
typoTolerance: false,
advancedSyntax: false,
},
// ...},
```
</TabItem>
<TabItem value="vitepress" label="VitePress">
**VitePress Example:**
```js
// In your Crawler config:
// actions: [ ...,
{
indexName: "my-markdown-index",
pathsToMatch: ["https://example.com/docs/**"],
recordExtractor: ({ $, url, helpers }) => {
// Target only the main content, excluding navigation
const text = helpers.markdown(
"main > *:not(nav):not(header):not(.breadcrumb)",
);
if (text === "") return [];
const language = $("html").attr("lang") || "en";
const title = $("head > title").text();
// Get the main heading for better searchability
const h1 = $("main h1").first().text();
return helpers.splitTextIntoRecords({
text,
baseRecord: {
url,
objectID: url,
title: title || h1,
heading: h1, // Add main heading as separate field
lang: language, // Required for VitePress
},
maxRecordBytes: 100000, // Higher = fewer, larger records. Lower = more, smaller records.
// Note: Increasing this value may increase the token count for LLMs, which can affect context size and cost.
orderingAttributeName: "part",
});
},
},
// ...],
// initialIndexSettings: { ...,
"my-markdown-index": {
attributesForFaceting: ["lang"], // Required for VitePress
ignorePlurals: true,
minProximity: 1,
removeStopWords: false,
searchableAttributes: ["title", "heading", "unordered(text)"],
removeWordsIfNoResults: "lastWords",
attributesToHighlight: ["title", "text"],
typoTolerance: false,
advancedSyntax: false,
},
// ...},
```
</TabItem>
<TabItem value="astro" label="Astro / Starlight">
**Astro / Starlight Example:**
```js
// In your Crawler config:
// actions: [ ...,
{
indexName: "my-markdown-index",
pathsToMatch: ["https://example.com/docs/**"],
recordExtractor: ({ $, url, helpers }) => {
// Target only the main content, excluding navigation
const text = helpers.markdown(
"main > *:not(nav):not(header):not(.breadcrumb)",
);
if (text === "") return [];
const language = $("html").attr("lang") || "en";
const title = $("head > title").text();
// Get the main heading for better searchability
const h1 = $("main h1").first().text();
return helpers.splitTextIntoRecords({
text,
baseRecord: {
url,
objectID: url,
title: title || h1,
heading: h1, // Add main heading as separate field
lang: language, // Required for Astro/StarLight
},
maxRecordBytes: 100000, // Higher = fewer, larger records. Lower = more, smaller records.
// Note: Increasing this value may increase the token count for LLMs, which can affect context size and cost.
orderingAttributeName: "part",
});
},
},
// ...],
// initialIndexSettings: { ...,
"my-markdown-index": {
attributesForFaceting: ["lang"], // Required for Astro/StarLight
ignorePlurals: true,
minProximity: 1,
removeStopWords: false,
searchableAttributes: ["title", "heading", "unordered(text)"],
removeWordsIfNoResults: "lastWords",
attributesToHighlight: ["title", "text"],
typoTolerance: false,
advancedSyntax: false,
},
// ...},
```
</TabItem>
</Tabs>
> Each example shows how to extract common facets and configure your markdown index for Ask AI. Adjust selectors and meta tag names as needed for your site.