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¶
- Core Concepts - Deep dive into domains, concepts, edges, and rules
- Querying - Graph-enhanced search with feedback
- Projecting Rules - Master the projection pipeline
- Consumer Integration - Connect to buildlog and other tools
- Case Studies - Framework adapter integrations