How to Use RAG to Answer Questions from Company Documents
Use Retrieval-Augmented Generation to get accurate answers from your business documents.
What This Integration Does
Retrieval-Augmented Generation (RAG) is the answer to AI's hallucination problem. Instead of letting a language model answer from training data of unknown provenance, you retrieve passages from documents you trust and ground the answer in those passages. The result is an answer that quotes your handbook, your contracts, and your runbooks, and politely says "I don't know" when the answer isn't there. In Spojit, the Knowledge node handles both halves of this in a single step: it retrieves the top-matching chunks from a collection and synthesizes a grounded answer with an AI model you choose.
This workflow exposes a question-answering endpoint over a Knowledge collection of company documents. At run time a Webhook Trigger receives the question, the Knowledge node in Query mode retrieves the most relevant chunks and composes a grounded answer, and a Response node returns the answer and its sources to the caller. The same pattern slots into Slack notifications, internal admin tools, and downstream workflows that need to reason over your documents.
Prerequisites
- A populated persistent Knowledge collection: handbook, policies, runbooks, customer-success playbooks, whatever fits the use case. Build one first with the Knowledge node in Embed mode, or upload documents in the Knowledge section of the sidebar.
- Note which embedding model the collection was created with. The Query must use the same collection, so the embedding model is already fixed.
- Optional: a Slack connection if you want to post answers to a channel.
Step 1: Webhook Trigger
Add a Trigger node and set its type to Webhook. The trigger gives you a URL that external callers POST to, and verifies the request via a signing connection. The parsed JSON body becomes the trigger output: expect at least a question field, available downstream as {{ input.question }}. Because you will return a synchronous answer with a Response node, keep the workflow short and fast.
Step 2: Knowledge Node - Query Mode
Add a Knowledge node and set its mode to Query. This node retrieves the most relevant chunks from your collection and synthesizes a grounded answer in one step. Configure:
- Collection: pick your persistent company-documents collection from the dropdown.
- Prompt: a natural-language instruction that forwards the caller's question, for example
Answer the following question using only our company documents: {{ input.question }}. - Model: the AI model used to synthesize the answer from the retrieved chunks.
- Result Count: how many chunks to retrieve before synthesis. The default is
5; raise it to8for broader questions, lower it for tight factual lookups. - Output Variable: name it
answerso later steps can read{{ answer }}.
The node grounds its answer in the retrieved passages, so an out-of-scope question yields a "not found" style reply rather than an invented one.
Step 3: Force Structured Output with a Response Schema
So that callers get machine-readable results instead of free text, add a Response Schema to the Knowledge Query node. The schema forces the model to return JSON with the answer text, a confidence-style flag, and a list of source documents:
{
"type": "object",
"properties": {
"answer": { "type": "string" },
"found": { "type": "boolean" },
"sources": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["answer", "found", "sources"]
}
With the schema in place, {{ answer.found }}, {{ answer.answer }}, and {{ answer.sources }} are all available to later nodes. Instruct the model in the Prompt to set found to false and leave sources empty when the documents do not cover the question.
Step 4: Condition - Branch on Whether an Answer Was Found
Add a Condition node that checks {{ answer.found }}. On the true branch you continue to the response; on the false branch you can return a friendly "I don't have that in our documents" message. Splitting the branches gives you a clean signal for content gaps: every false result is a question your documents do not yet answer.
Step 5: Respond to the Caller
Add a Response node to return the result to the synchronous webhook caller. Return the structured object directly, for example { "answer": "{{ answer.answer }}", "sources": "{{ answer.sources }}" }. Because the schema already shaped the data, downstream consumers (a browser UI, an admin tool, an audit log) can render the answer and its sources without extra parsing.
Step 6: Optional - Post the Answer to Slack
If you want answers to land in a channel as well as the HTTP response, add a Connector node in Direct mode using the Slack connector and the send-message tool. Map the text input to {{ answer.answer }} and append the source list. Run this alongside the Response node so the caller still gets an immediate reply.
Tips
- Quote, do not paraphrase: instruct the model in the Prompt to use exact wording for any factual claim. Paraphrased answers drift quickly under pressure.
- Tune Result Count: raising
Result Count pulls in more context for broad questions but adds noise to focused ones. Start at the default of5and adjust by question type. - Scope with separate collections: collections are workspace-scoped, so keep distinct corpora (handbook vs runbooks vs customer playbooks) in separate collections and point each Query at the right one. This is cleaner than mixing everything into one collection.
- Let Miraxa scaffold it: describe the workflow to Miraxa, the intelligent layer across your automation, and it can add the Trigger, Knowledge, Condition, and Response nodes for you, then fine-tune fields in the properties panel.
Common Pitfalls
- Letting the model "be helpful": without an explicit refusal instruction in the Prompt, the model may invent answers when retrieval misses. Always include the "if not in our documents, say so" clause and the
found flag.
- Stale collection: a Query is only as good as the embedded documents. If you do not re-embed when source documents change, the answer will confidently quote last quarter's policy. Re-upload and re-embed on change.
- Mismatched embedding model: always embed and query a collection with the same embedding model. The model is fixed when the collection is created, so reuse the same collection rather than creating a new one with different settings.
- Mixing audiences: if a collection mixes customer-facing and internal-only content, a public-facing endpoint can surface the wrong material. Keep sensitive content in a separate collection.
Testing
Create a question set: five questions you can verify by hand against a single source document, and five questions that are deliberately not covered. Run all ten through the workflow. Confirm the first five return correct answers with the right citations, and the second five fall through the refusal path cleanly. Only then expose the endpoint to the wider team.
Learn More