---
title: "RAG Setup · Nuxt AI Ready · Nuxt SEO"
canonical_url: "https://nuxtseo.com/docs/ai-ready/advanced/rag-example"
last_updated: "2026-08-16T09:50:27.266Z"
meta:
  description: "Vectorize your site's markdown for semantic search and RAG pipelines."
  "og:description": "Vectorize your site's markdown for semantic search and RAG pipelines."
  "og:title": "RAG Setup · Nuxt AI Ready · Nuxt SEO"
---

Nuxt SEO on GitHub

Switch to AI ReadySwitch to Nuxt SEOSwitch to RobotsSwitch to SitemapSwitch to OG ImageSwitch to Schema.orgSwitch to Link CheckerSwitch to SEO UtilsSwitch to Site ConfigSwitch to Skew Protection

**Advanced**

# **RAG Setup**

Nuxt AI Ready outputs clean markdown optimized for vectorizing. This guide shows how to build a RAG pipeline using `**llms-full.txt**`.

## Fetch Markdown Content

`**llms-full.txt**` contains independent Markdown documents. Each page starts with a `**---**` boundary followed by a compact metadata list; the page's original Markdown follows unchanged:

```ts
const RE_PAGE_BOUNDARY = /^---\n\n(?=- \*\*Page:\*\* )/gm
const RE_METADATA = /^- \*\*(Page|Source|Description):\*\* (.*)$/

const llmsFullUrl = 'https://yoursite.com/llms-full.txt'
const response = await fetch(llmsFullUrl)
const content = await response.text()

// The first block is the site header; subsequent blocks are pages.
const [, ...pageBlocks] = content.split(RE_PAGE_BOUNDARY)
const pages = pageBlocks.map((block) => {
  const [metadataBlock = '', ...markdownParts] = block.trim().split('\n\n')
  const metadata = Object.fromEntries(
    metadataBlock.split('\n').flatMap((line) => {
      const match = RE_METADATA.exec(line)
      return match ? [[match[1], match[2]]] : []
    }),
  )
  const source = new URL(metadata.Source, llmsFullUrl)

  return {
    title: metadata.Page,
    description: metadata.Description,
    source: source.href,
    route: source.pathname,
    markdown: markdownParts.join('\n\n').trim(),
  }
})
```

## Generate Embeddings

Use any embedding provider. Example with [**~~OpenAI~~**](https://openai.com):

```ts
import OpenAI from 'openai'

const openai = new OpenAI()

async function embed(text: string) {
  const response = await openai.embeddings.create({
    model: 'text-embedding-3-small',
    input: text,
  })
  return response.data[0].embedding
}

// Embed each page
const vectors = await Promise.all(
  pages.map(async page => ({
    id: page.route,
    embedding: await embed(page.markdown),
    metadata: { title: page.title, route: page.route }
  }))
)
```

## Store in Vector DB

### sqlite-vec (Local)

```ts
import Database from 'better-sqlite3'
import * as sqliteVec from 'sqlite-vec'

const db = new Database(':memory:')
sqliteVec.load(db)

db.exec(`
  CREATE VIRTUAL TABLE pages USING vec0(
    id TEXT PRIMARY KEY,
    embedding FLOAT[1536]
  )
`)

const insert = db.prepare('INSERT INTO pages VALUES (?, ?)')
for (const v of vectors) {
  insert.run(v.id, new Float32Array(v.embedding))
}
```

### Upstash Vector (Serverless)

```ts
import { Index } from '@upstash/vector'

const index = new Index()

await index.upsert(vectors.map(v => ({
  id: v.id,
  vector: v.embedding,
  metadata: v.metadata
})))
```

## Query

```ts
async function search(query: string, topK = 5) {
  const queryEmbedding = await embed(query)

  // sqlite-vec
  const results = db.prepare(`
    SELECT id, distance
    FROM pages
    WHERE embedding MATCH ?
    ORDER BY distance
    LIMIT ?
  `).all(new Float32Array(queryEmbedding), topK)

  return results
}

// Use in RAG prompt
const relevant = await search('how do I configure meta tags?')
const context = relevant.map(r => pages.find(p => p.route === r.id)?.markdown).join('\n\n')
```

## Chunking Strategy

By default, each page is one chunk. For large pages, split by heading:

```ts
const RE_HEADING_SPLIT = /^##\s+/m

function chunkByHeading(markdown: string, route: string) {
  const sections = markdown.split(RE_HEADING_SPLIT)
  return sections.map((section, i) => ({
    id: `${route}#${i}`,
    content: section.trim(),
    route
  }))
}
```

| **Strategy** | **When to use** |
| --- | --- |
| Page-level | Small pages (<2k tokens), general search |
| Heading-level | Long docs, precise retrieval needed |
| Sliding window | Dense technical content, overlap matters |

## Build Script

Run vectorization at build time:

```ts
// scripts/vectorize.ts
import { readFileSync } from 'node:fs'

const llmsFull = readFileSync('.output/public/llms-full.txt', 'utf-8')
// ... parse and vectorize as above
```

Add to your build:

```json
{
  "scripts": {
    "generate": "nuxt generate && tsx scripts/vectorize.ts"
  }
}
```

**Was this page helpful?**

### **Related **

[**llms.txt Configuration**](https://nuxtseo.com/docs/ai-ready/guides/llms-txt)

[**Markdown Output**](https://nuxtseo.com/docs/ai-ready/guides/markdown)

[**WebMCP** Register browser tools for AI agents with document.modelContext.](https://nuxtseo.com/docs/ai-ready/guides/webmcp) [**Nuxt Hooks** Nuxt hooks provided by nuxt-ai-ready for extending functionality.](https://nuxtseo.com/docs/ai-ready/api/nuxt-hooks)