Ask questions about your documents and get intelligent answers with source citations. Cortex's Ask AI feature uses Retrieval-Augmented Generation (RAG) with optional agentic reasoning for complex queries. The primary interface is the streaming endpoint, which delivers results in real time via Server-Sent Events (SSE).
Frontend UI
In the Cortex frontend, Ask AI is accessed via the Explore section with two dedicated tabs:
| Tab | Description | Best For |
|---|---|---|
| Deep Research | Multi-step agentic reasoning that breaks down complex questions into sub-queries | Complex questions requiring thorough analysis |
| Chat | Quick hybrid search with knowledge graph context | Fast lookups and straightforward questions |
Both modes share a simplified input interface with:
- A text input field (placeholder varies by mode)
- A Settings button (cog icon) to configure collection scope and streaming
- A Send button that activates when you type
- A collection indicator below the input showing the active search scope
The settings dropdown allows you to:
- Toggle Stream responses on/off (for debugging)
- Select a Collection Scope — defaults to All Collections so searches span the entire knowledge base. Select a specific collection to narrow results to that collection's documents only.
Response Layout
In the UI, responses are rendered in this order:
- Research Process (Deep Research only) — Sub-Questions, Thinking Steps, and Reasoning Steps are displayed above the main answer content. The research process section auto-scrolls to the bottom during streaming so the latest activity is always visible.
- Content — The main answer from the writer LLM.
- Graph Context — Related entities, relationships, and communities.
- Sources — Retrieved source documents with citations.
Source Citation Viewer
Clicking a source opens a modal that displays the full document content with the cited chunk highlighted. The surrounding text is shown at 60% opacity while the cited chunk appears at full opacity with an accent-colored left border. The modal auto-scrolls to bring the highlighted chunk into view.
How It Works
Both modes use a researcher/writer agent architecture. An LLM-driven researcher agent iteratively gathers information from the knowledge base using tool-calling, then a separate writer LLM synthesizes the gathered context into a streamed answer.
The researcher agent has access to these tools:
| Tool | Speed Mode | Quality Mode | Description |
|---|---|---|---|
knowledge_search | 1 call | 3-5+ calls | Hybrid RRF search (vector + keyword + graph) with cross-encoder reranking. Up to 3 queries per call. |
community_search | - | Yes | Search entity community summaries for thematic context |
entity_lookup | - | Yes | Look up specific entities by name for deeper exploration |
reasoning | - | Yes | Plan next research step (streamed to UI as thinking events) |
done | Yes | Yes | Signal research completion with a summary for the writer |
Streaming Endpoint
Code
This is the primary endpoint for asking questions. It returns a stream of Server-Sent Events containing sources, graph context, and answer tokens as they are generated.
Authentication
All requests require the X-API-Key header:
Code
Request Schema (RAGRequest)
| Parameter | Type | Default | Description |
|---|---|---|---|
question | string | required | The question to ask |
top_k | integer | 5 | Number of results to retrieve (1–20) |
use_reranking | boolean | true | Apply cross-encoder reranking to improve relevance |
use_graph | boolean | true | Include knowledge graph context in retrieval |
max_hops | integer | 2 | Graph traversal depth (1–3 hops) |
use_agentic | boolean | false | Enable deep research mode with multi-step reasoning |
use_fast_search | boolean | false | Use fast vector-only search (disables hybrid/reranking) |
collection_id | string | null | null | Scope search to a specific collection |
conversation_history | ConversationMessage[] | null | Previous messages for multi-turn context |
conversation_memory | object | null | null | Opt-in client-carried memory blob (see Conversation Memory). Omit for stateless behavior. |
ConversationMessage Schema
Code
The backend keeps the most recent messages (configured by MAX_CONVERSATION_HISTORY, default 6).
Two Modes
1. Chat Mode (Hybrid + Reranking)
The default mode for quick questions. Performs hybrid search (vector + keyword + graph), applies cross-encoder reranking, enriches with graph context, then streams the LLM answer.
Code
Event sequence: sources → graph_context → content (multiple) → done
Code
2. Deep Research Mode (Agentic)
For complex questions requiring multi-angle investigation. The researcher agent iteratively searches, explores communities, looks up entities, and cross-references information before handing off to the writer for a comprehensive answer (up to 4000 tokens).
The reflect-then-search rhythm is enforced by the loop, not left to the model: a search round the model didn't reason about triggers a forced reflection step (RESEARCHER_FORCE_REFLECTION), consecutive rounds that mostly re-surface already-seen sources end research early (RESEARCHER_NOVELTY_MIN_NEW_RATIO / RESEARCHER_NOVELTY_STALE_ROUNDS), and a wall-clock budget (RESEARCHER_WALL_CLOCK_SECONDS, default 120s) guarantees the answer starts even when the LLM provider is queueing. Answer quality is consistent across tool-calling models — including ones that don't reflect on their own.
Requires ENABLE_AGENTIC_RAG=true and ENABLE_AGENT_RESEARCH=true on the server.
Code
Event sequence: thinking (reasoning + search progress) → retrieval (per search) → sources → graph_context → retrieval_stats → content (multiple) → done
Code
SSE Event Reference
Every event is a JSON object on a data: line. Each event contains exactly one of these keys:
| Event Key | Type | Mode | Description |
|---|---|---|---|
status | {stage, message} | All (if stream_reasoning_steps) | Current pipeline stage — analyzing/searching/reranking/generating. Drives a live "working" indicator; removes the silent pre-token window |
content | string | All | A token of the streamed answer |
sources | SearchResult[] | All | Retrieved source documents with scores |
graph_context | object | All | Knowledge graph entities, relationships, and community data |
thinking | string | Deep Research | Status message describing the current reasoning step |
sub_questions | string[] | Deep Research | The decomposed research sub-questions |
retrieval | string | Deep Research | Per-sub-question retrieval progress |
retrieval_stats | object | Deep Research | Summary: total_sources, unique_sources, communities_used |
done | boolean | All | true when the answer is complete. On memory-carrying turns it includes pending_memory: true — a memory_update event still follows before the stream closes |
pending_memory | boolean | On the done event | Present (true) when a memory_update will follow the done event — keep reading the stream |
error | string | All | Error message if something went wrong |
communities_used | number[] | Deep Research | Community IDs used, included in the done event |
memory_update | object | When conversation_memory sent | Updated memory blob to store and replay next turn — emitted after done by default (see Conversation Memory) |
Each source object in sources also carries a stable sid (string) for cross-turn citation continuity.
Event order note: done fires as soon as the last answer token is streamed (finalize your UI there), but it is not necessarily the final frame: with EMIT_DONE_BEFORE_MEMORY=true (the default) the post-answer memory compaction runs afterwards and memory_update arrives 1–4 s later, then the stream closes. Consume the stream to its actual end — a client that stops reading at done loses memory continuity. Set EMIT_DONE_BEFORE_MEMORY=false to restore the legacy order (memory_update → done).
During silent windows (≥ 8 s with no event), the stream also emits SSE comment keep-alives (: ping) — ignored by the SSE spec and clients, present only to prevent proxy idle-timeouts.
Source Object Shape
Code
Conversation History
Maintain context across multiple questions by passing previous messages:
Code
The backend automatically trims conversation history to the most recent messages (default: 6, configurable via MAX_CONVERSATION_HISTORY).
Conversation Memory
For long conversations, raw history trimming silently forgets older turns. Conversation Memory is an opt-in, client-carried alternative: the backend stays stateless, the client round-trips an opaque conversation_memory blob, and the agent curates a bounded, knowledge-grounded context from it each turn. Omit the field for today's stateless behavior — it's fully backward-compatible.
How to use it:
- Start with
"conversation_memory": {}(or omit on turn 1). - Read the
memory_updateSSE event emitted at the end of each turn — by default it arrives after thedoneevent (which carriespending_memory: true), so keep consuming the stream until it actually closes. - Send that blob back as
conversation_memoryon the next turn (and keep sending the fullconversation_history).
Code
What it gives you:
- No amnesia — older turns fold into a rolling summary plus durable
facts/open_questions/intent, rebuilt into a small fixed context each turn (lower cost and latency than ever-growing history fed twice). - Citation continuity — every source in the
sourcesevent carries a conversation-stablesid, accumulated insource_ledger, so a source cited in turn 2 keeps its identity in turn 5. - Memory fast-path — follow-ups answerable from memory alone ("summarize that", "why?", "in German") skip retrieval entirely for a fast, cheap response (toggle with
ENABLE_MEMORY_FAST_PATH).
Compaction runs after the answer streams (a cheap fast-model call), so it adds no user-visible latency. Treat the blob as opaque — its shape may grow; only ever store and replay what the server returns.
Collection Scope
By default, both Chat and Deep Research search across all collections in your knowledge base. This means every document you've ingested is available for retrieval regardless of which collection it belongs to.
To narrow results to a specific collection, pass collection_id in your API request:
Code
When collection_id is omitted (or null), all collections are searched. When provided, all retrieval steps (vector search, keyword search, and graph traversal) are filtered to that collection's data. Collection scoping works identically in both Chat and Deep Research modes.
In the UI: Click the Settings (cog) icon next to the send button and use the Collection Scope dropdown. The dropdown defaults to All Collections and shows a persistent indicator below the input confirming the active scope. Select a specific collection to filter, or select "All Collections" to search everything.
Frontend Integration (JavaScript/TypeScript)
Consume the SSE stream using fetch and ReadableStream:
Code
Python Integration
Using httpx (recommended)
Code
Using requests
Code
Multi-Turn Conversation
Code
Configuration
Code
Agent vs Legacy Pipeline
The agent pipeline (ENABLE_AGENT_RESEARCH=true, default) provides adaptive research depth and reasoning transparency but has specific requirements and trade-offs:
| Agent Pipeline | Legacy Pipeline | |
|---|---|---|
| LLM requirement | Must support function calling / tool use (OpenAI tools parameter) | Any OpenAI-compatible chat endpoint |
| Compatible models | GPT-4o, GPT-4o-mini, Claude, Mistral Large, Command R+ | Any model (including local Ollama/vLLM) |
| Token usage | 3-5x higher (multiple researcher iterations) | Lower (2 LLM calls: decompose + synthesize) |
| Latency | 15-30s typical (4-8 LLM round-trips) | 5-10s typical (2 LLM calls) |
| Research depth | Adaptive — agent decides when to dig deeper | Fixed — always decomposes into N sub-questions |
| Behavior | Non-deterministic — agent chooses search queries dynamically | Deterministic — fixed decompose → search → synthesize path |
| Transparency | Reasoning tool streams agent's thought process | Hard-coded status messages |
Set ENABLE_AGENT_RESEARCH=false if your model doesn't support function calling, or if you prefer lower cost/latency with predictable behavior.
Prompt Security
Cortex includes built-in protection against prompt injection attacks:
Code
When enabled:
- Validates and sanitizes user input
- Injects anti-manipulation instructions into the system prompt
- Filters potentially harmful outputs
- Returns safe refusal messages when attacks are detected