1import{j as e}from"./index-BkgFiefw.js";let a={title:"Knowledge base",description:"Upload, attach, and cite â RAG for the Workspace chat product."},r=[{href:"/docs/api-reference"},{href:"/docs/cloud/workspace/for-admins"},{href:"/docs/cloud/workspace/for-employees"}],o={contents:[{heading:void 0,content:`The knowledge base is how you make Workspace smarter about your 2specific business. Upload PDFs, paste in policies, point at URLs; 3attach them to assistants; chats with that assistant cite the 4sources inline.`},{heading:"lifecycle",content:`Upload. Workspace â Knowledge Base â Upload PDF or paste 5text. Each source gets a UUID and a pending status.`},{heading:"lifecycle",content:`Ingest. The worker chunks the source into ~1000-char 6windows, embeds each chunk via OpenAI text-embedding-3-small, 7stores them in PostgreSQL (with pgvector when available). 8Status flips pending â processing â ready.`},{heading:"lifecycle",content:`Attach. Edit an assistant â Knowledge base sources â 9check the source. The join table records the attachment.`},{heading:"lifecycle",content:`Retrieve. When a user sends a message in a chat with 10that assistant, the gateway runs RAG: top-K most relevant 11chunks (by cosine similarity, falling back to keyword match) 12are pulled and added to the system prompt.`},{heading:"lifecycle",content:`Cite. The assistant's reply is persisted
12with the unique 13sources used as metadata. The chat UI renders source chips 14below the response.`},{heading:"supported-formats",content:"PDF (extracted via pdftotext)"},{heading:"supported-formats",content:"Plain text (markdown supported, just paste it in)"},{heading:"supported-formats",content:`URL (Bastio fetches and extracts the readable content; useful 15for public documentation pages)`},{heading:"supported-formats",content:`Word docs, slides, and image-OCR are not yet supported. Convert 16to PDF first.`},{heading:"sizing--limits",content:"Max file size: 25 MB"},{heading:"sizing--limits",content:`Max chunks per source: 5,000 (covers ~3,000 pages of plain 17text)`},{heading:"sizing--limits",content:`Workspace-wide chunk total drives retrieval latency. Up to 18~10k chunks runs in-process; pgvector's HNSW index kicks in 19beyond that and scales to ~1M chunks per workspace.`},{heading:"vector-vs-keyword-retrieval",content:`If your environment has OPENAI_API_KEY configured (cloud: 20always; OSS: optional), embeddings drive retrieval â much 21better recall. Without embeddings, Bastio falls back to a 22keyword-overlap search that handles ~80% of practical cases.`},{heading:"vector-vs-keyword-retrieval",content:`The chat surface looks identical either way; the source chips 23still render the same.`},{heading:"attaching-multiple-sources",content:`One assistant, many sources. The workspace handbook and the 24brand guide can both attach to the "Acme Helper" assistant â 25when a user asks "what's our return policy + how do we phrase 26it on social?" both show up as citations.`},{heading:"citations-in-the-ui",content:"The assistant's reply persists with metadata like:"},{heading:"citations-in-the-ui",content:`The chat UI renders one chip per unique source under the reply. 27Hover for the full source name. (Click-to-open is on the 28roadmap.)`},{heading:"what-gets-retrieved",content:`Bastio retrieves chunks at message-send time, not at attach 29time, so the context reflects the current user's prompt:`},{heading:"what-gets-retrieved",content:`"What's our PTO policy?" â handbook chunks about PTO win.`},{heading:"what-gets-retrieved",content:`"How do I greet a customer?" â brand-guide chunks about tone 30win, even though the same handbook is attached.`},{heading:"what-gets-retrieved",content:"Top-K defaults to 5 chunks. Tunable per workspace in settings."},{heading:"updating-a-source",content:`Re-uploading the same name re-ingests with a fresh chunk set. 31Old chunks are archived (not deleted), so existing 32conversations that cited them still display the citation 33correctly. Only new conversations see the new content.`},{heading:"detaching-a-source",content:`Edit the assistant â uncheck the source. The join row is 34removed. Existing conversations with that assistant continue to 35show old citations in their history, but new turns won't 36retrieve from the detached source.`},{heading:"archiving-a-source",content:`Knowledge Base tab â trash icon. Sets archived_at = NOW(). 37The source no longer appears in the picker for new attachments, 38but rows that were already attached keep working until the 39admin detaches them. Audited as knowledge.archived.`},{heading:"common-pitfalls",content:`No assistant attached â no RAG. A workspace with KB 40uploaded but no assistant attached gets no citations. Make 41sure the assistant the user picks has at least one source 42attached.`},{heading:"common-pitfalls",content:`Chat says "stuck on pending" for a PDF upload. Check that 43the worker is running. In dev, all-in-one mode handles this 44automatically; in split-binary prod, ensure cmd/worker is 45up. River migrations run on every server start since v2.`},{heading:"common-pitfalls",content:`Citations list is empty for a chat with KB attached. The 46retrieval found no relevant chunks. Either the question is 47far from your corpus, or your top-K threshold is too high. 48Look at the assistant's "Trace" tab for the retrieval scores 49per chunk.`},{heading:"api-surface",content:"For CI / programmatic use:"},{heading:"api-surface",content:"GET /v1/workspace/knowledge â list sources"},{heading:"api-surface",content:"POST /v1/workspace/knowledge/upload â multipart upload"},{heading:"api-surface",content:"POST /v1/workspace/knowledge â inline-text source"},{heading:"api-surface",content:"DELETE /v1/workspace/knowledge/{id} â archive"},{heading:"api-surface",content:`PATCH /v1/workspace/assistants/{id} with 50knowledge_source_ids: ["uuid1", "uuid2"] â attach (replaces 51the full set)`},{heading:"api-surface",content:`See the API reference on your running 52gateway for the full schema.`},{heading:"next",content:`For admins â the rest of 53the admin surface.`},{heading:"next",content:`For employees â what 54the citation chips look like to your team.`}],headings:[{id:"lifecycle",content:"Lifecycle"},{id:"supported-formats",content:"Supported formats"},{id:"sizing--limits",content:"Sizing + limits"},{id:"vector-vs-keyword-retrieval",content:"Vector vs. keyword retrieval"},{id:"attaching-multiple-sources",content:"Attaching multiple sources"},{id:"citations-in-the-ui",content:"Citations in the UI"},{id:"what-gets-retrieved",content:"What gets retrieved"},{id:"updating-a-source",content:"Updating a source"},{id:"detaching-a-source",content:"Detaching a source"}
54,{id:"archiving-a-source",content:"Archiving a source"},{id:"common-pitfalls",content:"Common pitfalls"},{id:"api-surface",content:"API surface"},{id:"next",content:"Next"}]};const h=[{depth:2,url:"#lifecycle",title:e.jsx(e.Fragment,{children:"Lifecycle"})},{depth:2,url:"#supported-formats",title:e.jsx(e.Fragment,{children:"Supported formats"})},{depth:2,url:"#sizing--limits",title:e.jsx(e.Fragment,{children:"Sizing + limits"})},{depth:2,url:"#vector-vs-keyword-retrieval",title:e.jsx(e.Fragment,{children:"Vector vs. keyword retrieval"})},{depth:2,url:"#attaching-multiple-sources",title:e.jsx(e.Fragment,{children:"Attaching multiple sources"})},{depth:2,url:"#citations-in-the-ui",title:e.jsx(e.Fragment,{children:"Citations in the UI"})},{depth:2,url:"#what-gets-retrieved",title:e.jsx(e.Fragment,{children:"What gets retrieved"})},{depth:2,url:"#updating-a-source",title:e.jsx(e.Fragment,{children:"Updating a source"})},{depth:2,url:"#detaching-a-source",title:e.jsx(e.Fragment,{children:"Detaching a source"})},{depth:2,url:"#archiving-a-source",title:e.jsx(e.Fragment,{children:"Archiving a source"})},{depth:2,url:"#common-pitfalls",title:e.jsx(e.Fragment,{children:"Common pitfalls"})},{depth:2,url:"#api-surface",title:e.jsx(e.Fragment,{children:"API surface"})},{depth:2,url:"#next",title:e.jsx(e.Fragment,{children:"Next"})}];function n(s){const t={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...s.components};return e.jsxs(e.Fragment,{children:[e.jsx(t.p,{children:`The knowledge base is how you make Workspace smarter about your 55specific business. Upload PDFs, paste in policies, point at URLs; 56attach them to assistants; chats with that assistant cite the 57sources inline.`}),` 58`,e.jsx(t.h2,{id:"lifecycle",children:"Lifecycle"}),` 59`,e.jsx(e.Fragment,{children:e.jsx(t.pre,{className:"shiki shiki-themes github-light github-dark",style:{"--shiki-light":"#24292e","--shiki-dark":"#e1e4e8","--shiki-light-bg":"#fff","--shiki-dark-bg":"#24292e"},tabIndex:"0",icon:'<svg viewBox="0 0 24 24"><path d="M 6,1 C 4.354992,1 3,2.354992 3,4 v 16 c 0,1.645008 1.354992,3 3,3 h 12 c 1.645008,0 3,-1.354992 3,-3 V 8 7 A 1.0001,1.0001 0 0 0 20.707031,6.2929687 l -5,-5 A 1.0001,1.0001 0 0 0 15,1 h -1 z m 0,2 h 7 v 3 c 0,1.645008 1.354992,3 3,3 h 3 v 11 c 0,0.564129 -0.435871,1 -1,1 H 6 C 5.4358712,21 5,20.564129 5,20 V 4 C 5,3.4358712 5.4358712,3 6,3 Z M 15,3.4140625 18.585937,7 H 16 C 15.435871,7 15,6.5641288 15,6 Z" fill="currentColor" /></svg>',children:e.jsx(t.code,{children:e.jsx(t.span,{className:"line",children:e.jsx(t.span,{children:"upload â ingest â ready â attach â retrieve â cite"})})})})}),` 60`,e.jsxs(t.ol,{children:[` 61`,e.jsxs(t.li,{children:[e.jsx(t.strong,{children:"Upload."}),` Workspace â Knowledge Base â Upload PDF or paste 62text. Each source gets a UUID and a `,e.jsx(t.code,{children:"pending"})," status."]}),` 63`,e.jsxs(t.li,{children:[e.jsx(t.strong,{children:"Ingest."}),` The worker chunks the source into ~1000-char 64windows, embeds each chunk via OpenAI text-embedding-3-small, 65stores them in PostgreSQL (with pgvector when available). 66Status flips `,e.jsx(t.code,{children:"pending â processing â ready"}),"."]}),` 67`,e.jsxs(t.li,{children:[e.jsx(t.strong,{children:"Attach."}),` Edit an assistant â Knowledge base sources â 68check the source. The join table records the attachment.`]}),` 69`,e.jsxs(t.li,{children:[e.jsx(t.strong,{children:"Retrieve."}),` When a user sends a message in a chat with 70that assistant, the gateway runs RAG: top-K most relevant 71chunks (by cosine similarity, falling back to keyword match) 72are pulled and added to the system prompt.`]}),` 73`,e.jsxs(t.li,{children:[e.jsx(t.strong,{children:"Cite."}
73),` The assistant's reply is persisted with the unique 74sources used as metadata. The chat UI renders source chips 75below the response.`]}),` 76`]}),` 77`,e.jsx(t.h2,{id:"supported-formats",children:"Supported formats"}),` 78`,e.jsxs(t.ul,{children:[` 79`,e.jsx(t.li,{children:"PDF (extracted via pdftotext)"}),` 80`,e.jsx(t.li,{children:"Plain text (markdown supported, just paste it in)"}),` 81`,e.jsx(t.li,{children:`URL (Bastio fetches and extracts the readable content; useful 82for public documentation pages)`}),` 83`]}),` 84`,e.jsx(t.p,{children:`Word docs, slides, and image-OCR are not yet supported. Convert 85to PDF first.`}),` 86`,e.jsx(t.h2,{id:"sizing--limits",children:"Sizing + limits"}),` 87`,e.jsxs(t.ul,{children:[` 88`,e.jsx(t.li,{children:"Max file size: 25 MB"}),` 89`,e.jsx(t.li,{children:`Max chunks per source: 5,000 (covers ~3,000 pages of plain 90text)`}),` 91`,e.jsx(t.li,{children:`Workspace-wide chunk total drives retrieval latency. Up to 92~10k chunks runs in-process; pgvector's HNSW index kicks in 93beyond that and scales to ~1M chunks per workspace.`}),` 94`]}),` 95`,e.jsx(t.h2,{id:"vector-vs-keyword-retrieval",children:"Vector vs. keyword retrieval"}),` 96`,e.jsxs(t.p,{children:["If your environment has ",e.jsx(t.code,{children:"OPENAI_API_KEY"}),` configured (cloud: 97always; OSS: optional), embeddings drive retrieval â much 98better recall. Without embeddings, Bastio falls back to a 99keyword-overlap search that handles ~80% of practical cases.`]}),` 100`,e.jsx(t.p,{children:`The chat surface looks identical either way; the source chips 101still render the same.`}),` 102`,e.jsx(t.h2,{id:"attaching-multiple-sources",children:"Attaching multiple sources"}),` 103`,e.jsx(t.p,{children:`One assistant, many sources. The workspace handbook and the 104brand guide can both attach to the "Acme Helper" assistant â 105when a user asks "what's our return policy + how do we phrase 106it on social?" both show up as citations.`}),` 107`,e.jsx(t.h2,{id:"citations-in-the-ui",children:"Citations in the UI"}),` 108`,e.jsx(t.p,{children:"The assistant's reply persists with metadata like:"}),` 109`,e.jsx(e.Fragment,{children:e.jsx(t.pre,{className:"shiki shiki-themes github-light github-dark",style:{"--shiki-light":"#24292e","--shiki-dark":"#e1e4e8","--shiki-light-bg":"#fff","--shiki-dark-bg":"#24292e"},tabIndex:"0",icon:'<svg viewBox="0 0 24 24"><path d="M 6,1 C 4.354992,1 3,2.354992 3,4 v 16 c 0,1.645008 1.354992,3 3,3 h 12 c 1.645008,0 3,-1.354992 3,-3 V 8 7 A 1.0001,1.0001 0 0 0 20.707031,6.2929687 l -5,-5 A 1.0001,1.0001 0 0 0 15,1 h -1 z m 0,2 h 7 v 3 c 0,1.645008 1.354992,3 3,3 h 3 v 11 c 0,0.564129 -0.435871,1 -1,1 H 6 C 5.4358712,21 5,20.564129 5,20 V 4 C 5,3.4358712 5.4358712,3 6,3 Z M 15,3.4140625 18.585937,7 H 16 C 15.435871,7 15,6.5641288 15,6 Z" fill="currentColor" /></svg>',children:e.jsxs(t.code,{children:[e.jsx(t.span,{className:"line",children:e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:"{"})}),` 110`,e.jsxs(t.span,{className:"line",children:[e.jsx(t.span,{style:{"--shiki-light":"#005CC5","--shiki-dark":"#79B8FF"},children:' "citations"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": ["})]}),` 111`,e.jsxs(t.span,{className:"line",children:[e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:" { "}),e.jsx(t.span,{style:{"--shiki-light":"#005CC5","--shiki-dark":"#79B8FF"},children:'"source_id"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),e.jsx(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:'"0193-â¦"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),e.jsx(t.span,{style:{"--shiki-light":"#005CC5","--shiki-dark":"#79B8FF"},children:'"source_name"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),e.jsx(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:'"employee-handbook.pdf"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:" },"})]}),` 112`,e.jsxs(t.span,{className:"line",children:[e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:" { "}),e.jsx(t.span,{style:{"--shiki-light":"#005CC5","--shiki-dark":"#79B8FF"},children:'"source_id"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),e.jsx(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:'"0193-â¦"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),e.jsx(t.span,{style:{"--shiki-light":"#005CC5","--shiki-dark":"#79B8FF"},children:'"source_name"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),e.jsx(t.span,{style:{"--shiki-light":"#032F62","--shiki-dark":"#9ECBFF"},children:'"brand-guide.md"'}),e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})]}),` 113`,e.jsx(t.span,{className:"line",children:e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:" ]"})}),` 114`,e.jsx(t.span,{className:"line",children:e.jsx(t.span,{style:{"--shiki-light":"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})})]})})}),` 115`,e.jsx(t.p,{children:`The chat UI renders one chip per unique source under the reply. 116Hover for the full source name. (Click-to-open is on the 117roadmap.)`}),` 118`,e.jsx(t.h2,{id:"what-gets-retrieved",children:"What gets retrieved"}),` 119`,e.jsxs(t.p,{children:[`Bastio retrieves chunks at message-send time, not at attach 120time, so the context reflects the `,e.jsx(t.em,{children:"current"})," user's prompt:"]}),` 121`,e.jsxs(t.ul,{children:[` 122`,e.jsx(t.li,{children:`"What's our PTO policy?" â handbook chunks about PTO win.`}),` 123`,e.jsx(t.li,{children:`"How do I greet a customer?" â brand-guide chunks about tone 124win, even though the same handbook is attached.`}),` 125`]}),` 126`,e.jsx(t.p,{children:"Top-K defaults to 5 chunks. Tunable per workspace in settings."}),` 127`,e.jsx(t.h2,{id:"updating-a-source",children:"Updating a source"}),` 128`,e.jsx(t.p,{children:`Re-uploading the same name re-ingests with a fresh chunk set. 129Old chunks are archived (not deleted), so existing 130conversations that cited them still display the citation 131correctly. Only new conversations see the new content.`}),` 132`,e.jsx(t.h2,{id:"detaching-a-source",children:"Detaching a source"}),` 133`,e.jsx(t.p,{children:`Edit the assistant â uncheck the source. The join row is 134removed. Existing conversations with that assistant continue to 135show old citations in their history, but new turns won't 136retrieve from the detached source.`}),` 137`,e.jsx(t.h2,{id:"archiving-a-source",children:"Archiving a source"}),` 138`,e.jsxs(t.p,{children:["Knowledge Base tab â trash icon. Sets ",e.jsx(t.code,{children:"archived_at = NOW()"}),`. 139The source no longer appears in the picker for new attachments, 140but rows that were already attached keep working until the 141admin detaches them. Audited as `,e.jsx(t.code,{children:"knowledge.archived"}),"."]}),` 142`,e.jsx(t.h2,{id:"common-pitfalls",children:"Common pitfalls"}),` 143`,e.jsxs(t.ul,{children:[` 144`,e.jsxs(t.li,{children:[e.jsx(t.strong,{children:"No assistant attached â no RAG."}),` A workspace with KB 145uploaded but no assistant attached gets no citations. Make 146sure the assistant the user picks has at least one source 147attached.`]}),` 148`,e.jsxs(t.li,{children:[e.jsx(t.strong,{children:'Chat says "stuck on pending" for a PDF upload.'}),` Check that 149the worker is running. In dev, all-in-one mode handles this 150automatically; in split-binary prod, ensure `,e.jsx(t.code,{children:"cmd/worker"}),` is 151up. River migrations run on every server start since v2.`]}),` 152`,e.jsxs(t.li,{children:[e.jsx(t.strong,{children:"Citations list is empty for a chat with KB attached."}),` The 153retrieval found no relevant chunks. Either the question is 154far from your corpus, or your top-K threshold is too high. 155Look at the assistant's "Trace" tab for the retrieval scores 156per chunk.`]}),` 157`]}),`
158`,e.jsx(t.h2,{id:"api-surface",children:"API surface"}),` 159`,e.jsx(t.p,{children:"For CI / programmatic use:"}),` 160`,e.jsxs(t.ul,{children:[` 161`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"GET /v1/workspace/knowledge"})," â list sources"]}),` 162`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"POST /v1/workspace/knowledge/upload"})," â multipart upload"]}),` 163`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"POST /v1/workspace/knowledge"})," â inline-text source"]}),` 164`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"DELETE /v1/workspace/knowledge/{id}"})," â archive"]}),` 165`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"PATCH /v1/workspace/assistants/{id}"}),` with 166`,e.jsx(t.code,{children:'knowledge_source_ids: ["uuid1", "uuid2"]'}),` â attach (replaces 167the full set)`]}),` 168`]}),` 169`,e.jsxs(t.p,{children:["See the ",e.jsx(t.a,{href:"/docs/api-reference",children:"API reference"}),` on your running 170gateway for the full schema.`]}),` 171`,e.jsx(t.h2,{id:"next",children:"Next"}),` 172`,e.jsxs(t.ul,{children:[` 173`,e.jsxs(t.li,{children:[e.jsx(t.a,{href:"/docs/cloud/workspace/for-admins",children:"For admins"}),` â the rest of 174the admin surface.`]}),` 175`,e.jsxs(t.li,{children:[e.jsx(t.a,{href:"/docs/cloud/workspace/for-employees",children:"For employees"}),` â what 176the citation chips look like to your team.`]}),` 177`]})]})}function c(s={}){const{wrapper:t}=s.components||{};return t?e.jsx(t,{...s,children:e.jsx(n,{...s})}):n(s)}export{c as default,r as extractedReferences,a as frontmatter,o as structuredData,h as toc};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.