$npx -y skills add butterbase-ai/butterbase-skills --skill rag-devUse when building knowledge bases, ingesting documents, running semantic search, or adding LLM-synthesized Q&A over private content with Butterbase RAG
| 1 | # Butterbase RAG (Retrieval-Augmented Generation) |
| 2 | |
| 3 | Two tools cover the entire RAG surface: |
| 4 | |
| 5 | - **`manage_rag_content`** — collections, document ingestion, status polling, deletion |
| 6 | - **`rag_query`** — semantic search, optional LLM synthesis |
| 7 | |
| 8 | Documents are ingested asynchronously: text or files become embeddings stored in pgvector, and queries do a similarity search at runtime. |
| 9 | |
| 10 | --- |
| 11 | |
| 12 | ## 1. The mental model |
| 13 | |
| 14 | ``` |
| 15 | Collection Documents Chunks |
| 16 | ────────── ────────── ────── |
| 17 | "product-faq" ──────────────► doc_1 (PDF) ───────────► chunk 1, 2, 3... |
| 18 | doc_2 (text) ──────────► chunk 4, 5... |
| 19 | doc_3 (markdown) ──────► chunk 6... |
| 20 | ``` |
| 21 | |
| 22 | A **collection** holds documents; a **document** is split into **chunks** and embedded; **`rag_query`** searches by cosine similarity across chunks within a collection. |
| 23 | |
| 24 | `chunk_size` and `chunk_overlap` are set **once at collection creation** and immutable — to change them, delete and recreate the collection. |
| 25 | |
| 26 | --- |
| 27 | |
| 28 | ## 2. End-to-end workflow |
| 29 | |
| 30 | ``` |
| 31 | ┌────────────────────────────────────────────┐ |
| 32 | │ 1. create_collection (once per knowledge) │ |
| 33 | ├────────────────────────────────────────────┤ |
| 34 | │ 2. ingest_document (text OR storage_object)│ |
| 35 | ├────────────────────────────────────────────┤ |
| 36 | │ 3. poll get_document_status until "ready" │ |
| 37 | ├────────────────────────────────────────────┤ |
| 38 | │ 4. rag_query (with or without synthesis) │ |
| 39 | └────────────────────────────────────────────┘ |
| 40 | ``` |
| 41 | |
| 42 | ### Step 1 — create the collection |
| 43 | |
| 44 | ```js |
| 45 | manage_rag_content({ |
| 46 | app_id: "app_abc123", |
| 47 | action: "create_collection", |
| 48 | name: "product-faq", |
| 49 | description: "Customer-facing product knowledge", |
| 50 | chunk_size: 512, // optional, default 512 tokens |
| 51 | chunk_overlap: 50, // optional, default 50 tokens |
| 52 | access_mode: "shared" // optional: "private" | "shared" | "custom" |
| 53 | }) |
| 54 | ``` |
| 55 | |
| 56 | | `access_mode` | Who can query | |
| 57 | |----------------|---------------| |
| 58 | | `private` (default) | Only the app owner / service key | |
| 59 | | `shared` | Any authenticated end-user with a valid JWT | |
| 60 | | `custom` | Respects RLS policies — for fine-grained control | |
| 61 | |
| 62 | ### Step 2a — ingest raw text |
| 63 | |
| 64 | ```js |
| 65 | manage_rag_content({ |
| 66 | app_id: "app_abc123", |
| 67 | action: "ingest_document", |
| 68 | collection: "product-faq", |
| 69 | text: "Our return policy is 30 days from purchase...", |
| 70 | filename: "return-policy.txt", // optional, for display |
| 71 | metadata: { category: "returns", tier: "all" } // filter later in rag_query |
| 72 | }) |
| 73 | // → { document_id: "doc_xyz", status: "pending" } |
| 74 | ``` |
| 75 | |
| 76 | ### Step 2b — ingest an uploaded file |
| 77 | |
| 78 | Files come from `manage_storage` first. Two-step: |
| 79 | |
| 80 | ```js |
| 81 | // 1. Upload the file via the storage skill — get an object_id |
| 82 | const { object_id } = await uploadPdfViaStorage(...); |
| 83 | |
| 84 | // 2. Hand that object_id to RAG ingestion |
| 85 | manage_rag_content({ |
| 86 | app_id: "app_abc123", |
| 87 | action: "ingest_document", |
| 88 | collection: "product-faq", |
| 89 | storage_object_id: object_id, |
| 90 | filename: "manual.pdf", |
| 91 | metadata: { product: "v3" } |
| 92 | }) |
| 93 | ``` |
| 94 | |
| 95 | Supported file types: **PDF, TXT, Markdown, CSV, HTML, DOCX, XLSX, PPTX**. |
| 96 | |
| 97 | ### Step 3 — poll until ready |
| 98 | |
| 99 | Ingestion is fire-and-forget. The document moves through `pending → processing → ready` (or `failed`). Poll: |
| 100 | |
| 101 | ```js |
| 102 | manage_rag_content({ |
| 103 | app_id: "app_abc123", |
| 104 | action: "get_document_status", |
| 105 | collection: "product-faq", |
| 106 | document_id: "doc_xyz" |
| 107 | }) |
| 108 | // → { id, filename, status: "processing", processedAt, errorMessage? } |
| 109 | ``` |
| 110 | |
| 111 | Recommended cadence: poll every 2–5 seconds for the first minute, back off after that. Bigger files (large PDFs, XLSX) take longer. |
| 112 | |
| 113 | ### Step 4 — query |
| 114 | |
| 115 | Two modes: raw retrieval (just chunks back) or synthesized (LLM answer + sources). |
| 116 | |
| 117 | #### Raw retrieval |
| 118 | |
| 119 | ```js |
| 120 | rag_query({ |
| 121 | app_id: "app_abc123", |
| 122 | collection: "product-faq", |
| 123 | query: "How long do I have to return an item?", |
| 124 | top_k: 5, // default 5, max 20 |
| 125 | threshold: 0.7, // optional similarity floor (0..1) |
| 126 | filter: { category: "returns" } // optional metadata filter |
| 127 | }) |
| 128 | // → { chunks: [{ text, score, document_id, metadata }, ...] } |
| 129 | ``` |
| 130 | |
| 131 | #### Synthesized answer |
| 132 | |
| 133 | ```js |
| 134 | rag_query({ |
| 135 | app_id: "app_abc123", |
| 136 | collection: "product-faq", |
| 137 | query: "How long do I have to return an item?", |
| 138 | synthesize: true, |
| 139 | model: "anthropic/claude-haiku-4.5" // default |
| 140 | }) |
| 141 | // → { answer, chunks, model } |
| 142 | ``` |
| 143 | |
| 144 | `synthesize: true` runs the retrieved chunks through an LLM and returns a grounded answer. `chunks` is still included so you can show citations. |
| 145 | |
| 146 | --- |
| 147 | |
| 148 | ## 3. Listing and cleanup |
| 149 | |
| 150 | ```js |
| 151 | manage_rag_content({ app_id, action: "list_collections" }) |
| 152 | manage_rag_content({ app_id, action: "get_collection", name: "product-faq" }) |
| 153 | manage_rag_content({ app_id, action: "list_documents", collection: "product-faq" }) |
| 154 | manage_rag_content({ |