Quick Start

This guide walks you through building a knowledge graph and running your first query in under 5 minutes.

1. Create a Backend

qortex uses backends to store the knowledge graph. For development, use InMemoryBackend:

from qortex.core.memory import InMemoryBackend

backend = InMemoryBackend()
backend.connect()

2. Add Some Data

Create a domain and add concepts with edges:

from qortex.core.models import ConceptNode, ConceptEdge, ExplicitRule, RelationType

# Create a domain
backend.create_domain("error_handling", "Error handling patterns")

# Add concepts
backend.add_node(ConceptNode(
    id="circuit_breaker",
    name="Circuit Breaker",
    description="Pattern that prevents cascading failures",
    domain="error_handling",
    source_id="patterns_book",
))

backend.add_node(ConceptNode(
    id="timeout",
    name="Timeout",
    description="Time limit for operations",
    domain="error_handling",
    source_id="patterns_book",
))

# Add an edge (circuit breaker REQUIRES timeout)
backend.add_edge(ConceptEdge(
    source_id="circuit_breaker",
    target_id="timeout",
    relation_type=RelationType.REQUIRES,
))

# Add an explicit rule
backend.add_rule(ExplicitRule(
    id="rule:timeout",
    text="Always configure timeouts for external calls",
    domain="error_handling",
    source_id="patterns_book",
))

3. Project Rules

Use the projection pipeline to generate rules:

from qortex.projectors.sources.flat import FlatRuleSource
from qortex.projectors.enrichers.template import TemplateEnricher
from qortex.projectors.targets.buildlog_seed import BuildlogSeedTarget
from qortex.projectors.projection import Projection

projection = Projection(
    source=FlatRuleSource(backend=backend),
    enricher=TemplateEnricher(domain="error_handling"),
    target=BuildlogSeedTarget(persona_name="error_handling_rules"),
)

result = projection.project(domains=["error_handling"])
print(f"Generated {result['metadata']['rule_count']} rules")

This produces both:

  • Explicit rules: Rules directly from your source material
  • Derived rules: Rules generated from edge templates (e.g., "Circuit Breaker requires Timeout to function correctly")

4. Output the Result

The result is a dict in the universal rule set schema:

import yaml
print(yaml.dump(result, default_flow_style=False))

Output:

persona: error_handling_rules
version: 1
rules:
  - rule: Always configure timeouts for external calls
    category: error_handling
    provenance:
      id: rule:timeout
      domain: error_handling
      derivation: explicit
      confidence: 1.0
  - rule: Circuit Breaker requires Timeout to function correctly
    category: dependency
    provenance:
      id: derived:circuit_breaker->timeout:imperative
      domain: error_handling
      derivation: derived
      confidence: 1.0
      relation_type: requires
      template_id: requires:imperative
metadata:
  source: qortex
  rule_count: 2

Using the CLI

The same workflow via CLI:

# Project to stdout
qortex project buildlog --domain error_handling

# Project to file
qortex project buildlog --domain error_handling -o rules.yaml

# Project to interop pending directory (for buildlog integration)
qortex project buildlog --domain error_handling --pending

5. Query the Graph

Use QortexClient for graph-enhanced search with feedback:

from qortex.client import LocalQortexClient
from qortex.vec.index import NumpyVectorIndex

# Set up vector search
vector_index = NumpyVectorIndex(dimensions=384)
# ... (add embeddings to vector_index)

client = LocalQortexClient(
    vector_index=vector_index,
    backend=backend,
    embedding_model=my_embedding,
    mode="graph",
)

# Search
result = client.query("circuit breaker patterns", domains=["error_handling"], top_k=5)
for item in result.items:
    print(f"{item.score:.2f}: {item.content}")

# Rules auto-surfaced in results
for rule in result.rules:
    print(f"Rule: {rule.text}")

# Explore graph from a result
explore = client.explore(result.items[0].node_id)
for edge in explore.edges:
    print(f"{edge.source_id} --{edge.relation_type}--> {edge.target_id}")

# Feedback: close the learning loop
client.feedback(result.query_id, {result.items[0].id: "accepted"})

Or use it as a LangChain VectorStore:

from qortex.adapters.langchain_vectorstore import QortexVectorStore

vs = QortexVectorStore(client=client, domain="error_handling")
docs = vs.similarity_search("timeout patterns", k=5)
retriever = vs.as_retriever()

See Querying Guide for the full query pipeline.

6. Compare Graph vs Cosine

Use qortex_compare (or client.compare() in Python) to see what graph-enhanced retrieval adds over plain cosine similarity:

# Via MCP: call qortex_compare with the same query
# Via Python:
compare_result = client.compare("circuit breaker patterns", domains=["error_handling"])
print(compare_result["summary"])
# "Graph-enhanced retrieval found 1 item(s) that cosine missed, surfaced 1 rule(s)."

# See what the graph found that cosine missed
for item in compare_result["diff"]["graph_found_that_cosine_missed"]:
    print(f"  {item['id']}: {item['content'][:80]}")

This is the fastest way to prove the graph is adding value on your own data.

7. Check Stats

Call qortex_stats to see knowledge coverage, learning progress, and activity:

stats = client.stats()
print(f"Domains: {stats['knowledge']['domains']}")
print(f"Concepts: {stats['knowledge']['concepts']}")
print(f"Queries served: {stats['activity']['queries_served']}")
print(f"Feedback rate: {stats['activity']['feedback_rate']}")

8. Remote Access via REST API

Start a qortex server and connect from any HTTP client:

# Terminal 1: start the server
pip install qortex[serve]
qortex serve
# Terminal 2: connect remotely
from qortex.http_client import HttpQortexClient

async with HttpQortexClient("http://localhost:8741") as client:
    result = await client.query("circuit breaker patterns", domains=["error_handling"])
    for item in result.items:
        print(f"{item.score:.2f}: {item.content}")

    await client.feedback(result.query_id, {result.items[0].id: "accepted"})

HttpQortexClient implements the same QortexClient protocol as LocalQortexClient. You can switch between local and remote access without changing your application code.

9. PostgreSQL for Production

For production deployments, switch from SQLite to PostgreSQL:

pip install qortex[postgres]

# Start with PostgreSQL backends
QORTEX_STORE=postgres \
DATABASE_URL=postgresql://user:pass@localhost/qortex \
qortex serve

This uses: - pgvector for vector storage (instead of sqlite-vec) - PostgresLearningStore for Thompson Sampling state (instead of aiosqlite) - PostgresInteroceptionStore for PPR teleportation factors (instead of in-memory)

All three stores share a single asyncpg connection pool for efficiency.

To migrate existing SQLite vectors:

qortex migrate vec --from sqlite

Next Steps