Skip to main content
Cost: 10 operations per call. Available on Starter plan and above.

POST /brain/retrieve/graph-qa

Graph QA is the highest-intelligence retrieval mode. Instead of simple vector similarity, it:
  1. Finds seed entities — uses vector search to locate the starting nodes most relevant to your question
  2. Traverses the graph — BFS expansion up to max_hops hops from each seed
  3. Grounds the answer — passes only the traversed facts to the LLM, forcing a fact-grounded response with zero hallucination
This is ideal for questions that require connecting multiple pieces of information stored across different ingestion sessions.

Request body

{
  "query": "Which engineers are connected to the PostgreSQL migration and what were their roles?",
  "max_hops": 3,
  "k": 10
}
FieldTypeRequiredDefaultDescription
querystringNatural language question to answer from the knowledge graph.
max_hopsint3Graph traversal depth (1–5). Higher = more connected context.
kint10Max facts to retrieve (1–50).
personastringnullRestrict graph traversal to a persona.

Response

{
  "answer": "Based on the knowledge graph [1][3], Sarah Jenkins led the PostgreSQL migration as Lead Engineer. She worked alongside Alex Chen (Database Administrator) [2] and the migration was approved by Mark Liu (CTO) [4]. The migration involved moving from MySQL to PostgreSQL for improved JSONB support.",
  "facts_used": 12
}
FieldTypeDescription
answerstringLLM-generated answer grounded in retrieved graph facts. Cites fact numbers.
facts_usedintNumber of graph facts passed to the LLM as context.

Code examples

from atlas_mem import AtlasMem

brain = AtlasMem(api_key="atlas_...", base_url="https://api.bsyncs.com", user_id="user-123")

# Ask a multi-hop question
answer = brain.ask("Which engineers worked on the PostgreSQL migration?")
print(answer)

# With deeper traversal
answer = brain.ask(
    "What is the relationship between Sarah Jenkins and the payment infrastructure?",
    max_hops=4,
)
print(answer)

Graph QA vs. regular retrieve

Featureretrievegraph-qa
Memory sourcesEpisodic + Semantic + WorkingSemantic graph only
Answer typeRanked facts listNatural language answer
Multi-hopOptionalAlways
LLM callOptional (compression)Always
Best forInjecting context into your own LLM callDirect factual Q&A from the graph
Cost2 ops10 ops
Use retrieve when you want to control the LLM call yourself. Use graph-qa when you want Atlas to answer the question directly.

Example — building a Q&A agent

from atlas_mem import AtlasMem

brain = AtlasMem(
    api_key="atlas_...",
    base_url="https://api.bsyncs.com",
    user_id="user-123",
)

# First, build up knowledge
brain.add("Sarah Jenkins is the Lead Engineer at Acme Corp.")
brain.add("Sarah led the PostgreSQL migration in Q3 2024.")
brain.add("The PostgreSQL migration improved query performance by 60%.")
brain.add("Alex Chen is the Database Administrator who executed the migration.")
brain.add("The migration was approved by Mark Liu, CTO of Acme Corp.")

# Now ask relational questions — no LLM key needed on your side
print(brain.ask("Who approved the database migration?"))
# → "The database migration was approved by Mark Liu, the CTO of Acme Corp. [4]"

print(brain.ask("What was the outcome of the PostgreSQL migration?"))
# → "The PostgreSQL migration improved query performance by 60%. [3]"

print(brain.ask("How is Sarah Jenkins connected to the CTO?"))
# → "Sarah Jenkins, as Lead Engineer, led the PostgreSQL migration which was approved by Mark Liu (CTO). [1][4][5]"

Guardrails

Graph QA has built-in prompt injection protection:
  • Facts are numbered and sandboxed — the LLM is instructed to cite by number, not quote
  • Instructions embedded in stored facts (e.g. “ignore previous instructions”) are stripped before being passed to the LLM
  • Results are scoped to your user_id — the traversal query includes user_id filters at every hop, so cross-tenant leakage is impossible at the database level
Graph QA requires OPENAI_API_KEY to be set on the server. If it is unavailable, the endpoint returns {"answer": "", "error": "LLM not available"}. In this case, use the retrieve endpoint instead and pass the facts to your own LLM.