Guides

Asking Questions

Get AI-powered answers from your documents

Get AI-powered answers to questions about your documents. EasyRAG finds relevant content and uses GPT-4 to generate accurate, grounded responses.

How It Works

When you ask a question:

  1. Search - EasyRAG finds relevant chunks from your documents
  2. Context - Top results are sent to GPT-4 with your question
  3. Answer - AI generates a response based only on your documents
  4. Stream - Response is delivered in real-time (optional)

The AI is instructed to answer only from your documents and admit when it doesn't know.

Basic Query

Using cURL

bash
curl -X POST https://api.easyrag.com/v1/query \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "datasetId": "my-documents", "question": "What are the key features?" }'

Using JavaScript (Non-Streaming)

javascript
const response = await fetch('https://api.easyrag.com/v1/query', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ datasetId: 'my-documents', question: 'What are the key features?', stream: false }) }); const { data } = await response.json(); console.log('Answer:', data.result);

Response

json
{ "success": true, "data": { "result": "Based on the documentation, the key features include:\n\n1. Automatic document processing for PDFs, Word docs, and spreadsheets\n2. Semantic search that understands meaning, not just keywords\n3. AI-powered question answering using GPT-4\n4. Multi-tenant architecture with complete data isolation\n\nThese features work together to provide a complete RAG solution.", "sources": [ { "pageContent": "The key features include automatic document processing...", "metadata": { "fileId": "f7a3b2c1", "originalName": "features.pdf" } } ] } }

Streaming Responses

Streaming provides a better user experience for chat interfaces - users see responses appear word-by-word.

JavaScript Streaming

javascript
async function streamQuery(datasetId, question) { const response = await fetch('https://api.easyrag.com/v1/query', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ datasetId, question, stream: true }) }); const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const data = JSON.parse(line.slice(6)); if (data.delta) { // New text chunk console.log(data.delta); } else if (data.done) { // Stream complete console.log('Done!'); } else if (data.error) { // Error occurred console.error(data.error); } } } } }

Streaming Response Format

The stream uses Server-Sent Events (SSE):

data: {"delta":"Based on"}

data: {"delta":" the documentation,"}

data: {"delta":" the key features"}

data: {"delta":" include:\n\n"}

data: {"delta":"1. Automatic"}

data: {"delta":" document processing\n"}

data: {"done":true}

React Chat Component

Here's a complete streaming chat interface:

javascript
import { useState, useRef, useEffect } from 'react'; function ChatInterface({ datasetId, token }) { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const messagesEndRef = useRef(null); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; useEffect(scrollToBottom, [messages]); const handleSubmit = async (e) => { e.preventDefault(); if (!input.trim() || loading) return; const userMessage = input; setInput(''); setMessages(prev => [...prev, { role: 'user', content: userMessage }]); setLoading(true); // Add placeholder for assistant const assistantIndex = messages.length + 1; setMessages(prev => [...prev, { role: 'assistant', content: '' }]); try { const response = await fetch('https://api.easyrag.com/v1/query', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ datasetId, question: userMessage, stream: true }) }); const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop(); // Keep incomplete line for (const line of lines) { if (line.startsWith('data: ')) { const data = JSON.parse(line.slice(6)); if (data.delta) { setMessages(prev => { const updated = [...prev]; updated[assistantIndex].content += data.delta; return updated; }); } } } } } catch (error) { console.error('Query failed:', error); setMessages(prev => { const updated = [...prev]; updated[assistantIndex].content = 'Error: Failed to get response'; return updated; }); } finally { setLoading(false); } }; return ( <div style={{ display: 'flex', flexDirection: 'column', height: '600px' }}> {/* Messages */} <div style={{ flex: 1, overflow: 'auto', padding: '20px' }}> {messages.map((msg, i) => ( <div key={i} style={{ margin: '10px 0', padding: '12px', background: msg.role === 'user' ? '#e3f2fd' : '#f5f5f5', borderRadius: '8px', maxWidth: '80%', marginLeft: msg.role === 'user' ? 'auto' : '0' }} > <strong>{msg.role === 'user' ? 'You' : 'Assistant'}:</strong> <div style={{ whiteSpace: 'pre-wrap', marginTop: '5px' }}> {msg.content} </div> </div> ))} <div ref={messagesEndRef} /> </div> {/* Input */} <form onSubmit={handleSubmit} style={{ padding: '20px', borderTop: '1px solid #ccc' }}> <input type="text" value={input} onChange={(e) => setInput(e.target.value)} placeholder="Ask a question..." disabled={loading} style={{ width: '100%', padding: '12px', fontSize: '16px', border: '2px solid #ddd', borderRadius: '8px' }} /> </form> </div> ); } export default ChatInterface;

Filtering Queries

Apply metadata filters to search specific documents:

javascript
const response = await fetch('https://api.easyrag.com/v1/query', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ datasetId: 'company-docs', question: 'What is the vacation policy?', filters: [ { key: 'department', match: { value: 'HR' } }, { key: 'year', match: { value: 2024 } } ] }) });

See Filtering with Metadata for details.

Understanding Responses

Grounded Answers

The AI is instructed to answer only from your documents:

System Prompt:

You are a RAG assistant. Answer ONLY based on the provided context. 
If the context is not enough, say you don't know and do NOT hallucinate.

When There's No Answer

If your documents don't contain the answer:

json
{ "data": { "result": "I don't have enough information in the provided documents to answer that question." } }

Context Format

The AI receives context like this:

### Document 1
Score: 0.892
Source: user-manual.pdf

[chunk content]

---

### Document 2
Score: 0.856
Source: faq.pdf

[chunk content]

---

User question:
What is the refund policy?

Common Use Cases

Customer Support Bot

javascript
const answer = await query( 'support-docs', 'How do I reset my password?' ); // Returns step-by-step instructions from your docs

Internal Knowledge Base

javascript
const answer = await query( 'company-policies', 'What is the work from home policy?', { filters: [ { key: 'department', match: { value: 'HR' } } ] } );

Document Summarization

javascript
const answer = await query( 'legal-docs', 'Summarize the key points of this contract', { filters: [ { key: 'fileId', match: { value: contractFileId } } ] } );

Multi-Language Support

The embeddings work in 100+ languages:

javascript
// Question in Spanish const answer = await query( 'multilingual-docs', '¿Cómo reinicio mi contraseña?' ); // Matches English docs about password reset

Best Practices

1. Use Specific Questions

javascript
// ✅ Good: Specific question "What are the steps to reset my password?" // ❌ Bad: Too vague "password"

2. Handle Empty Context

javascript
const { data } = await query(datasetId, question, { stream: false }); if (data.result.includes("don't have enough information")) { console.log("No relevant documents found"); } else { console.log("Answer:", data.result); }

3. Show Loading States

javascript
const [loading, setLoading] = useState(false); // Show loading indicator {loading && <div>Thinking...</div>}

4. Add Follow-up Questions

javascript
// Suggest related questions const followUps = [ "Can you elaborate on that?", "What are the alternatives?", "Are there any exceptions?" ];

Streaming vs Non-Streaming

FeatureStreamingNon-Streaming
UXBetter (real-time)Slower (wait for full response)
ComplexityMore codeSimpler
Use CaseChat interfacesAPI integrations, batch processing
CostSame (0.1 credit)Same (0.1 credit)

Use streaming when:

  • Building chat interfaces
  • User experience matters
  • Long responses expected

Use non-streaming when:

  • Simple integrations
  • Batch processing
  • Don't need real-time updates

Query vs Search

Feature/v1/query/v1/search
ReturnsAI-generated answerRaw chunks
ProcessingUses GPT-4No LLM
StreamingYesNo
Use CaseReady answersCustom chat UI
Cost0.1 credit0.1 credit

Use /v1/query when:

  • Want ready-to-use answers
  • Building standard chat
  • Don't need custom prompts

Use /v1/search when:

  • Building custom UI
  • Want raw context
  • Using your own LLM

See Searching Documents for search details.

Error Handling

Insufficient Credits

javascript
try { const response = await fetch('https://api.easyrag.com/v1/query', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ datasetId, question }) }); if (response.status === 402) { const error = await response.json(); alert('Out of credits! Please top up.'); return; } const data = await response.json(); console.log(data); } catch (error) { console.error('Query failed:', error); }

Stream Errors

javascript
// In your streaming loop if (data.error) { console.error('Stream error:', data.error); // Show error message to user setError(data.error); break; }

Billing

Each query costs 0.1 credit, regardless of streaming mode.

OperationCost
1 query0.1 credit
10 queries1 credit
100 queries10 credits

The cost includes both retrieval and GPT-4 generation.

Troubleshooting

Generic Answers

Problem: AI gives vague responses

Solutions:

  • Ask more specific questions
  • Upload more detailed documents
  • Check if relevant docs are indexed
  • Use filters to narrow scope

Wrong Information

Problem: AI provides incorrect info

Solutions:

  • Verify source documents are correct
  • Check relevance scores in search results
  • Update outdated documents
  • Add more comprehensive content

Slow Responses

Problem: Query takes too long

Solutions:

  • Use streaming for better perceived performance
  • Reduce dataset size with filters
  • Check your internet connection
  • Contact support if persistent

Next Steps

API Reference

For technical details, see Query API Reference.